diff options
Diffstat (limited to 'drivers/gpu/drm')
81 files changed, 35852 insertions, 0 deletions
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 91567ac806f1..470ef6779db3 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile | |||
@@ -31,3 +31,5 @@ obj-$(CONFIG_DRM_I915) += i915/ | |||
31 | obj-$(CONFIG_DRM_SIS) += sis/ | 31 | obj-$(CONFIG_DRM_SIS) += sis/ |
32 | obj-$(CONFIG_DRM_SAVAGE)+= savage/ | 32 | obj-$(CONFIG_DRM_SAVAGE)+= savage/ |
33 | obj-$(CONFIG_DRM_VIA) +=via/ | 33 | obj-$(CONFIG_DRM_VIA) +=via/ |
34 | obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/ | ||
35 | obj-y += i2c/ | ||
diff --git a/drivers/gpu/drm/i2c/Makefile b/drivers/gpu/drm/i2c/Makefile new file mode 100644 index 000000000000..6d2abaf35ba2 --- /dev/null +++ b/drivers/gpu/drm/i2c/Makefile | |||
@@ -0,0 +1,4 @@ | |||
1 | ccflags-y := -Iinclude/drm | ||
2 | |||
3 | ch7006-y := ch7006_drv.o ch7006_mode.o | ||
4 | obj-$(CONFIG_DRM_I2C_CH7006) += ch7006.o | ||
diff --git a/drivers/gpu/drm/i2c/ch7006_drv.c b/drivers/gpu/drm/i2c/ch7006_drv.c new file mode 100644 index 000000000000..9422a74c8b54 --- /dev/null +++ b/drivers/gpu/drm/i2c/ch7006_drv.c | |||
@@ -0,0 +1,531 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009 Francisco Jerez. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include "ch7006_priv.h" | ||
28 | |||
29 | /* DRM encoder functions */ | ||
30 | |||
31 | static void ch7006_encoder_set_config(struct drm_encoder *encoder, | ||
32 | void *params) | ||
33 | { | ||
34 | struct ch7006_priv *priv = to_ch7006_priv(encoder); | ||
35 | |||
36 | priv->params = params; | ||
37 | } | ||
38 | |||
39 | static void ch7006_encoder_destroy(struct drm_encoder *encoder) | ||
40 | { | ||
41 | struct ch7006_priv *priv = to_ch7006_priv(encoder); | ||
42 | |||
43 | drm_property_destroy(encoder->dev, priv->scale_property); | ||
44 | |||
45 | kfree(priv); | ||
46 | to_encoder_slave(encoder)->slave_priv = NULL; | ||
47 | |||
48 | drm_i2c_encoder_destroy(encoder); | ||
49 | } | ||
50 | |||
51 | static void ch7006_encoder_dpms(struct drm_encoder *encoder, int mode) | ||
52 | { | ||
53 | struct i2c_client *client = drm_i2c_encoder_get_client(encoder); | ||
54 | struct ch7006_priv *priv = to_ch7006_priv(encoder); | ||
55 | struct ch7006_state *state = &priv->state; | ||
56 | |||
57 | ch7006_dbg(client, "\n"); | ||
58 | |||
59 | if (mode == priv->last_dpms) | ||
60 | return; | ||
61 | priv->last_dpms = mode; | ||
62 | |||
63 | ch7006_setup_power_state(encoder); | ||
64 | |||
65 | ch7006_load_reg(client, state, CH7006_POWER); | ||
66 | } | ||
67 | |||
68 | static void ch7006_encoder_save(struct drm_encoder *encoder) | ||
69 | { | ||
70 | struct i2c_client *client = drm_i2c_encoder_get_client(encoder); | ||
71 | struct ch7006_priv *priv = to_ch7006_priv(encoder); | ||
72 | |||
73 | ch7006_dbg(client, "\n"); | ||
74 | |||
75 | ch7006_state_save(client, &priv->saved_state); | ||
76 | } | ||
77 | |||
78 | static void ch7006_encoder_restore(struct drm_encoder *encoder) | ||
79 | { | ||
80 | struct i2c_client *client = drm_i2c_encoder_get_client(encoder); | ||
81 | struct ch7006_priv *priv = to_ch7006_priv(encoder); | ||
82 | |||
83 | ch7006_dbg(client, "\n"); | ||
84 | |||
85 | ch7006_state_load(client, &priv->saved_state); | ||
86 | } | ||
87 | |||
88 | static bool ch7006_encoder_mode_fixup(struct drm_encoder *encoder, | ||
89 | struct drm_display_mode *mode, | ||
90 | struct drm_display_mode *adjusted_mode) | ||
91 | { | ||
92 | struct ch7006_priv *priv = to_ch7006_priv(encoder); | ||
93 | |||
94 | /* The ch7006 is painfully picky with the input timings so no | ||
95 | * custom modes for now... */ | ||
96 | |||
97 | priv->mode = ch7006_lookup_mode(encoder, mode); | ||
98 | |||
99 | return !!priv->mode; | ||
100 | } | ||
101 | |||
102 | static int ch7006_encoder_mode_valid(struct drm_encoder *encoder, | ||
103 | struct drm_display_mode *mode) | ||
104 | { | ||
105 | if (ch7006_lookup_mode(encoder, mode)) | ||
106 | return MODE_OK; | ||
107 | else | ||
108 | return MODE_BAD; | ||
109 | } | ||
110 | |||
111 | static void ch7006_encoder_mode_set(struct drm_encoder *encoder, | ||
112 | struct drm_display_mode *drm_mode, | ||
113 | struct drm_display_mode *adjusted_mode) | ||
114 | { | ||
115 | struct i2c_client *client = drm_i2c_encoder_get_client(encoder); | ||
116 | struct ch7006_priv *priv = to_ch7006_priv(encoder); | ||
117 | struct ch7006_encoder_params *params = priv->params; | ||
118 | struct ch7006_state *state = &priv->state; | ||
119 | uint8_t *regs = state->regs; | ||
120 | struct ch7006_mode *mode = priv->mode; | ||
121 | struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm]; | ||
122 | int start_active; | ||
123 | |||
124 | ch7006_dbg(client, "\n"); | ||
125 | |||
126 | regs[CH7006_DISPMODE] = norm->dispmode | mode->dispmode; | ||
127 | regs[CH7006_BWIDTH] = 0; | ||
128 | regs[CH7006_INPUT_FORMAT] = bitf(CH7006_INPUT_FORMAT_FORMAT, | ||
129 | params->input_format); | ||
130 | |||
131 | regs[CH7006_CLKMODE] = CH7006_CLKMODE_SUBC_LOCK | ||
132 | | bitf(CH7006_CLKMODE_XCM, params->xcm) | ||
133 | | bitf(CH7006_CLKMODE_PCM, params->pcm); | ||
134 | if (params->clock_mode) | ||
135 | regs[CH7006_CLKMODE] |= CH7006_CLKMODE_MASTER; | ||
136 | if (params->clock_edge) | ||
137 | regs[CH7006_CLKMODE] |= CH7006_CLKMODE_POS_EDGE; | ||
138 | |||
139 | start_active = (drm_mode->htotal & ~0x7) - (drm_mode->hsync_start & ~0x7); | ||
140 | regs[CH7006_POV] = bitf(CH7006_POV_START_ACTIVE_8, start_active); | ||
141 | regs[CH7006_START_ACTIVE] = bitf(CH7006_START_ACTIVE_0, start_active); | ||
142 | |||
143 | regs[CH7006_INPUT_SYNC] = 0; | ||
144 | if (params->sync_direction) | ||
145 | regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_OUTPUT; | ||
146 | if (params->sync_encoding) | ||
147 | regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_EMBEDDED; | ||
148 | if (drm_mode->flags & DRM_MODE_FLAG_PVSYNC) | ||
149 | regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_PVSYNC; | ||
150 | if (drm_mode->flags & DRM_MODE_FLAG_PHSYNC) | ||
151 | regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_PHSYNC; | ||
152 | |||
153 | regs[CH7006_DETECT] = 0; | ||
154 | regs[CH7006_BCLKOUT] = 0; | ||
155 | |||
156 | regs[CH7006_SUBC_INC3] = 0; | ||
157 | if (params->pout_level) | ||
158 | regs[CH7006_SUBC_INC3] |= CH7006_SUBC_INC3_POUT_3_3V; | ||
159 | |||
160 | regs[CH7006_SUBC_INC4] = 0; | ||
161 | if (params->active_detect) | ||
162 | regs[CH7006_SUBC_INC4] |= CH7006_SUBC_INC4_DS_INPUT; | ||
163 | |||
164 | regs[CH7006_PLL_CONTROL] = priv->saved_state.regs[CH7006_PLL_CONTROL]; | ||
165 | |||
166 | ch7006_setup_levels(encoder); | ||
167 | ch7006_setup_subcarrier(encoder); | ||
168 | ch7006_setup_pll(encoder); | ||
169 | ch7006_setup_power_state(encoder); | ||
170 | ch7006_setup_properties(encoder); | ||
171 | |||
172 | ch7006_state_load(client, state); | ||
173 | } | ||
174 | |||
175 | static enum drm_connector_status ch7006_encoder_detect(struct drm_encoder *encoder, | ||
176 | struct drm_connector *connector) | ||
177 | { | ||
178 | struct i2c_client *client = drm_i2c_encoder_get_client(encoder); | ||
179 | struct ch7006_priv *priv = to_ch7006_priv(encoder); | ||
180 | struct ch7006_state *state = &priv->state; | ||
181 | int det; | ||
182 | |||
183 | ch7006_dbg(client, "\n"); | ||
184 | |||
185 | ch7006_save_reg(client, state, CH7006_DETECT); | ||
186 | ch7006_save_reg(client, state, CH7006_POWER); | ||
187 | ch7006_save_reg(client, state, CH7006_CLKMODE); | ||
188 | |||
189 | ch7006_write(client, CH7006_POWER, CH7006_POWER_RESET | | ||
190 | bitfs(CH7006_POWER_LEVEL, NORMAL)); | ||
191 | ch7006_write(client, CH7006_CLKMODE, CH7006_CLKMODE_MASTER); | ||
192 | |||
193 | ch7006_write(client, CH7006_DETECT, CH7006_DETECT_SENSE); | ||
194 | |||
195 | ch7006_write(client, CH7006_DETECT, 0); | ||
196 | |||
197 | det = ch7006_read(client, CH7006_DETECT); | ||
198 | |||
199 | ch7006_load_reg(client, state, CH7006_CLKMODE); | ||
200 | ch7006_load_reg(client, state, CH7006_POWER); | ||
201 | ch7006_load_reg(client, state, CH7006_DETECT); | ||
202 | |||
203 | if ((det & (CH7006_DETECT_SVIDEO_Y_TEST| | ||
204 | CH7006_DETECT_SVIDEO_C_TEST| | ||
205 | CH7006_DETECT_CVBS_TEST)) == 0) | ||
206 | priv->subconnector = DRM_MODE_SUBCONNECTOR_SCART; | ||
207 | else if ((det & (CH7006_DETECT_SVIDEO_Y_TEST| | ||
208 | CH7006_DETECT_SVIDEO_C_TEST)) == 0) | ||
209 | priv->subconnector = DRM_MODE_SUBCONNECTOR_SVIDEO; | ||
210 | else if ((det & CH7006_DETECT_CVBS_TEST) == 0) | ||
211 | priv->subconnector = DRM_MODE_SUBCONNECTOR_Composite; | ||
212 | else | ||
213 | priv->subconnector = DRM_MODE_SUBCONNECTOR_Unknown; | ||
214 | |||
215 | drm_connector_property_set_value(connector, | ||
216 | encoder->dev->mode_config.tv_subconnector_property, | ||
217 | priv->subconnector); | ||
218 | |||
219 | return priv->subconnector ? connector_status_connected : | ||
220 | connector_status_disconnected; | ||
221 | } | ||
222 | |||
223 | static int ch7006_encoder_get_modes(struct drm_encoder *encoder, | ||
224 | struct drm_connector *connector) | ||
225 | { | ||
226 | struct ch7006_priv *priv = to_ch7006_priv(encoder); | ||
227 | struct ch7006_mode *mode; | ||
228 | int n = 0; | ||
229 | |||
230 | for (mode = ch7006_modes; mode->mode.clock; mode++) { | ||
231 | if (~mode->valid_scales & 1<<priv->scale || | ||
232 | ~mode->valid_norms & 1<<priv->norm) | ||
233 | continue; | ||
234 | |||
235 | drm_mode_probed_add(connector, | ||
236 | drm_mode_duplicate(encoder->dev, &mode->mode)); | ||
237 | |||
238 | n++; | ||
239 | } | ||
240 | |||
241 | return n; | ||
242 | } | ||
243 | |||
244 | static int ch7006_encoder_create_resources(struct drm_encoder *encoder, | ||
245 | struct drm_connector *connector) | ||
246 | { | ||
247 | struct ch7006_priv *priv = to_ch7006_priv(encoder); | ||
248 | struct drm_device *dev = encoder->dev; | ||
249 | struct drm_mode_config *conf = &dev->mode_config; | ||
250 | |||
251 | drm_mode_create_tv_properties(dev, NUM_TV_NORMS, ch7006_tv_norm_names); | ||
252 | |||
253 | priv->scale_property = drm_property_create(dev, DRM_MODE_PROP_RANGE, | ||
254 | "scale", 2); | ||
255 | priv->scale_property->values[0] = 0; | ||
256 | priv->scale_property->values[1] = 2; | ||
257 | |||
258 | drm_connector_attach_property(connector, conf->tv_select_subconnector_property, | ||
259 | priv->select_subconnector); | ||
260 | drm_connector_attach_property(connector, conf->tv_subconnector_property, | ||
261 | priv->subconnector); | ||
262 | drm_connector_attach_property(connector, conf->tv_left_margin_property, | ||
263 | priv->hmargin); | ||
264 | drm_connector_attach_property(connector, conf->tv_bottom_margin_property, | ||
265 | priv->vmargin); | ||
266 | drm_connector_attach_property(connector, conf->tv_mode_property, | ||
267 | priv->norm); | ||
268 | drm_connector_attach_property(connector, conf->tv_brightness_property, | ||
269 | priv->brightness); | ||
270 | drm_connector_attach_property(connector, conf->tv_contrast_property, | ||
271 | priv->contrast); | ||
272 | drm_connector_attach_property(connector, conf->tv_flicker_reduction_property, | ||
273 | priv->flicker); | ||
274 | drm_connector_attach_property(connector, priv->scale_property, | ||
275 | priv->scale); | ||
276 | |||
277 | return 0; | ||
278 | } | ||
279 | |||
280 | static int ch7006_encoder_set_property(struct drm_encoder *encoder, | ||
281 | struct drm_connector *connector, | ||
282 | struct drm_property *property, | ||
283 | uint64_t val) | ||
284 | { | ||
285 | struct i2c_client *client = drm_i2c_encoder_get_client(encoder); | ||
286 | struct ch7006_priv *priv = to_ch7006_priv(encoder); | ||
287 | struct ch7006_state *state = &priv->state; | ||
288 | struct drm_mode_config *conf = &encoder->dev->mode_config; | ||
289 | struct drm_crtc *crtc = encoder->crtc; | ||
290 | bool modes_changed = false; | ||
291 | |||
292 | ch7006_dbg(client, "\n"); | ||
293 | |||
294 | if (property == conf->tv_select_subconnector_property) { | ||
295 | priv->select_subconnector = val; | ||
296 | |||
297 | ch7006_setup_power_state(encoder); | ||
298 | |||
299 | ch7006_load_reg(client, state, CH7006_POWER); | ||
300 | |||
301 | } else if (property == conf->tv_left_margin_property) { | ||
302 | priv->hmargin = val; | ||
303 | |||
304 | ch7006_setup_properties(encoder); | ||
305 | |||
306 | ch7006_load_reg(client, state, CH7006_POV); | ||
307 | ch7006_load_reg(client, state, CH7006_HPOS); | ||
308 | |||
309 | } else if (property == conf->tv_bottom_margin_property) { | ||
310 | priv->vmargin = val; | ||
311 | |||
312 | ch7006_setup_properties(encoder); | ||
313 | |||
314 | ch7006_load_reg(client, state, CH7006_POV); | ||
315 | ch7006_load_reg(client, state, CH7006_VPOS); | ||
316 | |||
317 | } else if (property == conf->tv_mode_property) { | ||
318 | if (connector->dpms != DRM_MODE_DPMS_OFF) | ||
319 | return -EINVAL; | ||
320 | |||
321 | priv->norm = val; | ||
322 | |||
323 | modes_changed = true; | ||
324 | |||
325 | } else if (property == conf->tv_brightness_property) { | ||
326 | priv->brightness = val; | ||
327 | |||
328 | ch7006_setup_levels(encoder); | ||
329 | |||
330 | ch7006_load_reg(client, state, CH7006_BLACK_LEVEL); | ||
331 | |||
332 | } else if (property == conf->tv_contrast_property) { | ||
333 | priv->contrast = val; | ||
334 | |||
335 | ch7006_setup_properties(encoder); | ||
336 | |||
337 | ch7006_load_reg(client, state, CH7006_CONTRAST); | ||
338 | |||
339 | } else if (property == conf->tv_flicker_reduction_property) { | ||
340 | priv->flicker = val; | ||
341 | |||
342 | ch7006_setup_properties(encoder); | ||
343 | |||
344 | ch7006_load_reg(client, state, CH7006_FFILTER); | ||
345 | |||
346 | } else if (property == priv->scale_property) { | ||
347 | if (connector->dpms != DRM_MODE_DPMS_OFF) | ||
348 | return -EINVAL; | ||
349 | |||
350 | priv->scale = val; | ||
351 | |||
352 | modes_changed = true; | ||
353 | |||
354 | } else { | ||
355 | return -EINVAL; | ||
356 | } | ||
357 | |||
358 | if (modes_changed) { | ||
359 | drm_helper_probe_single_connector_modes(connector, 0, 0); | ||
360 | |||
361 | /* Disable the crtc to ensure a full modeset is | ||
362 | * performed whenever it's turned on again. */ | ||
363 | if (crtc) { | ||
364 | struct drm_mode_set modeset = { | ||
365 | .crtc = crtc, | ||
366 | }; | ||
367 | |||
368 | crtc->funcs->set_config(&modeset); | ||
369 | } | ||
370 | } | ||
371 | |||
372 | return 0; | ||
373 | } | ||
374 | |||
375 | static struct drm_encoder_slave_funcs ch7006_encoder_funcs = { | ||
376 | .set_config = ch7006_encoder_set_config, | ||
377 | .destroy = ch7006_encoder_destroy, | ||
378 | .dpms = ch7006_encoder_dpms, | ||
379 | .save = ch7006_encoder_save, | ||
380 | .restore = ch7006_encoder_restore, | ||
381 | .mode_fixup = ch7006_encoder_mode_fixup, | ||
382 | .mode_valid = ch7006_encoder_mode_valid, | ||
383 | .mode_set = ch7006_encoder_mode_set, | ||
384 | .detect = ch7006_encoder_detect, | ||
385 | .get_modes = ch7006_encoder_get_modes, | ||
386 | .create_resources = ch7006_encoder_create_resources, | ||
387 | .set_property = ch7006_encoder_set_property, | ||
388 | }; | ||
389 | |||
390 | |||
391 | /* I2C driver functions */ | ||
392 | |||
393 | static int ch7006_probe(struct i2c_client *client, const struct i2c_device_id *id) | ||
394 | { | ||
395 | uint8_t addr = CH7006_VERSION_ID; | ||
396 | uint8_t val; | ||
397 | int ret; | ||
398 | |||
399 | ch7006_dbg(client, "\n"); | ||
400 | |||
401 | ret = i2c_master_send(client, &addr, sizeof(addr)); | ||
402 | if (ret < 0) | ||
403 | goto fail; | ||
404 | |||
405 | ret = i2c_master_recv(client, &val, sizeof(val)); | ||
406 | if (ret < 0) | ||
407 | goto fail; | ||
408 | |||
409 | ch7006_info(client, "Detected version ID: %x\n", val); | ||
410 | |||
411 | return 0; | ||
412 | |||
413 | fail: | ||
414 | ch7006_err(client, "Error %d reading version ID\n", ret); | ||
415 | |||
416 | return -ENODEV; | ||
417 | } | ||
418 | |||
419 | static int ch7006_remove(struct i2c_client *client) | ||
420 | { | ||
421 | ch7006_dbg(client, "\n"); | ||
422 | |||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | static int ch7006_encoder_init(struct i2c_client *client, | ||
427 | struct drm_device *dev, | ||
428 | struct drm_encoder_slave *encoder) | ||
429 | { | ||
430 | struct ch7006_priv *priv; | ||
431 | int i; | ||
432 | |||
433 | ch7006_dbg(client, "\n"); | ||
434 | |||
435 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); | ||
436 | if (!priv) | ||
437 | return -ENOMEM; | ||
438 | |||
439 | encoder->slave_priv = priv; | ||
440 | encoder->slave_funcs = &ch7006_encoder_funcs; | ||
441 | |||
442 | priv->norm = TV_NORM_PAL; | ||
443 | priv->select_subconnector = DRM_MODE_SUBCONNECTOR_Automatic; | ||
444 | priv->subconnector = DRM_MODE_SUBCONNECTOR_Unknown; | ||
445 | priv->scale = 1; | ||
446 | priv->contrast = 50; | ||
447 | priv->brightness = 50; | ||
448 | priv->flicker = 50; | ||
449 | priv->hmargin = 50; | ||
450 | priv->vmargin = 50; | ||
451 | priv->last_dpms = -1; | ||
452 | |||
453 | if (ch7006_tv_norm) { | ||
454 | for (i = 0; i < NUM_TV_NORMS; i++) { | ||
455 | if (!strcmp(ch7006_tv_norm_names[i], ch7006_tv_norm)) { | ||
456 | priv->norm = i; | ||
457 | break; | ||
458 | } | ||
459 | } | ||
460 | |||
461 | if (i == NUM_TV_NORMS) | ||
462 | ch7006_err(client, "Invalid TV norm setting \"%s\".\n", | ||
463 | ch7006_tv_norm); | ||
464 | } | ||
465 | |||
466 | if (ch7006_scale >= 0 && ch7006_scale <= 2) | ||
467 | priv->scale = ch7006_scale; | ||
468 | else | ||
469 | ch7006_err(client, "Invalid scale setting \"%d\".\n", | ||
470 | ch7006_scale); | ||
471 | |||
472 | return 0; | ||
473 | } | ||
474 | |||
475 | static struct i2c_device_id ch7006_ids[] = { | ||
476 | { "ch7006", 0 }, | ||
477 | { } | ||
478 | }; | ||
479 | MODULE_DEVICE_TABLE(i2c, ch7006_ids); | ||
480 | |||
481 | static struct drm_i2c_encoder_driver ch7006_driver = { | ||
482 | .i2c_driver = { | ||
483 | .probe = ch7006_probe, | ||
484 | .remove = ch7006_remove, | ||
485 | |||
486 | .driver = { | ||
487 | .name = "ch7006", | ||
488 | }, | ||
489 | |||
490 | .id_table = ch7006_ids, | ||
491 | }, | ||
492 | |||
493 | .encoder_init = ch7006_encoder_init, | ||
494 | }; | ||
495 | |||
496 | |||
497 | /* Module initialization */ | ||
498 | |||
499 | static int __init ch7006_init(void) | ||
500 | { | ||
501 | return drm_i2c_encoder_register(THIS_MODULE, &ch7006_driver); | ||
502 | } | ||
503 | |||
504 | static void __exit ch7006_exit(void) | ||
505 | { | ||
506 | drm_i2c_encoder_unregister(&ch7006_driver); | ||
507 | } | ||
508 | |||
509 | int ch7006_debug; | ||
510 | module_param_named(debug, ch7006_debug, int, 0600); | ||
511 | MODULE_PARM_DESC(debug, "Enable debug output."); | ||
512 | |||
513 | char *ch7006_tv_norm; | ||
514 | module_param_named(tv_norm, ch7006_tv_norm, charp, 0600); | ||
515 | MODULE_PARM_DESC(tv_norm, "Default TV norm.\n" | ||
516 | "\t\tSupported: PAL, PAL-M, PAL-N, PAL-Nc, PAL-60, NTSC-M, NTSC-J.\n" | ||
517 | "\t\tDefault: PAL"); | ||
518 | |||
519 | int ch7006_scale = 1; | ||
520 | module_param_named(scale, ch7006_scale, int, 0600); | ||
521 | MODULE_PARM_DESC(scale, "Default scale.\n" | ||
522 | "\t\tSupported: 0 -> Select video modes with a higher blanking ratio.\n" | ||
523 | "\t\t\t1 -> Select default video modes.\n" | ||
524 | "\t\t\t2 -> Select video modes with a lower blanking ratio."); | ||
525 | |||
526 | MODULE_AUTHOR("Francisco Jerez <currojerez@riseup.net>"); | ||
527 | MODULE_DESCRIPTION("Chrontel ch7006 TV encoder driver"); | ||
528 | MODULE_LICENSE("GPL and additional rights"); | ||
529 | |||
530 | module_init(ch7006_init); | ||
531 | module_exit(ch7006_exit); | ||
diff --git a/drivers/gpu/drm/i2c/ch7006_mode.c b/drivers/gpu/drm/i2c/ch7006_mode.c new file mode 100644 index 000000000000..87f5445092e8 --- /dev/null +++ b/drivers/gpu/drm/i2c/ch7006_mode.c | |||
@@ -0,0 +1,473 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009 Francisco Jerez. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include "ch7006_priv.h" | ||
28 | |||
29 | char *ch7006_tv_norm_names[] = { | ||
30 | [TV_NORM_PAL] = "PAL", | ||
31 | [TV_NORM_PAL_M] = "PAL-M", | ||
32 | [TV_NORM_PAL_N] = "PAL-N", | ||
33 | [TV_NORM_PAL_NC] = "PAL-Nc", | ||
34 | [TV_NORM_PAL_60] = "PAL-60", | ||
35 | [TV_NORM_NTSC_M] = "NTSC-M", | ||
36 | [TV_NORM_NTSC_J] = "NTSC-J", | ||
37 | }; | ||
38 | |||
39 | #define NTSC_LIKE_TIMINGS .vrefresh = 60 * fixed1/1.001, \ | ||
40 | .vdisplay = 480, \ | ||
41 | .vtotal = 525, \ | ||
42 | .hvirtual = 660 | ||
43 | |||
44 | #define PAL_LIKE_TIMINGS .vrefresh = 50 * fixed1, \ | ||
45 | .vdisplay = 576, \ | ||
46 | .vtotal = 625, \ | ||
47 | .hvirtual = 810 | ||
48 | |||
49 | struct ch7006_tv_norm_info ch7006_tv_norms[] = { | ||
50 | [TV_NORM_NTSC_M] = { | ||
51 | NTSC_LIKE_TIMINGS, | ||
52 | .black_level = 0.339 * fixed1, | ||
53 | .subc_freq = 3579545 * fixed1, | ||
54 | .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, NTSC), | ||
55 | .voffset = 0, | ||
56 | }, | ||
57 | [TV_NORM_NTSC_J] = { | ||
58 | NTSC_LIKE_TIMINGS, | ||
59 | .black_level = 0.286 * fixed1, | ||
60 | .subc_freq = 3579545 * fixed1, | ||
61 | .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, NTSC_J), | ||
62 | .voffset = 0, | ||
63 | }, | ||
64 | [TV_NORM_PAL] = { | ||
65 | PAL_LIKE_TIMINGS, | ||
66 | .black_level = 0.3 * fixed1, | ||
67 | .subc_freq = 4433618.75 * fixed1, | ||
68 | .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL), | ||
69 | .voffset = 0, | ||
70 | }, | ||
71 | [TV_NORM_PAL_M] = { | ||
72 | NTSC_LIKE_TIMINGS, | ||
73 | .black_level = 0.339 * fixed1, | ||
74 | .subc_freq = 3575611.433 * fixed1, | ||
75 | .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL_M), | ||
76 | .voffset = 16, | ||
77 | }, | ||
78 | |||
79 | /* The following modes seem to work right but they're | ||
80 | * undocumented */ | ||
81 | |||
82 | [TV_NORM_PAL_N] = { | ||
83 | PAL_LIKE_TIMINGS, | ||
84 | .black_level = 0.339 * fixed1, | ||
85 | .subc_freq = 4433618.75 * fixed1, | ||
86 | .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL), | ||
87 | .voffset = 0, | ||
88 | }, | ||
89 | [TV_NORM_PAL_NC] = { | ||
90 | PAL_LIKE_TIMINGS, | ||
91 | .black_level = 0.3 * fixed1, | ||
92 | .subc_freq = 3582056.25 * fixed1, | ||
93 | .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL), | ||
94 | .voffset = 0, | ||
95 | }, | ||
96 | [TV_NORM_PAL_60] = { | ||
97 | NTSC_LIKE_TIMINGS, | ||
98 | .black_level = 0.3 * fixed1, | ||
99 | .subc_freq = 4433618.75 * fixed1, | ||
100 | .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL_M), | ||
101 | .voffset = 16, | ||
102 | }, | ||
103 | }; | ||
104 | |||
105 | #define __MODE(f, hd, vd, ht, vt, hsynp, vsynp, \ | ||
106 | subc, scale, scale_mask, norm_mask, e_hd, e_vd) { \ | ||
107 | .mode = { \ | ||
108 | .name = #hd "x" #vd, \ | ||
109 | .status = 0, \ | ||
110 | .type = DRM_MODE_TYPE_DRIVER, \ | ||
111 | .clock = f, \ | ||
112 | .hdisplay = hd, \ | ||
113 | .hsync_start = e_hd + 16, \ | ||
114 | .hsync_end = e_hd + 80, \ | ||
115 | .htotal = ht, \ | ||
116 | .hskew = 0, \ | ||
117 | .vdisplay = vd, \ | ||
118 | .vsync_start = vd + 10, \ | ||
119 | .vsync_end = vd + 26, \ | ||
120 | .vtotal = vt, \ | ||
121 | .vscan = 0, \ | ||
122 | .flags = DRM_MODE_FLAG_##hsynp##HSYNC | \ | ||
123 | DRM_MODE_FLAG_##vsynp##VSYNC, \ | ||
124 | .vrefresh = 0, \ | ||
125 | }, \ | ||
126 | .enc_hdisp = e_hd, \ | ||
127 | .enc_vdisp = e_vd, \ | ||
128 | .subc_coeff = subc * fixed1, \ | ||
129 | .dispmode = bitfs(CH7006_DISPMODE_SCALING_RATIO, scale) | \ | ||
130 | bitfs(CH7006_DISPMODE_INPUT_RES, e_hd##x##e_vd), \ | ||
131 | .valid_scales = scale_mask, \ | ||
132 | .valid_norms = norm_mask \ | ||
133 | } | ||
134 | |||
135 | #define MODE(f, hd, vd, ht, vt, hsynp, vsynp, \ | ||
136 | subc, scale, scale_mask, norm_mask) \ | ||
137 | __MODE(f, hd, vd, ht, vt, hsynp, vsynp, subc, scale, \ | ||
138 | scale_mask, norm_mask, hd, vd) | ||
139 | |||
140 | #define NTSC_LIKE (1 << TV_NORM_NTSC_M | 1 << TV_NORM_NTSC_J | \ | ||
141 | 1 << TV_NORM_PAL_M | 1 << TV_NORM_PAL_60) | ||
142 | |||
143 | #define PAL_LIKE (1 << TV_NORM_PAL | 1 << TV_NORM_PAL_N | 1 << TV_NORM_PAL_NC) | ||
144 | |||
145 | struct ch7006_mode ch7006_modes[] = { | ||
146 | MODE(21000, 512, 384, 840, 500, N, N, 181.797557582, 5_4, 0x6, PAL_LIKE), | ||
147 | MODE(26250, 512, 384, 840, 625, N, N, 145.438046066, 1_1, 0x1, PAL_LIKE), | ||
148 | MODE(20140, 512, 384, 800, 420, N, N, 213.257083791, 5_4, 0x4, NTSC_LIKE), | ||
149 | MODE(24671, 512, 384, 784, 525, N, N, 174.0874153, 1_1, 0x3, NTSC_LIKE), | ||
150 | MODE(28125, 720, 400, 1125, 500, N, N, 135.742176298, 5_4, 0x6, PAL_LIKE), | ||
151 | MODE(34875, 720, 400, 1116, 625, N, N, 109.469496898, 1_1, 0x1, PAL_LIKE), | ||
152 | MODE(23790, 720, 400, 945, 420, N, N, 160.475642016, 5_4, 0x4, NTSC_LIKE), | ||
153 | MODE(29455, 720, 400, 936, 525, N, N, 129.614941843, 1_1, 0x3, NTSC_LIKE), | ||
154 | MODE(25000, 640, 400, 1000, 500, N, N, 152.709948279, 5_4, 0x6, PAL_LIKE), | ||
155 | MODE(31500, 640, 400, 1008, 625, N, N, 121.198371646, 1_1, 0x1, PAL_LIKE), | ||
156 | MODE(21147, 640, 400, 840, 420, N, N, 180.535097338, 5_4, 0x4, NTSC_LIKE), | ||
157 | MODE(26434, 640, 400, 840, 525, N, N, 144.42807787, 1_1, 0x2, NTSC_LIKE), | ||
158 | MODE(30210, 640, 400, 840, 600, N, N, 126.374568276, 7_8, 0x1, NTSC_LIKE), | ||
159 | MODE(21000, 640, 480, 840, 500, N, N, 181.797557582, 5_4, 0x4, PAL_LIKE), | ||
160 | MODE(26250, 640, 480, 840, 625, N, N, 145.438046066, 1_1, 0x2, PAL_LIKE), | ||
161 | MODE(31500, 640, 480, 840, 750, N, N, 121.198371646, 5_6, 0x1, PAL_LIKE), | ||
162 | MODE(24671, 640, 480, 784, 525, N, N, 174.0874153, 1_1, 0x4, NTSC_LIKE), | ||
163 | MODE(28196, 640, 480, 784, 600, N, N, 152.326488422, 7_8, 0x2, NTSC_LIKE), | ||
164 | MODE(30210, 640, 480, 800, 630, N, N, 142.171389101, 5_6, 0x1, NTSC_LIKE), | ||
165 | __MODE(29500, 720, 576, 944, 625, P, P, 145.592111636, 1_1, 0x7, PAL_LIKE, 800, 600), | ||
166 | MODE(36000, 800, 600, 960, 750, P, P, 119.304647022, 5_6, 0x6, PAL_LIKE), | ||
167 | MODE(39000, 800, 600, 936, 836, P, P, 110.127366499, 3_4, 0x1, PAL_LIKE), | ||
168 | MODE(39273, 800, 600, 1040, 630, P, P, 145.816809399, 5_6, 0x4, NTSC_LIKE), | ||
169 | MODE(43636, 800, 600, 1040, 700, P, P, 131.235128487, 3_4, 0x2, NTSC_LIKE), | ||
170 | MODE(47832, 800, 600, 1064, 750, P, P, 119.723275165, 7_10, 0x1, NTSC_LIKE), | ||
171 | {} | ||
172 | }; | ||
173 | |||
174 | struct ch7006_mode *ch7006_lookup_mode(struct drm_encoder *encoder, | ||
175 | struct drm_display_mode *drm_mode) | ||
176 | { | ||
177 | struct ch7006_priv *priv = to_ch7006_priv(encoder); | ||
178 | struct ch7006_mode *mode; | ||
179 | |||
180 | for (mode = ch7006_modes; mode->mode.clock; mode++) { | ||
181 | |||
182 | if (~mode->valid_norms & 1<<priv->norm) | ||
183 | continue; | ||
184 | |||
185 | if (mode->mode.hdisplay != drm_mode->hdisplay || | ||
186 | mode->mode.vdisplay != drm_mode->vdisplay || | ||
187 | mode->mode.vtotal != drm_mode->vtotal || | ||
188 | mode->mode.htotal != drm_mode->htotal || | ||
189 | mode->mode.clock != drm_mode->clock) | ||
190 | continue; | ||
191 | |||
192 | return mode; | ||
193 | } | ||
194 | |||
195 | return NULL; | ||
196 | } | ||
197 | |||
198 | /* Some common HW state calculation code */ | ||
199 | |||
200 | void ch7006_setup_levels(struct drm_encoder *encoder) | ||
201 | { | ||
202 | struct i2c_client *client = drm_i2c_encoder_get_client(encoder); | ||
203 | struct ch7006_priv *priv = to_ch7006_priv(encoder); | ||
204 | uint8_t *regs = priv->state.regs; | ||
205 | struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm]; | ||
206 | int gain; | ||
207 | int black_level; | ||
208 | |||
209 | /* Set DAC_GAIN if the voltage drop between white and black is | ||
210 | * high enough. */ | ||
211 | if (norm->black_level < 339*fixed1/1000) { | ||
212 | gain = 76; | ||
213 | |||
214 | regs[CH7006_INPUT_FORMAT] |= CH7006_INPUT_FORMAT_DAC_GAIN; | ||
215 | } else { | ||
216 | gain = 71; | ||
217 | |||
218 | regs[CH7006_INPUT_FORMAT] &= ~CH7006_INPUT_FORMAT_DAC_GAIN; | ||
219 | } | ||
220 | |||
221 | black_level = round_fixed(norm->black_level*26625)/gain; | ||
222 | |||
223 | /* Correct it with the specified brightness. */ | ||
224 | black_level = interpolate(90, black_level, 208, priv->brightness); | ||
225 | |||
226 | regs[CH7006_BLACK_LEVEL] = bitf(CH7006_BLACK_LEVEL_0, black_level); | ||
227 | |||
228 | ch7006_dbg(client, "black level: %d\n", black_level); | ||
229 | } | ||
230 | |||
231 | void ch7006_setup_subcarrier(struct drm_encoder *encoder) | ||
232 | { | ||
233 | struct i2c_client *client = drm_i2c_encoder_get_client(encoder); | ||
234 | struct ch7006_priv *priv = to_ch7006_priv(encoder); | ||
235 | struct ch7006_state *state = &priv->state; | ||
236 | struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm]; | ||
237 | struct ch7006_mode *mode = priv->mode; | ||
238 | uint32_t subc_inc; | ||
239 | |||
240 | subc_inc = round_fixed((mode->subc_coeff >> 8) | ||
241 | * (norm->subc_freq >> 24)); | ||
242 | |||
243 | setbitf(state, CH7006_SUBC_INC0, 28, subc_inc); | ||
244 | setbitf(state, CH7006_SUBC_INC1, 24, subc_inc); | ||
245 | setbitf(state, CH7006_SUBC_INC2, 20, subc_inc); | ||
246 | setbitf(state, CH7006_SUBC_INC3, 16, subc_inc); | ||
247 | setbitf(state, CH7006_SUBC_INC4, 12, subc_inc); | ||
248 | setbitf(state, CH7006_SUBC_INC5, 8, subc_inc); | ||
249 | setbitf(state, CH7006_SUBC_INC6, 4, subc_inc); | ||
250 | setbitf(state, CH7006_SUBC_INC7, 0, subc_inc); | ||
251 | |||
252 | ch7006_dbg(client, "subcarrier inc: %u\n", subc_inc); | ||
253 | } | ||
254 | |||
255 | void ch7006_setup_pll(struct drm_encoder *encoder) | ||
256 | { | ||
257 | struct i2c_client *client = drm_i2c_encoder_get_client(encoder); | ||
258 | struct ch7006_priv *priv = to_ch7006_priv(encoder); | ||
259 | uint8_t *regs = priv->state.regs; | ||
260 | struct ch7006_mode *mode = priv->mode; | ||
261 | int n, best_n = 0; | ||
262 | int m, best_m = 0; | ||
263 | int freq, best_freq = 0; | ||
264 | |||
265 | for (n = 0; n < CH7006_MAXN; n++) { | ||
266 | for (m = 0; m < CH7006_MAXM; m++) { | ||
267 | freq = CH7006_FREQ0*(n+2)/(m+2); | ||
268 | |||
269 | if (abs(freq - mode->mode.clock) < | ||
270 | abs(best_freq - mode->mode.clock)) { | ||
271 | best_freq = freq; | ||
272 | best_n = n; | ||
273 | best_m = m; | ||
274 | } | ||
275 | } | ||
276 | } | ||
277 | |||
278 | regs[CH7006_PLLOV] = bitf(CH7006_PLLOV_N_8, best_n) | | ||
279 | bitf(CH7006_PLLOV_M_8, best_m); | ||
280 | |||
281 | regs[CH7006_PLLM] = bitf(CH7006_PLLM_0, best_m); | ||
282 | regs[CH7006_PLLN] = bitf(CH7006_PLLN_0, best_n); | ||
283 | |||
284 | if (best_n < 108) | ||
285 | regs[CH7006_PLL_CONTROL] |= CH7006_PLL_CONTROL_CAPACITOR; | ||
286 | else | ||
287 | regs[CH7006_PLL_CONTROL] &= ~CH7006_PLL_CONTROL_CAPACITOR; | ||
288 | |||
289 | ch7006_dbg(client, "n=%d m=%d f=%d c=%d\n", | ||
290 | best_n, best_m, best_freq, best_n < 108); | ||
291 | } | ||
292 | |||
293 | void ch7006_setup_power_state(struct drm_encoder *encoder) | ||
294 | { | ||
295 | struct ch7006_priv *priv = to_ch7006_priv(encoder); | ||
296 | uint8_t *power = &priv->state.regs[CH7006_POWER]; | ||
297 | int subconnector; | ||
298 | |||
299 | subconnector = priv->select_subconnector ? priv->select_subconnector : | ||
300 | priv->subconnector; | ||
301 | |||
302 | *power = CH7006_POWER_RESET; | ||
303 | |||
304 | if (priv->last_dpms == DRM_MODE_DPMS_ON) { | ||
305 | switch (subconnector) { | ||
306 | case DRM_MODE_SUBCONNECTOR_SVIDEO: | ||
307 | *power |= bitfs(CH7006_POWER_LEVEL, CVBS_OFF); | ||
308 | break; | ||
309 | case DRM_MODE_SUBCONNECTOR_Composite: | ||
310 | *power |= bitfs(CH7006_POWER_LEVEL, SVIDEO_OFF); | ||
311 | break; | ||
312 | case DRM_MODE_SUBCONNECTOR_SCART: | ||
313 | *power |= bitfs(CH7006_POWER_LEVEL, NORMAL) | | ||
314 | CH7006_POWER_SCART; | ||
315 | break; | ||
316 | } | ||
317 | |||
318 | } else { | ||
319 | *power |= bitfs(CH7006_POWER_LEVEL, FULL_POWER_OFF); | ||
320 | } | ||
321 | } | ||
322 | |||
323 | void ch7006_setup_properties(struct drm_encoder *encoder) | ||
324 | { | ||
325 | struct i2c_client *client = drm_i2c_encoder_get_client(encoder); | ||
326 | struct ch7006_priv *priv = to_ch7006_priv(encoder); | ||
327 | struct ch7006_state *state = &priv->state; | ||
328 | struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm]; | ||
329 | struct ch7006_mode *ch_mode = priv->mode; | ||
330 | struct drm_display_mode *mode = &ch_mode->mode; | ||
331 | uint8_t *regs = state->regs; | ||
332 | int flicker, contrast, hpos, vpos; | ||
333 | uint64_t scale, aspect; | ||
334 | |||
335 | flicker = interpolate(0, 2, 3, priv->flicker); | ||
336 | regs[CH7006_FFILTER] = bitf(CH7006_FFILTER_TEXT, flicker) | | ||
337 | bitf(CH7006_FFILTER_LUMA, flicker) | | ||
338 | bitf(CH7006_FFILTER_CHROMA, 1); | ||
339 | |||
340 | contrast = interpolate(0, 5, 7, priv->contrast); | ||
341 | regs[CH7006_CONTRAST] = bitf(CH7006_CONTRAST_0, contrast); | ||
342 | |||
343 | scale = norm->vtotal*fixed1; | ||
344 | do_div(scale, mode->vtotal); | ||
345 | |||
346 | aspect = ch_mode->enc_hdisp*fixed1; | ||
347 | do_div(aspect, ch_mode->enc_vdisp); | ||
348 | |||
349 | hpos = round_fixed((norm->hvirtual * aspect - mode->hdisplay * scale) | ||
350 | * priv->hmargin * mode->vtotal) / norm->vtotal / 100 / 4; | ||
351 | |||
352 | setbitf(state, CH7006_POV, HPOS_8, hpos); | ||
353 | setbitf(state, CH7006_HPOS, 0, hpos); | ||
354 | |||
355 | vpos = max(0, norm->vdisplay - round_fixed(mode->vdisplay*scale) | ||
356 | + norm->voffset) * priv->vmargin / 100 / 2; | ||
357 | |||
358 | setbitf(state, CH7006_POV, VPOS_8, vpos); | ||
359 | setbitf(state, CH7006_VPOS, 0, vpos); | ||
360 | |||
361 | ch7006_dbg(client, "hpos: %d, vpos: %d\n", hpos, vpos); | ||
362 | } | ||
363 | |||
364 | /* HW access functions */ | ||
365 | |||
366 | void ch7006_write(struct i2c_client *client, uint8_t addr, uint8_t val) | ||
367 | { | ||
368 | uint8_t buf[] = {addr, val}; | ||
369 | int ret; | ||
370 | |||
371 | ret = i2c_master_send(client, buf, ARRAY_SIZE(buf)); | ||
372 | if (ret < 0) | ||
373 | ch7006_err(client, "Error %d writing to subaddress 0x%x\n", | ||
374 | ret, addr); | ||
375 | } | ||
376 | |||
377 | uint8_t ch7006_read(struct i2c_client *client, uint8_t addr) | ||
378 | { | ||
379 | uint8_t val; | ||
380 | int ret; | ||
381 | |||
382 | ret = i2c_master_send(client, &addr, sizeof(addr)); | ||
383 | if (ret < 0) | ||
384 | goto fail; | ||
385 | |||
386 | ret = i2c_master_recv(client, &val, sizeof(val)); | ||
387 | if (ret < 0) | ||
388 | goto fail; | ||
389 | |||
390 | return val; | ||
391 | |||
392 | fail: | ||
393 | ch7006_err(client, "Error %d reading from subaddress 0x%x\n", | ||
394 | ret, addr); | ||
395 | return 0; | ||
396 | } | ||
397 | |||
398 | void ch7006_state_load(struct i2c_client *client, | ||
399 | struct ch7006_state *state) | ||
400 | { | ||
401 | ch7006_load_reg(client, state, CH7006_POWER); | ||
402 | |||
403 | ch7006_load_reg(client, state, CH7006_DISPMODE); | ||
404 | ch7006_load_reg(client, state, CH7006_FFILTER); | ||
405 | ch7006_load_reg(client, state, CH7006_BWIDTH); | ||
406 | ch7006_load_reg(client, state, CH7006_INPUT_FORMAT); | ||
407 | ch7006_load_reg(client, state, CH7006_CLKMODE); | ||
408 | ch7006_load_reg(client, state, CH7006_START_ACTIVE); | ||
409 | ch7006_load_reg(client, state, CH7006_POV); | ||
410 | ch7006_load_reg(client, state, CH7006_BLACK_LEVEL); | ||
411 | ch7006_load_reg(client, state, CH7006_HPOS); | ||
412 | ch7006_load_reg(client, state, CH7006_VPOS); | ||
413 | ch7006_load_reg(client, state, CH7006_INPUT_SYNC); | ||
414 | ch7006_load_reg(client, state, CH7006_DETECT); | ||
415 | ch7006_load_reg(client, state, CH7006_CONTRAST); | ||
416 | ch7006_load_reg(client, state, CH7006_PLLOV); | ||
417 | ch7006_load_reg(client, state, CH7006_PLLM); | ||
418 | ch7006_load_reg(client, state, CH7006_PLLN); | ||
419 | ch7006_load_reg(client, state, CH7006_BCLKOUT); | ||
420 | ch7006_load_reg(client, state, CH7006_SUBC_INC0); | ||
421 | ch7006_load_reg(client, state, CH7006_SUBC_INC1); | ||
422 | ch7006_load_reg(client, state, CH7006_SUBC_INC2); | ||
423 | ch7006_load_reg(client, state, CH7006_SUBC_INC3); | ||
424 | ch7006_load_reg(client, state, CH7006_SUBC_INC4); | ||
425 | ch7006_load_reg(client, state, CH7006_SUBC_INC5); | ||
426 | ch7006_load_reg(client, state, CH7006_SUBC_INC6); | ||
427 | ch7006_load_reg(client, state, CH7006_SUBC_INC7); | ||
428 | ch7006_load_reg(client, state, CH7006_PLL_CONTROL); | ||
429 | ch7006_load_reg(client, state, CH7006_CALC_SUBC_INC0); | ||
430 | |||
431 | /* I don't know what this is for, but otherwise I get no | ||
432 | * signal. | ||
433 | */ | ||
434 | ch7006_write(client, 0x3d, 0x0); | ||
435 | } | ||
436 | |||
437 | void ch7006_state_save(struct i2c_client *client, | ||
438 | struct ch7006_state *state) | ||
439 | { | ||
440 | ch7006_save_reg(client, state, CH7006_POWER); | ||
441 | |||
442 | ch7006_save_reg(client, state, CH7006_DISPMODE); | ||
443 | ch7006_save_reg(client, state, CH7006_FFILTER); | ||
444 | ch7006_save_reg(client, state, CH7006_BWIDTH); | ||
445 | ch7006_save_reg(client, state, CH7006_INPUT_FORMAT); | ||
446 | ch7006_save_reg(client, state, CH7006_CLKMODE); | ||
447 | ch7006_save_reg(client, state, CH7006_START_ACTIVE); | ||
448 | ch7006_save_reg(client, state, CH7006_POV); | ||
449 | ch7006_save_reg(client, state, CH7006_BLACK_LEVEL); | ||
450 | ch7006_save_reg(client, state, CH7006_HPOS); | ||
451 | ch7006_save_reg(client, state, CH7006_VPOS); | ||
452 | ch7006_save_reg(client, state, CH7006_INPUT_SYNC); | ||
453 | ch7006_save_reg(client, state, CH7006_DETECT); | ||
454 | ch7006_save_reg(client, state, CH7006_CONTRAST); | ||
455 | ch7006_save_reg(client, state, CH7006_PLLOV); | ||
456 | ch7006_save_reg(client, state, CH7006_PLLM); | ||
457 | ch7006_save_reg(client, state, CH7006_PLLN); | ||
458 | ch7006_save_reg(client, state, CH7006_BCLKOUT); | ||
459 | ch7006_save_reg(client, state, CH7006_SUBC_INC0); | ||
460 | ch7006_save_reg(client, state, CH7006_SUBC_INC1); | ||
461 | ch7006_save_reg(client, state, CH7006_SUBC_INC2); | ||
462 | ch7006_save_reg(client, state, CH7006_SUBC_INC3); | ||
463 | ch7006_save_reg(client, state, CH7006_SUBC_INC4); | ||
464 | ch7006_save_reg(client, state, CH7006_SUBC_INC5); | ||
465 | ch7006_save_reg(client, state, CH7006_SUBC_INC6); | ||
466 | ch7006_save_reg(client, state, CH7006_SUBC_INC7); | ||
467 | ch7006_save_reg(client, state, CH7006_PLL_CONTROL); | ||
468 | ch7006_save_reg(client, state, CH7006_CALC_SUBC_INC0); | ||
469 | |||
470 | state->regs[CH7006_FFILTER] = (state->regs[CH7006_FFILTER] & 0xf0) | | ||
471 | (state->regs[CH7006_FFILTER] & 0x0c) >> 2 | | ||
472 | (state->regs[CH7006_FFILTER] & 0x03) << 2; | ||
473 | } | ||
diff --git a/drivers/gpu/drm/i2c/ch7006_priv.h b/drivers/gpu/drm/i2c/ch7006_priv.h new file mode 100644 index 000000000000..b06d3d93d8ac --- /dev/null +++ b/drivers/gpu/drm/i2c/ch7006_priv.h | |||
@@ -0,0 +1,344 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009 Francisco Jerez. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #ifndef __DRM_I2C_CH7006_PRIV_H__ | ||
28 | #define __DRM_I2C_CH7006_PRIV_H__ | ||
29 | |||
30 | #include "drmP.h" | ||
31 | #include "drm_crtc_helper.h" | ||
32 | #include "drm_encoder_slave.h" | ||
33 | #include "i2c/ch7006.h" | ||
34 | |||
35 | typedef int64_t fixed; | ||
36 | #define fixed1 (1LL << 32) | ||
37 | |||
38 | enum ch7006_tv_norm { | ||
39 | TV_NORM_PAL, | ||
40 | TV_NORM_PAL_M, | ||
41 | TV_NORM_PAL_N, | ||
42 | TV_NORM_PAL_NC, | ||
43 | TV_NORM_PAL_60, | ||
44 | TV_NORM_NTSC_M, | ||
45 | TV_NORM_NTSC_J, | ||
46 | NUM_TV_NORMS | ||
47 | }; | ||
48 | |||
49 | struct ch7006_tv_norm_info { | ||
50 | fixed vrefresh; | ||
51 | int vdisplay; | ||
52 | int vtotal; | ||
53 | int hvirtual; | ||
54 | |||
55 | fixed subc_freq; | ||
56 | fixed black_level; | ||
57 | |||
58 | uint32_t dispmode; | ||
59 | int voffset; | ||
60 | }; | ||
61 | |||
62 | struct ch7006_mode { | ||
63 | struct drm_display_mode mode; | ||
64 | |||
65 | int enc_hdisp; | ||
66 | int enc_vdisp; | ||
67 | |||
68 | fixed subc_coeff; | ||
69 | uint32_t dispmode; | ||
70 | |||
71 | uint32_t valid_scales; | ||
72 | uint32_t valid_norms; | ||
73 | }; | ||
74 | |||
75 | struct ch7006_state { | ||
76 | uint8_t regs[0x26]; | ||
77 | }; | ||
78 | |||
79 | struct ch7006_priv { | ||
80 | struct ch7006_encoder_params *params; | ||
81 | struct ch7006_mode *mode; | ||
82 | |||
83 | struct ch7006_state state; | ||
84 | struct ch7006_state saved_state; | ||
85 | |||
86 | struct drm_property *scale_property; | ||
87 | |||
88 | int select_subconnector; | ||
89 | int subconnector; | ||
90 | int hmargin; | ||
91 | int vmargin; | ||
92 | enum ch7006_tv_norm norm; | ||
93 | int brightness; | ||
94 | int contrast; | ||
95 | int flicker; | ||
96 | int scale; | ||
97 | |||
98 | int last_dpms; | ||
99 | }; | ||
100 | |||
101 | #define to_ch7006_priv(x) \ | ||
102 | ((struct ch7006_priv *)to_encoder_slave(x)->slave_priv) | ||
103 | |||
104 | extern int ch7006_debug; | ||
105 | extern char *ch7006_tv_norm; | ||
106 | extern int ch7006_scale; | ||
107 | |||
108 | extern char *ch7006_tv_norm_names[]; | ||
109 | extern struct ch7006_tv_norm_info ch7006_tv_norms[]; | ||
110 | extern struct ch7006_mode ch7006_modes[]; | ||
111 | |||
112 | struct ch7006_mode *ch7006_lookup_mode(struct drm_encoder *encoder, | ||
113 | struct drm_display_mode *drm_mode); | ||
114 | |||
115 | void ch7006_setup_levels(struct drm_encoder *encoder); | ||
116 | void ch7006_setup_subcarrier(struct drm_encoder *encoder); | ||
117 | void ch7006_setup_pll(struct drm_encoder *encoder); | ||
118 | void ch7006_setup_power_state(struct drm_encoder *encoder); | ||
119 | void ch7006_setup_properties(struct drm_encoder *encoder); | ||
120 | |||
121 | void ch7006_write(struct i2c_client *client, uint8_t addr, uint8_t val); | ||
122 | uint8_t ch7006_read(struct i2c_client *client, uint8_t addr); | ||
123 | |||
124 | void ch7006_state_load(struct i2c_client *client, | ||
125 | struct ch7006_state *state); | ||
126 | void ch7006_state_save(struct i2c_client *client, | ||
127 | struct ch7006_state *state); | ||
128 | |||
129 | /* Some helper macros */ | ||
130 | |||
131 | #define ch7006_dbg(client, format, ...) do { \ | ||
132 | if (ch7006_debug) \ | ||
133 | dev_printk(KERN_DEBUG, &client->dev, \ | ||
134 | "%s: " format, __func__, ## __VA_ARGS__); \ | ||
135 | } while (0) | ||
136 | #define ch7006_info(client, format, ...) \ | ||
137 | dev_info(&client->dev, format, __VA_ARGS__) | ||
138 | #define ch7006_err(client, format, ...) \ | ||
139 | dev_err(&client->dev, format, __VA_ARGS__) | ||
140 | |||
141 | #define __mask(src, bitfield) \ | ||
142 | (((2 << (1 ? bitfield)) - 1) & ~((1 << (0 ? bitfield)) - 1)) | ||
143 | #define mask(bitfield) __mask(bitfield) | ||
144 | |||
145 | #define __bitf(src, bitfield, x) \ | ||
146 | (((x) >> (src) << (0 ? bitfield)) & __mask(src, bitfield)) | ||
147 | #define bitf(bitfield, x) __bitf(bitfield, x) | ||
148 | #define bitfs(bitfield, s) __bitf(bitfield, bitfield##_##s) | ||
149 | #define setbitf(state, reg, bitfield, x) \ | ||
150 | state->regs[reg] = (state->regs[reg] & ~mask(reg##_##bitfield)) \ | ||
151 | | bitf(reg##_##bitfield, x) | ||
152 | |||
153 | #define __unbitf(src, bitfield, x) \ | ||
154 | ((x & __mask(src, bitfield)) >> (0 ? bitfield) << (src)) | ||
155 | #define unbitf(bitfield, x) __unbitf(bitfield, x) | ||
156 | |||
157 | static inline int interpolate(int y0, int y1, int y2, int x) | ||
158 | { | ||
159 | return y1 + (x < 50 ? y1 - y0 : y2 - y1) * (x - 50) / 50; | ||
160 | } | ||
161 | |||
162 | static inline int32_t round_fixed(fixed x) | ||
163 | { | ||
164 | return (x + fixed1/2) >> 32; | ||
165 | } | ||
166 | |||
167 | #define ch7006_load_reg(client, state, reg) ch7006_write(client, reg, state->regs[reg]) | ||
168 | #define ch7006_save_reg(client, state, reg) state->regs[reg] = ch7006_read(client, reg) | ||
169 | |||
170 | /* Fixed hardware specs */ | ||
171 | |||
172 | #define CH7006_FREQ0 14318 | ||
173 | #define CH7006_MAXN 650 | ||
174 | #define CH7006_MAXM 315 | ||
175 | |||
176 | /* Register definitions */ | ||
177 | |||
178 | #define CH7006_DISPMODE 0x00 | ||
179 | #define CH7006_DISPMODE_INPUT_RES 0, 7:5 | ||
180 | #define CH7006_DISPMODE_INPUT_RES_512x384 0x0 | ||
181 | #define CH7006_DISPMODE_INPUT_RES_720x400 0x1 | ||
182 | #define CH7006_DISPMODE_INPUT_RES_640x400 0x2 | ||
183 | #define CH7006_DISPMODE_INPUT_RES_640x480 0x3 | ||
184 | #define CH7006_DISPMODE_INPUT_RES_800x600 0x4 | ||
185 | #define CH7006_DISPMODE_INPUT_RES_NATIVE 0x5 | ||
186 | #define CH7006_DISPMODE_OUTPUT_STD 0, 4:3 | ||
187 | #define CH7006_DISPMODE_OUTPUT_STD_PAL 0x0 | ||
188 | #define CH7006_DISPMODE_OUTPUT_STD_NTSC 0x1 | ||
189 | #define CH7006_DISPMODE_OUTPUT_STD_PAL_M 0x2 | ||
190 | #define CH7006_DISPMODE_OUTPUT_STD_NTSC_J 0x3 | ||
191 | #define CH7006_DISPMODE_SCALING_RATIO 0, 2:0 | ||
192 | #define CH7006_DISPMODE_SCALING_RATIO_5_4 0x0 | ||
193 | #define CH7006_DISPMODE_SCALING_RATIO_1_1 0x1 | ||
194 | #define CH7006_DISPMODE_SCALING_RATIO_7_8 0x2 | ||
195 | #define CH7006_DISPMODE_SCALING_RATIO_5_6 0x3 | ||
196 | #define CH7006_DISPMODE_SCALING_RATIO_3_4 0x4 | ||
197 | #define CH7006_DISPMODE_SCALING_RATIO_7_10 0x5 | ||
198 | |||
199 | #define CH7006_FFILTER 0x01 | ||
200 | #define CH7006_FFILTER_TEXT 0, 5:4 | ||
201 | #define CH7006_FFILTER_LUMA 0, 3:2 | ||
202 | #define CH7006_FFILTER_CHROMA 0, 1:0 | ||
203 | #define CH7006_FFILTER_CHROMA_NO_DCRAWL 0x3 | ||
204 | |||
205 | #define CH7006_BWIDTH 0x03 | ||
206 | #define CH7006_BWIDTH_5L_FFILER (1 << 7) | ||
207 | #define CH7006_BWIDTH_CVBS_NO_CHROMA (1 << 6) | ||
208 | #define CH7006_BWIDTH_CHROMA 0, 5:4 | ||
209 | #define CH7006_BWIDTH_SVIDEO_YPEAK (1 << 3) | ||
210 | #define CH7006_BWIDTH_SVIDEO_LUMA 0, 2:1 | ||
211 | #define CH7006_BWIDTH_CVBS_LUMA 0, 0:0 | ||
212 | |||
213 | #define CH7006_INPUT_FORMAT 0x04 | ||
214 | #define CH7006_INPUT_FORMAT_DAC_GAIN (1 << 6) | ||
215 | #define CH7006_INPUT_FORMAT_RGB_PASS_THROUGH (1 << 5) | ||
216 | #define CH7006_INPUT_FORMAT_FORMAT 0, 3:0 | ||
217 | #define CH7006_INPUT_FORMAT_FORMAT_RGB16 0x0 | ||
218 | #define CH7006_INPUT_FORMAT_FORMAT_YCrCb24m16 0x1 | ||
219 | #define CH7006_INPUT_FORMAT_FORMAT_RGB24m16 0x2 | ||
220 | #define CH7006_INPUT_FORMAT_FORMAT_RGB15 0x3 | ||
221 | #define CH7006_INPUT_FORMAT_FORMAT_RGB24m12C 0x4 | ||
222 | #define CH7006_INPUT_FORMAT_FORMAT_RGB24m12I 0x5 | ||
223 | #define CH7006_INPUT_FORMAT_FORMAT_RGB24m8 0x6 | ||
224 | #define CH7006_INPUT_FORMAT_FORMAT_RGB16m8 0x7 | ||
225 | #define CH7006_INPUT_FORMAT_FORMAT_RGB15m8 0x8 | ||
226 | #define CH7006_INPUT_FORMAT_FORMAT_YCrCb24m8 0x9 | ||
227 | |||
228 | #define CH7006_CLKMODE 0x06 | ||
229 | #define CH7006_CLKMODE_SUBC_LOCK (1 << 7) | ||
230 | #define CH7006_CLKMODE_MASTER (1 << 6) | ||
231 | #define CH7006_CLKMODE_POS_EDGE (1 << 4) | ||
232 | #define CH7006_CLKMODE_XCM 0, 3:2 | ||
233 | #define CH7006_CLKMODE_PCM 0, 1:0 | ||
234 | |||
235 | #define CH7006_START_ACTIVE 0x07 | ||
236 | #define CH7006_START_ACTIVE_0 0, 7:0 | ||
237 | |||
238 | #define CH7006_POV 0x08 | ||
239 | #define CH7006_POV_START_ACTIVE_8 8, 2:2 | ||
240 | #define CH7006_POV_HPOS_8 8, 1:1 | ||
241 | #define CH7006_POV_VPOS_8 8, 0:0 | ||
242 | |||
243 | #define CH7006_BLACK_LEVEL 0x09 | ||
244 | #define CH7006_BLACK_LEVEL_0 0, 7:0 | ||
245 | |||
246 | #define CH7006_HPOS 0x0a | ||
247 | #define CH7006_HPOS_0 0, 7:0 | ||
248 | |||
249 | #define CH7006_VPOS 0x0b | ||
250 | #define CH7006_VPOS_0 0, 7:0 | ||
251 | |||
252 | #define CH7006_INPUT_SYNC 0x0d | ||
253 | #define CH7006_INPUT_SYNC_EMBEDDED (1 << 3) | ||
254 | #define CH7006_INPUT_SYNC_OUTPUT (1 << 2) | ||
255 | #define CH7006_INPUT_SYNC_PVSYNC (1 << 1) | ||
256 | #define CH7006_INPUT_SYNC_PHSYNC (1 << 0) | ||
257 | |||
258 | #define CH7006_POWER 0x0e | ||
259 | #define CH7006_POWER_SCART (1 << 4) | ||
260 | #define CH7006_POWER_RESET (1 << 3) | ||
261 | #define CH7006_POWER_LEVEL 0, 2:0 | ||
262 | #define CH7006_POWER_LEVEL_CVBS_OFF 0x0 | ||
263 | #define CH7006_POWER_LEVEL_POWER_OFF 0x1 | ||
264 | #define CH7006_POWER_LEVEL_SVIDEO_OFF 0x2 | ||
265 | #define CH7006_POWER_LEVEL_NORMAL 0x3 | ||
266 | #define CH7006_POWER_LEVEL_FULL_POWER_OFF 0x4 | ||
267 | |||
268 | #define CH7006_DETECT 0x10 | ||
269 | #define CH7006_DETECT_SVIDEO_Y_TEST (1 << 3) | ||
270 | #define CH7006_DETECT_SVIDEO_C_TEST (1 << 2) | ||
271 | #define CH7006_DETECT_CVBS_TEST (1 << 1) | ||
272 | #define CH7006_DETECT_SENSE (1 << 0) | ||
273 | |||
274 | #define CH7006_CONTRAST 0x11 | ||
275 | #define CH7006_CONTRAST_0 0, 2:0 | ||
276 | |||
277 | #define CH7006_PLLOV 0x13 | ||
278 | #define CH7006_PLLOV_N_8 8, 2:1 | ||
279 | #define CH7006_PLLOV_M_8 8, 0:0 | ||
280 | |||
281 | #define CH7006_PLLM 0x14 | ||
282 | #define CH7006_PLLM_0 0, 7:0 | ||
283 | |||
284 | #define CH7006_PLLN 0x15 | ||
285 | #define CH7006_PLLN_0 0, 7:0 | ||
286 | |||
287 | #define CH7006_BCLKOUT 0x17 | ||
288 | |||
289 | #define CH7006_SUBC_INC0 0x18 | ||
290 | #define CH7006_SUBC_INC0_28 28, 3:0 | ||
291 | |||
292 | #define CH7006_SUBC_INC1 0x19 | ||
293 | #define CH7006_SUBC_INC1_24 24, 3:0 | ||
294 | |||
295 | #define CH7006_SUBC_INC2 0x1a | ||
296 | #define CH7006_SUBC_INC2_20 20, 3:0 | ||
297 | |||
298 | #define CH7006_SUBC_INC3 0x1b | ||
299 | #define CH7006_SUBC_INC3_GPIO1_VAL (1 << 7) | ||
300 | #define CH7006_SUBC_INC3_GPIO0_VAL (1 << 6) | ||
301 | #define CH7006_SUBC_INC3_POUT_3_3V (1 << 5) | ||
302 | #define CH7006_SUBC_INC3_POUT_INV (1 << 4) | ||
303 | #define CH7006_SUBC_INC3_16 16, 3:0 | ||
304 | |||
305 | #define CH7006_SUBC_INC4 0x1c | ||
306 | #define CH7006_SUBC_INC4_GPIO1_IN (1 << 7) | ||
307 | #define CH7006_SUBC_INC4_GPIO0_IN (1 << 6) | ||
308 | #define CH7006_SUBC_INC4_DS_INPUT (1 << 4) | ||
309 | #define CH7006_SUBC_INC4_12 12, 3:0 | ||
310 | |||
311 | #define CH7006_SUBC_INC5 0x1d | ||
312 | #define CH7006_SUBC_INC5_8 8, 3:0 | ||
313 | |||
314 | #define CH7006_SUBC_INC6 0x1e | ||
315 | #define CH7006_SUBC_INC6_4 4, 3:0 | ||
316 | |||
317 | #define CH7006_SUBC_INC7 0x1f | ||
318 | #define CH7006_SUBC_INC7_0 0, 3:0 | ||
319 | |||
320 | #define CH7006_PLL_CONTROL 0x20 | ||
321 | #define CH7006_PLL_CONTROL_CPI (1 << 5) | ||
322 | #define CH7006_PLL_CONTROL_CAPACITOR (1 << 4) | ||
323 | #define CH7006_PLL_CONTROL_7STAGES (1 << 3) | ||
324 | #define CH7006_PLL_CONTROL_DIGITAL_5V (1 << 2) | ||
325 | #define CH7006_PLL_CONTROL_ANALOG_5V (1 << 1) | ||
326 | #define CH7006_PLL_CONTROL_MEMORY_5V (1 << 0) | ||
327 | |||
328 | #define CH7006_CALC_SUBC_INC0 0x21 | ||
329 | #define CH7006_CALC_SUBC_INC0_24 24, 4:3 | ||
330 | #define CH7006_CALC_SUBC_INC0_HYST 0, 2:1 | ||
331 | #define CH7006_CALC_SUBC_INC0_AUTO (1 << 0) | ||
332 | |||
333 | #define CH7006_CALC_SUBC_INC1 0x22 | ||
334 | #define CH7006_CALC_SUBC_INC1_16 16, 7:0 | ||
335 | |||
336 | #define CH7006_CALC_SUBC_INC2 0x23 | ||
337 | #define CH7006_CALC_SUBC_INC2_8 8, 7:0 | ||
338 | |||
339 | #define CH7006_CALC_SUBC_INC3 0x24 | ||
340 | #define CH7006_CALC_SUBC_INC3_0 0, 7:0 | ||
341 | |||
342 | #define CH7006_VERSION_ID 0x25 | ||
343 | |||
344 | #endif | ||
diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig new file mode 100644 index 000000000000..d823e6319516 --- /dev/null +++ b/drivers/gpu/drm/nouveau/Kconfig | |||
@@ -0,0 +1,44 @@ | |||
1 | config DRM_NOUVEAU | ||
2 | tristate "Nouveau (nVidia) cards" | ||
3 | depends on DRM | ||
4 | select FW_LOADER | ||
5 | select DRM_KMS_HELPER | ||
6 | select DRM_TTM | ||
7 | select FB_CFB_FILLRECT | ||
8 | select FB_CFB_COPYAREA | ||
9 | select FB_CFB_IMAGEBLIT | ||
10 | select FB | ||
11 | select FRAMEBUFFER_CONSOLE if !EMBEDDED | ||
12 | select FB_BACKLIGHT if DRM_NOUVEAU_BACKLIGHT | ||
13 | help | ||
14 | Choose this option for open-source nVidia support. | ||
15 | |||
16 | config DRM_NOUVEAU_BACKLIGHT | ||
17 | bool "Support for backlight control" | ||
18 | depends on DRM_NOUVEAU | ||
19 | default y | ||
20 | help | ||
21 | Say Y here if you want to control the backlight of your display | ||
22 | (e.g. a laptop panel). | ||
23 | |||
24 | config DRM_NOUVEAU_DEBUG | ||
25 | bool "Build in Nouveau's debugfs support" | ||
26 | depends on DRM_NOUVEAU && DEBUG_FS | ||
27 | default y | ||
28 | help | ||
29 | Say Y here if you want Nouveau to output debugging information | ||
30 | via debugfs. | ||
31 | |||
32 | menu "I2C encoder or helper chips" | ||
33 | depends on DRM | ||
34 | |||
35 | config DRM_I2C_CH7006 | ||
36 | tristate "Chrontel ch7006 TV encoder" | ||
37 | default m if DRM_NOUVEAU | ||
38 | help | ||
39 | Support for Chrontel ch7006 and similar TV encoders, found | ||
40 | on some nVidia video cards. | ||
41 | |||
42 | This driver is currently only useful if you're also using | ||
43 | the nouveau driver. | ||
44 | endmenu | ||
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile new file mode 100644 index 000000000000..1d90d4d0144f --- /dev/null +++ b/drivers/gpu/drm/nouveau/Makefile | |||
@@ -0,0 +1,31 @@ | |||
1 | # | ||
2 | # Makefile for the drm device driver. This driver provides support for the | ||
3 | # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. | ||
4 | |||
5 | ccflags-y := -Iinclude/drm | ||
6 | nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \ | ||
7 | nouveau_object.o nouveau_irq.o nouveau_notifier.o \ | ||
8 | nouveau_sgdma.o nouveau_dma.o \ | ||
9 | nouveau_bo.o nouveau_fence.o nouveau_gem.o nouveau_ttm.o \ | ||
10 | nouveau_hw.o nouveau_calc.o nouveau_bios.o nouveau_i2c.o \ | ||
11 | nouveau_display.o nouveau_connector.o nouveau_fbcon.o \ | ||
12 | nouveau_dp.o \ | ||
13 | nv04_timer.o \ | ||
14 | nv04_mc.o nv40_mc.o nv50_mc.o \ | ||
15 | nv04_fb.o nv10_fb.o nv40_fb.o \ | ||
16 | nv04_fifo.o nv10_fifo.o nv40_fifo.o nv50_fifo.o \ | ||
17 | nv04_graph.o nv10_graph.o nv20_graph.o \ | ||
18 | nv40_graph.o nv50_graph.o \ | ||
19 | nv04_instmem.o nv50_instmem.o \ | ||
20 | nv50_crtc.o nv50_dac.o nv50_sor.o \ | ||
21 | nv50_cursor.o nv50_display.o nv50_fbcon.o \ | ||
22 | nv04_dac.o nv04_dfp.o nv04_tv.o nv17_tv.o nv17_tv_modes.o \ | ||
23 | nv04_crtc.o nv04_display.o nv04_cursor.o nv04_fbcon.o \ | ||
24 | nv17_gpio.o | ||
25 | |||
26 | nouveau-$(CONFIG_DRM_NOUVEAU_DEBUG) += nouveau_debugfs.o | ||
27 | nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o | ||
28 | nouveau-$(CONFIG_DRM_NOUVEAU_BACKLIGHT) += nouveau_backlight.o | ||
29 | nouveau-$(CONFIG_ACPI) += nouveau_acpi.o | ||
30 | |||
31 | obj-$(CONFIG_DRM_NOUVEAU)+= nouveau.o | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c new file mode 100644 index 000000000000..1cf488247a16 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c | |||
@@ -0,0 +1,125 @@ | |||
1 | #include <linux/pci.h> | ||
2 | #include <linux/acpi.h> | ||
3 | #include <acpi/acpi_drivers.h> | ||
4 | #include <acpi/acpi_bus.h> | ||
5 | |||
6 | #include "drmP.h" | ||
7 | #include "drm.h" | ||
8 | #include "drm_sarea.h" | ||
9 | #include "drm_crtc_helper.h" | ||
10 | #include "nouveau_drv.h" | ||
11 | #include "nouveau_drm.h" | ||
12 | #include "nv50_display.h" | ||
13 | |||
14 | #define NOUVEAU_DSM_SUPPORTED 0x00 | ||
15 | #define NOUVEAU_DSM_SUPPORTED_FUNCTIONS 0x00 | ||
16 | |||
17 | #define NOUVEAU_DSM_ACTIVE 0x01 | ||
18 | #define NOUVEAU_DSM_ACTIVE_QUERY 0x00 | ||
19 | |||
20 | #define NOUVEAU_DSM_LED 0x02 | ||
21 | #define NOUVEAU_DSM_LED_STATE 0x00 | ||
22 | #define NOUVEAU_DSM_LED_OFF 0x10 | ||
23 | #define NOUVEAU_DSM_LED_STAMINA 0x11 | ||
24 | #define NOUVEAU_DSM_LED_SPEED 0x12 | ||
25 | |||
26 | #define NOUVEAU_DSM_POWER 0x03 | ||
27 | #define NOUVEAU_DSM_POWER_STATE 0x00 | ||
28 | #define NOUVEAU_DSM_POWER_SPEED 0x01 | ||
29 | #define NOUVEAU_DSM_POWER_STAMINA 0x02 | ||
30 | |||
31 | static int nouveau_dsm(struct drm_device *dev, int func, int arg, int *result) | ||
32 | { | ||
33 | static char muid[] = { | ||
34 | 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, | ||
35 | 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4, | ||
36 | }; | ||
37 | |||
38 | struct pci_dev *pdev = dev->pdev; | ||
39 | struct acpi_handle *handle; | ||
40 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
41 | struct acpi_object_list input; | ||
42 | union acpi_object params[4]; | ||
43 | union acpi_object *obj; | ||
44 | int err; | ||
45 | |||
46 | handle = DEVICE_ACPI_HANDLE(&pdev->dev); | ||
47 | |||
48 | if (!handle) | ||
49 | return -ENODEV; | ||
50 | |||
51 | input.count = 4; | ||
52 | input.pointer = params; | ||
53 | params[0].type = ACPI_TYPE_BUFFER; | ||
54 | params[0].buffer.length = sizeof(muid); | ||
55 | params[0].buffer.pointer = (char *)muid; | ||
56 | params[1].type = ACPI_TYPE_INTEGER; | ||
57 | params[1].integer.value = 0x00000102; | ||
58 | params[2].type = ACPI_TYPE_INTEGER; | ||
59 | params[2].integer.value = func; | ||
60 | params[3].type = ACPI_TYPE_INTEGER; | ||
61 | params[3].integer.value = arg; | ||
62 | |||
63 | err = acpi_evaluate_object(handle, "_DSM", &input, &output); | ||
64 | if (err) { | ||
65 | NV_INFO(dev, "failed to evaluate _DSM: %d\n", err); | ||
66 | return err; | ||
67 | } | ||
68 | |||
69 | obj = (union acpi_object *)output.pointer; | ||
70 | |||
71 | if (obj->type == ACPI_TYPE_INTEGER) | ||
72 | if (obj->integer.value == 0x80000002) | ||
73 | return -ENODEV; | ||
74 | |||
75 | if (obj->type == ACPI_TYPE_BUFFER) { | ||
76 | if (obj->buffer.length == 4 && result) { | ||
77 | *result = 0; | ||
78 | *result |= obj->buffer.pointer[0]; | ||
79 | *result |= (obj->buffer.pointer[1] << 8); | ||
80 | *result |= (obj->buffer.pointer[2] << 16); | ||
81 | *result |= (obj->buffer.pointer[3] << 24); | ||
82 | } | ||
83 | } | ||
84 | |||
85 | kfree(output.pointer); | ||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | int nouveau_hybrid_setup(struct drm_device *dev) | ||
90 | { | ||
91 | int result; | ||
92 | |||
93 | if (nouveau_dsm(dev, NOUVEAU_DSM_ACTIVE, NOUVEAU_DSM_ACTIVE_QUERY, | ||
94 | &result)) | ||
95 | return -ENODEV; | ||
96 | |||
97 | NV_INFO(dev, "_DSM hardware status gave 0x%x\n", result); | ||
98 | |||
99 | if (result & 0x1) { /* Stamina mode - disable the external GPU */ | ||
100 | nouveau_dsm(dev, NOUVEAU_DSM_LED, NOUVEAU_DSM_LED_STAMINA, | ||
101 | NULL); | ||
102 | nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_STAMINA, | ||
103 | NULL); | ||
104 | } else { /* Ensure that the external GPU is enabled */ | ||
105 | nouveau_dsm(dev, NOUVEAU_DSM_LED, NOUVEAU_DSM_LED_SPEED, NULL); | ||
106 | nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_SPEED, | ||
107 | NULL); | ||
108 | } | ||
109 | |||
110 | return 0; | ||
111 | } | ||
112 | |||
113 | bool nouveau_dsm_probe(struct drm_device *dev) | ||
114 | { | ||
115 | int support = 0; | ||
116 | |||
117 | if (nouveau_dsm(dev, NOUVEAU_DSM_SUPPORTED, | ||
118 | NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &support)) | ||
119 | return false; | ||
120 | |||
121 | if (!support) | ||
122 | return false; | ||
123 | |||
124 | return true; | ||
125 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_backlight.c b/drivers/gpu/drm/nouveau/nouveau_backlight.c new file mode 100644 index 000000000000..20564f8cb0ec --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_backlight.c | |||
@@ -0,0 +1,155 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009 Red Hat <mjg@redhat.com> | ||
3 | * | ||
4 | * Permission is hereby granted, free of charge, to any person obtaining | ||
5 | * a copy of this software and associated documentation files (the | ||
6 | * "Software"), to deal in the Software without restriction, including | ||
7 | * without limitation the rights to use, copy, modify, merge, publish, | ||
8 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
9 | * permit persons to whom the Software is furnished to do so, subject to | ||
10 | * the following conditions: | ||
11 | * | ||
12 | * The above copyright notice and this permission notice (including the | ||
13 | * next paragraph) shall be included in all copies or substantial | ||
14 | * portions of the Software. | ||
15 | * | ||
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
17 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
19 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
20 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
21 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
22 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
23 | * | ||
24 | */ | ||
25 | |||
26 | /* | ||
27 | * Authors: | ||
28 | * Matthew Garrett <mjg@redhat.com> | ||
29 | * | ||
30 | * Register locations derived from NVClock by Roderick Colenbrander | ||
31 | */ | ||
32 | |||
33 | #include <linux/backlight.h> | ||
34 | |||
35 | #include "drmP.h" | ||
36 | #include "nouveau_drv.h" | ||
37 | #include "nouveau_drm.h" | ||
38 | #include "nouveau_reg.h" | ||
39 | |||
40 | static int nv40_get_intensity(struct backlight_device *bd) | ||
41 | { | ||
42 | struct drm_device *dev = bl_get_data(bd); | ||
43 | int val = (nv_rd32(dev, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK) | ||
44 | >> 16; | ||
45 | |||
46 | return val; | ||
47 | } | ||
48 | |||
49 | static int nv40_set_intensity(struct backlight_device *bd) | ||
50 | { | ||
51 | struct drm_device *dev = bl_get_data(bd); | ||
52 | int val = bd->props.brightness; | ||
53 | int reg = nv_rd32(dev, NV40_PMC_BACKLIGHT); | ||
54 | |||
55 | nv_wr32(dev, NV40_PMC_BACKLIGHT, | ||
56 | (val << 16) | (reg & ~NV40_PMC_BACKLIGHT_MASK)); | ||
57 | |||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | static struct backlight_ops nv40_bl_ops = { | ||
62 | .options = BL_CORE_SUSPENDRESUME, | ||
63 | .get_brightness = nv40_get_intensity, | ||
64 | .update_status = nv40_set_intensity, | ||
65 | }; | ||
66 | |||
67 | static int nv50_get_intensity(struct backlight_device *bd) | ||
68 | { | ||
69 | struct drm_device *dev = bl_get_data(bd); | ||
70 | |||
71 | return nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT); | ||
72 | } | ||
73 | |||
74 | static int nv50_set_intensity(struct backlight_device *bd) | ||
75 | { | ||
76 | struct drm_device *dev = bl_get_data(bd); | ||
77 | int val = bd->props.brightness; | ||
78 | |||
79 | nv_wr32(dev, NV50_PDISPLAY_SOR_BACKLIGHT, | ||
80 | val | NV50_PDISPLAY_SOR_BACKLIGHT_ENABLE); | ||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | static struct backlight_ops nv50_bl_ops = { | ||
85 | .options = BL_CORE_SUSPENDRESUME, | ||
86 | .get_brightness = nv50_get_intensity, | ||
87 | .update_status = nv50_set_intensity, | ||
88 | }; | ||
89 | |||
90 | static int nouveau_nv40_backlight_init(struct drm_device *dev) | ||
91 | { | ||
92 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
93 | struct backlight_device *bd; | ||
94 | |||
95 | if (!(nv_rd32(dev, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK)) | ||
96 | return 0; | ||
97 | |||
98 | bd = backlight_device_register("nv_backlight", &dev->pdev->dev, dev, | ||
99 | &nv40_bl_ops); | ||
100 | if (IS_ERR(bd)) | ||
101 | return PTR_ERR(bd); | ||
102 | |||
103 | dev_priv->backlight = bd; | ||
104 | bd->props.max_brightness = 31; | ||
105 | bd->props.brightness = nv40_get_intensity(bd); | ||
106 | backlight_update_status(bd); | ||
107 | |||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | static int nouveau_nv50_backlight_init(struct drm_device *dev) | ||
112 | { | ||
113 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
114 | struct backlight_device *bd; | ||
115 | |||
116 | if (!nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT)) | ||
117 | return 0; | ||
118 | |||
119 | bd = backlight_device_register("nv_backlight", &dev->pdev->dev, dev, | ||
120 | &nv50_bl_ops); | ||
121 | if (IS_ERR(bd)) | ||
122 | return PTR_ERR(bd); | ||
123 | |||
124 | dev_priv->backlight = bd; | ||
125 | bd->props.max_brightness = 1025; | ||
126 | bd->props.brightness = nv50_get_intensity(bd); | ||
127 | backlight_update_status(bd); | ||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | int nouveau_backlight_init(struct drm_device *dev) | ||
132 | { | ||
133 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
134 | |||
135 | switch (dev_priv->card_type) { | ||
136 | case NV_40: | ||
137 | return nouveau_nv40_backlight_init(dev); | ||
138 | case NV_50: | ||
139 | return nouveau_nv50_backlight_init(dev); | ||
140 | default: | ||
141 | break; | ||
142 | } | ||
143 | |||
144 | return 0; | ||
145 | } | ||
146 | |||
147 | void nouveau_backlight_exit(struct drm_device *dev) | ||
148 | { | ||
149 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
150 | |||
151 | if (dev_priv->backlight) { | ||
152 | backlight_device_unregister(dev_priv->backlight); | ||
153 | dev_priv->backlight = NULL; | ||
154 | } | ||
155 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c new file mode 100644 index 000000000000..5eec5ed69489 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c | |||
@@ -0,0 +1,6095 @@ | |||
1 | /* | ||
2 | * Copyright 2005-2006 Erik Waling | ||
3 | * Copyright 2006 Stephane Marchesin | ||
4 | * Copyright 2007-2009 Stuart Bennett | ||
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 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||
20 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF | ||
21 | * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
22 | * SOFTWARE. | ||
23 | */ | ||
24 | |||
25 | #include "drmP.h" | ||
26 | #define NV_DEBUG_NOTRACE | ||
27 | #include "nouveau_drv.h" | ||
28 | #include "nouveau_hw.h" | ||
29 | |||
30 | /* these defines are made up */ | ||
31 | #define NV_CIO_CRE_44_HEADA 0x0 | ||
32 | #define NV_CIO_CRE_44_HEADB 0x3 | ||
33 | #define FEATURE_MOBILE 0x10 /* also FEATURE_QUADRO for BMP */ | ||
34 | #define LEGACY_I2C_CRT 0x80 | ||
35 | #define LEGACY_I2C_PANEL 0x81 | ||
36 | #define LEGACY_I2C_TV 0x82 | ||
37 | |||
38 | #define EDID1_LEN 128 | ||
39 | |||
40 | #define BIOSLOG(sip, fmt, arg...) NV_DEBUG(sip->dev, fmt, ##arg) | ||
41 | #define LOG_OLD_VALUE(x) | ||
42 | |||
43 | #define ROM16(x) le16_to_cpu(*(uint16_t *)&(x)) | ||
44 | #define ROM32(x) le32_to_cpu(*(uint32_t *)&(x)) | ||
45 | |||
46 | struct init_exec { | ||
47 | bool execute; | ||
48 | bool repeat; | ||
49 | }; | ||
50 | |||
51 | static bool nv_cksum(const uint8_t *data, unsigned int length) | ||
52 | { | ||
53 | /* | ||
54 | * There's a few checksums in the BIOS, so here's a generic checking | ||
55 | * function. | ||
56 | */ | ||
57 | int i; | ||
58 | uint8_t sum = 0; | ||
59 | |||
60 | for (i = 0; i < length; i++) | ||
61 | sum += data[i]; | ||
62 | |||
63 | if (sum) | ||
64 | return true; | ||
65 | |||
66 | return false; | ||
67 | } | ||
68 | |||
69 | static int | ||
70 | score_vbios(struct drm_device *dev, const uint8_t *data, const bool writeable) | ||
71 | { | ||
72 | if (!(data[0] == 0x55 && data[1] == 0xAA)) { | ||
73 | NV_TRACEWARN(dev, "... BIOS signature not found\n"); | ||
74 | return 0; | ||
75 | } | ||
76 | |||
77 | if (nv_cksum(data, data[2] * 512)) { | ||
78 | NV_TRACEWARN(dev, "... BIOS checksum invalid\n"); | ||
79 | /* if a ro image is somewhat bad, it's probably all rubbish */ | ||
80 | return writeable ? 2 : 1; | ||
81 | } else | ||
82 | NV_TRACE(dev, "... appears to be valid\n"); | ||
83 | |||
84 | return 3; | ||
85 | } | ||
86 | |||
87 | static void load_vbios_prom(struct drm_device *dev, uint8_t *data) | ||
88 | { | ||
89 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
90 | uint32_t pci_nv_20, save_pci_nv_20; | ||
91 | int pcir_ptr; | ||
92 | int i; | ||
93 | |||
94 | if (dev_priv->card_type >= NV_50) | ||
95 | pci_nv_20 = 0x88050; | ||
96 | else | ||
97 | pci_nv_20 = NV_PBUS_PCI_NV_20; | ||
98 | |||
99 | /* enable ROM access */ | ||
100 | save_pci_nv_20 = nvReadMC(dev, pci_nv_20); | ||
101 | nvWriteMC(dev, pci_nv_20, | ||
102 | save_pci_nv_20 & ~NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED); | ||
103 | |||
104 | /* bail if no rom signature */ | ||
105 | if (nv_rd08(dev, NV_PROM_OFFSET) != 0x55 || | ||
106 | nv_rd08(dev, NV_PROM_OFFSET + 1) != 0xaa) | ||
107 | goto out; | ||
108 | |||
109 | /* additional check (see note below) - read PCI record header */ | ||
110 | pcir_ptr = nv_rd08(dev, NV_PROM_OFFSET + 0x18) | | ||
111 | nv_rd08(dev, NV_PROM_OFFSET + 0x19) << 8; | ||
112 | if (nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr) != 'P' || | ||
113 | nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr + 1) != 'C' || | ||
114 | nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr + 2) != 'I' || | ||
115 | nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr + 3) != 'R') | ||
116 | goto out; | ||
117 | |||
118 | /* on some 6600GT/6800LE prom reads are messed up. nvclock alleges a | ||
119 | * a good read may be obtained by waiting or re-reading (cargocult: 5x) | ||
120 | * each byte. we'll hope pramin has something usable instead | ||
121 | */ | ||
122 | for (i = 0; i < NV_PROM_SIZE; i++) | ||
123 | data[i] = nv_rd08(dev, NV_PROM_OFFSET + i); | ||
124 | |||
125 | out: | ||
126 | /* disable ROM access */ | ||
127 | nvWriteMC(dev, pci_nv_20, | ||
128 | save_pci_nv_20 | NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED); | ||
129 | } | ||
130 | |||
131 | static void load_vbios_pramin(struct drm_device *dev, uint8_t *data) | ||
132 | { | ||
133 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
134 | uint32_t old_bar0_pramin = 0; | ||
135 | int i; | ||
136 | |||
137 | if (dev_priv->card_type >= NV_50) { | ||
138 | uint32_t vbios_vram = (nv_rd32(dev, 0x619f04) & ~0xff) << 8; | ||
139 | |||
140 | if (!vbios_vram) | ||
141 | vbios_vram = (nv_rd32(dev, 0x1700) << 16) + 0xf0000; | ||
142 | |||
143 | old_bar0_pramin = nv_rd32(dev, 0x1700); | ||
144 | nv_wr32(dev, 0x1700, vbios_vram >> 16); | ||
145 | } | ||
146 | |||
147 | /* bail if no rom signature */ | ||
148 | if (nv_rd08(dev, NV_PRAMIN_OFFSET) != 0x55 || | ||
149 | nv_rd08(dev, NV_PRAMIN_OFFSET + 1) != 0xaa) | ||
150 | goto out; | ||
151 | |||
152 | for (i = 0; i < NV_PROM_SIZE; i++) | ||
153 | data[i] = nv_rd08(dev, NV_PRAMIN_OFFSET + i); | ||
154 | |||
155 | out: | ||
156 | if (dev_priv->card_type >= NV_50) | ||
157 | nv_wr32(dev, 0x1700, old_bar0_pramin); | ||
158 | } | ||
159 | |||
160 | static void load_vbios_pci(struct drm_device *dev, uint8_t *data) | ||
161 | { | ||
162 | void __iomem *rom = NULL; | ||
163 | size_t rom_len; | ||
164 | int ret; | ||
165 | |||
166 | ret = pci_enable_rom(dev->pdev); | ||
167 | if (ret) | ||
168 | return; | ||
169 | |||
170 | rom = pci_map_rom(dev->pdev, &rom_len); | ||
171 | if (!rom) | ||
172 | goto out; | ||
173 | memcpy_fromio(data, rom, rom_len); | ||
174 | pci_unmap_rom(dev->pdev, rom); | ||
175 | |||
176 | out: | ||
177 | pci_disable_rom(dev->pdev); | ||
178 | } | ||
179 | |||
180 | struct methods { | ||
181 | const char desc[8]; | ||
182 | void (*loadbios)(struct drm_device *, uint8_t *); | ||
183 | const bool rw; | ||
184 | int score; | ||
185 | }; | ||
186 | |||
187 | static struct methods nv04_methods[] = { | ||
188 | { "PROM", load_vbios_prom, false }, | ||
189 | { "PRAMIN", load_vbios_pramin, true }, | ||
190 | { "PCIROM", load_vbios_pci, true }, | ||
191 | { } | ||
192 | }; | ||
193 | |||
194 | static struct methods nv50_methods[] = { | ||
195 | { "PRAMIN", load_vbios_pramin, true }, | ||
196 | { "PROM", load_vbios_prom, false }, | ||
197 | { "PCIROM", load_vbios_pci, true }, | ||
198 | { } | ||
199 | }; | ||
200 | |||
201 | static bool NVShadowVBIOS(struct drm_device *dev, uint8_t *data) | ||
202 | { | ||
203 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
204 | struct methods *methods, *method; | ||
205 | int testscore = 3; | ||
206 | |||
207 | if (nouveau_vbios) { | ||
208 | method = nv04_methods; | ||
209 | while (method->loadbios) { | ||
210 | if (!strcasecmp(nouveau_vbios, method->desc)) | ||
211 | break; | ||
212 | method++; | ||
213 | } | ||
214 | |||
215 | if (method->loadbios) { | ||
216 | NV_INFO(dev, "Attempting to use BIOS image from %s\n", | ||
217 | method->desc); | ||
218 | |||
219 | method->loadbios(dev, data); | ||
220 | if (score_vbios(dev, data, method->rw)) | ||
221 | return true; | ||
222 | } | ||
223 | |||
224 | NV_ERROR(dev, "VBIOS source \'%s\' invalid\n", nouveau_vbios); | ||
225 | } | ||
226 | |||
227 | if (dev_priv->card_type < NV_50) | ||
228 | methods = nv04_methods; | ||
229 | else | ||
230 | methods = nv50_methods; | ||
231 | |||
232 | method = methods; | ||
233 | while (method->loadbios) { | ||
234 | NV_TRACE(dev, "Attempting to load BIOS image from %s\n", | ||
235 | method->desc); | ||
236 | data[0] = data[1] = 0; /* avoid reuse of previous image */ | ||
237 | method->loadbios(dev, data); | ||
238 | method->score = score_vbios(dev, data, method->rw); | ||
239 | if (method->score == testscore) | ||
240 | return true; | ||
241 | method++; | ||
242 | } | ||
243 | |||
244 | while (--testscore > 0) { | ||
245 | method = methods; | ||
246 | while (method->loadbios) { | ||
247 | if (method->score == testscore) { | ||
248 | NV_TRACE(dev, "Using BIOS image from %s\n", | ||
249 | method->desc); | ||
250 | method->loadbios(dev, data); | ||
251 | return true; | ||
252 | } | ||
253 | method++; | ||
254 | } | ||
255 | } | ||
256 | |||
257 | NV_ERROR(dev, "No valid BIOS image found\n"); | ||
258 | return false; | ||
259 | } | ||
260 | |||
261 | struct init_tbl_entry { | ||
262 | char *name; | ||
263 | uint8_t id; | ||
264 | int length; | ||
265 | int length_offset; | ||
266 | int length_multiplier; | ||
267 | bool (*handler)(struct nvbios *, uint16_t, struct init_exec *); | ||
268 | }; | ||
269 | |||
270 | struct bit_entry { | ||
271 | uint8_t id[2]; | ||
272 | uint16_t length; | ||
273 | uint16_t offset; | ||
274 | }; | ||
275 | |||
276 | static int parse_init_table(struct nvbios *, unsigned int, struct init_exec *); | ||
277 | |||
278 | #define MACRO_INDEX_SIZE 2 | ||
279 | #define MACRO_SIZE 8 | ||
280 | #define CONDITION_SIZE 12 | ||
281 | #define IO_FLAG_CONDITION_SIZE 9 | ||
282 | #define IO_CONDITION_SIZE 5 | ||
283 | #define MEM_INIT_SIZE 66 | ||
284 | |||
285 | static void still_alive(void) | ||
286 | { | ||
287 | #if 0 | ||
288 | sync(); | ||
289 | msleep(2); | ||
290 | #endif | ||
291 | } | ||
292 | |||
293 | static uint32_t | ||
294 | munge_reg(struct nvbios *bios, uint32_t reg) | ||
295 | { | ||
296 | struct drm_nouveau_private *dev_priv = bios->dev->dev_private; | ||
297 | struct dcb_entry *dcbent = bios->display.output; | ||
298 | |||
299 | if (dev_priv->card_type < NV_50) | ||
300 | return reg; | ||
301 | |||
302 | if (reg & 0x40000000) { | ||
303 | BUG_ON(!dcbent); | ||
304 | |||
305 | reg += (ffs(dcbent->or) - 1) * 0x800; | ||
306 | if ((reg & 0x20000000) && !(dcbent->sorconf.link & 1)) | ||
307 | reg += 0x00000080; | ||
308 | } | ||
309 | |||
310 | reg &= ~0x60000000; | ||
311 | return reg; | ||
312 | } | ||
313 | |||
314 | static int | ||
315 | valid_reg(struct nvbios *bios, uint32_t reg) | ||
316 | { | ||
317 | struct drm_nouveau_private *dev_priv = bios->dev->dev_private; | ||
318 | struct drm_device *dev = bios->dev; | ||
319 | |||
320 | /* C51 has misaligned regs on purpose. Marvellous */ | ||
321 | if (reg & 0x2 || (reg & 0x1 && dev_priv->VBIOS.pub.chip_version != 0x51)) { | ||
322 | NV_ERROR(dev, "========== misaligned reg 0x%08X ==========\n", | ||
323 | reg); | ||
324 | return 0; | ||
325 | } | ||
326 | /* | ||
327 | * Warn on C51 regs that have not been verified accessible in | ||
328 | * mmiotracing | ||
329 | */ | ||
330 | if (reg & 0x1 && dev_priv->VBIOS.pub.chip_version == 0x51 && | ||
331 | reg != 0x130d && reg != 0x1311 && reg != 0x60081d) | ||
332 | NV_WARN(dev, "=== C51 misaligned reg 0x%08X not verified ===\n", | ||
333 | reg); | ||
334 | |||
335 | /* Trust the init scripts on G80 */ | ||
336 | if (dev_priv->card_type >= NV_50) | ||
337 | return 1; | ||
338 | |||
339 | #define WITHIN(x, y, z) ((x >= y) && (x < y + z)) | ||
340 | if (WITHIN(reg, NV_PMC_OFFSET, NV_PMC_SIZE)) | ||
341 | return 1; | ||
342 | if (WITHIN(reg, NV_PBUS_OFFSET, NV_PBUS_SIZE)) | ||
343 | return 1; | ||
344 | if (WITHIN(reg, NV_PFIFO_OFFSET, NV_PFIFO_SIZE)) | ||
345 | return 1; | ||
346 | if (dev_priv->VBIOS.pub.chip_version >= 0x30 && | ||
347 | (WITHIN(reg, 0x4000, 0x600) || reg == 0x00004600)) | ||
348 | return 1; | ||
349 | if (dev_priv->VBIOS.pub.chip_version >= 0x40 && | ||
350 | WITHIN(reg, 0xc000, 0x48)) | ||
351 | return 1; | ||
352 | if (dev_priv->VBIOS.pub.chip_version >= 0x17 && reg == 0x0000d204) | ||
353 | return 1; | ||
354 | if (dev_priv->VBIOS.pub.chip_version >= 0x40) { | ||
355 | if (reg == 0x00011014 || reg == 0x00020328) | ||
356 | return 1; | ||
357 | if (WITHIN(reg, 0x88000, NV_PBUS_SIZE)) /* new PBUS */ | ||
358 | return 1; | ||
359 | } | ||
360 | if (WITHIN(reg, NV_PFB_OFFSET, NV_PFB_SIZE)) | ||
361 | return 1; | ||
362 | if (WITHIN(reg, NV_PEXTDEV_OFFSET, NV_PEXTDEV_SIZE)) | ||
363 | return 1; | ||
364 | if (WITHIN(reg, NV_PCRTC0_OFFSET, NV_PCRTC0_SIZE * 2)) | ||
365 | return 1; | ||
366 | if (WITHIN(reg, NV_PRAMDAC0_OFFSET, NV_PRAMDAC0_SIZE * 2)) | ||
367 | return 1; | ||
368 | if (dev_priv->VBIOS.pub.chip_version >= 0x17 && reg == 0x0070fff0) | ||
369 | return 1; | ||
370 | if (dev_priv->VBIOS.pub.chip_version == 0x51 && | ||
371 | WITHIN(reg, NV_PRAMIN_OFFSET, NV_PRAMIN_SIZE)) | ||
372 | return 1; | ||
373 | #undef WITHIN | ||
374 | |||
375 | NV_ERROR(dev, "========== unknown reg 0x%08X ==========\n", reg); | ||
376 | |||
377 | return 0; | ||
378 | } | ||
379 | |||
380 | static bool | ||
381 | valid_idx_port(struct nvbios *bios, uint16_t port) | ||
382 | { | ||
383 | struct drm_nouveau_private *dev_priv = bios->dev->dev_private; | ||
384 | struct drm_device *dev = bios->dev; | ||
385 | |||
386 | /* | ||
387 | * If adding more ports here, the read/write functions below will need | ||
388 | * updating so that the correct mmio range (PRMCIO, PRMDIO, PRMVIO) is | ||
389 | * used for the port in question | ||
390 | */ | ||
391 | if (dev_priv->card_type < NV_50) { | ||
392 | if (port == NV_CIO_CRX__COLOR) | ||
393 | return true; | ||
394 | if (port == NV_VIO_SRX) | ||
395 | return true; | ||
396 | } else { | ||
397 | if (port == NV_CIO_CRX__COLOR) | ||
398 | return true; | ||
399 | } | ||
400 | |||
401 | NV_ERROR(dev, "========== unknown indexed io port 0x%04X ==========\n", | ||
402 | port); | ||
403 | |||
404 | return false; | ||
405 | } | ||
406 | |||
407 | static bool | ||
408 | valid_port(struct nvbios *bios, uint16_t port) | ||
409 | { | ||
410 | struct drm_device *dev = bios->dev; | ||
411 | |||
412 | /* | ||
413 | * If adding more ports here, the read/write functions below will need | ||
414 | * updating so that the correct mmio range (PRMCIO, PRMDIO, PRMVIO) is | ||
415 | * used for the port in question | ||
416 | */ | ||
417 | if (port == NV_VIO_VSE2) | ||
418 | return true; | ||
419 | |||
420 | NV_ERROR(dev, "========== unknown io port 0x%04X ==========\n", port); | ||
421 | |||
422 | return false; | ||
423 | } | ||
424 | |||
425 | static uint32_t | ||
426 | bios_rd32(struct nvbios *bios, uint32_t reg) | ||
427 | { | ||
428 | uint32_t data; | ||
429 | |||
430 | reg = munge_reg(bios, reg); | ||
431 | if (!valid_reg(bios, reg)) | ||
432 | return 0; | ||
433 | |||
434 | /* | ||
435 | * C51 sometimes uses regs with bit0 set in the address. For these | ||
436 | * cases there should exist a translation in a BIOS table to an IO | ||
437 | * port address which the BIOS uses for accessing the reg | ||
438 | * | ||
439 | * These only seem to appear for the power control regs to a flat panel, | ||
440 | * and the GPIO regs at 0x60081*. In C51 mmio traces the normal regs | ||
441 | * for 0x1308 and 0x1310 are used - hence the mask below. An S3 | ||
442 | * suspend-resume mmio trace from a C51 will be required to see if this | ||
443 | * is true for the power microcode in 0x14.., or whether the direct IO | ||
444 | * port access method is needed | ||
445 | */ | ||
446 | if (reg & 0x1) | ||
447 | reg &= ~0x1; | ||
448 | |||
449 | data = nv_rd32(bios->dev, reg); | ||
450 | |||
451 | BIOSLOG(bios, " Read: Reg: 0x%08X, Data: 0x%08X\n", reg, data); | ||
452 | |||
453 | return data; | ||
454 | } | ||
455 | |||
456 | static void | ||
457 | bios_wr32(struct nvbios *bios, uint32_t reg, uint32_t data) | ||
458 | { | ||
459 | struct drm_nouveau_private *dev_priv = bios->dev->dev_private; | ||
460 | |||
461 | reg = munge_reg(bios, reg); | ||
462 | if (!valid_reg(bios, reg)) | ||
463 | return; | ||
464 | |||
465 | /* see note in bios_rd32 */ | ||
466 | if (reg & 0x1) | ||
467 | reg &= 0xfffffffe; | ||
468 | |||
469 | LOG_OLD_VALUE(bios_rd32(bios, reg)); | ||
470 | BIOSLOG(bios, " Write: Reg: 0x%08X, Data: 0x%08X\n", reg, data); | ||
471 | |||
472 | if (dev_priv->VBIOS.execute) { | ||
473 | still_alive(); | ||
474 | nv_wr32(bios->dev, reg, data); | ||
475 | } | ||
476 | } | ||
477 | |||
478 | static uint8_t | ||
479 | bios_idxprt_rd(struct nvbios *bios, uint16_t port, uint8_t index) | ||
480 | { | ||
481 | struct drm_nouveau_private *dev_priv = bios->dev->dev_private; | ||
482 | struct drm_device *dev = bios->dev; | ||
483 | uint8_t data; | ||
484 | |||
485 | if (!valid_idx_port(bios, port)) | ||
486 | return 0; | ||
487 | |||
488 | if (dev_priv->card_type < NV_50) { | ||
489 | if (port == NV_VIO_SRX) | ||
490 | data = NVReadVgaSeq(dev, bios->state.crtchead, index); | ||
491 | else /* assume NV_CIO_CRX__COLOR */ | ||
492 | data = NVReadVgaCrtc(dev, bios->state.crtchead, index); | ||
493 | } else { | ||
494 | uint32_t data32; | ||
495 | |||
496 | data32 = bios_rd32(bios, NV50_PDISPLAY_VGACRTC(index & ~3)); | ||
497 | data = (data32 >> ((index & 3) << 3)) & 0xff; | ||
498 | } | ||
499 | |||
500 | BIOSLOG(bios, " Indexed IO read: Port: 0x%04X, Index: 0x%02X, " | ||
501 | "Head: 0x%02X, Data: 0x%02X\n", | ||
502 | port, index, bios->state.crtchead, data); | ||
503 | return data; | ||
504 | } | ||
505 | |||
506 | static void | ||
507 | bios_idxprt_wr(struct nvbios *bios, uint16_t port, uint8_t index, uint8_t data) | ||
508 | { | ||
509 | struct drm_nouveau_private *dev_priv = bios->dev->dev_private; | ||
510 | struct drm_device *dev = bios->dev; | ||
511 | |||
512 | if (!valid_idx_port(bios, port)) | ||
513 | return; | ||
514 | |||
515 | /* | ||
516 | * The current head is maintained in the nvbios member state.crtchead. | ||
517 | * We trap changes to CR44 and update the head variable and hence the | ||
518 | * register set written. | ||
519 | * As CR44 only exists on CRTC0, we update crtchead to head0 in advance | ||
520 | * of the write, and to head1 after the write | ||
521 | */ | ||
522 | if (port == NV_CIO_CRX__COLOR && index == NV_CIO_CRE_44 && | ||
523 | data != NV_CIO_CRE_44_HEADB) | ||
524 | bios->state.crtchead = 0; | ||
525 | |||
526 | LOG_OLD_VALUE(bios_idxprt_rd(bios, port, index)); | ||
527 | BIOSLOG(bios, " Indexed IO write: Port: 0x%04X, Index: 0x%02X, " | ||
528 | "Head: 0x%02X, Data: 0x%02X\n", | ||
529 | port, index, bios->state.crtchead, data); | ||
530 | |||
531 | if (bios->execute && dev_priv->card_type < NV_50) { | ||
532 | still_alive(); | ||
533 | if (port == NV_VIO_SRX) | ||
534 | NVWriteVgaSeq(dev, bios->state.crtchead, index, data); | ||
535 | else /* assume NV_CIO_CRX__COLOR */ | ||
536 | NVWriteVgaCrtc(dev, bios->state.crtchead, index, data); | ||
537 | } else | ||
538 | if (bios->execute) { | ||
539 | uint32_t data32, shift = (index & 3) << 3; | ||
540 | |||
541 | still_alive(); | ||
542 | |||
543 | data32 = bios_rd32(bios, NV50_PDISPLAY_VGACRTC(index & ~3)); | ||
544 | data32 &= ~(0xff << shift); | ||
545 | data32 |= (data << shift); | ||
546 | bios_wr32(bios, NV50_PDISPLAY_VGACRTC(index & ~3), data32); | ||
547 | } | ||
548 | |||
549 | if (port == NV_CIO_CRX__COLOR && | ||
550 | index == NV_CIO_CRE_44 && data == NV_CIO_CRE_44_HEADB) | ||
551 | bios->state.crtchead = 1; | ||
552 | } | ||
553 | |||
554 | static uint8_t | ||
555 | bios_port_rd(struct nvbios *bios, uint16_t port) | ||
556 | { | ||
557 | uint8_t data, head = bios->state.crtchead; | ||
558 | |||
559 | if (!valid_port(bios, port)) | ||
560 | return 0; | ||
561 | |||
562 | data = NVReadPRMVIO(bios->dev, head, NV_PRMVIO0_OFFSET + port); | ||
563 | |||
564 | BIOSLOG(bios, " IO read: Port: 0x%04X, Head: 0x%02X, Data: 0x%02X\n", | ||
565 | port, head, data); | ||
566 | |||
567 | return data; | ||
568 | } | ||
569 | |||
570 | static void | ||
571 | bios_port_wr(struct nvbios *bios, uint16_t port, uint8_t data) | ||
572 | { | ||
573 | int head = bios->state.crtchead; | ||
574 | |||
575 | if (!valid_port(bios, port)) | ||
576 | return; | ||
577 | |||
578 | LOG_OLD_VALUE(bios_port_rd(bios, port)); | ||
579 | BIOSLOG(bios, " IO write: Port: 0x%04X, Head: 0x%02X, Data: 0x%02X\n", | ||
580 | port, head, data); | ||
581 | |||
582 | if (!bios->execute) | ||
583 | return; | ||
584 | |||
585 | still_alive(); | ||
586 | NVWritePRMVIO(bios->dev, head, NV_PRMVIO0_OFFSET + port, data); | ||
587 | } | ||
588 | |||
589 | static bool | ||
590 | io_flag_condition_met(struct nvbios *bios, uint16_t offset, uint8_t cond) | ||
591 | { | ||
592 | /* | ||
593 | * The IO flag condition entry has 2 bytes for the CRTC port; 1 byte | ||
594 | * for the CRTC index; 1 byte for the mask to apply to the value | ||
595 | * retrieved from the CRTC; 1 byte for the shift right to apply to the | ||
596 | * masked CRTC value; 2 bytes for the offset to the flag array, to | ||
597 | * which the shifted value is added; 1 byte for the mask applied to the | ||
598 | * value read from the flag array; and 1 byte for the value to compare | ||
599 | * against the masked byte from the flag table. | ||
600 | */ | ||
601 | |||
602 | uint16_t condptr = bios->io_flag_condition_tbl_ptr + cond * IO_FLAG_CONDITION_SIZE; | ||
603 | uint16_t crtcport = ROM16(bios->data[condptr]); | ||
604 | uint8_t crtcindex = bios->data[condptr + 2]; | ||
605 | uint8_t mask = bios->data[condptr + 3]; | ||
606 | uint8_t shift = bios->data[condptr + 4]; | ||
607 | uint16_t flagarray = ROM16(bios->data[condptr + 5]); | ||
608 | uint8_t flagarraymask = bios->data[condptr + 7]; | ||
609 | uint8_t cmpval = bios->data[condptr + 8]; | ||
610 | uint8_t data; | ||
611 | |||
612 | BIOSLOG(bios, "0x%04X: Port: 0x%04X, Index: 0x%02X, Mask: 0x%02X, " | ||
613 | "Shift: 0x%02X, FlagArray: 0x%04X, FAMask: 0x%02X, " | ||
614 | "Cmpval: 0x%02X\n", | ||
615 | offset, crtcport, crtcindex, mask, shift, flagarray, flagarraymask, cmpval); | ||
616 | |||
617 | data = bios_idxprt_rd(bios, crtcport, crtcindex); | ||
618 | |||
619 | data = bios->data[flagarray + ((data & mask) >> shift)]; | ||
620 | data &= flagarraymask; | ||
621 | |||
622 | BIOSLOG(bios, "0x%04X: Checking if 0x%02X equals 0x%02X\n", | ||
623 | offset, data, cmpval); | ||
624 | |||
625 | return (data == cmpval); | ||
626 | } | ||
627 | |||
628 | static bool | ||
629 | bios_condition_met(struct nvbios *bios, uint16_t offset, uint8_t cond) | ||
630 | { | ||
631 | /* | ||
632 | * The condition table entry has 4 bytes for the address of the | ||
633 | * register to check, 4 bytes for a mask to apply to the register and | ||
634 | * 4 for a test comparison value | ||
635 | */ | ||
636 | |||
637 | uint16_t condptr = bios->condition_tbl_ptr + cond * CONDITION_SIZE; | ||
638 | uint32_t reg = ROM32(bios->data[condptr]); | ||
639 | uint32_t mask = ROM32(bios->data[condptr + 4]); | ||
640 | uint32_t cmpval = ROM32(bios->data[condptr + 8]); | ||
641 | uint32_t data; | ||
642 | |||
643 | BIOSLOG(bios, "0x%04X: Cond: 0x%02X, Reg: 0x%08X, Mask: 0x%08X\n", | ||
644 | offset, cond, reg, mask); | ||
645 | |||
646 | data = bios_rd32(bios, reg) & mask; | ||
647 | |||
648 | BIOSLOG(bios, "0x%04X: Checking if 0x%08X equals 0x%08X\n", | ||
649 | offset, data, cmpval); | ||
650 | |||
651 | return (data == cmpval); | ||
652 | } | ||
653 | |||
654 | static bool | ||
655 | io_condition_met(struct nvbios *bios, uint16_t offset, uint8_t cond) | ||
656 | { | ||
657 | /* | ||
658 | * The IO condition entry has 2 bytes for the IO port address; 1 byte | ||
659 | * for the index to write to io_port; 1 byte for the mask to apply to | ||
660 | * the byte read from io_port+1; and 1 byte for the value to compare | ||
661 | * against the masked byte. | ||
662 | */ | ||
663 | |||
664 | uint16_t condptr = bios->io_condition_tbl_ptr + cond * IO_CONDITION_SIZE; | ||
665 | uint16_t io_port = ROM16(bios->data[condptr]); | ||
666 | uint8_t port_index = bios->data[condptr + 2]; | ||
667 | uint8_t mask = bios->data[condptr + 3]; | ||
668 | uint8_t cmpval = bios->data[condptr + 4]; | ||
669 | |||
670 | uint8_t data = bios_idxprt_rd(bios, io_port, port_index) & mask; | ||
671 | |||
672 | BIOSLOG(bios, "0x%04X: Checking if 0x%02X equals 0x%02X\n", | ||
673 | offset, data, cmpval); | ||
674 | |||
675 | return (data == cmpval); | ||
676 | } | ||
677 | |||
678 | static int | ||
679 | nv50_pll_set(struct drm_device *dev, uint32_t reg, uint32_t clk) | ||
680 | { | ||
681 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
682 | uint32_t reg0 = nv_rd32(dev, reg + 0); | ||
683 | uint32_t reg1 = nv_rd32(dev, reg + 4); | ||
684 | struct nouveau_pll_vals pll; | ||
685 | struct pll_lims pll_limits; | ||
686 | int ret; | ||
687 | |||
688 | ret = get_pll_limits(dev, reg, &pll_limits); | ||
689 | if (ret) | ||
690 | return ret; | ||
691 | |||
692 | clk = nouveau_calc_pll_mnp(dev, &pll_limits, clk, &pll); | ||
693 | if (!clk) | ||
694 | return -ERANGE; | ||
695 | |||
696 | reg0 = (reg0 & 0xfff8ffff) | (pll.log2P << 16); | ||
697 | reg1 = (reg1 & 0xffff0000) | (pll.N1 << 8) | pll.M1; | ||
698 | |||
699 | if (dev_priv->VBIOS.execute) { | ||
700 | still_alive(); | ||
701 | nv_wr32(dev, reg + 4, reg1); | ||
702 | nv_wr32(dev, reg + 0, reg0); | ||
703 | } | ||
704 | |||
705 | return 0; | ||
706 | } | ||
707 | |||
708 | static int | ||
709 | setPLL(struct nvbios *bios, uint32_t reg, uint32_t clk) | ||
710 | { | ||
711 | struct drm_device *dev = bios->dev; | ||
712 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
713 | /* clk in kHz */ | ||
714 | struct pll_lims pll_lim; | ||
715 | struct nouveau_pll_vals pllvals; | ||
716 | int ret; | ||
717 | |||
718 | if (dev_priv->card_type >= NV_50) | ||
719 | return nv50_pll_set(dev, reg, clk); | ||
720 | |||
721 | /* high regs (such as in the mac g5 table) are not -= 4 */ | ||
722 | ret = get_pll_limits(dev, reg > 0x405c ? reg : reg - 4, &pll_lim); | ||
723 | if (ret) | ||
724 | return ret; | ||
725 | |||
726 | clk = nouveau_calc_pll_mnp(dev, &pll_lim, clk, &pllvals); | ||
727 | if (!clk) | ||
728 | return -ERANGE; | ||
729 | |||
730 | if (bios->execute) { | ||
731 | still_alive(); | ||
732 | nouveau_hw_setpll(dev, reg, &pllvals); | ||
733 | } | ||
734 | |||
735 | return 0; | ||
736 | } | ||
737 | |||
738 | static int dcb_entry_idx_from_crtchead(struct drm_device *dev) | ||
739 | { | ||
740 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
741 | struct nvbios *bios = &dev_priv->VBIOS; | ||
742 | |||
743 | /* | ||
744 | * For the results of this function to be correct, CR44 must have been | ||
745 | * set (using bios_idxprt_wr to set crtchead), CR58 set for CR57 = 0, | ||
746 | * and the DCB table parsed, before the script calling the function is | ||
747 | * run. run_digital_op_script is example of how to do such setup | ||
748 | */ | ||
749 | |||
750 | uint8_t dcb_entry = NVReadVgaCrtc5758(dev, bios->state.crtchead, 0); | ||
751 | |||
752 | if (dcb_entry > bios->bdcb.dcb.entries) { | ||
753 | NV_ERROR(dev, "CR58 doesn't have a valid DCB entry currently " | ||
754 | "(%02X)\n", dcb_entry); | ||
755 | dcb_entry = 0x7f; /* unused / invalid marker */ | ||
756 | } | ||
757 | |||
758 | return dcb_entry; | ||
759 | } | ||
760 | |||
761 | static struct nouveau_i2c_chan * | ||
762 | init_i2c_device_find(struct drm_device *dev, int i2c_index) | ||
763 | { | ||
764 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
765 | struct bios_parsed_dcb *bdcb = &dev_priv->VBIOS.bdcb; | ||
766 | |||
767 | if (i2c_index == 0xff) { | ||
768 | /* note: dcb_entry_idx_from_crtchead needs pre-script set-up */ | ||
769 | int idx = dcb_entry_idx_from_crtchead(dev), shift = 0; | ||
770 | int default_indices = bdcb->i2c_default_indices; | ||
771 | |||
772 | if (idx != 0x7f && bdcb->dcb.entry[idx].i2c_upper_default) | ||
773 | shift = 4; | ||
774 | |||
775 | i2c_index = (default_indices >> shift) & 0xf; | ||
776 | } | ||
777 | if (i2c_index == 0x80) /* g80+ */ | ||
778 | i2c_index = bdcb->i2c_default_indices & 0xf; | ||
779 | |||
780 | return nouveau_i2c_find(dev, i2c_index); | ||
781 | } | ||
782 | |||
783 | static uint32_t get_tmds_index_reg(struct drm_device *dev, uint8_t mlv) | ||
784 | { | ||
785 | /* | ||
786 | * For mlv < 0x80, it is an index into a table of TMDS base addresses. | ||
787 | * For mlv == 0x80 use the "or" value of the dcb_entry indexed by | ||
788 | * CR58 for CR57 = 0 to index a table of offsets to the basic | ||
789 | * 0x6808b0 address. | ||
790 | * For mlv == 0x81 use the "or" value of the dcb_entry indexed by | ||
791 | * CR58 for CR57 = 0 to index a table of offsets to the basic | ||
792 | * 0x6808b0 address, and then flip the offset by 8. | ||
793 | */ | ||
794 | |||
795 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
796 | const int pramdac_offset[13] = { | ||
797 | 0, 0, 0x8, 0, 0x2000, 0, 0, 0, 0x2008, 0, 0, 0, 0x2000 }; | ||
798 | const uint32_t pramdac_table[4] = { | ||
799 | 0x6808b0, 0x6808b8, 0x6828b0, 0x6828b8 }; | ||
800 | |||
801 | if (mlv >= 0x80) { | ||
802 | int dcb_entry, dacoffset; | ||
803 | |||
804 | /* note: dcb_entry_idx_from_crtchead needs pre-script set-up */ | ||
805 | dcb_entry = dcb_entry_idx_from_crtchead(dev); | ||
806 | if (dcb_entry == 0x7f) | ||
807 | return 0; | ||
808 | dacoffset = pramdac_offset[ | ||
809 | dev_priv->VBIOS.bdcb.dcb.entry[dcb_entry].or]; | ||
810 | if (mlv == 0x81) | ||
811 | dacoffset ^= 8; | ||
812 | return 0x6808b0 + dacoffset; | ||
813 | } else { | ||
814 | if (mlv > ARRAY_SIZE(pramdac_table)) { | ||
815 | NV_ERROR(dev, "Magic Lookup Value too big (%02X)\n", | ||
816 | mlv); | ||
817 | return 0; | ||
818 | } | ||
819 | return pramdac_table[mlv]; | ||
820 | } | ||
821 | } | ||
822 | |||
823 | static bool | ||
824 | init_io_restrict_prog(struct nvbios *bios, uint16_t offset, | ||
825 | struct init_exec *iexec) | ||
826 | { | ||
827 | /* | ||
828 | * INIT_IO_RESTRICT_PROG opcode: 0x32 ('2') | ||
829 | * | ||
830 | * offset (8 bit): opcode | ||
831 | * offset + 1 (16 bit): CRTC port | ||
832 | * offset + 3 (8 bit): CRTC index | ||
833 | * offset + 4 (8 bit): mask | ||
834 | * offset + 5 (8 bit): shift | ||
835 | * offset + 6 (8 bit): count | ||
836 | * offset + 7 (32 bit): register | ||
837 | * offset + 11 (32 bit): configuration 1 | ||
838 | * ... | ||
839 | * | ||
840 | * Starting at offset + 11 there are "count" 32 bit values. | ||
841 | * To find out which value to use read index "CRTC index" on "CRTC | ||
842 | * port", AND this value with "mask" and then bit shift right "shift" | ||
843 | * bits. Read the appropriate value using this index and write to | ||
844 | * "register" | ||
845 | */ | ||
846 | |||
847 | uint16_t crtcport = ROM16(bios->data[offset + 1]); | ||
848 | uint8_t crtcindex = bios->data[offset + 3]; | ||
849 | uint8_t mask = bios->data[offset + 4]; | ||
850 | uint8_t shift = bios->data[offset + 5]; | ||
851 | uint8_t count = bios->data[offset + 6]; | ||
852 | uint32_t reg = ROM32(bios->data[offset + 7]); | ||
853 | uint8_t config; | ||
854 | uint32_t configval; | ||
855 | |||
856 | if (!iexec->execute) | ||
857 | return true; | ||
858 | |||
859 | BIOSLOG(bios, "0x%04X: Port: 0x%04X, Index: 0x%02X, Mask: 0x%02X, " | ||
860 | "Shift: 0x%02X, Count: 0x%02X, Reg: 0x%08X\n", | ||
861 | offset, crtcport, crtcindex, mask, shift, count, reg); | ||
862 | |||
863 | config = (bios_idxprt_rd(bios, crtcport, crtcindex) & mask) >> shift; | ||
864 | if (config > count) { | ||
865 | NV_ERROR(bios->dev, | ||
866 | "0x%04X: Config 0x%02X exceeds maximal bound 0x%02X\n", | ||
867 | offset, config, count); | ||
868 | return false; | ||
869 | } | ||
870 | |||
871 | configval = ROM32(bios->data[offset + 11 + config * 4]); | ||
872 | |||
873 | BIOSLOG(bios, "0x%04X: Writing config %02X\n", offset, config); | ||
874 | |||
875 | bios_wr32(bios, reg, configval); | ||
876 | |||
877 | return true; | ||
878 | } | ||
879 | |||
880 | static bool | ||
881 | init_repeat(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
882 | { | ||
883 | /* | ||
884 | * INIT_REPEAT opcode: 0x33 ('3') | ||
885 | * | ||
886 | * offset (8 bit): opcode | ||
887 | * offset + 1 (8 bit): count | ||
888 | * | ||
889 | * Execute script following this opcode up to INIT_REPEAT_END | ||
890 | * "count" times | ||
891 | */ | ||
892 | |||
893 | uint8_t count = bios->data[offset + 1]; | ||
894 | uint8_t i; | ||
895 | |||
896 | /* no iexec->execute check by design */ | ||
897 | |||
898 | BIOSLOG(bios, "0x%04X: Repeating following segment %d times\n", | ||
899 | offset, count); | ||
900 | |||
901 | iexec->repeat = true; | ||
902 | |||
903 | /* | ||
904 | * count - 1, as the script block will execute once when we leave this | ||
905 | * opcode -- this is compatible with bios behaviour as: | ||
906 | * a) the block is always executed at least once, even if count == 0 | ||
907 | * b) the bios interpreter skips to the op following INIT_END_REPEAT, | ||
908 | * while we don't | ||
909 | */ | ||
910 | for (i = 0; i < count - 1; i++) | ||
911 | parse_init_table(bios, offset + 2, iexec); | ||
912 | |||
913 | iexec->repeat = false; | ||
914 | |||
915 | return true; | ||
916 | } | ||
917 | |||
918 | static bool | ||
919 | init_io_restrict_pll(struct nvbios *bios, uint16_t offset, | ||
920 | struct init_exec *iexec) | ||
921 | { | ||
922 | /* | ||
923 | * INIT_IO_RESTRICT_PLL opcode: 0x34 ('4') | ||
924 | * | ||
925 | * offset (8 bit): opcode | ||
926 | * offset + 1 (16 bit): CRTC port | ||
927 | * offset + 3 (8 bit): CRTC index | ||
928 | * offset + 4 (8 bit): mask | ||
929 | * offset + 5 (8 bit): shift | ||
930 | * offset + 6 (8 bit): IO flag condition index | ||
931 | * offset + 7 (8 bit): count | ||
932 | * offset + 8 (32 bit): register | ||
933 | * offset + 12 (16 bit): frequency 1 | ||
934 | * ... | ||
935 | * | ||
936 | * Starting at offset + 12 there are "count" 16 bit frequencies (10kHz). | ||
937 | * Set PLL register "register" to coefficients for frequency n, | ||
938 | * selected by reading index "CRTC index" of "CRTC port" ANDed with | ||
939 | * "mask" and shifted right by "shift". | ||
940 | * | ||
941 | * If "IO flag condition index" > 0, and condition met, double | ||
942 | * frequency before setting it. | ||
943 | */ | ||
944 | |||
945 | uint16_t crtcport = ROM16(bios->data[offset + 1]); | ||
946 | uint8_t crtcindex = bios->data[offset + 3]; | ||
947 | uint8_t mask = bios->data[offset + 4]; | ||
948 | uint8_t shift = bios->data[offset + 5]; | ||
949 | int8_t io_flag_condition_idx = bios->data[offset + 6]; | ||
950 | uint8_t count = bios->data[offset + 7]; | ||
951 | uint32_t reg = ROM32(bios->data[offset + 8]); | ||
952 | uint8_t config; | ||
953 | uint16_t freq; | ||
954 | |||
955 | if (!iexec->execute) | ||
956 | return true; | ||
957 | |||
958 | BIOSLOG(bios, "0x%04X: Port: 0x%04X, Index: 0x%02X, Mask: 0x%02X, " | ||
959 | "Shift: 0x%02X, IO Flag Condition: 0x%02X, " | ||
960 | "Count: 0x%02X, Reg: 0x%08X\n", | ||
961 | offset, crtcport, crtcindex, mask, shift, | ||
962 | io_flag_condition_idx, count, reg); | ||
963 | |||
964 | config = (bios_idxprt_rd(bios, crtcport, crtcindex) & mask) >> shift; | ||
965 | if (config > count) { | ||
966 | NV_ERROR(bios->dev, | ||
967 | "0x%04X: Config 0x%02X exceeds maximal bound 0x%02X\n", | ||
968 | offset, config, count); | ||
969 | return false; | ||
970 | } | ||
971 | |||
972 | freq = ROM16(bios->data[offset + 12 + config * 2]); | ||
973 | |||
974 | if (io_flag_condition_idx > 0) { | ||
975 | if (io_flag_condition_met(bios, offset, io_flag_condition_idx)) { | ||
976 | BIOSLOG(bios, "0x%04X: Condition fulfilled -- " | ||
977 | "frequency doubled\n", offset); | ||
978 | freq *= 2; | ||
979 | } else | ||
980 | BIOSLOG(bios, "0x%04X: Condition not fulfilled -- " | ||
981 | "frequency unchanged\n", offset); | ||
982 | } | ||
983 | |||
984 | BIOSLOG(bios, "0x%04X: Reg: 0x%08X, Config: 0x%02X, Freq: %d0kHz\n", | ||
985 | offset, reg, config, freq); | ||
986 | |||
987 | setPLL(bios, reg, freq * 10); | ||
988 | |||
989 | return true; | ||
990 | } | ||
991 | |||
992 | static bool | ||
993 | init_end_repeat(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
994 | { | ||
995 | /* | ||
996 | * INIT_END_REPEAT opcode: 0x36 ('6') | ||
997 | * | ||
998 | * offset (8 bit): opcode | ||
999 | * | ||
1000 | * Marks the end of the block for INIT_REPEAT to repeat | ||
1001 | */ | ||
1002 | |||
1003 | /* no iexec->execute check by design */ | ||
1004 | |||
1005 | /* | ||
1006 | * iexec->repeat flag necessary to go past INIT_END_REPEAT opcode when | ||
1007 | * we're not in repeat mode | ||
1008 | */ | ||
1009 | if (iexec->repeat) | ||
1010 | return false; | ||
1011 | |||
1012 | return true; | ||
1013 | } | ||
1014 | |||
1015 | static bool | ||
1016 | init_copy(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
1017 | { | ||
1018 | /* | ||
1019 | * INIT_COPY opcode: 0x37 ('7') | ||
1020 | * | ||
1021 | * offset (8 bit): opcode | ||
1022 | * offset + 1 (32 bit): register | ||
1023 | * offset + 5 (8 bit): shift | ||
1024 | * offset + 6 (8 bit): srcmask | ||
1025 | * offset + 7 (16 bit): CRTC port | ||
1026 | * offset + 9 (8 bit): CRTC index | ||
1027 | * offset + 10 (8 bit): mask | ||
1028 | * | ||
1029 | * Read index "CRTC index" on "CRTC port", AND with "mask", OR with | ||
1030 | * (REGVAL("register") >> "shift" & "srcmask") and write-back to CRTC | ||
1031 | * port | ||
1032 | */ | ||
1033 | |||
1034 | uint32_t reg = ROM32(bios->data[offset + 1]); | ||
1035 | uint8_t shift = bios->data[offset + 5]; | ||
1036 | uint8_t srcmask = bios->data[offset + 6]; | ||
1037 | uint16_t crtcport = ROM16(bios->data[offset + 7]); | ||
1038 | uint8_t crtcindex = bios->data[offset + 9]; | ||
1039 | uint8_t mask = bios->data[offset + 10]; | ||
1040 | uint32_t data; | ||
1041 | uint8_t crtcdata; | ||
1042 | |||
1043 | if (!iexec->execute) | ||
1044 | return true; | ||
1045 | |||
1046 | BIOSLOG(bios, "0x%04X: Reg: 0x%08X, Shift: 0x%02X, SrcMask: 0x%02X, " | ||
1047 | "Port: 0x%04X, Index: 0x%02X, Mask: 0x%02X\n", | ||
1048 | offset, reg, shift, srcmask, crtcport, crtcindex, mask); | ||
1049 | |||
1050 | data = bios_rd32(bios, reg); | ||
1051 | |||
1052 | if (shift < 0x80) | ||
1053 | data >>= shift; | ||
1054 | else | ||
1055 | data <<= (0x100 - shift); | ||
1056 | |||
1057 | data &= srcmask; | ||
1058 | |||
1059 | crtcdata = bios_idxprt_rd(bios, crtcport, crtcindex) & mask; | ||
1060 | crtcdata |= (uint8_t)data; | ||
1061 | bios_idxprt_wr(bios, crtcport, crtcindex, crtcdata); | ||
1062 | |||
1063 | return true; | ||
1064 | } | ||
1065 | |||
1066 | static bool | ||
1067 | init_not(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
1068 | { | ||
1069 | /* | ||
1070 | * INIT_NOT opcode: 0x38 ('8') | ||
1071 | * | ||
1072 | * offset (8 bit): opcode | ||
1073 | * | ||
1074 | * Invert the current execute / no-execute condition (i.e. "else") | ||
1075 | */ | ||
1076 | if (iexec->execute) | ||
1077 | BIOSLOG(bios, "0x%04X: ------ Skipping following commands ------\n", offset); | ||
1078 | else | ||
1079 | BIOSLOG(bios, "0x%04X: ------ Executing following commands ------\n", offset); | ||
1080 | |||
1081 | iexec->execute = !iexec->execute; | ||
1082 | return true; | ||
1083 | } | ||
1084 | |||
1085 | static bool | ||
1086 | init_io_flag_condition(struct nvbios *bios, uint16_t offset, | ||
1087 | struct init_exec *iexec) | ||
1088 | { | ||
1089 | /* | ||
1090 | * INIT_IO_FLAG_CONDITION opcode: 0x39 ('9') | ||
1091 | * | ||
1092 | * offset (8 bit): opcode | ||
1093 | * offset + 1 (8 bit): condition number | ||
1094 | * | ||
1095 | * Check condition "condition number" in the IO flag condition table. | ||
1096 | * If condition not met skip subsequent opcodes until condition is | ||
1097 | * inverted (INIT_NOT), or we hit INIT_RESUME | ||
1098 | */ | ||
1099 | |||
1100 | uint8_t cond = bios->data[offset + 1]; | ||
1101 | |||
1102 | if (!iexec->execute) | ||
1103 | return true; | ||
1104 | |||
1105 | if (io_flag_condition_met(bios, offset, cond)) | ||
1106 | BIOSLOG(bios, "0x%04X: Condition fulfilled -- continuing to execute\n", offset); | ||
1107 | else { | ||
1108 | BIOSLOG(bios, "0x%04X: Condition not fulfilled -- skipping following commands\n", offset); | ||
1109 | iexec->execute = false; | ||
1110 | } | ||
1111 | |||
1112 | return true; | ||
1113 | } | ||
1114 | |||
1115 | static bool | ||
1116 | init_idx_addr_latched(struct nvbios *bios, uint16_t offset, | ||
1117 | struct init_exec *iexec) | ||
1118 | { | ||
1119 | /* | ||
1120 | * INIT_INDEX_ADDRESS_LATCHED opcode: 0x49 ('I') | ||
1121 | * | ||
1122 | * offset (8 bit): opcode | ||
1123 | * offset + 1 (32 bit): control register | ||
1124 | * offset + 5 (32 bit): data register | ||
1125 | * offset + 9 (32 bit): mask | ||
1126 | * offset + 13 (32 bit): data | ||
1127 | * offset + 17 (8 bit): count | ||
1128 | * offset + 18 (8 bit): address 1 | ||
1129 | * offset + 19 (8 bit): data 1 | ||
1130 | * ... | ||
1131 | * | ||
1132 | * For each of "count" address and data pairs, write "data n" to | ||
1133 | * "data register", read the current value of "control register", | ||
1134 | * and write it back once ANDed with "mask", ORed with "data", | ||
1135 | * and ORed with "address n" | ||
1136 | */ | ||
1137 | |||
1138 | uint32_t controlreg = ROM32(bios->data[offset + 1]); | ||
1139 | uint32_t datareg = ROM32(bios->data[offset + 5]); | ||
1140 | uint32_t mask = ROM32(bios->data[offset + 9]); | ||
1141 | uint32_t data = ROM32(bios->data[offset + 13]); | ||
1142 | uint8_t count = bios->data[offset + 17]; | ||
1143 | uint32_t value; | ||
1144 | int i; | ||
1145 | |||
1146 | if (!iexec->execute) | ||
1147 | return true; | ||
1148 | |||
1149 | BIOSLOG(bios, "0x%04X: ControlReg: 0x%08X, DataReg: 0x%08X, " | ||
1150 | "Mask: 0x%08X, Data: 0x%08X, Count: 0x%02X\n", | ||
1151 | offset, controlreg, datareg, mask, data, count); | ||
1152 | |||
1153 | for (i = 0; i < count; i++) { | ||
1154 | uint8_t instaddress = bios->data[offset + 18 + i * 2]; | ||
1155 | uint8_t instdata = bios->data[offset + 19 + i * 2]; | ||
1156 | |||
1157 | BIOSLOG(bios, "0x%04X: Address: 0x%02X, Data: 0x%02X\n", | ||
1158 | offset, instaddress, instdata); | ||
1159 | |||
1160 | bios_wr32(bios, datareg, instdata); | ||
1161 | value = bios_rd32(bios, controlreg) & mask; | ||
1162 | value |= data; | ||
1163 | value |= instaddress; | ||
1164 | bios_wr32(bios, controlreg, value); | ||
1165 | } | ||
1166 | |||
1167 | return true; | ||
1168 | } | ||
1169 | |||
1170 | static bool | ||
1171 | init_io_restrict_pll2(struct nvbios *bios, uint16_t offset, | ||
1172 | struct init_exec *iexec) | ||
1173 | { | ||
1174 | /* | ||
1175 | * INIT_IO_RESTRICT_PLL2 opcode: 0x4A ('J') | ||
1176 | * | ||
1177 | * offset (8 bit): opcode | ||
1178 | * offset + 1 (16 bit): CRTC port | ||
1179 | * offset + 3 (8 bit): CRTC index | ||
1180 | * offset + 4 (8 bit): mask | ||
1181 | * offset + 5 (8 bit): shift | ||
1182 | * offset + 6 (8 bit): count | ||
1183 | * offset + 7 (32 bit): register | ||
1184 | * offset + 11 (32 bit): frequency 1 | ||
1185 | * ... | ||
1186 | * | ||
1187 | * Starting at offset + 11 there are "count" 32 bit frequencies (kHz). | ||
1188 | * Set PLL register "register" to coefficients for frequency n, | ||
1189 | * selected by reading index "CRTC index" of "CRTC port" ANDed with | ||
1190 | * "mask" and shifted right by "shift". | ||
1191 | */ | ||
1192 | |||
1193 | uint16_t crtcport = ROM16(bios->data[offset + 1]); | ||
1194 | uint8_t crtcindex = bios->data[offset + 3]; | ||
1195 | uint8_t mask = bios->data[offset + 4]; | ||
1196 | uint8_t shift = bios->data[offset + 5]; | ||
1197 | uint8_t count = bios->data[offset + 6]; | ||
1198 | uint32_t reg = ROM32(bios->data[offset + 7]); | ||
1199 | uint8_t config; | ||
1200 | uint32_t freq; | ||
1201 | |||
1202 | if (!iexec->execute) | ||
1203 | return true; | ||
1204 | |||
1205 | BIOSLOG(bios, "0x%04X: Port: 0x%04X, Index: 0x%02X, Mask: 0x%02X, " | ||
1206 | "Shift: 0x%02X, Count: 0x%02X, Reg: 0x%08X\n", | ||
1207 | offset, crtcport, crtcindex, mask, shift, count, reg); | ||
1208 | |||
1209 | if (!reg) | ||
1210 | return true; | ||
1211 | |||
1212 | config = (bios_idxprt_rd(bios, crtcport, crtcindex) & mask) >> shift; | ||
1213 | if (config > count) { | ||
1214 | NV_ERROR(bios->dev, | ||
1215 | "0x%04X: Config 0x%02X exceeds maximal bound 0x%02X\n", | ||
1216 | offset, config, count); | ||
1217 | return false; | ||
1218 | } | ||
1219 | |||
1220 | freq = ROM32(bios->data[offset + 11 + config * 4]); | ||
1221 | |||
1222 | BIOSLOG(bios, "0x%04X: Reg: 0x%08X, Config: 0x%02X, Freq: %dkHz\n", | ||
1223 | offset, reg, config, freq); | ||
1224 | |||
1225 | setPLL(bios, reg, freq); | ||
1226 | |||
1227 | return true; | ||
1228 | } | ||
1229 | |||
1230 | static bool | ||
1231 | init_pll2(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
1232 | { | ||
1233 | /* | ||
1234 | * INIT_PLL2 opcode: 0x4B ('K') | ||
1235 | * | ||
1236 | * offset (8 bit): opcode | ||
1237 | * offset + 1 (32 bit): register | ||
1238 | * offset + 5 (32 bit): freq | ||
1239 | * | ||
1240 | * Set PLL register "register" to coefficients for frequency "freq" | ||
1241 | */ | ||
1242 | |||
1243 | uint32_t reg = ROM32(bios->data[offset + 1]); | ||
1244 | uint32_t freq = ROM32(bios->data[offset + 5]); | ||
1245 | |||
1246 | if (!iexec->execute) | ||
1247 | return true; | ||
1248 | |||
1249 | BIOSLOG(bios, "0x%04X: Reg: 0x%04X, Freq: %dkHz\n", | ||
1250 | offset, reg, freq); | ||
1251 | |||
1252 | setPLL(bios, reg, freq); | ||
1253 | return true; | ||
1254 | } | ||
1255 | |||
1256 | static bool | ||
1257 | init_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
1258 | { | ||
1259 | /* | ||
1260 | * INIT_I2C_BYTE opcode: 0x4C ('L') | ||
1261 | * | ||
1262 | * offset (8 bit): opcode | ||
1263 | * offset + 1 (8 bit): DCB I2C table entry index | ||
1264 | * offset + 2 (8 bit): I2C slave address | ||
1265 | * offset + 3 (8 bit): count | ||
1266 | * offset + 4 (8 bit): I2C register 1 | ||
1267 | * offset + 5 (8 bit): mask 1 | ||
1268 | * offset + 6 (8 bit): data 1 | ||
1269 | * ... | ||
1270 | * | ||
1271 | * For each of "count" registers given by "I2C register n" on the device | ||
1272 | * addressed by "I2C slave address" on the I2C bus given by | ||
1273 | * "DCB I2C table entry index", read the register, AND the result with | ||
1274 | * "mask n" and OR it with "data n" before writing it back to the device | ||
1275 | */ | ||
1276 | |||
1277 | uint8_t i2c_index = bios->data[offset + 1]; | ||
1278 | uint8_t i2c_address = bios->data[offset + 2]; | ||
1279 | uint8_t count = bios->data[offset + 3]; | ||
1280 | struct nouveau_i2c_chan *chan; | ||
1281 | struct i2c_msg msg; | ||
1282 | int i; | ||
1283 | |||
1284 | if (!iexec->execute) | ||
1285 | return true; | ||
1286 | |||
1287 | BIOSLOG(bios, "0x%04X: DCBI2CIndex: 0x%02X, I2CAddress: 0x%02X, " | ||
1288 | "Count: 0x%02X\n", | ||
1289 | offset, i2c_index, i2c_address, count); | ||
1290 | |||
1291 | chan = init_i2c_device_find(bios->dev, i2c_index); | ||
1292 | if (!chan) | ||
1293 | return false; | ||
1294 | |||
1295 | for (i = 0; i < count; i++) { | ||
1296 | uint8_t i2c_reg = bios->data[offset + 4 + i * 3]; | ||
1297 | uint8_t mask = bios->data[offset + 5 + i * 3]; | ||
1298 | uint8_t data = bios->data[offset + 6 + i * 3]; | ||
1299 | uint8_t value; | ||
1300 | |||
1301 | msg.addr = i2c_address; | ||
1302 | msg.flags = I2C_M_RD; | ||
1303 | msg.len = 1; | ||
1304 | msg.buf = &value; | ||
1305 | if (i2c_transfer(&chan->adapter, &msg, 1) != 1) | ||
1306 | return false; | ||
1307 | |||
1308 | BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X, Value: 0x%02X, " | ||
1309 | "Mask: 0x%02X, Data: 0x%02X\n", | ||
1310 | offset, i2c_reg, value, mask, data); | ||
1311 | |||
1312 | value = (value & mask) | data; | ||
1313 | |||
1314 | if (bios->execute) { | ||
1315 | msg.addr = i2c_address; | ||
1316 | msg.flags = 0; | ||
1317 | msg.len = 1; | ||
1318 | msg.buf = &value; | ||
1319 | if (i2c_transfer(&chan->adapter, &msg, 1) != 1) | ||
1320 | return false; | ||
1321 | } | ||
1322 | } | ||
1323 | |||
1324 | return true; | ||
1325 | } | ||
1326 | |||
1327 | static bool | ||
1328 | init_zm_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
1329 | { | ||
1330 | /* | ||
1331 | * INIT_ZM_I2C_BYTE opcode: 0x4D ('M') | ||
1332 | * | ||
1333 | * offset (8 bit): opcode | ||
1334 | * offset + 1 (8 bit): DCB I2C table entry index | ||
1335 | * offset + 2 (8 bit): I2C slave address | ||
1336 | * offset + 3 (8 bit): count | ||
1337 | * offset + 4 (8 bit): I2C register 1 | ||
1338 | * offset + 5 (8 bit): data 1 | ||
1339 | * ... | ||
1340 | * | ||
1341 | * For each of "count" registers given by "I2C register n" on the device | ||
1342 | * addressed by "I2C slave address" on the I2C bus given by | ||
1343 | * "DCB I2C table entry index", set the register to "data n" | ||
1344 | */ | ||
1345 | |||
1346 | uint8_t i2c_index = bios->data[offset + 1]; | ||
1347 | uint8_t i2c_address = bios->data[offset + 2]; | ||
1348 | uint8_t count = bios->data[offset + 3]; | ||
1349 | struct nouveau_i2c_chan *chan; | ||
1350 | struct i2c_msg msg; | ||
1351 | int i; | ||
1352 | |||
1353 | if (!iexec->execute) | ||
1354 | return true; | ||
1355 | |||
1356 | BIOSLOG(bios, "0x%04X: DCBI2CIndex: 0x%02X, I2CAddress: 0x%02X, " | ||
1357 | "Count: 0x%02X\n", | ||
1358 | offset, i2c_index, i2c_address, count); | ||
1359 | |||
1360 | chan = init_i2c_device_find(bios->dev, i2c_index); | ||
1361 | if (!chan) | ||
1362 | return false; | ||
1363 | |||
1364 | for (i = 0; i < count; i++) { | ||
1365 | uint8_t i2c_reg = bios->data[offset + 4 + i * 2]; | ||
1366 | uint8_t data = bios->data[offset + 5 + i * 2]; | ||
1367 | |||
1368 | BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X, Data: 0x%02X\n", | ||
1369 | offset, i2c_reg, data); | ||
1370 | |||
1371 | if (bios->execute) { | ||
1372 | msg.addr = i2c_address; | ||
1373 | msg.flags = 0; | ||
1374 | msg.len = 1; | ||
1375 | msg.buf = &data; | ||
1376 | if (i2c_transfer(&chan->adapter, &msg, 1) != 1) | ||
1377 | return false; | ||
1378 | } | ||
1379 | } | ||
1380 | |||
1381 | return true; | ||
1382 | } | ||
1383 | |||
1384 | static bool | ||
1385 | init_zm_i2c(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
1386 | { | ||
1387 | /* | ||
1388 | * INIT_ZM_I2C opcode: 0x4E ('N') | ||
1389 | * | ||
1390 | * offset (8 bit): opcode | ||
1391 | * offset + 1 (8 bit): DCB I2C table entry index | ||
1392 | * offset + 2 (8 bit): I2C slave address | ||
1393 | * offset + 3 (8 bit): count | ||
1394 | * offset + 4 (8 bit): data 1 | ||
1395 | * ... | ||
1396 | * | ||
1397 | * Send "count" bytes ("data n") to the device addressed by "I2C slave | ||
1398 | * address" on the I2C bus given by "DCB I2C table entry index" | ||
1399 | */ | ||
1400 | |||
1401 | uint8_t i2c_index = bios->data[offset + 1]; | ||
1402 | uint8_t i2c_address = bios->data[offset + 2]; | ||
1403 | uint8_t count = bios->data[offset + 3]; | ||
1404 | struct nouveau_i2c_chan *chan; | ||
1405 | struct i2c_msg msg; | ||
1406 | uint8_t data[256]; | ||
1407 | int i; | ||
1408 | |||
1409 | if (!iexec->execute) | ||
1410 | return true; | ||
1411 | |||
1412 | BIOSLOG(bios, "0x%04X: DCBI2CIndex: 0x%02X, I2CAddress: 0x%02X, " | ||
1413 | "Count: 0x%02X\n", | ||
1414 | offset, i2c_index, i2c_address, count); | ||
1415 | |||
1416 | chan = init_i2c_device_find(bios->dev, i2c_index); | ||
1417 | if (!chan) | ||
1418 | return false; | ||
1419 | |||
1420 | for (i = 0; i < count; i++) { | ||
1421 | data[i] = bios->data[offset + 4 + i]; | ||
1422 | |||
1423 | BIOSLOG(bios, "0x%04X: Data: 0x%02X\n", offset, data[i]); | ||
1424 | } | ||
1425 | |||
1426 | if (bios->execute) { | ||
1427 | msg.addr = i2c_address; | ||
1428 | msg.flags = 0; | ||
1429 | msg.len = count; | ||
1430 | msg.buf = data; | ||
1431 | if (i2c_transfer(&chan->adapter, &msg, 1) != 1) | ||
1432 | return false; | ||
1433 | } | ||
1434 | |||
1435 | return true; | ||
1436 | } | ||
1437 | |||
1438 | static bool | ||
1439 | init_tmds(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
1440 | { | ||
1441 | /* | ||
1442 | * INIT_TMDS opcode: 0x4F ('O') (non-canon name) | ||
1443 | * | ||
1444 | * offset (8 bit): opcode | ||
1445 | * offset + 1 (8 bit): magic lookup value | ||
1446 | * offset + 2 (8 bit): TMDS address | ||
1447 | * offset + 3 (8 bit): mask | ||
1448 | * offset + 4 (8 bit): data | ||
1449 | * | ||
1450 | * Read the data reg for TMDS address "TMDS address", AND it with mask | ||
1451 | * and OR it with data, then write it back | ||
1452 | * "magic lookup value" determines which TMDS base address register is | ||
1453 | * used -- see get_tmds_index_reg() | ||
1454 | */ | ||
1455 | |||
1456 | uint8_t mlv = bios->data[offset + 1]; | ||
1457 | uint32_t tmdsaddr = bios->data[offset + 2]; | ||
1458 | uint8_t mask = bios->data[offset + 3]; | ||
1459 | uint8_t data = bios->data[offset + 4]; | ||
1460 | uint32_t reg, value; | ||
1461 | |||
1462 | if (!iexec->execute) | ||
1463 | return true; | ||
1464 | |||
1465 | BIOSLOG(bios, "0x%04X: MagicLookupValue: 0x%02X, TMDSAddr: 0x%02X, " | ||
1466 | "Mask: 0x%02X, Data: 0x%02X\n", | ||
1467 | offset, mlv, tmdsaddr, mask, data); | ||
1468 | |||
1469 | reg = get_tmds_index_reg(bios->dev, mlv); | ||
1470 | if (!reg) | ||
1471 | return false; | ||
1472 | |||
1473 | bios_wr32(bios, reg, | ||
1474 | tmdsaddr | NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE); | ||
1475 | value = (bios_rd32(bios, reg + 4) & mask) | data; | ||
1476 | bios_wr32(bios, reg + 4, value); | ||
1477 | bios_wr32(bios, reg, tmdsaddr); | ||
1478 | |||
1479 | return true; | ||
1480 | } | ||
1481 | |||
1482 | static bool | ||
1483 | init_zm_tmds_group(struct nvbios *bios, uint16_t offset, | ||
1484 | struct init_exec *iexec) | ||
1485 | { | ||
1486 | /* | ||
1487 | * INIT_ZM_TMDS_GROUP opcode: 0x50 ('P') (non-canon name) | ||
1488 | * | ||
1489 | * offset (8 bit): opcode | ||
1490 | * offset + 1 (8 bit): magic lookup value | ||
1491 | * offset + 2 (8 bit): count | ||
1492 | * offset + 3 (8 bit): addr 1 | ||
1493 | * offset + 4 (8 bit): data 1 | ||
1494 | * ... | ||
1495 | * | ||
1496 | * For each of "count" TMDS address and data pairs write "data n" to | ||
1497 | * "addr n". "magic lookup value" determines which TMDS base address | ||
1498 | * register is used -- see get_tmds_index_reg() | ||
1499 | */ | ||
1500 | |||
1501 | uint8_t mlv = bios->data[offset + 1]; | ||
1502 | uint8_t count = bios->data[offset + 2]; | ||
1503 | uint32_t reg; | ||
1504 | int i; | ||
1505 | |||
1506 | if (!iexec->execute) | ||
1507 | return true; | ||
1508 | |||
1509 | BIOSLOG(bios, "0x%04X: MagicLookupValue: 0x%02X, Count: 0x%02X\n", | ||
1510 | offset, mlv, count); | ||
1511 | |||
1512 | reg = get_tmds_index_reg(bios->dev, mlv); | ||
1513 | if (!reg) | ||
1514 | return false; | ||
1515 | |||
1516 | for (i = 0; i < count; i++) { | ||
1517 | uint8_t tmdsaddr = bios->data[offset + 3 + i * 2]; | ||
1518 | uint8_t tmdsdata = bios->data[offset + 4 + i * 2]; | ||
1519 | |||
1520 | bios_wr32(bios, reg + 4, tmdsdata); | ||
1521 | bios_wr32(bios, reg, tmdsaddr); | ||
1522 | } | ||
1523 | |||
1524 | return true; | ||
1525 | } | ||
1526 | |||
1527 | static bool | ||
1528 | init_cr_idx_adr_latch(struct nvbios *bios, uint16_t offset, | ||
1529 | struct init_exec *iexec) | ||
1530 | { | ||
1531 | /* | ||
1532 | * INIT_CR_INDEX_ADDRESS_LATCHED opcode: 0x51 ('Q') | ||
1533 | * | ||
1534 | * offset (8 bit): opcode | ||
1535 | * offset + 1 (8 bit): CRTC index1 | ||
1536 | * offset + 2 (8 bit): CRTC index2 | ||
1537 | * offset + 3 (8 bit): baseaddr | ||
1538 | * offset + 4 (8 bit): count | ||
1539 | * offset + 5 (8 bit): data 1 | ||
1540 | * ... | ||
1541 | * | ||
1542 | * For each of "count" address and data pairs, write "baseaddr + n" to | ||
1543 | * "CRTC index1" and "data n" to "CRTC index2" | ||
1544 | * Once complete, restore initial value read from "CRTC index1" | ||
1545 | */ | ||
1546 | uint8_t crtcindex1 = bios->data[offset + 1]; | ||
1547 | uint8_t crtcindex2 = bios->data[offset + 2]; | ||
1548 | uint8_t baseaddr = bios->data[offset + 3]; | ||
1549 | uint8_t count = bios->data[offset + 4]; | ||
1550 | uint8_t oldaddr, data; | ||
1551 | int i; | ||
1552 | |||
1553 | if (!iexec->execute) | ||
1554 | return true; | ||
1555 | |||
1556 | BIOSLOG(bios, "0x%04X: Index1: 0x%02X, Index2: 0x%02X, " | ||
1557 | "BaseAddr: 0x%02X, Count: 0x%02X\n", | ||
1558 | offset, crtcindex1, crtcindex2, baseaddr, count); | ||
1559 | |||
1560 | oldaddr = bios_idxprt_rd(bios, NV_CIO_CRX__COLOR, crtcindex1); | ||
1561 | |||
1562 | for (i = 0; i < count; i++) { | ||
1563 | bios_idxprt_wr(bios, NV_CIO_CRX__COLOR, crtcindex1, | ||
1564 | baseaddr + i); | ||
1565 | data = bios->data[offset + 5 + i]; | ||
1566 | bios_idxprt_wr(bios, NV_CIO_CRX__COLOR, crtcindex2, data); | ||
1567 | } | ||
1568 | |||
1569 | bios_idxprt_wr(bios, NV_CIO_CRX__COLOR, crtcindex1, oldaddr); | ||
1570 | |||
1571 | return true; | ||
1572 | } | ||
1573 | |||
1574 | static bool | ||
1575 | init_cr(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
1576 | { | ||
1577 | /* | ||
1578 | * INIT_CR opcode: 0x52 ('R') | ||
1579 | * | ||
1580 | * offset (8 bit): opcode | ||
1581 | * offset + 1 (8 bit): CRTC index | ||
1582 | * offset + 2 (8 bit): mask | ||
1583 | * offset + 3 (8 bit): data | ||
1584 | * | ||
1585 | * Assign the value of at "CRTC index" ANDed with mask and ORed with | ||
1586 | * data back to "CRTC index" | ||
1587 | */ | ||
1588 | |||
1589 | uint8_t crtcindex = bios->data[offset + 1]; | ||
1590 | uint8_t mask = bios->data[offset + 2]; | ||
1591 | uint8_t data = bios->data[offset + 3]; | ||
1592 | uint8_t value; | ||
1593 | |||
1594 | if (!iexec->execute) | ||
1595 | return true; | ||
1596 | |||
1597 | BIOSLOG(bios, "0x%04X: Index: 0x%02X, Mask: 0x%02X, Data: 0x%02X\n", | ||
1598 | offset, crtcindex, mask, data); | ||
1599 | |||
1600 | value = bios_idxprt_rd(bios, NV_CIO_CRX__COLOR, crtcindex) & mask; | ||
1601 | value |= data; | ||
1602 | bios_idxprt_wr(bios, NV_CIO_CRX__COLOR, crtcindex, value); | ||
1603 | |||
1604 | return true; | ||
1605 | } | ||
1606 | |||
1607 | static bool | ||
1608 | init_zm_cr(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
1609 | { | ||
1610 | /* | ||
1611 | * INIT_ZM_CR opcode: 0x53 ('S') | ||
1612 | * | ||
1613 | * offset (8 bit): opcode | ||
1614 | * offset + 1 (8 bit): CRTC index | ||
1615 | * offset + 2 (8 bit): value | ||
1616 | * | ||
1617 | * Assign "value" to CRTC register with index "CRTC index". | ||
1618 | */ | ||
1619 | |||
1620 | uint8_t crtcindex = ROM32(bios->data[offset + 1]); | ||
1621 | uint8_t data = bios->data[offset + 2]; | ||
1622 | |||
1623 | if (!iexec->execute) | ||
1624 | return true; | ||
1625 | |||
1626 | bios_idxprt_wr(bios, NV_CIO_CRX__COLOR, crtcindex, data); | ||
1627 | |||
1628 | return true; | ||
1629 | } | ||
1630 | |||
1631 | static bool | ||
1632 | init_zm_cr_group(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
1633 | { | ||
1634 | /* | ||
1635 | * INIT_ZM_CR_GROUP opcode: 0x54 ('T') | ||
1636 | * | ||
1637 | * offset (8 bit): opcode | ||
1638 | * offset + 1 (8 bit): count | ||
1639 | * offset + 2 (8 bit): CRTC index 1 | ||
1640 | * offset + 3 (8 bit): value 1 | ||
1641 | * ... | ||
1642 | * | ||
1643 | * For "count", assign "value n" to CRTC register with index | ||
1644 | * "CRTC index n". | ||
1645 | */ | ||
1646 | |||
1647 | uint8_t count = bios->data[offset + 1]; | ||
1648 | int i; | ||
1649 | |||
1650 | if (!iexec->execute) | ||
1651 | return true; | ||
1652 | |||
1653 | for (i = 0; i < count; i++) | ||
1654 | init_zm_cr(bios, offset + 2 + 2 * i - 1, iexec); | ||
1655 | |||
1656 | return true; | ||
1657 | } | ||
1658 | |||
1659 | static bool | ||
1660 | init_condition_time(struct nvbios *bios, uint16_t offset, | ||
1661 | struct init_exec *iexec) | ||
1662 | { | ||
1663 | /* | ||
1664 | * INIT_CONDITION_TIME opcode: 0x56 ('V') | ||
1665 | * | ||
1666 | * offset (8 bit): opcode | ||
1667 | * offset + 1 (8 bit): condition number | ||
1668 | * offset + 2 (8 bit): retries / 50 | ||
1669 | * | ||
1670 | * Check condition "condition number" in the condition table. | ||
1671 | * Bios code then sleeps for 2ms if the condition is not met, and | ||
1672 | * repeats up to "retries" times, but on one C51 this has proved | ||
1673 | * insufficient. In mmiotraces the driver sleeps for 20ms, so we do | ||
1674 | * this, and bail after "retries" times, or 2s, whichever is less. | ||
1675 | * If still not met after retries, clear execution flag for this table. | ||
1676 | */ | ||
1677 | |||
1678 | uint8_t cond = bios->data[offset + 1]; | ||
1679 | uint16_t retries = bios->data[offset + 2] * 50; | ||
1680 | unsigned cnt; | ||
1681 | |||
1682 | if (!iexec->execute) | ||
1683 | return true; | ||
1684 | |||
1685 | if (retries > 100) | ||
1686 | retries = 100; | ||
1687 | |||
1688 | BIOSLOG(bios, "0x%04X: Condition: 0x%02X, Retries: 0x%02X\n", | ||
1689 | offset, cond, retries); | ||
1690 | |||
1691 | if (!bios->execute) /* avoid 2s delays when "faking" execution */ | ||
1692 | retries = 1; | ||
1693 | |||
1694 | for (cnt = 0; cnt < retries; cnt++) { | ||
1695 | if (bios_condition_met(bios, offset, cond)) { | ||
1696 | BIOSLOG(bios, "0x%04X: Condition met, continuing\n", | ||
1697 | offset); | ||
1698 | break; | ||
1699 | } else { | ||
1700 | BIOSLOG(bios, "0x%04X: " | ||
1701 | "Condition not met, sleeping for 20ms\n", | ||
1702 | offset); | ||
1703 | msleep(20); | ||
1704 | } | ||
1705 | } | ||
1706 | |||
1707 | if (!bios_condition_met(bios, offset, cond)) { | ||
1708 | NV_WARN(bios->dev, | ||
1709 | "0x%04X: Condition still not met after %dms, " | ||
1710 | "skipping following opcodes\n", offset, 20 * retries); | ||
1711 | iexec->execute = false; | ||
1712 | } | ||
1713 | |||
1714 | return true; | ||
1715 | } | ||
1716 | |||
1717 | static bool | ||
1718 | init_zm_reg_sequence(struct nvbios *bios, uint16_t offset, | ||
1719 | struct init_exec *iexec) | ||
1720 | { | ||
1721 | /* | ||
1722 | * INIT_ZM_REG_SEQUENCE opcode: 0x58 ('X') | ||
1723 | * | ||
1724 | * offset (8 bit): opcode | ||
1725 | * offset + 1 (32 bit): base register | ||
1726 | * offset + 5 (8 bit): count | ||
1727 | * offset + 6 (32 bit): value 1 | ||
1728 | * ... | ||
1729 | * | ||
1730 | * Starting at offset + 6 there are "count" 32 bit values. | ||
1731 | * For "count" iterations set "base register" + 4 * current_iteration | ||
1732 | * to "value current_iteration" | ||
1733 | */ | ||
1734 | |||
1735 | uint32_t basereg = ROM32(bios->data[offset + 1]); | ||
1736 | uint32_t count = bios->data[offset + 5]; | ||
1737 | int i; | ||
1738 | |||
1739 | if (!iexec->execute) | ||
1740 | return true; | ||
1741 | |||
1742 | BIOSLOG(bios, "0x%04X: BaseReg: 0x%08X, Count: 0x%02X\n", | ||
1743 | offset, basereg, count); | ||
1744 | |||
1745 | for (i = 0; i < count; i++) { | ||
1746 | uint32_t reg = basereg + i * 4; | ||
1747 | uint32_t data = ROM32(bios->data[offset + 6 + i * 4]); | ||
1748 | |||
1749 | bios_wr32(bios, reg, data); | ||
1750 | } | ||
1751 | |||
1752 | return true; | ||
1753 | } | ||
1754 | |||
1755 | static bool | ||
1756 | init_sub_direct(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
1757 | { | ||
1758 | /* | ||
1759 | * INIT_SUB_DIRECT opcode: 0x5B ('[') | ||
1760 | * | ||
1761 | * offset (8 bit): opcode | ||
1762 | * offset + 1 (16 bit): subroutine offset (in bios) | ||
1763 | * | ||
1764 | * Calls a subroutine that will execute commands until INIT_DONE | ||
1765 | * is found. | ||
1766 | */ | ||
1767 | |||
1768 | uint16_t sub_offset = ROM16(bios->data[offset + 1]); | ||
1769 | |||
1770 | if (!iexec->execute) | ||
1771 | return true; | ||
1772 | |||
1773 | BIOSLOG(bios, "0x%04X: Executing subroutine at 0x%04X\n", | ||
1774 | offset, sub_offset); | ||
1775 | |||
1776 | parse_init_table(bios, sub_offset, iexec); | ||
1777 | |||
1778 | BIOSLOG(bios, "0x%04X: End of 0x%04X subroutine\n", offset, sub_offset); | ||
1779 | |||
1780 | return true; | ||
1781 | } | ||
1782 | |||
1783 | static bool | ||
1784 | init_copy_nv_reg(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
1785 | { | ||
1786 | /* | ||
1787 | * INIT_COPY_NV_REG opcode: 0x5F ('_') | ||
1788 | * | ||
1789 | * offset (8 bit): opcode | ||
1790 | * offset + 1 (32 bit): src reg | ||
1791 | * offset + 5 (8 bit): shift | ||
1792 | * offset + 6 (32 bit): src mask | ||
1793 | * offset + 10 (32 bit): xor | ||
1794 | * offset + 14 (32 bit): dst reg | ||
1795 | * offset + 18 (32 bit): dst mask | ||
1796 | * | ||
1797 | * Shift REGVAL("src reg") right by (signed) "shift", AND result with | ||
1798 | * "src mask", then XOR with "xor". Write this OR'd with | ||
1799 | * (REGVAL("dst reg") AND'd with "dst mask") to "dst reg" | ||
1800 | */ | ||
1801 | |||
1802 | uint32_t srcreg = *((uint32_t *)(&bios->data[offset + 1])); | ||
1803 | uint8_t shift = bios->data[offset + 5]; | ||
1804 | uint32_t srcmask = *((uint32_t *)(&bios->data[offset + 6])); | ||
1805 | uint32_t xor = *((uint32_t *)(&bios->data[offset + 10])); | ||
1806 | uint32_t dstreg = *((uint32_t *)(&bios->data[offset + 14])); | ||
1807 | uint32_t dstmask = *((uint32_t *)(&bios->data[offset + 18])); | ||
1808 | uint32_t srcvalue, dstvalue; | ||
1809 | |||
1810 | if (!iexec->execute) | ||
1811 | return true; | ||
1812 | |||
1813 | BIOSLOG(bios, "0x%04X: SrcReg: 0x%08X, Shift: 0x%02X, SrcMask: 0x%08X, " | ||
1814 | "Xor: 0x%08X, DstReg: 0x%08X, DstMask: 0x%08X\n", | ||
1815 | offset, srcreg, shift, srcmask, xor, dstreg, dstmask); | ||
1816 | |||
1817 | srcvalue = bios_rd32(bios, srcreg); | ||
1818 | |||
1819 | if (shift < 0x80) | ||
1820 | srcvalue >>= shift; | ||
1821 | else | ||
1822 | srcvalue <<= (0x100 - shift); | ||
1823 | |||
1824 | srcvalue = (srcvalue & srcmask) ^ xor; | ||
1825 | |||
1826 | dstvalue = bios_rd32(bios, dstreg) & dstmask; | ||
1827 | |||
1828 | bios_wr32(bios, dstreg, dstvalue | srcvalue); | ||
1829 | |||
1830 | return true; | ||
1831 | } | ||
1832 | |||
1833 | static bool | ||
1834 | init_zm_index_io(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
1835 | { | ||
1836 | /* | ||
1837 | * INIT_ZM_INDEX_IO opcode: 0x62 ('b') | ||
1838 | * | ||
1839 | * offset (8 bit): opcode | ||
1840 | * offset + 1 (16 bit): CRTC port | ||
1841 | * offset + 3 (8 bit): CRTC index | ||
1842 | * offset + 4 (8 bit): data | ||
1843 | * | ||
1844 | * Write "data" to index "CRTC index" of "CRTC port" | ||
1845 | */ | ||
1846 | uint16_t crtcport = ROM16(bios->data[offset + 1]); | ||
1847 | uint8_t crtcindex = bios->data[offset + 3]; | ||
1848 | uint8_t data = bios->data[offset + 4]; | ||
1849 | |||
1850 | if (!iexec->execute) | ||
1851 | return true; | ||
1852 | |||
1853 | bios_idxprt_wr(bios, crtcport, crtcindex, data); | ||
1854 | |||
1855 | return true; | ||
1856 | } | ||
1857 | |||
1858 | static bool | ||
1859 | init_compute_mem(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
1860 | { | ||
1861 | /* | ||
1862 | * INIT_COMPUTE_MEM opcode: 0x63 ('c') | ||
1863 | * | ||
1864 | * offset (8 bit): opcode | ||
1865 | * | ||
1866 | * This opcode is meant to set NV_PFB_CFG0 (0x100200) appropriately so | ||
1867 | * that the hardware can correctly calculate how much VRAM it has | ||
1868 | * (and subsequently report that value in NV_PFB_CSTATUS (0x10020C)) | ||
1869 | * | ||
1870 | * The implementation of this opcode in general consists of two parts: | ||
1871 | * 1) determination of the memory bus width | ||
1872 | * 2) determination of how many of the card's RAM pads have ICs attached | ||
1873 | * | ||
1874 | * 1) is done by a cunning combination of writes to offsets 0x1c and | ||
1875 | * 0x3c in the framebuffer, and seeing whether the written values are | ||
1876 | * read back correctly. This then affects bits 4-7 of NV_PFB_CFG0 | ||
1877 | * | ||
1878 | * 2) is done by a cunning combination of writes to an offset slightly | ||
1879 | * less than the maximum memory reported by NV_PFB_CSTATUS, then seeing | ||
1880 | * if the test pattern can be read back. This then affects bits 12-15 of | ||
1881 | * NV_PFB_CFG0 | ||
1882 | * | ||
1883 | * In this context a "cunning combination" may include multiple reads | ||
1884 | * and writes to varying locations, often alternating the test pattern | ||
1885 | * and 0, doubtless to make sure buffers are filled, residual charges | ||
1886 | * on tracks are removed etc. | ||
1887 | * | ||
1888 | * Unfortunately, the "cunning combination"s mentioned above, and the | ||
1889 | * changes to the bits in NV_PFB_CFG0 differ with nearly every bios | ||
1890 | * trace I have. | ||
1891 | * | ||
1892 | * Therefore, we cheat and assume the value of NV_PFB_CFG0 with which | ||
1893 | * we started was correct, and use that instead | ||
1894 | */ | ||
1895 | |||
1896 | /* no iexec->execute check by design */ | ||
1897 | |||
1898 | /* | ||
1899 | * This appears to be a NOP on G8x chipsets, both io logs of the VBIOS | ||
1900 | * and kmmio traces of the binary driver POSTing the card show nothing | ||
1901 | * being done for this opcode. why is it still listed in the table?! | ||
1902 | */ | ||
1903 | |||
1904 | struct drm_nouveau_private *dev_priv = bios->dev->dev_private; | ||
1905 | |||
1906 | if (dev_priv->card_type >= NV_50) | ||
1907 | return true; | ||
1908 | |||
1909 | /* | ||
1910 | * On every card I've seen, this step gets done for us earlier in | ||
1911 | * the init scripts | ||
1912 | uint8_t crdata = bios_idxprt_rd(dev, NV_VIO_SRX, 0x01); | ||
1913 | bios_idxprt_wr(dev, NV_VIO_SRX, 0x01, crdata | 0x20); | ||
1914 | */ | ||
1915 | |||
1916 | /* | ||
1917 | * This also has probably been done in the scripts, but an mmio trace of | ||
1918 | * s3 resume shows nvidia doing it anyway (unlike the NV_VIO_SRX write) | ||
1919 | */ | ||
1920 | bios_wr32(bios, NV_PFB_REFCTRL, NV_PFB_REFCTRL_VALID_1); | ||
1921 | |||
1922 | /* write back the saved configuration value */ | ||
1923 | bios_wr32(bios, NV_PFB_CFG0, bios->state.saved_nv_pfb_cfg0); | ||
1924 | |||
1925 | return true; | ||
1926 | } | ||
1927 | |||
1928 | static bool | ||
1929 | init_reset(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
1930 | { | ||
1931 | /* | ||
1932 | * INIT_RESET opcode: 0x65 ('e') | ||
1933 | * | ||
1934 | * offset (8 bit): opcode | ||
1935 | * offset + 1 (32 bit): register | ||
1936 | * offset + 5 (32 bit): value1 | ||
1937 | * offset + 9 (32 bit): value2 | ||
1938 | * | ||
1939 | * Assign "value1" to "register", then assign "value2" to "register" | ||
1940 | */ | ||
1941 | |||
1942 | uint32_t reg = ROM32(bios->data[offset + 1]); | ||
1943 | uint32_t value1 = ROM32(bios->data[offset + 5]); | ||
1944 | uint32_t value2 = ROM32(bios->data[offset + 9]); | ||
1945 | uint32_t pci_nv_19, pci_nv_20; | ||
1946 | |||
1947 | /* no iexec->execute check by design */ | ||
1948 | |||
1949 | pci_nv_19 = bios_rd32(bios, NV_PBUS_PCI_NV_19); | ||
1950 | bios_wr32(bios, NV_PBUS_PCI_NV_19, 0); | ||
1951 | bios_wr32(bios, reg, value1); | ||
1952 | |||
1953 | udelay(10); | ||
1954 | |||
1955 | bios_wr32(bios, reg, value2); | ||
1956 | bios_wr32(bios, NV_PBUS_PCI_NV_19, pci_nv_19); | ||
1957 | |||
1958 | pci_nv_20 = bios_rd32(bios, NV_PBUS_PCI_NV_20); | ||
1959 | pci_nv_20 &= ~NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED; /* 0xfffffffe */ | ||
1960 | bios_wr32(bios, NV_PBUS_PCI_NV_20, pci_nv_20); | ||
1961 | |||
1962 | return true; | ||
1963 | } | ||
1964 | |||
1965 | static bool | ||
1966 | init_configure_mem(struct nvbios *bios, uint16_t offset, | ||
1967 | struct init_exec *iexec) | ||
1968 | { | ||
1969 | /* | ||
1970 | * INIT_CONFIGURE_MEM opcode: 0x66 ('f') | ||
1971 | * | ||
1972 | * offset (8 bit): opcode | ||
1973 | * | ||
1974 | * Equivalent to INIT_DONE on bios version 3 or greater. | ||
1975 | * For early bios versions, sets up the memory registers, using values | ||
1976 | * taken from the memory init table | ||
1977 | */ | ||
1978 | |||
1979 | /* no iexec->execute check by design */ | ||
1980 | |||
1981 | uint16_t meminitoffs = bios->legacy.mem_init_tbl_ptr + MEM_INIT_SIZE * (bios_idxprt_rd(bios, NV_CIO_CRX__COLOR, NV_CIO_CRE_SCRATCH4__INDEX) >> 4); | ||
1982 | uint16_t seqtbloffs = bios->legacy.sdr_seq_tbl_ptr, meminitdata = meminitoffs + 6; | ||
1983 | uint32_t reg, data; | ||
1984 | |||
1985 | if (bios->major_version > 2) | ||
1986 | return false; | ||
1987 | |||
1988 | bios_idxprt_wr(bios, NV_VIO_SRX, NV_VIO_SR_CLOCK_INDEX, bios_idxprt_rd( | ||
1989 | bios, NV_VIO_SRX, NV_VIO_SR_CLOCK_INDEX) | 0x20); | ||
1990 | |||
1991 | if (bios->data[meminitoffs] & 1) | ||
1992 | seqtbloffs = bios->legacy.ddr_seq_tbl_ptr; | ||
1993 | |||
1994 | for (reg = ROM32(bios->data[seqtbloffs]); | ||
1995 | reg != 0xffffffff; | ||
1996 | reg = ROM32(bios->data[seqtbloffs += 4])) { | ||
1997 | |||
1998 | switch (reg) { | ||
1999 | case NV_PFB_PRE: | ||
2000 | data = NV_PFB_PRE_CMD_PRECHARGE; | ||
2001 | break; | ||
2002 | case NV_PFB_PAD: | ||
2003 | data = NV_PFB_PAD_CKE_NORMAL; | ||
2004 | break; | ||
2005 | case NV_PFB_REF: | ||
2006 | data = NV_PFB_REF_CMD_REFRESH; | ||
2007 | break; | ||
2008 | default: | ||
2009 | data = ROM32(bios->data[meminitdata]); | ||
2010 | meminitdata += 4; | ||
2011 | if (data == 0xffffffff) | ||
2012 | continue; | ||
2013 | } | ||
2014 | |||
2015 | bios_wr32(bios, reg, data); | ||
2016 | } | ||
2017 | |||
2018 | return true; | ||
2019 | } | ||
2020 | |||
2021 | static bool | ||
2022 | init_configure_clk(struct nvbios *bios, uint16_t offset, | ||
2023 | struct init_exec *iexec) | ||
2024 | { | ||
2025 | /* | ||
2026 | * INIT_CONFIGURE_CLK opcode: 0x67 ('g') | ||
2027 | * | ||
2028 | * offset (8 bit): opcode | ||
2029 | * | ||
2030 | * Equivalent to INIT_DONE on bios version 3 or greater. | ||
2031 | * For early bios versions, sets up the NVClk and MClk PLLs, using | ||
2032 | * values taken from the memory init table | ||
2033 | */ | ||
2034 | |||
2035 | /* no iexec->execute check by design */ | ||
2036 | |||
2037 | uint16_t meminitoffs = bios->legacy.mem_init_tbl_ptr + MEM_INIT_SIZE * (bios_idxprt_rd(bios, NV_CIO_CRX__COLOR, NV_CIO_CRE_SCRATCH4__INDEX) >> 4); | ||
2038 | int clock; | ||
2039 | |||
2040 | if (bios->major_version > 2) | ||
2041 | return false; | ||
2042 | |||
2043 | clock = ROM16(bios->data[meminitoffs + 4]) * 10; | ||
2044 | setPLL(bios, NV_PRAMDAC_NVPLL_COEFF, clock); | ||
2045 | |||
2046 | clock = ROM16(bios->data[meminitoffs + 2]) * 10; | ||
2047 | if (bios->data[meminitoffs] & 1) /* DDR */ | ||
2048 | clock *= 2; | ||
2049 | setPLL(bios, NV_PRAMDAC_MPLL_COEFF, clock); | ||
2050 | |||
2051 | return true; | ||
2052 | } | ||
2053 | |||
2054 | static bool | ||
2055 | init_configure_preinit(struct nvbios *bios, uint16_t offset, | ||
2056 | struct init_exec *iexec) | ||
2057 | { | ||
2058 | /* | ||
2059 | * INIT_CONFIGURE_PREINIT opcode: 0x68 ('h') | ||
2060 | * | ||
2061 | * offset (8 bit): opcode | ||
2062 | * | ||
2063 | * Equivalent to INIT_DONE on bios version 3 or greater. | ||
2064 | * For early bios versions, does early init, loading ram and crystal | ||
2065 | * configuration from straps into CR3C | ||
2066 | */ | ||
2067 | |||
2068 | /* no iexec->execute check by design */ | ||
2069 | |||
2070 | uint32_t straps = bios_rd32(bios, NV_PEXTDEV_BOOT_0); | ||
2071 | uint8_t cr3c = ((straps << 2) & 0xf0) | (straps & (1 << 6)); | ||
2072 | |||
2073 | if (bios->major_version > 2) | ||
2074 | return false; | ||
2075 | |||
2076 | bios_idxprt_wr(bios, NV_CIO_CRX__COLOR, | ||
2077 | NV_CIO_CRE_SCRATCH4__INDEX, cr3c); | ||
2078 | |||
2079 | return true; | ||
2080 | } | ||
2081 | |||
2082 | static bool | ||
2083 | init_io(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
2084 | { | ||
2085 | /* | ||
2086 | * INIT_IO opcode: 0x69 ('i') | ||
2087 | * | ||
2088 | * offset (8 bit): opcode | ||
2089 | * offset + 1 (16 bit): CRTC port | ||
2090 | * offset + 3 (8 bit): mask | ||
2091 | * offset + 4 (8 bit): data | ||
2092 | * | ||
2093 | * Assign ((IOVAL("crtc port") & "mask") | "data") to "crtc port" | ||
2094 | */ | ||
2095 | |||
2096 | struct drm_nouveau_private *dev_priv = bios->dev->dev_private; | ||
2097 | uint16_t crtcport = ROM16(bios->data[offset + 1]); | ||
2098 | uint8_t mask = bios->data[offset + 3]; | ||
2099 | uint8_t data = bios->data[offset + 4]; | ||
2100 | |||
2101 | if (!iexec->execute) | ||
2102 | return true; | ||
2103 | |||
2104 | BIOSLOG(bios, "0x%04X: Port: 0x%04X, Mask: 0x%02X, Data: 0x%02X\n", | ||
2105 | offset, crtcport, mask, data); | ||
2106 | |||
2107 | /* | ||
2108 | * I have no idea what this does, but NVIDIA do this magic sequence | ||
2109 | * in the places where this INIT_IO happens.. | ||
2110 | */ | ||
2111 | if (dev_priv->card_type >= NV_50 && crtcport == 0x3c3 && data == 1) { | ||
2112 | int i; | ||
2113 | |||
2114 | bios_wr32(bios, 0x614100, (bios_rd32( | ||
2115 | bios, 0x614100) & 0x0fffffff) | 0x00800000); | ||
2116 | |||
2117 | bios_wr32(bios, 0x00e18c, bios_rd32( | ||
2118 | bios, 0x00e18c) | 0x00020000); | ||
2119 | |||
2120 | bios_wr32(bios, 0x614900, (bios_rd32( | ||
2121 | bios, 0x614900) & 0x0fffffff) | 0x00800000); | ||
2122 | |||
2123 | bios_wr32(bios, 0x000200, bios_rd32( | ||
2124 | bios, 0x000200) & ~0x40000000); | ||
2125 | |||
2126 | mdelay(10); | ||
2127 | |||
2128 | bios_wr32(bios, 0x00e18c, bios_rd32( | ||
2129 | bios, 0x00e18c) & ~0x00020000); | ||
2130 | |||
2131 | bios_wr32(bios, 0x000200, bios_rd32( | ||
2132 | bios, 0x000200) | 0x40000000); | ||
2133 | |||
2134 | bios_wr32(bios, 0x614100, 0x00800018); | ||
2135 | bios_wr32(bios, 0x614900, 0x00800018); | ||
2136 | |||
2137 | mdelay(10); | ||
2138 | |||
2139 | bios_wr32(bios, 0x614100, 0x10000018); | ||
2140 | bios_wr32(bios, 0x614900, 0x10000018); | ||
2141 | |||
2142 | for (i = 0; i < 3; i++) | ||
2143 | bios_wr32(bios, 0x614280 + (i*0x800), bios_rd32( | ||
2144 | bios, 0x614280 + (i*0x800)) & 0xf0f0f0f0); | ||
2145 | |||
2146 | for (i = 0; i < 2; i++) | ||
2147 | bios_wr32(bios, 0x614300 + (i*0x800), bios_rd32( | ||
2148 | bios, 0x614300 + (i*0x800)) & 0xfffff0f0); | ||
2149 | |||
2150 | for (i = 0; i < 3; i++) | ||
2151 | bios_wr32(bios, 0x614380 + (i*0x800), bios_rd32( | ||
2152 | bios, 0x614380 + (i*0x800)) & 0xfffff0f0); | ||
2153 | |||
2154 | for (i = 0; i < 2; i++) | ||
2155 | bios_wr32(bios, 0x614200 + (i*0x800), bios_rd32( | ||
2156 | bios, 0x614200 + (i*0x800)) & 0xfffffff0); | ||
2157 | |||
2158 | for (i = 0; i < 2; i++) | ||
2159 | bios_wr32(bios, 0x614108 + (i*0x800), bios_rd32( | ||
2160 | bios, 0x614108 + (i*0x800)) & 0x0fffffff); | ||
2161 | return true; | ||
2162 | } | ||
2163 | |||
2164 | bios_port_wr(bios, crtcport, (bios_port_rd(bios, crtcport) & mask) | | ||
2165 | data); | ||
2166 | return true; | ||
2167 | } | ||
2168 | |||
2169 | static bool | ||
2170 | init_sub(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
2171 | { | ||
2172 | /* | ||
2173 | * INIT_SUB opcode: 0x6B ('k') | ||
2174 | * | ||
2175 | * offset (8 bit): opcode | ||
2176 | * offset + 1 (8 bit): script number | ||
2177 | * | ||
2178 | * Execute script number "script number", as a subroutine | ||
2179 | */ | ||
2180 | |||
2181 | uint8_t sub = bios->data[offset + 1]; | ||
2182 | |||
2183 | if (!iexec->execute) | ||
2184 | return true; | ||
2185 | |||
2186 | BIOSLOG(bios, "0x%04X: Calling script %d\n", offset, sub); | ||
2187 | |||
2188 | parse_init_table(bios, | ||
2189 | ROM16(bios->data[bios->init_script_tbls_ptr + sub * 2]), | ||
2190 | iexec); | ||
2191 | |||
2192 | BIOSLOG(bios, "0x%04X: End of script %d\n", offset, sub); | ||
2193 | |||
2194 | return true; | ||
2195 | } | ||
2196 | |||
2197 | static bool | ||
2198 | init_ram_condition(struct nvbios *bios, uint16_t offset, | ||
2199 | struct init_exec *iexec) | ||
2200 | { | ||
2201 | /* | ||
2202 | * INIT_RAM_CONDITION opcode: 0x6D ('m') | ||
2203 | * | ||
2204 | * offset (8 bit): opcode | ||
2205 | * offset + 1 (8 bit): mask | ||
2206 | * offset + 2 (8 bit): cmpval | ||
2207 | * | ||
2208 | * Test if (NV_PFB_BOOT_0 & "mask") equals "cmpval". | ||
2209 | * If condition not met skip subsequent opcodes until condition is | ||
2210 | * inverted (INIT_NOT), or we hit INIT_RESUME | ||
2211 | */ | ||
2212 | |||
2213 | uint8_t mask = bios->data[offset + 1]; | ||
2214 | uint8_t cmpval = bios->data[offset + 2]; | ||
2215 | uint8_t data; | ||
2216 | |||
2217 | if (!iexec->execute) | ||
2218 | return true; | ||
2219 | |||
2220 | data = bios_rd32(bios, NV_PFB_BOOT_0) & mask; | ||
2221 | |||
2222 | BIOSLOG(bios, "0x%04X: Checking if 0x%08X equals 0x%08X\n", | ||
2223 | offset, data, cmpval); | ||
2224 | |||
2225 | if (data == cmpval) | ||
2226 | BIOSLOG(bios, "0x%04X: Condition fulfilled -- continuing to execute\n", offset); | ||
2227 | else { | ||
2228 | BIOSLOG(bios, "0x%04X: Condition not fulfilled -- skipping following commands\n", offset); | ||
2229 | iexec->execute = false; | ||
2230 | } | ||
2231 | |||
2232 | return true; | ||
2233 | } | ||
2234 | |||
2235 | static bool | ||
2236 | init_nv_reg(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
2237 | { | ||
2238 | /* | ||
2239 | * INIT_NV_REG opcode: 0x6E ('n') | ||
2240 | * | ||
2241 | * offset (8 bit): opcode | ||
2242 | * offset + 1 (32 bit): register | ||
2243 | * offset + 5 (32 bit): mask | ||
2244 | * offset + 9 (32 bit): data | ||
2245 | * | ||
2246 | * Assign ((REGVAL("register") & "mask") | "data") to "register" | ||
2247 | */ | ||
2248 | |||
2249 | uint32_t reg = ROM32(bios->data[offset + 1]); | ||
2250 | uint32_t mask = ROM32(bios->data[offset + 5]); | ||
2251 | uint32_t data = ROM32(bios->data[offset + 9]); | ||
2252 | |||
2253 | if (!iexec->execute) | ||
2254 | return true; | ||
2255 | |||
2256 | BIOSLOG(bios, "0x%04X: Reg: 0x%08X, Mask: 0x%08X, Data: 0x%08X\n", | ||
2257 | offset, reg, mask, data); | ||
2258 | |||
2259 | bios_wr32(bios, reg, (bios_rd32(bios, reg) & mask) | data); | ||
2260 | |||
2261 | return true; | ||
2262 | } | ||
2263 | |||
2264 | static bool | ||
2265 | init_macro(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
2266 | { | ||
2267 | /* | ||
2268 | * INIT_MACRO opcode: 0x6F ('o') | ||
2269 | * | ||
2270 | * offset (8 bit): opcode | ||
2271 | * offset + 1 (8 bit): macro number | ||
2272 | * | ||
2273 | * Look up macro index "macro number" in the macro index table. | ||
2274 | * The macro index table entry has 1 byte for the index in the macro | ||
2275 | * table, and 1 byte for the number of times to repeat the macro. | ||
2276 | * The macro table entry has 4 bytes for the register address and | ||
2277 | * 4 bytes for the value to write to that register | ||
2278 | */ | ||
2279 | |||
2280 | uint8_t macro_index_tbl_idx = bios->data[offset + 1]; | ||
2281 | uint16_t tmp = bios->macro_index_tbl_ptr + (macro_index_tbl_idx * MACRO_INDEX_SIZE); | ||
2282 | uint8_t macro_tbl_idx = bios->data[tmp]; | ||
2283 | uint8_t count = bios->data[tmp + 1]; | ||
2284 | uint32_t reg, data; | ||
2285 | int i; | ||
2286 | |||
2287 | if (!iexec->execute) | ||
2288 | return true; | ||
2289 | |||
2290 | BIOSLOG(bios, "0x%04X: Macro: 0x%02X, MacroTableIndex: 0x%02X, " | ||
2291 | "Count: 0x%02X\n", | ||
2292 | offset, macro_index_tbl_idx, macro_tbl_idx, count); | ||
2293 | |||
2294 | for (i = 0; i < count; i++) { | ||
2295 | uint16_t macroentryptr = bios->macro_tbl_ptr + (macro_tbl_idx + i) * MACRO_SIZE; | ||
2296 | |||
2297 | reg = ROM32(bios->data[macroentryptr]); | ||
2298 | data = ROM32(bios->data[macroentryptr + 4]); | ||
2299 | |||
2300 | bios_wr32(bios, reg, data); | ||
2301 | } | ||
2302 | |||
2303 | return true; | ||
2304 | } | ||
2305 | |||
2306 | static bool | ||
2307 | init_done(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
2308 | { | ||
2309 | /* | ||
2310 | * INIT_DONE opcode: 0x71 ('q') | ||
2311 | * | ||
2312 | * offset (8 bit): opcode | ||
2313 | * | ||
2314 | * End the current script | ||
2315 | */ | ||
2316 | |||
2317 | /* mild retval abuse to stop parsing this table */ | ||
2318 | return false; | ||
2319 | } | ||
2320 | |||
2321 | static bool | ||
2322 | init_resume(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
2323 | { | ||
2324 | /* | ||
2325 | * INIT_RESUME opcode: 0x72 ('r') | ||
2326 | * | ||
2327 | * offset (8 bit): opcode | ||
2328 | * | ||
2329 | * End the current execute / no-execute condition | ||
2330 | */ | ||
2331 | |||
2332 | if (iexec->execute) | ||
2333 | return true; | ||
2334 | |||
2335 | iexec->execute = true; | ||
2336 | BIOSLOG(bios, "0x%04X: ---- Executing following commands ----\n", offset); | ||
2337 | |||
2338 | return true; | ||
2339 | } | ||
2340 | |||
2341 | static bool | ||
2342 | init_time(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
2343 | { | ||
2344 | /* | ||
2345 | * INIT_TIME opcode: 0x74 ('t') | ||
2346 | * | ||
2347 | * offset (8 bit): opcode | ||
2348 | * offset + 1 (16 bit): time | ||
2349 | * | ||
2350 | * Sleep for "time" microseconds. | ||
2351 | */ | ||
2352 | |||
2353 | unsigned time = ROM16(bios->data[offset + 1]); | ||
2354 | |||
2355 | if (!iexec->execute) | ||
2356 | return true; | ||
2357 | |||
2358 | BIOSLOG(bios, "0x%04X: Sleeping for 0x%04X microseconds\n", | ||
2359 | offset, time); | ||
2360 | |||
2361 | if (time < 1000) | ||
2362 | udelay(time); | ||
2363 | else | ||
2364 | msleep((time + 900) / 1000); | ||
2365 | |||
2366 | return true; | ||
2367 | } | ||
2368 | |||
2369 | static bool | ||
2370 | init_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
2371 | { | ||
2372 | /* | ||
2373 | * INIT_CONDITION opcode: 0x75 ('u') | ||
2374 | * | ||
2375 | * offset (8 bit): opcode | ||
2376 | * offset + 1 (8 bit): condition number | ||
2377 | * | ||
2378 | * Check condition "condition number" in the condition table. | ||
2379 | * If condition not met skip subsequent opcodes until condition is | ||
2380 | * inverted (INIT_NOT), or we hit INIT_RESUME | ||
2381 | */ | ||
2382 | |||
2383 | uint8_t cond = bios->data[offset + 1]; | ||
2384 | |||
2385 | if (!iexec->execute) | ||
2386 | return true; | ||
2387 | |||
2388 | BIOSLOG(bios, "0x%04X: Condition: 0x%02X\n", offset, cond); | ||
2389 | |||
2390 | if (bios_condition_met(bios, offset, cond)) | ||
2391 | BIOSLOG(bios, "0x%04X: Condition fulfilled -- continuing to execute\n", offset); | ||
2392 | else { | ||
2393 | BIOSLOG(bios, "0x%04X: Condition not fulfilled -- skipping following commands\n", offset); | ||
2394 | iexec->execute = false; | ||
2395 | } | ||
2396 | |||
2397 | return true; | ||
2398 | } | ||
2399 | |||
2400 | static bool | ||
2401 | init_io_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
2402 | { | ||
2403 | /* | ||
2404 | * INIT_IO_CONDITION opcode: 0x76 | ||
2405 | * | ||
2406 | * offset (8 bit): opcode | ||
2407 | * offset + 1 (8 bit): condition number | ||
2408 | * | ||
2409 | * Check condition "condition number" in the io condition table. | ||
2410 | * If condition not met skip subsequent opcodes until condition is | ||
2411 | * inverted (INIT_NOT), or we hit INIT_RESUME | ||
2412 | */ | ||
2413 | |||
2414 | uint8_t cond = bios->data[offset + 1]; | ||
2415 | |||
2416 | if (!iexec->execute) | ||
2417 | return true; | ||
2418 | |||
2419 | BIOSLOG(bios, "0x%04X: IO condition: 0x%02X\n", offset, cond); | ||
2420 | |||
2421 | if (io_condition_met(bios, offset, cond)) | ||
2422 | BIOSLOG(bios, "0x%04X: Condition fulfilled -- continuing to execute\n", offset); | ||
2423 | else { | ||
2424 | BIOSLOG(bios, "0x%04X: Condition not fulfilled -- skipping following commands\n", offset); | ||
2425 | iexec->execute = false; | ||
2426 | } | ||
2427 | |||
2428 | return true; | ||
2429 | } | ||
2430 | |||
2431 | static bool | ||
2432 | init_index_io(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
2433 | { | ||
2434 | /* | ||
2435 | * INIT_INDEX_IO opcode: 0x78 ('x') | ||
2436 | * | ||
2437 | * offset (8 bit): opcode | ||
2438 | * offset + 1 (16 bit): CRTC port | ||
2439 | * offset + 3 (8 bit): CRTC index | ||
2440 | * offset + 4 (8 bit): mask | ||
2441 | * offset + 5 (8 bit): data | ||
2442 | * | ||
2443 | * Read value at index "CRTC index" on "CRTC port", AND with "mask", | ||
2444 | * OR with "data", write-back | ||
2445 | */ | ||
2446 | |||
2447 | uint16_t crtcport = ROM16(bios->data[offset + 1]); | ||
2448 | uint8_t crtcindex = bios->data[offset + 3]; | ||
2449 | uint8_t mask = bios->data[offset + 4]; | ||
2450 | uint8_t data = bios->data[offset + 5]; | ||
2451 | uint8_t value; | ||
2452 | |||
2453 | if (!iexec->execute) | ||
2454 | return true; | ||
2455 | |||
2456 | BIOSLOG(bios, "0x%04X: Port: 0x%04X, Index: 0x%02X, Mask: 0x%02X, " | ||
2457 | "Data: 0x%02X\n", | ||
2458 | offset, crtcport, crtcindex, mask, data); | ||
2459 | |||
2460 | value = (bios_idxprt_rd(bios, crtcport, crtcindex) & mask) | data; | ||
2461 | bios_idxprt_wr(bios, crtcport, crtcindex, value); | ||
2462 | |||
2463 | return true; | ||
2464 | } | ||
2465 | |||
2466 | static bool | ||
2467 | init_pll(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
2468 | { | ||
2469 | /* | ||
2470 | * INIT_PLL opcode: 0x79 ('y') | ||
2471 | * | ||
2472 | * offset (8 bit): opcode | ||
2473 | * offset + 1 (32 bit): register | ||
2474 | * offset + 5 (16 bit): freq | ||
2475 | * | ||
2476 | * Set PLL register "register" to coefficients for frequency (10kHz) | ||
2477 | * "freq" | ||
2478 | */ | ||
2479 | |||
2480 | uint32_t reg = ROM32(bios->data[offset + 1]); | ||
2481 | uint16_t freq = ROM16(bios->data[offset + 5]); | ||
2482 | |||
2483 | if (!iexec->execute) | ||
2484 | return true; | ||
2485 | |||
2486 | BIOSLOG(bios, "0x%04X: Reg: 0x%08X, Freq: %d0kHz\n", offset, reg, freq); | ||
2487 | |||
2488 | setPLL(bios, reg, freq * 10); | ||
2489 | |||
2490 | return true; | ||
2491 | } | ||
2492 | |||
2493 | static bool | ||
2494 | init_zm_reg(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
2495 | { | ||
2496 | /* | ||
2497 | * INIT_ZM_REG opcode: 0x7A ('z') | ||
2498 | * | ||
2499 | * offset (8 bit): opcode | ||
2500 | * offset + 1 (32 bit): register | ||
2501 | * offset + 5 (32 bit): value | ||
2502 | * | ||
2503 | * Assign "value" to "register" | ||
2504 | */ | ||
2505 | |||
2506 | uint32_t reg = ROM32(bios->data[offset + 1]); | ||
2507 | uint32_t value = ROM32(bios->data[offset + 5]); | ||
2508 | |||
2509 | if (!iexec->execute) | ||
2510 | return true; | ||
2511 | |||
2512 | if (reg == 0x000200) | ||
2513 | value |= 1; | ||
2514 | |||
2515 | bios_wr32(bios, reg, value); | ||
2516 | |||
2517 | return true; | ||
2518 | } | ||
2519 | |||
2520 | static bool | ||
2521 | init_ram_restrict_pll(struct nvbios *bios, uint16_t offset, | ||
2522 | struct init_exec *iexec) | ||
2523 | { | ||
2524 | /* | ||
2525 | * INIT_RAM_RESTRICT_PLL opcode: 0x87 ('') | ||
2526 | * | ||
2527 | * offset (8 bit): opcode | ||
2528 | * offset + 1 (8 bit): PLL type | ||
2529 | * offset + 2 (32 bit): frequency 0 | ||
2530 | * | ||
2531 | * Uses the RAMCFG strap of PEXTDEV_BOOT as an index into the table at | ||
2532 | * ram_restrict_table_ptr. The value read from there is used to select | ||
2533 | * a frequency from the table starting at 'frequency 0' to be | ||
2534 | * programmed into the PLL corresponding to 'type'. | ||
2535 | * | ||
2536 | * The PLL limits table on cards using this opcode has a mapping of | ||
2537 | * 'type' to the relevant registers. | ||
2538 | */ | ||
2539 | |||
2540 | struct drm_device *dev = bios->dev; | ||
2541 | uint32_t strap = (bios_rd32(bios, NV_PEXTDEV_BOOT_0) & 0x0000003c) >> 2; | ||
2542 | uint8_t index = bios->data[bios->ram_restrict_tbl_ptr + strap]; | ||
2543 | uint8_t type = bios->data[offset + 1]; | ||
2544 | uint32_t freq = ROM32(bios->data[offset + 2 + (index * 4)]); | ||
2545 | uint8_t *pll_limits = &bios->data[bios->pll_limit_tbl_ptr], *entry; | ||
2546 | int i; | ||
2547 | |||
2548 | if (!iexec->execute) | ||
2549 | return true; | ||
2550 | |||
2551 | if (!bios->pll_limit_tbl_ptr || (pll_limits[0] & 0xf0) != 0x30) { | ||
2552 | NV_ERROR(dev, "PLL limits table not version 3.x\n"); | ||
2553 | return true; /* deliberate, allow default clocks to remain */ | ||
2554 | } | ||
2555 | |||
2556 | entry = pll_limits + pll_limits[1]; | ||
2557 | for (i = 0; i < pll_limits[3]; i++, entry += pll_limits[2]) { | ||
2558 | if (entry[0] == type) { | ||
2559 | uint32_t reg = ROM32(entry[3]); | ||
2560 | |||
2561 | BIOSLOG(bios, "0x%04X: " | ||
2562 | "Type %02x Reg 0x%08x Freq %dKHz\n", | ||
2563 | offset, type, reg, freq); | ||
2564 | |||
2565 | setPLL(bios, reg, freq); | ||
2566 | return true; | ||
2567 | } | ||
2568 | } | ||
2569 | |||
2570 | NV_ERROR(dev, "PLL type 0x%02x not found in PLL limits table", type); | ||
2571 | return true; | ||
2572 | } | ||
2573 | |||
2574 | static bool | ||
2575 | init_8c(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
2576 | { | ||
2577 | /* | ||
2578 | * INIT_8C opcode: 0x8C ('') | ||
2579 | * | ||
2580 | * NOP so far.... | ||
2581 | * | ||
2582 | */ | ||
2583 | |||
2584 | return true; | ||
2585 | } | ||
2586 | |||
2587 | static bool | ||
2588 | init_8d(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
2589 | { | ||
2590 | /* | ||
2591 | * INIT_8D opcode: 0x8D ('') | ||
2592 | * | ||
2593 | * NOP so far.... | ||
2594 | * | ||
2595 | */ | ||
2596 | |||
2597 | return true; | ||
2598 | } | ||
2599 | |||
2600 | static bool | ||
2601 | init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
2602 | { | ||
2603 | /* | ||
2604 | * INIT_GPIO opcode: 0x8E ('') | ||
2605 | * | ||
2606 | * offset (8 bit): opcode | ||
2607 | * | ||
2608 | * Loop over all entries in the DCB GPIO table, and initialise | ||
2609 | * each GPIO according to various values listed in each entry | ||
2610 | */ | ||
2611 | |||
2612 | const uint32_t nv50_gpio_reg[4] = { 0xe104, 0xe108, 0xe280, 0xe284 }; | ||
2613 | const uint32_t nv50_gpio_ctl[2] = { 0xe100, 0xe28c }; | ||
2614 | const uint8_t *gpio_table = &bios->data[bios->bdcb.gpio_table_ptr]; | ||
2615 | const uint8_t *gpio_entry; | ||
2616 | int i; | ||
2617 | |||
2618 | if (bios->bdcb.version != 0x40) { | ||
2619 | NV_ERROR(bios->dev, "DCB table not version 4.0\n"); | ||
2620 | return false; | ||
2621 | } | ||
2622 | |||
2623 | if (!bios->bdcb.gpio_table_ptr) { | ||
2624 | NV_WARN(bios->dev, "Invalid pointer to INIT_8E table\n"); | ||
2625 | return false; | ||
2626 | } | ||
2627 | |||
2628 | gpio_entry = gpio_table + gpio_table[1]; | ||
2629 | for (i = 0; i < gpio_table[2]; i++, gpio_entry += gpio_table[3]) { | ||
2630 | uint32_t entry = ROM32(gpio_entry[0]), r, s, v; | ||
2631 | int line = (entry & 0x0000001f); | ||
2632 | |||
2633 | BIOSLOG(bios, "0x%04X: Entry: 0x%08X\n", offset, entry); | ||
2634 | |||
2635 | if ((entry & 0x0000ff00) == 0x0000ff00) | ||
2636 | continue; | ||
2637 | |||
2638 | r = nv50_gpio_reg[line >> 3]; | ||
2639 | s = (line & 0x07) << 2; | ||
2640 | v = bios_rd32(bios, r) & ~(0x00000003 << s); | ||
2641 | if (entry & 0x01000000) | ||
2642 | v |= (((entry & 0x60000000) >> 29) ^ 2) << s; | ||
2643 | else | ||
2644 | v |= (((entry & 0x18000000) >> 27) ^ 2) << s; | ||
2645 | bios_wr32(bios, r, v); | ||
2646 | |||
2647 | r = nv50_gpio_ctl[line >> 4]; | ||
2648 | s = (line & 0x0f); | ||
2649 | v = bios_rd32(bios, r) & ~(0x00010001 << s); | ||
2650 | switch ((entry & 0x06000000) >> 25) { | ||
2651 | case 1: | ||
2652 | v |= (0x00000001 << s); | ||
2653 | break; | ||
2654 | case 2: | ||
2655 | v |= (0x00010000 << s); | ||
2656 | break; | ||
2657 | default: | ||
2658 | break; | ||
2659 | } | ||
2660 | bios_wr32(bios, r, v); | ||
2661 | } | ||
2662 | |||
2663 | return true; | ||
2664 | } | ||
2665 | |||
2666 | /* hack to avoid moving the itbl_entry array before this function */ | ||
2667 | int init_ram_restrict_zm_reg_group_blocklen; | ||
2668 | |||
2669 | static bool | ||
2670 | init_ram_restrict_zm_reg_group(struct nvbios *bios, uint16_t offset, | ||
2671 | struct init_exec *iexec) | ||
2672 | { | ||
2673 | /* | ||
2674 | * INIT_RAM_RESTRICT_ZM_REG_GROUP opcode: 0x8F ('') | ||
2675 | * | ||
2676 | * offset (8 bit): opcode | ||
2677 | * offset + 1 (32 bit): reg | ||
2678 | * offset + 5 (8 bit): regincrement | ||
2679 | * offset + 6 (8 bit): count | ||
2680 | * offset + 7 (32 bit): value 1,1 | ||
2681 | * ... | ||
2682 | * | ||
2683 | * Use the RAMCFG strap of PEXTDEV_BOOT as an index into the table at | ||
2684 | * ram_restrict_table_ptr. The value read from here is 'n', and | ||
2685 | * "value 1,n" gets written to "reg". This repeats "count" times and on | ||
2686 | * each iteration 'm', "reg" increases by "regincrement" and | ||
2687 | * "value m,n" is used. The extent of n is limited by a number read | ||
2688 | * from the 'M' BIT table, herein called "blocklen" | ||
2689 | */ | ||
2690 | |||
2691 | uint32_t reg = ROM32(bios->data[offset + 1]); | ||
2692 | uint8_t regincrement = bios->data[offset + 5]; | ||
2693 | uint8_t count = bios->data[offset + 6]; | ||
2694 | uint32_t strap_ramcfg, data; | ||
2695 | uint16_t blocklen; | ||
2696 | uint8_t index; | ||
2697 | int i; | ||
2698 | |||
2699 | /* previously set by 'M' BIT table */ | ||
2700 | blocklen = init_ram_restrict_zm_reg_group_blocklen; | ||
2701 | |||
2702 | if (!iexec->execute) | ||
2703 | return true; | ||
2704 | |||
2705 | if (!blocklen) { | ||
2706 | NV_ERROR(bios->dev, | ||
2707 | "0x%04X: Zero block length - has the M table " | ||
2708 | "been parsed?\n", offset); | ||
2709 | return false; | ||
2710 | } | ||
2711 | |||
2712 | strap_ramcfg = (bios_rd32(bios, NV_PEXTDEV_BOOT_0) >> 2) & 0xf; | ||
2713 | index = bios->data[bios->ram_restrict_tbl_ptr + strap_ramcfg]; | ||
2714 | |||
2715 | BIOSLOG(bios, "0x%04X: Reg: 0x%08X, RegIncrement: 0x%02X, " | ||
2716 | "Count: 0x%02X, StrapRamCfg: 0x%02X, Index: 0x%02X\n", | ||
2717 | offset, reg, regincrement, count, strap_ramcfg, index); | ||
2718 | |||
2719 | for (i = 0; i < count; i++) { | ||
2720 | data = ROM32(bios->data[offset + 7 + index * 4 + blocklen * i]); | ||
2721 | |||
2722 | bios_wr32(bios, reg, data); | ||
2723 | |||
2724 | reg += regincrement; | ||
2725 | } | ||
2726 | |||
2727 | return true; | ||
2728 | } | ||
2729 | |||
2730 | static bool | ||
2731 | init_copy_zm_reg(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
2732 | { | ||
2733 | /* | ||
2734 | * INIT_COPY_ZM_REG opcode: 0x90 ('') | ||
2735 | * | ||
2736 | * offset (8 bit): opcode | ||
2737 | * offset + 1 (32 bit): src reg | ||
2738 | * offset + 5 (32 bit): dst reg | ||
2739 | * | ||
2740 | * Put contents of "src reg" into "dst reg" | ||
2741 | */ | ||
2742 | |||
2743 | uint32_t srcreg = ROM32(bios->data[offset + 1]); | ||
2744 | uint32_t dstreg = ROM32(bios->data[offset + 5]); | ||
2745 | |||
2746 | if (!iexec->execute) | ||
2747 | return true; | ||
2748 | |||
2749 | bios_wr32(bios, dstreg, bios_rd32(bios, srcreg)); | ||
2750 | |||
2751 | return true; | ||
2752 | } | ||
2753 | |||
2754 | static bool | ||
2755 | init_zm_reg_group_addr_latched(struct nvbios *bios, uint16_t offset, | ||
2756 | struct init_exec *iexec) | ||
2757 | { | ||
2758 | /* | ||
2759 | * INIT_ZM_REG_GROUP_ADDRESS_LATCHED opcode: 0x91 ('') | ||
2760 | * | ||
2761 | * offset (8 bit): opcode | ||
2762 | * offset + 1 (32 bit): dst reg | ||
2763 | * offset + 5 (8 bit): count | ||
2764 | * offset + 6 (32 bit): data 1 | ||
2765 | * ... | ||
2766 | * | ||
2767 | * For each of "count" values write "data n" to "dst reg" | ||
2768 | */ | ||
2769 | |||
2770 | uint32_t reg = ROM32(bios->data[offset + 1]); | ||
2771 | uint8_t count = bios->data[offset + 5]; | ||
2772 | int i; | ||
2773 | |||
2774 | if (!iexec->execute) | ||
2775 | return true; | ||
2776 | |||
2777 | for (i = 0; i < count; i++) { | ||
2778 | uint32_t data = ROM32(bios->data[offset + 6 + 4 * i]); | ||
2779 | bios_wr32(bios, reg, data); | ||
2780 | } | ||
2781 | |||
2782 | return true; | ||
2783 | } | ||
2784 | |||
2785 | static bool | ||
2786 | init_reserved(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
2787 | { | ||
2788 | /* | ||
2789 | * INIT_RESERVED opcode: 0x92 ('') | ||
2790 | * | ||
2791 | * offset (8 bit): opcode | ||
2792 | * | ||
2793 | * Seemingly does nothing | ||
2794 | */ | ||
2795 | |||
2796 | return true; | ||
2797 | } | ||
2798 | |||
2799 | static bool | ||
2800 | init_96(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
2801 | { | ||
2802 | /* | ||
2803 | * INIT_96 opcode: 0x96 ('') | ||
2804 | * | ||
2805 | * offset (8 bit): opcode | ||
2806 | * offset + 1 (32 bit): sreg | ||
2807 | * offset + 5 (8 bit): sshift | ||
2808 | * offset + 6 (8 bit): smask | ||
2809 | * offset + 7 (8 bit): index | ||
2810 | * offset + 8 (32 bit): reg | ||
2811 | * offset + 12 (32 bit): mask | ||
2812 | * offset + 16 (8 bit): shift | ||
2813 | * | ||
2814 | */ | ||
2815 | |||
2816 | uint16_t xlatptr = bios->init96_tbl_ptr + (bios->data[offset + 7] * 2); | ||
2817 | uint32_t reg = ROM32(bios->data[offset + 8]); | ||
2818 | uint32_t mask = ROM32(bios->data[offset + 12]); | ||
2819 | uint32_t val; | ||
2820 | |||
2821 | val = bios_rd32(bios, ROM32(bios->data[offset + 1])); | ||
2822 | if (bios->data[offset + 5] < 0x80) | ||
2823 | val >>= bios->data[offset + 5]; | ||
2824 | else | ||
2825 | val <<= (0x100 - bios->data[offset + 5]); | ||
2826 | val &= bios->data[offset + 6]; | ||
2827 | |||
2828 | val = bios->data[ROM16(bios->data[xlatptr]) + val]; | ||
2829 | val <<= bios->data[offset + 16]; | ||
2830 | |||
2831 | if (!iexec->execute) | ||
2832 | return true; | ||
2833 | |||
2834 | bios_wr32(bios, reg, (bios_rd32(bios, reg) & mask) | val); | ||
2835 | return true; | ||
2836 | } | ||
2837 | |||
2838 | static bool | ||
2839 | init_97(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
2840 | { | ||
2841 | /* | ||
2842 | * INIT_97 opcode: 0x97 ('') | ||
2843 | * | ||
2844 | * offset (8 bit): opcode | ||
2845 | * offset + 1 (32 bit): register | ||
2846 | * offset + 5 (32 bit): mask | ||
2847 | * offset + 9 (32 bit): value | ||
2848 | * | ||
2849 | * Adds "value" to "register" preserving the fields specified | ||
2850 | * by "mask" | ||
2851 | */ | ||
2852 | |||
2853 | uint32_t reg = ROM32(bios->data[offset + 1]); | ||
2854 | uint32_t mask = ROM32(bios->data[offset + 5]); | ||
2855 | uint32_t add = ROM32(bios->data[offset + 9]); | ||
2856 | uint32_t val; | ||
2857 | |||
2858 | val = bios_rd32(bios, reg); | ||
2859 | val = (val & mask) | ((val + add) & ~mask); | ||
2860 | |||
2861 | if (!iexec->execute) | ||
2862 | return true; | ||
2863 | |||
2864 | bios_wr32(bios, reg, val); | ||
2865 | return true; | ||
2866 | } | ||
2867 | |||
2868 | static bool | ||
2869 | init_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
2870 | { | ||
2871 | /* | ||
2872 | * INIT_AUXCH opcode: 0x98 ('') | ||
2873 | * | ||
2874 | * offset (8 bit): opcode | ||
2875 | * offset + 1 (32 bit): address | ||
2876 | * offset + 5 (8 bit): count | ||
2877 | * offset + 6 (8 bit): mask 0 | ||
2878 | * offset + 7 (8 bit): data 0 | ||
2879 | * ... | ||
2880 | * | ||
2881 | */ | ||
2882 | |||
2883 | struct drm_device *dev = bios->dev; | ||
2884 | struct nouveau_i2c_chan *auxch; | ||
2885 | uint32_t addr = ROM32(bios->data[offset + 1]); | ||
2886 | uint8_t len = bios->data[offset + 5]; | ||
2887 | int ret, i; | ||
2888 | |||
2889 | if (!bios->display.output) { | ||
2890 | NV_ERROR(dev, "INIT_AUXCH: no active output\n"); | ||
2891 | return false; | ||
2892 | } | ||
2893 | |||
2894 | auxch = init_i2c_device_find(dev, bios->display.output->i2c_index); | ||
2895 | if (!auxch) { | ||
2896 | NV_ERROR(dev, "INIT_AUXCH: couldn't get auxch %d\n", | ||
2897 | bios->display.output->i2c_index); | ||
2898 | return false; | ||
2899 | } | ||
2900 | |||
2901 | if (!iexec->execute) | ||
2902 | return true; | ||
2903 | |||
2904 | offset += 6; | ||
2905 | for (i = 0; i < len; i++, offset += 2) { | ||
2906 | uint8_t data; | ||
2907 | |||
2908 | ret = nouveau_dp_auxch(auxch, 9, addr, &data, 1); | ||
2909 | if (ret) { | ||
2910 | NV_ERROR(dev, "INIT_AUXCH: rd auxch fail %d\n", ret); | ||
2911 | return false; | ||
2912 | } | ||
2913 | |||
2914 | data &= bios->data[offset + 0]; | ||
2915 | data |= bios->data[offset + 1]; | ||
2916 | |||
2917 | ret = nouveau_dp_auxch(auxch, 8, addr, &data, 1); | ||
2918 | if (ret) { | ||
2919 | NV_ERROR(dev, "INIT_AUXCH: wr auxch fail %d\n", ret); | ||
2920 | return false; | ||
2921 | } | ||
2922 | } | ||
2923 | |||
2924 | return true; | ||
2925 | } | ||
2926 | |||
2927 | static bool | ||
2928 | init_zm_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) | ||
2929 | { | ||
2930 | /* | ||
2931 | * INIT_ZM_AUXCH opcode: 0x99 ('') | ||
2932 | * | ||
2933 | * offset (8 bit): opcode | ||
2934 | * offset + 1 (32 bit): address | ||
2935 | * offset + 5 (8 bit): count | ||
2936 | * offset + 6 (8 bit): data 0 | ||
2937 | * ... | ||
2938 | * | ||
2939 | */ | ||
2940 | |||
2941 | struct drm_device *dev = bios->dev; | ||
2942 | struct nouveau_i2c_chan *auxch; | ||
2943 | uint32_t addr = ROM32(bios->data[offset + 1]); | ||
2944 | uint8_t len = bios->data[offset + 5]; | ||
2945 | int ret, i; | ||
2946 | |||
2947 | if (!bios->display.output) { | ||
2948 | NV_ERROR(dev, "INIT_ZM_AUXCH: no active output\n"); | ||
2949 | return false; | ||
2950 | } | ||
2951 | |||
2952 | auxch = init_i2c_device_find(dev, bios->display.output->i2c_index); | ||
2953 | if (!auxch) { | ||
2954 | NV_ERROR(dev, "INIT_ZM_AUXCH: couldn't get auxch %d\n", | ||
2955 | bios->display.output->i2c_index); | ||
2956 | return false; | ||
2957 | } | ||
2958 | |||
2959 | if (!iexec->execute) | ||
2960 | return true; | ||
2961 | |||
2962 | offset += 6; | ||
2963 | for (i = 0; i < len; i++, offset++) { | ||
2964 | ret = nouveau_dp_auxch(auxch, 8, addr, &bios->data[offset], 1); | ||
2965 | if (ret) { | ||
2966 | NV_ERROR(dev, "INIT_ZM_AUXCH: wr auxch fail %d\n", ret); | ||
2967 | return false; | ||
2968 | } | ||
2969 | } | ||
2970 | |||
2971 | return true; | ||
2972 | } | ||
2973 | |||
2974 | static struct init_tbl_entry itbl_entry[] = { | ||
2975 | /* command name , id , length , offset , mult , command handler */ | ||
2976 | /* INIT_PROG (0x31, 15, 10, 4) removed due to no example of use */ | ||
2977 | { "INIT_IO_RESTRICT_PROG" , 0x32, 11 , 6 , 4 , init_io_restrict_prog }, | ||
2978 | { "INIT_REPEAT" , 0x33, 2 , 0 , 0 , init_repeat }, | ||
2979 | { "INIT_IO_RESTRICT_PLL" , 0x34, 12 , 7 , 2 , init_io_restrict_pll }, | ||
2980 | { "INIT_END_REPEAT" , 0x36, 1 , 0 , 0 , init_end_repeat }, | ||
2981 | { "INIT_COPY" , 0x37, 11 , 0 , 0 , init_copy }, | ||
2982 | { "INIT_NOT" , 0x38, 1 , 0 , 0 , init_not }, | ||
2983 | { "INIT_IO_FLAG_CONDITION" , 0x39, 2 , 0 , 0 , init_io_flag_condition }, | ||
2984 | { "INIT_INDEX_ADDRESS_LATCHED" , 0x49, 18 , 17 , 2 , init_idx_addr_latched }, | ||
2985 | { "INIT_IO_RESTRICT_PLL2" , 0x4A, 11 , 6 , 4 , init_io_restrict_pll2 }, | ||
2986 | { "INIT_PLL2" , 0x4B, 9 , 0 , 0 , init_pll2 }, | ||
2987 | { "INIT_I2C_BYTE" , 0x4C, 4 , 3 , 3 , init_i2c_byte }, | ||
2988 | { "INIT_ZM_I2C_BYTE" , 0x4D, 4 , 3 , 2 , init_zm_i2c_byte }, | ||
2989 | { "INIT_ZM_I2C" , 0x4E, 4 , 3 , 1 , init_zm_i2c }, | ||
2990 | { "INIT_TMDS" , 0x4F, 5 , 0 , 0 , init_tmds }, | ||
2991 | { "INIT_ZM_TMDS_GROUP" , 0x50, 3 , 2 , 2 , init_zm_tmds_group }, | ||
2992 | { "INIT_CR_INDEX_ADDRESS_LATCHED" , 0x51, 5 , 4 , 1 , init_cr_idx_adr_latch }, | ||
2993 | { "INIT_CR" , 0x52, 4 , 0 , 0 , init_cr }, | ||
2994 | { "INIT_ZM_CR" , 0x53, 3 , 0 , 0 , init_zm_cr }, | ||
2995 | { "INIT_ZM_CR_GROUP" , 0x54, 2 , 1 , 2 , init_zm_cr_group }, | ||
2996 | { "INIT_CONDITION_TIME" , 0x56, 3 , 0 , 0 , init_condition_time }, | ||
2997 | { "INIT_ZM_REG_SEQUENCE" , 0x58, 6 , 5 , 4 , init_zm_reg_sequence }, | ||
2998 | /* INIT_INDIRECT_REG (0x5A, 7, 0, 0) removed due to no example of use */ | ||
2999 | { "INIT_SUB_DIRECT" , 0x5B, 3 , 0 , 0 , init_sub_direct }, | ||
3000 | { "INIT_COPY_NV_REG" , 0x5F, 22 , 0 , 0 , init_copy_nv_reg }, | ||
3001 | { "INIT_ZM_INDEX_IO" , 0x62, 5 , 0 , 0 , init_zm_index_io }, | ||
3002 | { "INIT_COMPUTE_MEM" , 0x63, 1 , 0 , 0 , init_compute_mem }, | ||
3003 | { "INIT_RESET" , 0x65, 13 , 0 , 0 , init_reset }, | ||
3004 | { "INIT_CONFIGURE_MEM" , 0x66, 1 , 0 , 0 , init_configure_mem }, | ||
3005 | { "INIT_CONFIGURE_CLK" , 0x67, 1 , 0 , 0 , init_configure_clk }, | ||
3006 | { "INIT_CONFIGURE_PREINIT" , 0x68, 1 , 0 , 0 , init_configure_preinit }, | ||
3007 | { "INIT_IO" , 0x69, 5 , 0 , 0 , init_io }, | ||
3008 | { "INIT_SUB" , 0x6B, 2 , 0 , 0 , init_sub }, | ||
3009 | { "INIT_RAM_CONDITION" , 0x6D, 3 , 0 , 0 , init_ram_condition }, | ||
3010 | { "INIT_NV_REG" , 0x6E, 13 , 0 , 0 , init_nv_reg }, | ||
3011 | { "INIT_MACRO" , 0x6F, 2 , 0 , 0 , init_macro }, | ||
3012 | { "INIT_DONE" , 0x71, 1 , 0 , 0 , init_done }, | ||
3013 | { "INIT_RESUME" , 0x72, 1 , 0 , 0 , init_resume }, | ||
3014 | /* INIT_RAM_CONDITION2 (0x73, 9, 0, 0) removed due to no example of use */ | ||
3015 | { "INIT_TIME" , 0x74, 3 , 0 , 0 , init_time }, | ||
3016 | { "INIT_CONDITION" , 0x75, 2 , 0 , 0 , init_condition }, | ||
3017 | { "INIT_IO_CONDITION" , 0x76, 2 , 0 , 0 , init_io_condition }, | ||
3018 | { "INIT_INDEX_IO" , 0x78, 6 , 0 , 0 , init_index_io }, | ||
3019 | { "INIT_PLL" , 0x79, 7 , 0 , 0 , init_pll }, | ||
3020 | { "INIT_ZM_REG" , 0x7A, 9 , 0 , 0 , init_zm_reg }, | ||
3021 | /* INIT_RAM_RESTRICT_PLL's length is adjusted by the BIT M table */ | ||
3022 | { "INIT_RAM_RESTRICT_PLL" , 0x87, 2 , 0 , 0 , init_ram_restrict_pll }, | ||
3023 | { "INIT_8C" , 0x8C, 1 , 0 , 0 , init_8c }, | ||
3024 | { "INIT_8D" , 0x8D, 1 , 0 , 0 , init_8d }, | ||
3025 | { "INIT_GPIO" , 0x8E, 1 , 0 , 0 , init_gpio }, | ||
3026 | /* INIT_RAM_RESTRICT_ZM_REG_GROUP's mult is loaded by M table in BIT */ | ||
3027 | { "INIT_RAM_RESTRICT_ZM_REG_GROUP" , 0x8F, 7 , 6 , 0 , init_ram_restrict_zm_reg_group }, | ||
3028 | { "INIT_COPY_ZM_REG" , 0x90, 9 , 0 , 0 , init_copy_zm_reg }, | ||
3029 | { "INIT_ZM_REG_GROUP_ADDRESS_LATCHED" , 0x91, 6 , 5 , 4 , init_zm_reg_group_addr_latched }, | ||
3030 | { "INIT_RESERVED" , 0x92, 1 , 0 , 0 , init_reserved }, | ||
3031 | { "INIT_96" , 0x96, 17 , 0 , 0 , init_96 }, | ||
3032 | { "INIT_97" , 0x97, 13 , 0 , 0 , init_97 }, | ||
3033 | { "INIT_AUXCH" , 0x98, 6 , 5 , 2 , init_auxch }, | ||
3034 | { "INIT_ZM_AUXCH" , 0x99, 6 , 5 , 1 , init_zm_auxch }, | ||
3035 | { NULL , 0 , 0 , 0 , 0 , NULL } | ||
3036 | }; | ||
3037 | |||
3038 | static unsigned int get_init_table_entry_length(struct nvbios *bios, unsigned int offset, int i) | ||
3039 | { | ||
3040 | /* Calculates the length of a given init table entry. */ | ||
3041 | return itbl_entry[i].length + bios->data[offset + itbl_entry[i].length_offset]*itbl_entry[i].length_multiplier; | ||
3042 | } | ||
3043 | |||
3044 | #define MAX_TABLE_OPS 1000 | ||
3045 | |||
3046 | static int | ||
3047 | parse_init_table(struct nvbios *bios, unsigned int offset, | ||
3048 | struct init_exec *iexec) | ||
3049 | { | ||
3050 | /* | ||
3051 | * Parses all commands in an init table. | ||
3052 | * | ||
3053 | * We start out executing all commands found in the init table. Some | ||
3054 | * opcodes may change the status of iexec->execute to SKIP, which will | ||
3055 | * cause the following opcodes to perform no operation until the value | ||
3056 | * is changed back to EXECUTE. | ||
3057 | */ | ||
3058 | |||
3059 | int count = 0, i; | ||
3060 | uint8_t id; | ||
3061 | |||
3062 | /* | ||
3063 | * Loop until INIT_DONE causes us to break out of the loop | ||
3064 | * (or until offset > bios length just in case... ) | ||
3065 | * (and no more than MAX_TABLE_OPS iterations, just in case... ) | ||
3066 | */ | ||
3067 | while ((offset < bios->length) && (count++ < MAX_TABLE_OPS)) { | ||
3068 | id = bios->data[offset]; | ||
3069 | |||
3070 | /* Find matching id in itbl_entry */ | ||
3071 | for (i = 0; itbl_entry[i].name && (itbl_entry[i].id != id); i++) | ||
3072 | ; | ||
3073 | |||
3074 | if (itbl_entry[i].name) { | ||
3075 | BIOSLOG(bios, "0x%04X: [ (0x%02X) - %s ]\n", | ||
3076 | offset, itbl_entry[i].id, itbl_entry[i].name); | ||
3077 | |||
3078 | /* execute eventual command handler */ | ||
3079 | if (itbl_entry[i].handler) | ||
3080 | if (!(*itbl_entry[i].handler)(bios, offset, iexec)) | ||
3081 | break; | ||
3082 | } else { | ||
3083 | NV_ERROR(bios->dev, | ||
3084 | "0x%04X: Init table command not found: " | ||
3085 | "0x%02X\n", offset, id); | ||
3086 | return -ENOENT; | ||
3087 | } | ||
3088 | |||
3089 | /* | ||
3090 | * Add the offset of the current command including all data | ||
3091 | * of that command. The offset will then be pointing on the | ||
3092 | * next op code. | ||
3093 | */ | ||
3094 | offset += get_init_table_entry_length(bios, offset, i); | ||
3095 | } | ||
3096 | |||
3097 | if (offset >= bios->length) | ||
3098 | NV_WARN(bios->dev, | ||
3099 | "Offset 0x%04X greater than known bios image length. " | ||
3100 | "Corrupt image?\n", offset); | ||
3101 | if (count >= MAX_TABLE_OPS) | ||
3102 | NV_WARN(bios->dev, | ||
3103 | "More than %d opcodes to a table is unlikely, " | ||
3104 | "is the bios image corrupt?\n", MAX_TABLE_OPS); | ||
3105 | |||
3106 | return 0; | ||
3107 | } | ||
3108 | |||
3109 | static void | ||
3110 | parse_init_tables(struct nvbios *bios) | ||
3111 | { | ||
3112 | /* Loops and calls parse_init_table() for each present table. */ | ||
3113 | |||
3114 | int i = 0; | ||
3115 | uint16_t table; | ||
3116 | struct init_exec iexec = {true, false}; | ||
3117 | |||
3118 | if (bios->old_style_init) { | ||
3119 | if (bios->init_script_tbls_ptr) | ||
3120 | parse_init_table(bios, bios->init_script_tbls_ptr, &iexec); | ||
3121 | if (bios->extra_init_script_tbl_ptr) | ||
3122 | parse_init_table(bios, bios->extra_init_script_tbl_ptr, &iexec); | ||
3123 | |||
3124 | return; | ||
3125 | } | ||
3126 | |||
3127 | while ((table = ROM16(bios->data[bios->init_script_tbls_ptr + i]))) { | ||
3128 | NV_INFO(bios->dev, | ||
3129 | "Parsing VBIOS init table %d at offset 0x%04X\n", | ||
3130 | i / 2, table); | ||
3131 | BIOSLOG(bios, "0x%04X: ------ Executing following commands ------\n", table); | ||
3132 | |||
3133 | parse_init_table(bios, table, &iexec); | ||
3134 | i += 2; | ||
3135 | } | ||
3136 | } | ||
3137 | |||
3138 | static uint16_t clkcmptable(struct nvbios *bios, uint16_t clktable, int pxclk) | ||
3139 | { | ||
3140 | int compare_record_len, i = 0; | ||
3141 | uint16_t compareclk, scriptptr = 0; | ||
3142 | |||
3143 | if (bios->major_version < 5) /* pre BIT */ | ||
3144 | compare_record_len = 3; | ||
3145 | else | ||
3146 | compare_record_len = 4; | ||
3147 | |||
3148 | do { | ||
3149 | compareclk = ROM16(bios->data[clktable + compare_record_len * i]); | ||
3150 | if (pxclk >= compareclk * 10) { | ||
3151 | if (bios->major_version < 5) { | ||
3152 | uint8_t tmdssub = bios->data[clktable + 2 + compare_record_len * i]; | ||
3153 | scriptptr = ROM16(bios->data[bios->init_script_tbls_ptr + tmdssub * 2]); | ||
3154 | } else | ||
3155 | scriptptr = ROM16(bios->data[clktable + 2 + compare_record_len * i]); | ||
3156 | break; | ||
3157 | } | ||
3158 | i++; | ||
3159 | } while (compareclk); | ||
3160 | |||
3161 | return scriptptr; | ||
3162 | } | ||
3163 | |||
3164 | static void | ||
3165 | run_digital_op_script(struct drm_device *dev, uint16_t scriptptr, | ||
3166 | struct dcb_entry *dcbent, int head, bool dl) | ||
3167 | { | ||
3168 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
3169 | struct nvbios *bios = &dev_priv->VBIOS; | ||
3170 | struct init_exec iexec = {true, false}; | ||
3171 | |||
3172 | NV_TRACE(dev, "0x%04X: Parsing digital output script table\n", | ||
3173 | scriptptr); | ||
3174 | bios_idxprt_wr(bios, NV_CIO_CRX__COLOR, NV_CIO_CRE_44, | ||
3175 | head ? NV_CIO_CRE_44_HEADB : NV_CIO_CRE_44_HEADA); | ||
3176 | /* note: if dcb entries have been merged, index may be misleading */ | ||
3177 | NVWriteVgaCrtc5758(dev, head, 0, dcbent->index); | ||
3178 | parse_init_table(bios, scriptptr, &iexec); | ||
3179 | |||
3180 | nv04_dfp_bind_head(dev, dcbent, head, dl); | ||
3181 | } | ||
3182 | |||
3183 | static int call_lvds_manufacturer_script(struct drm_device *dev, struct dcb_entry *dcbent, int head, enum LVDS_script script) | ||
3184 | { | ||
3185 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
3186 | struct nvbios *bios = &dev_priv->VBIOS; | ||
3187 | uint8_t sub = bios->data[bios->fp.xlated_entry + script] + (bios->fp.link_c_increment && dcbent->or & OUTPUT_C ? 1 : 0); | ||
3188 | uint16_t scriptofs = ROM16(bios->data[bios->init_script_tbls_ptr + sub * 2]); | ||
3189 | |||
3190 | if (!bios->fp.xlated_entry || !sub || !scriptofs) | ||
3191 | return -EINVAL; | ||
3192 | |||
3193 | run_digital_op_script(dev, scriptofs, dcbent, head, bios->fp.dual_link); | ||
3194 | |||
3195 | if (script == LVDS_PANEL_OFF) { | ||
3196 | /* off-on delay in ms */ | ||
3197 | msleep(ROM16(bios->data[bios->fp.xlated_entry + 7])); | ||
3198 | } | ||
3199 | #ifdef __powerpc__ | ||
3200 | /* Powerbook specific quirks */ | ||
3201 | if (script == LVDS_RESET && ((dev->pci_device & 0xffff) == 0x0179 || (dev->pci_device & 0xffff) == 0x0329)) | ||
3202 | nv_write_tmds(dev, dcbent->or, 0, 0x02, 0x72); | ||
3203 | if ((dev->pci_device & 0xffff) == 0x0179 || (dev->pci_device & 0xffff) == 0x0189 || (dev->pci_device & 0xffff) == 0x0329) { | ||
3204 | if (script == LVDS_PANEL_ON) { | ||
3205 | bios_wr32(bios, NV_PBUS_DEBUG_DUALHEAD_CTL, bios_rd32(bios, NV_PBUS_DEBUG_DUALHEAD_CTL) | (1 << 31)); | ||
3206 | bios_wr32(bios, NV_PCRTC_GPIO_EXT, bios_rd32(bios, NV_PCRTC_GPIO_EXT) | 1); | ||
3207 | } | ||
3208 | if (script == LVDS_PANEL_OFF) { | ||
3209 | bios_wr32(bios, NV_PBUS_DEBUG_DUALHEAD_CTL, bios_rd32(bios, NV_PBUS_DEBUG_DUALHEAD_CTL) & ~(1 << 31)); | ||
3210 | bios_wr32(bios, NV_PCRTC_GPIO_EXT, bios_rd32(bios, NV_PCRTC_GPIO_EXT) & ~3); | ||
3211 | } | ||
3212 | } | ||
3213 | #endif | ||
3214 | |||
3215 | return 0; | ||
3216 | } | ||
3217 | |||
3218 | static int run_lvds_table(struct drm_device *dev, struct dcb_entry *dcbent, int head, enum LVDS_script script, int pxclk) | ||
3219 | { | ||
3220 | /* | ||
3221 | * The BIT LVDS table's header has the information to setup the | ||
3222 | * necessary registers. Following the standard 4 byte header are: | ||
3223 | * A bitmask byte and a dual-link transition pxclk value for use in | ||
3224 | * selecting the init script when not using straps; 4 script pointers | ||
3225 | * for panel power, selected by output and on/off; and 8 table pointers | ||
3226 | * for panel init, the needed one determined by output, and bits in the | ||
3227 | * conf byte. These tables are similar to the TMDS tables, consisting | ||
3228 | * of a list of pxclks and script pointers. | ||
3229 | */ | ||
3230 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
3231 | struct nvbios *bios = &dev_priv->VBIOS; | ||
3232 | unsigned int outputset = (dcbent->or == 4) ? 1 : 0; | ||
3233 | uint16_t scriptptr = 0, clktable; | ||
3234 | uint8_t clktableptr = 0; | ||
3235 | |||
3236 | /* | ||
3237 | * For now we assume version 3.0 table - g80 support will need some | ||
3238 | * changes | ||
3239 | */ | ||
3240 | |||
3241 | switch (script) { | ||
3242 | case LVDS_INIT: | ||
3243 | return -ENOSYS; | ||
3244 | case LVDS_BACKLIGHT_ON: | ||
3245 | case LVDS_PANEL_ON: | ||
3246 | scriptptr = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 7 + outputset * 2]); | ||
3247 | break; | ||
3248 | case LVDS_BACKLIGHT_OFF: | ||
3249 | case LVDS_PANEL_OFF: | ||
3250 | scriptptr = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 11 + outputset * 2]); | ||
3251 | break; | ||
3252 | case LVDS_RESET: | ||
3253 | if (dcbent->lvdsconf.use_straps_for_mode) { | ||
3254 | if (bios->fp.dual_link) | ||
3255 | clktableptr += 2; | ||
3256 | if (bios->fp.BITbit1) | ||
3257 | clktableptr++; | ||
3258 | } else { | ||
3259 | /* using EDID */ | ||
3260 | uint8_t fallback = bios->data[bios->fp.lvdsmanufacturerpointer + 4]; | ||
3261 | int fallbackcmpval = (dcbent->or == 4) ? 4 : 1; | ||
3262 | |||
3263 | if (bios->fp.dual_link) { | ||
3264 | clktableptr += 2; | ||
3265 | fallbackcmpval *= 2; | ||
3266 | } | ||
3267 | if (fallbackcmpval & fallback) | ||
3268 | clktableptr++; | ||
3269 | } | ||
3270 | |||
3271 | /* adding outputset * 8 may not be correct */ | ||
3272 | clktable = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 15 + clktableptr * 2 + outputset * 8]); | ||
3273 | if (!clktable) { | ||
3274 | NV_ERROR(dev, "Pixel clock comparison table not found\n"); | ||
3275 | return -ENOENT; | ||
3276 | } | ||
3277 | scriptptr = clkcmptable(bios, clktable, pxclk); | ||
3278 | } | ||
3279 | |||
3280 | if (!scriptptr) { | ||
3281 | NV_ERROR(dev, "LVDS output init script not found\n"); | ||
3282 | return -ENOENT; | ||
3283 | } | ||
3284 | run_digital_op_script(dev, scriptptr, dcbent, head, bios->fp.dual_link); | ||
3285 | |||
3286 | return 0; | ||
3287 | } | ||
3288 | |||
3289 | int call_lvds_script(struct drm_device *dev, struct dcb_entry *dcbent, int head, enum LVDS_script script, int pxclk) | ||
3290 | { | ||
3291 | /* | ||
3292 | * LVDS operations are multiplexed in an effort to present a single API | ||
3293 | * which works with two vastly differing underlying structures. | ||
3294 | * This acts as the demux | ||
3295 | */ | ||
3296 | |||
3297 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
3298 | struct nvbios *bios = &dev_priv->VBIOS; | ||
3299 | uint8_t lvds_ver = bios->data[bios->fp.lvdsmanufacturerpointer]; | ||
3300 | uint32_t sel_clk_binding, sel_clk; | ||
3301 | int ret; | ||
3302 | |||
3303 | if (bios->fp.last_script_invoc == (script << 1 | head) || !lvds_ver || | ||
3304 | (lvds_ver >= 0x30 && script == LVDS_INIT)) | ||
3305 | return 0; | ||
3306 | |||
3307 | if (!bios->fp.lvds_init_run) { | ||
3308 | bios->fp.lvds_init_run = true; | ||
3309 | call_lvds_script(dev, dcbent, head, LVDS_INIT, pxclk); | ||
3310 | } | ||
3311 | |||
3312 | if (script == LVDS_PANEL_ON && bios->fp.reset_after_pclk_change) | ||
3313 | call_lvds_script(dev, dcbent, head, LVDS_RESET, pxclk); | ||
3314 | if (script == LVDS_RESET && bios->fp.power_off_for_reset) | ||
3315 | call_lvds_script(dev, dcbent, head, LVDS_PANEL_OFF, pxclk); | ||
3316 | |||
3317 | NV_TRACE(dev, "Calling LVDS script %d:\n", script); | ||
3318 | |||
3319 | /* don't let script change pll->head binding */ | ||
3320 | sel_clk_binding = bios_rd32(bios, NV_PRAMDAC_SEL_CLK) & 0x50000; | ||
3321 | |||
3322 | if (lvds_ver < 0x30) | ||
3323 | ret = call_lvds_manufacturer_script(dev, dcbent, head, script); | ||
3324 | else | ||
3325 | ret = run_lvds_table(dev, dcbent, head, script, pxclk); | ||
3326 | |||
3327 | bios->fp.last_script_invoc = (script << 1 | head); | ||
3328 | |||
3329 | sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK) & ~0x50000; | ||
3330 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, sel_clk | sel_clk_binding); | ||
3331 | /* some scripts set a value in NV_PBUS_POWERCTRL_2 and break video overlay */ | ||
3332 | nvWriteMC(dev, NV_PBUS_POWERCTRL_2, 0); | ||
3333 | |||
3334 | return ret; | ||
3335 | } | ||
3336 | |||
3337 | struct lvdstableheader { | ||
3338 | uint8_t lvds_ver, headerlen, recordlen; | ||
3339 | }; | ||
3340 | |||
3341 | static int parse_lvds_manufacturer_table_header(struct drm_device *dev, struct nvbios *bios, struct lvdstableheader *lth) | ||
3342 | { | ||
3343 | /* | ||
3344 | * BMP version (0xa) LVDS table has a simple header of version and | ||
3345 | * record length. The BIT LVDS table has the typical BIT table header: | ||
3346 | * version byte, header length byte, record length byte, and a byte for | ||
3347 | * the maximum number of records that can be held in the table. | ||
3348 | */ | ||
3349 | |||
3350 | uint8_t lvds_ver, headerlen, recordlen; | ||
3351 | |||
3352 | memset(lth, 0, sizeof(struct lvdstableheader)); | ||
3353 | |||
3354 | if (bios->fp.lvdsmanufacturerpointer == 0x0) { | ||
3355 | NV_ERROR(dev, "Pointer to LVDS manufacturer table invalid\n"); | ||
3356 | return -EINVAL; | ||
3357 | } | ||
3358 | |||
3359 | lvds_ver = bios->data[bios->fp.lvdsmanufacturerpointer]; | ||
3360 | |||
3361 | switch (lvds_ver) { | ||
3362 | case 0x0a: /* pre NV40 */ | ||
3363 | headerlen = 2; | ||
3364 | recordlen = bios->data[bios->fp.lvdsmanufacturerpointer + 1]; | ||
3365 | break; | ||
3366 | case 0x30: /* NV4x */ | ||
3367 | headerlen = bios->data[bios->fp.lvdsmanufacturerpointer + 1]; | ||
3368 | if (headerlen < 0x1f) { | ||
3369 | NV_ERROR(dev, "LVDS table header not understood\n"); | ||
3370 | return -EINVAL; | ||
3371 | } | ||
3372 | recordlen = bios->data[bios->fp.lvdsmanufacturerpointer + 2]; | ||
3373 | break; | ||
3374 | case 0x40: /* G80/G90 */ | ||
3375 | headerlen = bios->data[bios->fp.lvdsmanufacturerpointer + 1]; | ||
3376 | if (headerlen < 0x7) { | ||
3377 | NV_ERROR(dev, "LVDS table header not understood\n"); | ||
3378 | return -EINVAL; | ||
3379 | } | ||
3380 | recordlen = bios->data[bios->fp.lvdsmanufacturerpointer + 2]; | ||
3381 | break; | ||
3382 | default: | ||
3383 | NV_ERROR(dev, | ||
3384 | "LVDS table revision %d.%d not currently supported\n", | ||
3385 | lvds_ver >> 4, lvds_ver & 0xf); | ||
3386 | return -ENOSYS; | ||
3387 | } | ||
3388 | |||
3389 | lth->lvds_ver = lvds_ver; | ||
3390 | lth->headerlen = headerlen; | ||
3391 | lth->recordlen = recordlen; | ||
3392 | |||
3393 | return 0; | ||
3394 | } | ||
3395 | |||
3396 | static int | ||
3397 | get_fp_strap(struct drm_device *dev, struct nvbios *bios) | ||
3398 | { | ||
3399 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
3400 | |||
3401 | /* | ||
3402 | * The fp strap is normally dictated by the "User Strap" in | ||
3403 | * PEXTDEV_BOOT_0[20:16], but on BMP cards when bit 2 of the | ||
3404 | * Internal_Flags struct at 0x48 is set, the user strap gets overriden | ||
3405 | * by the PCI subsystem ID during POST, but not before the previous user | ||
3406 | * strap has been committed to CR58 for CR57=0xf on head A, which may be | ||
3407 | * read and used instead | ||
3408 | */ | ||
3409 | |||
3410 | if (bios->major_version < 5 && bios->data[0x48] & 0x4) | ||
3411 | return NVReadVgaCrtc5758(dev, 0, 0xf) & 0xf; | ||
3412 | |||
3413 | if (dev_priv->card_type >= NV_50) | ||
3414 | return (bios_rd32(bios, NV_PEXTDEV_BOOT_0) >> 24) & 0xf; | ||
3415 | else | ||
3416 | return (bios_rd32(bios, NV_PEXTDEV_BOOT_0) >> 16) & 0xf; | ||
3417 | } | ||
3418 | |||
3419 | static int parse_fp_mode_table(struct drm_device *dev, struct nvbios *bios) | ||
3420 | { | ||
3421 | uint8_t *fptable; | ||
3422 | uint8_t fptable_ver, headerlen = 0, recordlen, fpentries = 0xf, fpindex; | ||
3423 | int ret, ofs, fpstrapping; | ||
3424 | struct lvdstableheader lth; | ||
3425 | |||
3426 | if (bios->fp.fptablepointer == 0x0) { | ||
3427 | /* Apple cards don't have the fp table; the laptops use DDC */ | ||
3428 | /* The table is also missing on some x86 IGPs */ | ||
3429 | #ifndef __powerpc__ | ||
3430 | NV_ERROR(dev, "Pointer to flat panel table invalid\n"); | ||
3431 | #endif | ||
3432 | bios->pub.digital_min_front_porch = 0x4b; | ||
3433 | return 0; | ||
3434 | } | ||
3435 | |||
3436 | fptable = &bios->data[bios->fp.fptablepointer]; | ||
3437 | fptable_ver = fptable[0]; | ||
3438 | |||
3439 | switch (fptable_ver) { | ||
3440 | /* | ||
3441 | * BMP version 0x5.0x11 BIOSen have version 1 like tables, but no | ||
3442 | * version field, and miss one of the spread spectrum/PWM bytes. | ||
3443 | * This could affect early GF2Go parts (not seen any appropriate ROMs | ||
3444 | * though). Here we assume that a version of 0x05 matches this case | ||
3445 | * (combining with a BMP version check would be better), as the | ||
3446 | * common case for the panel type field is 0x0005, and that is in | ||
3447 | * fact what we are reading the first byte of. | ||
3448 | */ | ||
3449 | case 0x05: /* some NV10, 11, 15, 16 */ | ||
3450 | recordlen = 42; | ||
3451 | ofs = -1; | ||
3452 | break; | ||
3453 | case 0x10: /* some NV15/16, and NV11+ */ | ||
3454 | recordlen = 44; | ||
3455 | ofs = 0; | ||
3456 | break; | ||
3457 | case 0x20: /* NV40+ */ | ||
3458 | headerlen = fptable[1]; | ||
3459 | recordlen = fptable[2]; | ||
3460 | fpentries = fptable[3]; | ||
3461 | /* | ||
3462 | * fptable[4] is the minimum | ||
3463 | * RAMDAC_FP_HCRTC -> RAMDAC_FP_HSYNC_START gap | ||
3464 | */ | ||
3465 | bios->pub.digital_min_front_porch = fptable[4]; | ||
3466 | ofs = -7; | ||
3467 | break; | ||
3468 | default: | ||
3469 | NV_ERROR(dev, | ||
3470 | "FP table revision %d.%d not currently supported\n", | ||
3471 | fptable_ver >> 4, fptable_ver & 0xf); | ||
3472 | return -ENOSYS; | ||
3473 | } | ||
3474 | |||
3475 | if (!bios->is_mobile) /* !mobile only needs digital_min_front_porch */ | ||
3476 | return 0; | ||
3477 | |||
3478 | ret = parse_lvds_manufacturer_table_header(dev, bios, <h); | ||
3479 | if (ret) | ||
3480 | return ret; | ||
3481 | |||
3482 | if (lth.lvds_ver == 0x30 || lth.lvds_ver == 0x40) { | ||
3483 | bios->fp.fpxlatetableptr = bios->fp.lvdsmanufacturerpointer + | ||
3484 | lth.headerlen + 1; | ||
3485 | bios->fp.xlatwidth = lth.recordlen; | ||
3486 | } | ||
3487 | if (bios->fp.fpxlatetableptr == 0x0) { | ||
3488 | NV_ERROR(dev, "Pointer to flat panel xlat table invalid\n"); | ||
3489 | return -EINVAL; | ||
3490 | } | ||
3491 | |||
3492 | fpstrapping = get_fp_strap(dev, bios); | ||
3493 | |||
3494 | fpindex = bios->data[bios->fp.fpxlatetableptr + | ||
3495 | fpstrapping * bios->fp.xlatwidth]; | ||
3496 | |||
3497 | if (fpindex > fpentries) { | ||
3498 | NV_ERROR(dev, "Bad flat panel table index\n"); | ||
3499 | return -ENOENT; | ||
3500 | } | ||
3501 | |||
3502 | /* nv4x cards need both a strap value and fpindex of 0xf to use DDC */ | ||
3503 | if (lth.lvds_ver > 0x10) | ||
3504 | bios->pub.fp_no_ddc = fpstrapping != 0xf || fpindex != 0xf; | ||
3505 | |||
3506 | /* | ||
3507 | * If either the strap or xlated fpindex value are 0xf there is no | ||
3508 | * panel using a strap-derived bios mode present. this condition | ||
3509 | * includes, but is different from, the DDC panel indicator above | ||
3510 | */ | ||
3511 | if (fpstrapping == 0xf || fpindex == 0xf) | ||
3512 | return 0; | ||
3513 | |||
3514 | bios->fp.mode_ptr = bios->fp.fptablepointer + headerlen + | ||
3515 | recordlen * fpindex + ofs; | ||
3516 | |||
3517 | NV_TRACE(dev, "BIOS FP mode: %dx%d (%dkHz pixel clock)\n", | ||
3518 | ROM16(bios->data[bios->fp.mode_ptr + 11]) + 1, | ||
3519 | ROM16(bios->data[bios->fp.mode_ptr + 25]) + 1, | ||
3520 | ROM16(bios->data[bios->fp.mode_ptr + 7]) * 10); | ||
3521 | |||
3522 | return 0; | ||
3523 | } | ||
3524 | |||
3525 | bool nouveau_bios_fp_mode(struct drm_device *dev, struct drm_display_mode *mode) | ||
3526 | { | ||
3527 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
3528 | struct nvbios *bios = &dev_priv->VBIOS; | ||
3529 | uint8_t *mode_entry = &bios->data[bios->fp.mode_ptr]; | ||
3530 | |||
3531 | if (!mode) /* just checking whether we can produce a mode */ | ||
3532 | return bios->fp.mode_ptr; | ||
3533 | |||
3534 | memset(mode, 0, sizeof(struct drm_display_mode)); | ||
3535 | /* | ||
3536 | * For version 1.0 (version in byte 0): | ||
3537 | * bytes 1-2 are "panel type", including bits on whether Colour/mono, | ||
3538 | * single/dual link, and type (TFT etc.) | ||
3539 | * bytes 3-6 are bits per colour in RGBX | ||
3540 | */ | ||
3541 | mode->clock = ROM16(mode_entry[7]) * 10; | ||
3542 | /* bytes 9-10 is HActive */ | ||
3543 | mode->hdisplay = ROM16(mode_entry[11]) + 1; | ||
3544 | /* | ||
3545 | * bytes 13-14 is HValid Start | ||
3546 | * bytes 15-16 is HValid End | ||
3547 | */ | ||
3548 | mode->hsync_start = ROM16(mode_entry[17]) + 1; | ||
3549 | mode->hsync_end = ROM16(mode_entry[19]) + 1; | ||
3550 | mode->htotal = ROM16(mode_entry[21]) + 1; | ||
3551 | /* bytes 23-24, 27-30 similarly, but vertical */ | ||
3552 | mode->vdisplay = ROM16(mode_entry[25]) + 1; | ||
3553 | mode->vsync_start = ROM16(mode_entry[31]) + 1; | ||
3554 | mode->vsync_end = ROM16(mode_entry[33]) + 1; | ||
3555 | mode->vtotal = ROM16(mode_entry[35]) + 1; | ||
3556 | mode->flags |= (mode_entry[37] & 0x10) ? | ||
3557 | DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC; | ||
3558 | mode->flags |= (mode_entry[37] & 0x1) ? | ||
3559 | DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC; | ||
3560 | /* | ||
3561 | * bytes 38-39 relate to spread spectrum settings | ||
3562 | * bytes 40-43 are something to do with PWM | ||
3563 | */ | ||
3564 | |||
3565 | mode->status = MODE_OK; | ||
3566 | mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; | ||
3567 | drm_mode_set_name(mode); | ||
3568 | return bios->fp.mode_ptr; | ||
3569 | } | ||
3570 | |||
3571 | int nouveau_bios_parse_lvds_table(struct drm_device *dev, int pxclk, bool *dl, bool *if_is_24bit) | ||
3572 | { | ||
3573 | /* | ||
3574 | * The LVDS table header is (mostly) described in | ||
3575 | * parse_lvds_manufacturer_table_header(): the BIT header additionally | ||
3576 | * contains the dual-link transition pxclk (in 10s kHz), at byte 5 - if | ||
3577 | * straps are not being used for the panel, this specifies the frequency | ||
3578 | * at which modes should be set up in the dual link style. | ||
3579 | * | ||
3580 | * Following the header, the BMP (ver 0xa) table has several records, | ||
3581 | * indexed by a seperate xlat table, indexed in turn by the fp strap in | ||
3582 | * EXTDEV_BOOT. Each record had a config byte, followed by 6 script | ||
3583 | * numbers for use by INIT_SUB which controlled panel init and power, | ||
3584 | * and finally a dword of ms to sleep between power off and on | ||
3585 | * operations. | ||
3586 | * | ||
3587 | * In the BIT versions, the table following the header serves as an | ||
3588 | * integrated config and xlat table: the records in the table are | ||
3589 | * indexed by the FP strap nibble in EXTDEV_BOOT, and each record has | ||
3590 | * two bytes - the first as a config byte, the second for indexing the | ||
3591 | * fp mode table pointed to by the BIT 'D' table | ||
3592 | * | ||
3593 | * DDC is not used until after card init, so selecting the correct table | ||
3594 | * entry and setting the dual link flag for EDID equipped panels, | ||
3595 | * requiring tests against the native-mode pixel clock, cannot be done | ||
3596 | * until later, when this function should be called with non-zero pxclk | ||
3597 | */ | ||
3598 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
3599 | struct nvbios *bios = &dev_priv->VBIOS; | ||
3600 | int fpstrapping = get_fp_strap(dev, bios), lvdsmanufacturerindex = 0; | ||
3601 | struct lvdstableheader lth; | ||
3602 | uint16_t lvdsofs; | ||
3603 | int ret, chip_version = bios->pub.chip_version; | ||
3604 | |||
3605 | ret = parse_lvds_manufacturer_table_header(dev, bios, <h); | ||
3606 | if (ret) | ||
3607 | return ret; | ||
3608 | |||
3609 | switch (lth.lvds_ver) { | ||
3610 | case 0x0a: /* pre NV40 */ | ||
3611 | lvdsmanufacturerindex = bios->data[ | ||
3612 | bios->fp.fpxlatemanufacturertableptr + | ||
3613 | fpstrapping]; | ||
3614 | |||
3615 | /* we're done if this isn't the EDID panel case */ | ||
3616 | if (!pxclk) | ||
3617 | break; | ||
3618 | |||
3619 | if (chip_version < 0x25) { | ||
3620 | /* nv17 behaviour | ||
3621 | * | ||
3622 | * It seems the old style lvds script pointer is reused | ||
3623 | * to select 18/24 bit colour depth for EDID panels. | ||
3624 | */ | ||
3625 | lvdsmanufacturerindex = | ||
3626 | (bios->legacy.lvds_single_a_script_ptr & 1) ? | ||
3627 | 2 : 0; | ||
3628 | if (pxclk >= bios->fp.duallink_transition_clk) | ||
3629 | lvdsmanufacturerindex++; | ||
3630 | } else if (chip_version < 0x30) { | ||
3631 | /* nv28 behaviour (off-chip encoder) | ||
3632 | * | ||
3633 | * nv28 does a complex dance of first using byte 121 of | ||
3634 | * the EDID to choose the lvdsmanufacturerindex, then | ||
3635 | * later attempting to match the EDID manufacturer and | ||
3636 | * product IDs in a table (signature 'pidt' (panel id | ||
3637 | * table?)), setting an lvdsmanufacturerindex of 0 and | ||
3638 | * an fp strap of the match index (or 0xf if none) | ||
3639 | */ | ||
3640 | lvdsmanufacturerindex = 0; | ||
3641 | } else { | ||
3642 | /* nv31, nv34 behaviour */ | ||
3643 | lvdsmanufacturerindex = 0; | ||
3644 | if (pxclk >= bios->fp.duallink_transition_clk) | ||
3645 | lvdsmanufacturerindex = 2; | ||
3646 | if (pxclk >= 140000) | ||
3647 | lvdsmanufacturerindex = 3; | ||
3648 | } | ||
3649 | |||
3650 | /* | ||
3651 | * nvidia set the high nibble of (cr57=f, cr58) to | ||
3652 | * lvdsmanufacturerindex in this case; we don't | ||
3653 | */ | ||
3654 | break; | ||
3655 | case 0x30: /* NV4x */ | ||
3656 | case 0x40: /* G80/G90 */ | ||
3657 | lvdsmanufacturerindex = fpstrapping; | ||
3658 | break; | ||
3659 | default: | ||
3660 | NV_ERROR(dev, "LVDS table revision not currently supported\n"); | ||
3661 | return -ENOSYS; | ||
3662 | } | ||
3663 | |||
3664 | lvdsofs = bios->fp.xlated_entry = bios->fp.lvdsmanufacturerpointer + lth.headerlen + lth.recordlen * lvdsmanufacturerindex; | ||
3665 | switch (lth.lvds_ver) { | ||
3666 | case 0x0a: | ||
3667 | bios->fp.power_off_for_reset = bios->data[lvdsofs] & 1; | ||
3668 | bios->fp.reset_after_pclk_change = bios->data[lvdsofs] & 2; | ||
3669 | bios->fp.dual_link = bios->data[lvdsofs] & 4; | ||
3670 | bios->fp.link_c_increment = bios->data[lvdsofs] & 8; | ||
3671 | *if_is_24bit = bios->data[lvdsofs] & 16; | ||
3672 | break; | ||
3673 | case 0x30: | ||
3674 | /* | ||
3675 | * My money would be on there being a 24 bit interface bit in | ||
3676 | * this table, but I have no example of a laptop bios with a | ||
3677 | * 24 bit panel to confirm that. Hence we shout loudly if any | ||
3678 | * bit other than bit 0 is set (I've not even seen bit 1) | ||
3679 | */ | ||
3680 | if (bios->data[lvdsofs] > 1) | ||
3681 | NV_ERROR(dev, | ||
3682 | "You have a very unusual laptop display; please report it\n"); | ||
3683 | /* | ||
3684 | * No sign of the "power off for reset" or "reset for panel | ||
3685 | * on" bits, but it's safer to assume we should | ||
3686 | */ | ||
3687 | bios->fp.power_off_for_reset = true; | ||
3688 | bios->fp.reset_after_pclk_change = true; | ||
3689 | /* | ||
3690 | * It's ok lvdsofs is wrong for nv4x edid case; dual_link is | ||
3691 | * over-written, and BITbit1 isn't used | ||
3692 | */ | ||
3693 | bios->fp.dual_link = bios->data[lvdsofs] & 1; | ||
3694 | bios->fp.BITbit1 = bios->data[lvdsofs] & 2; | ||
3695 | bios->fp.duallink_transition_clk = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 5]) * 10; | ||
3696 | break; | ||
3697 | case 0x40: | ||
3698 | bios->fp.dual_link = bios->data[lvdsofs] & 1; | ||
3699 | bios->fp.if_is_24bit = bios->data[lvdsofs] & 2; | ||
3700 | bios->fp.strapless_is_24bit = bios->data[bios->fp.lvdsmanufacturerpointer + 4]; | ||
3701 | bios->fp.duallink_transition_clk = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 5]) * 10; | ||
3702 | break; | ||
3703 | } | ||
3704 | |||
3705 | /* set dual_link flag for EDID case */ | ||
3706 | if (pxclk && (chip_version < 0x25 || chip_version > 0x28)) | ||
3707 | bios->fp.dual_link = (pxclk >= bios->fp.duallink_transition_clk); | ||
3708 | |||
3709 | *dl = bios->fp.dual_link; | ||
3710 | |||
3711 | return 0; | ||
3712 | } | ||
3713 | |||
3714 | static uint8_t * | ||
3715 | bios_output_config_match(struct drm_device *dev, struct dcb_entry *dcbent, | ||
3716 | uint16_t record, int record_len, int record_nr) | ||
3717 | { | ||
3718 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
3719 | struct nvbios *bios = &dev_priv->VBIOS; | ||
3720 | uint32_t entry; | ||
3721 | uint16_t table; | ||
3722 | int i, v; | ||
3723 | |||
3724 | for (i = 0; i < record_nr; i++, record += record_len) { | ||
3725 | table = ROM16(bios->data[record]); | ||
3726 | if (!table) | ||
3727 | continue; | ||
3728 | entry = ROM32(bios->data[table]); | ||
3729 | |||
3730 | v = (entry & 0x000f0000) >> 16; | ||
3731 | if (!(v & dcbent->or)) | ||
3732 | continue; | ||
3733 | |||
3734 | v = (entry & 0x000000f0) >> 4; | ||
3735 | if (v != dcbent->location) | ||
3736 | continue; | ||
3737 | |||
3738 | v = (entry & 0x0000000f); | ||
3739 | if (v != dcbent->type) | ||
3740 | continue; | ||
3741 | |||
3742 | return &bios->data[table]; | ||
3743 | } | ||
3744 | |||
3745 | return NULL; | ||
3746 | } | ||
3747 | |||
3748 | void * | ||
3749 | nouveau_bios_dp_table(struct drm_device *dev, struct dcb_entry *dcbent, | ||
3750 | int *length) | ||
3751 | { | ||
3752 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
3753 | struct nvbios *bios = &dev_priv->VBIOS; | ||
3754 | uint8_t *table; | ||
3755 | |||
3756 | if (!bios->display.dp_table_ptr) { | ||
3757 | NV_ERROR(dev, "No pointer to DisplayPort table\n"); | ||
3758 | return NULL; | ||
3759 | } | ||
3760 | table = &bios->data[bios->display.dp_table_ptr]; | ||
3761 | |||
3762 | if (table[0] != 0x21) { | ||
3763 | NV_ERROR(dev, "DisplayPort table version 0x%02x unknown\n", | ||
3764 | table[0]); | ||
3765 | return NULL; | ||
3766 | } | ||
3767 | |||
3768 | *length = table[4]; | ||
3769 | return bios_output_config_match(dev, dcbent, | ||
3770 | bios->display.dp_table_ptr + table[1], | ||
3771 | table[2], table[3]); | ||
3772 | } | ||
3773 | |||
3774 | int | ||
3775 | nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent, | ||
3776 | uint32_t sub, int pxclk) | ||
3777 | { | ||
3778 | /* | ||
3779 | * The display script table is located by the BIT 'U' table. | ||
3780 | * | ||
3781 | * It contains an array of pointers to various tables describing | ||
3782 | * a particular output type. The first 32-bits of the output | ||
3783 | * tables contains similar information to a DCB entry, and is | ||
3784 | * used to decide whether that particular table is suitable for | ||
3785 | * the output you want to access. | ||
3786 | * | ||
3787 | * The "record header length" field here seems to indicate the | ||
3788 | * offset of the first configuration entry in the output tables. | ||
3789 | * This is 10 on most cards I've seen, but 12 has been witnessed | ||
3790 | * on DP cards, and there's another script pointer within the | ||
3791 | * header. | ||
3792 | * | ||
3793 | * offset + 0 ( 8 bits): version | ||
3794 | * offset + 1 ( 8 bits): header length | ||
3795 | * offset + 2 ( 8 bits): record length | ||
3796 | * offset + 3 ( 8 bits): number of records | ||
3797 | * offset + 4 ( 8 bits): record header length | ||
3798 | * offset + 5 (16 bits): pointer to first output script table | ||
3799 | */ | ||
3800 | |||
3801 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
3802 | struct init_exec iexec = {true, false}; | ||
3803 | struct nvbios *bios = &dev_priv->VBIOS; | ||
3804 | uint8_t *table = &bios->data[bios->display.script_table_ptr]; | ||
3805 | uint8_t *otable = NULL; | ||
3806 | uint16_t script; | ||
3807 | int i = 0; | ||
3808 | |||
3809 | if (!bios->display.script_table_ptr) { | ||
3810 | NV_ERROR(dev, "No pointer to output script table\n"); | ||
3811 | return 1; | ||
3812 | } | ||
3813 | |||
3814 | /* | ||
3815 | * Nothing useful has been in any of the pre-2.0 tables I've seen, | ||
3816 | * so until they are, we really don't need to care. | ||
3817 | */ | ||
3818 | if (table[0] < 0x20) | ||
3819 | return 1; | ||
3820 | |||
3821 | if (table[0] != 0x20 && table[0] != 0x21) { | ||
3822 | NV_ERROR(dev, "Output script table version 0x%02x unknown\n", | ||
3823 | table[0]); | ||
3824 | return 1; | ||
3825 | } | ||
3826 | |||
3827 | /* | ||
3828 | * The output script tables describing a particular output type | ||
3829 | * look as follows: | ||
3830 | * | ||
3831 | * offset + 0 (32 bits): output this table matches (hash of DCB) | ||
3832 | * offset + 4 ( 8 bits): unknown | ||
3833 | * offset + 5 ( 8 bits): number of configurations | ||
3834 | * offset + 6 (16 bits): pointer to some script | ||
3835 | * offset + 8 (16 bits): pointer to some script | ||
3836 | * | ||
3837 | * headerlen == 10 | ||
3838 | * offset + 10 : configuration 0 | ||
3839 | * | ||
3840 | * headerlen == 12 | ||
3841 | * offset + 10 : pointer to some script | ||
3842 | * offset + 12 : configuration 0 | ||
3843 | * | ||
3844 | * Each config entry is as follows: | ||
3845 | * | ||
3846 | * offset + 0 (16 bits): unknown, assumed to be a match value | ||
3847 | * offset + 2 (16 bits): pointer to script table (clock set?) | ||
3848 | * offset + 4 (16 bits): pointer to script table (reset?) | ||
3849 | * | ||
3850 | * There doesn't appear to be a count value to say how many | ||
3851 | * entries exist in each script table, instead, a 0 value in | ||
3852 | * the first 16-bit word seems to indicate both the end of the | ||
3853 | * list and the default entry. The second 16-bit word in the | ||
3854 | * script tables is a pointer to the script to execute. | ||
3855 | */ | ||
3856 | |||
3857 | NV_DEBUG(dev, "Searching for output entry for %d %d %d\n", | ||
3858 | dcbent->type, dcbent->location, dcbent->or); | ||
3859 | otable = bios_output_config_match(dev, dcbent, table[1] + | ||
3860 | bios->display.script_table_ptr, | ||
3861 | table[2], table[3]); | ||
3862 | if (!otable) { | ||
3863 | NV_ERROR(dev, "Couldn't find matching output script table\n"); | ||
3864 | return 1; | ||
3865 | } | ||
3866 | |||
3867 | if (pxclk < -2 || pxclk > 0) { | ||
3868 | /* Try to find matching script table entry */ | ||
3869 | for (i = 0; i < otable[5]; i++) { | ||
3870 | if (ROM16(otable[table[4] + i*6]) == sub) | ||
3871 | break; | ||
3872 | } | ||
3873 | |||
3874 | if (i == otable[5]) { | ||
3875 | NV_ERROR(dev, "Table 0x%04x not found for %d/%d, " | ||
3876 | "using first\n", | ||
3877 | sub, dcbent->type, dcbent->or); | ||
3878 | i = 0; | ||
3879 | } | ||
3880 | } | ||
3881 | |||
3882 | bios->display.output = dcbent; | ||
3883 | |||
3884 | if (pxclk == 0) { | ||
3885 | script = ROM16(otable[6]); | ||
3886 | if (!script) { | ||
3887 | NV_DEBUG(dev, "output script 0 not found\n"); | ||
3888 | return 1; | ||
3889 | } | ||
3890 | |||
3891 | NV_TRACE(dev, "0x%04X: parsing output script 0\n", script); | ||
3892 | parse_init_table(bios, script, &iexec); | ||
3893 | } else | ||
3894 | if (pxclk == -1) { | ||
3895 | script = ROM16(otable[8]); | ||
3896 | if (!script) { | ||
3897 | NV_DEBUG(dev, "output script 1 not found\n"); | ||
3898 | return 1; | ||
3899 | } | ||
3900 | |||
3901 | NV_TRACE(dev, "0x%04X: parsing output script 1\n", script); | ||
3902 | parse_init_table(bios, script, &iexec); | ||
3903 | } else | ||
3904 | if (pxclk == -2) { | ||
3905 | if (table[4] >= 12) | ||
3906 | script = ROM16(otable[10]); | ||
3907 | else | ||
3908 | script = 0; | ||
3909 | if (!script) { | ||
3910 | NV_DEBUG(dev, "output script 2 not found\n"); | ||
3911 | return 1; | ||
3912 | } | ||
3913 | |||
3914 | NV_TRACE(dev, "0x%04X: parsing output script 2\n", script); | ||
3915 | parse_init_table(bios, script, &iexec); | ||
3916 | } else | ||
3917 | if (pxclk > 0) { | ||
3918 | script = ROM16(otable[table[4] + i*6 + 2]); | ||
3919 | if (script) | ||
3920 | script = clkcmptable(bios, script, pxclk); | ||
3921 | if (!script) { | ||
3922 | NV_ERROR(dev, "clock script 0 not found\n"); | ||
3923 | return 1; | ||
3924 | } | ||
3925 | |||
3926 | NV_TRACE(dev, "0x%04X: parsing clock script 0\n", script); | ||
3927 | parse_init_table(bios, script, &iexec); | ||
3928 | } else | ||
3929 | if (pxclk < 0) { | ||
3930 | script = ROM16(otable[table[4] + i*6 + 4]); | ||
3931 | if (script) | ||
3932 | script = clkcmptable(bios, script, -pxclk); | ||
3933 | if (!script) { | ||
3934 | NV_DEBUG(dev, "clock script 1 not found\n"); | ||
3935 | return 1; | ||
3936 | } | ||
3937 | |||
3938 | NV_TRACE(dev, "0x%04X: parsing clock script 1\n", script); | ||
3939 | parse_init_table(bios, script, &iexec); | ||
3940 | } | ||
3941 | |||
3942 | return 0; | ||
3943 | } | ||
3944 | |||
3945 | |||
3946 | int run_tmds_table(struct drm_device *dev, struct dcb_entry *dcbent, int head, int pxclk) | ||
3947 | { | ||
3948 | /* | ||
3949 | * the pxclk parameter is in kHz | ||
3950 | * | ||
3951 | * This runs the TMDS regs setting code found on BIT bios cards | ||
3952 | * | ||
3953 | * For ffs(or) == 1 use the first table, for ffs(or) == 2 and | ||
3954 | * ffs(or) == 3, use the second. | ||
3955 | */ | ||
3956 | |||
3957 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
3958 | struct nvbios *bios = &dev_priv->VBIOS; | ||
3959 | int cv = bios->pub.chip_version; | ||
3960 | uint16_t clktable = 0, scriptptr; | ||
3961 | uint32_t sel_clk_binding, sel_clk; | ||
3962 | |||
3963 | /* pre-nv17 off-chip tmds uses scripts, post nv17 doesn't */ | ||
3964 | if (cv >= 0x17 && cv != 0x1a && cv != 0x20 && | ||
3965 | dcbent->location != DCB_LOC_ON_CHIP) | ||
3966 | return 0; | ||
3967 | |||
3968 | switch (ffs(dcbent->or)) { | ||
3969 | case 1: | ||
3970 | clktable = bios->tmds.output0_script_ptr; | ||
3971 | break; | ||
3972 | case 2: | ||
3973 | case 3: | ||
3974 | clktable = bios->tmds.output1_script_ptr; | ||
3975 | break; | ||
3976 | } | ||
3977 | |||
3978 | if (!clktable) { | ||
3979 | NV_ERROR(dev, "Pixel clock comparison table not found\n"); | ||
3980 | return -EINVAL; | ||
3981 | } | ||
3982 | |||
3983 | scriptptr = clkcmptable(bios, clktable, pxclk); | ||
3984 | |||
3985 | if (!scriptptr) { | ||
3986 | NV_ERROR(dev, "TMDS output init script not found\n"); | ||
3987 | return -ENOENT; | ||
3988 | } | ||
3989 | |||
3990 | /* don't let script change pll->head binding */ | ||
3991 | sel_clk_binding = bios_rd32(bios, NV_PRAMDAC_SEL_CLK) & 0x50000; | ||
3992 | run_digital_op_script(dev, scriptptr, dcbent, head, pxclk >= 165000); | ||
3993 | sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK) & ~0x50000; | ||
3994 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, sel_clk | sel_clk_binding); | ||
3995 | |||
3996 | return 0; | ||
3997 | } | ||
3998 | |||
3999 | int get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims *pll_lim) | ||
4000 | { | ||
4001 | /* | ||
4002 | * PLL limits table | ||
4003 | * | ||
4004 | * Version 0x10: NV30, NV31 | ||
4005 | * One byte header (version), one record of 24 bytes | ||
4006 | * Version 0x11: NV36 - Not implemented | ||
4007 | * Seems to have same record style as 0x10, but 3 records rather than 1 | ||
4008 | * Version 0x20: Found on Geforce 6 cards | ||
4009 | * Trivial 4 byte BIT header. 31 (0x1f) byte record length | ||
4010 | * Version 0x21: Found on Geforce 7, 8 and some Geforce 6 cards | ||
4011 | * 5 byte header, fifth byte of unknown purpose. 35 (0x23) byte record | ||
4012 | * length in general, some (integrated) have an extra configuration byte | ||
4013 | * Version 0x30: Found on Geforce 8, separates the register mapping | ||
4014 | * from the limits tables. | ||
4015 | */ | ||
4016 | |||
4017 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
4018 | struct nvbios *bios = &dev_priv->VBIOS; | ||
4019 | int cv = bios->pub.chip_version, pllindex = 0; | ||
4020 | uint8_t pll_lim_ver = 0, headerlen = 0, recordlen = 0, entries = 0; | ||
4021 | uint32_t crystal_strap_mask, crystal_straps; | ||
4022 | |||
4023 | if (!bios->pll_limit_tbl_ptr) { | ||
4024 | if (cv == 0x30 || cv == 0x31 || cv == 0x35 || cv == 0x36 || | ||
4025 | cv >= 0x40) { | ||
4026 | NV_ERROR(dev, "Pointer to PLL limits table invalid\n"); | ||
4027 | return -EINVAL; | ||
4028 | } | ||
4029 | } else | ||
4030 | pll_lim_ver = bios->data[bios->pll_limit_tbl_ptr]; | ||
4031 | |||
4032 | crystal_strap_mask = 1 << 6; | ||
4033 | /* open coded dev->twoHeads test */ | ||
4034 | if (cv > 0x10 && cv != 0x15 && cv != 0x1a && cv != 0x20) | ||
4035 | crystal_strap_mask |= 1 << 22; | ||
4036 | crystal_straps = nvReadEXTDEV(dev, NV_PEXTDEV_BOOT_0) & | ||
4037 | crystal_strap_mask; | ||
4038 | |||
4039 | switch (pll_lim_ver) { | ||
4040 | /* | ||
4041 | * We use version 0 to indicate a pre limit table bios (single stage | ||
4042 | * pll) and load the hard coded limits instead. | ||
4043 | */ | ||
4044 | case 0: | ||
4045 | break; | ||
4046 | case 0x10: | ||
4047 | case 0x11: | ||
4048 | /* | ||
4049 | * Strictly v0x11 has 3 entries, but the last two don't seem | ||
4050 | * to get used. | ||
4051 | */ | ||
4052 | headerlen = 1; | ||
4053 | recordlen = 0x18; | ||
4054 | entries = 1; | ||
4055 | pllindex = 0; | ||
4056 | break; | ||
4057 | case 0x20: | ||
4058 | case 0x21: | ||
4059 | case 0x30: | ||
4060 | case 0x40: | ||
4061 | headerlen = bios->data[bios->pll_limit_tbl_ptr + 1]; | ||
4062 | recordlen = bios->data[bios->pll_limit_tbl_ptr + 2]; | ||
4063 | entries = bios->data[bios->pll_limit_tbl_ptr + 3]; | ||
4064 | break; | ||
4065 | default: | ||
4066 | NV_ERROR(dev, "PLL limits table revision 0x%X not currently " | ||
4067 | "supported\n", pll_lim_ver); | ||
4068 | return -ENOSYS; | ||
4069 | } | ||
4070 | |||
4071 | /* initialize all members to zero */ | ||
4072 | memset(pll_lim, 0, sizeof(struct pll_lims)); | ||
4073 | |||
4074 | if (pll_lim_ver == 0x10 || pll_lim_ver == 0x11) { | ||
4075 | uint8_t *pll_rec = &bios->data[bios->pll_limit_tbl_ptr + headerlen + recordlen * pllindex]; | ||
4076 | |||
4077 | pll_lim->vco1.minfreq = ROM32(pll_rec[0]); | ||
4078 | pll_lim->vco1.maxfreq = ROM32(pll_rec[4]); | ||
4079 | pll_lim->vco2.minfreq = ROM32(pll_rec[8]); | ||
4080 | pll_lim->vco2.maxfreq = ROM32(pll_rec[12]); | ||
4081 | pll_lim->vco1.min_inputfreq = ROM32(pll_rec[16]); | ||
4082 | pll_lim->vco2.min_inputfreq = ROM32(pll_rec[20]); | ||
4083 | pll_lim->vco1.max_inputfreq = pll_lim->vco2.max_inputfreq = INT_MAX; | ||
4084 | |||
4085 | /* these values taken from nv30/31/36 */ | ||
4086 | pll_lim->vco1.min_n = 0x1; | ||
4087 | if (cv == 0x36) | ||
4088 | pll_lim->vco1.min_n = 0x5; | ||
4089 | pll_lim->vco1.max_n = 0xff; | ||
4090 | pll_lim->vco1.min_m = 0x1; | ||
4091 | pll_lim->vco1.max_m = 0xd; | ||
4092 | pll_lim->vco2.min_n = 0x4; | ||
4093 | /* | ||
4094 | * On nv30, 31, 36 (i.e. all cards with two stage PLLs with this | ||
4095 | * table version (apart from nv35)), N2 is compared to | ||
4096 | * maxN2 (0x46) and 10 * maxM2 (0x4), so set maxN2 to 0x28 and | ||
4097 | * save a comparison | ||
4098 | */ | ||
4099 | pll_lim->vco2.max_n = 0x28; | ||
4100 | if (cv == 0x30 || cv == 0x35) | ||
4101 | /* only 5 bits available for N2 on nv30/35 */ | ||
4102 | pll_lim->vco2.max_n = 0x1f; | ||
4103 | pll_lim->vco2.min_m = 0x1; | ||
4104 | pll_lim->vco2.max_m = 0x4; | ||
4105 | pll_lim->max_log2p = 0x7; | ||
4106 | pll_lim->max_usable_log2p = 0x6; | ||
4107 | } else if (pll_lim_ver == 0x20 || pll_lim_ver == 0x21) { | ||
4108 | uint16_t plloffs = bios->pll_limit_tbl_ptr + headerlen; | ||
4109 | uint32_t reg = 0; /* default match */ | ||
4110 | uint8_t *pll_rec; | ||
4111 | int i; | ||
4112 | |||
4113 | /* | ||
4114 | * First entry is default match, if nothing better. warn if | ||
4115 | * reg field nonzero | ||
4116 | */ | ||
4117 | if (ROM32(bios->data[plloffs])) | ||
4118 | NV_WARN(dev, "Default PLL limit entry has non-zero " | ||
4119 | "register field\n"); | ||
4120 | |||
4121 | if (limit_match > MAX_PLL_TYPES) | ||
4122 | /* we've been passed a reg as the match */ | ||
4123 | reg = limit_match; | ||
4124 | else /* limit match is a pll type */ | ||
4125 | for (i = 1; i < entries && !reg; i++) { | ||
4126 | uint32_t cmpreg = ROM32(bios->data[plloffs + recordlen * i]); | ||
4127 | |||
4128 | if (limit_match == NVPLL && | ||
4129 | (cmpreg == NV_PRAMDAC_NVPLL_COEFF || cmpreg == 0x4000)) | ||
4130 | reg = cmpreg; | ||
4131 | if (limit_match == MPLL && | ||
4132 | (cmpreg == NV_PRAMDAC_MPLL_COEFF || cmpreg == 0x4020)) | ||
4133 | reg = cmpreg; | ||
4134 | if (limit_match == VPLL1 && | ||
4135 | (cmpreg == NV_PRAMDAC_VPLL_COEFF || cmpreg == 0x4010)) | ||
4136 | reg = cmpreg; | ||
4137 | if (limit_match == VPLL2 && | ||
4138 | (cmpreg == NV_RAMDAC_VPLL2 || cmpreg == 0x4018)) | ||
4139 | reg = cmpreg; | ||
4140 | } | ||
4141 | |||
4142 | for (i = 1; i < entries; i++) | ||
4143 | if (ROM32(bios->data[plloffs + recordlen * i]) == reg) { | ||
4144 | pllindex = i; | ||
4145 | break; | ||
4146 | } | ||
4147 | |||
4148 | pll_rec = &bios->data[plloffs + recordlen * pllindex]; | ||
4149 | |||
4150 | BIOSLOG(bios, "Loading PLL limits for reg 0x%08x\n", | ||
4151 | pllindex ? reg : 0); | ||
4152 | |||
4153 | /* | ||
4154 | * Frequencies are stored in tables in MHz, kHz are more | ||
4155 | * useful, so we convert. | ||
4156 | */ | ||
4157 | |||
4158 | /* What output frequencies can each VCO generate? */ | ||
4159 | pll_lim->vco1.minfreq = ROM16(pll_rec[4]) * 1000; | ||
4160 | pll_lim->vco1.maxfreq = ROM16(pll_rec[6]) * 1000; | ||
4161 | pll_lim->vco2.minfreq = ROM16(pll_rec[8]) * 1000; | ||
4162 | pll_lim->vco2.maxfreq = ROM16(pll_rec[10]) * 1000; | ||
4163 | |||
4164 | /* What input frequencies they accept (past the m-divider)? */ | ||
4165 | pll_lim->vco1.min_inputfreq = ROM16(pll_rec[12]) * 1000; | ||
4166 | pll_lim->vco2.min_inputfreq = ROM16(pll_rec[14]) * 1000; | ||
4167 | pll_lim->vco1.max_inputfreq = ROM16(pll_rec[16]) * 1000; | ||
4168 | pll_lim->vco2.max_inputfreq = ROM16(pll_rec[18]) * 1000; | ||
4169 | |||
4170 | /* What values are accepted as multiplier and divider? */ | ||
4171 | pll_lim->vco1.min_n = pll_rec[20]; | ||
4172 | pll_lim->vco1.max_n = pll_rec[21]; | ||
4173 | pll_lim->vco1.min_m = pll_rec[22]; | ||
4174 | pll_lim->vco1.max_m = pll_rec[23]; | ||
4175 | pll_lim->vco2.min_n = pll_rec[24]; | ||
4176 | pll_lim->vco2.max_n = pll_rec[25]; | ||
4177 | pll_lim->vco2.min_m = pll_rec[26]; | ||
4178 | pll_lim->vco2.max_m = pll_rec[27]; | ||
4179 | |||
4180 | pll_lim->max_usable_log2p = pll_lim->max_log2p = pll_rec[29]; | ||
4181 | if (pll_lim->max_log2p > 0x7) | ||
4182 | /* pll decoding in nv_hw.c assumes never > 7 */ | ||
4183 | NV_WARN(dev, "Max log2 P value greater than 7 (%d)\n", | ||
4184 | pll_lim->max_log2p); | ||
4185 | if (cv < 0x60) | ||
4186 | pll_lim->max_usable_log2p = 0x6; | ||
4187 | pll_lim->log2p_bias = pll_rec[30]; | ||
4188 | |||
4189 | if (recordlen > 0x22) | ||
4190 | pll_lim->refclk = ROM32(pll_rec[31]); | ||
4191 | |||
4192 | if (recordlen > 0x23 && pll_rec[35]) | ||
4193 | NV_WARN(dev, | ||
4194 | "Bits set in PLL configuration byte (%x)\n", | ||
4195 | pll_rec[35]); | ||
4196 | |||
4197 | /* C51 special not seen elsewhere */ | ||
4198 | if (cv == 0x51 && !pll_lim->refclk) { | ||
4199 | uint32_t sel_clk = bios_rd32(bios, NV_PRAMDAC_SEL_CLK); | ||
4200 | |||
4201 | if (((limit_match == NV_PRAMDAC_VPLL_COEFF || limit_match == VPLL1) && sel_clk & 0x20) || | ||
4202 | ((limit_match == NV_RAMDAC_VPLL2 || limit_match == VPLL2) && sel_clk & 0x80)) { | ||
4203 | if (bios_idxprt_rd(bios, NV_CIO_CRX__COLOR, NV_CIO_CRE_CHIP_ID_INDEX) < 0xa3) | ||
4204 | pll_lim->refclk = 200000; | ||
4205 | else | ||
4206 | pll_lim->refclk = 25000; | ||
4207 | } | ||
4208 | } | ||
4209 | } else if (pll_lim_ver == 0x30) { /* ver 0x30 */ | ||
4210 | uint8_t *entry = &bios->data[bios->pll_limit_tbl_ptr + headerlen]; | ||
4211 | uint8_t *record = NULL; | ||
4212 | int i; | ||
4213 | |||
4214 | BIOSLOG(bios, "Loading PLL limits for register 0x%08x\n", | ||
4215 | limit_match); | ||
4216 | |||
4217 | for (i = 0; i < entries; i++, entry += recordlen) { | ||
4218 | if (ROM32(entry[3]) == limit_match) { | ||
4219 | record = &bios->data[ROM16(entry[1])]; | ||
4220 | break; | ||
4221 | } | ||
4222 | } | ||
4223 | |||
4224 | if (!record) { | ||
4225 | NV_ERROR(dev, "Register 0x%08x not found in PLL " | ||
4226 | "limits table", limit_match); | ||
4227 | return -ENOENT; | ||
4228 | } | ||
4229 | |||
4230 | pll_lim->vco1.minfreq = ROM16(record[0]) * 1000; | ||
4231 | pll_lim->vco1.maxfreq = ROM16(record[2]) * 1000; | ||
4232 | pll_lim->vco2.minfreq = ROM16(record[4]) * 1000; | ||
4233 | pll_lim->vco2.maxfreq = ROM16(record[6]) * 1000; | ||
4234 | pll_lim->vco1.min_inputfreq = ROM16(record[8]) * 1000; | ||
4235 | pll_lim->vco2.min_inputfreq = ROM16(record[10]) * 1000; | ||
4236 | pll_lim->vco1.max_inputfreq = ROM16(record[12]) * 1000; | ||
4237 | pll_lim->vco2.max_inputfreq = ROM16(record[14]) * 1000; | ||
4238 | pll_lim->vco1.min_n = record[16]; | ||
4239 | pll_lim->vco1.max_n = record[17]; | ||
4240 | pll_lim->vco1.min_m = record[18]; | ||
4241 | pll_lim->vco1.max_m = record[19]; | ||
4242 | pll_lim->vco2.min_n = record[20]; | ||
4243 | pll_lim->vco2.max_n = record[21]; | ||
4244 | pll_lim->vco2.min_m = record[22]; | ||
4245 | pll_lim->vco2.max_m = record[23]; | ||
4246 | pll_lim->max_usable_log2p = pll_lim->max_log2p = record[25]; | ||
4247 | pll_lim->log2p_bias = record[27]; | ||
4248 | pll_lim->refclk = ROM32(record[28]); | ||
4249 | } else if (pll_lim_ver) { /* ver 0x40 */ | ||
4250 | uint8_t *entry = &bios->data[bios->pll_limit_tbl_ptr + headerlen]; | ||
4251 | uint8_t *record = NULL; | ||
4252 | int i; | ||
4253 | |||
4254 | BIOSLOG(bios, "Loading PLL limits for register 0x%08x\n", | ||
4255 | limit_match); | ||
4256 | |||
4257 | for (i = 0; i < entries; i++, entry += recordlen) { | ||
4258 | if (ROM32(entry[3]) == limit_match) { | ||
4259 | record = &bios->data[ROM16(entry[1])]; | ||
4260 | break; | ||
4261 | } | ||
4262 | } | ||
4263 | |||
4264 | if (!record) { | ||
4265 | NV_ERROR(dev, "Register 0x%08x not found in PLL " | ||
4266 | "limits table", limit_match); | ||
4267 | return -ENOENT; | ||
4268 | } | ||
4269 | |||
4270 | pll_lim->vco1.minfreq = ROM16(record[0]) * 1000; | ||
4271 | pll_lim->vco1.maxfreq = ROM16(record[2]) * 1000; | ||
4272 | pll_lim->vco1.min_inputfreq = ROM16(record[4]) * 1000; | ||
4273 | pll_lim->vco1.max_inputfreq = ROM16(record[6]) * 1000; | ||
4274 | pll_lim->vco1.min_m = record[8]; | ||
4275 | pll_lim->vco1.max_m = record[9]; | ||
4276 | pll_lim->vco1.min_n = record[10]; | ||
4277 | pll_lim->vco1.max_n = record[11]; | ||
4278 | pll_lim->min_p = record[12]; | ||
4279 | pll_lim->max_p = record[13]; | ||
4280 | /* where did this go to?? */ | ||
4281 | if (limit_match == 0x00614100 || limit_match == 0x00614900) | ||
4282 | pll_lim->refclk = 27000; | ||
4283 | else | ||
4284 | pll_lim->refclk = 100000; | ||
4285 | } | ||
4286 | |||
4287 | /* | ||
4288 | * By now any valid limit table ought to have set a max frequency for | ||
4289 | * vco1, so if it's zero it's either a pre limit table bios, or one | ||
4290 | * with an empty limit table (seen on nv18) | ||
4291 | */ | ||
4292 | if (!pll_lim->vco1.maxfreq) { | ||
4293 | pll_lim->vco1.minfreq = bios->fminvco; | ||
4294 | pll_lim->vco1.maxfreq = bios->fmaxvco; | ||
4295 | pll_lim->vco1.min_inputfreq = 0; | ||
4296 | pll_lim->vco1.max_inputfreq = INT_MAX; | ||
4297 | pll_lim->vco1.min_n = 0x1; | ||
4298 | pll_lim->vco1.max_n = 0xff; | ||
4299 | pll_lim->vco1.min_m = 0x1; | ||
4300 | if (crystal_straps == 0) { | ||
4301 | /* nv05 does this, nv11 doesn't, nv10 unknown */ | ||
4302 | if (cv < 0x11) | ||
4303 | pll_lim->vco1.min_m = 0x7; | ||
4304 | pll_lim->vco1.max_m = 0xd; | ||
4305 | } else { | ||
4306 | if (cv < 0x11) | ||
4307 | pll_lim->vco1.min_m = 0x8; | ||
4308 | pll_lim->vco1.max_m = 0xe; | ||
4309 | } | ||
4310 | if (cv < 0x17 || cv == 0x1a || cv == 0x20) | ||
4311 | pll_lim->max_log2p = 4; | ||
4312 | else | ||
4313 | pll_lim->max_log2p = 5; | ||
4314 | pll_lim->max_usable_log2p = pll_lim->max_log2p; | ||
4315 | } | ||
4316 | |||
4317 | if (!pll_lim->refclk) | ||
4318 | switch (crystal_straps) { | ||
4319 | case 0: | ||
4320 | pll_lim->refclk = 13500; | ||
4321 | break; | ||
4322 | case (1 << 6): | ||
4323 | pll_lim->refclk = 14318; | ||
4324 | break; | ||
4325 | case (1 << 22): | ||
4326 | pll_lim->refclk = 27000; | ||
4327 | break; | ||
4328 | case (1 << 22 | 1 << 6): | ||
4329 | pll_lim->refclk = 25000; | ||
4330 | break; | ||
4331 | } | ||
4332 | |||
4333 | #if 0 /* for easy debugging */ | ||
4334 | ErrorF("pll.vco1.minfreq: %d\n", pll_lim->vco1.minfreq); | ||
4335 | ErrorF("pll.vco1.maxfreq: %d\n", pll_lim->vco1.maxfreq); | ||
4336 | ErrorF("pll.vco2.minfreq: %d\n", pll_lim->vco2.minfreq); | ||
4337 | ErrorF("pll.vco2.maxfreq: %d\n", pll_lim->vco2.maxfreq); | ||
4338 | |||
4339 | ErrorF("pll.vco1.min_inputfreq: %d\n", pll_lim->vco1.min_inputfreq); | ||
4340 | ErrorF("pll.vco1.max_inputfreq: %d\n", pll_lim->vco1.max_inputfreq); | ||
4341 | ErrorF("pll.vco2.min_inputfreq: %d\n", pll_lim->vco2.min_inputfreq); | ||
4342 | ErrorF("pll.vco2.max_inputfreq: %d\n", pll_lim->vco2.max_inputfreq); | ||
4343 | |||
4344 | ErrorF("pll.vco1.min_n: %d\n", pll_lim->vco1.min_n); | ||
4345 | ErrorF("pll.vco1.max_n: %d\n", pll_lim->vco1.max_n); | ||
4346 | ErrorF("pll.vco1.min_m: %d\n", pll_lim->vco1.min_m); | ||
4347 | ErrorF("pll.vco1.max_m: %d\n", pll_lim->vco1.max_m); | ||
4348 | ErrorF("pll.vco2.min_n: %d\n", pll_lim->vco2.min_n); | ||
4349 | ErrorF("pll.vco2.max_n: %d\n", pll_lim->vco2.max_n); | ||
4350 | ErrorF("pll.vco2.min_m: %d\n", pll_lim->vco2.min_m); | ||
4351 | ErrorF("pll.vco2.max_m: %d\n", pll_lim->vco2.max_m); | ||
4352 | |||
4353 | ErrorF("pll.max_log2p: %d\n", pll_lim->max_log2p); | ||
4354 | ErrorF("pll.log2p_bias: %d\n", pll_lim->log2p_bias); | ||
4355 | |||
4356 | ErrorF("pll.refclk: %d\n", pll_lim->refclk); | ||
4357 | #endif | ||
4358 | |||
4359 | return 0; | ||
4360 | } | ||
4361 | |||
4362 | static void parse_bios_version(struct drm_device *dev, struct nvbios *bios, uint16_t offset) | ||
4363 | { | ||
4364 | /* | ||
4365 | * offset + 0 (8 bits): Micro version | ||
4366 | * offset + 1 (8 bits): Minor version | ||
4367 | * offset + 2 (8 bits): Chip version | ||
4368 | * offset + 3 (8 bits): Major version | ||
4369 | */ | ||
4370 | |||
4371 | bios->major_version = bios->data[offset + 3]; | ||
4372 | bios->pub.chip_version = bios->data[offset + 2]; | ||
4373 | NV_TRACE(dev, "Bios version %02x.%02x.%02x.%02x\n", | ||
4374 | bios->data[offset + 3], bios->data[offset + 2], | ||
4375 | bios->data[offset + 1], bios->data[offset]); | ||
4376 | } | ||
4377 | |||
4378 | static void parse_script_table_pointers(struct nvbios *bios, uint16_t offset) | ||
4379 | { | ||
4380 | /* | ||
4381 | * Parses the init table segment for pointers used in script execution. | ||
4382 | * | ||
4383 | * offset + 0 (16 bits): init script tables pointer | ||
4384 | * offset + 2 (16 bits): macro index table pointer | ||
4385 | * offset + 4 (16 bits): macro table pointer | ||
4386 | * offset + 6 (16 bits): condition table pointer | ||
4387 | * offset + 8 (16 bits): io condition table pointer | ||
4388 | * offset + 10 (16 bits): io flag condition table pointer | ||
4389 | * offset + 12 (16 bits): init function table pointer | ||
4390 | */ | ||
4391 | |||
4392 | bios->init_script_tbls_ptr = ROM16(bios->data[offset]); | ||
4393 | bios->macro_index_tbl_ptr = ROM16(bios->data[offset + 2]); | ||
4394 | bios->macro_tbl_ptr = ROM16(bios->data[offset + 4]); | ||
4395 | bios->condition_tbl_ptr = ROM16(bios->data[offset + 6]); | ||
4396 | bios->io_condition_tbl_ptr = ROM16(bios->data[offset + 8]); | ||
4397 | bios->io_flag_condition_tbl_ptr = ROM16(bios->data[offset + 10]); | ||
4398 | bios->init_function_tbl_ptr = ROM16(bios->data[offset + 12]); | ||
4399 | } | ||
4400 | |||
4401 | static int parse_bit_A_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry) | ||
4402 | { | ||
4403 | /* | ||
4404 | * Parses the load detect values for g80 cards. | ||
4405 | * | ||
4406 | * offset + 0 (16 bits): loadval table pointer | ||
4407 | */ | ||
4408 | |||
4409 | uint16_t load_table_ptr; | ||
4410 | uint8_t version, headerlen, entrylen, num_entries; | ||
4411 | |||
4412 | if (bitentry->length != 3) { | ||
4413 | NV_ERROR(dev, "Do not understand BIT A table\n"); | ||
4414 | return -EINVAL; | ||
4415 | } | ||
4416 | |||
4417 | load_table_ptr = ROM16(bios->data[bitentry->offset]); | ||
4418 | |||
4419 | if (load_table_ptr == 0x0) { | ||
4420 | NV_ERROR(dev, "Pointer to BIT loadval table invalid\n"); | ||
4421 | return -EINVAL; | ||
4422 | } | ||
4423 | |||
4424 | version = bios->data[load_table_ptr]; | ||
4425 | |||
4426 | if (version != 0x10) { | ||
4427 | NV_ERROR(dev, "BIT loadval table version %d.%d not supported\n", | ||
4428 | version >> 4, version & 0xF); | ||
4429 | return -ENOSYS; | ||
4430 | } | ||
4431 | |||
4432 | headerlen = bios->data[load_table_ptr + 1]; | ||
4433 | entrylen = bios->data[load_table_ptr + 2]; | ||
4434 | num_entries = bios->data[load_table_ptr + 3]; | ||
4435 | |||
4436 | if (headerlen != 4 || entrylen != 4 || num_entries != 2) { | ||
4437 | NV_ERROR(dev, "Do not understand BIT loadval table\n"); | ||
4438 | return -EINVAL; | ||
4439 | } | ||
4440 | |||
4441 | /* First entry is normal dac, 2nd tv-out perhaps? */ | ||
4442 | bios->pub.dactestval = ROM32(bios->data[load_table_ptr + headerlen]) & 0x3ff; | ||
4443 | |||
4444 | return 0; | ||
4445 | } | ||
4446 | |||
4447 | static int parse_bit_C_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry) | ||
4448 | { | ||
4449 | /* | ||
4450 | * offset + 8 (16 bits): PLL limits table pointer | ||
4451 | * | ||
4452 | * There's more in here, but that's unknown. | ||
4453 | */ | ||
4454 | |||
4455 | if (bitentry->length < 10) { | ||
4456 | NV_ERROR(dev, "Do not understand BIT C table\n"); | ||
4457 | return -EINVAL; | ||
4458 | } | ||
4459 | |||
4460 | bios->pll_limit_tbl_ptr = ROM16(bios->data[bitentry->offset + 8]); | ||
4461 | |||
4462 | return 0; | ||
4463 | } | ||
4464 | |||
4465 | static int parse_bit_display_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry) | ||
4466 | { | ||
4467 | /* | ||
4468 | * Parses the flat panel table segment that the bit entry points to. | ||
4469 | * Starting at bitentry->offset: | ||
4470 | * | ||
4471 | * offset + 0 (16 bits): ??? table pointer - seems to have 18 byte | ||
4472 | * records beginning with a freq. | ||
4473 | * offset + 2 (16 bits): mode table pointer | ||
4474 | */ | ||
4475 | |||
4476 | if (bitentry->length != 4) { | ||
4477 | NV_ERROR(dev, "Do not understand BIT display table\n"); | ||
4478 | return -EINVAL; | ||
4479 | } | ||
4480 | |||
4481 | bios->fp.fptablepointer = ROM16(bios->data[bitentry->offset + 2]); | ||
4482 | |||
4483 | return 0; | ||
4484 | } | ||
4485 | |||
4486 | static int parse_bit_init_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry) | ||
4487 | { | ||
4488 | /* | ||
4489 | * Parses the init table segment that the bit entry points to. | ||
4490 | * | ||
4491 | * See parse_script_table_pointers for layout | ||
4492 | */ | ||
4493 | |||
4494 | if (bitentry->length < 14) { | ||
4495 | NV_ERROR(dev, "Do not understand init table\n"); | ||
4496 | return -EINVAL; | ||
4497 | } | ||
4498 | |||
4499 | parse_script_table_pointers(bios, bitentry->offset); | ||
4500 | |||
4501 | if (bitentry->length >= 16) | ||
4502 | bios->some_script_ptr = ROM16(bios->data[bitentry->offset + 14]); | ||
4503 | if (bitentry->length >= 18) | ||
4504 | bios->init96_tbl_ptr = ROM16(bios->data[bitentry->offset + 16]); | ||
4505 | |||
4506 | return 0; | ||
4507 | } | ||
4508 | |||
4509 | static int parse_bit_i_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry) | ||
4510 | { | ||
4511 | /* | ||
4512 | * BIT 'i' (info?) table | ||
4513 | * | ||
4514 | * offset + 0 (32 bits): BIOS version dword (as in B table) | ||
4515 | * offset + 5 (8 bits): BIOS feature byte (same as for BMP?) | ||
4516 | * offset + 13 (16 bits): pointer to table containing DAC load | ||
4517 | * detection comparison values | ||
4518 | * | ||
4519 | * There's other things in the table, purpose unknown | ||
4520 | */ | ||
4521 | |||
4522 | uint16_t daccmpoffset; | ||
4523 | uint8_t dacver, dacheaderlen; | ||
4524 | |||
4525 | if (bitentry->length < 6) { | ||
4526 | NV_ERROR(dev, "BIT i table too short for needed information\n"); | ||
4527 | return -EINVAL; | ||
4528 | } | ||
4529 | |||
4530 | parse_bios_version(dev, bios, bitentry->offset); | ||
4531 | |||
4532 | /* | ||
4533 | * bit 4 seems to indicate a mobile bios (doesn't suffer from BMP's | ||
4534 | * Quadro identity crisis), other bits possibly as for BMP feature byte | ||
4535 | */ | ||
4536 | bios->feature_byte = bios->data[bitentry->offset + 5]; | ||
4537 | bios->is_mobile = bios->feature_byte & FEATURE_MOBILE; | ||
4538 | |||
4539 | if (bitentry->length < 15) { | ||
4540 | NV_WARN(dev, "BIT i table not long enough for DAC load " | ||
4541 | "detection comparison table\n"); | ||
4542 | return -EINVAL; | ||
4543 | } | ||
4544 | |||
4545 | daccmpoffset = ROM16(bios->data[bitentry->offset + 13]); | ||
4546 | |||
4547 | /* doesn't exist on g80 */ | ||
4548 | if (!daccmpoffset) | ||
4549 | return 0; | ||
4550 | |||
4551 | /* | ||
4552 | * The first value in the table, following the header, is the | ||
4553 | * comparison value, the second entry is a comparison value for | ||
4554 | * TV load detection. | ||
4555 | */ | ||
4556 | |||
4557 | dacver = bios->data[daccmpoffset]; | ||
4558 | dacheaderlen = bios->data[daccmpoffset + 1]; | ||
4559 | |||
4560 | if (dacver != 0x00 && dacver != 0x10) { | ||
4561 | NV_WARN(dev, "DAC load detection comparison table version " | ||
4562 | "%d.%d not known\n", dacver >> 4, dacver & 0xf); | ||
4563 | return -ENOSYS; | ||
4564 | } | ||
4565 | |||
4566 | bios->pub.dactestval = ROM32(bios->data[daccmpoffset + dacheaderlen]); | ||
4567 | bios->pub.tvdactestval = ROM32(bios->data[daccmpoffset + dacheaderlen + 4]); | ||
4568 | |||
4569 | return 0; | ||
4570 | } | ||
4571 | |||
4572 | static int parse_bit_lvds_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry) | ||
4573 | { | ||
4574 | /* | ||
4575 | * Parses the LVDS table segment that the bit entry points to. | ||
4576 | * Starting at bitentry->offset: | ||
4577 | * | ||
4578 | * offset + 0 (16 bits): LVDS strap xlate table pointer | ||
4579 | */ | ||
4580 | |||
4581 | if (bitentry->length != 2) { | ||
4582 | NV_ERROR(dev, "Do not understand BIT LVDS table\n"); | ||
4583 | return -EINVAL; | ||
4584 | } | ||
4585 | |||
4586 | /* | ||
4587 | * No idea if it's still called the LVDS manufacturer table, but | ||
4588 | * the concept's close enough. | ||
4589 | */ | ||
4590 | bios->fp.lvdsmanufacturerpointer = ROM16(bios->data[bitentry->offset]); | ||
4591 | |||
4592 | return 0; | ||
4593 | } | ||
4594 | |||
4595 | static int | ||
4596 | parse_bit_M_tbl_entry(struct drm_device *dev, struct nvbios *bios, | ||
4597 | struct bit_entry *bitentry) | ||
4598 | { | ||
4599 | /* | ||
4600 | * offset + 2 (8 bits): number of options in an | ||
4601 | * INIT_RAM_RESTRICT_ZM_REG_GROUP opcode option set | ||
4602 | * offset + 3 (16 bits): pointer to strap xlate table for RAM | ||
4603 | * restrict option selection | ||
4604 | * | ||
4605 | * There's a bunch of bits in this table other than the RAM restrict | ||
4606 | * stuff that we don't use - their use currently unknown | ||
4607 | */ | ||
4608 | |||
4609 | uint16_t rr_strap_xlat; | ||
4610 | uint8_t rr_group_count; | ||
4611 | int i; | ||
4612 | |||
4613 | /* | ||
4614 | * Older bios versions don't have a sufficiently long table for | ||
4615 | * what we want | ||
4616 | */ | ||
4617 | if (bitentry->length < 0x5) | ||
4618 | return 0; | ||
4619 | |||
4620 | if (bitentry->id[1] < 2) { | ||
4621 | rr_group_count = bios->data[bitentry->offset + 2]; | ||
4622 | rr_strap_xlat = ROM16(bios->data[bitentry->offset + 3]); | ||
4623 | } else { | ||
4624 | rr_group_count = bios->data[bitentry->offset + 0]; | ||
4625 | rr_strap_xlat = ROM16(bios->data[bitentry->offset + 1]); | ||
4626 | } | ||
4627 | |||
4628 | /* adjust length of INIT_87 */ | ||
4629 | for (i = 0; itbl_entry[i].name && (itbl_entry[i].id != 0x87); i++); | ||
4630 | itbl_entry[i].length += rr_group_count * 4; | ||
4631 | |||
4632 | /* set up multiplier for INIT_RAM_RESTRICT_ZM_REG_GROUP */ | ||
4633 | for (; itbl_entry[i].name && (itbl_entry[i].id != 0x8f); i++); | ||
4634 | itbl_entry[i].length_multiplier = rr_group_count * 4; | ||
4635 | |||
4636 | init_ram_restrict_zm_reg_group_blocklen = itbl_entry[i].length_multiplier; | ||
4637 | bios->ram_restrict_tbl_ptr = rr_strap_xlat; | ||
4638 | |||
4639 | return 0; | ||
4640 | } | ||
4641 | |||
4642 | static int parse_bit_tmds_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry) | ||
4643 | { | ||
4644 | /* | ||
4645 | * Parses the pointer to the TMDS table | ||
4646 | * | ||
4647 | * Starting at bitentry->offset: | ||
4648 | * | ||
4649 | * offset + 0 (16 bits): TMDS table pointer | ||
4650 | * | ||
4651 | * The TMDS table is typically found just before the DCB table, with a | ||
4652 | * characteristic signature of 0x11,0x13 (1.1 being version, 0x13 being | ||
4653 | * length?) | ||
4654 | * | ||
4655 | * At offset +7 is a pointer to a script, which I don't know how to | ||
4656 | * run yet. | ||
4657 | * At offset +9 is a pointer to another script, likewise | ||
4658 | * Offset +11 has a pointer to a table where the first word is a pxclk | ||
4659 | * frequency and the second word a pointer to a script, which should be | ||
4660 | * run if the comparison pxclk frequency is less than the pxclk desired. | ||
4661 | * This repeats for decreasing comparison frequencies | ||
4662 | * Offset +13 has a pointer to a similar table | ||
4663 | * The selection of table (and possibly +7/+9 script) is dictated by | ||
4664 | * "or" from the DCB. | ||
4665 | */ | ||
4666 | |||
4667 | uint16_t tmdstableptr, script1, script2; | ||
4668 | |||
4669 | if (bitentry->length != 2) { | ||
4670 | NV_ERROR(dev, "Do not understand BIT TMDS table\n"); | ||
4671 | return -EINVAL; | ||
4672 | } | ||
4673 | |||
4674 | tmdstableptr = ROM16(bios->data[bitentry->offset]); | ||
4675 | |||
4676 | if (tmdstableptr == 0x0) { | ||
4677 | NV_ERROR(dev, "Pointer to TMDS table invalid\n"); | ||
4678 | return -EINVAL; | ||
4679 | } | ||
4680 | |||
4681 | /* nv50+ has v2.0, but we don't parse it atm */ | ||
4682 | if (bios->data[tmdstableptr] != 0x11) { | ||
4683 | NV_WARN(dev, | ||
4684 | "TMDS table revision %d.%d not currently supported\n", | ||
4685 | bios->data[tmdstableptr] >> 4, bios->data[tmdstableptr] & 0xf); | ||
4686 | return -ENOSYS; | ||
4687 | } | ||
4688 | |||
4689 | /* | ||
4690 | * These two scripts are odd: they don't seem to get run even when | ||
4691 | * they are not stubbed. | ||
4692 | */ | ||
4693 | script1 = ROM16(bios->data[tmdstableptr + 7]); | ||
4694 | script2 = ROM16(bios->data[tmdstableptr + 9]); | ||
4695 | if (bios->data[script1] != 'q' || bios->data[script2] != 'q') | ||
4696 | NV_WARN(dev, "TMDS table script pointers not stubbed\n"); | ||
4697 | |||
4698 | bios->tmds.output0_script_ptr = ROM16(bios->data[tmdstableptr + 11]); | ||
4699 | bios->tmds.output1_script_ptr = ROM16(bios->data[tmdstableptr + 13]); | ||
4700 | |||
4701 | return 0; | ||
4702 | } | ||
4703 | |||
4704 | static int | ||
4705 | parse_bit_U_tbl_entry(struct drm_device *dev, struct nvbios *bios, | ||
4706 | struct bit_entry *bitentry) | ||
4707 | { | ||
4708 | /* | ||
4709 | * Parses the pointer to the G80 output script tables | ||
4710 | * | ||
4711 | * Starting at bitentry->offset: | ||
4712 | * | ||
4713 | * offset + 0 (16 bits): output script table pointer | ||
4714 | */ | ||
4715 | |||
4716 | uint16_t outputscripttableptr; | ||
4717 | |||
4718 | if (bitentry->length != 3) { | ||
4719 | NV_ERROR(dev, "Do not understand BIT U table\n"); | ||
4720 | return -EINVAL; | ||
4721 | } | ||
4722 | |||
4723 | outputscripttableptr = ROM16(bios->data[bitentry->offset]); | ||
4724 | bios->display.script_table_ptr = outputscripttableptr; | ||
4725 | return 0; | ||
4726 | } | ||
4727 | |||
4728 | static int | ||
4729 | parse_bit_displayport_tbl_entry(struct drm_device *dev, struct nvbios *bios, | ||
4730 | struct bit_entry *bitentry) | ||
4731 | { | ||
4732 | bios->display.dp_table_ptr = ROM16(bios->data[bitentry->offset]); | ||
4733 | return 0; | ||
4734 | } | ||
4735 | |||
4736 | struct bit_table { | ||
4737 | const char id; | ||
4738 | int (* const parse_fn)(struct drm_device *, struct nvbios *, struct bit_entry *); | ||
4739 | }; | ||
4740 | |||
4741 | #define BIT_TABLE(id, funcid) ((struct bit_table){ id, parse_bit_##funcid##_tbl_entry }) | ||
4742 | |||
4743 | static int | ||
4744 | parse_bit_table(struct nvbios *bios, const uint16_t bitoffset, | ||
4745 | struct bit_table *table) | ||
4746 | { | ||
4747 | struct drm_device *dev = bios->dev; | ||
4748 | uint8_t maxentries = bios->data[bitoffset + 4]; | ||
4749 | int i, offset; | ||
4750 | struct bit_entry bitentry; | ||
4751 | |||
4752 | for (i = 0, offset = bitoffset + 6; i < maxentries; i++, offset += 6) { | ||
4753 | bitentry.id[0] = bios->data[offset]; | ||
4754 | |||
4755 | if (bitentry.id[0] != table->id) | ||
4756 | continue; | ||
4757 | |||
4758 | bitentry.id[1] = bios->data[offset + 1]; | ||
4759 | bitentry.length = ROM16(bios->data[offset + 2]); | ||
4760 | bitentry.offset = ROM16(bios->data[offset + 4]); | ||
4761 | |||
4762 | return table->parse_fn(dev, bios, &bitentry); | ||
4763 | } | ||
4764 | |||
4765 | NV_INFO(dev, "BIT table '%c' not found\n", table->id); | ||
4766 | return -ENOSYS; | ||
4767 | } | ||
4768 | |||
4769 | static int | ||
4770 | parse_bit_structure(struct nvbios *bios, const uint16_t bitoffset) | ||
4771 | { | ||
4772 | int ret; | ||
4773 | |||
4774 | /* | ||
4775 | * The only restriction on parsing order currently is having 'i' first | ||
4776 | * for use of bios->*_version or bios->feature_byte while parsing; | ||
4777 | * functions shouldn't be actually *doing* anything apart from pulling | ||
4778 | * data from the image into the bios struct, thus no interdependencies | ||
4779 | */ | ||
4780 | ret = parse_bit_table(bios, bitoffset, &BIT_TABLE('i', i)); | ||
4781 | if (ret) /* info? */ | ||
4782 | return ret; | ||
4783 | if (bios->major_version >= 0x60) /* g80+ */ | ||
4784 | parse_bit_table(bios, bitoffset, &BIT_TABLE('A', A)); | ||
4785 | ret = parse_bit_table(bios, bitoffset, &BIT_TABLE('C', C)); | ||
4786 | if (ret) | ||
4787 | return ret; | ||
4788 | parse_bit_table(bios, bitoffset, &BIT_TABLE('D', display)); | ||
4789 | ret = parse_bit_table(bios, bitoffset, &BIT_TABLE('I', init)); | ||
4790 | if (ret) | ||
4791 | return ret; | ||
4792 | parse_bit_table(bios, bitoffset, &BIT_TABLE('M', M)); /* memory? */ | ||
4793 | parse_bit_table(bios, bitoffset, &BIT_TABLE('L', lvds)); | ||
4794 | parse_bit_table(bios, bitoffset, &BIT_TABLE('T', tmds)); | ||
4795 | parse_bit_table(bios, bitoffset, &BIT_TABLE('U', U)); | ||
4796 | parse_bit_table(bios, bitoffset, &BIT_TABLE('d', displayport)); | ||
4797 | |||
4798 | return 0; | ||
4799 | } | ||
4800 | |||
4801 | static int parse_bmp_structure(struct drm_device *dev, struct nvbios *bios, unsigned int offset) | ||
4802 | { | ||
4803 | /* | ||
4804 | * Parses the BMP structure for useful things, but does not act on them | ||
4805 | * | ||
4806 | * offset + 5: BMP major version | ||
4807 | * offset + 6: BMP minor version | ||
4808 | * offset + 9: BMP feature byte | ||
4809 | * offset + 10: BCD encoded BIOS version | ||
4810 | * | ||
4811 | * offset + 18: init script table pointer (for bios versions < 5.10h) | ||
4812 | * offset + 20: extra init script table pointer (for bios | ||
4813 | * versions < 5.10h) | ||
4814 | * | ||
4815 | * offset + 24: memory init table pointer (used on early bios versions) | ||
4816 | * offset + 26: SDR memory sequencing setup data table | ||
4817 | * offset + 28: DDR memory sequencing setup data table | ||
4818 | * | ||
4819 | * offset + 54: index of I2C CRTC pair to use for CRT output | ||
4820 | * offset + 55: index of I2C CRTC pair to use for TV output | ||
4821 | * offset + 56: index of I2C CRTC pair to use for flat panel output | ||
4822 | * offset + 58: write CRTC index for I2C pair 0 | ||
4823 | * offset + 59: read CRTC index for I2C pair 0 | ||
4824 | * offset + 60: write CRTC index for I2C pair 1 | ||
4825 | * offset + 61: read CRTC index for I2C pair 1 | ||
4826 | * | ||
4827 | * offset + 67: maximum internal PLL frequency (single stage PLL) | ||
4828 | * offset + 71: minimum internal PLL frequency (single stage PLL) | ||
4829 | * | ||
4830 | * offset + 75: script table pointers, as described in | ||
4831 | * parse_script_table_pointers | ||
4832 | * | ||
4833 | * offset + 89: TMDS single link output A table pointer | ||
4834 | * offset + 91: TMDS single link output B table pointer | ||
4835 | * offset + 95: LVDS single link output A table pointer | ||
4836 | * offset + 105: flat panel timings table pointer | ||
4837 | * offset + 107: flat panel strapping translation table pointer | ||
4838 | * offset + 117: LVDS manufacturer panel config table pointer | ||
4839 | * offset + 119: LVDS manufacturer strapping translation table pointer | ||
4840 | * | ||
4841 | * offset + 142: PLL limits table pointer | ||
4842 | * | ||
4843 | * offset + 156: minimum pixel clock for LVDS dual link | ||
4844 | */ | ||
4845 | |||
4846 | uint8_t *bmp = &bios->data[offset], bmp_version_major, bmp_version_minor; | ||
4847 | uint16_t bmplength; | ||
4848 | uint16_t legacy_scripts_offset, legacy_i2c_offset; | ||
4849 | |||
4850 | /* load needed defaults in case we can't parse this info */ | ||
4851 | bios->bdcb.dcb.i2c[0].write = NV_CIO_CRE_DDC_WR__INDEX; | ||
4852 | bios->bdcb.dcb.i2c[0].read = NV_CIO_CRE_DDC_STATUS__INDEX; | ||
4853 | bios->bdcb.dcb.i2c[1].write = NV_CIO_CRE_DDC0_WR__INDEX; | ||
4854 | bios->bdcb.dcb.i2c[1].read = NV_CIO_CRE_DDC0_STATUS__INDEX; | ||
4855 | bios->pub.digital_min_front_porch = 0x4b; | ||
4856 | bios->fmaxvco = 256000; | ||
4857 | bios->fminvco = 128000; | ||
4858 | bios->fp.duallink_transition_clk = 90000; | ||
4859 | |||
4860 | bmp_version_major = bmp[5]; | ||
4861 | bmp_version_minor = bmp[6]; | ||
4862 | |||
4863 | NV_TRACE(dev, "BMP version %d.%d\n", | ||
4864 | bmp_version_major, bmp_version_minor); | ||
4865 | |||
4866 | /* | ||
4867 | * Make sure that 0x36 is blank and can't be mistaken for a DCB | ||
4868 | * pointer on early versions | ||
4869 | */ | ||
4870 | if (bmp_version_major < 5) | ||
4871 | *(uint16_t *)&bios->data[0x36] = 0; | ||
4872 | |||
4873 | /* | ||
4874 | * Seems that the minor version was 1 for all major versions prior | ||
4875 | * to 5. Version 6 could theoretically exist, but I suspect BIT | ||
4876 | * happened instead. | ||
4877 | */ | ||
4878 | if ((bmp_version_major < 5 && bmp_version_minor != 1) || bmp_version_major > 5) { | ||
4879 | NV_ERROR(dev, "You have an unsupported BMP version. " | ||
4880 | "Please send in your bios\n"); | ||
4881 | return -ENOSYS; | ||
4882 | } | ||
4883 | |||
4884 | if (bmp_version_major == 0) | ||
4885 | /* nothing that's currently useful in this version */ | ||
4886 | return 0; | ||
4887 | else if (bmp_version_major == 1) | ||
4888 | bmplength = 44; /* exact for 1.01 */ | ||
4889 | else if (bmp_version_major == 2) | ||
4890 | bmplength = 48; /* exact for 2.01 */ | ||
4891 | else if (bmp_version_major == 3) | ||
4892 | bmplength = 54; | ||
4893 | /* guessed - mem init tables added in this version */ | ||
4894 | else if (bmp_version_major == 4 || bmp_version_minor < 0x1) | ||
4895 | /* don't know if 5.0 exists... */ | ||
4896 | bmplength = 62; | ||
4897 | /* guessed - BMP I2C indices added in version 4*/ | ||
4898 | else if (bmp_version_minor < 0x6) | ||
4899 | bmplength = 67; /* exact for 5.01 */ | ||
4900 | else if (bmp_version_minor < 0x10) | ||
4901 | bmplength = 75; /* exact for 5.06 */ | ||
4902 | else if (bmp_version_minor == 0x10) | ||
4903 | bmplength = 89; /* exact for 5.10h */ | ||
4904 | else if (bmp_version_minor < 0x14) | ||
4905 | bmplength = 118; /* exact for 5.11h */ | ||
4906 | else if (bmp_version_minor < 0x24) | ||
4907 | /* | ||
4908 | * Not sure of version where pll limits came in; | ||
4909 | * certainly exist by 0x24 though. | ||
4910 | */ | ||
4911 | /* length not exact: this is long enough to get lvds members */ | ||
4912 | bmplength = 123; | ||
4913 | else if (bmp_version_minor < 0x27) | ||
4914 | /* | ||
4915 | * Length not exact: this is long enough to get pll limit | ||
4916 | * member | ||
4917 | */ | ||
4918 | bmplength = 144; | ||
4919 | else | ||
4920 | /* | ||
4921 | * Length not exact: this is long enough to get dual link | ||
4922 | * transition clock. | ||
4923 | */ | ||
4924 | bmplength = 158; | ||
4925 | |||
4926 | /* checksum */ | ||
4927 | if (nv_cksum(bmp, 8)) { | ||
4928 | NV_ERROR(dev, "Bad BMP checksum\n"); | ||
4929 | return -EINVAL; | ||
4930 | } | ||
4931 | |||
4932 | /* | ||
4933 | * Bit 4 seems to indicate either a mobile bios or a quadro card -- | ||
4934 | * mobile behaviour consistent (nv11+), quadro only seen nv18gl-nv36gl | ||
4935 | * (not nv10gl), bit 5 that the flat panel tables are present, and | ||
4936 | * bit 6 a tv bios. | ||
4937 | */ | ||
4938 | bios->feature_byte = bmp[9]; | ||
4939 | |||
4940 | parse_bios_version(dev, bios, offset + 10); | ||
4941 | |||
4942 | if (bmp_version_major < 5 || bmp_version_minor < 0x10) | ||
4943 | bios->old_style_init = true; | ||
4944 | legacy_scripts_offset = 18; | ||
4945 | if (bmp_version_major < 2) | ||
4946 | legacy_scripts_offset -= 4; | ||
4947 | bios->init_script_tbls_ptr = ROM16(bmp[legacy_scripts_offset]); | ||
4948 | bios->extra_init_script_tbl_ptr = ROM16(bmp[legacy_scripts_offset + 2]); | ||
4949 | |||
4950 | if (bmp_version_major > 2) { /* appears in BMP 3 */ | ||
4951 | bios->legacy.mem_init_tbl_ptr = ROM16(bmp[24]); | ||
4952 | bios->legacy.sdr_seq_tbl_ptr = ROM16(bmp[26]); | ||
4953 | bios->legacy.ddr_seq_tbl_ptr = ROM16(bmp[28]); | ||
4954 | } | ||
4955 | |||
4956 | legacy_i2c_offset = 0x48; /* BMP version 2 & 3 */ | ||
4957 | if (bmplength > 61) | ||
4958 | legacy_i2c_offset = offset + 54; | ||
4959 | bios->legacy.i2c_indices.crt = bios->data[legacy_i2c_offset]; | ||
4960 | bios->legacy.i2c_indices.tv = bios->data[legacy_i2c_offset + 1]; | ||
4961 | bios->legacy.i2c_indices.panel = bios->data[legacy_i2c_offset + 2]; | ||
4962 | bios->bdcb.dcb.i2c[0].write = bios->data[legacy_i2c_offset + 4]; | ||
4963 | bios->bdcb.dcb.i2c[0].read = bios->data[legacy_i2c_offset + 5]; | ||
4964 | bios->bdcb.dcb.i2c[1].write = bios->data[legacy_i2c_offset + 6]; | ||
4965 | bios->bdcb.dcb.i2c[1].read = bios->data[legacy_i2c_offset + 7]; | ||
4966 | |||
4967 | if (bmplength > 74) { | ||
4968 | bios->fmaxvco = ROM32(bmp[67]); | ||
4969 | bios->fminvco = ROM32(bmp[71]); | ||
4970 | } | ||
4971 | if (bmplength > 88) | ||
4972 | parse_script_table_pointers(bios, offset + 75); | ||
4973 | if (bmplength > 94) { | ||
4974 | bios->tmds.output0_script_ptr = ROM16(bmp[89]); | ||
4975 | bios->tmds.output1_script_ptr = ROM16(bmp[91]); | ||
4976 | /* | ||
4977 | * Never observed in use with lvds scripts, but is reused for | ||
4978 | * 18/24 bit panel interface default for EDID equipped panels | ||
4979 | * (if_is_24bit not set directly to avoid any oscillation). | ||
4980 | */ | ||
4981 | bios->legacy.lvds_single_a_script_ptr = ROM16(bmp[95]); | ||
4982 | } | ||
4983 | if (bmplength > 108) { | ||
4984 | bios->fp.fptablepointer = ROM16(bmp[105]); | ||
4985 | bios->fp.fpxlatetableptr = ROM16(bmp[107]); | ||
4986 | bios->fp.xlatwidth = 1; | ||
4987 | } | ||
4988 | if (bmplength > 120) { | ||
4989 | bios->fp.lvdsmanufacturerpointer = ROM16(bmp[117]); | ||
4990 | bios->fp.fpxlatemanufacturertableptr = ROM16(bmp[119]); | ||
4991 | } | ||
4992 | if (bmplength > 143) | ||
4993 | bios->pll_limit_tbl_ptr = ROM16(bmp[142]); | ||
4994 | |||
4995 | if (bmplength > 157) | ||
4996 | bios->fp.duallink_transition_clk = ROM16(bmp[156]) * 10; | ||
4997 | |||
4998 | return 0; | ||
4999 | } | ||
5000 | |||
5001 | static uint16_t findstr(uint8_t *data, int n, const uint8_t *str, int len) | ||
5002 | { | ||
5003 | int i, j; | ||
5004 | |||
5005 | for (i = 0; i <= (n - len); i++) { | ||
5006 | for (j = 0; j < len; j++) | ||
5007 | if (data[i + j] != str[j]) | ||
5008 | break; | ||
5009 | if (j == len) | ||
5010 | return i; | ||
5011 | } | ||
5012 | |||
5013 | return 0; | ||
5014 | } | ||
5015 | |||
5016 | static int | ||
5017 | read_dcb_i2c_entry(struct drm_device *dev, int dcb_version, uint8_t *i2ctable, int index, struct dcb_i2c_entry *i2c) | ||
5018 | { | ||
5019 | uint8_t dcb_i2c_ver = dcb_version, headerlen = 0, entry_len = 4; | ||
5020 | int i2c_entries = DCB_MAX_NUM_I2C_ENTRIES; | ||
5021 | int recordoffset = 0, rdofs = 1, wrofs = 0; | ||
5022 | uint8_t port_type = 0; | ||
5023 | |||
5024 | if (!i2ctable) | ||
5025 | return -EINVAL; | ||
5026 | |||
5027 | if (dcb_version >= 0x30) { | ||
5028 | if (i2ctable[0] != dcb_version) /* necessary? */ | ||
5029 | NV_WARN(dev, | ||
5030 | "DCB I2C table version mismatch (%02X vs %02X)\n", | ||
5031 | i2ctable[0], dcb_version); | ||
5032 | dcb_i2c_ver = i2ctable[0]; | ||
5033 | headerlen = i2ctable[1]; | ||
5034 | if (i2ctable[2] <= DCB_MAX_NUM_I2C_ENTRIES) | ||
5035 | i2c_entries = i2ctable[2]; | ||
5036 | else | ||
5037 | NV_WARN(dev, | ||
5038 | "DCB I2C table has more entries than indexable " | ||
5039 | "(%d entries, max index 15)\n", i2ctable[2]); | ||
5040 | entry_len = i2ctable[3]; | ||
5041 | /* [4] is i2c_default_indices, read in parse_dcb_table() */ | ||
5042 | } | ||
5043 | /* | ||
5044 | * It's your own fault if you call this function on a DCB 1.1 BIOS -- | ||
5045 | * the test below is for DCB 1.2 | ||
5046 | */ | ||
5047 | if (dcb_version < 0x14) { | ||
5048 | recordoffset = 2; | ||
5049 | rdofs = 0; | ||
5050 | wrofs = 1; | ||
5051 | } | ||
5052 | |||
5053 | if (index == 0xf) | ||
5054 | return 0; | ||
5055 | if (index > i2c_entries) { | ||
5056 | NV_ERROR(dev, "DCB I2C index too big (%d > %d)\n", | ||
5057 | index, i2ctable[2]); | ||
5058 | return -ENOENT; | ||
5059 | } | ||
5060 | if (i2ctable[headerlen + entry_len * index + 3] == 0xff) { | ||
5061 | NV_ERROR(dev, "DCB I2C entry invalid\n"); | ||
5062 | return -EINVAL; | ||
5063 | } | ||
5064 | |||
5065 | if (dcb_i2c_ver >= 0x30) { | ||
5066 | port_type = i2ctable[headerlen + recordoffset + 3 + entry_len * index]; | ||
5067 | |||
5068 | /* | ||
5069 | * Fixup for chips using same address offset for read and | ||
5070 | * write. | ||
5071 | */ | ||
5072 | if (port_type == 4) /* seen on C51 */ | ||
5073 | rdofs = wrofs = 1; | ||
5074 | if (port_type >= 5) /* G80+ */ | ||
5075 | rdofs = wrofs = 0; | ||
5076 | } | ||
5077 | |||
5078 | if (dcb_i2c_ver >= 0x40 && port_type != 5 && port_type != 6) | ||
5079 | NV_WARN(dev, "DCB I2C table has port type %d\n", port_type); | ||
5080 | |||
5081 | i2c->port_type = port_type; | ||
5082 | i2c->read = i2ctable[headerlen + recordoffset + rdofs + entry_len * index]; | ||
5083 | i2c->write = i2ctable[headerlen + recordoffset + wrofs + entry_len * index]; | ||
5084 | |||
5085 | return 0; | ||
5086 | } | ||
5087 | |||
5088 | static struct dcb_gpio_entry * | ||
5089 | new_gpio_entry(struct nvbios *bios) | ||
5090 | { | ||
5091 | struct parsed_dcb_gpio *gpio = &bios->bdcb.gpio; | ||
5092 | |||
5093 | return &gpio->entry[gpio->entries++]; | ||
5094 | } | ||
5095 | |||
5096 | struct dcb_gpio_entry * | ||
5097 | nouveau_bios_gpio_entry(struct drm_device *dev, enum dcb_gpio_tag tag) | ||
5098 | { | ||
5099 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
5100 | struct nvbios *bios = &dev_priv->VBIOS; | ||
5101 | int i; | ||
5102 | |||
5103 | for (i = 0; i < bios->bdcb.gpio.entries; i++) { | ||
5104 | if (bios->bdcb.gpio.entry[i].tag != tag) | ||
5105 | continue; | ||
5106 | |||
5107 | return &bios->bdcb.gpio.entry[i]; | ||
5108 | } | ||
5109 | |||
5110 | return NULL; | ||
5111 | } | ||
5112 | |||
5113 | static void | ||
5114 | parse_dcb30_gpio_entry(struct nvbios *bios, uint16_t offset) | ||
5115 | { | ||
5116 | struct dcb_gpio_entry *gpio; | ||
5117 | uint16_t ent = ROM16(bios->data[offset]); | ||
5118 | uint8_t line = ent & 0x1f, | ||
5119 | tag = ent >> 5 & 0x3f, | ||
5120 | flags = ent >> 11 & 0x1f; | ||
5121 | |||
5122 | if (tag == 0x3f) | ||
5123 | return; | ||
5124 | |||
5125 | gpio = new_gpio_entry(bios); | ||
5126 | |||
5127 | gpio->tag = tag; | ||
5128 | gpio->line = line; | ||
5129 | gpio->invert = flags != 4; | ||
5130 | } | ||
5131 | |||
5132 | static void | ||
5133 | parse_dcb40_gpio_entry(struct nvbios *bios, uint16_t offset) | ||
5134 | { | ||
5135 | struct dcb_gpio_entry *gpio; | ||
5136 | uint32_t ent = ROM32(bios->data[offset]); | ||
5137 | uint8_t line = ent & 0x1f, | ||
5138 | tag = ent >> 8 & 0xff; | ||
5139 | |||
5140 | if (tag == 0xff) | ||
5141 | return; | ||
5142 | |||
5143 | gpio = new_gpio_entry(bios); | ||
5144 | |||
5145 | /* Currently unused, we may need more fields parsed at some | ||
5146 | * point. */ | ||
5147 | gpio->tag = tag; | ||
5148 | gpio->line = line; | ||
5149 | } | ||
5150 | |||
5151 | static void | ||
5152 | parse_dcb_gpio_table(struct nvbios *bios) | ||
5153 | { | ||
5154 | struct drm_device *dev = bios->dev; | ||
5155 | uint16_t gpio_table_ptr = bios->bdcb.gpio_table_ptr; | ||
5156 | uint8_t *gpio_table = &bios->data[gpio_table_ptr]; | ||
5157 | int header_len = gpio_table[1], | ||
5158 | entries = gpio_table[2], | ||
5159 | entry_len = gpio_table[3]; | ||
5160 | void (*parse_entry)(struct nvbios *, uint16_t) = NULL; | ||
5161 | int i; | ||
5162 | |||
5163 | if (bios->bdcb.version >= 0x40) { | ||
5164 | if (gpio_table_ptr && entry_len != 4) { | ||
5165 | NV_WARN(dev, "Invalid DCB GPIO table entry length.\n"); | ||
5166 | return; | ||
5167 | } | ||
5168 | |||
5169 | parse_entry = parse_dcb40_gpio_entry; | ||
5170 | |||
5171 | } else if (bios->bdcb.version >= 0x30) { | ||
5172 | if (gpio_table_ptr && entry_len != 2) { | ||
5173 | NV_WARN(dev, "Invalid DCB GPIO table entry length.\n"); | ||
5174 | return; | ||
5175 | } | ||
5176 | |||
5177 | parse_entry = parse_dcb30_gpio_entry; | ||
5178 | |||
5179 | } else if (bios->bdcb.version >= 0x22) { | ||
5180 | /* | ||
5181 | * DCBs older than v3.0 don't really have a GPIO | ||
5182 | * table, instead they keep some GPIO info at fixed | ||
5183 | * locations. | ||
5184 | */ | ||
5185 | uint16_t dcbptr = ROM16(bios->data[0x36]); | ||
5186 | uint8_t *tvdac_gpio = &bios->data[dcbptr - 5]; | ||
5187 | |||
5188 | if (tvdac_gpio[0] & 1) { | ||
5189 | struct dcb_gpio_entry *gpio = new_gpio_entry(bios); | ||
5190 | |||
5191 | gpio->tag = DCB_GPIO_TVDAC0; | ||
5192 | gpio->line = tvdac_gpio[1] >> 4; | ||
5193 | gpio->invert = tvdac_gpio[0] & 2; | ||
5194 | } | ||
5195 | } | ||
5196 | |||
5197 | if (!gpio_table_ptr) | ||
5198 | return; | ||
5199 | |||
5200 | if (entries > DCB_MAX_NUM_GPIO_ENTRIES) { | ||
5201 | NV_WARN(dev, "Too many entries in the DCB GPIO table.\n"); | ||
5202 | entries = DCB_MAX_NUM_GPIO_ENTRIES; | ||
5203 | } | ||
5204 | |||
5205 | for (i = 0; i < entries; i++) | ||
5206 | parse_entry(bios, gpio_table_ptr + header_len + entry_len * i); | ||
5207 | } | ||
5208 | |||
5209 | struct dcb_connector_table_entry * | ||
5210 | nouveau_bios_connector_entry(struct drm_device *dev, int index) | ||
5211 | { | ||
5212 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
5213 | struct nvbios *bios = &dev_priv->VBIOS; | ||
5214 | struct dcb_connector_table_entry *cte; | ||
5215 | |||
5216 | if (index >= bios->bdcb.connector.entries) | ||
5217 | return NULL; | ||
5218 | |||
5219 | cte = &bios->bdcb.connector.entry[index]; | ||
5220 | if (cte->type == 0xff) | ||
5221 | return NULL; | ||
5222 | |||
5223 | return cte; | ||
5224 | } | ||
5225 | |||
5226 | static void | ||
5227 | parse_dcb_connector_table(struct nvbios *bios) | ||
5228 | { | ||
5229 | struct drm_device *dev = bios->dev; | ||
5230 | struct dcb_connector_table *ct = &bios->bdcb.connector; | ||
5231 | struct dcb_connector_table_entry *cte; | ||
5232 | uint8_t *conntab = &bios->data[bios->bdcb.connector_table_ptr]; | ||
5233 | uint8_t *entry; | ||
5234 | int i; | ||
5235 | |||
5236 | if (!bios->bdcb.connector_table_ptr) { | ||
5237 | NV_DEBUG(dev, "No DCB connector table present\n"); | ||
5238 | return; | ||
5239 | } | ||
5240 | |||
5241 | NV_INFO(dev, "DCB connector table: VHER 0x%02x %d %d %d\n", | ||
5242 | conntab[0], conntab[1], conntab[2], conntab[3]); | ||
5243 | if ((conntab[0] != 0x30 && conntab[0] != 0x40) || | ||
5244 | (conntab[3] != 2 && conntab[3] != 4)) { | ||
5245 | NV_ERROR(dev, " Unknown! Please report.\n"); | ||
5246 | return; | ||
5247 | } | ||
5248 | |||
5249 | ct->entries = conntab[2]; | ||
5250 | |||
5251 | entry = conntab + conntab[1]; | ||
5252 | cte = &ct->entry[0]; | ||
5253 | for (i = 0; i < conntab[2]; i++, entry += conntab[3], cte++) { | ||
5254 | if (conntab[3] == 2) | ||
5255 | cte->entry = ROM16(entry[0]); | ||
5256 | else | ||
5257 | cte->entry = ROM32(entry[0]); | ||
5258 | cte->type = (cte->entry & 0x000000ff) >> 0; | ||
5259 | cte->index = (cte->entry & 0x00000f00) >> 8; | ||
5260 | switch (cte->entry & 0x00033000) { | ||
5261 | case 0x00001000: | ||
5262 | cte->gpio_tag = 0x07; | ||
5263 | break; | ||
5264 | case 0x00002000: | ||
5265 | cte->gpio_tag = 0x08; | ||
5266 | break; | ||
5267 | case 0x00010000: | ||
5268 | cte->gpio_tag = 0x51; | ||
5269 | break; | ||
5270 | case 0x00020000: | ||
5271 | cte->gpio_tag = 0x52; | ||
5272 | break; | ||
5273 | default: | ||
5274 | cte->gpio_tag = 0xff; | ||
5275 | break; | ||
5276 | } | ||
5277 | |||
5278 | if (cte->type == 0xff) | ||
5279 | continue; | ||
5280 | |||
5281 | NV_INFO(dev, " %d: 0x%08x: type 0x%02x idx %d tag 0x%02x\n", | ||
5282 | i, cte->entry, cte->type, cte->index, cte->gpio_tag); | ||
5283 | } | ||
5284 | } | ||
5285 | |||
5286 | static struct dcb_entry *new_dcb_entry(struct parsed_dcb *dcb) | ||
5287 | { | ||
5288 | struct dcb_entry *entry = &dcb->entry[dcb->entries]; | ||
5289 | |||
5290 | memset(entry, 0, sizeof(struct dcb_entry)); | ||
5291 | entry->index = dcb->entries++; | ||
5292 | |||
5293 | return entry; | ||
5294 | } | ||
5295 | |||
5296 | static void fabricate_vga_output(struct parsed_dcb *dcb, int i2c, int heads) | ||
5297 | { | ||
5298 | struct dcb_entry *entry = new_dcb_entry(dcb); | ||
5299 | |||
5300 | entry->type = 0; | ||
5301 | entry->i2c_index = i2c; | ||
5302 | entry->heads = heads; | ||
5303 | entry->location = DCB_LOC_ON_CHIP; | ||
5304 | /* "or" mostly unused in early gen crt modesetting, 0 is fine */ | ||
5305 | } | ||
5306 | |||
5307 | static void fabricate_dvi_i_output(struct parsed_dcb *dcb, bool twoHeads) | ||
5308 | { | ||
5309 | struct dcb_entry *entry = new_dcb_entry(dcb); | ||
5310 | |||
5311 | entry->type = 2; | ||
5312 | entry->i2c_index = LEGACY_I2C_PANEL; | ||
5313 | entry->heads = twoHeads ? 3 : 1; | ||
5314 | entry->location = !DCB_LOC_ON_CHIP; /* ie OFF CHIP */ | ||
5315 | entry->or = 1; /* means |0x10 gets set on CRE_LCD__INDEX */ | ||
5316 | entry->duallink_possible = false; /* SiI164 and co. are single link */ | ||
5317 | |||
5318 | #if 0 | ||
5319 | /* | ||
5320 | * For dvi-a either crtc probably works, but my card appears to only | ||
5321 | * support dvi-d. "nvidia" still attempts to program it for dvi-a, | ||
5322 | * doing the full fp output setup (program 0x6808.. fp dimension regs, | ||
5323 | * setting 0x680848 to 0x10000111 to enable, maybe setting 0x680880); | ||
5324 | * the monitor picks up the mode res ok and lights up, but no pixel | ||
5325 | * data appears, so the board manufacturer probably connected up the | ||
5326 | * sync lines, but missed the video traces / components | ||
5327 | * | ||
5328 | * with this introduction, dvi-a left as an exercise for the reader. | ||
5329 | */ | ||
5330 | fabricate_vga_output(dcb, LEGACY_I2C_PANEL, entry->heads); | ||
5331 | #endif | ||
5332 | } | ||
5333 | |||
5334 | static void fabricate_tv_output(struct parsed_dcb *dcb, bool twoHeads) | ||
5335 | { | ||
5336 | struct dcb_entry *entry = new_dcb_entry(dcb); | ||
5337 | |||
5338 | entry->type = 1; | ||
5339 | entry->i2c_index = LEGACY_I2C_TV; | ||
5340 | entry->heads = twoHeads ? 3 : 1; | ||
5341 | entry->location = !DCB_LOC_ON_CHIP; /* ie OFF CHIP */ | ||
5342 | } | ||
5343 | |||
5344 | static bool | ||
5345 | parse_dcb20_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb, | ||
5346 | uint32_t conn, uint32_t conf, struct dcb_entry *entry) | ||
5347 | { | ||
5348 | entry->type = conn & 0xf; | ||
5349 | entry->i2c_index = (conn >> 4) & 0xf; | ||
5350 | entry->heads = (conn >> 8) & 0xf; | ||
5351 | if (bdcb->version >= 0x40) | ||
5352 | entry->connector = (conn >> 12) & 0xf; | ||
5353 | entry->bus = (conn >> 16) & 0xf; | ||
5354 | entry->location = (conn >> 20) & 0x3; | ||
5355 | entry->or = (conn >> 24) & 0xf; | ||
5356 | /* | ||
5357 | * Normal entries consist of a single bit, but dual link has the | ||
5358 | * next most significant bit set too | ||
5359 | */ | ||
5360 | entry->duallink_possible = | ||
5361 | ((1 << (ffs(entry->or) - 1)) * 3 == entry->or); | ||
5362 | |||
5363 | switch (entry->type) { | ||
5364 | case OUTPUT_ANALOG: | ||
5365 | /* | ||
5366 | * Although the rest of a CRT conf dword is usually | ||
5367 | * zeros, mac biosen have stuff there so we must mask | ||
5368 | */ | ||
5369 | entry->crtconf.maxfreq = (bdcb->version < 0x30) ? | ||
5370 | (conf & 0xffff) * 10 : | ||
5371 | (conf & 0xff) * 10000; | ||
5372 | break; | ||
5373 | case OUTPUT_LVDS: | ||
5374 | { | ||
5375 | uint32_t mask; | ||
5376 | if (conf & 0x1) | ||
5377 | entry->lvdsconf.use_straps_for_mode = true; | ||
5378 | if (bdcb->version < 0x22) { | ||
5379 | mask = ~0xd; | ||
5380 | /* | ||
5381 | * The laptop in bug 14567 lies and claims to not use | ||
5382 | * straps when it does, so assume all DCB 2.0 laptops | ||
5383 | * use straps, until a broken EDID using one is produced | ||
5384 | */ | ||
5385 | entry->lvdsconf.use_straps_for_mode = true; | ||
5386 | /* | ||
5387 | * Both 0x4 and 0x8 show up in v2.0 tables; assume they | ||
5388 | * mean the same thing (probably wrong, but might work) | ||
5389 | */ | ||
5390 | if (conf & 0x4 || conf & 0x8) | ||
5391 | entry->lvdsconf.use_power_scripts = true; | ||
5392 | } else { | ||
5393 | mask = ~0x5; | ||
5394 | if (conf & 0x4) | ||
5395 | entry->lvdsconf.use_power_scripts = true; | ||
5396 | } | ||
5397 | if (conf & mask) { | ||
5398 | /* | ||
5399 | * Until we even try to use these on G8x, it's | ||
5400 | * useless reporting unknown bits. They all are. | ||
5401 | */ | ||
5402 | if (bdcb->version >= 0x40) | ||
5403 | break; | ||
5404 | |||
5405 | NV_ERROR(dev, "Unknown LVDS configuration bits, " | ||
5406 | "please report\n"); | ||
5407 | } | ||
5408 | break; | ||
5409 | } | ||
5410 | case OUTPUT_TV: | ||
5411 | { | ||
5412 | if (bdcb->version >= 0x30) | ||
5413 | entry->tvconf.has_component_output = conf & (0x8 << 4); | ||
5414 | else | ||
5415 | entry->tvconf.has_component_output = false; | ||
5416 | |||
5417 | break; | ||
5418 | } | ||
5419 | case OUTPUT_DP: | ||
5420 | entry->dpconf.sor.link = (conf & 0x00000030) >> 4; | ||
5421 | entry->dpconf.link_bw = (conf & 0x00e00000) >> 21; | ||
5422 | switch ((conf & 0x0f000000) >> 24) { | ||
5423 | case 0xf: | ||
5424 | entry->dpconf.link_nr = 4; | ||
5425 | break; | ||
5426 | case 0x3: | ||
5427 | entry->dpconf.link_nr = 2; | ||
5428 | break; | ||
5429 | default: | ||
5430 | entry->dpconf.link_nr = 1; | ||
5431 | break; | ||
5432 | } | ||
5433 | break; | ||
5434 | case OUTPUT_TMDS: | ||
5435 | entry->tmdsconf.sor.link = (conf & 0x00000030) >> 4; | ||
5436 | break; | ||
5437 | case 0xe: | ||
5438 | /* weird g80 mobile type that "nv" treats as a terminator */ | ||
5439 | bdcb->dcb.entries--; | ||
5440 | return false; | ||
5441 | } | ||
5442 | |||
5443 | /* unsure what DCB version introduces this, 3.0? */ | ||
5444 | if (conf & 0x100000) | ||
5445 | entry->i2c_upper_default = true; | ||
5446 | |||
5447 | return true; | ||
5448 | } | ||
5449 | |||
5450 | static bool | ||
5451 | parse_dcb15_entry(struct drm_device *dev, struct parsed_dcb *dcb, | ||
5452 | uint32_t conn, uint32_t conf, struct dcb_entry *entry) | ||
5453 | { | ||
5454 | if (conn != 0xf0003f00 && conn != 0xf2247f10 && conn != 0xf2204001 && | ||
5455 | conn != 0xf2204301 && conn != 0xf2204311 && conn != 0xf2208001 && | ||
5456 | conn != 0xf2244001 && conn != 0xf2244301 && conn != 0xf2244311 && | ||
5457 | conn != 0xf4204011 && conn != 0xf4208011 && conn != 0xf4248011 && | ||
5458 | conn != 0xf2045ff2 && conn != 0xf2045f14 && conn != 0xf207df14 && | ||
5459 | conn != 0xf2205004 && conn != 0xf2209004) { | ||
5460 | NV_ERROR(dev, "Unknown DCB 1.5 entry, please report\n"); | ||
5461 | |||
5462 | /* cause output setting to fail for !TV, so message is seen */ | ||
5463 | if ((conn & 0xf) != 0x1) | ||
5464 | dcb->entries = 0; | ||
5465 | |||
5466 | return false; | ||
5467 | } | ||
5468 | /* most of the below is a "best guess" atm */ | ||
5469 | entry->type = conn & 0xf; | ||
5470 | if (entry->type == 2) | ||
5471 | /* another way of specifying straps based lvds... */ | ||
5472 | entry->type = OUTPUT_LVDS; | ||
5473 | if (entry->type == 4) { /* digital */ | ||
5474 | if (conn & 0x10) | ||
5475 | entry->type = OUTPUT_LVDS; | ||
5476 | else | ||
5477 | entry->type = OUTPUT_TMDS; | ||
5478 | } | ||
5479 | /* what's in bits 5-13? could be some encoder maker thing, in tv case */ | ||
5480 | entry->i2c_index = (conn >> 14) & 0xf; | ||
5481 | /* raw heads field is in range 0-1, so move to 1-2 */ | ||
5482 | entry->heads = ((conn >> 18) & 0x7) + 1; | ||
5483 | entry->location = (conn >> 21) & 0xf; | ||
5484 | /* unused: entry->bus = (conn >> 25) & 0x7; */ | ||
5485 | /* set or to be same as heads -- hopefully safe enough */ | ||
5486 | entry->or = entry->heads; | ||
5487 | entry->duallink_possible = false; | ||
5488 | |||
5489 | switch (entry->type) { | ||
5490 | case OUTPUT_ANALOG: | ||
5491 | entry->crtconf.maxfreq = (conf & 0xffff) * 10; | ||
5492 | break; | ||
5493 | case OUTPUT_LVDS: | ||
5494 | /* | ||
5495 | * This is probably buried in conn's unknown bits. | ||
5496 | * This will upset EDID-ful models, if they exist | ||
5497 | */ | ||
5498 | entry->lvdsconf.use_straps_for_mode = true; | ||
5499 | entry->lvdsconf.use_power_scripts = true; | ||
5500 | break; | ||
5501 | case OUTPUT_TMDS: | ||
5502 | /* | ||
5503 | * Invent a DVI-A output, by copying the fields of the DVI-D | ||
5504 | * output; reported to work by math_b on an NV20(!). | ||
5505 | */ | ||
5506 | fabricate_vga_output(dcb, entry->i2c_index, entry->heads); | ||
5507 | break; | ||
5508 | case OUTPUT_TV: | ||
5509 | entry->tvconf.has_component_output = false; | ||
5510 | break; | ||
5511 | } | ||
5512 | |||
5513 | return true; | ||
5514 | } | ||
5515 | |||
5516 | static bool parse_dcb_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb, | ||
5517 | uint32_t conn, uint32_t conf) | ||
5518 | { | ||
5519 | struct dcb_entry *entry = new_dcb_entry(&bdcb->dcb); | ||
5520 | bool ret; | ||
5521 | |||
5522 | if (bdcb->version >= 0x20) | ||
5523 | ret = parse_dcb20_entry(dev, bdcb, conn, conf, entry); | ||
5524 | else | ||
5525 | ret = parse_dcb15_entry(dev, &bdcb->dcb, conn, conf, entry); | ||
5526 | if (!ret) | ||
5527 | return ret; | ||
5528 | |||
5529 | read_dcb_i2c_entry(dev, bdcb->version, bdcb->i2c_table, | ||
5530 | entry->i2c_index, &bdcb->dcb.i2c[entry->i2c_index]); | ||
5531 | |||
5532 | return true; | ||
5533 | } | ||
5534 | |||
5535 | static | ||
5536 | void merge_like_dcb_entries(struct drm_device *dev, struct parsed_dcb *dcb) | ||
5537 | { | ||
5538 | /* | ||
5539 | * DCB v2.0 lists each output combination separately. | ||
5540 | * Here we merge compatible entries to have fewer outputs, with | ||
5541 | * more options | ||
5542 | */ | ||
5543 | |||
5544 | int i, newentries = 0; | ||
5545 | |||
5546 | for (i = 0; i < dcb->entries; i++) { | ||
5547 | struct dcb_entry *ient = &dcb->entry[i]; | ||
5548 | int j; | ||
5549 | |||
5550 | for (j = i + 1; j < dcb->entries; j++) { | ||
5551 | struct dcb_entry *jent = &dcb->entry[j]; | ||
5552 | |||
5553 | if (jent->type == 100) /* already merged entry */ | ||
5554 | continue; | ||
5555 | |||
5556 | /* merge heads field when all other fields the same */ | ||
5557 | if (jent->i2c_index == ient->i2c_index && | ||
5558 | jent->type == ient->type && | ||
5559 | jent->location == ient->location && | ||
5560 | jent->or == ient->or) { | ||
5561 | NV_TRACE(dev, "Merging DCB entries %d and %d\n", | ||
5562 | i, j); | ||
5563 | ient->heads |= jent->heads; | ||
5564 | jent->type = 100; /* dummy value */ | ||
5565 | } | ||
5566 | } | ||
5567 | } | ||
5568 | |||
5569 | /* Compact entries merged into others out of dcb */ | ||
5570 | for (i = 0; i < dcb->entries; i++) { | ||
5571 | if (dcb->entry[i].type == 100) | ||
5572 | continue; | ||
5573 | |||
5574 | if (newentries != i) { | ||
5575 | dcb->entry[newentries] = dcb->entry[i]; | ||
5576 | dcb->entry[newentries].index = newentries; | ||
5577 | } | ||
5578 | newentries++; | ||
5579 | } | ||
5580 | |||
5581 | dcb->entries = newentries; | ||
5582 | } | ||
5583 | |||
5584 | static int parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads) | ||
5585 | { | ||
5586 | struct bios_parsed_dcb *bdcb = &bios->bdcb; | ||
5587 | struct parsed_dcb *dcb; | ||
5588 | uint16_t dcbptr, i2ctabptr = 0; | ||
5589 | uint8_t *dcbtable; | ||
5590 | uint8_t headerlen = 0x4, entries = DCB_MAX_NUM_ENTRIES; | ||
5591 | bool configblock = true; | ||
5592 | int recordlength = 8, confofs = 4; | ||
5593 | int i; | ||
5594 | |||
5595 | dcb = bios->pub.dcb = &bdcb->dcb; | ||
5596 | dcb->entries = 0; | ||
5597 | |||
5598 | /* get the offset from 0x36 */ | ||
5599 | dcbptr = ROM16(bios->data[0x36]); | ||
5600 | |||
5601 | if (dcbptr == 0x0) { | ||
5602 | NV_WARN(dev, "No output data (DCB) found in BIOS, " | ||
5603 | "assuming a CRT output exists\n"); | ||
5604 | /* this situation likely means a really old card, pre DCB */ | ||
5605 | fabricate_vga_output(dcb, LEGACY_I2C_CRT, 1); | ||
5606 | |||
5607 | if (nv04_tv_identify(dev, | ||
5608 | bios->legacy.i2c_indices.tv) >= 0) | ||
5609 | fabricate_tv_output(dcb, twoHeads); | ||
5610 | |||
5611 | return 0; | ||
5612 | } | ||
5613 | |||
5614 | dcbtable = &bios->data[dcbptr]; | ||
5615 | |||
5616 | /* get DCB version */ | ||
5617 | bdcb->version = dcbtable[0]; | ||
5618 | NV_TRACE(dev, "Found Display Configuration Block version %d.%d\n", | ||
5619 | bdcb->version >> 4, bdcb->version & 0xf); | ||
5620 | |||
5621 | if (bdcb->version >= 0x20) { /* NV17+ */ | ||
5622 | uint32_t sig; | ||
5623 | |||
5624 | if (bdcb->version >= 0x30) { /* NV40+ */ | ||
5625 | headerlen = dcbtable[1]; | ||
5626 | entries = dcbtable[2]; | ||
5627 | recordlength = dcbtable[3]; | ||
5628 | i2ctabptr = ROM16(dcbtable[4]); | ||
5629 | sig = ROM32(dcbtable[6]); | ||
5630 | bdcb->gpio_table_ptr = ROM16(dcbtable[10]); | ||
5631 | bdcb->connector_table_ptr = ROM16(dcbtable[20]); | ||
5632 | } else { | ||
5633 | i2ctabptr = ROM16(dcbtable[2]); | ||
5634 | sig = ROM32(dcbtable[4]); | ||
5635 | headerlen = 8; | ||
5636 | } | ||
5637 | |||
5638 | if (sig != 0x4edcbdcb) { | ||
5639 | NV_ERROR(dev, "Bad Display Configuration Block " | ||
5640 | "signature (%08X)\n", sig); | ||
5641 | return -EINVAL; | ||
5642 | } | ||
5643 | } else if (bdcb->version >= 0x15) { /* some NV11 and NV20 */ | ||
5644 | char sig[8] = { 0 }; | ||
5645 | |||
5646 | strncpy(sig, (char *)&dcbtable[-7], 7); | ||
5647 | i2ctabptr = ROM16(dcbtable[2]); | ||
5648 | recordlength = 10; | ||
5649 | confofs = 6; | ||
5650 | |||
5651 | if (strcmp(sig, "DEV_REC")) { | ||
5652 | NV_ERROR(dev, "Bad Display Configuration Block " | ||
5653 | "signature (%s)\n", sig); | ||
5654 | return -EINVAL; | ||
5655 | } | ||
5656 | } else { | ||
5657 | /* | ||
5658 | * v1.4 (some NV15/16, NV11+) seems the same as v1.5, but always | ||
5659 | * has the same single (crt) entry, even when tv-out present, so | ||
5660 | * the conclusion is this version cannot really be used. | ||
5661 | * v1.2 tables (some NV6/10, and NV15+) normally have the same | ||
5662 | * 5 entries, which are not specific to the card and so no use. | ||
5663 | * v1.2 does have an I2C table that read_dcb_i2c_table can | ||
5664 | * handle, but cards exist (nv11 in #14821) with a bad i2c table | ||
5665 | * pointer, so use the indices parsed in parse_bmp_structure. | ||
5666 | * v1.1 (NV5+, maybe some NV4) is entirely unhelpful | ||
5667 | */ | ||
5668 | NV_TRACEWARN(dev, "No useful information in BIOS output table; " | ||
5669 | "adding all possible outputs\n"); | ||
5670 | fabricate_vga_output(dcb, LEGACY_I2C_CRT, 1); | ||
5671 | |||
5672 | /* | ||
5673 | * Attempt to detect TV before DVI because the test | ||
5674 | * for the former is more accurate and it rules the | ||
5675 | * latter out. | ||
5676 | */ | ||
5677 | if (nv04_tv_identify(dev, | ||
5678 | bios->legacy.i2c_indices.tv) >= 0) | ||
5679 | fabricate_tv_output(dcb, twoHeads); | ||
5680 | |||
5681 | else if (bios->tmds.output0_script_ptr || | ||
5682 | bios->tmds.output1_script_ptr) | ||
5683 | fabricate_dvi_i_output(dcb, twoHeads); | ||
5684 | |||
5685 | return 0; | ||
5686 | } | ||
5687 | |||
5688 | if (!i2ctabptr) | ||
5689 | NV_WARN(dev, "No pointer to DCB I2C port table\n"); | ||
5690 | else { | ||
5691 | bdcb->i2c_table = &bios->data[i2ctabptr]; | ||
5692 | if (bdcb->version >= 0x30) | ||
5693 | bdcb->i2c_default_indices = bdcb->i2c_table[4]; | ||
5694 | } | ||
5695 | |||
5696 | parse_dcb_gpio_table(bios); | ||
5697 | parse_dcb_connector_table(bios); | ||
5698 | |||
5699 | if (entries > DCB_MAX_NUM_ENTRIES) | ||
5700 | entries = DCB_MAX_NUM_ENTRIES; | ||
5701 | |||
5702 | for (i = 0; i < entries; i++) { | ||
5703 | uint32_t connection, config = 0; | ||
5704 | |||
5705 | connection = ROM32(dcbtable[headerlen + recordlength * i]); | ||
5706 | if (configblock) | ||
5707 | config = ROM32(dcbtable[headerlen + confofs + recordlength * i]); | ||
5708 | |||
5709 | /* seen on an NV11 with DCB v1.5 */ | ||
5710 | if (connection == 0x00000000) | ||
5711 | break; | ||
5712 | |||
5713 | /* seen on an NV17 with DCB v2.0 */ | ||
5714 | if (connection == 0xffffffff) | ||
5715 | break; | ||
5716 | |||
5717 | if ((connection & 0x0000000f) == 0x0000000f) | ||
5718 | continue; | ||
5719 | |||
5720 | NV_TRACEWARN(dev, "Raw DCB entry %d: %08x %08x\n", | ||
5721 | dcb->entries, connection, config); | ||
5722 | |||
5723 | if (!parse_dcb_entry(dev, bdcb, connection, config)) | ||
5724 | break; | ||
5725 | } | ||
5726 | |||
5727 | /* | ||
5728 | * apart for v2.1+ not being known for requiring merging, this | ||
5729 | * guarantees dcbent->index is the index of the entry in the rom image | ||
5730 | */ | ||
5731 | if (bdcb->version < 0x21) | ||
5732 | merge_like_dcb_entries(dev, dcb); | ||
5733 | |||
5734 | return dcb->entries ? 0 : -ENXIO; | ||
5735 | } | ||
5736 | |||
5737 | static void | ||
5738 | fixup_legacy_connector(struct nvbios *bios) | ||
5739 | { | ||
5740 | struct bios_parsed_dcb *bdcb = &bios->bdcb; | ||
5741 | struct parsed_dcb *dcb = &bdcb->dcb; | ||
5742 | int high = 0, i; | ||
5743 | |||
5744 | /* | ||
5745 | * DCB 3.0 also has the table in most cases, but there are some cards | ||
5746 | * where the table is filled with stub entries, and the DCB entriy | ||
5747 | * indices are all 0. We don't need the connector indices on pre-G80 | ||
5748 | * chips (yet?) so limit the use to DCB 4.0 and above. | ||
5749 | */ | ||
5750 | if (bdcb->version >= 0x40) | ||
5751 | return; | ||
5752 | |||
5753 | /* | ||
5754 | * No known connector info before v3.0, so make it up. the rule here | ||
5755 | * is: anything on the same i2c bus is considered to be on the same | ||
5756 | * connector. any output without an associated i2c bus is assigned | ||
5757 | * its own unique connector index. | ||
5758 | */ | ||
5759 | for (i = 0; i < dcb->entries; i++) { | ||
5760 | if (dcb->entry[i].i2c_index == 0xf) | ||
5761 | continue; | ||
5762 | |||
5763 | /* | ||
5764 | * Ignore the I2C index for on-chip TV-out, as there | ||
5765 | * are cards with bogus values (nv31m in bug 23212), | ||
5766 | * and it's otherwise useless. | ||
5767 | */ | ||
5768 | if (dcb->entry[i].type == OUTPUT_TV && | ||
5769 | dcb->entry[i].location == DCB_LOC_ON_CHIP) { | ||
5770 | dcb->entry[i].i2c_index = 0xf; | ||
5771 | continue; | ||
5772 | } | ||
5773 | |||
5774 | dcb->entry[i].connector = dcb->entry[i].i2c_index; | ||
5775 | if (dcb->entry[i].connector > high) | ||
5776 | high = dcb->entry[i].connector; | ||
5777 | } | ||
5778 | |||
5779 | for (i = 0; i < dcb->entries; i++) { | ||
5780 | if (dcb->entry[i].i2c_index != 0xf) | ||
5781 | continue; | ||
5782 | |||
5783 | dcb->entry[i].connector = ++high; | ||
5784 | } | ||
5785 | } | ||
5786 | |||
5787 | static void | ||
5788 | fixup_legacy_i2c(struct nvbios *bios) | ||
5789 | { | ||
5790 | struct parsed_dcb *dcb = &bios->bdcb.dcb; | ||
5791 | int i; | ||
5792 | |||
5793 | for (i = 0; i < dcb->entries; i++) { | ||
5794 | if (dcb->entry[i].i2c_index == LEGACY_I2C_CRT) | ||
5795 | dcb->entry[i].i2c_index = bios->legacy.i2c_indices.crt; | ||
5796 | if (dcb->entry[i].i2c_index == LEGACY_I2C_PANEL) | ||
5797 | dcb->entry[i].i2c_index = bios->legacy.i2c_indices.panel; | ||
5798 | if (dcb->entry[i].i2c_index == LEGACY_I2C_TV) | ||
5799 | dcb->entry[i].i2c_index = bios->legacy.i2c_indices.tv; | ||
5800 | } | ||
5801 | } | ||
5802 | |||
5803 | static int load_nv17_hwsq_ucode_entry(struct drm_device *dev, struct nvbios *bios, uint16_t hwsq_offset, int entry) | ||
5804 | { | ||
5805 | /* | ||
5806 | * The header following the "HWSQ" signature has the number of entries, | ||
5807 | * and the entry size | ||
5808 | * | ||
5809 | * An entry consists of a dword to write to the sequencer control reg | ||
5810 | * (0x00001304), followed by the ucode bytes, written sequentially, | ||
5811 | * starting at reg 0x00001400 | ||
5812 | */ | ||
5813 | |||
5814 | uint8_t bytes_to_write; | ||
5815 | uint16_t hwsq_entry_offset; | ||
5816 | int i; | ||
5817 | |||
5818 | if (bios->data[hwsq_offset] <= entry) { | ||
5819 | NV_ERROR(dev, "Too few entries in HW sequencer table for " | ||
5820 | "requested entry\n"); | ||
5821 | return -ENOENT; | ||
5822 | } | ||
5823 | |||
5824 | bytes_to_write = bios->data[hwsq_offset + 1]; | ||
5825 | |||
5826 | if (bytes_to_write != 36) { | ||
5827 | NV_ERROR(dev, "Unknown HW sequencer entry size\n"); | ||
5828 | return -EINVAL; | ||
5829 | } | ||
5830 | |||
5831 | NV_TRACE(dev, "Loading NV17 power sequencing microcode\n"); | ||
5832 | |||
5833 | hwsq_entry_offset = hwsq_offset + 2 + entry * bytes_to_write; | ||
5834 | |||
5835 | /* set sequencer control */ | ||
5836 | bios_wr32(bios, 0x00001304, ROM32(bios->data[hwsq_entry_offset])); | ||
5837 | bytes_to_write -= 4; | ||
5838 | |||
5839 | /* write ucode */ | ||
5840 | for (i = 0; i < bytes_to_write; i += 4) | ||
5841 | bios_wr32(bios, 0x00001400 + i, ROM32(bios->data[hwsq_entry_offset + i + 4])); | ||
5842 | |||
5843 | /* twiddle NV_PBUS_DEBUG_4 */ | ||
5844 | bios_wr32(bios, NV_PBUS_DEBUG_4, bios_rd32(bios, NV_PBUS_DEBUG_4) | 0x18); | ||
5845 | |||
5846 | return 0; | ||
5847 | } | ||
5848 | |||
5849 | static int load_nv17_hw_sequencer_ucode(struct drm_device *dev, | ||
5850 | struct nvbios *bios) | ||
5851 | { | ||
5852 | /* | ||
5853 | * BMP based cards, from NV17, need a microcode loading to correctly | ||
5854 | * control the GPIO etc for LVDS panels | ||
5855 | * | ||
5856 | * BIT based cards seem to do this directly in the init scripts | ||
5857 | * | ||
5858 | * The microcode entries are found by the "HWSQ" signature. | ||
5859 | */ | ||
5860 | |||
5861 | const uint8_t hwsq_signature[] = { 'H', 'W', 'S', 'Q' }; | ||
5862 | const int sz = sizeof(hwsq_signature); | ||
5863 | int hwsq_offset; | ||
5864 | |||
5865 | hwsq_offset = findstr(bios->data, bios->length, hwsq_signature, sz); | ||
5866 | if (!hwsq_offset) | ||
5867 | return 0; | ||
5868 | |||
5869 | /* always use entry 0? */ | ||
5870 | return load_nv17_hwsq_ucode_entry(dev, bios, hwsq_offset + sz, 0); | ||
5871 | } | ||
5872 | |||
5873 | uint8_t *nouveau_bios_embedded_edid(struct drm_device *dev) | ||
5874 | { | ||
5875 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
5876 | struct nvbios *bios = &dev_priv->VBIOS; | ||
5877 | const uint8_t edid_sig[] = { | ||
5878 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; | ||
5879 | uint16_t offset = 0; | ||
5880 | uint16_t newoffset; | ||
5881 | int searchlen = NV_PROM_SIZE; | ||
5882 | |||
5883 | if (bios->fp.edid) | ||
5884 | return bios->fp.edid; | ||
5885 | |||
5886 | while (searchlen) { | ||
5887 | newoffset = findstr(&bios->data[offset], searchlen, | ||
5888 | edid_sig, 8); | ||
5889 | if (!newoffset) | ||
5890 | return NULL; | ||
5891 | offset += newoffset; | ||
5892 | if (!nv_cksum(&bios->data[offset], EDID1_LEN)) | ||
5893 | break; | ||
5894 | |||
5895 | searchlen -= offset; | ||
5896 | offset++; | ||
5897 | } | ||
5898 | |||
5899 | NV_TRACE(dev, "Found EDID in BIOS\n"); | ||
5900 | |||
5901 | return bios->fp.edid = &bios->data[offset]; | ||
5902 | } | ||
5903 | |||
5904 | void | ||
5905 | nouveau_bios_run_init_table(struct drm_device *dev, uint16_t table, | ||
5906 | struct dcb_entry *dcbent) | ||
5907 | { | ||
5908 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
5909 | struct nvbios *bios = &dev_priv->VBIOS; | ||
5910 | struct init_exec iexec = { true, false }; | ||
5911 | |||
5912 | bios->display.output = dcbent; | ||
5913 | parse_init_table(bios, table, &iexec); | ||
5914 | bios->display.output = NULL; | ||
5915 | } | ||
5916 | |||
5917 | static bool NVInitVBIOS(struct drm_device *dev) | ||
5918 | { | ||
5919 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
5920 | struct nvbios *bios = &dev_priv->VBIOS; | ||
5921 | |||
5922 | memset(bios, 0, sizeof(struct nvbios)); | ||
5923 | bios->dev = dev; | ||
5924 | |||
5925 | if (!NVShadowVBIOS(dev, bios->data)) | ||
5926 | return false; | ||
5927 | |||
5928 | bios->length = NV_PROM_SIZE; | ||
5929 | return true; | ||
5930 | } | ||
5931 | |||
5932 | static int nouveau_parse_vbios_struct(struct drm_device *dev) | ||
5933 | { | ||
5934 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
5935 | struct nvbios *bios = &dev_priv->VBIOS; | ||
5936 | const uint8_t bit_signature[] = { 0xff, 0xb8, 'B', 'I', 'T' }; | ||
5937 | const uint8_t bmp_signature[] = { 0xff, 0x7f, 'N', 'V', 0x0 }; | ||
5938 | int offset; | ||
5939 | |||
5940 | offset = findstr(bios->data, bios->length, | ||
5941 | bit_signature, sizeof(bit_signature)); | ||
5942 | if (offset) { | ||
5943 | NV_TRACE(dev, "BIT BIOS found\n"); | ||
5944 | return parse_bit_structure(bios, offset + 6); | ||
5945 | } | ||
5946 | |||
5947 | offset = findstr(bios->data, bios->length, | ||
5948 | bmp_signature, sizeof(bmp_signature)); | ||
5949 | if (offset) { | ||
5950 | NV_TRACE(dev, "BMP BIOS found\n"); | ||
5951 | return parse_bmp_structure(dev, bios, offset); | ||
5952 | } | ||
5953 | |||
5954 | NV_ERROR(dev, "No known BIOS signature found\n"); | ||
5955 | return -ENODEV; | ||
5956 | } | ||
5957 | |||
5958 | int | ||
5959 | nouveau_run_vbios_init(struct drm_device *dev) | ||
5960 | { | ||
5961 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
5962 | struct nvbios *bios = &dev_priv->VBIOS; | ||
5963 | int i, ret = 0; | ||
5964 | |||
5965 | NVLockVgaCrtcs(dev, false); | ||
5966 | if (nv_two_heads(dev)) | ||
5967 | NVSetOwner(dev, bios->state.crtchead); | ||
5968 | |||
5969 | if (bios->major_version < 5) /* BMP only */ | ||
5970 | load_nv17_hw_sequencer_ucode(dev, bios); | ||
5971 | |||
5972 | if (bios->execute) { | ||
5973 | bios->fp.last_script_invoc = 0; | ||
5974 | bios->fp.lvds_init_run = false; | ||
5975 | } | ||
5976 | |||
5977 | parse_init_tables(bios); | ||
5978 | |||
5979 | /* | ||
5980 | * Runs some additional script seen on G8x VBIOSen. The VBIOS' | ||
5981 | * parser will run this right after the init tables, the binary | ||
5982 | * driver appears to run it at some point later. | ||
5983 | */ | ||
5984 | if (bios->some_script_ptr) { | ||
5985 | struct init_exec iexec = {true, false}; | ||
5986 | |||
5987 | NV_INFO(dev, "Parsing VBIOS init table at offset 0x%04X\n", | ||
5988 | bios->some_script_ptr); | ||
5989 | parse_init_table(bios, bios->some_script_ptr, &iexec); | ||
5990 | } | ||
5991 | |||
5992 | if (dev_priv->card_type >= NV_50) { | ||
5993 | for (i = 0; i < bios->bdcb.dcb.entries; i++) { | ||
5994 | nouveau_bios_run_display_table(dev, | ||
5995 | &bios->bdcb.dcb.entry[i], | ||
5996 | 0, 0); | ||
5997 | } | ||
5998 | } | ||
5999 | |||
6000 | NVLockVgaCrtcs(dev, true); | ||
6001 | |||
6002 | return ret; | ||
6003 | } | ||
6004 | |||
6005 | static void | ||
6006 | nouveau_bios_i2c_devices_takedown(struct drm_device *dev) | ||
6007 | { | ||
6008 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
6009 | struct nvbios *bios = &dev_priv->VBIOS; | ||
6010 | struct dcb_i2c_entry *entry; | ||
6011 | int i; | ||
6012 | |||
6013 | entry = &bios->bdcb.dcb.i2c[0]; | ||
6014 | for (i = 0; i < DCB_MAX_NUM_I2C_ENTRIES; i++, entry++) | ||
6015 | nouveau_i2c_fini(dev, entry); | ||
6016 | } | ||
6017 | |||
6018 | int | ||
6019 | nouveau_bios_init(struct drm_device *dev) | ||
6020 | { | ||
6021 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
6022 | struct nvbios *bios = &dev_priv->VBIOS; | ||
6023 | uint32_t saved_nv_pextdev_boot_0; | ||
6024 | bool was_locked; | ||
6025 | int ret; | ||
6026 | |||
6027 | dev_priv->vbios = &bios->pub; | ||
6028 | |||
6029 | if (!NVInitVBIOS(dev)) | ||
6030 | return -ENODEV; | ||
6031 | |||
6032 | ret = nouveau_parse_vbios_struct(dev); | ||
6033 | if (ret) | ||
6034 | return ret; | ||
6035 | |||
6036 | ret = parse_dcb_table(dev, bios, nv_two_heads(dev)); | ||
6037 | if (ret) | ||
6038 | return ret; | ||
6039 | |||
6040 | fixup_legacy_i2c(bios); | ||
6041 | fixup_legacy_connector(bios); | ||
6042 | |||
6043 | if (!bios->major_version) /* we don't run version 0 bios */ | ||
6044 | return 0; | ||
6045 | |||
6046 | /* these will need remembering across a suspend */ | ||
6047 | saved_nv_pextdev_boot_0 = bios_rd32(bios, NV_PEXTDEV_BOOT_0); | ||
6048 | bios->state.saved_nv_pfb_cfg0 = bios_rd32(bios, NV_PFB_CFG0); | ||
6049 | |||
6050 | /* init script execution disabled */ | ||
6051 | bios->execute = false; | ||
6052 | |||
6053 | /* ... unless card isn't POSTed already */ | ||
6054 | if (dev_priv->card_type >= NV_10 && | ||
6055 | NVReadVgaCrtc(dev, 0, 0x00) == 0 && | ||
6056 | NVReadVgaCrtc(dev, 0, 0x1a) == 0) { | ||
6057 | NV_INFO(dev, "Adaptor not initialised\n"); | ||
6058 | if (dev_priv->card_type < NV_50) { | ||
6059 | NV_ERROR(dev, "Unable to POST this chipset\n"); | ||
6060 | return -ENODEV; | ||
6061 | } | ||
6062 | |||
6063 | NV_INFO(dev, "Running VBIOS init tables\n"); | ||
6064 | bios->execute = true; | ||
6065 | } | ||
6066 | |||
6067 | bios_wr32(bios, NV_PEXTDEV_BOOT_0, saved_nv_pextdev_boot_0); | ||
6068 | |||
6069 | ret = nouveau_run_vbios_init(dev); | ||
6070 | if (ret) { | ||
6071 | dev_priv->vbios = NULL; | ||
6072 | return ret; | ||
6073 | } | ||
6074 | |||
6075 | /* feature_byte on BMP is poor, but init always sets CR4B */ | ||
6076 | was_locked = NVLockVgaCrtcs(dev, false); | ||
6077 | if (bios->major_version < 5) | ||
6078 | bios->is_mobile = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_4B) & 0x40; | ||
6079 | |||
6080 | /* all BIT systems need p_f_m_t for digital_min_front_porch */ | ||
6081 | if (bios->is_mobile || bios->major_version >= 5) | ||
6082 | ret = parse_fp_mode_table(dev, bios); | ||
6083 | NVLockVgaCrtcs(dev, was_locked); | ||
6084 | |||
6085 | /* allow subsequent scripts to execute */ | ||
6086 | bios->execute = true; | ||
6087 | |||
6088 | return 0; | ||
6089 | } | ||
6090 | |||
6091 | void | ||
6092 | nouveau_bios_takedown(struct drm_device *dev) | ||
6093 | { | ||
6094 | nouveau_bios_i2c_devices_takedown(dev); | ||
6095 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h new file mode 100644 index 000000000000..1d5f10bd78ed --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_bios.h | |||
@@ -0,0 +1,289 @@ | |||
1 | /* | ||
2 | * Copyright 2007-2008 Nouveau Project | ||
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 (including the next | ||
12 | * paragraph) shall be included in all copies or substantial portions of the | ||
13 | * Software. | ||
14 | * | ||
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
21 | * DEALINGS IN THE SOFTWARE. | ||
22 | */ | ||
23 | |||
24 | #ifndef __NOUVEAU_BIOS_H__ | ||
25 | #define __NOUVEAU_BIOS_H__ | ||
26 | |||
27 | #include "nvreg.h" | ||
28 | #include "nouveau_i2c.h" | ||
29 | |||
30 | #define DCB_MAX_NUM_ENTRIES 16 | ||
31 | #define DCB_MAX_NUM_I2C_ENTRIES 16 | ||
32 | #define DCB_MAX_NUM_GPIO_ENTRIES 32 | ||
33 | #define DCB_MAX_NUM_CONNECTOR_ENTRIES 16 | ||
34 | |||
35 | #define DCB_LOC_ON_CHIP 0 | ||
36 | |||
37 | struct dcb_entry { | ||
38 | int index; /* may not be raw dcb index if merging has happened */ | ||
39 | uint8_t type; | ||
40 | uint8_t i2c_index; | ||
41 | uint8_t heads; | ||
42 | uint8_t connector; | ||
43 | uint8_t bus; | ||
44 | uint8_t location; | ||
45 | uint8_t or; | ||
46 | bool duallink_possible; | ||
47 | union { | ||
48 | struct sor_conf { | ||
49 | int link; | ||
50 | } sorconf; | ||
51 | struct { | ||
52 | int maxfreq; | ||
53 | } crtconf; | ||
54 | struct { | ||
55 | struct sor_conf sor; | ||
56 | bool use_straps_for_mode; | ||
57 | bool use_power_scripts; | ||
58 | } lvdsconf; | ||
59 | struct { | ||
60 | bool has_component_output; | ||
61 | } tvconf; | ||
62 | struct { | ||
63 | struct sor_conf sor; | ||
64 | int link_nr; | ||
65 | int link_bw; | ||
66 | } dpconf; | ||
67 | struct { | ||
68 | struct sor_conf sor; | ||
69 | } tmdsconf; | ||
70 | }; | ||
71 | bool i2c_upper_default; | ||
72 | }; | ||
73 | |||
74 | struct dcb_i2c_entry { | ||
75 | uint8_t port_type; | ||
76 | uint8_t read, write; | ||
77 | struct nouveau_i2c_chan *chan; | ||
78 | }; | ||
79 | |||
80 | struct parsed_dcb { | ||
81 | int entries; | ||
82 | struct dcb_entry entry[DCB_MAX_NUM_ENTRIES]; | ||
83 | struct dcb_i2c_entry i2c[DCB_MAX_NUM_I2C_ENTRIES]; | ||
84 | }; | ||
85 | |||
86 | enum dcb_gpio_tag { | ||
87 | DCB_GPIO_TVDAC0 = 0xc, | ||
88 | DCB_GPIO_TVDAC1 = 0x2d, | ||
89 | }; | ||
90 | |||
91 | struct dcb_gpio_entry { | ||
92 | enum dcb_gpio_tag tag; | ||
93 | int line; | ||
94 | bool invert; | ||
95 | }; | ||
96 | |||
97 | struct parsed_dcb_gpio { | ||
98 | int entries; | ||
99 | struct dcb_gpio_entry entry[DCB_MAX_NUM_GPIO_ENTRIES]; | ||
100 | }; | ||
101 | |||
102 | struct dcb_connector_table_entry { | ||
103 | uint32_t entry; | ||
104 | uint8_t type; | ||
105 | uint8_t index; | ||
106 | uint8_t gpio_tag; | ||
107 | }; | ||
108 | |||
109 | struct dcb_connector_table { | ||
110 | int entries; | ||
111 | struct dcb_connector_table_entry entry[DCB_MAX_NUM_CONNECTOR_ENTRIES]; | ||
112 | }; | ||
113 | |||
114 | struct bios_parsed_dcb { | ||
115 | uint8_t version; | ||
116 | |||
117 | struct parsed_dcb dcb; | ||
118 | |||
119 | uint8_t *i2c_table; | ||
120 | uint8_t i2c_default_indices; | ||
121 | |||
122 | uint16_t gpio_table_ptr; | ||
123 | struct parsed_dcb_gpio gpio; | ||
124 | uint16_t connector_table_ptr; | ||
125 | struct dcb_connector_table connector; | ||
126 | }; | ||
127 | |||
128 | enum nouveau_encoder_type { | ||
129 | OUTPUT_ANALOG = 0, | ||
130 | OUTPUT_TV = 1, | ||
131 | OUTPUT_TMDS = 2, | ||
132 | OUTPUT_LVDS = 3, | ||
133 | OUTPUT_DP = 6, | ||
134 | OUTPUT_ANY = -1 | ||
135 | }; | ||
136 | |||
137 | enum nouveau_or { | ||
138 | OUTPUT_A = (1 << 0), | ||
139 | OUTPUT_B = (1 << 1), | ||
140 | OUTPUT_C = (1 << 2) | ||
141 | }; | ||
142 | |||
143 | enum LVDS_script { | ||
144 | /* Order *does* matter here */ | ||
145 | LVDS_INIT = 1, | ||
146 | LVDS_RESET, | ||
147 | LVDS_BACKLIGHT_ON, | ||
148 | LVDS_BACKLIGHT_OFF, | ||
149 | LVDS_PANEL_ON, | ||
150 | LVDS_PANEL_OFF | ||
151 | }; | ||
152 | |||
153 | /* changing these requires matching changes to reg tables in nv_get_clock */ | ||
154 | #define MAX_PLL_TYPES 4 | ||
155 | enum pll_types { | ||
156 | NVPLL, | ||
157 | MPLL, | ||
158 | VPLL1, | ||
159 | VPLL2 | ||
160 | }; | ||
161 | |||
162 | struct pll_lims { | ||
163 | struct { | ||
164 | int minfreq; | ||
165 | int maxfreq; | ||
166 | int min_inputfreq; | ||
167 | int max_inputfreq; | ||
168 | |||
169 | uint8_t min_m; | ||
170 | uint8_t max_m; | ||
171 | uint8_t min_n; | ||
172 | uint8_t max_n; | ||
173 | } vco1, vco2; | ||
174 | |||
175 | uint8_t max_log2p; | ||
176 | /* | ||
177 | * for most pre nv50 cards setting a log2P of 7 (the common max_log2p | ||
178 | * value) is no different to 6 (at least for vplls) so allowing the MNP | ||
179 | * calc to use 7 causes the generated clock to be out by a factor of 2. | ||
180 | * however, max_log2p cannot be fixed-up during parsing as the | ||
181 | * unmodified max_log2p value is still needed for setting mplls, hence | ||
182 | * an additional max_usable_log2p member | ||
183 | */ | ||
184 | uint8_t max_usable_log2p; | ||
185 | uint8_t log2p_bias; | ||
186 | |||
187 | uint8_t min_p; | ||
188 | uint8_t max_p; | ||
189 | |||
190 | int refclk; | ||
191 | }; | ||
192 | |||
193 | struct nouveau_bios_info { | ||
194 | struct parsed_dcb *dcb; | ||
195 | |||
196 | uint8_t chip_version; | ||
197 | |||
198 | uint32_t dactestval; | ||
199 | uint32_t tvdactestval; | ||
200 | uint8_t digital_min_front_porch; | ||
201 | bool fp_no_ddc; | ||
202 | }; | ||
203 | |||
204 | struct nvbios { | ||
205 | struct drm_device *dev; | ||
206 | struct nouveau_bios_info pub; | ||
207 | |||
208 | uint8_t data[NV_PROM_SIZE]; | ||
209 | unsigned int length; | ||
210 | bool execute; | ||
211 | |||
212 | uint8_t major_version; | ||
213 | uint8_t feature_byte; | ||
214 | bool is_mobile; | ||
215 | |||
216 | uint32_t fmaxvco, fminvco; | ||
217 | |||
218 | bool old_style_init; | ||
219 | uint16_t init_script_tbls_ptr; | ||
220 | uint16_t extra_init_script_tbl_ptr; | ||
221 | uint16_t macro_index_tbl_ptr; | ||
222 | uint16_t macro_tbl_ptr; | ||
223 | uint16_t condition_tbl_ptr; | ||
224 | uint16_t io_condition_tbl_ptr; | ||
225 | uint16_t io_flag_condition_tbl_ptr; | ||
226 | uint16_t init_function_tbl_ptr; | ||
227 | |||
228 | uint16_t pll_limit_tbl_ptr; | ||
229 | uint16_t ram_restrict_tbl_ptr; | ||
230 | |||
231 | uint16_t some_script_ptr; /* BIT I + 14 */ | ||
232 | uint16_t init96_tbl_ptr; /* BIT I + 16 */ | ||
233 | |||
234 | struct bios_parsed_dcb bdcb; | ||
235 | |||
236 | struct { | ||
237 | int crtchead; | ||
238 | /* these need remembering across suspend */ | ||
239 | uint32_t saved_nv_pfb_cfg0; | ||
240 | } state; | ||
241 | |||
242 | struct { | ||
243 | struct dcb_entry *output; | ||
244 | uint16_t script_table_ptr; | ||
245 | uint16_t dp_table_ptr; | ||
246 | } display; | ||
247 | |||
248 | struct { | ||
249 | uint16_t fptablepointer; /* also used by tmds */ | ||
250 | uint16_t fpxlatetableptr; | ||
251 | int xlatwidth; | ||
252 | uint16_t lvdsmanufacturerpointer; | ||
253 | uint16_t fpxlatemanufacturertableptr; | ||
254 | uint16_t mode_ptr; | ||
255 | uint16_t xlated_entry; | ||
256 | bool power_off_for_reset; | ||
257 | bool reset_after_pclk_change; | ||
258 | bool dual_link; | ||
259 | bool link_c_increment; | ||
260 | bool BITbit1; | ||
261 | bool if_is_24bit; | ||
262 | int duallink_transition_clk; | ||
263 | uint8_t strapless_is_24bit; | ||
264 | uint8_t *edid; | ||
265 | |||
266 | /* will need resetting after suspend */ | ||
267 | int last_script_invoc; | ||
268 | bool lvds_init_run; | ||
269 | } fp; | ||
270 | |||
271 | struct { | ||
272 | uint16_t output0_script_ptr; | ||
273 | uint16_t output1_script_ptr; | ||
274 | } tmds; | ||
275 | |||
276 | struct { | ||
277 | uint16_t mem_init_tbl_ptr; | ||
278 | uint16_t sdr_seq_tbl_ptr; | ||
279 | uint16_t ddr_seq_tbl_ptr; | ||
280 | |||
281 | struct { | ||
282 | uint8_t crt, tv, panel; | ||
283 | } i2c_indices; | ||
284 | |||
285 | uint16_t lvds_single_a_script_ptr; | ||
286 | } legacy; | ||
287 | }; | ||
288 | |||
289 | #endif | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c new file mode 100644 index 000000000000..320a14bceb99 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c | |||
@@ -0,0 +1,671 @@ | |||
1 | /* | ||
2 | * Copyright 2007 Dave Airlied | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
6 | * copy of this software and associated documentation files (the "Software"), | ||
7 | * to deal in the Software without restriction, including without limitation | ||
8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
9 | * and/or sell copies of the Software, and to permit persons to whom the | ||
10 | * Software is furnished to do so, subject to the following conditions: | ||
11 | * | ||
12 | * The above copyright notice and this permission notice (including the next | ||
13 | * paragraph) shall be included in all copies or substantial portions of the | ||
14 | * 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 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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 | /* | ||
25 | * Authors: Dave Airlied <airlied@linux.ie> | ||
26 | * Ben Skeggs <darktama@iinet.net.au> | ||
27 | * Jeremy Kolb <jkolb@brandeis.edu> | ||
28 | */ | ||
29 | |||
30 | #include "drmP.h" | ||
31 | |||
32 | #include "nouveau_drm.h" | ||
33 | #include "nouveau_drv.h" | ||
34 | #include "nouveau_dma.h" | ||
35 | |||
36 | static void | ||
37 | nouveau_bo_del_ttm(struct ttm_buffer_object *bo) | ||
38 | { | ||
39 | struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev); | ||
40 | struct nouveau_bo *nvbo = nouveau_bo(bo); | ||
41 | |||
42 | ttm_bo_kunmap(&nvbo->kmap); | ||
43 | |||
44 | if (unlikely(nvbo->gem)) | ||
45 | DRM_ERROR("bo %p still attached to GEM object\n", bo); | ||
46 | |||
47 | spin_lock(&dev_priv->ttm.bo_list_lock); | ||
48 | list_del(&nvbo->head); | ||
49 | spin_unlock(&dev_priv->ttm.bo_list_lock); | ||
50 | kfree(nvbo); | ||
51 | } | ||
52 | |||
53 | int | ||
54 | nouveau_bo_new(struct drm_device *dev, struct nouveau_channel *chan, | ||
55 | int size, int align, uint32_t flags, uint32_t tile_mode, | ||
56 | uint32_t tile_flags, bool no_vm, bool mappable, | ||
57 | struct nouveau_bo **pnvbo) | ||
58 | { | ||
59 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
60 | struct nouveau_bo *nvbo; | ||
61 | int ret, n = 0; | ||
62 | |||
63 | nvbo = kzalloc(sizeof(struct nouveau_bo), GFP_KERNEL); | ||
64 | if (!nvbo) | ||
65 | return -ENOMEM; | ||
66 | INIT_LIST_HEAD(&nvbo->head); | ||
67 | INIT_LIST_HEAD(&nvbo->entry); | ||
68 | nvbo->mappable = mappable; | ||
69 | nvbo->no_vm = no_vm; | ||
70 | nvbo->tile_mode = tile_mode; | ||
71 | nvbo->tile_flags = tile_flags; | ||
72 | |||
73 | /* | ||
74 | * Some of the tile_flags have a periodic structure of N*4096 bytes, | ||
75 | * align to to that as well as the page size. Overallocate memory to | ||
76 | * avoid corruption of other buffer objects. | ||
77 | */ | ||
78 | switch (tile_flags) { | ||
79 | case 0x1800: | ||
80 | case 0x2800: | ||
81 | case 0x4800: | ||
82 | case 0x7a00: | ||
83 | if (dev_priv->chipset >= 0xA0) { | ||
84 | /* This is based on high end cards with 448 bits | ||
85 | * memory bus, could be different elsewhere.*/ | ||
86 | size += 6 * 28672; | ||
87 | /* 8 * 28672 is the actual alignment requirement, | ||
88 | * but we must also align to page size. */ | ||
89 | align = 2 * 8 * 28672; | ||
90 | } else if (dev_priv->chipset >= 0x90) { | ||
91 | size += 3 * 16384; | ||
92 | align = 12 * 16834; | ||
93 | } else { | ||
94 | size += 3 * 8192; | ||
95 | /* 12 * 8192 is the actual alignment requirement, | ||
96 | * but we must also align to page size. */ | ||
97 | align = 2 * 12 * 8192; | ||
98 | } | ||
99 | break; | ||
100 | default: | ||
101 | break; | ||
102 | } | ||
103 | |||
104 | align >>= PAGE_SHIFT; | ||
105 | |||
106 | size = (size + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1); | ||
107 | if (dev_priv->card_type == NV_50) { | ||
108 | size = (size + 65535) & ~65535; | ||
109 | if (align < (65536 / PAGE_SIZE)) | ||
110 | align = (65536 / PAGE_SIZE); | ||
111 | } | ||
112 | |||
113 | if (flags & TTM_PL_FLAG_VRAM) | ||
114 | nvbo->placements[n++] = TTM_PL_FLAG_VRAM | TTM_PL_MASK_CACHING; | ||
115 | if (flags & TTM_PL_FLAG_TT) | ||
116 | nvbo->placements[n++] = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING; | ||
117 | nvbo->placement.fpfn = 0; | ||
118 | nvbo->placement.lpfn = mappable ? dev_priv->fb_mappable_pages : 0; | ||
119 | nvbo->placement.placement = nvbo->placements; | ||
120 | nvbo->placement.busy_placement = nvbo->placements; | ||
121 | nvbo->placement.num_placement = n; | ||
122 | nvbo->placement.num_busy_placement = n; | ||
123 | |||
124 | nvbo->channel = chan; | ||
125 | nouveau_bo_placement_set(nvbo, flags); | ||
126 | ret = ttm_bo_init(&dev_priv->ttm.bdev, &nvbo->bo, size, | ||
127 | ttm_bo_type_device, &nvbo->placement, align, 0, | ||
128 | false, NULL, size, nouveau_bo_del_ttm); | ||
129 | nvbo->channel = NULL; | ||
130 | if (ret) { | ||
131 | /* ttm will call nouveau_bo_del_ttm if it fails.. */ | ||
132 | return ret; | ||
133 | } | ||
134 | |||
135 | spin_lock(&dev_priv->ttm.bo_list_lock); | ||
136 | list_add_tail(&nvbo->head, &dev_priv->ttm.bo_list); | ||
137 | spin_unlock(&dev_priv->ttm.bo_list_lock); | ||
138 | *pnvbo = nvbo; | ||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | void | ||
143 | nouveau_bo_placement_set(struct nouveau_bo *nvbo, uint32_t memtype) | ||
144 | { | ||
145 | int n = 0; | ||
146 | |||
147 | if (memtype & TTM_PL_FLAG_VRAM) | ||
148 | nvbo->placements[n++] = TTM_PL_FLAG_VRAM | TTM_PL_MASK_CACHING; | ||
149 | if (memtype & TTM_PL_FLAG_TT) | ||
150 | nvbo->placements[n++] = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING; | ||
151 | if (memtype & TTM_PL_FLAG_SYSTEM) | ||
152 | nvbo->placements[n++] = TTM_PL_FLAG_SYSTEM | TTM_PL_MASK_CACHING; | ||
153 | nvbo->placement.placement = nvbo->placements; | ||
154 | nvbo->placement.busy_placement = nvbo->placements; | ||
155 | nvbo->placement.num_placement = n; | ||
156 | nvbo->placement.num_busy_placement = n; | ||
157 | } | ||
158 | |||
159 | int | ||
160 | nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t memtype) | ||
161 | { | ||
162 | struct drm_nouveau_private *dev_priv = nouveau_bdev(nvbo->bo.bdev); | ||
163 | struct ttm_buffer_object *bo = &nvbo->bo; | ||
164 | int ret, i; | ||
165 | |||
166 | if (nvbo->pin_refcnt && !(memtype & (1 << bo->mem.mem_type))) { | ||
167 | NV_ERROR(nouveau_bdev(bo->bdev)->dev, | ||
168 | "bo %p pinned elsewhere: 0x%08x vs 0x%08x\n", bo, | ||
169 | 1 << bo->mem.mem_type, memtype); | ||
170 | return -EINVAL; | ||
171 | } | ||
172 | |||
173 | if (nvbo->pin_refcnt++) | ||
174 | return 0; | ||
175 | |||
176 | ret = ttm_bo_reserve(bo, false, false, false, 0); | ||
177 | if (ret) | ||
178 | goto out; | ||
179 | |||
180 | nouveau_bo_placement_set(nvbo, memtype); | ||
181 | for (i = 0; i < nvbo->placement.num_placement; i++) | ||
182 | nvbo->placements[i] |= TTM_PL_FLAG_NO_EVICT; | ||
183 | |||
184 | ret = ttm_bo_validate(bo, &nvbo->placement, false, false); | ||
185 | if (ret == 0) { | ||
186 | switch (bo->mem.mem_type) { | ||
187 | case TTM_PL_VRAM: | ||
188 | dev_priv->fb_aper_free -= bo->mem.size; | ||
189 | break; | ||
190 | case TTM_PL_TT: | ||
191 | dev_priv->gart_info.aper_free -= bo->mem.size; | ||
192 | break; | ||
193 | default: | ||
194 | break; | ||
195 | } | ||
196 | } | ||
197 | ttm_bo_unreserve(bo); | ||
198 | out: | ||
199 | if (unlikely(ret)) | ||
200 | nvbo->pin_refcnt--; | ||
201 | return ret; | ||
202 | } | ||
203 | |||
204 | int | ||
205 | nouveau_bo_unpin(struct nouveau_bo *nvbo) | ||
206 | { | ||
207 | struct drm_nouveau_private *dev_priv = nouveau_bdev(nvbo->bo.bdev); | ||
208 | struct ttm_buffer_object *bo = &nvbo->bo; | ||
209 | int ret, i; | ||
210 | |||
211 | if (--nvbo->pin_refcnt) | ||
212 | return 0; | ||
213 | |||
214 | ret = ttm_bo_reserve(bo, false, false, false, 0); | ||
215 | if (ret) | ||
216 | return ret; | ||
217 | |||
218 | for (i = 0; i < nvbo->placement.num_placement; i++) | ||
219 | nvbo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT; | ||
220 | |||
221 | ret = ttm_bo_validate(bo, &nvbo->placement, false, false); | ||
222 | if (ret == 0) { | ||
223 | switch (bo->mem.mem_type) { | ||
224 | case TTM_PL_VRAM: | ||
225 | dev_priv->fb_aper_free += bo->mem.size; | ||
226 | break; | ||
227 | case TTM_PL_TT: | ||
228 | dev_priv->gart_info.aper_free += bo->mem.size; | ||
229 | break; | ||
230 | default: | ||
231 | break; | ||
232 | } | ||
233 | } | ||
234 | |||
235 | ttm_bo_unreserve(bo); | ||
236 | return ret; | ||
237 | } | ||
238 | |||
239 | int | ||
240 | nouveau_bo_map(struct nouveau_bo *nvbo) | ||
241 | { | ||
242 | int ret; | ||
243 | |||
244 | ret = ttm_bo_reserve(&nvbo->bo, false, false, false, 0); | ||
245 | if (ret) | ||
246 | return ret; | ||
247 | |||
248 | ret = ttm_bo_kmap(&nvbo->bo, 0, nvbo->bo.mem.num_pages, &nvbo->kmap); | ||
249 | ttm_bo_unreserve(&nvbo->bo); | ||
250 | return ret; | ||
251 | } | ||
252 | |||
253 | void | ||
254 | nouveau_bo_unmap(struct nouveau_bo *nvbo) | ||
255 | { | ||
256 | ttm_bo_kunmap(&nvbo->kmap); | ||
257 | } | ||
258 | |||
259 | u16 | ||
260 | nouveau_bo_rd16(struct nouveau_bo *nvbo, unsigned index) | ||
261 | { | ||
262 | bool is_iomem; | ||
263 | u16 *mem = ttm_kmap_obj_virtual(&nvbo->kmap, &is_iomem); | ||
264 | mem = &mem[index]; | ||
265 | if (is_iomem) | ||
266 | return ioread16_native((void __force __iomem *)mem); | ||
267 | else | ||
268 | return *mem; | ||
269 | } | ||
270 | |||
271 | void | ||
272 | nouveau_bo_wr16(struct nouveau_bo *nvbo, unsigned index, u16 val) | ||
273 | { | ||
274 | bool is_iomem; | ||
275 | u16 *mem = ttm_kmap_obj_virtual(&nvbo->kmap, &is_iomem); | ||
276 | mem = &mem[index]; | ||
277 | if (is_iomem) | ||
278 | iowrite16_native(val, (void __force __iomem *)mem); | ||
279 | else | ||
280 | *mem = val; | ||
281 | } | ||
282 | |||
283 | u32 | ||
284 | nouveau_bo_rd32(struct nouveau_bo *nvbo, unsigned index) | ||
285 | { | ||
286 | bool is_iomem; | ||
287 | u32 *mem = ttm_kmap_obj_virtual(&nvbo->kmap, &is_iomem); | ||
288 | mem = &mem[index]; | ||
289 | if (is_iomem) | ||
290 | return ioread32_native((void __force __iomem *)mem); | ||
291 | else | ||
292 | return *mem; | ||
293 | } | ||
294 | |||
295 | void | ||
296 | nouveau_bo_wr32(struct nouveau_bo *nvbo, unsigned index, u32 val) | ||
297 | { | ||
298 | bool is_iomem; | ||
299 | u32 *mem = ttm_kmap_obj_virtual(&nvbo->kmap, &is_iomem); | ||
300 | mem = &mem[index]; | ||
301 | if (is_iomem) | ||
302 | iowrite32_native(val, (void __force __iomem *)mem); | ||
303 | else | ||
304 | *mem = val; | ||
305 | } | ||
306 | |||
307 | static struct ttm_backend * | ||
308 | nouveau_bo_create_ttm_backend_entry(struct ttm_bo_device *bdev) | ||
309 | { | ||
310 | struct drm_nouveau_private *dev_priv = nouveau_bdev(bdev); | ||
311 | struct drm_device *dev = dev_priv->dev; | ||
312 | |||
313 | switch (dev_priv->gart_info.type) { | ||
314 | case NOUVEAU_GART_AGP: | ||
315 | return ttm_agp_backend_init(bdev, dev->agp->bridge); | ||
316 | case NOUVEAU_GART_SGDMA: | ||
317 | return nouveau_sgdma_init_ttm(dev); | ||
318 | default: | ||
319 | NV_ERROR(dev, "Unknown GART type %d\n", | ||
320 | dev_priv->gart_info.type); | ||
321 | break; | ||
322 | } | ||
323 | |||
324 | return NULL; | ||
325 | } | ||
326 | |||
327 | static int | ||
328 | nouveau_bo_invalidate_caches(struct ttm_bo_device *bdev, uint32_t flags) | ||
329 | { | ||
330 | /* We'll do this from user space. */ | ||
331 | return 0; | ||
332 | } | ||
333 | |||
334 | static int | ||
335 | nouveau_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, | ||
336 | struct ttm_mem_type_manager *man) | ||
337 | { | ||
338 | struct drm_nouveau_private *dev_priv = nouveau_bdev(bdev); | ||
339 | struct drm_device *dev = dev_priv->dev; | ||
340 | |||
341 | switch (type) { | ||
342 | case TTM_PL_SYSTEM: | ||
343 | man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; | ||
344 | man->available_caching = TTM_PL_MASK_CACHING; | ||
345 | man->default_caching = TTM_PL_FLAG_CACHED; | ||
346 | break; | ||
347 | case TTM_PL_VRAM: | ||
348 | man->flags = TTM_MEMTYPE_FLAG_FIXED | | ||
349 | TTM_MEMTYPE_FLAG_MAPPABLE | | ||
350 | TTM_MEMTYPE_FLAG_NEEDS_IOREMAP; | ||
351 | man->available_caching = TTM_PL_FLAG_UNCACHED | | ||
352 | TTM_PL_FLAG_WC; | ||
353 | man->default_caching = TTM_PL_FLAG_WC; | ||
354 | |||
355 | man->io_addr = NULL; | ||
356 | man->io_offset = drm_get_resource_start(dev, 1); | ||
357 | man->io_size = drm_get_resource_len(dev, 1); | ||
358 | if (man->io_size > nouveau_mem_fb_amount(dev)) | ||
359 | man->io_size = nouveau_mem_fb_amount(dev); | ||
360 | |||
361 | man->gpu_offset = dev_priv->vm_vram_base; | ||
362 | break; | ||
363 | case TTM_PL_TT: | ||
364 | switch (dev_priv->gart_info.type) { | ||
365 | case NOUVEAU_GART_AGP: | ||
366 | man->flags = TTM_MEMTYPE_FLAG_MAPPABLE | | ||
367 | TTM_MEMTYPE_FLAG_NEEDS_IOREMAP; | ||
368 | man->available_caching = TTM_PL_FLAG_UNCACHED; | ||
369 | man->default_caching = TTM_PL_FLAG_UNCACHED; | ||
370 | break; | ||
371 | case NOUVEAU_GART_SGDMA: | ||
372 | man->flags = TTM_MEMTYPE_FLAG_MAPPABLE | | ||
373 | TTM_MEMTYPE_FLAG_CMA; | ||
374 | man->available_caching = TTM_PL_MASK_CACHING; | ||
375 | man->default_caching = TTM_PL_FLAG_CACHED; | ||
376 | break; | ||
377 | default: | ||
378 | NV_ERROR(dev, "Unknown GART type: %d\n", | ||
379 | dev_priv->gart_info.type); | ||
380 | return -EINVAL; | ||
381 | } | ||
382 | |||
383 | man->io_offset = dev_priv->gart_info.aper_base; | ||
384 | man->io_size = dev_priv->gart_info.aper_size; | ||
385 | man->io_addr = NULL; | ||
386 | man->gpu_offset = dev_priv->vm_gart_base; | ||
387 | break; | ||
388 | default: | ||
389 | NV_ERROR(dev, "Unsupported memory type %u\n", (unsigned)type); | ||
390 | return -EINVAL; | ||
391 | } | ||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | static void | ||
396 | nouveau_bo_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *pl) | ||
397 | { | ||
398 | struct nouveau_bo *nvbo = nouveau_bo(bo); | ||
399 | |||
400 | switch (bo->mem.mem_type) { | ||
401 | default: | ||
402 | nouveau_bo_placement_set(nvbo, TTM_PL_FLAG_SYSTEM); | ||
403 | break; | ||
404 | } | ||
405 | } | ||
406 | |||
407 | |||
408 | /* GPU-assisted copy using NV_MEMORY_TO_MEMORY_FORMAT, can access | ||
409 | * TTM_PL_{VRAM,TT} directly. | ||
410 | */ | ||
411 | static int | ||
412 | nouveau_bo_move_accel_cleanup(struct nouveau_channel *chan, | ||
413 | struct nouveau_bo *nvbo, bool evict, bool no_wait, | ||
414 | struct ttm_mem_reg *new_mem) | ||
415 | { | ||
416 | struct nouveau_fence *fence = NULL; | ||
417 | int ret; | ||
418 | |||
419 | ret = nouveau_fence_new(chan, &fence, true); | ||
420 | if (ret) | ||
421 | return ret; | ||
422 | |||
423 | ret = ttm_bo_move_accel_cleanup(&nvbo->bo, fence, NULL, | ||
424 | evict, no_wait, new_mem); | ||
425 | nouveau_fence_unref((void *)&fence); | ||
426 | return ret; | ||
427 | } | ||
428 | |||
429 | static inline uint32_t | ||
430 | nouveau_bo_mem_ctxdma(struct nouveau_bo *nvbo, struct nouveau_channel *chan, | ||
431 | struct ttm_mem_reg *mem) | ||
432 | { | ||
433 | if (chan == nouveau_bdev(nvbo->bo.bdev)->channel) { | ||
434 | if (mem->mem_type == TTM_PL_TT) | ||
435 | return NvDmaGART; | ||
436 | return NvDmaVRAM; | ||
437 | } | ||
438 | |||
439 | if (mem->mem_type == TTM_PL_TT) | ||
440 | return chan->gart_handle; | ||
441 | return chan->vram_handle; | ||
442 | } | ||
443 | |||
444 | static int | ||
445 | nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, int no_wait, | ||
446 | struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem) | ||
447 | { | ||
448 | struct nouveau_bo *nvbo = nouveau_bo(bo); | ||
449 | struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev); | ||
450 | struct nouveau_channel *chan; | ||
451 | uint64_t src_offset, dst_offset; | ||
452 | uint32_t page_count; | ||
453 | int ret; | ||
454 | |||
455 | chan = nvbo->channel; | ||
456 | if (!chan || nvbo->tile_flags || nvbo->no_vm) { | ||
457 | chan = dev_priv->channel; | ||
458 | if (!chan) | ||
459 | return -EINVAL; | ||
460 | } | ||
461 | |||
462 | src_offset = old_mem->mm_node->start << PAGE_SHIFT; | ||
463 | dst_offset = new_mem->mm_node->start << PAGE_SHIFT; | ||
464 | if (chan != dev_priv->channel) { | ||
465 | if (old_mem->mem_type == TTM_PL_TT) | ||
466 | src_offset += dev_priv->vm_gart_base; | ||
467 | else | ||
468 | src_offset += dev_priv->vm_vram_base; | ||
469 | |||
470 | if (new_mem->mem_type == TTM_PL_TT) | ||
471 | dst_offset += dev_priv->vm_gart_base; | ||
472 | else | ||
473 | dst_offset += dev_priv->vm_vram_base; | ||
474 | } | ||
475 | |||
476 | ret = RING_SPACE(chan, 3); | ||
477 | if (ret) | ||
478 | return ret; | ||
479 | BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_DMA_SOURCE, 2); | ||
480 | OUT_RING(chan, nouveau_bo_mem_ctxdma(nvbo, chan, old_mem)); | ||
481 | OUT_RING(chan, nouveau_bo_mem_ctxdma(nvbo, chan, new_mem)); | ||
482 | |||
483 | if (dev_priv->card_type >= NV_50) { | ||
484 | ret = RING_SPACE(chan, 4); | ||
485 | if (ret) | ||
486 | return ret; | ||
487 | BEGIN_RING(chan, NvSubM2MF, 0x0200, 1); | ||
488 | OUT_RING(chan, 1); | ||
489 | BEGIN_RING(chan, NvSubM2MF, 0x021c, 1); | ||
490 | OUT_RING(chan, 1); | ||
491 | } | ||
492 | |||
493 | page_count = new_mem->num_pages; | ||
494 | while (page_count) { | ||
495 | int line_count = (page_count > 2047) ? 2047 : page_count; | ||
496 | |||
497 | if (dev_priv->card_type >= NV_50) { | ||
498 | ret = RING_SPACE(chan, 3); | ||
499 | if (ret) | ||
500 | return ret; | ||
501 | BEGIN_RING(chan, NvSubM2MF, 0x0238, 2); | ||
502 | OUT_RING(chan, upper_32_bits(src_offset)); | ||
503 | OUT_RING(chan, upper_32_bits(dst_offset)); | ||
504 | } | ||
505 | ret = RING_SPACE(chan, 11); | ||
506 | if (ret) | ||
507 | return ret; | ||
508 | BEGIN_RING(chan, NvSubM2MF, | ||
509 | NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8); | ||
510 | OUT_RING(chan, lower_32_bits(src_offset)); | ||
511 | OUT_RING(chan, lower_32_bits(dst_offset)); | ||
512 | OUT_RING(chan, PAGE_SIZE); /* src_pitch */ | ||
513 | OUT_RING(chan, PAGE_SIZE); /* dst_pitch */ | ||
514 | OUT_RING(chan, PAGE_SIZE); /* line_length */ | ||
515 | OUT_RING(chan, line_count); | ||
516 | OUT_RING(chan, (1<<8)|(1<<0)); | ||
517 | OUT_RING(chan, 0); | ||
518 | BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_NOP, 1); | ||
519 | OUT_RING(chan, 0); | ||
520 | |||
521 | page_count -= line_count; | ||
522 | src_offset += (PAGE_SIZE * line_count); | ||
523 | dst_offset += (PAGE_SIZE * line_count); | ||
524 | } | ||
525 | |||
526 | return nouveau_bo_move_accel_cleanup(chan, nvbo, evict, no_wait, new_mem); | ||
527 | } | ||
528 | |||
529 | static int | ||
530 | nouveau_bo_move_flipd(struct ttm_buffer_object *bo, bool evict, bool intr, | ||
531 | bool no_wait, struct ttm_mem_reg *new_mem) | ||
532 | { | ||
533 | u32 placement_memtype = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING; | ||
534 | struct ttm_placement placement; | ||
535 | struct ttm_mem_reg tmp_mem; | ||
536 | int ret; | ||
537 | |||
538 | placement.fpfn = placement.lpfn = 0; | ||
539 | placement.num_placement = placement.num_busy_placement = 1; | ||
540 | placement.placement = &placement_memtype; | ||
541 | |||
542 | tmp_mem = *new_mem; | ||
543 | tmp_mem.mm_node = NULL; | ||
544 | ret = ttm_bo_mem_space(bo, &placement, &tmp_mem, intr, no_wait); | ||
545 | if (ret) | ||
546 | return ret; | ||
547 | |||
548 | ret = ttm_tt_bind(bo->ttm, &tmp_mem); | ||
549 | if (ret) | ||
550 | goto out; | ||
551 | |||
552 | ret = nouveau_bo_move_m2mf(bo, true, no_wait, &bo->mem, &tmp_mem); | ||
553 | if (ret) | ||
554 | goto out; | ||
555 | |||
556 | ret = ttm_bo_move_ttm(bo, evict, no_wait, new_mem); | ||
557 | out: | ||
558 | if (tmp_mem.mm_node) { | ||
559 | spin_lock(&bo->bdev->glob->lru_lock); | ||
560 | drm_mm_put_block(tmp_mem.mm_node); | ||
561 | spin_unlock(&bo->bdev->glob->lru_lock); | ||
562 | } | ||
563 | |||
564 | return ret; | ||
565 | } | ||
566 | |||
567 | static int | ||
568 | nouveau_bo_move_flips(struct ttm_buffer_object *bo, bool evict, bool intr, | ||
569 | bool no_wait, struct ttm_mem_reg *new_mem) | ||
570 | { | ||
571 | u32 placement_memtype = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING; | ||
572 | struct ttm_placement placement; | ||
573 | struct ttm_mem_reg tmp_mem; | ||
574 | int ret; | ||
575 | |||
576 | placement.fpfn = placement.lpfn = 0; | ||
577 | placement.num_placement = placement.num_busy_placement = 1; | ||
578 | placement.placement = &placement_memtype; | ||
579 | |||
580 | tmp_mem = *new_mem; | ||
581 | tmp_mem.mm_node = NULL; | ||
582 | ret = ttm_bo_mem_space(bo, &placement, &tmp_mem, intr, no_wait); | ||
583 | if (ret) | ||
584 | return ret; | ||
585 | |||
586 | ret = ttm_bo_move_ttm(bo, evict, no_wait, &tmp_mem); | ||
587 | if (ret) | ||
588 | goto out; | ||
589 | |||
590 | ret = nouveau_bo_move_m2mf(bo, true, no_wait, &bo->mem, new_mem); | ||
591 | if (ret) | ||
592 | goto out; | ||
593 | |||
594 | out: | ||
595 | if (tmp_mem.mm_node) { | ||
596 | spin_lock(&bo->bdev->glob->lru_lock); | ||
597 | drm_mm_put_block(tmp_mem.mm_node); | ||
598 | spin_unlock(&bo->bdev->glob->lru_lock); | ||
599 | } | ||
600 | |||
601 | return ret; | ||
602 | } | ||
603 | |||
604 | static int | ||
605 | nouveau_bo_move(struct ttm_buffer_object *bo, bool evict, bool intr, | ||
606 | bool no_wait, struct ttm_mem_reg *new_mem) | ||
607 | { | ||
608 | struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev); | ||
609 | struct nouveau_bo *nvbo = nouveau_bo(bo); | ||
610 | struct drm_device *dev = dev_priv->dev; | ||
611 | struct ttm_mem_reg *old_mem = &bo->mem; | ||
612 | int ret; | ||
613 | |||
614 | if (dev_priv->card_type == NV_50 && new_mem->mem_type == TTM_PL_VRAM && | ||
615 | !nvbo->no_vm) { | ||
616 | uint64_t offset = new_mem->mm_node->start << PAGE_SHIFT; | ||
617 | |||
618 | ret = nv50_mem_vm_bind_linear(dev, | ||
619 | offset + dev_priv->vm_vram_base, | ||
620 | new_mem->size, nvbo->tile_flags, | ||
621 | offset); | ||
622 | if (ret) | ||
623 | return ret; | ||
624 | } | ||
625 | |||
626 | if (dev_priv->init_state != NOUVEAU_CARD_INIT_DONE) | ||
627 | return ttm_bo_move_memcpy(bo, evict, no_wait, new_mem); | ||
628 | |||
629 | if (old_mem->mem_type == TTM_PL_SYSTEM && !bo->ttm) { | ||
630 | BUG_ON(bo->mem.mm_node != NULL); | ||
631 | bo->mem = *new_mem; | ||
632 | new_mem->mm_node = NULL; | ||
633 | return 0; | ||
634 | } | ||
635 | |||
636 | if (new_mem->mem_type == TTM_PL_SYSTEM) { | ||
637 | if (old_mem->mem_type == TTM_PL_SYSTEM) | ||
638 | return ttm_bo_move_memcpy(bo, evict, no_wait, new_mem); | ||
639 | if (nouveau_bo_move_flipd(bo, evict, intr, no_wait, new_mem)) | ||
640 | return ttm_bo_move_memcpy(bo, evict, no_wait, new_mem); | ||
641 | } else if (old_mem->mem_type == TTM_PL_SYSTEM) { | ||
642 | if (nouveau_bo_move_flips(bo, evict, intr, no_wait, new_mem)) | ||
643 | return ttm_bo_move_memcpy(bo, evict, no_wait, new_mem); | ||
644 | } else { | ||
645 | if (nouveau_bo_move_m2mf(bo, evict, no_wait, old_mem, new_mem)) | ||
646 | return ttm_bo_move_memcpy(bo, evict, no_wait, new_mem); | ||
647 | } | ||
648 | |||
649 | return 0; | ||
650 | } | ||
651 | |||
652 | static int | ||
653 | nouveau_bo_verify_access(struct ttm_buffer_object *bo, struct file *filp) | ||
654 | { | ||
655 | return 0; | ||
656 | } | ||
657 | |||
658 | struct ttm_bo_driver nouveau_bo_driver = { | ||
659 | .create_ttm_backend_entry = nouveau_bo_create_ttm_backend_entry, | ||
660 | .invalidate_caches = nouveau_bo_invalidate_caches, | ||
661 | .init_mem_type = nouveau_bo_init_mem_type, | ||
662 | .evict_flags = nouveau_bo_evict_flags, | ||
663 | .move = nouveau_bo_move, | ||
664 | .verify_access = nouveau_bo_verify_access, | ||
665 | .sync_obj_signaled = nouveau_fence_signalled, | ||
666 | .sync_obj_wait = nouveau_fence_wait, | ||
667 | .sync_obj_flush = nouveau_fence_flush, | ||
668 | .sync_obj_unref = nouveau_fence_unref, | ||
669 | .sync_obj_ref = nouveau_fence_ref, | ||
670 | }; | ||
671 | |||
diff --git a/drivers/gpu/drm/nouveau/nouveau_calc.c b/drivers/gpu/drm/nouveau/nouveau_calc.c new file mode 100644 index 000000000000..ee2b84504d05 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_calc.c | |||
@@ -0,0 +1,478 @@ | |||
1 | /* | ||
2 | * Copyright 1993-2003 NVIDIA, Corporation | ||
3 | * Copyright 2007-2009 Stuart Bennett | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
6 | * copy of this software and associated documentation files (the "Software"), | ||
7 | * to deal in the Software without restriction, including without limitation | ||
8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
9 | * and/or sell copies of the Software, and to permit persons to whom the | ||
10 | * Software is furnished to do so, subject to the following conditions: | ||
11 | * | ||
12 | * The above copyright notice and this permission notice shall be included in | ||
13 | * all copies or substantial portions of the Software. | ||
14 | * | ||
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
18 | * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||
19 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF | ||
20 | * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
21 | * SOFTWARE. | ||
22 | */ | ||
23 | |||
24 | #include "drmP.h" | ||
25 | #include "nouveau_drv.h" | ||
26 | #include "nouveau_hw.h" | ||
27 | |||
28 | /****************************************************************************\ | ||
29 | * * | ||
30 | * The video arbitration routines calculate some "magic" numbers. Fixes * | ||
31 | * the snow seen when accessing the framebuffer without it. * | ||
32 | * It just works (I hope). * | ||
33 | * * | ||
34 | \****************************************************************************/ | ||
35 | |||
36 | struct nv_fifo_info { | ||
37 | int lwm; | ||
38 | int burst; | ||
39 | }; | ||
40 | |||
41 | struct nv_sim_state { | ||
42 | int pclk_khz; | ||
43 | int mclk_khz; | ||
44 | int nvclk_khz; | ||
45 | int bpp; | ||
46 | int mem_page_miss; | ||
47 | int mem_latency; | ||
48 | int memory_type; | ||
49 | int memory_width; | ||
50 | int two_heads; | ||
51 | }; | ||
52 | |||
53 | static void | ||
54 | nv04_calc_arb(struct nv_fifo_info *fifo, struct nv_sim_state *arb) | ||
55 | { | ||
56 | int pagemiss, cas, width, bpp; | ||
57 | int nvclks, mclks, pclks, crtpagemiss; | ||
58 | int found, mclk_extra, mclk_loop, cbs, m1, p1; | ||
59 | int mclk_freq, pclk_freq, nvclk_freq; | ||
60 | int us_m, us_n, us_p, crtc_drain_rate; | ||
61 | int cpm_us, us_crt, clwm; | ||
62 | |||
63 | pclk_freq = arb->pclk_khz; | ||
64 | mclk_freq = arb->mclk_khz; | ||
65 | nvclk_freq = arb->nvclk_khz; | ||
66 | pagemiss = arb->mem_page_miss; | ||
67 | cas = arb->mem_latency; | ||
68 | width = arb->memory_width >> 6; | ||
69 | bpp = arb->bpp; | ||
70 | cbs = 128; | ||
71 | |||
72 | pclks = 2; | ||
73 | nvclks = 10; | ||
74 | mclks = 13 + cas; | ||
75 | mclk_extra = 3; | ||
76 | found = 0; | ||
77 | |||
78 | while (!found) { | ||
79 | found = 1; | ||
80 | |||
81 | mclk_loop = mclks + mclk_extra; | ||
82 | us_m = mclk_loop * 1000 * 1000 / mclk_freq; | ||
83 | us_n = nvclks * 1000 * 1000 / nvclk_freq; | ||
84 | us_p = nvclks * 1000 * 1000 / pclk_freq; | ||
85 | |||
86 | crtc_drain_rate = pclk_freq * bpp / 8; | ||
87 | crtpagemiss = 2; | ||
88 | crtpagemiss += 1; | ||
89 | cpm_us = crtpagemiss * pagemiss * 1000 * 1000 / mclk_freq; | ||
90 | us_crt = cpm_us + us_m + us_n + us_p; | ||
91 | clwm = us_crt * crtc_drain_rate / (1000 * 1000); | ||
92 | clwm++; | ||
93 | |||
94 | m1 = clwm + cbs - 512; | ||
95 | p1 = m1 * pclk_freq / mclk_freq; | ||
96 | p1 = p1 * bpp / 8; | ||
97 | if ((p1 < m1 && m1 > 0) || clwm > 519) { | ||
98 | found = !mclk_extra; | ||
99 | mclk_extra--; | ||
100 | } | ||
101 | if (clwm < 384) | ||
102 | clwm = 384; | ||
103 | |||
104 | fifo->lwm = clwm; | ||
105 | fifo->burst = cbs; | ||
106 | } | ||
107 | } | ||
108 | |||
109 | static void | ||
110 | nv10_calc_arb(struct nv_fifo_info *fifo, struct nv_sim_state *arb) | ||
111 | { | ||
112 | int fill_rate, drain_rate; | ||
113 | int pclks, nvclks, mclks, xclks; | ||
114 | int pclk_freq, nvclk_freq, mclk_freq; | ||
115 | int fill_lat, extra_lat; | ||
116 | int max_burst_o, max_burst_l; | ||
117 | int fifo_len, min_lwm, max_lwm; | ||
118 | const int burst_lat = 80; /* Maximum allowable latency due | ||
119 | * to the CRTC FIFO burst. (ns) */ | ||
120 | |||
121 | pclk_freq = arb->pclk_khz; | ||
122 | nvclk_freq = arb->nvclk_khz; | ||
123 | mclk_freq = arb->mclk_khz; | ||
124 | |||
125 | fill_rate = mclk_freq * arb->memory_width / 8; /* kB/s */ | ||
126 | drain_rate = pclk_freq * arb->bpp / 8; /* kB/s */ | ||
127 | |||
128 | fifo_len = arb->two_heads ? 1536 : 1024; /* B */ | ||
129 | |||
130 | /* Fixed FIFO refill latency. */ | ||
131 | |||
132 | pclks = 4; /* lwm detect. */ | ||
133 | |||
134 | nvclks = 3 /* lwm -> sync. */ | ||
135 | + 2 /* fbi bus cycles (1 req + 1 busy) */ | ||
136 | + 1 /* 2 edge sync. may be very close to edge so | ||
137 | * just put one. */ | ||
138 | + 1 /* fbi_d_rdv_n */ | ||
139 | + 1 /* Fbi_d_rdata */ | ||
140 | + 1; /* crtfifo load */ | ||
141 | |||
142 | mclks = 1 /* 2 edge sync. may be very close to edge so | ||
143 | * just put one. */ | ||
144 | + 1 /* arb_hp_req */ | ||
145 | + 5 /* tiling pipeline */ | ||
146 | + 2 /* latency fifo */ | ||
147 | + 2 /* memory request to fbio block */ | ||
148 | + 7; /* data returned from fbio block */ | ||
149 | |||
150 | /* Need to accumulate 256 bits for read */ | ||
151 | mclks += (arb->memory_type == 0 ? 2 : 1) | ||
152 | * arb->memory_width / 32; | ||
153 | |||
154 | fill_lat = mclks * 1000 * 1000 / mclk_freq /* minimum mclk latency */ | ||
155 | + nvclks * 1000 * 1000 / nvclk_freq /* nvclk latency */ | ||
156 | + pclks * 1000 * 1000 / pclk_freq; /* pclk latency */ | ||
157 | |||
158 | /* Conditional FIFO refill latency. */ | ||
159 | |||
160 | xclks = 2 * arb->mem_page_miss + mclks /* Extra latency due to | ||
161 | * the overlay. */ | ||
162 | + 2 * arb->mem_page_miss /* Extra pagemiss latency. */ | ||
163 | + (arb->bpp == 32 ? 8 : 4); /* Margin of error. */ | ||
164 | |||
165 | extra_lat = xclks * 1000 * 1000 / mclk_freq; | ||
166 | |||
167 | if (arb->two_heads) | ||
168 | /* Account for another CRTC. */ | ||
169 | extra_lat += fill_lat + extra_lat + burst_lat; | ||
170 | |||
171 | /* FIFO burst */ | ||
172 | |||
173 | /* Max burst not leading to overflows. */ | ||
174 | max_burst_o = (1 + fifo_len - extra_lat * drain_rate / (1000 * 1000)) | ||
175 | * (fill_rate / 1000) / ((fill_rate - drain_rate) / 1000); | ||
176 | fifo->burst = min(max_burst_o, 1024); | ||
177 | |||
178 | /* Max burst value with an acceptable latency. */ | ||
179 | max_burst_l = burst_lat * fill_rate / (1000 * 1000); | ||
180 | fifo->burst = min(max_burst_l, fifo->burst); | ||
181 | |||
182 | fifo->burst = rounddown_pow_of_two(fifo->burst); | ||
183 | |||
184 | /* FIFO low watermark */ | ||
185 | |||
186 | min_lwm = (fill_lat + extra_lat) * drain_rate / (1000 * 1000) + 1; | ||
187 | max_lwm = fifo_len - fifo->burst | ||
188 | + fill_lat * drain_rate / (1000 * 1000) | ||
189 | + fifo->burst * drain_rate / fill_rate; | ||
190 | |||
191 | fifo->lwm = min_lwm + 10 * (max_lwm - min_lwm) / 100; /* Empirical. */ | ||
192 | } | ||
193 | |||
194 | static void | ||
195 | nv04_update_arb(struct drm_device *dev, int VClk, int bpp, | ||
196 | int *burst, int *lwm) | ||
197 | { | ||
198 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
199 | struct nv_fifo_info fifo_data; | ||
200 | struct nv_sim_state sim_data; | ||
201 | int MClk = nouveau_hw_get_clock(dev, MPLL); | ||
202 | int NVClk = nouveau_hw_get_clock(dev, NVPLL); | ||
203 | uint32_t cfg1 = nvReadFB(dev, NV_PFB_CFG1); | ||
204 | |||
205 | sim_data.pclk_khz = VClk; | ||
206 | sim_data.mclk_khz = MClk; | ||
207 | sim_data.nvclk_khz = NVClk; | ||
208 | sim_data.bpp = bpp; | ||
209 | sim_data.two_heads = nv_two_heads(dev); | ||
210 | if ((dev->pci_device & 0xffff) == 0x01a0 /*CHIPSET_NFORCE*/ || | ||
211 | (dev->pci_device & 0xffff) == 0x01f0 /*CHIPSET_NFORCE2*/) { | ||
212 | uint32_t type; | ||
213 | |||
214 | pci_read_config_dword(pci_get_bus_and_slot(0, 1), 0x7c, &type); | ||
215 | |||
216 | sim_data.memory_type = (type >> 12) & 1; | ||
217 | sim_data.memory_width = 64; | ||
218 | sim_data.mem_latency = 3; | ||
219 | sim_data.mem_page_miss = 10; | ||
220 | } else { | ||
221 | sim_data.memory_type = nvReadFB(dev, NV_PFB_CFG0) & 0x1; | ||
222 | sim_data.memory_width = (nvReadEXTDEV(dev, NV_PEXTDEV_BOOT_0) & 0x10) ? 128 : 64; | ||
223 | sim_data.mem_latency = cfg1 & 0xf; | ||
224 | sim_data.mem_page_miss = ((cfg1 >> 4) & 0xf) + ((cfg1 >> 31) & 0x1); | ||
225 | } | ||
226 | |||
227 | if (dev_priv->card_type == NV_04) | ||
228 | nv04_calc_arb(&fifo_data, &sim_data); | ||
229 | else | ||
230 | nv10_calc_arb(&fifo_data, &sim_data); | ||
231 | |||
232 | *burst = ilog2(fifo_data.burst >> 4); | ||
233 | *lwm = fifo_data.lwm >> 3; | ||
234 | } | ||
235 | |||
236 | static void | ||
237 | nv30_update_arb(int *burst, int *lwm) | ||
238 | { | ||
239 | unsigned int fifo_size, burst_size, graphics_lwm; | ||
240 | |||
241 | fifo_size = 2048; | ||
242 | burst_size = 512; | ||
243 | graphics_lwm = fifo_size - burst_size; | ||
244 | |||
245 | *burst = ilog2(burst_size >> 5); | ||
246 | *lwm = graphics_lwm >> 3; | ||
247 | } | ||
248 | |||
249 | void | ||
250 | nouveau_calc_arb(struct drm_device *dev, int vclk, int bpp, int *burst, int *lwm) | ||
251 | { | ||
252 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
253 | |||
254 | if (dev_priv->card_type < NV_30) | ||
255 | nv04_update_arb(dev, vclk, bpp, burst, lwm); | ||
256 | else if ((dev->pci_device & 0xfff0) == 0x0240 /*CHIPSET_C51*/ || | ||
257 | (dev->pci_device & 0xfff0) == 0x03d0 /*CHIPSET_C512*/) { | ||
258 | *burst = 128; | ||
259 | *lwm = 0x0480; | ||
260 | } else | ||
261 | nv30_update_arb(burst, lwm); | ||
262 | } | ||
263 | |||
264 | static int | ||
265 | getMNP_single(struct drm_device *dev, struct pll_lims *pll_lim, int clk, | ||
266 | struct nouveau_pll_vals *bestpv) | ||
267 | { | ||
268 | /* Find M, N and P for a single stage PLL | ||
269 | * | ||
270 | * Note that some bioses (NV3x) have lookup tables of precomputed MNP | ||
271 | * values, but we're too lazy to use those atm | ||
272 | * | ||
273 | * "clk" parameter in kHz | ||
274 | * returns calculated clock | ||
275 | */ | ||
276 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
277 | int cv = dev_priv->vbios->chip_version; | ||
278 | int minvco = pll_lim->vco1.minfreq, maxvco = pll_lim->vco1.maxfreq; | ||
279 | int minM = pll_lim->vco1.min_m, maxM = pll_lim->vco1.max_m; | ||
280 | int minN = pll_lim->vco1.min_n, maxN = pll_lim->vco1.max_n; | ||
281 | int minU = pll_lim->vco1.min_inputfreq; | ||
282 | int maxU = pll_lim->vco1.max_inputfreq; | ||
283 | int minP = pll_lim->max_p ? pll_lim->min_p : 0; | ||
284 | int maxP = pll_lim->max_p ? pll_lim->max_p : pll_lim->max_usable_log2p; | ||
285 | int crystal = pll_lim->refclk; | ||
286 | int M, N, thisP, P; | ||
287 | int clkP, calcclk; | ||
288 | int delta, bestdelta = INT_MAX; | ||
289 | int bestclk = 0; | ||
290 | |||
291 | /* this division verified for nv20, nv18, nv28 (Haiku), and nv34 */ | ||
292 | /* possibly correlated with introduction of 27MHz crystal */ | ||
293 | if (dev_priv->card_type < NV_50) { | ||
294 | if (cv < 0x17 || cv == 0x1a || cv == 0x20) { | ||
295 | if (clk > 250000) | ||
296 | maxM = 6; | ||
297 | if (clk > 340000) | ||
298 | maxM = 2; | ||
299 | } else if (cv < 0x40) { | ||
300 | if (clk > 150000) | ||
301 | maxM = 6; | ||
302 | if (clk > 200000) | ||
303 | maxM = 4; | ||
304 | if (clk > 340000) | ||
305 | maxM = 2; | ||
306 | } | ||
307 | } | ||
308 | |||
309 | P = pll_lim->max_p ? maxP : (1 << maxP); | ||
310 | if ((clk * P) < minvco) { | ||
311 | minvco = clk * maxP; | ||
312 | maxvco = minvco * 2; | ||
313 | } | ||
314 | |||
315 | if (clk + clk/200 > maxvco) /* +0.5% */ | ||
316 | maxvco = clk + clk/200; | ||
317 | |||
318 | /* NV34 goes maxlog2P->0, NV20 goes 0->maxlog2P */ | ||
319 | for (thisP = minP; thisP <= maxP; thisP++) { | ||
320 | P = pll_lim->max_p ? thisP : (1 << thisP); | ||
321 | clkP = clk * P; | ||
322 | |||
323 | if (clkP < minvco) | ||
324 | continue; | ||
325 | if (clkP > maxvco) | ||
326 | return bestclk; | ||
327 | |||
328 | for (M = minM; M <= maxM; M++) { | ||
329 | if (crystal/M < minU) | ||
330 | return bestclk; | ||
331 | if (crystal/M > maxU) | ||
332 | continue; | ||
333 | |||
334 | /* add crystal/2 to round better */ | ||
335 | N = (clkP * M + crystal/2) / crystal; | ||
336 | |||
337 | if (N < minN) | ||
338 | continue; | ||
339 | if (N > maxN) | ||
340 | break; | ||
341 | |||
342 | /* more rounding additions */ | ||
343 | calcclk = ((N * crystal + P/2) / P + M/2) / M; | ||
344 | delta = abs(calcclk - clk); | ||
345 | /* we do an exhaustive search rather than terminating | ||
346 | * on an optimality condition... | ||
347 | */ | ||
348 | if (delta < bestdelta) { | ||
349 | bestdelta = delta; | ||
350 | bestclk = calcclk; | ||
351 | bestpv->N1 = N; | ||
352 | bestpv->M1 = M; | ||
353 | bestpv->log2P = thisP; | ||
354 | if (delta == 0) /* except this one */ | ||
355 | return bestclk; | ||
356 | } | ||
357 | } | ||
358 | } | ||
359 | |||
360 | return bestclk; | ||
361 | } | ||
362 | |||
363 | static int | ||
364 | getMNP_double(struct drm_device *dev, struct pll_lims *pll_lim, int clk, | ||
365 | struct nouveau_pll_vals *bestpv) | ||
366 | { | ||
367 | /* Find M, N and P for a two stage PLL | ||
368 | * | ||
369 | * Note that some bioses (NV30+) have lookup tables of precomputed MNP | ||
370 | * values, but we're too lazy to use those atm | ||
371 | * | ||
372 | * "clk" parameter in kHz | ||
373 | * returns calculated clock | ||
374 | */ | ||
375 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
376 | int chip_version = dev_priv->vbios->chip_version; | ||
377 | int minvco1 = pll_lim->vco1.minfreq, maxvco1 = pll_lim->vco1.maxfreq; | ||
378 | int minvco2 = pll_lim->vco2.minfreq, maxvco2 = pll_lim->vco2.maxfreq; | ||
379 | int minU1 = pll_lim->vco1.min_inputfreq, minU2 = pll_lim->vco2.min_inputfreq; | ||
380 | int maxU1 = pll_lim->vco1.max_inputfreq, maxU2 = pll_lim->vco2.max_inputfreq; | ||
381 | int minM1 = pll_lim->vco1.min_m, maxM1 = pll_lim->vco1.max_m; | ||
382 | int minN1 = pll_lim->vco1.min_n, maxN1 = pll_lim->vco1.max_n; | ||
383 | int minM2 = pll_lim->vco2.min_m, maxM2 = pll_lim->vco2.max_m; | ||
384 | int minN2 = pll_lim->vco2.min_n, maxN2 = pll_lim->vco2.max_n; | ||
385 | int maxlog2P = pll_lim->max_usable_log2p; | ||
386 | int crystal = pll_lim->refclk; | ||
387 | bool fixedgain2 = (minM2 == maxM2 && minN2 == maxN2); | ||
388 | int M1, N1, M2, N2, log2P; | ||
389 | int clkP, calcclk1, calcclk2, calcclkout; | ||
390 | int delta, bestdelta = INT_MAX; | ||
391 | int bestclk = 0; | ||
392 | |||
393 | int vco2 = (maxvco2 - maxvco2/200) / 2; | ||
394 | for (log2P = 0; clk && log2P < maxlog2P && clk <= (vco2 >> log2P); log2P++) | ||
395 | ; | ||
396 | clkP = clk << log2P; | ||
397 | |||
398 | if (maxvco2 < clk + clk/200) /* +0.5% */ | ||
399 | maxvco2 = clk + clk/200; | ||
400 | |||
401 | for (M1 = minM1; M1 <= maxM1; M1++) { | ||
402 | if (crystal/M1 < minU1) | ||
403 | return bestclk; | ||
404 | if (crystal/M1 > maxU1) | ||
405 | continue; | ||
406 | |||
407 | for (N1 = minN1; N1 <= maxN1; N1++) { | ||
408 | calcclk1 = crystal * N1 / M1; | ||
409 | if (calcclk1 < minvco1) | ||
410 | continue; | ||
411 | if (calcclk1 > maxvco1) | ||
412 | break; | ||
413 | |||
414 | for (M2 = minM2; M2 <= maxM2; M2++) { | ||
415 | if (calcclk1/M2 < minU2) | ||
416 | break; | ||
417 | if (calcclk1/M2 > maxU2) | ||
418 | continue; | ||
419 | |||
420 | /* add calcclk1/2 to round better */ | ||
421 | N2 = (clkP * M2 + calcclk1/2) / calcclk1; | ||
422 | if (N2 < minN2) | ||
423 | continue; | ||
424 | if (N2 > maxN2) | ||
425 | break; | ||
426 | |||
427 | if (!fixedgain2) { | ||
428 | if (chip_version < 0x60) | ||
429 | if (N2/M2 < 4 || N2/M2 > 10) | ||
430 | continue; | ||
431 | |||
432 | calcclk2 = calcclk1 * N2 / M2; | ||
433 | if (calcclk2 < minvco2) | ||
434 | break; | ||
435 | if (calcclk2 > maxvco2) | ||
436 | continue; | ||
437 | } else | ||
438 | calcclk2 = calcclk1; | ||
439 | |||
440 | calcclkout = calcclk2 >> log2P; | ||
441 | delta = abs(calcclkout - clk); | ||
442 | /* we do an exhaustive search rather than terminating | ||
443 | * on an optimality condition... | ||
444 | */ | ||
445 | if (delta < bestdelta) { | ||
446 | bestdelta = delta; | ||
447 | bestclk = calcclkout; | ||
448 | bestpv->N1 = N1; | ||
449 | bestpv->M1 = M1; | ||
450 | bestpv->N2 = N2; | ||
451 | bestpv->M2 = M2; | ||
452 | bestpv->log2P = log2P; | ||
453 | if (delta == 0) /* except this one */ | ||
454 | return bestclk; | ||
455 | } | ||
456 | } | ||
457 | } | ||
458 | } | ||
459 | |||
460 | return bestclk; | ||
461 | } | ||
462 | |||
463 | int | ||
464 | nouveau_calc_pll_mnp(struct drm_device *dev, struct pll_lims *pll_lim, int clk, | ||
465 | struct nouveau_pll_vals *pv) | ||
466 | { | ||
467 | int outclk; | ||
468 | |||
469 | if (!pll_lim->vco2.maxfreq) | ||
470 | outclk = getMNP_single(dev, pll_lim, clk, pv); | ||
471 | else | ||
472 | outclk = getMNP_double(dev, pll_lim, clk, pv); | ||
473 | |||
474 | if (!outclk) | ||
475 | NV_ERROR(dev, "Could not find a compatible set of PLL values\n"); | ||
476 | |||
477 | return outclk; | ||
478 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_channel.c b/drivers/gpu/drm/nouveau/nouveau_channel.c new file mode 100644 index 000000000000..9aaa972f8822 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_channel.c | |||
@@ -0,0 +1,468 @@ | |||
1 | /* | ||
2 | * Copyright 2005-2006 Stephane Marchesin | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
6 | * copy of this software and associated documentation files (the "Software"), | ||
7 | * to deal in the Software without restriction, including without limitation | ||
8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
9 | * and/or sell copies of the Software, and to permit persons to whom the | ||
10 | * Software is furnished to do so, subject to the following conditions: | ||
11 | * | ||
12 | * The above copyright notice and this permission notice (including the next | ||
13 | * paragraph) shall be included in all copies or substantial portions of the | ||
14 | * 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 | * PRECISION INSIGHT AND/OR ITS SUPPLIERS 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 OTHER | ||
22 | * DEALINGS IN THE SOFTWARE. | ||
23 | */ | ||
24 | |||
25 | #include "drmP.h" | ||
26 | #include "drm.h" | ||
27 | #include "nouveau_drv.h" | ||
28 | #include "nouveau_drm.h" | ||
29 | #include "nouveau_dma.h" | ||
30 | |||
31 | static int | ||
32 | nouveau_channel_pushbuf_ctxdma_init(struct nouveau_channel *chan) | ||
33 | { | ||
34 | struct drm_device *dev = chan->dev; | ||
35 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
36 | struct nouveau_bo *pb = chan->pushbuf_bo; | ||
37 | struct nouveau_gpuobj *pushbuf = NULL; | ||
38 | uint32_t start = pb->bo.mem.mm_node->start << PAGE_SHIFT; | ||
39 | int ret; | ||
40 | |||
41 | if (pb->bo.mem.mem_type == TTM_PL_TT) { | ||
42 | ret = nouveau_gpuobj_gart_dma_new(chan, 0, | ||
43 | dev_priv->gart_info.aper_size, | ||
44 | NV_DMA_ACCESS_RO, &pushbuf, | ||
45 | NULL); | ||
46 | chan->pushbuf_base = start; | ||
47 | } else | ||
48 | if (dev_priv->card_type != NV_04) { | ||
49 | ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, 0, | ||
50 | dev_priv->fb_available_size, | ||
51 | NV_DMA_ACCESS_RO, | ||
52 | NV_DMA_TARGET_VIDMEM, &pushbuf); | ||
53 | chan->pushbuf_base = start; | ||
54 | } else { | ||
55 | /* NV04 cmdbuf hack, from original ddx.. not sure of it's | ||
56 | * exact reason for existing :) PCI access to cmdbuf in | ||
57 | * VRAM. | ||
58 | */ | ||
59 | ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, | ||
60 | drm_get_resource_start(dev, 1), | ||
61 | dev_priv->fb_available_size, | ||
62 | NV_DMA_ACCESS_RO, | ||
63 | NV_DMA_TARGET_PCI, &pushbuf); | ||
64 | chan->pushbuf_base = start; | ||
65 | } | ||
66 | |||
67 | ret = nouveau_gpuobj_ref_add(dev, chan, 0, pushbuf, &chan->pushbuf); | ||
68 | if (ret) { | ||
69 | NV_ERROR(dev, "Error referencing pushbuf ctxdma: %d\n", ret); | ||
70 | if (pushbuf != dev_priv->gart_info.sg_ctxdma) | ||
71 | nouveau_gpuobj_del(dev, &pushbuf); | ||
72 | return ret; | ||
73 | } | ||
74 | |||
75 | return 0; | ||
76 | } | ||
77 | |||
78 | static struct nouveau_bo * | ||
79 | nouveau_channel_user_pushbuf_alloc(struct drm_device *dev) | ||
80 | { | ||
81 | struct nouveau_bo *pushbuf = NULL; | ||
82 | int location, ret; | ||
83 | |||
84 | if (nouveau_vram_pushbuf) | ||
85 | location = TTM_PL_FLAG_VRAM; | ||
86 | else | ||
87 | location = TTM_PL_FLAG_TT; | ||
88 | |||
89 | ret = nouveau_bo_new(dev, NULL, 65536, 0, location, 0, 0x0000, false, | ||
90 | true, &pushbuf); | ||
91 | if (ret) { | ||
92 | NV_ERROR(dev, "error allocating DMA push buffer: %d\n", ret); | ||
93 | return NULL; | ||
94 | } | ||
95 | |||
96 | ret = nouveau_bo_pin(pushbuf, location); | ||
97 | if (ret) { | ||
98 | NV_ERROR(dev, "error pinning DMA push buffer: %d\n", ret); | ||
99 | nouveau_bo_ref(NULL, &pushbuf); | ||
100 | return NULL; | ||
101 | } | ||
102 | |||
103 | return pushbuf; | ||
104 | } | ||
105 | |||
106 | /* allocates and initializes a fifo for user space consumption */ | ||
107 | int | ||
108 | nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret, | ||
109 | struct drm_file *file_priv, | ||
110 | uint32_t vram_handle, uint32_t tt_handle) | ||
111 | { | ||
112 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
113 | struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; | ||
114 | struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; | ||
115 | struct nouveau_channel *chan; | ||
116 | int channel, user; | ||
117 | int ret; | ||
118 | |||
119 | /* | ||
120 | * Alright, here is the full story | ||
121 | * Nvidia cards have multiple hw fifo contexts (praise them for that, | ||
122 | * no complicated crash-prone context switches) | ||
123 | * We allocate a new context for each app and let it write to it | ||
124 | * directly (woo, full userspace command submission !) | ||
125 | * When there are no more contexts, you lost | ||
126 | */ | ||
127 | for (channel = 0; channel < pfifo->channels; channel++) { | ||
128 | if (dev_priv->fifos[channel] == NULL) | ||
129 | break; | ||
130 | } | ||
131 | |||
132 | /* no more fifos. you lost. */ | ||
133 | if (channel == pfifo->channels) | ||
134 | return -EINVAL; | ||
135 | |||
136 | dev_priv->fifos[channel] = kzalloc(sizeof(struct nouveau_channel), | ||
137 | GFP_KERNEL); | ||
138 | if (!dev_priv->fifos[channel]) | ||
139 | return -ENOMEM; | ||
140 | dev_priv->fifo_alloc_count++; | ||
141 | chan = dev_priv->fifos[channel]; | ||
142 | INIT_LIST_HEAD(&chan->nvsw.vbl_wait); | ||
143 | INIT_LIST_HEAD(&chan->fence.pending); | ||
144 | chan->dev = dev; | ||
145 | chan->id = channel; | ||
146 | chan->file_priv = file_priv; | ||
147 | chan->vram_handle = vram_handle; | ||
148 | chan->gart_handle = tt_handle; | ||
149 | |||
150 | NV_INFO(dev, "Allocating FIFO number %d\n", channel); | ||
151 | |||
152 | /* Allocate DMA push buffer */ | ||
153 | chan->pushbuf_bo = nouveau_channel_user_pushbuf_alloc(dev); | ||
154 | if (!chan->pushbuf_bo) { | ||
155 | ret = -ENOMEM; | ||
156 | NV_ERROR(dev, "pushbuf %d\n", ret); | ||
157 | nouveau_channel_free(chan); | ||
158 | return ret; | ||
159 | } | ||
160 | |||
161 | /* Locate channel's user control regs */ | ||
162 | if (dev_priv->card_type < NV_40) | ||
163 | user = NV03_USER(channel); | ||
164 | else | ||
165 | if (dev_priv->card_type < NV_50) | ||
166 | user = NV40_USER(channel); | ||
167 | else | ||
168 | user = NV50_USER(channel); | ||
169 | |||
170 | chan->user = ioremap(pci_resource_start(dev->pdev, 0) + user, | ||
171 | PAGE_SIZE); | ||
172 | if (!chan->user) { | ||
173 | NV_ERROR(dev, "ioremap of regs failed.\n"); | ||
174 | nouveau_channel_free(chan); | ||
175 | return -ENOMEM; | ||
176 | } | ||
177 | chan->user_put = 0x40; | ||
178 | chan->user_get = 0x44; | ||
179 | |||
180 | /* Allocate space for per-channel fixed notifier memory */ | ||
181 | ret = nouveau_notifier_init_channel(chan); | ||
182 | if (ret) { | ||
183 | NV_ERROR(dev, "ntfy %d\n", ret); | ||
184 | nouveau_channel_free(chan); | ||
185 | return ret; | ||
186 | } | ||
187 | |||
188 | /* Setup channel's default objects */ | ||
189 | ret = nouveau_gpuobj_channel_init(chan, vram_handle, tt_handle); | ||
190 | if (ret) { | ||
191 | NV_ERROR(dev, "gpuobj %d\n", ret); | ||
192 | nouveau_channel_free(chan); | ||
193 | return ret; | ||
194 | } | ||
195 | |||
196 | /* Create a dma object for the push buffer */ | ||
197 | ret = nouveau_channel_pushbuf_ctxdma_init(chan); | ||
198 | if (ret) { | ||
199 | NV_ERROR(dev, "pbctxdma %d\n", ret); | ||
200 | nouveau_channel_free(chan); | ||
201 | return ret; | ||
202 | } | ||
203 | |||
204 | /* disable the fifo caches */ | ||
205 | pfifo->reassign(dev, false); | ||
206 | |||
207 | /* Create a graphics context for new channel */ | ||
208 | ret = pgraph->create_context(chan); | ||
209 | if (ret) { | ||
210 | nouveau_channel_free(chan); | ||
211 | return ret; | ||
212 | } | ||
213 | |||
214 | /* Construct inital RAMFC for new channel */ | ||
215 | ret = pfifo->create_context(chan); | ||
216 | if (ret) { | ||
217 | nouveau_channel_free(chan); | ||
218 | return ret; | ||
219 | } | ||
220 | |||
221 | pfifo->reassign(dev, true); | ||
222 | |||
223 | ret = nouveau_dma_init(chan); | ||
224 | if (!ret) | ||
225 | ret = nouveau_fence_init(chan); | ||
226 | if (ret) { | ||
227 | nouveau_channel_free(chan); | ||
228 | return ret; | ||
229 | } | ||
230 | |||
231 | nouveau_debugfs_channel_init(chan); | ||
232 | |||
233 | NV_INFO(dev, "%s: initialised FIFO %d\n", __func__, channel); | ||
234 | *chan_ret = chan; | ||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | int | ||
239 | nouveau_channel_idle(struct nouveau_channel *chan) | ||
240 | { | ||
241 | struct drm_device *dev = chan->dev; | ||
242 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
243 | struct nouveau_engine *engine = &dev_priv->engine; | ||
244 | uint32_t caches; | ||
245 | int idle; | ||
246 | |||
247 | if (!chan) { | ||
248 | NV_ERROR(dev, "no channel...\n"); | ||
249 | return 1; | ||
250 | } | ||
251 | |||
252 | caches = nv_rd32(dev, NV03_PFIFO_CACHES); | ||
253 | nv_wr32(dev, NV03_PFIFO_CACHES, caches & ~1); | ||
254 | |||
255 | if (engine->fifo.channel_id(dev) != chan->id) { | ||
256 | struct nouveau_gpuobj *ramfc = | ||
257 | chan->ramfc ? chan->ramfc->gpuobj : NULL; | ||
258 | |||
259 | if (!ramfc) { | ||
260 | NV_ERROR(dev, "No RAMFC for channel %d\n", chan->id); | ||
261 | return 1; | ||
262 | } | ||
263 | |||
264 | engine->instmem.prepare_access(dev, false); | ||
265 | if (nv_ro32(dev, ramfc, 0) != nv_ro32(dev, ramfc, 1)) | ||
266 | idle = 0; | ||
267 | else | ||
268 | idle = 1; | ||
269 | engine->instmem.finish_access(dev); | ||
270 | } else { | ||
271 | idle = (nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET) == | ||
272 | nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUT)); | ||
273 | } | ||
274 | |||
275 | nv_wr32(dev, NV03_PFIFO_CACHES, caches); | ||
276 | return idle; | ||
277 | } | ||
278 | |||
279 | /* stops a fifo */ | ||
280 | void | ||
281 | nouveau_channel_free(struct nouveau_channel *chan) | ||
282 | { | ||
283 | struct drm_device *dev = chan->dev; | ||
284 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
285 | struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; | ||
286 | struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; | ||
287 | unsigned long flags; | ||
288 | int ret; | ||
289 | |||
290 | NV_INFO(dev, "%s: freeing fifo %d\n", __func__, chan->id); | ||
291 | |||
292 | nouveau_debugfs_channel_fini(chan); | ||
293 | |||
294 | /* Give outstanding push buffers a chance to complete */ | ||
295 | spin_lock_irqsave(&chan->fence.lock, flags); | ||
296 | nouveau_fence_update(chan); | ||
297 | spin_unlock_irqrestore(&chan->fence.lock, flags); | ||
298 | if (chan->fence.sequence != chan->fence.sequence_ack) { | ||
299 | struct nouveau_fence *fence = NULL; | ||
300 | |||
301 | ret = nouveau_fence_new(chan, &fence, true); | ||
302 | if (ret == 0) { | ||
303 | ret = nouveau_fence_wait(fence, NULL, false, false); | ||
304 | nouveau_fence_unref((void *)&fence); | ||
305 | } | ||
306 | |||
307 | if (ret) | ||
308 | NV_ERROR(dev, "Failed to idle channel %d.\n", chan->id); | ||
309 | } | ||
310 | |||
311 | /* Ensure all outstanding fences are signaled. They should be if the | ||
312 | * above attempts at idling were OK, but if we failed this'll tell TTM | ||
313 | * we're done with the buffers. | ||
314 | */ | ||
315 | nouveau_fence_fini(chan); | ||
316 | |||
317 | /* Ensure the channel is no longer active on the GPU */ | ||
318 | pfifo->reassign(dev, false); | ||
319 | |||
320 | if (pgraph->channel(dev) == chan) { | ||
321 | pgraph->fifo_access(dev, false); | ||
322 | pgraph->unload_context(dev); | ||
323 | pgraph->fifo_access(dev, true); | ||
324 | } | ||
325 | pgraph->destroy_context(chan); | ||
326 | |||
327 | if (pfifo->channel_id(dev) == chan->id) { | ||
328 | pfifo->disable(dev); | ||
329 | pfifo->unload_context(dev); | ||
330 | pfifo->enable(dev); | ||
331 | } | ||
332 | pfifo->destroy_context(chan); | ||
333 | |||
334 | pfifo->reassign(dev, true); | ||
335 | |||
336 | /* Release the channel's resources */ | ||
337 | nouveau_gpuobj_ref_del(dev, &chan->pushbuf); | ||
338 | if (chan->pushbuf_bo) { | ||
339 | nouveau_bo_unpin(chan->pushbuf_bo); | ||
340 | nouveau_bo_ref(NULL, &chan->pushbuf_bo); | ||
341 | } | ||
342 | nouveau_gpuobj_channel_takedown(chan); | ||
343 | nouveau_notifier_takedown_channel(chan); | ||
344 | if (chan->user) | ||
345 | iounmap(chan->user); | ||
346 | |||
347 | dev_priv->fifos[chan->id] = NULL; | ||
348 | dev_priv->fifo_alloc_count--; | ||
349 | kfree(chan); | ||
350 | } | ||
351 | |||
352 | /* cleans up all the fifos from file_priv */ | ||
353 | void | ||
354 | nouveau_channel_cleanup(struct drm_device *dev, struct drm_file *file_priv) | ||
355 | { | ||
356 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
357 | struct nouveau_engine *engine = &dev_priv->engine; | ||
358 | int i; | ||
359 | |||
360 | NV_DEBUG(dev, "clearing FIFO enables from file_priv\n"); | ||
361 | for (i = 0; i < engine->fifo.channels; i++) { | ||
362 | struct nouveau_channel *chan = dev_priv->fifos[i]; | ||
363 | |||
364 | if (chan && chan->file_priv == file_priv) | ||
365 | nouveau_channel_free(chan); | ||
366 | } | ||
367 | } | ||
368 | |||
369 | int | ||
370 | nouveau_channel_owner(struct drm_device *dev, struct drm_file *file_priv, | ||
371 | int channel) | ||
372 | { | ||
373 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
374 | struct nouveau_engine *engine = &dev_priv->engine; | ||
375 | |||
376 | if (channel >= engine->fifo.channels) | ||
377 | return 0; | ||
378 | if (dev_priv->fifos[channel] == NULL) | ||
379 | return 0; | ||
380 | |||
381 | return (dev_priv->fifos[channel]->file_priv == file_priv); | ||
382 | } | ||
383 | |||
384 | /*********************************** | ||
385 | * ioctls wrapping the functions | ||
386 | ***********************************/ | ||
387 | |||
388 | static int | ||
389 | nouveau_ioctl_fifo_alloc(struct drm_device *dev, void *data, | ||
390 | struct drm_file *file_priv) | ||
391 | { | ||
392 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
393 | struct drm_nouveau_channel_alloc *init = data; | ||
394 | struct nouveau_channel *chan; | ||
395 | int ret; | ||
396 | |||
397 | NOUVEAU_CHECK_INITIALISED_WITH_RETURN; | ||
398 | |||
399 | if (dev_priv->engine.graph.accel_blocked) | ||
400 | return -ENODEV; | ||
401 | |||
402 | if (init->fb_ctxdma_handle == ~0 || init->tt_ctxdma_handle == ~0) | ||
403 | return -EINVAL; | ||
404 | |||
405 | ret = nouveau_channel_alloc(dev, &chan, file_priv, | ||
406 | init->fb_ctxdma_handle, | ||
407 | init->tt_ctxdma_handle); | ||
408 | if (ret) | ||
409 | return ret; | ||
410 | init->channel = chan->id; | ||
411 | |||
412 | init->subchan[0].handle = NvM2MF; | ||
413 | if (dev_priv->card_type < NV_50) | ||
414 | init->subchan[0].grclass = 0x0039; | ||
415 | else | ||
416 | init->subchan[0].grclass = 0x5039; | ||
417 | init->nr_subchan = 1; | ||
418 | |||
419 | /* Named memory object area */ | ||
420 | ret = drm_gem_handle_create(file_priv, chan->notifier_bo->gem, | ||
421 | &init->notifier_handle); | ||
422 | if (ret) { | ||
423 | nouveau_channel_free(chan); | ||
424 | return ret; | ||
425 | } | ||
426 | |||
427 | return 0; | ||
428 | } | ||
429 | |||
430 | static int | ||
431 | nouveau_ioctl_fifo_free(struct drm_device *dev, void *data, | ||
432 | struct drm_file *file_priv) | ||
433 | { | ||
434 | struct drm_nouveau_channel_free *cfree = data; | ||
435 | struct nouveau_channel *chan; | ||
436 | |||
437 | NOUVEAU_CHECK_INITIALISED_WITH_RETURN; | ||
438 | NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(cfree->channel, file_priv, chan); | ||
439 | |||
440 | nouveau_channel_free(chan); | ||
441 | return 0; | ||
442 | } | ||
443 | |||
444 | /*********************************** | ||
445 | * finally, the ioctl table | ||
446 | ***********************************/ | ||
447 | |||
448 | struct drm_ioctl_desc nouveau_ioctls[] = { | ||
449 | DRM_IOCTL_DEF(DRM_NOUVEAU_CARD_INIT, nouveau_ioctl_card_init, DRM_AUTH), | ||
450 | DRM_IOCTL_DEF(DRM_NOUVEAU_GETPARAM, nouveau_ioctl_getparam, DRM_AUTH), | ||
451 | DRM_IOCTL_DEF(DRM_NOUVEAU_SETPARAM, nouveau_ioctl_setparam, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), | ||
452 | DRM_IOCTL_DEF(DRM_NOUVEAU_CHANNEL_ALLOC, nouveau_ioctl_fifo_alloc, DRM_AUTH), | ||
453 | DRM_IOCTL_DEF(DRM_NOUVEAU_CHANNEL_FREE, nouveau_ioctl_fifo_free, DRM_AUTH), | ||
454 | DRM_IOCTL_DEF(DRM_NOUVEAU_GROBJ_ALLOC, nouveau_ioctl_grobj_alloc, DRM_AUTH), | ||
455 | DRM_IOCTL_DEF(DRM_NOUVEAU_NOTIFIEROBJ_ALLOC, nouveau_ioctl_notifier_alloc, DRM_AUTH), | ||
456 | DRM_IOCTL_DEF(DRM_NOUVEAU_GPUOBJ_FREE, nouveau_ioctl_gpuobj_free, DRM_AUTH), | ||
457 | DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_NEW, nouveau_gem_ioctl_new, DRM_AUTH), | ||
458 | DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PUSHBUF, nouveau_gem_ioctl_pushbuf, DRM_AUTH), | ||
459 | DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PUSHBUF_CALL, nouveau_gem_ioctl_pushbuf_call, DRM_AUTH), | ||
460 | DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PIN, nouveau_gem_ioctl_pin, DRM_AUTH), | ||
461 | DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_UNPIN, nouveau_gem_ioctl_unpin, DRM_AUTH), | ||
462 | DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_CPU_PREP, nouveau_gem_ioctl_cpu_prep, DRM_AUTH), | ||
463 | DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_CPU_FINI, nouveau_gem_ioctl_cpu_fini, DRM_AUTH), | ||
464 | DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_INFO, nouveau_gem_ioctl_info, DRM_AUTH), | ||
465 | DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PUSHBUF_CALL2, nouveau_gem_ioctl_pushbuf_call2, DRM_AUTH), | ||
466 | }; | ||
467 | |||
468 | int nouveau_max_ioctl = DRM_ARRAY_SIZE(nouveau_ioctls); | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c new file mode 100644 index 000000000000..032cf098fa1c --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c | |||
@@ -0,0 +1,824 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2008 Maarten Maathuis. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include "drmP.h" | ||
28 | #include "drm_edid.h" | ||
29 | #include "drm_crtc_helper.h" | ||
30 | #include "nouveau_reg.h" | ||
31 | #include "nouveau_drv.h" | ||
32 | #include "nouveau_encoder.h" | ||
33 | #include "nouveau_crtc.h" | ||
34 | #include "nouveau_connector.h" | ||
35 | #include "nouveau_hw.h" | ||
36 | |||
37 | static inline struct drm_encoder_slave_funcs * | ||
38 | get_slave_funcs(struct nouveau_encoder *enc) | ||
39 | { | ||
40 | return to_encoder_slave(to_drm_encoder(enc))->slave_funcs; | ||
41 | } | ||
42 | |||
43 | static struct nouveau_encoder * | ||
44 | find_encoder_by_type(struct drm_connector *connector, int type) | ||
45 | { | ||
46 | struct drm_device *dev = connector->dev; | ||
47 | struct nouveau_encoder *nv_encoder; | ||
48 | struct drm_mode_object *obj; | ||
49 | int i, id; | ||
50 | |||
51 | for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { | ||
52 | id = connector->encoder_ids[i]; | ||
53 | if (!id) | ||
54 | break; | ||
55 | |||
56 | obj = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER); | ||
57 | if (!obj) | ||
58 | continue; | ||
59 | nv_encoder = nouveau_encoder(obj_to_encoder(obj)); | ||
60 | |||
61 | if (type == OUTPUT_ANY || nv_encoder->dcb->type == type) | ||
62 | return nv_encoder; | ||
63 | } | ||
64 | |||
65 | return NULL; | ||
66 | } | ||
67 | |||
68 | struct nouveau_connector * | ||
69 | nouveau_encoder_connector_get(struct nouveau_encoder *encoder) | ||
70 | { | ||
71 | struct drm_device *dev = to_drm_encoder(encoder)->dev; | ||
72 | struct drm_connector *drm_connector; | ||
73 | |||
74 | list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) { | ||
75 | if (drm_connector->encoder == to_drm_encoder(encoder)) | ||
76 | return nouveau_connector(drm_connector); | ||
77 | } | ||
78 | |||
79 | return NULL; | ||
80 | } | ||
81 | |||
82 | |||
83 | static void | ||
84 | nouveau_connector_destroy(struct drm_connector *drm_connector) | ||
85 | { | ||
86 | struct nouveau_connector *connector = nouveau_connector(drm_connector); | ||
87 | struct drm_device *dev = connector->base.dev; | ||
88 | |||
89 | NV_DEBUG(dev, "\n"); | ||
90 | |||
91 | if (!connector) | ||
92 | return; | ||
93 | |||
94 | drm_sysfs_connector_remove(drm_connector); | ||
95 | drm_connector_cleanup(drm_connector); | ||
96 | kfree(drm_connector); | ||
97 | } | ||
98 | |||
99 | static void | ||
100 | nouveau_connector_ddc_prepare(struct drm_connector *connector, int *flags) | ||
101 | { | ||
102 | struct drm_nouveau_private *dev_priv = connector->dev->dev_private; | ||
103 | |||
104 | if (dev_priv->card_type >= NV_50) | ||
105 | return; | ||
106 | |||
107 | *flags = 0; | ||
108 | if (NVLockVgaCrtcs(dev_priv->dev, false)) | ||
109 | *flags |= 1; | ||
110 | if (nv_heads_tied(dev_priv->dev)) | ||
111 | *flags |= 2; | ||
112 | |||
113 | if (*flags & 2) | ||
114 | NVSetOwner(dev_priv->dev, 0); /* necessary? */ | ||
115 | } | ||
116 | |||
117 | static void | ||
118 | nouveau_connector_ddc_finish(struct drm_connector *connector, int flags) | ||
119 | { | ||
120 | struct drm_nouveau_private *dev_priv = connector->dev->dev_private; | ||
121 | |||
122 | if (dev_priv->card_type >= NV_50) | ||
123 | return; | ||
124 | |||
125 | if (flags & 2) | ||
126 | NVSetOwner(dev_priv->dev, 4); | ||
127 | if (flags & 1) | ||
128 | NVLockVgaCrtcs(dev_priv->dev, true); | ||
129 | } | ||
130 | |||
131 | static struct nouveau_i2c_chan * | ||
132 | nouveau_connector_ddc_detect(struct drm_connector *connector, | ||
133 | struct nouveau_encoder **pnv_encoder) | ||
134 | { | ||
135 | struct drm_device *dev = connector->dev; | ||
136 | uint8_t out_buf[] = { 0x0, 0x0}, buf[2]; | ||
137 | int ret, flags, i; | ||
138 | |||
139 | struct i2c_msg msgs[] = { | ||
140 | { | ||
141 | .addr = 0x50, | ||
142 | .flags = 0, | ||
143 | .len = 1, | ||
144 | .buf = out_buf, | ||
145 | }, | ||
146 | { | ||
147 | .addr = 0x50, | ||
148 | .flags = I2C_M_RD, | ||
149 | .len = 1, | ||
150 | .buf = buf, | ||
151 | } | ||
152 | }; | ||
153 | |||
154 | for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { | ||
155 | struct nouveau_i2c_chan *i2c = NULL; | ||
156 | struct nouveau_encoder *nv_encoder; | ||
157 | struct drm_mode_object *obj; | ||
158 | int id; | ||
159 | |||
160 | id = connector->encoder_ids[i]; | ||
161 | if (!id) | ||
162 | break; | ||
163 | |||
164 | obj = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER); | ||
165 | if (!obj) | ||
166 | continue; | ||
167 | nv_encoder = nouveau_encoder(obj_to_encoder(obj)); | ||
168 | |||
169 | if (nv_encoder->dcb->i2c_index < 0xf) | ||
170 | i2c = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index); | ||
171 | if (!i2c) | ||
172 | continue; | ||
173 | |||
174 | nouveau_connector_ddc_prepare(connector, &flags); | ||
175 | ret = i2c_transfer(&i2c->adapter, msgs, 2); | ||
176 | nouveau_connector_ddc_finish(connector, flags); | ||
177 | |||
178 | if (ret == 2) { | ||
179 | *pnv_encoder = nv_encoder; | ||
180 | return i2c; | ||
181 | } | ||
182 | } | ||
183 | |||
184 | return NULL; | ||
185 | } | ||
186 | |||
187 | static void | ||
188 | nouveau_connector_set_encoder(struct drm_connector *connector, | ||
189 | struct nouveau_encoder *nv_encoder) | ||
190 | { | ||
191 | struct nouveau_connector *nv_connector = nouveau_connector(connector); | ||
192 | struct drm_nouveau_private *dev_priv = connector->dev->dev_private; | ||
193 | struct drm_device *dev = connector->dev; | ||
194 | |||
195 | if (nv_connector->detected_encoder == nv_encoder) | ||
196 | return; | ||
197 | nv_connector->detected_encoder = nv_encoder; | ||
198 | |||
199 | if (nv_encoder->dcb->type == OUTPUT_LVDS || | ||
200 | nv_encoder->dcb->type == OUTPUT_TMDS) { | ||
201 | connector->doublescan_allowed = false; | ||
202 | connector->interlace_allowed = false; | ||
203 | } else { | ||
204 | connector->doublescan_allowed = true; | ||
205 | if (dev_priv->card_type == NV_20 || | ||
206 | (dev_priv->card_type == NV_10 && | ||
207 | (dev->pci_device & 0x0ff0) != 0x0100 && | ||
208 | (dev->pci_device & 0x0ff0) != 0x0150)) | ||
209 | /* HW is broken */ | ||
210 | connector->interlace_allowed = false; | ||
211 | else | ||
212 | connector->interlace_allowed = true; | ||
213 | } | ||
214 | |||
215 | if (connector->connector_type == DRM_MODE_CONNECTOR_DVII) { | ||
216 | drm_connector_property_set_value(connector, | ||
217 | dev->mode_config.dvi_i_subconnector_property, | ||
218 | nv_encoder->dcb->type == OUTPUT_TMDS ? | ||
219 | DRM_MODE_SUBCONNECTOR_DVID : | ||
220 | DRM_MODE_SUBCONNECTOR_DVIA); | ||
221 | } | ||
222 | } | ||
223 | |||
224 | static enum drm_connector_status | ||
225 | nouveau_connector_detect(struct drm_connector *connector) | ||
226 | { | ||
227 | struct drm_device *dev = connector->dev; | ||
228 | struct nouveau_connector *nv_connector = nouveau_connector(connector); | ||
229 | struct nouveau_encoder *nv_encoder = NULL; | ||
230 | struct nouveau_i2c_chan *i2c; | ||
231 | int type, flags; | ||
232 | |||
233 | if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) | ||
234 | nv_encoder = find_encoder_by_type(connector, OUTPUT_LVDS); | ||
235 | if (nv_encoder && nv_connector->native_mode) { | ||
236 | nouveau_connector_set_encoder(connector, nv_encoder); | ||
237 | return connector_status_connected; | ||
238 | } | ||
239 | |||
240 | i2c = nouveau_connector_ddc_detect(connector, &nv_encoder); | ||
241 | if (i2c) { | ||
242 | nouveau_connector_ddc_prepare(connector, &flags); | ||
243 | nv_connector->edid = drm_get_edid(connector, &i2c->adapter); | ||
244 | nouveau_connector_ddc_finish(connector, flags); | ||
245 | drm_mode_connector_update_edid_property(connector, | ||
246 | nv_connector->edid); | ||
247 | if (!nv_connector->edid) { | ||
248 | NV_ERROR(dev, "DDC responded, but no EDID for %s\n", | ||
249 | drm_get_connector_name(connector)); | ||
250 | return connector_status_disconnected; | ||
251 | } | ||
252 | |||
253 | if (nv_encoder->dcb->type == OUTPUT_DP && | ||
254 | !nouveau_dp_detect(to_drm_encoder(nv_encoder))) { | ||
255 | NV_ERROR(dev, "Detected %s, but failed init\n", | ||
256 | drm_get_connector_name(connector)); | ||
257 | return connector_status_disconnected; | ||
258 | } | ||
259 | |||
260 | /* Override encoder type for DVI-I based on whether EDID | ||
261 | * says the display is digital or analog, both use the | ||
262 | * same i2c channel so the value returned from ddc_detect | ||
263 | * isn't necessarily correct. | ||
264 | */ | ||
265 | if (connector->connector_type == DRM_MODE_CONNECTOR_DVII) { | ||
266 | if (nv_connector->edid->input & DRM_EDID_INPUT_DIGITAL) | ||
267 | type = OUTPUT_TMDS; | ||
268 | else | ||
269 | type = OUTPUT_ANALOG; | ||
270 | |||
271 | nv_encoder = find_encoder_by_type(connector, type); | ||
272 | if (!nv_encoder) { | ||
273 | NV_ERROR(dev, "Detected %d encoder on %s, " | ||
274 | "but no object!\n", type, | ||
275 | drm_get_connector_name(connector)); | ||
276 | return connector_status_disconnected; | ||
277 | } | ||
278 | } | ||
279 | |||
280 | nouveau_connector_set_encoder(connector, nv_encoder); | ||
281 | return connector_status_connected; | ||
282 | } | ||
283 | |||
284 | nv_encoder = find_encoder_by_type(connector, OUTPUT_ANALOG); | ||
285 | if (!nv_encoder) | ||
286 | nv_encoder = find_encoder_by_type(connector, OUTPUT_TV); | ||
287 | if (nv_encoder) { | ||
288 | struct drm_encoder *encoder = to_drm_encoder(nv_encoder); | ||
289 | struct drm_encoder_helper_funcs *helper = | ||
290 | encoder->helper_private; | ||
291 | |||
292 | if (helper->detect(encoder, connector) == | ||
293 | connector_status_connected) { | ||
294 | nouveau_connector_set_encoder(connector, nv_encoder); | ||
295 | return connector_status_connected; | ||
296 | } | ||
297 | |||
298 | } | ||
299 | |||
300 | return connector_status_disconnected; | ||
301 | } | ||
302 | |||
303 | static void | ||
304 | nouveau_connector_force(struct drm_connector *connector) | ||
305 | { | ||
306 | struct drm_device *dev = connector->dev; | ||
307 | struct nouveau_encoder *nv_encoder; | ||
308 | int type; | ||
309 | |||
310 | if (connector->connector_type == DRM_MODE_CONNECTOR_DVII) { | ||
311 | if (connector->force == DRM_FORCE_ON_DIGITAL) | ||
312 | type = OUTPUT_TMDS; | ||
313 | else | ||
314 | type = OUTPUT_ANALOG; | ||
315 | } else | ||
316 | type = OUTPUT_ANY; | ||
317 | |||
318 | nv_encoder = find_encoder_by_type(connector, type); | ||
319 | if (!nv_encoder) { | ||
320 | NV_ERROR(dev, "can't find encoder to force %s on!\n", | ||
321 | drm_get_connector_name(connector)); | ||
322 | connector->status = connector_status_disconnected; | ||
323 | return; | ||
324 | } | ||
325 | |||
326 | nouveau_connector_set_encoder(connector, nv_encoder); | ||
327 | } | ||
328 | |||
329 | static int | ||
330 | nouveau_connector_set_property(struct drm_connector *connector, | ||
331 | struct drm_property *property, uint64_t value) | ||
332 | { | ||
333 | struct nouveau_connector *nv_connector = nouveau_connector(connector); | ||
334 | struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; | ||
335 | struct drm_device *dev = connector->dev; | ||
336 | int ret; | ||
337 | |||
338 | /* Scaling mode */ | ||
339 | if (property == dev->mode_config.scaling_mode_property) { | ||
340 | struct nouveau_crtc *nv_crtc = NULL; | ||
341 | bool modeset = false; | ||
342 | |||
343 | switch (value) { | ||
344 | case DRM_MODE_SCALE_NONE: | ||
345 | case DRM_MODE_SCALE_FULLSCREEN: | ||
346 | case DRM_MODE_SCALE_CENTER: | ||
347 | case DRM_MODE_SCALE_ASPECT: | ||
348 | break; | ||
349 | default: | ||
350 | return -EINVAL; | ||
351 | } | ||
352 | |||
353 | /* LVDS always needs gpu scaling */ | ||
354 | if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS && | ||
355 | value == DRM_MODE_SCALE_NONE) | ||
356 | return -EINVAL; | ||
357 | |||
358 | /* Changing between GPU and panel scaling requires a full | ||
359 | * modeset | ||
360 | */ | ||
361 | if ((nv_connector->scaling_mode == DRM_MODE_SCALE_NONE) || | ||
362 | (value == DRM_MODE_SCALE_NONE)) | ||
363 | modeset = true; | ||
364 | nv_connector->scaling_mode = value; | ||
365 | |||
366 | if (connector->encoder && connector->encoder->crtc) | ||
367 | nv_crtc = nouveau_crtc(connector->encoder->crtc); | ||
368 | if (!nv_crtc) | ||
369 | return 0; | ||
370 | |||
371 | if (modeset || !nv_crtc->set_scale) { | ||
372 | ret = drm_crtc_helper_set_mode(&nv_crtc->base, | ||
373 | &nv_crtc->base.mode, | ||
374 | nv_crtc->base.x, | ||
375 | nv_crtc->base.y, NULL); | ||
376 | if (!ret) | ||
377 | return -EINVAL; | ||
378 | } else { | ||
379 | ret = nv_crtc->set_scale(nv_crtc, value, true); | ||
380 | if (ret) | ||
381 | return ret; | ||
382 | } | ||
383 | |||
384 | return 0; | ||
385 | } | ||
386 | |||
387 | /* Dithering */ | ||
388 | if (property == dev->mode_config.dithering_mode_property) { | ||
389 | struct nouveau_crtc *nv_crtc = NULL; | ||
390 | |||
391 | if (value == DRM_MODE_DITHERING_ON) | ||
392 | nv_connector->use_dithering = true; | ||
393 | else | ||
394 | nv_connector->use_dithering = false; | ||
395 | |||
396 | if (connector->encoder && connector->encoder->crtc) | ||
397 | nv_crtc = nouveau_crtc(connector->encoder->crtc); | ||
398 | |||
399 | if (!nv_crtc || !nv_crtc->set_dither) | ||
400 | return 0; | ||
401 | |||
402 | return nv_crtc->set_dither(nv_crtc, nv_connector->use_dithering, | ||
403 | true); | ||
404 | } | ||
405 | |||
406 | if (nv_encoder && nv_encoder->dcb->type == OUTPUT_TV) | ||
407 | return get_slave_funcs(nv_encoder)-> | ||
408 | set_property(to_drm_encoder(nv_encoder), connector, property, value); | ||
409 | |||
410 | return -EINVAL; | ||
411 | } | ||
412 | |||
413 | static struct drm_display_mode * | ||
414 | nouveau_connector_native_mode(struct nouveau_connector *connector) | ||
415 | { | ||
416 | struct drm_device *dev = connector->base.dev; | ||
417 | struct drm_display_mode *mode, *largest = NULL; | ||
418 | int high_w = 0, high_h = 0, high_v = 0; | ||
419 | |||
420 | /* Use preferred mode if there is one.. */ | ||
421 | list_for_each_entry(mode, &connector->base.probed_modes, head) { | ||
422 | if (mode->type & DRM_MODE_TYPE_PREFERRED) { | ||
423 | NV_DEBUG(dev, "native mode from preferred\n"); | ||
424 | return drm_mode_duplicate(dev, mode); | ||
425 | } | ||
426 | } | ||
427 | |||
428 | /* Otherwise, take the resolution with the largest width, then height, | ||
429 | * then vertical refresh | ||
430 | */ | ||
431 | list_for_each_entry(mode, &connector->base.probed_modes, head) { | ||
432 | if (mode->hdisplay < high_w) | ||
433 | continue; | ||
434 | |||
435 | if (mode->hdisplay == high_w && mode->vdisplay < high_h) | ||
436 | continue; | ||
437 | |||
438 | if (mode->hdisplay == high_w && mode->vdisplay == high_h && | ||
439 | mode->vrefresh < high_v) | ||
440 | continue; | ||
441 | |||
442 | high_w = mode->hdisplay; | ||
443 | high_h = mode->vdisplay; | ||
444 | high_v = mode->vrefresh; | ||
445 | largest = mode; | ||
446 | } | ||
447 | |||
448 | NV_DEBUG(dev, "native mode from largest: %dx%d@%d\n", | ||
449 | high_w, high_h, high_v); | ||
450 | return largest ? drm_mode_duplicate(dev, largest) : NULL; | ||
451 | } | ||
452 | |||
453 | struct moderec { | ||
454 | int hdisplay; | ||
455 | int vdisplay; | ||
456 | }; | ||
457 | |||
458 | static struct moderec scaler_modes[] = { | ||
459 | { 1920, 1200 }, | ||
460 | { 1920, 1080 }, | ||
461 | { 1680, 1050 }, | ||
462 | { 1600, 1200 }, | ||
463 | { 1400, 1050 }, | ||
464 | { 1280, 1024 }, | ||
465 | { 1280, 960 }, | ||
466 | { 1152, 864 }, | ||
467 | { 1024, 768 }, | ||
468 | { 800, 600 }, | ||
469 | { 720, 400 }, | ||
470 | { 640, 480 }, | ||
471 | { 640, 400 }, | ||
472 | { 640, 350 }, | ||
473 | {} | ||
474 | }; | ||
475 | |||
476 | static int | ||
477 | nouveau_connector_scaler_modes_add(struct drm_connector *connector) | ||
478 | { | ||
479 | struct nouveau_connector *nv_connector = nouveau_connector(connector); | ||
480 | struct drm_display_mode *native = nv_connector->native_mode, *m; | ||
481 | struct drm_device *dev = connector->dev; | ||
482 | struct moderec *mode = &scaler_modes[0]; | ||
483 | int modes = 0; | ||
484 | |||
485 | if (!native) | ||
486 | return 0; | ||
487 | |||
488 | while (mode->hdisplay) { | ||
489 | if (mode->hdisplay <= native->hdisplay && | ||
490 | mode->vdisplay <= native->vdisplay) { | ||
491 | m = drm_cvt_mode(dev, mode->hdisplay, mode->vdisplay, | ||
492 | drm_mode_vrefresh(native), false, | ||
493 | false, false); | ||
494 | if (!m) | ||
495 | continue; | ||
496 | |||
497 | m->type |= DRM_MODE_TYPE_DRIVER; | ||
498 | |||
499 | drm_mode_probed_add(connector, m); | ||
500 | modes++; | ||
501 | } | ||
502 | |||
503 | mode++; | ||
504 | } | ||
505 | |||
506 | return modes; | ||
507 | } | ||
508 | |||
509 | static int | ||
510 | nouveau_connector_get_modes(struct drm_connector *connector) | ||
511 | { | ||
512 | struct drm_device *dev = connector->dev; | ||
513 | struct nouveau_connector *nv_connector = nouveau_connector(connector); | ||
514 | struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; | ||
515 | int ret = 0; | ||
516 | |||
517 | /* If we're not LVDS, destroy the previous native mode, the attached | ||
518 | * monitor could have changed. | ||
519 | */ | ||
520 | if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS && | ||
521 | nv_connector->native_mode) { | ||
522 | drm_mode_destroy(dev, nv_connector->native_mode); | ||
523 | nv_connector->native_mode = NULL; | ||
524 | } | ||
525 | |||
526 | if (nv_connector->edid) | ||
527 | ret = drm_add_edid_modes(connector, nv_connector->edid); | ||
528 | |||
529 | /* Find the native mode if this is a digital panel, if we didn't | ||
530 | * find any modes through DDC previously add the native mode to | ||
531 | * the list of modes. | ||
532 | */ | ||
533 | if (!nv_connector->native_mode) | ||
534 | nv_connector->native_mode = | ||
535 | nouveau_connector_native_mode(nv_connector); | ||
536 | if (ret == 0 && nv_connector->native_mode) { | ||
537 | struct drm_display_mode *mode; | ||
538 | |||
539 | mode = drm_mode_duplicate(dev, nv_connector->native_mode); | ||
540 | drm_mode_probed_add(connector, mode); | ||
541 | ret = 1; | ||
542 | } | ||
543 | |||
544 | if (nv_encoder->dcb->type == OUTPUT_TV) | ||
545 | ret = get_slave_funcs(nv_encoder)-> | ||
546 | get_modes(to_drm_encoder(nv_encoder), connector); | ||
547 | |||
548 | if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) | ||
549 | ret += nouveau_connector_scaler_modes_add(connector); | ||
550 | |||
551 | return ret; | ||
552 | } | ||
553 | |||
554 | static int | ||
555 | nouveau_connector_mode_valid(struct drm_connector *connector, | ||
556 | struct drm_display_mode *mode) | ||
557 | { | ||
558 | struct drm_nouveau_private *dev_priv = connector->dev->dev_private; | ||
559 | struct nouveau_connector *nv_connector = nouveau_connector(connector); | ||
560 | struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; | ||
561 | unsigned min_clock = 25000, max_clock = min_clock; | ||
562 | unsigned clock = mode->clock; | ||
563 | |||
564 | switch (nv_encoder->dcb->type) { | ||
565 | case OUTPUT_LVDS: | ||
566 | BUG_ON(!nv_connector->native_mode); | ||
567 | if (mode->hdisplay > nv_connector->native_mode->hdisplay || | ||
568 | mode->vdisplay > nv_connector->native_mode->vdisplay) | ||
569 | return MODE_PANEL; | ||
570 | |||
571 | min_clock = 0; | ||
572 | max_clock = 400000; | ||
573 | break; | ||
574 | case OUTPUT_TMDS: | ||
575 | if ((dev_priv->card_type >= NV_50 && !nouveau_duallink) || | ||
576 | (dev_priv->card_type < NV_50 && | ||
577 | !nv_encoder->dcb->duallink_possible)) | ||
578 | max_clock = 165000; | ||
579 | else | ||
580 | max_clock = 330000; | ||
581 | break; | ||
582 | case OUTPUT_ANALOG: | ||
583 | max_clock = nv_encoder->dcb->crtconf.maxfreq; | ||
584 | if (!max_clock) | ||
585 | max_clock = 350000; | ||
586 | break; | ||
587 | case OUTPUT_TV: | ||
588 | return get_slave_funcs(nv_encoder)-> | ||
589 | mode_valid(to_drm_encoder(nv_encoder), mode); | ||
590 | case OUTPUT_DP: | ||
591 | if (nv_encoder->dp.link_bw == DP_LINK_BW_2_7) | ||
592 | max_clock = nv_encoder->dp.link_nr * 270000; | ||
593 | else | ||
594 | max_clock = nv_encoder->dp.link_nr * 162000; | ||
595 | |||
596 | clock *= 3; | ||
597 | break; | ||
598 | } | ||
599 | |||
600 | if (clock < min_clock) | ||
601 | return MODE_CLOCK_LOW; | ||
602 | |||
603 | if (clock > max_clock) | ||
604 | return MODE_CLOCK_HIGH; | ||
605 | |||
606 | return MODE_OK; | ||
607 | } | ||
608 | |||
609 | static struct drm_encoder * | ||
610 | nouveau_connector_best_encoder(struct drm_connector *connector) | ||
611 | { | ||
612 | struct nouveau_connector *nv_connector = nouveau_connector(connector); | ||
613 | |||
614 | if (nv_connector->detected_encoder) | ||
615 | return to_drm_encoder(nv_connector->detected_encoder); | ||
616 | |||
617 | return NULL; | ||
618 | } | ||
619 | |||
620 | static const struct drm_connector_helper_funcs | ||
621 | nouveau_connector_helper_funcs = { | ||
622 | .get_modes = nouveau_connector_get_modes, | ||
623 | .mode_valid = nouveau_connector_mode_valid, | ||
624 | .best_encoder = nouveau_connector_best_encoder, | ||
625 | }; | ||
626 | |||
627 | static const struct drm_connector_funcs | ||
628 | nouveau_connector_funcs = { | ||
629 | .dpms = drm_helper_connector_dpms, | ||
630 | .save = NULL, | ||
631 | .restore = NULL, | ||
632 | .detect = nouveau_connector_detect, | ||
633 | .destroy = nouveau_connector_destroy, | ||
634 | .fill_modes = drm_helper_probe_single_connector_modes, | ||
635 | .set_property = nouveau_connector_set_property, | ||
636 | .force = nouveau_connector_force | ||
637 | }; | ||
638 | |||
639 | static int | ||
640 | nouveau_connector_create_lvds(struct drm_device *dev, | ||
641 | struct drm_connector *connector) | ||
642 | { | ||
643 | struct nouveau_connector *nv_connector = nouveau_connector(connector); | ||
644 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
645 | struct nouveau_i2c_chan *i2c = NULL; | ||
646 | struct nouveau_encoder *nv_encoder; | ||
647 | struct drm_display_mode native, *mode, *temp; | ||
648 | bool dummy, if_is_24bit = false; | ||
649 | int ret, flags; | ||
650 | |||
651 | nv_encoder = find_encoder_by_type(connector, OUTPUT_LVDS); | ||
652 | if (!nv_encoder) | ||
653 | return -ENODEV; | ||
654 | |||
655 | ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &if_is_24bit); | ||
656 | if (ret) { | ||
657 | NV_ERROR(dev, "Error parsing LVDS table, disabling LVDS\n"); | ||
658 | return ret; | ||
659 | } | ||
660 | nv_connector->use_dithering = !if_is_24bit; | ||
661 | |||
662 | /* Firstly try getting EDID over DDC, if allowed and I2C channel | ||
663 | * is available. | ||
664 | */ | ||
665 | if (!dev_priv->VBIOS.pub.fp_no_ddc && nv_encoder->dcb->i2c_index < 0xf) | ||
666 | i2c = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index); | ||
667 | |||
668 | if (i2c) { | ||
669 | nouveau_connector_ddc_prepare(connector, &flags); | ||
670 | nv_connector->edid = drm_get_edid(connector, &i2c->adapter); | ||
671 | nouveau_connector_ddc_finish(connector, flags); | ||
672 | } | ||
673 | |||
674 | /* If no EDID found above, and the VBIOS indicates a hardcoded | ||
675 | * modeline is avalilable for the panel, set it as the panel's | ||
676 | * native mode and exit. | ||
677 | */ | ||
678 | if (!nv_connector->edid && nouveau_bios_fp_mode(dev, &native) && | ||
679 | (nv_encoder->dcb->lvdsconf.use_straps_for_mode || | ||
680 | dev_priv->VBIOS.pub.fp_no_ddc)) { | ||
681 | nv_connector->native_mode = drm_mode_duplicate(dev, &native); | ||
682 | goto out; | ||
683 | } | ||
684 | |||
685 | /* Still nothing, some VBIOS images have a hardcoded EDID block | ||
686 | * stored for the panel stored in them. | ||
687 | */ | ||
688 | if (!nv_connector->edid && !nv_connector->native_mode && | ||
689 | !dev_priv->VBIOS.pub.fp_no_ddc) { | ||
690 | nv_connector->edid = | ||
691 | (struct edid *)nouveau_bios_embedded_edid(dev); | ||
692 | } | ||
693 | |||
694 | if (!nv_connector->edid) | ||
695 | goto out; | ||
696 | |||
697 | /* We didn't find/use a panel mode from the VBIOS, so parse the EDID | ||
698 | * block and look for the preferred mode there. | ||
699 | */ | ||
700 | ret = drm_add_edid_modes(connector, nv_connector->edid); | ||
701 | if (ret == 0) | ||
702 | goto out; | ||
703 | nv_connector->detected_encoder = nv_encoder; | ||
704 | nv_connector->native_mode = nouveau_connector_native_mode(nv_connector); | ||
705 | list_for_each_entry_safe(mode, temp, &connector->probed_modes, head) | ||
706 | drm_mode_remove(connector, mode); | ||
707 | |||
708 | out: | ||
709 | if (!nv_connector->native_mode) { | ||
710 | NV_ERROR(dev, "LVDS present in DCB table, but couldn't " | ||
711 | "determine its native mode. Disabling.\n"); | ||
712 | return -ENODEV; | ||
713 | } | ||
714 | |||
715 | drm_mode_connector_update_edid_property(connector, nv_connector->edid); | ||
716 | return 0; | ||
717 | } | ||
718 | |||
719 | int | ||
720 | nouveau_connector_create(struct drm_device *dev, int index, int type) | ||
721 | { | ||
722 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
723 | struct nouveau_connector *nv_connector = NULL; | ||
724 | struct drm_connector *connector; | ||
725 | struct drm_encoder *encoder; | ||
726 | int ret; | ||
727 | |||
728 | NV_DEBUG(dev, "\n"); | ||
729 | |||
730 | nv_connector = kzalloc(sizeof(*nv_connector), GFP_KERNEL); | ||
731 | if (!nv_connector) | ||
732 | return -ENOMEM; | ||
733 | nv_connector->dcb = nouveau_bios_connector_entry(dev, index); | ||
734 | connector = &nv_connector->base; | ||
735 | |||
736 | switch (type) { | ||
737 | case DRM_MODE_CONNECTOR_VGA: | ||
738 | NV_INFO(dev, "Detected a VGA connector\n"); | ||
739 | break; | ||
740 | case DRM_MODE_CONNECTOR_DVID: | ||
741 | NV_INFO(dev, "Detected a DVI-D connector\n"); | ||
742 | break; | ||
743 | case DRM_MODE_CONNECTOR_DVII: | ||
744 | NV_INFO(dev, "Detected a DVI-I connector\n"); | ||
745 | break; | ||
746 | case DRM_MODE_CONNECTOR_LVDS: | ||
747 | NV_INFO(dev, "Detected a LVDS connector\n"); | ||
748 | break; | ||
749 | case DRM_MODE_CONNECTOR_TV: | ||
750 | NV_INFO(dev, "Detected a TV connector\n"); | ||
751 | break; | ||
752 | case DRM_MODE_CONNECTOR_DisplayPort: | ||
753 | NV_INFO(dev, "Detected a DisplayPort connector\n"); | ||
754 | break; | ||
755 | default: | ||
756 | NV_ERROR(dev, "Unknown connector, this is not good.\n"); | ||
757 | break; | ||
758 | } | ||
759 | |||
760 | /* defaults, will get overridden in detect() */ | ||
761 | connector->interlace_allowed = false; | ||
762 | connector->doublescan_allowed = false; | ||
763 | |||
764 | drm_connector_init(dev, connector, &nouveau_connector_funcs, type); | ||
765 | drm_connector_helper_add(connector, &nouveau_connector_helper_funcs); | ||
766 | |||
767 | /* Init DVI-I specific properties */ | ||
768 | if (type == DRM_MODE_CONNECTOR_DVII) { | ||
769 | drm_mode_create_dvi_i_properties(dev); | ||
770 | drm_connector_attach_property(connector, dev->mode_config.dvi_i_subconnector_property, 0); | ||
771 | drm_connector_attach_property(connector, dev->mode_config.dvi_i_select_subconnector_property, 0); | ||
772 | } | ||
773 | |||
774 | if (type != DRM_MODE_CONNECTOR_LVDS) | ||
775 | nv_connector->use_dithering = false; | ||
776 | |||
777 | if (type == DRM_MODE_CONNECTOR_DVID || | ||
778 | type == DRM_MODE_CONNECTOR_DVII || | ||
779 | type == DRM_MODE_CONNECTOR_LVDS || | ||
780 | type == DRM_MODE_CONNECTOR_DisplayPort) { | ||
781 | nv_connector->scaling_mode = DRM_MODE_SCALE_FULLSCREEN; | ||
782 | |||
783 | drm_connector_attach_property(connector, dev->mode_config.scaling_mode_property, | ||
784 | nv_connector->scaling_mode); | ||
785 | drm_connector_attach_property(connector, dev->mode_config.dithering_mode_property, | ||
786 | nv_connector->use_dithering ? DRM_MODE_DITHERING_ON | ||
787 | : DRM_MODE_DITHERING_OFF); | ||
788 | |||
789 | } else { | ||
790 | nv_connector->scaling_mode = DRM_MODE_SCALE_NONE; | ||
791 | |||
792 | if (type == DRM_MODE_CONNECTOR_VGA && | ||
793 | dev_priv->card_type >= NV_50) { | ||
794 | drm_connector_attach_property(connector, | ||
795 | dev->mode_config.scaling_mode_property, | ||
796 | nv_connector->scaling_mode); | ||
797 | } | ||
798 | } | ||
799 | |||
800 | /* attach encoders */ | ||
801 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | ||
802 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
803 | |||
804 | if (nv_encoder->dcb->connector != index) | ||
805 | continue; | ||
806 | |||
807 | if (get_slave_funcs(nv_encoder)) | ||
808 | get_slave_funcs(nv_encoder)->create_resources(encoder, connector); | ||
809 | |||
810 | drm_mode_connector_attach_encoder(connector, encoder); | ||
811 | } | ||
812 | |||
813 | drm_sysfs_connector_add(connector); | ||
814 | |||
815 | if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) { | ||
816 | ret = nouveau_connector_create_lvds(dev, connector); | ||
817 | if (ret) { | ||
818 | connector->funcs->destroy(connector); | ||
819 | return ret; | ||
820 | } | ||
821 | } | ||
822 | |||
823 | return 0; | ||
824 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h new file mode 100644 index 000000000000..728b8090e5ff --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_connector.h | |||
@@ -0,0 +1,54 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2008 Maarten Maathuis. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #ifndef __NOUVEAU_CONNECTOR_H__ | ||
28 | #define __NOUVEAU_CONNECTOR_H__ | ||
29 | |||
30 | #include "drm_edid.h" | ||
31 | #include "nouveau_i2c.h" | ||
32 | |||
33 | struct nouveau_connector { | ||
34 | struct drm_connector base; | ||
35 | |||
36 | struct dcb_connector_table_entry *dcb; | ||
37 | |||
38 | int scaling_mode; | ||
39 | bool use_dithering; | ||
40 | |||
41 | struct nouveau_encoder *detected_encoder; | ||
42 | struct edid *edid; | ||
43 | struct drm_display_mode *native_mode; | ||
44 | }; | ||
45 | |||
46 | static inline struct nouveau_connector *nouveau_connector( | ||
47 | struct drm_connector *con) | ||
48 | { | ||
49 | return container_of(con, struct nouveau_connector, base); | ||
50 | } | ||
51 | |||
52 | int nouveau_connector_create(struct drm_device *dev, int i2c_index, int type); | ||
53 | |||
54 | #endif /* __NOUVEAU_CONNECTOR_H__ */ | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_crtc.h b/drivers/gpu/drm/nouveau/nouveau_crtc.h new file mode 100644 index 000000000000..49fa7b2d257e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_crtc.h | |||
@@ -0,0 +1,95 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2008 Maarten Maathuis. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #ifndef __NOUVEAU_CRTC_H__ | ||
28 | #define __NOUVEAU_CRTC_H__ | ||
29 | |||
30 | struct nouveau_crtc { | ||
31 | struct drm_crtc base; | ||
32 | |||
33 | int index; | ||
34 | |||
35 | struct drm_display_mode *mode; | ||
36 | |||
37 | uint32_t dpms_saved_fp_control; | ||
38 | uint32_t fp_users; | ||
39 | int saturation; | ||
40 | int sharpness; | ||
41 | int last_dpms; | ||
42 | |||
43 | struct { | ||
44 | int cpp; | ||
45 | bool blanked; | ||
46 | uint32_t offset; | ||
47 | uint32_t tile_flags; | ||
48 | } fb; | ||
49 | |||
50 | struct { | ||
51 | struct nouveau_bo *nvbo; | ||
52 | bool visible; | ||
53 | uint32_t offset; | ||
54 | void (*set_offset)(struct nouveau_crtc *, uint32_t offset); | ||
55 | void (*set_pos)(struct nouveau_crtc *, int x, int y); | ||
56 | void (*hide)(struct nouveau_crtc *, bool update); | ||
57 | void (*show)(struct nouveau_crtc *, bool update); | ||
58 | } cursor; | ||
59 | |||
60 | struct { | ||
61 | struct nouveau_bo *nvbo; | ||
62 | uint16_t r[256]; | ||
63 | uint16_t g[256]; | ||
64 | uint16_t b[256]; | ||
65 | int depth; | ||
66 | } lut; | ||
67 | |||
68 | int (*set_dither)(struct nouveau_crtc *crtc, bool on, bool update); | ||
69 | int (*set_scale)(struct nouveau_crtc *crtc, int mode, bool update); | ||
70 | }; | ||
71 | |||
72 | static inline struct nouveau_crtc *nouveau_crtc(struct drm_crtc *crtc) | ||
73 | { | ||
74 | return container_of(crtc, struct nouveau_crtc, base); | ||
75 | } | ||
76 | |||
77 | static inline struct drm_crtc *to_drm_crtc(struct nouveau_crtc *crtc) | ||
78 | { | ||
79 | return &crtc->base; | ||
80 | } | ||
81 | |||
82 | int nv50_crtc_create(struct drm_device *dev, int index); | ||
83 | int nv50_cursor_init(struct nouveau_crtc *); | ||
84 | void nv50_cursor_fini(struct nouveau_crtc *); | ||
85 | int nv50_crtc_cursor_set(struct drm_crtc *drm_crtc, struct drm_file *file_priv, | ||
86 | uint32_t buffer_handle, uint32_t width, | ||
87 | uint32_t height); | ||
88 | int nv50_crtc_cursor_move(struct drm_crtc *drm_crtc, int x, int y); | ||
89 | |||
90 | int nv04_cursor_init(struct nouveau_crtc *); | ||
91 | |||
92 | struct nouveau_connector * | ||
93 | nouveau_crtc_connector_get(struct nouveau_crtc *crtc); | ||
94 | |||
95 | #endif /* __NOUVEAU_CRTC_H__ */ | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_debugfs.c b/drivers/gpu/drm/nouveau/nouveau_debugfs.c new file mode 100644 index 000000000000..d79db3698f16 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_debugfs.c | |||
@@ -0,0 +1,155 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009 Red Hat <bskeggs@redhat.com> | ||
3 | * | ||
4 | * Permission is hereby granted, free of charge, to any person obtaining | ||
5 | * a copy of this software and associated documentation files (the | ||
6 | * "Software"), to deal in the Software without restriction, including | ||
7 | * without limitation the rights to use, copy, modify, merge, publish, | ||
8 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
9 | * permit persons to whom the Software is furnished to do so, subject to | ||
10 | * the following conditions: | ||
11 | * | ||
12 | * The above copyright notice and this permission notice (including the | ||
13 | * next paragraph) shall be included in all copies or substantial | ||
14 | * portions of the Software. | ||
15 | * | ||
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
17 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
18 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
19 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
20 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
21 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
22 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
23 | * | ||
24 | */ | ||
25 | |||
26 | /* | ||
27 | * Authors: | ||
28 | * Ben Skeggs <bskeggs@redhat.com> | ||
29 | */ | ||
30 | |||
31 | #include <linux/debugfs.h> | ||
32 | |||
33 | #include "drmP.h" | ||
34 | #include "nouveau_drv.h" | ||
35 | |||
36 | static int | ||
37 | nouveau_debugfs_channel_info(struct seq_file *m, void *data) | ||
38 | { | ||
39 | struct drm_info_node *node = (struct drm_info_node *) m->private; | ||
40 | struct nouveau_channel *chan = node->info_ent->data; | ||
41 | |||
42 | seq_printf(m, "channel id : %d\n", chan->id); | ||
43 | |||
44 | seq_printf(m, "cpu fifo state:\n"); | ||
45 | seq_printf(m, " base: 0x%08x\n", chan->pushbuf_base); | ||
46 | seq_printf(m, " max: 0x%08x\n", chan->dma.max << 2); | ||
47 | seq_printf(m, " cur: 0x%08x\n", chan->dma.cur << 2); | ||
48 | seq_printf(m, " put: 0x%08x\n", chan->dma.put << 2); | ||
49 | seq_printf(m, " free: 0x%08x\n", chan->dma.free << 2); | ||
50 | |||
51 | seq_printf(m, "gpu fifo state:\n"); | ||
52 | seq_printf(m, " get: 0x%08x\n", | ||
53 | nvchan_rd32(chan, chan->user_get)); | ||
54 | seq_printf(m, " put: 0x%08x\n", | ||
55 | nvchan_rd32(chan, chan->user_put)); | ||
56 | |||
57 | seq_printf(m, "last fence : %d\n", chan->fence.sequence); | ||
58 | seq_printf(m, "last signalled: %d\n", chan->fence.sequence_ack); | ||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | int | ||
63 | nouveau_debugfs_channel_init(struct nouveau_channel *chan) | ||
64 | { | ||
65 | struct drm_nouveau_private *dev_priv = chan->dev->dev_private; | ||
66 | struct drm_minor *minor = chan->dev->primary; | ||
67 | int ret; | ||
68 | |||
69 | if (!dev_priv->debugfs.channel_root) { | ||
70 | dev_priv->debugfs.channel_root = | ||
71 | debugfs_create_dir("channel", minor->debugfs_root); | ||
72 | if (!dev_priv->debugfs.channel_root) | ||
73 | return -ENOENT; | ||
74 | } | ||
75 | |||
76 | snprintf(chan->debugfs.name, 32, "%d", chan->id); | ||
77 | chan->debugfs.info.name = chan->debugfs.name; | ||
78 | chan->debugfs.info.show = nouveau_debugfs_channel_info; | ||
79 | chan->debugfs.info.driver_features = 0; | ||
80 | chan->debugfs.info.data = chan; | ||
81 | |||
82 | ret = drm_debugfs_create_files(&chan->debugfs.info, 1, | ||
83 | dev_priv->debugfs.channel_root, | ||
84 | chan->dev->primary); | ||
85 | if (ret == 0) | ||
86 | chan->debugfs.active = true; | ||
87 | return ret; | ||
88 | } | ||
89 | |||
90 | void | ||
91 | nouveau_debugfs_channel_fini(struct nouveau_channel *chan) | ||
92 | { | ||
93 | struct drm_nouveau_private *dev_priv = chan->dev->dev_private; | ||
94 | |||
95 | if (!chan->debugfs.active) | ||
96 | return; | ||
97 | |||
98 | drm_debugfs_remove_files(&chan->debugfs.info, 1, chan->dev->primary); | ||
99 | chan->debugfs.active = false; | ||
100 | |||
101 | if (chan == dev_priv->channel) { | ||
102 | debugfs_remove(dev_priv->debugfs.channel_root); | ||
103 | dev_priv->debugfs.channel_root = NULL; | ||
104 | } | ||
105 | } | ||
106 | |||
107 | static int | ||
108 | nouveau_debugfs_chipset_info(struct seq_file *m, void *data) | ||
109 | { | ||
110 | struct drm_info_node *node = (struct drm_info_node *) m->private; | ||
111 | struct drm_minor *minor = node->minor; | ||
112 | struct drm_device *dev = minor->dev; | ||
113 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
114 | uint32_t ppci_0; | ||
115 | |||
116 | ppci_0 = nv_rd32(dev, dev_priv->chipset >= 0x40 ? 0x88000 : 0x1800); | ||
117 | |||
118 | seq_printf(m, "PMC_BOOT_0: 0x%08x\n", nv_rd32(dev, NV03_PMC_BOOT_0)); | ||
119 | seq_printf(m, "PCI ID : 0x%04x:0x%04x\n", | ||
120 | ppci_0 & 0xffff, ppci_0 >> 16); | ||
121 | return 0; | ||
122 | } | ||
123 | |||
124 | static int | ||
125 | nouveau_debugfs_memory_info(struct seq_file *m, void *data) | ||
126 | { | ||
127 | struct drm_info_node *node = (struct drm_info_node *) m->private; | ||
128 | struct drm_minor *minor = node->minor; | ||
129 | struct drm_device *dev = minor->dev; | ||
130 | |||
131 | seq_printf(m, "VRAM total: %dKiB\n", | ||
132 | (int)(nouveau_mem_fb_amount(dev) >> 10)); | ||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | static struct drm_info_list nouveau_debugfs_list[] = { | ||
137 | { "chipset", nouveau_debugfs_chipset_info, 0, NULL }, | ||
138 | { "memory", nouveau_debugfs_memory_info, 0, NULL }, | ||
139 | }; | ||
140 | #define NOUVEAU_DEBUGFS_ENTRIES ARRAY_SIZE(nouveau_debugfs_list) | ||
141 | |||
142 | int | ||
143 | nouveau_debugfs_init(struct drm_minor *minor) | ||
144 | { | ||
145 | drm_debugfs_create_files(nouveau_debugfs_list, NOUVEAU_DEBUGFS_ENTRIES, | ||
146 | minor->debugfs_root, minor); | ||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | void | ||
151 | nouveau_debugfs_takedown(struct drm_minor *minor) | ||
152 | { | ||
153 | drm_debugfs_remove_files(nouveau_debugfs_list, NOUVEAU_DEBUGFS_ENTRIES, | ||
154 | minor); | ||
155 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c new file mode 100644 index 000000000000..dfc94391d71e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_display.c | |||
@@ -0,0 +1,115 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2008 Maarten Maathuis. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include "drmP.h" | ||
28 | #include "drm_crtc_helper.h" | ||
29 | #include "nouveau_drv.h" | ||
30 | #include "nouveau_fb.h" | ||
31 | #include "nouveau_fbcon.h" | ||
32 | |||
33 | static void | ||
34 | nouveau_user_framebuffer_destroy(struct drm_framebuffer *drm_fb) | ||
35 | { | ||
36 | struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb); | ||
37 | struct drm_device *dev = drm_fb->dev; | ||
38 | |||
39 | if (drm_fb->fbdev) | ||
40 | nouveau_fbcon_remove(dev, drm_fb); | ||
41 | |||
42 | if (fb->nvbo) { | ||
43 | mutex_lock(&dev->struct_mutex); | ||
44 | drm_gem_object_unreference(fb->nvbo->gem); | ||
45 | mutex_unlock(&dev->struct_mutex); | ||
46 | } | ||
47 | |||
48 | drm_framebuffer_cleanup(drm_fb); | ||
49 | kfree(fb); | ||
50 | } | ||
51 | |||
52 | static int | ||
53 | nouveau_user_framebuffer_create_handle(struct drm_framebuffer *drm_fb, | ||
54 | struct drm_file *file_priv, | ||
55 | unsigned int *handle) | ||
56 | { | ||
57 | struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb); | ||
58 | |||
59 | return drm_gem_handle_create(file_priv, fb->nvbo->gem, handle); | ||
60 | } | ||
61 | |||
62 | static const struct drm_framebuffer_funcs nouveau_framebuffer_funcs = { | ||
63 | .destroy = nouveau_user_framebuffer_destroy, | ||
64 | .create_handle = nouveau_user_framebuffer_create_handle, | ||
65 | }; | ||
66 | |||
67 | struct drm_framebuffer * | ||
68 | nouveau_framebuffer_create(struct drm_device *dev, struct nouveau_bo *nvbo, | ||
69 | struct drm_mode_fb_cmd *mode_cmd) | ||
70 | { | ||
71 | struct nouveau_framebuffer *fb; | ||
72 | int ret; | ||
73 | |||
74 | fb = kzalloc(sizeof(struct nouveau_framebuffer), GFP_KERNEL); | ||
75 | if (!fb) | ||
76 | return NULL; | ||
77 | |||
78 | ret = drm_framebuffer_init(dev, &fb->base, &nouveau_framebuffer_funcs); | ||
79 | if (ret) { | ||
80 | kfree(fb); | ||
81 | return NULL; | ||
82 | } | ||
83 | |||
84 | drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd); | ||
85 | |||
86 | fb->nvbo = nvbo; | ||
87 | return &fb->base; | ||
88 | } | ||
89 | |||
90 | static struct drm_framebuffer * | ||
91 | nouveau_user_framebuffer_create(struct drm_device *dev, | ||
92 | struct drm_file *file_priv, | ||
93 | struct drm_mode_fb_cmd *mode_cmd) | ||
94 | { | ||
95 | struct drm_framebuffer *fb; | ||
96 | struct drm_gem_object *gem; | ||
97 | |||
98 | gem = drm_gem_object_lookup(dev, file_priv, mode_cmd->handle); | ||
99 | if (!gem) | ||
100 | return NULL; | ||
101 | |||
102 | fb = nouveau_framebuffer_create(dev, nouveau_gem_object(gem), mode_cmd); | ||
103 | if (!fb) { | ||
104 | drm_gem_object_unreference(gem); | ||
105 | return NULL; | ||
106 | } | ||
107 | |||
108 | return fb; | ||
109 | } | ||
110 | |||
111 | const struct drm_mode_config_funcs nouveau_mode_config_funcs = { | ||
112 | .fb_create = nouveau_user_framebuffer_create, | ||
113 | .fb_changed = nouveau_fbcon_probe, | ||
114 | }; | ||
115 | |||
diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.c b/drivers/gpu/drm/nouveau/nouveau_dma.c new file mode 100644 index 000000000000..703553687b20 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_dma.c | |||
@@ -0,0 +1,206 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007 Ben Skeggs. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include "drmP.h" | ||
28 | #include "drm.h" | ||
29 | #include "nouveau_drv.h" | ||
30 | #include "nouveau_dma.h" | ||
31 | |||
32 | int | ||
33 | nouveau_dma_init(struct nouveau_channel *chan) | ||
34 | { | ||
35 | struct drm_device *dev = chan->dev; | ||
36 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
37 | struct nouveau_gpuobj *m2mf = NULL; | ||
38 | int ret, i; | ||
39 | |||
40 | /* Create NV_MEMORY_TO_MEMORY_FORMAT for buffer moves */ | ||
41 | ret = nouveau_gpuobj_gr_new(chan, dev_priv->card_type < NV_50 ? | ||
42 | 0x0039 : 0x5039, &m2mf); | ||
43 | if (ret) | ||
44 | return ret; | ||
45 | |||
46 | ret = nouveau_gpuobj_ref_add(dev, chan, NvM2MF, m2mf, NULL); | ||
47 | if (ret) | ||
48 | return ret; | ||
49 | |||
50 | /* NV_MEMORY_TO_MEMORY_FORMAT requires a notifier object */ | ||
51 | ret = nouveau_notifier_alloc(chan, NvNotify0, 32, &chan->m2mf_ntfy); | ||
52 | if (ret) | ||
53 | return ret; | ||
54 | |||
55 | /* Map push buffer */ | ||
56 | ret = nouveau_bo_map(chan->pushbuf_bo); | ||
57 | if (ret) | ||
58 | return ret; | ||
59 | |||
60 | /* Map M2MF notifier object - fbcon. */ | ||
61 | if (drm_core_check_feature(dev, DRIVER_MODESET)) { | ||
62 | ret = nouveau_bo_map(chan->notifier_bo); | ||
63 | if (ret) | ||
64 | return ret; | ||
65 | } | ||
66 | |||
67 | /* Initialise DMA vars */ | ||
68 | chan->dma.max = (chan->pushbuf_bo->bo.mem.size >> 2) - 2; | ||
69 | chan->dma.put = 0; | ||
70 | chan->dma.cur = chan->dma.put; | ||
71 | chan->dma.free = chan->dma.max - chan->dma.cur; | ||
72 | |||
73 | /* Insert NOPS for NOUVEAU_DMA_SKIPS */ | ||
74 | ret = RING_SPACE(chan, NOUVEAU_DMA_SKIPS); | ||
75 | if (ret) | ||
76 | return ret; | ||
77 | |||
78 | for (i = 0; i < NOUVEAU_DMA_SKIPS; i++) | ||
79 | OUT_RING(chan, 0); | ||
80 | |||
81 | /* Initialise NV_MEMORY_TO_MEMORY_FORMAT */ | ||
82 | ret = RING_SPACE(chan, 4); | ||
83 | if (ret) | ||
84 | return ret; | ||
85 | BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_NAME, 1); | ||
86 | OUT_RING(chan, NvM2MF); | ||
87 | BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_DMA_NOTIFY, 1); | ||
88 | OUT_RING(chan, NvNotify0); | ||
89 | |||
90 | /* Sit back and pray the channel works.. */ | ||
91 | FIRE_RING(chan); | ||
92 | |||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | void | ||
97 | OUT_RINGp(struct nouveau_channel *chan, const void *data, unsigned nr_dwords) | ||
98 | { | ||
99 | bool is_iomem; | ||
100 | u32 *mem = ttm_kmap_obj_virtual(&chan->pushbuf_bo->kmap, &is_iomem); | ||
101 | mem = &mem[chan->dma.cur]; | ||
102 | if (is_iomem) | ||
103 | memcpy_toio((void __force __iomem *)mem, data, nr_dwords * 4); | ||
104 | else | ||
105 | memcpy(mem, data, nr_dwords * 4); | ||
106 | chan->dma.cur += nr_dwords; | ||
107 | } | ||
108 | |||
109 | static inline bool | ||
110 | READ_GET(struct nouveau_channel *chan, uint32_t *get) | ||
111 | { | ||
112 | uint32_t val; | ||
113 | |||
114 | val = nvchan_rd32(chan, chan->user_get); | ||
115 | if (val < chan->pushbuf_base || | ||
116 | val >= chan->pushbuf_base + chan->pushbuf_bo->bo.mem.size) { | ||
117 | /* meaningless to dma_wait() except to know whether the | ||
118 | * GPU has stalled or not | ||
119 | */ | ||
120 | *get = val; | ||
121 | return false; | ||
122 | } | ||
123 | |||
124 | *get = (val - chan->pushbuf_base) >> 2; | ||
125 | return true; | ||
126 | } | ||
127 | |||
128 | int | ||
129 | nouveau_dma_wait(struct nouveau_channel *chan, int size) | ||
130 | { | ||
131 | uint32_t get, prev_get = 0, cnt = 0; | ||
132 | bool get_valid; | ||
133 | |||
134 | while (chan->dma.free < size) { | ||
135 | /* reset counter as long as GET is still advancing, this is | ||
136 | * to avoid misdetecting a GPU lockup if the GPU happens to | ||
137 | * just be processing an operation that takes a long time | ||
138 | */ | ||
139 | get_valid = READ_GET(chan, &get); | ||
140 | if (get != prev_get) { | ||
141 | prev_get = get; | ||
142 | cnt = 0; | ||
143 | } | ||
144 | |||
145 | if ((++cnt & 0xff) == 0) { | ||
146 | DRM_UDELAY(1); | ||
147 | if (cnt > 100000) | ||
148 | return -EBUSY; | ||
149 | } | ||
150 | |||
151 | /* loop until we have a usable GET pointer. the value | ||
152 | * we read from the GPU may be outside the main ring if | ||
153 | * PFIFO is processing a buffer called from the main ring, | ||
154 | * discard these values until something sensible is seen. | ||
155 | * | ||
156 | * the other case we discard GET is while the GPU is fetching | ||
157 | * from the SKIPS area, so the code below doesn't have to deal | ||
158 | * with some fun corner cases. | ||
159 | */ | ||
160 | if (!get_valid || get < NOUVEAU_DMA_SKIPS) | ||
161 | continue; | ||
162 | |||
163 | if (get <= chan->dma.cur) { | ||
164 | /* engine is fetching behind us, or is completely | ||
165 | * idle (GET == PUT) so we have free space up until | ||
166 | * the end of the push buffer | ||
167 | * | ||
168 | * we can only hit that path once per call due to | ||
169 | * looping back to the beginning of the push buffer, | ||
170 | * we'll hit the fetching-ahead-of-us path from that | ||
171 | * point on. | ||
172 | * | ||
173 | * the *one* exception to that rule is if we read | ||
174 | * GET==PUT, in which case the below conditional will | ||
175 | * always succeed and break us out of the wait loop. | ||
176 | */ | ||
177 | chan->dma.free = chan->dma.max - chan->dma.cur; | ||
178 | if (chan->dma.free >= size) | ||
179 | break; | ||
180 | |||
181 | /* not enough space left at the end of the push buffer, | ||
182 | * instruct the GPU to jump back to the start right | ||
183 | * after processing the currently pending commands. | ||
184 | */ | ||
185 | OUT_RING(chan, chan->pushbuf_base | 0x20000000); | ||
186 | WRITE_PUT(NOUVEAU_DMA_SKIPS); | ||
187 | |||
188 | /* we're now submitting commands at the start of | ||
189 | * the push buffer. | ||
190 | */ | ||
191 | chan->dma.cur = | ||
192 | chan->dma.put = NOUVEAU_DMA_SKIPS; | ||
193 | } | ||
194 | |||
195 | /* engine fetching ahead of us, we have space up until the | ||
196 | * current GET pointer. the "- 1" is to ensure there's | ||
197 | * space left to emit a jump back to the beginning of the | ||
198 | * push buffer if we require it. we can never get GET == PUT | ||
199 | * here, so this is safe. | ||
200 | */ | ||
201 | chan->dma.free = get - chan->dma.cur - 1; | ||
202 | } | ||
203 | |||
204 | return 0; | ||
205 | } | ||
206 | |||
diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.h b/drivers/gpu/drm/nouveau/nouveau_dma.h new file mode 100644 index 000000000000..04e85d8f757e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_dma.h | |||
@@ -0,0 +1,157 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007 Ben Skeggs. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #ifndef __NOUVEAU_DMA_H__ | ||
28 | #define __NOUVEAU_DMA_H__ | ||
29 | |||
30 | #ifndef NOUVEAU_DMA_DEBUG | ||
31 | #define NOUVEAU_DMA_DEBUG 0 | ||
32 | #endif | ||
33 | |||
34 | /* | ||
35 | * There's a hw race condition where you can't jump to your PUT offset, | ||
36 | * to avoid this we jump to offset + SKIPS and fill the difference with | ||
37 | * NOPs. | ||
38 | * | ||
39 | * xf86-video-nv configures the DMA fetch size to 32 bytes, and uses | ||
40 | * a SKIPS value of 8. Lets assume that the race condition is to do | ||
41 | * with writing into the fetch area, we configure a fetch size of 128 | ||
42 | * bytes so we need a larger SKIPS value. | ||
43 | */ | ||
44 | #define NOUVEAU_DMA_SKIPS (128 / 4) | ||
45 | |||
46 | /* Hardcoded object assignments to subchannels (subchannel id). */ | ||
47 | enum { | ||
48 | NvSubM2MF = 0, | ||
49 | NvSub2D = 1, | ||
50 | NvSubCtxSurf2D = 1, | ||
51 | NvSubGdiRect = 2, | ||
52 | NvSubImageBlit = 3 | ||
53 | }; | ||
54 | |||
55 | /* Object handles. */ | ||
56 | enum { | ||
57 | NvM2MF = 0x80000001, | ||
58 | NvDmaFB = 0x80000002, | ||
59 | NvDmaTT = 0x80000003, | ||
60 | NvDmaVRAM = 0x80000004, | ||
61 | NvDmaGART = 0x80000005, | ||
62 | NvNotify0 = 0x80000006, | ||
63 | Nv2D = 0x80000007, | ||
64 | NvCtxSurf2D = 0x80000008, | ||
65 | NvRop = 0x80000009, | ||
66 | NvImagePatt = 0x8000000a, | ||
67 | NvClipRect = 0x8000000b, | ||
68 | NvGdiRect = 0x8000000c, | ||
69 | NvImageBlit = 0x8000000d, | ||
70 | |||
71 | /* G80+ display objects */ | ||
72 | NvEvoVRAM = 0x01000000, | ||
73 | NvEvoFB16 = 0x01000001, | ||
74 | NvEvoFB32 = 0x01000002 | ||
75 | }; | ||
76 | |||
77 | #define NV_MEMORY_TO_MEMORY_FORMAT 0x00000039 | ||
78 | #define NV_MEMORY_TO_MEMORY_FORMAT_NAME 0x00000000 | ||
79 | #define NV_MEMORY_TO_MEMORY_FORMAT_SET_REF 0x00000050 | ||
80 | #define NV_MEMORY_TO_MEMORY_FORMAT_NOP 0x00000100 | ||
81 | #define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY 0x00000104 | ||
82 | #define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY_STYLE_WRITE 0x00000000 | ||
83 | #define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY_STYLE_WRITE_LE_AWAKEN 0x00000001 | ||
84 | #define NV_MEMORY_TO_MEMORY_FORMAT_DMA_NOTIFY 0x00000180 | ||
85 | #define NV_MEMORY_TO_MEMORY_FORMAT_DMA_SOURCE 0x00000184 | ||
86 | #define NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN 0x0000030c | ||
87 | |||
88 | #define NV50_MEMORY_TO_MEMORY_FORMAT 0x00005039 | ||
89 | #define NV50_MEMORY_TO_MEMORY_FORMAT_UNK200 0x00000200 | ||
90 | #define NV50_MEMORY_TO_MEMORY_FORMAT_UNK21C 0x0000021c | ||
91 | #define NV50_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN_HIGH 0x00000238 | ||
92 | #define NV50_MEMORY_TO_MEMORY_FORMAT_OFFSET_OUT_HIGH 0x0000023c | ||
93 | |||
94 | static __must_check inline int | ||
95 | RING_SPACE(struct nouveau_channel *chan, int size) | ||
96 | { | ||
97 | if (chan->dma.free < size) { | ||
98 | int ret; | ||
99 | |||
100 | ret = nouveau_dma_wait(chan, size); | ||
101 | if (ret) | ||
102 | return ret; | ||
103 | } | ||
104 | |||
105 | chan->dma.free -= size; | ||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | static inline void | ||
110 | OUT_RING(struct nouveau_channel *chan, int data) | ||
111 | { | ||
112 | if (NOUVEAU_DMA_DEBUG) { | ||
113 | NV_INFO(chan->dev, "Ch%d/0x%08x: 0x%08x\n", | ||
114 | chan->id, chan->dma.cur << 2, data); | ||
115 | } | ||
116 | |||
117 | nouveau_bo_wr32(chan->pushbuf_bo, chan->dma.cur++, data); | ||
118 | } | ||
119 | |||
120 | extern void | ||
121 | OUT_RINGp(struct nouveau_channel *chan, const void *data, unsigned nr_dwords); | ||
122 | |||
123 | static inline void | ||
124 | BEGIN_RING(struct nouveau_channel *chan, int subc, int mthd, int size) | ||
125 | { | ||
126 | OUT_RING(chan, (subc << 13) | (size << 18) | mthd); | ||
127 | } | ||
128 | |||
129 | #define WRITE_PUT(val) do { \ | ||
130 | DRM_MEMORYBARRIER(); \ | ||
131 | nouveau_bo_rd32(chan->pushbuf_bo, 0); \ | ||
132 | nvchan_wr32(chan, chan->user_put, ((val) << 2) + chan->pushbuf_base); \ | ||
133 | } while (0) | ||
134 | |||
135 | static inline void | ||
136 | FIRE_RING(struct nouveau_channel *chan) | ||
137 | { | ||
138 | if (NOUVEAU_DMA_DEBUG) { | ||
139 | NV_INFO(chan->dev, "Ch%d/0x%08x: PUSH!\n", | ||
140 | chan->id, chan->dma.cur << 2); | ||
141 | } | ||
142 | |||
143 | if (chan->dma.cur == chan->dma.put) | ||
144 | return; | ||
145 | chan->accel_done = true; | ||
146 | |||
147 | WRITE_PUT(chan->dma.cur); | ||
148 | chan->dma.put = chan->dma.cur; | ||
149 | } | ||
150 | |||
151 | static inline void | ||
152 | WIND_RING(struct nouveau_channel *chan) | ||
153 | { | ||
154 | chan->dma.cur = chan->dma.put; | ||
155 | } | ||
156 | |||
157 | #endif | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c new file mode 100644 index 000000000000..de61f4640e12 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c | |||
@@ -0,0 +1,569 @@ | |||
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 | * Authors: Ben Skeggs | ||
23 | */ | ||
24 | |||
25 | #include "drmP.h" | ||
26 | #include "nouveau_drv.h" | ||
27 | #include "nouveau_i2c.h" | ||
28 | #include "nouveau_encoder.h" | ||
29 | |||
30 | static int | ||
31 | auxch_rd(struct drm_encoder *encoder, int address, uint8_t *buf, int size) | ||
32 | { | ||
33 | struct drm_device *dev = encoder->dev; | ||
34 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
35 | struct nouveau_i2c_chan *auxch; | ||
36 | int ret; | ||
37 | |||
38 | auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index); | ||
39 | if (!auxch) | ||
40 | return -ENODEV; | ||
41 | |||
42 | ret = nouveau_dp_auxch(auxch, 9, address, buf, size); | ||
43 | if (ret) | ||
44 | return ret; | ||
45 | |||
46 | return 0; | ||
47 | } | ||
48 | |||
49 | static int | ||
50 | auxch_wr(struct drm_encoder *encoder, int address, uint8_t *buf, int size) | ||
51 | { | ||
52 | struct drm_device *dev = encoder->dev; | ||
53 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
54 | struct nouveau_i2c_chan *auxch; | ||
55 | int ret; | ||
56 | |||
57 | auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index); | ||
58 | if (!auxch) | ||
59 | return -ENODEV; | ||
60 | |||
61 | ret = nouveau_dp_auxch(auxch, 8, address, buf, size); | ||
62 | return ret; | ||
63 | } | ||
64 | |||
65 | static int | ||
66 | nouveau_dp_lane_count_set(struct drm_encoder *encoder, uint8_t cmd) | ||
67 | { | ||
68 | struct drm_device *dev = encoder->dev; | ||
69 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
70 | uint32_t tmp; | ||
71 | int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1); | ||
72 | |||
73 | tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)); | ||
74 | tmp &= ~(NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED | | ||
75 | NV50_SOR_DP_CTRL_LANE_MASK); | ||
76 | tmp |= ((1 << (cmd & DP_LANE_COUNT_MASK)) - 1) << 16; | ||
77 | if (cmd & DP_LANE_COUNT_ENHANCED_FRAME_EN) | ||
78 | tmp |= NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED; | ||
79 | nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp); | ||
80 | |||
81 | return auxch_wr(encoder, DP_LANE_COUNT_SET, &cmd, 1); | ||
82 | } | ||
83 | |||
84 | static int | ||
85 | nouveau_dp_link_bw_set(struct drm_encoder *encoder, uint8_t cmd) | ||
86 | { | ||
87 | struct drm_device *dev = encoder->dev; | ||
88 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
89 | uint32_t tmp; | ||
90 | int reg = 0x614300 + (nv_encoder->or * 0x800); | ||
91 | |||
92 | tmp = nv_rd32(dev, reg); | ||
93 | tmp &= 0xfff3ffff; | ||
94 | if (cmd == DP_LINK_BW_2_7) | ||
95 | tmp |= 0x00040000; | ||
96 | nv_wr32(dev, reg, tmp); | ||
97 | |||
98 | return auxch_wr(encoder, DP_LINK_BW_SET, &cmd, 1); | ||
99 | } | ||
100 | |||
101 | static int | ||
102 | nouveau_dp_link_train_set(struct drm_encoder *encoder, int pattern) | ||
103 | { | ||
104 | struct drm_device *dev = encoder->dev; | ||
105 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
106 | uint32_t tmp; | ||
107 | uint8_t cmd; | ||
108 | int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1); | ||
109 | int ret; | ||
110 | |||
111 | tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)); | ||
112 | tmp &= ~NV50_SOR_DP_CTRL_TRAINING_PATTERN; | ||
113 | tmp |= (pattern << 24); | ||
114 | nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp); | ||
115 | |||
116 | ret = auxch_rd(encoder, DP_TRAINING_PATTERN_SET, &cmd, 1); | ||
117 | if (ret) | ||
118 | return ret; | ||
119 | cmd &= ~DP_TRAINING_PATTERN_MASK; | ||
120 | cmd |= (pattern & DP_TRAINING_PATTERN_MASK); | ||
121 | return auxch_wr(encoder, DP_TRAINING_PATTERN_SET, &cmd, 1); | ||
122 | } | ||
123 | |||
124 | static int | ||
125 | nouveau_dp_max_voltage_swing(struct drm_encoder *encoder) | ||
126 | { | ||
127 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
128 | struct drm_device *dev = encoder->dev; | ||
129 | struct bit_displayport_encoder_table_entry *dpse; | ||
130 | struct bit_displayport_encoder_table *dpe; | ||
131 | int i, dpe_headerlen, max_vs = 0; | ||
132 | |||
133 | dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen); | ||
134 | if (!dpe) | ||
135 | return false; | ||
136 | dpse = (void *)((char *)dpe + dpe_headerlen); | ||
137 | |||
138 | for (i = 0; i < dpe_headerlen; i++, dpse++) { | ||
139 | if (dpse->vs_level > max_vs) | ||
140 | max_vs = dpse->vs_level; | ||
141 | } | ||
142 | |||
143 | return max_vs; | ||
144 | } | ||
145 | |||
146 | static int | ||
147 | nouveau_dp_max_pre_emphasis(struct drm_encoder *encoder, int vs) | ||
148 | { | ||
149 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
150 | struct drm_device *dev = encoder->dev; | ||
151 | struct bit_displayport_encoder_table_entry *dpse; | ||
152 | struct bit_displayport_encoder_table *dpe; | ||
153 | int i, dpe_headerlen, max_pre = 0; | ||
154 | |||
155 | dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen); | ||
156 | if (!dpe) | ||
157 | return false; | ||
158 | dpse = (void *)((char *)dpe + dpe_headerlen); | ||
159 | |||
160 | for (i = 0; i < dpe_headerlen; i++, dpse++) { | ||
161 | if (dpse->vs_level != vs) | ||
162 | continue; | ||
163 | |||
164 | if (dpse->pre_level > max_pre) | ||
165 | max_pre = dpse->pre_level; | ||
166 | } | ||
167 | |||
168 | return max_pre; | ||
169 | } | ||
170 | |||
171 | static bool | ||
172 | nouveau_dp_link_train_adjust(struct drm_encoder *encoder, uint8_t *config) | ||
173 | { | ||
174 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
175 | struct drm_device *dev = encoder->dev; | ||
176 | struct bit_displayport_encoder_table_entry *dpse; | ||
177 | struct bit_displayport_encoder_table *dpe; | ||
178 | int ret, i, dpe_headerlen, vs = 0, pre = 0; | ||
179 | uint8_t request[2]; | ||
180 | |||
181 | dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen); | ||
182 | if (!dpe) | ||
183 | return false; | ||
184 | dpse = (void *)((char *)dpe + dpe_headerlen); | ||
185 | |||
186 | ret = auxch_rd(encoder, DP_ADJUST_REQUEST_LANE0_1, request, 2); | ||
187 | if (ret) | ||
188 | return false; | ||
189 | |||
190 | NV_DEBUG(dev, "\t\tadjust 0x%02x 0x%02x\n", request[0], request[1]); | ||
191 | |||
192 | /* Keep all lanes at the same level.. */ | ||
193 | for (i = 0; i < nv_encoder->dp.link_nr; i++) { | ||
194 | int lane_req = (request[i >> 1] >> ((i & 1) << 2)) & 0xf; | ||
195 | int lane_vs = lane_req & 3; | ||
196 | int lane_pre = (lane_req >> 2) & 3; | ||
197 | |||
198 | if (lane_vs > vs) | ||
199 | vs = lane_vs; | ||
200 | if (lane_pre > pre) | ||
201 | pre = lane_pre; | ||
202 | } | ||
203 | |||
204 | if (vs >= nouveau_dp_max_voltage_swing(encoder)) { | ||
205 | vs = nouveau_dp_max_voltage_swing(encoder); | ||
206 | vs |= 4; | ||
207 | } | ||
208 | |||
209 | if (pre >= nouveau_dp_max_pre_emphasis(encoder, vs & 3)) { | ||
210 | pre = nouveau_dp_max_pre_emphasis(encoder, vs & 3); | ||
211 | pre |= 4; | ||
212 | } | ||
213 | |||
214 | /* Update the configuration for all lanes.. */ | ||
215 | for (i = 0; i < nv_encoder->dp.link_nr; i++) | ||
216 | config[i] = (pre << 3) | vs; | ||
217 | |||
218 | return true; | ||
219 | } | ||
220 | |||
221 | static bool | ||
222 | nouveau_dp_link_train_commit(struct drm_encoder *encoder, uint8_t *config) | ||
223 | { | ||
224 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
225 | struct drm_device *dev = encoder->dev; | ||
226 | struct bit_displayport_encoder_table_entry *dpse; | ||
227 | struct bit_displayport_encoder_table *dpe; | ||
228 | int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1); | ||
229 | int dpe_headerlen, ret, i; | ||
230 | |||
231 | NV_DEBUG(dev, "\t\tconfig 0x%02x 0x%02x 0x%02x 0x%02x\n", | ||
232 | config[0], config[1], config[2], config[3]); | ||
233 | |||
234 | dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen); | ||
235 | if (!dpe) | ||
236 | return false; | ||
237 | dpse = (void *)((char *)dpe + dpe_headerlen); | ||
238 | |||
239 | for (i = 0; i < dpe->record_nr; i++, dpse++) { | ||
240 | if (dpse->vs_level == (config[0] & 3) && | ||
241 | dpse->pre_level == ((config[0] >> 3) & 3)) | ||
242 | break; | ||
243 | } | ||
244 | BUG_ON(i == dpe->record_nr); | ||
245 | |||
246 | for (i = 0; i < nv_encoder->dp.link_nr; i++) { | ||
247 | const int shift[4] = { 16, 8, 0, 24 }; | ||
248 | uint32_t mask = 0xff << shift[i]; | ||
249 | uint32_t reg0, reg1, reg2; | ||
250 | |||
251 | reg0 = nv_rd32(dev, NV50_SOR_DP_UNK118(or, link)) & ~mask; | ||
252 | reg0 |= (dpse->reg0 << shift[i]); | ||
253 | reg1 = nv_rd32(dev, NV50_SOR_DP_UNK120(or, link)) & ~mask; | ||
254 | reg1 |= (dpse->reg1 << shift[i]); | ||
255 | reg2 = nv_rd32(dev, NV50_SOR_DP_UNK130(or, link)) & 0xffff00ff; | ||
256 | reg2 |= (dpse->reg2 << 8); | ||
257 | nv_wr32(dev, NV50_SOR_DP_UNK118(or, link), reg0); | ||
258 | nv_wr32(dev, NV50_SOR_DP_UNK120(or, link), reg1); | ||
259 | nv_wr32(dev, NV50_SOR_DP_UNK130(or, link), reg2); | ||
260 | } | ||
261 | |||
262 | ret = auxch_wr(encoder, DP_TRAINING_LANE0_SET, config, 4); | ||
263 | if (ret) | ||
264 | return false; | ||
265 | |||
266 | return true; | ||
267 | } | ||
268 | |||
269 | bool | ||
270 | nouveau_dp_link_train(struct drm_encoder *encoder) | ||
271 | { | ||
272 | struct drm_device *dev = encoder->dev; | ||
273 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
274 | uint8_t config[4]; | ||
275 | uint8_t status[3]; | ||
276 | bool cr_done, cr_max_vs, eq_done; | ||
277 | int ret = 0, i, tries, voltage; | ||
278 | |||
279 | NV_DEBUG(dev, "link training!!\n"); | ||
280 | train: | ||
281 | cr_done = eq_done = false; | ||
282 | |||
283 | /* set link configuration */ | ||
284 | NV_DEBUG(dev, "\tbegin train: bw %d, lanes %d\n", | ||
285 | nv_encoder->dp.link_bw, nv_encoder->dp.link_nr); | ||
286 | |||
287 | ret = nouveau_dp_link_bw_set(encoder, nv_encoder->dp.link_bw); | ||
288 | if (ret) | ||
289 | return false; | ||
290 | |||
291 | config[0] = nv_encoder->dp.link_nr; | ||
292 | if (nv_encoder->dp.dpcd_version >= 0x11) | ||
293 | config[0] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; | ||
294 | |||
295 | ret = nouveau_dp_lane_count_set(encoder, config[0]); | ||
296 | if (ret) | ||
297 | return false; | ||
298 | |||
299 | /* clock recovery */ | ||
300 | NV_DEBUG(dev, "\tbegin cr\n"); | ||
301 | ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_1); | ||
302 | if (ret) | ||
303 | goto stop; | ||
304 | |||
305 | tries = 0; | ||
306 | voltage = -1; | ||
307 | memset(config, 0x00, sizeof(config)); | ||
308 | for (;;) { | ||
309 | if (!nouveau_dp_link_train_commit(encoder, config)) | ||
310 | break; | ||
311 | |||
312 | udelay(100); | ||
313 | |||
314 | ret = auxch_rd(encoder, DP_LANE0_1_STATUS, status, 2); | ||
315 | if (ret) | ||
316 | break; | ||
317 | NV_DEBUG(dev, "\t\tstatus: 0x%02x 0x%02x\n", | ||
318 | status[0], status[1]); | ||
319 | |||
320 | cr_done = true; | ||
321 | cr_max_vs = false; | ||
322 | for (i = 0; i < nv_encoder->dp.link_nr; i++) { | ||
323 | int lane = (status[i >> 1] >> ((i & 1) * 4)) & 0xf; | ||
324 | |||
325 | if (!(lane & DP_LANE_CR_DONE)) { | ||
326 | cr_done = false; | ||
327 | if (config[i] & DP_TRAIN_MAX_PRE_EMPHASIS_REACHED) | ||
328 | cr_max_vs = true; | ||
329 | break; | ||
330 | } | ||
331 | } | ||
332 | |||
333 | if ((config[0] & DP_TRAIN_VOLTAGE_SWING_MASK) != voltage) { | ||
334 | voltage = config[0] & DP_TRAIN_VOLTAGE_SWING_MASK; | ||
335 | tries = 0; | ||
336 | } | ||
337 | |||
338 | if (cr_done || cr_max_vs || (++tries == 5)) | ||
339 | break; | ||
340 | |||
341 | if (!nouveau_dp_link_train_adjust(encoder, config)) | ||
342 | break; | ||
343 | } | ||
344 | |||
345 | if (!cr_done) | ||
346 | goto stop; | ||
347 | |||
348 | /* channel equalisation */ | ||
349 | NV_DEBUG(dev, "\tbegin eq\n"); | ||
350 | ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_2); | ||
351 | if (ret) | ||
352 | goto stop; | ||
353 | |||
354 | for (tries = 0; tries <= 5; tries++) { | ||
355 | udelay(400); | ||
356 | |||
357 | ret = auxch_rd(encoder, DP_LANE0_1_STATUS, status, 3); | ||
358 | if (ret) | ||
359 | break; | ||
360 | NV_DEBUG(dev, "\t\tstatus: 0x%02x 0x%02x\n", | ||
361 | status[0], status[1]); | ||
362 | |||
363 | eq_done = true; | ||
364 | if (!(status[2] & DP_INTERLANE_ALIGN_DONE)) | ||
365 | eq_done = false; | ||
366 | |||
367 | for (i = 0; eq_done && i < nv_encoder->dp.link_nr; i++) { | ||
368 | int lane = (status[i >> 1] >> ((i & 1) * 4)) & 0xf; | ||
369 | |||
370 | if (!(lane & DP_LANE_CR_DONE)) { | ||
371 | cr_done = false; | ||
372 | break; | ||
373 | } | ||
374 | |||
375 | if (!(lane & DP_LANE_CHANNEL_EQ_DONE) || | ||
376 | !(lane & DP_LANE_SYMBOL_LOCKED)) { | ||
377 | eq_done = false; | ||
378 | break; | ||
379 | } | ||
380 | } | ||
381 | |||
382 | if (eq_done || !cr_done) | ||
383 | break; | ||
384 | |||
385 | if (!nouveau_dp_link_train_adjust(encoder, config) || | ||
386 | !nouveau_dp_link_train_commit(encoder, config)) | ||
387 | break; | ||
388 | } | ||
389 | |||
390 | stop: | ||
391 | /* end link training */ | ||
392 | ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_DISABLE); | ||
393 | if (ret) | ||
394 | return false; | ||
395 | |||
396 | /* retry at a lower setting, if possible */ | ||
397 | if (!ret && !(eq_done && cr_done)) { | ||
398 | NV_DEBUG(dev, "\twe failed\n"); | ||
399 | if (nv_encoder->dp.link_bw != DP_LINK_BW_1_62) { | ||
400 | NV_DEBUG(dev, "retry link training at low rate\n"); | ||
401 | nv_encoder->dp.link_bw = DP_LINK_BW_1_62; | ||
402 | goto train; | ||
403 | } | ||
404 | } | ||
405 | |||
406 | return eq_done; | ||
407 | } | ||
408 | |||
409 | bool | ||
410 | nouveau_dp_detect(struct drm_encoder *encoder) | ||
411 | { | ||
412 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
413 | struct drm_device *dev = encoder->dev; | ||
414 | uint8_t dpcd[4]; | ||
415 | int ret; | ||
416 | |||
417 | ret = auxch_rd(encoder, 0x0000, dpcd, 4); | ||
418 | if (ret) | ||
419 | return false; | ||
420 | |||
421 | NV_DEBUG(dev, "encoder: link_bw %d, link_nr %d\n" | ||
422 | "display: link_bw %d, link_nr %d version 0x%02x\n", | ||
423 | nv_encoder->dcb->dpconf.link_bw, | ||
424 | nv_encoder->dcb->dpconf.link_nr, | ||
425 | dpcd[1], dpcd[2] & 0x0f, dpcd[0]); | ||
426 | |||
427 | nv_encoder->dp.dpcd_version = dpcd[0]; | ||
428 | |||
429 | nv_encoder->dp.link_bw = dpcd[1]; | ||
430 | if (nv_encoder->dp.link_bw != DP_LINK_BW_1_62 && | ||
431 | !nv_encoder->dcb->dpconf.link_bw) | ||
432 | nv_encoder->dp.link_bw = DP_LINK_BW_1_62; | ||
433 | |||
434 | nv_encoder->dp.link_nr = dpcd[2] & 0xf; | ||
435 | if (nv_encoder->dp.link_nr > nv_encoder->dcb->dpconf.link_nr) | ||
436 | nv_encoder->dp.link_nr = nv_encoder->dcb->dpconf.link_nr; | ||
437 | |||
438 | return true; | ||
439 | } | ||
440 | |||
441 | int | ||
442 | nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr, | ||
443 | uint8_t *data, int data_nr) | ||
444 | { | ||
445 | struct drm_device *dev = auxch->dev; | ||
446 | uint32_t tmp, ctrl, stat = 0, data32[4] = {}; | ||
447 | int ret = 0, i, index = auxch->rd; | ||
448 | |||
449 | NV_DEBUG(dev, "ch %d cmd %d addr 0x%x len %d\n", index, cmd, addr, data_nr); | ||
450 | |||
451 | tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd)); | ||
452 | nv_wr32(dev, NV50_AUXCH_CTRL(auxch->rd), tmp | 0x00100000); | ||
453 | tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd)); | ||
454 | if (!(tmp & 0x01000000)) { | ||
455 | NV_ERROR(dev, "expected bit 24 == 1, got 0x%08x\n", tmp); | ||
456 | ret = -EIO; | ||
457 | goto out; | ||
458 | } | ||
459 | |||
460 | for (i = 0; i < 3; i++) { | ||
461 | tmp = nv_rd32(dev, NV50_AUXCH_STAT(auxch->rd)); | ||
462 | if (tmp & NV50_AUXCH_STAT_STATE_READY) | ||
463 | break; | ||
464 | udelay(100); | ||
465 | } | ||
466 | |||
467 | if (i == 3) { | ||
468 | ret = -EBUSY; | ||
469 | goto out; | ||
470 | } | ||
471 | |||
472 | if (!(cmd & 1)) { | ||
473 | memcpy(data32, data, data_nr); | ||
474 | for (i = 0; i < 4; i++) { | ||
475 | NV_DEBUG(dev, "wr %d: 0x%08x\n", i, data32[i]); | ||
476 | nv_wr32(dev, NV50_AUXCH_DATA_OUT(index, i), data32[i]); | ||
477 | } | ||
478 | } | ||
479 | |||
480 | nv_wr32(dev, NV50_AUXCH_ADDR(index), addr); | ||
481 | ctrl = nv_rd32(dev, NV50_AUXCH_CTRL(index)); | ||
482 | ctrl &= ~(NV50_AUXCH_CTRL_CMD | NV50_AUXCH_CTRL_LEN); | ||
483 | ctrl |= (cmd << NV50_AUXCH_CTRL_CMD_SHIFT); | ||
484 | ctrl |= ((data_nr - 1) << NV50_AUXCH_CTRL_LEN_SHIFT); | ||
485 | |||
486 | for (;;) { | ||
487 | nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl | 0x80000000); | ||
488 | nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl); | ||
489 | nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl | 0x00010000); | ||
490 | if (!nv_wait(NV50_AUXCH_CTRL(index), 0x00010000, 0x00000000)) { | ||
491 | NV_ERROR(dev, "expected bit 16 == 0, got 0x%08x\n", | ||
492 | nv_rd32(dev, NV50_AUXCH_CTRL(index))); | ||
493 | return -EBUSY; | ||
494 | } | ||
495 | |||
496 | udelay(400); | ||
497 | |||
498 | stat = nv_rd32(dev, NV50_AUXCH_STAT(index)); | ||
499 | if ((stat & NV50_AUXCH_STAT_REPLY_AUX) != | ||
500 | NV50_AUXCH_STAT_REPLY_AUX_DEFER) | ||
501 | break; | ||
502 | } | ||
503 | |||
504 | if (cmd & 1) { | ||
505 | for (i = 0; i < 4; i++) { | ||
506 | data32[i] = nv_rd32(dev, NV50_AUXCH_DATA_IN(index, i)); | ||
507 | NV_DEBUG(dev, "rd %d: 0x%08x\n", i, data32[i]); | ||
508 | } | ||
509 | memcpy(data, data32, data_nr); | ||
510 | } | ||
511 | |||
512 | out: | ||
513 | tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd)); | ||
514 | nv_wr32(dev, NV50_AUXCH_CTRL(auxch->rd), tmp & ~0x00100000); | ||
515 | tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd)); | ||
516 | if (tmp & 0x01000000) { | ||
517 | NV_ERROR(dev, "expected bit 24 == 0, got 0x%08x\n", tmp); | ||
518 | ret = -EIO; | ||
519 | } | ||
520 | |||
521 | udelay(400); | ||
522 | |||
523 | return ret ? ret : (stat & NV50_AUXCH_STAT_REPLY); | ||
524 | } | ||
525 | |||
526 | int | ||
527 | nouveau_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, | ||
528 | uint8_t write_byte, uint8_t *read_byte) | ||
529 | { | ||
530 | struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; | ||
531 | struct nouveau_i2c_chan *auxch = (struct nouveau_i2c_chan *)adapter; | ||
532 | struct drm_device *dev = auxch->dev; | ||
533 | int ret = 0, cmd, addr = algo_data->address; | ||
534 | uint8_t *buf; | ||
535 | |||
536 | if (mode == MODE_I2C_READ) { | ||
537 | cmd = AUX_I2C_READ; | ||
538 | buf = read_byte; | ||
539 | } else { | ||
540 | cmd = (mode & MODE_I2C_READ) ? AUX_I2C_READ : AUX_I2C_WRITE; | ||
541 | buf = &write_byte; | ||
542 | } | ||
543 | |||
544 | if (!(mode & MODE_I2C_STOP)) | ||
545 | cmd |= AUX_I2C_MOT; | ||
546 | |||
547 | if (mode & MODE_I2C_START) | ||
548 | return 1; | ||
549 | |||
550 | for (;;) { | ||
551 | ret = nouveau_dp_auxch(auxch, cmd, addr, buf, 1); | ||
552 | if (ret < 0) | ||
553 | return ret; | ||
554 | |||
555 | switch (ret & NV50_AUXCH_STAT_REPLY_I2C) { | ||
556 | case NV50_AUXCH_STAT_REPLY_I2C_ACK: | ||
557 | return 1; | ||
558 | case NV50_AUXCH_STAT_REPLY_I2C_NACK: | ||
559 | return -EREMOTEIO; | ||
560 | case NV50_AUXCH_STAT_REPLY_I2C_DEFER: | ||
561 | udelay(100); | ||
562 | break; | ||
563 | default: | ||
564 | NV_ERROR(dev, "invalid auxch status: 0x%08x\n", ret); | ||
565 | return -EREMOTEIO; | ||
566 | } | ||
567 | } | ||
568 | } | ||
569 | |||
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c new file mode 100644 index 000000000000..35249c35118f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_drv.c | |||
@@ -0,0 +1,405 @@ | |||
1 | /* | ||
2 | * Copyright 2005 Stephane Marchesin. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
6 | * copy of this software and associated documentation files (the "Software"), | ||
7 | * to deal in the Software without restriction, including without limitation | ||
8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
9 | * and/or sell copies of the Software, and to permit persons to whom the | ||
10 | * Software is furnished to do so, subject to the following conditions: | ||
11 | * | ||
12 | * The above copyright notice and this permission notice (including the next | ||
13 | * paragraph) shall be included in all copies or substantial portions of the | ||
14 | * 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 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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 | |||
25 | #include <linux/console.h> | ||
26 | |||
27 | #include "drmP.h" | ||
28 | #include "drm.h" | ||
29 | #include "drm_crtc_helper.h" | ||
30 | #include "nouveau_drv.h" | ||
31 | #include "nouveau_hw.h" | ||
32 | #include "nouveau_fb.h" | ||
33 | #include "nouveau_fbcon.h" | ||
34 | #include "nv50_display.h" | ||
35 | |||
36 | #include "drm_pciids.h" | ||
37 | |||
38 | MODULE_PARM_DESC(noagp, "Disable AGP"); | ||
39 | int nouveau_noagp; | ||
40 | module_param_named(noagp, nouveau_noagp, int, 0400); | ||
41 | |||
42 | MODULE_PARM_DESC(modeset, "Enable kernel modesetting"); | ||
43 | static int nouveau_modeset = -1; /* kms */ | ||
44 | module_param_named(modeset, nouveau_modeset, int, 0400); | ||
45 | |||
46 | MODULE_PARM_DESC(vbios, "Override default VBIOS location"); | ||
47 | char *nouveau_vbios; | ||
48 | module_param_named(vbios, nouveau_vbios, charp, 0400); | ||
49 | |||
50 | MODULE_PARM_DESC(vram_pushbuf, "Force DMA push buffers to be in VRAM"); | ||
51 | int nouveau_vram_pushbuf; | ||
52 | module_param_named(vram_pushbuf, nouveau_vram_pushbuf, int, 0400); | ||
53 | |||
54 | MODULE_PARM_DESC(vram_notify, "Force DMA notifiers to be in VRAM"); | ||
55 | int nouveau_vram_notify; | ||
56 | module_param_named(vram_notify, nouveau_vram_notify, int, 0400); | ||
57 | |||
58 | MODULE_PARM_DESC(duallink, "Allow dual-link TMDS (>=GeForce 8)"); | ||
59 | int nouveau_duallink = 1; | ||
60 | module_param_named(duallink, nouveau_duallink, int, 0400); | ||
61 | |||
62 | MODULE_PARM_DESC(uscript_lvds, "LVDS output script table ID (>=GeForce 8)"); | ||
63 | int nouveau_uscript_lvds = -1; | ||
64 | module_param_named(uscript_lvds, nouveau_uscript_lvds, int, 0400); | ||
65 | |||
66 | MODULE_PARM_DESC(uscript_tmds, "TMDS output script table ID (>=GeForce 8)"); | ||
67 | int nouveau_uscript_tmds = -1; | ||
68 | module_param_named(uscript_tmds, nouveau_uscript_tmds, int, 0400); | ||
69 | |||
70 | MODULE_PARM_DESC(tv_norm, "Default TV norm.\n" | ||
71 | "\t\tSupported: PAL, PAL-M, PAL-N, PAL-Nc, NTSC-M, NTSC-J,\n" | ||
72 | "\t\t\thd480i, hd480p, hd576i, hd576p, hd720p, hd1080i.\n" | ||
73 | "\t\tDefault: PAL\n" | ||
74 | "\t\t*NOTE* Ignored for cards with external TV encoders."); | ||
75 | char *nouveau_tv_norm; | ||
76 | module_param_named(tv_norm, nouveau_tv_norm, charp, 0400); | ||
77 | |||
78 | MODULE_PARM_DESC(reg_debug, "Register access debug bitmask:\n" | ||
79 | "\t\t0x1 mc, 0x2 video, 0x4 fb, 0x8 extdev,\n" | ||
80 | "\t\t0x10 crtc, 0x20 ramdac, 0x40 vgacrtc, 0x80 rmvio,\n" | ||
81 | "\t\t0x100 vgaattr, 0x200 EVO (G80+). "); | ||
82 | int nouveau_reg_debug; | ||
83 | module_param_named(reg_debug, nouveau_reg_debug, int, 0600); | ||
84 | |||
85 | int nouveau_fbpercrtc; | ||
86 | #if 0 | ||
87 | module_param_named(fbpercrtc, nouveau_fbpercrtc, int, 0400); | ||
88 | #endif | ||
89 | |||
90 | static struct pci_device_id pciidlist[] = { | ||
91 | { | ||
92 | PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID), | ||
93 | .class = PCI_BASE_CLASS_DISPLAY << 16, | ||
94 | .class_mask = 0xff << 16, | ||
95 | }, | ||
96 | { | ||
97 | PCI_DEVICE(PCI_VENDOR_ID_NVIDIA_SGS, PCI_ANY_ID), | ||
98 | .class = PCI_BASE_CLASS_DISPLAY << 16, | ||
99 | .class_mask = 0xff << 16, | ||
100 | }, | ||
101 | {} | ||
102 | }; | ||
103 | |||
104 | MODULE_DEVICE_TABLE(pci, pciidlist); | ||
105 | |||
106 | static struct drm_driver driver; | ||
107 | |||
108 | static int __devinit | ||
109 | nouveau_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | ||
110 | { | ||
111 | return drm_get_dev(pdev, ent, &driver); | ||
112 | } | ||
113 | |||
114 | static void | ||
115 | nouveau_pci_remove(struct pci_dev *pdev) | ||
116 | { | ||
117 | struct drm_device *dev = pci_get_drvdata(pdev); | ||
118 | |||
119 | drm_put_dev(dev); | ||
120 | } | ||
121 | |||
122 | static int | ||
123 | nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state) | ||
124 | { | ||
125 | struct drm_device *dev = pci_get_drvdata(pdev); | ||
126 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
127 | struct nouveau_instmem_engine *pinstmem = &dev_priv->engine.instmem; | ||
128 | struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; | ||
129 | struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; | ||
130 | struct nouveau_channel *chan; | ||
131 | struct drm_crtc *crtc; | ||
132 | uint32_t fbdev_flags; | ||
133 | int ret, i; | ||
134 | |||
135 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) | ||
136 | return -ENODEV; | ||
137 | |||
138 | if (pm_state.event == PM_EVENT_PRETHAW) | ||
139 | return 0; | ||
140 | |||
141 | fbdev_flags = dev_priv->fbdev_info->flags; | ||
142 | dev_priv->fbdev_info->flags |= FBINFO_HWACCEL_DISABLED; | ||
143 | |||
144 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | ||
145 | struct nouveau_framebuffer *nouveau_fb; | ||
146 | |||
147 | nouveau_fb = nouveau_framebuffer(crtc->fb); | ||
148 | if (!nouveau_fb || !nouveau_fb->nvbo) | ||
149 | continue; | ||
150 | |||
151 | nouveau_bo_unpin(nouveau_fb->nvbo); | ||
152 | } | ||
153 | |||
154 | NV_INFO(dev, "Evicting buffers...\n"); | ||
155 | ttm_bo_evict_mm(&dev_priv->ttm.bdev, TTM_PL_VRAM); | ||
156 | |||
157 | NV_INFO(dev, "Idling channels...\n"); | ||
158 | for (i = 0; i < pfifo->channels; i++) { | ||
159 | struct nouveau_fence *fence = NULL; | ||
160 | |||
161 | chan = dev_priv->fifos[i]; | ||
162 | if (!chan || (dev_priv->card_type >= NV_50 && | ||
163 | chan == dev_priv->fifos[0])) | ||
164 | continue; | ||
165 | |||
166 | ret = nouveau_fence_new(chan, &fence, true); | ||
167 | if (ret == 0) { | ||
168 | ret = nouveau_fence_wait(fence, NULL, false, false); | ||
169 | nouveau_fence_unref((void *)&fence); | ||
170 | } | ||
171 | |||
172 | if (ret) { | ||
173 | NV_ERROR(dev, "Failed to idle channel %d for suspend\n", | ||
174 | chan->id); | ||
175 | } | ||
176 | } | ||
177 | |||
178 | pgraph->fifo_access(dev, false); | ||
179 | nouveau_wait_for_idle(dev); | ||
180 | pfifo->reassign(dev, false); | ||
181 | pfifo->disable(dev); | ||
182 | pfifo->unload_context(dev); | ||
183 | pgraph->unload_context(dev); | ||
184 | |||
185 | NV_INFO(dev, "Suspending GPU objects...\n"); | ||
186 | ret = nouveau_gpuobj_suspend(dev); | ||
187 | if (ret) { | ||
188 | NV_ERROR(dev, "... failed: %d\n", ret); | ||
189 | goto out_abort; | ||
190 | } | ||
191 | |||
192 | ret = pinstmem->suspend(dev); | ||
193 | if (ret) { | ||
194 | NV_ERROR(dev, "... failed: %d\n", ret); | ||
195 | nouveau_gpuobj_suspend_cleanup(dev); | ||
196 | goto out_abort; | ||
197 | } | ||
198 | |||
199 | NV_INFO(dev, "And we're gone!\n"); | ||
200 | pci_save_state(pdev); | ||
201 | if (pm_state.event == PM_EVENT_SUSPEND) { | ||
202 | pci_disable_device(pdev); | ||
203 | pci_set_power_state(pdev, PCI_D3hot); | ||
204 | } | ||
205 | |||
206 | acquire_console_sem(); | ||
207 | fb_set_suspend(dev_priv->fbdev_info, 1); | ||
208 | release_console_sem(); | ||
209 | dev_priv->fbdev_info->flags = fbdev_flags; | ||
210 | return 0; | ||
211 | |||
212 | out_abort: | ||
213 | NV_INFO(dev, "Re-enabling acceleration..\n"); | ||
214 | pfifo->enable(dev); | ||
215 | pfifo->reassign(dev, true); | ||
216 | pgraph->fifo_access(dev, true); | ||
217 | return ret; | ||
218 | } | ||
219 | |||
220 | static int | ||
221 | nouveau_pci_resume(struct pci_dev *pdev) | ||
222 | { | ||
223 | struct drm_device *dev = pci_get_drvdata(pdev); | ||
224 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
225 | struct nouveau_engine *engine = &dev_priv->engine; | ||
226 | struct drm_crtc *crtc; | ||
227 | uint32_t fbdev_flags; | ||
228 | int ret, i; | ||
229 | |||
230 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) | ||
231 | return -ENODEV; | ||
232 | |||
233 | fbdev_flags = dev_priv->fbdev_info->flags; | ||
234 | dev_priv->fbdev_info->flags |= FBINFO_HWACCEL_DISABLED; | ||
235 | |||
236 | NV_INFO(dev, "We're back, enabling device...\n"); | ||
237 | pci_set_power_state(pdev, PCI_D0); | ||
238 | pci_restore_state(pdev); | ||
239 | if (pci_enable_device(pdev)) | ||
240 | return -1; | ||
241 | pci_set_master(dev->pdev); | ||
242 | |||
243 | NV_INFO(dev, "POSTing device...\n"); | ||
244 | ret = nouveau_run_vbios_init(dev); | ||
245 | if (ret) | ||
246 | return ret; | ||
247 | |||
248 | if (dev_priv->gart_info.type == NOUVEAU_GART_AGP) { | ||
249 | ret = nouveau_mem_init_agp(dev); | ||
250 | if (ret) { | ||
251 | NV_ERROR(dev, "error reinitialising AGP: %d\n", ret); | ||
252 | return ret; | ||
253 | } | ||
254 | } | ||
255 | |||
256 | NV_INFO(dev, "Reinitialising engines...\n"); | ||
257 | engine->instmem.resume(dev); | ||
258 | engine->mc.init(dev); | ||
259 | engine->timer.init(dev); | ||
260 | engine->fb.init(dev); | ||
261 | engine->graph.init(dev); | ||
262 | engine->fifo.init(dev); | ||
263 | |||
264 | NV_INFO(dev, "Restoring GPU objects...\n"); | ||
265 | nouveau_gpuobj_resume(dev); | ||
266 | |||
267 | nouveau_irq_postinstall(dev); | ||
268 | |||
269 | /* Re-write SKIPS, they'll have been lost over the suspend */ | ||
270 | if (nouveau_vram_pushbuf) { | ||
271 | struct nouveau_channel *chan; | ||
272 | int j; | ||
273 | |||
274 | for (i = 0; i < dev_priv->engine.fifo.channels; i++) { | ||
275 | chan = dev_priv->fifos[i]; | ||
276 | if (!chan) | ||
277 | continue; | ||
278 | |||
279 | for (j = 0; j < NOUVEAU_DMA_SKIPS; j++) | ||
280 | nouveau_bo_wr32(chan->pushbuf_bo, i, 0); | ||
281 | } | ||
282 | } | ||
283 | |||
284 | NV_INFO(dev, "Restoring mode...\n"); | ||
285 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | ||
286 | struct nouveau_framebuffer *nouveau_fb; | ||
287 | |||
288 | nouveau_fb = nouveau_framebuffer(crtc->fb); | ||
289 | if (!nouveau_fb || !nouveau_fb->nvbo) | ||
290 | continue; | ||
291 | |||
292 | nouveau_bo_pin(nouveau_fb->nvbo, TTM_PL_FLAG_VRAM); | ||
293 | } | ||
294 | |||
295 | if (dev_priv->card_type < NV_50) { | ||
296 | nv04_display_restore(dev); | ||
297 | NVLockVgaCrtcs(dev, false); | ||
298 | } else | ||
299 | nv50_display_init(dev); | ||
300 | |||
301 | /* Force CLUT to get re-loaded during modeset */ | ||
302 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | ||
303 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | ||
304 | |||
305 | nv_crtc->lut.depth = 0; | ||
306 | } | ||
307 | |||
308 | acquire_console_sem(); | ||
309 | fb_set_suspend(dev_priv->fbdev_info, 0); | ||
310 | release_console_sem(); | ||
311 | |||
312 | nouveau_fbcon_zfill(dev); | ||
313 | |||
314 | drm_helper_resume_force_mode(dev); | ||
315 | dev_priv->fbdev_info->flags = fbdev_flags; | ||
316 | return 0; | ||
317 | } | ||
318 | |||
319 | static struct drm_driver driver = { | ||
320 | .driver_features = | ||
321 | DRIVER_USE_AGP | DRIVER_PCI_DMA | DRIVER_SG | | ||
322 | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_GEM, | ||
323 | .load = nouveau_load, | ||
324 | .firstopen = nouveau_firstopen, | ||
325 | .lastclose = nouveau_lastclose, | ||
326 | .unload = nouveau_unload, | ||
327 | .preclose = nouveau_preclose, | ||
328 | #if defined(CONFIG_DRM_NOUVEAU_DEBUG) | ||
329 | .debugfs_init = nouveau_debugfs_init, | ||
330 | .debugfs_cleanup = nouveau_debugfs_takedown, | ||
331 | #endif | ||
332 | .irq_preinstall = nouveau_irq_preinstall, | ||
333 | .irq_postinstall = nouveau_irq_postinstall, | ||
334 | .irq_uninstall = nouveau_irq_uninstall, | ||
335 | .irq_handler = nouveau_irq_handler, | ||
336 | .reclaim_buffers = drm_core_reclaim_buffers, | ||
337 | .get_map_ofs = drm_core_get_map_ofs, | ||
338 | .get_reg_ofs = drm_core_get_reg_ofs, | ||
339 | .ioctls = nouveau_ioctls, | ||
340 | .fops = { | ||
341 | .owner = THIS_MODULE, | ||
342 | .open = drm_open, | ||
343 | .release = drm_release, | ||
344 | .ioctl = drm_ioctl, | ||
345 | .mmap = nouveau_ttm_mmap, | ||
346 | .poll = drm_poll, | ||
347 | .fasync = drm_fasync, | ||
348 | #if defined(CONFIG_COMPAT) | ||
349 | .compat_ioctl = nouveau_compat_ioctl, | ||
350 | #endif | ||
351 | }, | ||
352 | .pci_driver = { | ||
353 | .name = DRIVER_NAME, | ||
354 | .id_table = pciidlist, | ||
355 | .probe = nouveau_pci_probe, | ||
356 | .remove = nouveau_pci_remove, | ||
357 | .suspend = nouveau_pci_suspend, | ||
358 | .resume = nouveau_pci_resume | ||
359 | }, | ||
360 | |||
361 | .gem_init_object = nouveau_gem_object_new, | ||
362 | .gem_free_object = nouveau_gem_object_del, | ||
363 | |||
364 | .name = DRIVER_NAME, | ||
365 | .desc = DRIVER_DESC, | ||
366 | #ifdef GIT_REVISION | ||
367 | .date = GIT_REVISION, | ||
368 | #else | ||
369 | .date = DRIVER_DATE, | ||
370 | #endif | ||
371 | .major = DRIVER_MAJOR, | ||
372 | .minor = DRIVER_MINOR, | ||
373 | .patchlevel = DRIVER_PATCHLEVEL, | ||
374 | }; | ||
375 | |||
376 | static int __init nouveau_init(void) | ||
377 | { | ||
378 | driver.num_ioctls = nouveau_max_ioctl; | ||
379 | |||
380 | if (nouveau_modeset == -1) { | ||
381 | #ifdef CONFIG_VGA_CONSOLE | ||
382 | if (vgacon_text_force()) | ||
383 | nouveau_modeset = 0; | ||
384 | else | ||
385 | #endif | ||
386 | nouveau_modeset = 1; | ||
387 | } | ||
388 | |||
389 | if (nouveau_modeset == 1) | ||
390 | driver.driver_features |= DRIVER_MODESET; | ||
391 | |||
392 | return drm_init(&driver); | ||
393 | } | ||
394 | |||
395 | static void __exit nouveau_exit(void) | ||
396 | { | ||
397 | drm_exit(&driver); | ||
398 | } | ||
399 | |||
400 | module_init(nouveau_init); | ||
401 | module_exit(nouveau_exit); | ||
402 | |||
403 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
404 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
405 | MODULE_LICENSE("GPL and additional rights"); | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h new file mode 100644 index 000000000000..88b4c7b77e7f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h | |||
@@ -0,0 +1,1286 @@ | |||
1 | /* | ||
2 | * Copyright 2005 Stephane Marchesin. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
6 | * copy of this software and associated documentation files (the "Software"), | ||
7 | * to deal in the Software without restriction, including without limitation | ||
8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
9 | * and/or sell copies of the Software, and to permit persons to whom the | ||
10 | * Software is furnished to do so, subject to the following conditions: | ||
11 | * | ||
12 | * The above copyright notice and this permission notice (including the next | ||
13 | * paragraph) shall be included in all copies or substantial portions of the | ||
14 | * 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 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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 | |||
25 | #ifndef __NOUVEAU_DRV_H__ | ||
26 | #define __NOUVEAU_DRV_H__ | ||
27 | |||
28 | #define DRIVER_AUTHOR "Stephane Marchesin" | ||
29 | #define DRIVER_EMAIL "dri-devel@lists.sourceforge.net" | ||
30 | |||
31 | #define DRIVER_NAME "nouveau" | ||
32 | #define DRIVER_DESC "nVidia Riva/TNT/GeForce" | ||
33 | #define DRIVER_DATE "20090420" | ||
34 | |||
35 | #define DRIVER_MAJOR 0 | ||
36 | #define DRIVER_MINOR 0 | ||
37 | #define DRIVER_PATCHLEVEL 15 | ||
38 | |||
39 | #define NOUVEAU_FAMILY 0x0000FFFF | ||
40 | #define NOUVEAU_FLAGS 0xFFFF0000 | ||
41 | |||
42 | #include "ttm/ttm_bo_api.h" | ||
43 | #include "ttm/ttm_bo_driver.h" | ||
44 | #include "ttm/ttm_placement.h" | ||
45 | #include "ttm/ttm_memory.h" | ||
46 | #include "ttm/ttm_module.h" | ||
47 | |||
48 | struct nouveau_fpriv { | ||
49 | struct ttm_object_file *tfile; | ||
50 | }; | ||
51 | |||
52 | #define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT) | ||
53 | |||
54 | #include "nouveau_drm.h" | ||
55 | #include "nouveau_reg.h" | ||
56 | #include "nouveau_bios.h" | ||
57 | |||
58 | #define MAX_NUM_DCB_ENTRIES 16 | ||
59 | |||
60 | #define NOUVEAU_MAX_CHANNEL_NR 128 | ||
61 | |||
62 | #define NV50_VM_MAX_VRAM (2*1024*1024*1024ULL) | ||
63 | #define NV50_VM_BLOCK (512*1024*1024ULL) | ||
64 | #define NV50_VM_VRAM_NR (NV50_VM_MAX_VRAM / NV50_VM_BLOCK) | ||
65 | |||
66 | struct nouveau_bo { | ||
67 | struct ttm_buffer_object bo; | ||
68 | struct ttm_placement placement; | ||
69 | u32 placements[3]; | ||
70 | struct ttm_bo_kmap_obj kmap; | ||
71 | struct list_head head; | ||
72 | |||
73 | /* protected by ttm_bo_reserve() */ | ||
74 | struct drm_file *reserved_by; | ||
75 | struct list_head entry; | ||
76 | int pbbo_index; | ||
77 | |||
78 | struct nouveau_channel *channel; | ||
79 | |||
80 | bool mappable; | ||
81 | bool no_vm; | ||
82 | |||
83 | uint32_t tile_mode; | ||
84 | uint32_t tile_flags; | ||
85 | |||
86 | struct drm_gem_object *gem; | ||
87 | struct drm_file *cpu_filp; | ||
88 | int pin_refcnt; | ||
89 | }; | ||
90 | |||
91 | static inline struct nouveau_bo * | ||
92 | nouveau_bo(struct ttm_buffer_object *bo) | ||
93 | { | ||
94 | return container_of(bo, struct nouveau_bo, bo); | ||
95 | } | ||
96 | |||
97 | static inline struct nouveau_bo * | ||
98 | nouveau_gem_object(struct drm_gem_object *gem) | ||
99 | { | ||
100 | return gem ? gem->driver_private : NULL; | ||
101 | } | ||
102 | |||
103 | /* TODO: submit equivalent to TTM generic API upstream? */ | ||
104 | static inline void __iomem * | ||
105 | nvbo_kmap_obj_iovirtual(struct nouveau_bo *nvbo) | ||
106 | { | ||
107 | bool is_iomem; | ||
108 | void __iomem *ioptr = (void __force __iomem *)ttm_kmap_obj_virtual( | ||
109 | &nvbo->kmap, &is_iomem); | ||
110 | WARN_ON_ONCE(ioptr && !is_iomem); | ||
111 | return ioptr; | ||
112 | } | ||
113 | |||
114 | struct mem_block { | ||
115 | struct mem_block *next; | ||
116 | struct mem_block *prev; | ||
117 | uint64_t start; | ||
118 | uint64_t size; | ||
119 | struct drm_file *file_priv; /* NULL: free, -1: heap, other: real files */ | ||
120 | }; | ||
121 | |||
122 | enum nouveau_flags { | ||
123 | NV_NFORCE = 0x10000000, | ||
124 | NV_NFORCE2 = 0x20000000 | ||
125 | }; | ||
126 | |||
127 | #define NVOBJ_ENGINE_SW 0 | ||
128 | #define NVOBJ_ENGINE_GR 1 | ||
129 | #define NVOBJ_ENGINE_DISPLAY 2 | ||
130 | #define NVOBJ_ENGINE_INT 0xdeadbeef | ||
131 | |||
132 | #define NVOBJ_FLAG_ALLOW_NO_REFS (1 << 0) | ||
133 | #define NVOBJ_FLAG_ZERO_ALLOC (1 << 1) | ||
134 | #define NVOBJ_FLAG_ZERO_FREE (1 << 2) | ||
135 | #define NVOBJ_FLAG_FAKE (1 << 3) | ||
136 | struct nouveau_gpuobj { | ||
137 | struct list_head list; | ||
138 | |||
139 | struct nouveau_channel *im_channel; | ||
140 | struct mem_block *im_pramin; | ||
141 | struct nouveau_bo *im_backing; | ||
142 | uint32_t im_backing_start; | ||
143 | uint32_t *im_backing_suspend; | ||
144 | int im_bound; | ||
145 | |||
146 | uint32_t flags; | ||
147 | int refcount; | ||
148 | |||
149 | uint32_t engine; | ||
150 | uint32_t class; | ||
151 | |||
152 | void (*dtor)(struct drm_device *, struct nouveau_gpuobj *); | ||
153 | void *priv; | ||
154 | }; | ||
155 | |||
156 | struct nouveau_gpuobj_ref { | ||
157 | struct list_head list; | ||
158 | |||
159 | struct nouveau_gpuobj *gpuobj; | ||
160 | uint32_t instance; | ||
161 | |||
162 | struct nouveau_channel *channel; | ||
163 | int handle; | ||
164 | }; | ||
165 | |||
166 | struct nouveau_channel { | ||
167 | struct drm_device *dev; | ||
168 | int id; | ||
169 | |||
170 | /* owner of this fifo */ | ||
171 | struct drm_file *file_priv; | ||
172 | /* mapping of the fifo itself */ | ||
173 | struct drm_local_map *map; | ||
174 | |||
175 | /* mapping of the regs controling the fifo */ | ||
176 | void __iomem *user; | ||
177 | uint32_t user_get; | ||
178 | uint32_t user_put; | ||
179 | |||
180 | /* Fencing */ | ||
181 | struct { | ||
182 | /* lock protects the pending list only */ | ||
183 | spinlock_t lock; | ||
184 | struct list_head pending; | ||
185 | uint32_t sequence; | ||
186 | uint32_t sequence_ack; | ||
187 | uint32_t last_sequence_irq; | ||
188 | } fence; | ||
189 | |||
190 | /* DMA push buffer */ | ||
191 | struct nouveau_gpuobj_ref *pushbuf; | ||
192 | struct nouveau_bo *pushbuf_bo; | ||
193 | uint32_t pushbuf_base; | ||
194 | |||
195 | /* Notifier memory */ | ||
196 | struct nouveau_bo *notifier_bo; | ||
197 | struct mem_block *notifier_heap; | ||
198 | |||
199 | /* PFIFO context */ | ||
200 | struct nouveau_gpuobj_ref *ramfc; | ||
201 | struct nouveau_gpuobj_ref *cache; | ||
202 | |||
203 | /* PGRAPH context */ | ||
204 | /* XXX may be merge 2 pointers as private data ??? */ | ||
205 | struct nouveau_gpuobj_ref *ramin_grctx; | ||
206 | void *pgraph_ctx; | ||
207 | |||
208 | /* NV50 VM */ | ||
209 | struct nouveau_gpuobj *vm_pd; | ||
210 | struct nouveau_gpuobj_ref *vm_gart_pt; | ||
211 | struct nouveau_gpuobj_ref *vm_vram_pt[NV50_VM_VRAM_NR]; | ||
212 | |||
213 | /* Objects */ | ||
214 | struct nouveau_gpuobj_ref *ramin; /* Private instmem */ | ||
215 | struct mem_block *ramin_heap; /* Private PRAMIN heap */ | ||
216 | struct nouveau_gpuobj_ref *ramht; /* Hash table */ | ||
217 | struct list_head ramht_refs; /* Objects referenced by RAMHT */ | ||
218 | |||
219 | /* GPU object info for stuff used in-kernel (mm_enabled) */ | ||
220 | uint32_t m2mf_ntfy; | ||
221 | uint32_t vram_handle; | ||
222 | uint32_t gart_handle; | ||
223 | bool accel_done; | ||
224 | |||
225 | /* Push buffer state (only for drm's channel on !mm_enabled) */ | ||
226 | struct { | ||
227 | int max; | ||
228 | int free; | ||
229 | int cur; | ||
230 | int put; | ||
231 | /* access via pushbuf_bo */ | ||
232 | } dma; | ||
233 | |||
234 | uint32_t sw_subchannel[8]; | ||
235 | |||
236 | struct { | ||
237 | struct nouveau_gpuobj *vblsem; | ||
238 | uint32_t vblsem_offset; | ||
239 | uint32_t vblsem_rval; | ||
240 | struct list_head vbl_wait; | ||
241 | } nvsw; | ||
242 | |||
243 | struct { | ||
244 | bool active; | ||
245 | char name[32]; | ||
246 | struct drm_info_list info; | ||
247 | } debugfs; | ||
248 | }; | ||
249 | |||
250 | struct nouveau_instmem_engine { | ||
251 | void *priv; | ||
252 | |||
253 | int (*init)(struct drm_device *dev); | ||
254 | void (*takedown)(struct drm_device *dev); | ||
255 | int (*suspend)(struct drm_device *dev); | ||
256 | void (*resume)(struct drm_device *dev); | ||
257 | |||
258 | int (*populate)(struct drm_device *, struct nouveau_gpuobj *, | ||
259 | uint32_t *size); | ||
260 | void (*clear)(struct drm_device *, struct nouveau_gpuobj *); | ||
261 | int (*bind)(struct drm_device *, struct nouveau_gpuobj *); | ||
262 | int (*unbind)(struct drm_device *, struct nouveau_gpuobj *); | ||
263 | void (*prepare_access)(struct drm_device *, bool write); | ||
264 | void (*finish_access)(struct drm_device *); | ||
265 | }; | ||
266 | |||
267 | struct nouveau_mc_engine { | ||
268 | int (*init)(struct drm_device *dev); | ||
269 | void (*takedown)(struct drm_device *dev); | ||
270 | }; | ||
271 | |||
272 | struct nouveau_timer_engine { | ||
273 | int (*init)(struct drm_device *dev); | ||
274 | void (*takedown)(struct drm_device *dev); | ||
275 | uint64_t (*read)(struct drm_device *dev); | ||
276 | }; | ||
277 | |||
278 | struct nouveau_fb_engine { | ||
279 | int (*init)(struct drm_device *dev); | ||
280 | void (*takedown)(struct drm_device *dev); | ||
281 | }; | ||
282 | |||
283 | struct nouveau_fifo_engine { | ||
284 | void *priv; | ||
285 | |||
286 | int channels; | ||
287 | |||
288 | int (*init)(struct drm_device *); | ||
289 | void (*takedown)(struct drm_device *); | ||
290 | |||
291 | void (*disable)(struct drm_device *); | ||
292 | void (*enable)(struct drm_device *); | ||
293 | bool (*reassign)(struct drm_device *, bool enable); | ||
294 | |||
295 | int (*channel_id)(struct drm_device *); | ||
296 | |||
297 | int (*create_context)(struct nouveau_channel *); | ||
298 | void (*destroy_context)(struct nouveau_channel *); | ||
299 | int (*load_context)(struct nouveau_channel *); | ||
300 | int (*unload_context)(struct drm_device *); | ||
301 | }; | ||
302 | |||
303 | struct nouveau_pgraph_object_method { | ||
304 | int id; | ||
305 | int (*exec)(struct nouveau_channel *chan, int grclass, int mthd, | ||
306 | uint32_t data); | ||
307 | }; | ||
308 | |||
309 | struct nouveau_pgraph_object_class { | ||
310 | int id; | ||
311 | bool software; | ||
312 | struct nouveau_pgraph_object_method *methods; | ||
313 | }; | ||
314 | |||
315 | struct nouveau_pgraph_engine { | ||
316 | struct nouveau_pgraph_object_class *grclass; | ||
317 | bool accel_blocked; | ||
318 | void *ctxprog; | ||
319 | void *ctxvals; | ||
320 | |||
321 | int (*init)(struct drm_device *); | ||
322 | void (*takedown)(struct drm_device *); | ||
323 | |||
324 | void (*fifo_access)(struct drm_device *, bool); | ||
325 | |||
326 | struct nouveau_channel *(*channel)(struct drm_device *); | ||
327 | int (*create_context)(struct nouveau_channel *); | ||
328 | void (*destroy_context)(struct nouveau_channel *); | ||
329 | int (*load_context)(struct nouveau_channel *); | ||
330 | int (*unload_context)(struct drm_device *); | ||
331 | }; | ||
332 | |||
333 | struct nouveau_engine { | ||
334 | struct nouveau_instmem_engine instmem; | ||
335 | struct nouveau_mc_engine mc; | ||
336 | struct nouveau_timer_engine timer; | ||
337 | struct nouveau_fb_engine fb; | ||
338 | struct nouveau_pgraph_engine graph; | ||
339 | struct nouveau_fifo_engine fifo; | ||
340 | }; | ||
341 | |||
342 | struct nouveau_pll_vals { | ||
343 | union { | ||
344 | struct { | ||
345 | #ifdef __BIG_ENDIAN | ||
346 | uint8_t N1, M1, N2, M2; | ||
347 | #else | ||
348 | uint8_t M1, N1, M2, N2; | ||
349 | #endif | ||
350 | }; | ||
351 | struct { | ||
352 | uint16_t NM1, NM2; | ||
353 | } __attribute__((packed)); | ||
354 | }; | ||
355 | int log2P; | ||
356 | |||
357 | int refclk; | ||
358 | }; | ||
359 | |||
360 | enum nv04_fp_display_regs { | ||
361 | FP_DISPLAY_END, | ||
362 | FP_TOTAL, | ||
363 | FP_CRTC, | ||
364 | FP_SYNC_START, | ||
365 | FP_SYNC_END, | ||
366 | FP_VALID_START, | ||
367 | FP_VALID_END | ||
368 | }; | ||
369 | |||
370 | struct nv04_crtc_reg { | ||
371 | unsigned char MiscOutReg; /* */ | ||
372 | uint8_t CRTC[0x9f]; | ||
373 | uint8_t CR58[0x10]; | ||
374 | uint8_t Sequencer[5]; | ||
375 | uint8_t Graphics[9]; | ||
376 | uint8_t Attribute[21]; | ||
377 | unsigned char DAC[768]; /* Internal Colorlookuptable */ | ||
378 | |||
379 | /* PCRTC regs */ | ||
380 | uint32_t fb_start; | ||
381 | uint32_t crtc_cfg; | ||
382 | uint32_t cursor_cfg; | ||
383 | uint32_t gpio_ext; | ||
384 | uint32_t crtc_830; | ||
385 | uint32_t crtc_834; | ||
386 | uint32_t crtc_850; | ||
387 | uint32_t crtc_eng_ctrl; | ||
388 | |||
389 | /* PRAMDAC regs */ | ||
390 | uint32_t nv10_cursync; | ||
391 | struct nouveau_pll_vals pllvals; | ||
392 | uint32_t ramdac_gen_ctrl; | ||
393 | uint32_t ramdac_630; | ||
394 | uint32_t ramdac_634; | ||
395 | uint32_t tv_setup; | ||
396 | uint32_t tv_vtotal; | ||
397 | uint32_t tv_vskew; | ||
398 | uint32_t tv_vsync_delay; | ||
399 | uint32_t tv_htotal; | ||
400 | uint32_t tv_hskew; | ||
401 | uint32_t tv_hsync_delay; | ||
402 | uint32_t tv_hsync_delay2; | ||
403 | uint32_t fp_horiz_regs[7]; | ||
404 | uint32_t fp_vert_regs[7]; | ||
405 | uint32_t dither; | ||
406 | uint32_t fp_control; | ||
407 | uint32_t dither_regs[6]; | ||
408 | uint32_t fp_debug_0; | ||
409 | uint32_t fp_debug_1; | ||
410 | uint32_t fp_debug_2; | ||
411 | uint32_t fp_margin_color; | ||
412 | uint32_t ramdac_8c0; | ||
413 | uint32_t ramdac_a20; | ||
414 | uint32_t ramdac_a24; | ||
415 | uint32_t ramdac_a34; | ||
416 | uint32_t ctv_regs[38]; | ||
417 | }; | ||
418 | |||
419 | struct nv04_output_reg { | ||
420 | uint32_t output; | ||
421 | int head; | ||
422 | }; | ||
423 | |||
424 | struct nv04_mode_state { | ||
425 | uint32_t bpp; | ||
426 | uint32_t width; | ||
427 | uint32_t height; | ||
428 | uint32_t interlace; | ||
429 | uint32_t repaint0; | ||
430 | uint32_t repaint1; | ||
431 | uint32_t screen; | ||
432 | uint32_t scale; | ||
433 | uint32_t dither; | ||
434 | uint32_t extra; | ||
435 | uint32_t fifo; | ||
436 | uint32_t pixel; | ||
437 | uint32_t horiz; | ||
438 | int arbitration0; | ||
439 | int arbitration1; | ||
440 | uint32_t pll; | ||
441 | uint32_t pllB; | ||
442 | uint32_t vpll; | ||
443 | uint32_t vpll2; | ||
444 | uint32_t vpllB; | ||
445 | uint32_t vpll2B; | ||
446 | uint32_t pllsel; | ||
447 | uint32_t sel_clk; | ||
448 | uint32_t general; | ||
449 | uint32_t crtcOwner; | ||
450 | uint32_t head; | ||
451 | uint32_t head2; | ||
452 | uint32_t cursorConfig; | ||
453 | uint32_t cursor0; | ||
454 | uint32_t cursor1; | ||
455 | uint32_t cursor2; | ||
456 | uint32_t timingH; | ||
457 | uint32_t timingV; | ||
458 | uint32_t displayV; | ||
459 | uint32_t crtcSync; | ||
460 | |||
461 | struct nv04_crtc_reg crtc_reg[2]; | ||
462 | }; | ||
463 | |||
464 | enum nouveau_card_type { | ||
465 | NV_04 = 0x00, | ||
466 | NV_10 = 0x10, | ||
467 | NV_20 = 0x20, | ||
468 | NV_30 = 0x30, | ||
469 | NV_40 = 0x40, | ||
470 | NV_50 = 0x50, | ||
471 | }; | ||
472 | |||
473 | struct drm_nouveau_private { | ||
474 | struct drm_device *dev; | ||
475 | enum { | ||
476 | NOUVEAU_CARD_INIT_DOWN, | ||
477 | NOUVEAU_CARD_INIT_DONE, | ||
478 | NOUVEAU_CARD_INIT_FAILED | ||
479 | } init_state; | ||
480 | |||
481 | /* the card type, takes NV_* as values */ | ||
482 | enum nouveau_card_type card_type; | ||
483 | /* exact chipset, derived from NV_PMC_BOOT_0 */ | ||
484 | int chipset; | ||
485 | int flags; | ||
486 | |||
487 | void __iomem *mmio; | ||
488 | void __iomem *ramin; | ||
489 | uint32_t ramin_size; | ||
490 | |||
491 | struct workqueue_struct *wq; | ||
492 | struct work_struct irq_work; | ||
493 | |||
494 | struct list_head vbl_waiting; | ||
495 | |||
496 | struct { | ||
497 | struct ttm_global_reference mem_global_ref; | ||
498 | struct ttm_bo_global_ref bo_global_ref; | ||
499 | struct ttm_bo_device bdev; | ||
500 | spinlock_t bo_list_lock; | ||
501 | struct list_head bo_list; | ||
502 | atomic_t validate_sequence; | ||
503 | } ttm; | ||
504 | |||
505 | struct fb_info *fbdev_info; | ||
506 | |||
507 | int fifo_alloc_count; | ||
508 | struct nouveau_channel *fifos[NOUVEAU_MAX_CHANNEL_NR]; | ||
509 | |||
510 | struct nouveau_engine engine; | ||
511 | struct nouveau_channel *channel; | ||
512 | |||
513 | /* RAMIN configuration, RAMFC, RAMHT and RAMRO offsets */ | ||
514 | struct nouveau_gpuobj *ramht; | ||
515 | uint32_t ramin_rsvd_vram; | ||
516 | uint32_t ramht_offset; | ||
517 | uint32_t ramht_size; | ||
518 | uint32_t ramht_bits; | ||
519 | uint32_t ramfc_offset; | ||
520 | uint32_t ramfc_size; | ||
521 | uint32_t ramro_offset; | ||
522 | uint32_t ramro_size; | ||
523 | |||
524 | /* base physical adresses */ | ||
525 | uint64_t fb_phys; | ||
526 | uint64_t fb_available_size; | ||
527 | uint64_t fb_mappable_pages; | ||
528 | uint64_t fb_aper_free; | ||
529 | |||
530 | struct { | ||
531 | enum { | ||
532 | NOUVEAU_GART_NONE = 0, | ||
533 | NOUVEAU_GART_AGP, | ||
534 | NOUVEAU_GART_SGDMA | ||
535 | } type; | ||
536 | uint64_t aper_base; | ||
537 | uint64_t aper_size; | ||
538 | uint64_t aper_free; | ||
539 | |||
540 | struct nouveau_gpuobj *sg_ctxdma; | ||
541 | struct page *sg_dummy_page; | ||
542 | dma_addr_t sg_dummy_bus; | ||
543 | |||
544 | /* nottm hack */ | ||
545 | struct drm_ttm_backend *sg_be; | ||
546 | unsigned long sg_handle; | ||
547 | } gart_info; | ||
548 | |||
549 | /* G8x/G9x virtual address space */ | ||
550 | uint64_t vm_gart_base; | ||
551 | uint64_t vm_gart_size; | ||
552 | uint64_t vm_vram_base; | ||
553 | uint64_t vm_vram_size; | ||
554 | uint64_t vm_end; | ||
555 | struct nouveau_gpuobj *vm_vram_pt[NV50_VM_VRAM_NR]; | ||
556 | int vm_vram_pt_nr; | ||
557 | |||
558 | /* the mtrr covering the FB */ | ||
559 | int fb_mtrr; | ||
560 | |||
561 | struct mem_block *ramin_heap; | ||
562 | |||
563 | /* context table pointed to be NV_PGRAPH_CHANNEL_CTX_TABLE (0x400780) */ | ||
564 | uint32_t ctx_table_size; | ||
565 | struct nouveau_gpuobj_ref *ctx_table; | ||
566 | |||
567 | struct list_head gpuobj_list; | ||
568 | |||
569 | struct nvbios VBIOS; | ||
570 | struct nouveau_bios_info *vbios; | ||
571 | |||
572 | struct nv04_mode_state mode_reg; | ||
573 | struct nv04_mode_state saved_reg; | ||
574 | uint32_t saved_vga_font[4][16384]; | ||
575 | uint32_t crtc_owner; | ||
576 | uint32_t dac_users[4]; | ||
577 | |||
578 | struct nouveau_suspend_resume { | ||
579 | uint32_t fifo_mode; | ||
580 | uint32_t graph_ctx_control; | ||
581 | uint32_t graph_state; | ||
582 | uint32_t *ramin_copy; | ||
583 | uint64_t ramin_size; | ||
584 | } susres; | ||
585 | |||
586 | struct backlight_device *backlight; | ||
587 | bool acpi_dsm; | ||
588 | |||
589 | struct nouveau_channel *evo; | ||
590 | |||
591 | struct { | ||
592 | struct dentry *channel_root; | ||
593 | } debugfs; | ||
594 | }; | ||
595 | |||
596 | static inline struct drm_nouveau_private * | ||
597 | nouveau_bdev(struct ttm_bo_device *bd) | ||
598 | { | ||
599 | return container_of(bd, struct drm_nouveau_private, ttm.bdev); | ||
600 | } | ||
601 | |||
602 | static inline int | ||
603 | nouveau_bo_ref(struct nouveau_bo *ref, struct nouveau_bo **pnvbo) | ||
604 | { | ||
605 | struct nouveau_bo *prev; | ||
606 | |||
607 | if (!pnvbo) | ||
608 | return -EINVAL; | ||
609 | prev = *pnvbo; | ||
610 | |||
611 | *pnvbo = ref ? nouveau_bo(ttm_bo_reference(&ref->bo)) : NULL; | ||
612 | if (prev) { | ||
613 | struct ttm_buffer_object *bo = &prev->bo; | ||
614 | |||
615 | ttm_bo_unref(&bo); | ||
616 | } | ||
617 | |||
618 | return 0; | ||
619 | } | ||
620 | |||
621 | #define NOUVEAU_CHECK_INITIALISED_WITH_RETURN do { \ | ||
622 | struct drm_nouveau_private *nv = dev->dev_private; \ | ||
623 | if (nv->init_state != NOUVEAU_CARD_INIT_DONE) { \ | ||
624 | NV_ERROR(dev, "called without init\n"); \ | ||
625 | return -EINVAL; \ | ||
626 | } \ | ||
627 | } while (0) | ||
628 | |||
629 | #define NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(id, cl, ch) do { \ | ||
630 | struct drm_nouveau_private *nv = dev->dev_private; \ | ||
631 | if (!nouveau_channel_owner(dev, (cl), (id))) { \ | ||
632 | NV_ERROR(dev, "pid %d doesn't own channel %d\n", \ | ||
633 | DRM_CURRENTPID, (id)); \ | ||
634 | return -EPERM; \ | ||
635 | } \ | ||
636 | (ch) = nv->fifos[(id)]; \ | ||
637 | } while (0) | ||
638 | |||
639 | /* nouveau_drv.c */ | ||
640 | extern int nouveau_noagp; | ||
641 | extern int nouveau_duallink; | ||
642 | extern int nouveau_uscript_lvds; | ||
643 | extern int nouveau_uscript_tmds; | ||
644 | extern int nouveau_vram_pushbuf; | ||
645 | extern int nouveau_vram_notify; | ||
646 | extern int nouveau_fbpercrtc; | ||
647 | extern char *nouveau_tv_norm; | ||
648 | extern int nouveau_reg_debug; | ||
649 | extern char *nouveau_vbios; | ||
650 | |||
651 | /* nouveau_state.c */ | ||
652 | extern void nouveau_preclose(struct drm_device *dev, struct drm_file *); | ||
653 | extern int nouveau_load(struct drm_device *, unsigned long flags); | ||
654 | extern int nouveau_firstopen(struct drm_device *); | ||
655 | extern void nouveau_lastclose(struct drm_device *); | ||
656 | extern int nouveau_unload(struct drm_device *); | ||
657 | extern int nouveau_ioctl_getparam(struct drm_device *, void *data, | ||
658 | struct drm_file *); | ||
659 | extern int nouveau_ioctl_setparam(struct drm_device *, void *data, | ||
660 | struct drm_file *); | ||
661 | extern bool nouveau_wait_until(struct drm_device *, uint64_t timeout, | ||
662 | uint32_t reg, uint32_t mask, uint32_t val); | ||
663 | extern bool nouveau_wait_for_idle(struct drm_device *); | ||
664 | extern int nouveau_card_init(struct drm_device *); | ||
665 | extern int nouveau_ioctl_card_init(struct drm_device *, void *data, | ||
666 | struct drm_file *); | ||
667 | extern int nouveau_ioctl_suspend(struct drm_device *, void *data, | ||
668 | struct drm_file *); | ||
669 | extern int nouveau_ioctl_resume(struct drm_device *, void *data, | ||
670 | struct drm_file *); | ||
671 | |||
672 | /* nouveau_mem.c */ | ||
673 | extern int nouveau_mem_init_heap(struct mem_block **, uint64_t start, | ||
674 | uint64_t size); | ||
675 | extern struct mem_block *nouveau_mem_alloc_block(struct mem_block *, | ||
676 | uint64_t size, int align2, | ||
677 | struct drm_file *, int tail); | ||
678 | extern void nouveau_mem_takedown(struct mem_block **heap); | ||
679 | extern void nouveau_mem_free_block(struct mem_block *); | ||
680 | extern uint64_t nouveau_mem_fb_amount(struct drm_device *); | ||
681 | extern void nouveau_mem_release(struct drm_file *, struct mem_block *heap); | ||
682 | extern int nouveau_mem_init(struct drm_device *); | ||
683 | extern int nouveau_mem_init_agp(struct drm_device *); | ||
684 | extern void nouveau_mem_close(struct drm_device *); | ||
685 | extern int nv50_mem_vm_bind_linear(struct drm_device *, uint64_t virt, | ||
686 | uint32_t size, uint32_t flags, | ||
687 | uint64_t phys); | ||
688 | extern void nv50_mem_vm_unbind(struct drm_device *, uint64_t virt, | ||
689 | uint32_t size); | ||
690 | |||
691 | /* nouveau_notifier.c */ | ||
692 | extern int nouveau_notifier_init_channel(struct nouveau_channel *); | ||
693 | extern void nouveau_notifier_takedown_channel(struct nouveau_channel *); | ||
694 | extern int nouveau_notifier_alloc(struct nouveau_channel *, uint32_t handle, | ||
695 | int cout, uint32_t *offset); | ||
696 | extern int nouveau_notifier_offset(struct nouveau_gpuobj *, uint32_t *); | ||
697 | extern int nouveau_ioctl_notifier_alloc(struct drm_device *, void *data, | ||
698 | struct drm_file *); | ||
699 | extern int nouveau_ioctl_notifier_free(struct drm_device *, void *data, | ||
700 | struct drm_file *); | ||
701 | |||
702 | /* nouveau_channel.c */ | ||
703 | extern struct drm_ioctl_desc nouveau_ioctls[]; | ||
704 | extern int nouveau_max_ioctl; | ||
705 | extern void nouveau_channel_cleanup(struct drm_device *, struct drm_file *); | ||
706 | extern int nouveau_channel_owner(struct drm_device *, struct drm_file *, | ||
707 | int channel); | ||
708 | extern int nouveau_channel_alloc(struct drm_device *dev, | ||
709 | struct nouveau_channel **chan, | ||
710 | struct drm_file *file_priv, | ||
711 | uint32_t fb_ctxdma, uint32_t tt_ctxdma); | ||
712 | extern void nouveau_channel_free(struct nouveau_channel *); | ||
713 | extern int nouveau_channel_idle(struct nouveau_channel *chan); | ||
714 | |||
715 | /* nouveau_object.c */ | ||
716 | extern int nouveau_gpuobj_early_init(struct drm_device *); | ||
717 | extern int nouveau_gpuobj_init(struct drm_device *); | ||
718 | extern void nouveau_gpuobj_takedown(struct drm_device *); | ||
719 | extern void nouveau_gpuobj_late_takedown(struct drm_device *); | ||
720 | extern int nouveau_gpuobj_suspend(struct drm_device *dev); | ||
721 | extern void nouveau_gpuobj_suspend_cleanup(struct drm_device *dev); | ||
722 | extern void nouveau_gpuobj_resume(struct drm_device *dev); | ||
723 | extern int nouveau_gpuobj_channel_init(struct nouveau_channel *, | ||
724 | uint32_t vram_h, uint32_t tt_h); | ||
725 | extern void nouveau_gpuobj_channel_takedown(struct nouveau_channel *); | ||
726 | extern int nouveau_gpuobj_new(struct drm_device *, struct nouveau_channel *, | ||
727 | uint32_t size, int align, uint32_t flags, | ||
728 | struct nouveau_gpuobj **); | ||
729 | extern int nouveau_gpuobj_del(struct drm_device *, struct nouveau_gpuobj **); | ||
730 | extern int nouveau_gpuobj_ref_add(struct drm_device *, struct nouveau_channel *, | ||
731 | uint32_t handle, struct nouveau_gpuobj *, | ||
732 | struct nouveau_gpuobj_ref **); | ||
733 | extern int nouveau_gpuobj_ref_del(struct drm_device *, | ||
734 | struct nouveau_gpuobj_ref **); | ||
735 | extern int nouveau_gpuobj_ref_find(struct nouveau_channel *, uint32_t handle, | ||
736 | struct nouveau_gpuobj_ref **ref_ret); | ||
737 | extern int nouveau_gpuobj_new_ref(struct drm_device *, | ||
738 | struct nouveau_channel *alloc_chan, | ||
739 | struct nouveau_channel *ref_chan, | ||
740 | uint32_t handle, uint32_t size, int align, | ||
741 | uint32_t flags, struct nouveau_gpuobj_ref **); | ||
742 | extern int nouveau_gpuobj_new_fake(struct drm_device *, | ||
743 | uint32_t p_offset, uint32_t b_offset, | ||
744 | uint32_t size, uint32_t flags, | ||
745 | struct nouveau_gpuobj **, | ||
746 | struct nouveau_gpuobj_ref**); | ||
747 | extern int nouveau_gpuobj_dma_new(struct nouveau_channel *, int class, | ||
748 | uint64_t offset, uint64_t size, int access, | ||
749 | int target, struct nouveau_gpuobj **); | ||
750 | extern int nouveau_gpuobj_gart_dma_new(struct nouveau_channel *, | ||
751 | uint64_t offset, uint64_t size, | ||
752 | int access, struct nouveau_gpuobj **, | ||
753 | uint32_t *o_ret); | ||
754 | extern int nouveau_gpuobj_gr_new(struct nouveau_channel *, int class, | ||
755 | struct nouveau_gpuobj **); | ||
756 | extern int nouveau_ioctl_grobj_alloc(struct drm_device *, void *data, | ||
757 | struct drm_file *); | ||
758 | extern int nouveau_ioctl_gpuobj_free(struct drm_device *, void *data, | ||
759 | struct drm_file *); | ||
760 | |||
761 | /* nouveau_irq.c */ | ||
762 | extern irqreturn_t nouveau_irq_handler(DRM_IRQ_ARGS); | ||
763 | extern void nouveau_irq_preinstall(struct drm_device *); | ||
764 | extern int nouveau_irq_postinstall(struct drm_device *); | ||
765 | extern void nouveau_irq_uninstall(struct drm_device *); | ||
766 | |||
767 | /* nouveau_sgdma.c */ | ||
768 | extern int nouveau_sgdma_init(struct drm_device *); | ||
769 | extern void nouveau_sgdma_takedown(struct drm_device *); | ||
770 | extern int nouveau_sgdma_get_page(struct drm_device *, uint32_t offset, | ||
771 | uint32_t *page); | ||
772 | extern struct ttm_backend *nouveau_sgdma_init_ttm(struct drm_device *); | ||
773 | |||
774 | /* nouveau_debugfs.c */ | ||
775 | #if defined(CONFIG_DRM_NOUVEAU_DEBUG) | ||
776 | extern int nouveau_debugfs_init(struct drm_minor *); | ||
777 | extern void nouveau_debugfs_takedown(struct drm_minor *); | ||
778 | extern int nouveau_debugfs_channel_init(struct nouveau_channel *); | ||
779 | extern void nouveau_debugfs_channel_fini(struct nouveau_channel *); | ||
780 | #else | ||
781 | static inline int | ||
782 | nouveau_debugfs_init(struct drm_minor *minor) | ||
783 | { | ||
784 | return 0; | ||
785 | } | ||
786 | |||
787 | static inline void nouveau_debugfs_takedown(struct drm_minor *minor) | ||
788 | { | ||
789 | } | ||
790 | |||
791 | static inline int | ||
792 | nouveau_debugfs_channel_init(struct nouveau_channel *chan) | ||
793 | { | ||
794 | return 0; | ||
795 | } | ||
796 | |||
797 | static inline void | ||
798 | nouveau_debugfs_channel_fini(struct nouveau_channel *chan) | ||
799 | { | ||
800 | } | ||
801 | #endif | ||
802 | |||
803 | /* nouveau_dma.c */ | ||
804 | extern int nouveau_dma_init(struct nouveau_channel *); | ||
805 | extern int nouveau_dma_wait(struct nouveau_channel *, int size); | ||
806 | |||
807 | /* nouveau_acpi.c */ | ||
808 | #ifdef CONFIG_ACPI | ||
809 | extern int nouveau_hybrid_setup(struct drm_device *dev); | ||
810 | extern bool nouveau_dsm_probe(struct drm_device *dev); | ||
811 | #else | ||
812 | static inline int nouveau_hybrid_setup(struct drm_device *dev) | ||
813 | { | ||
814 | return 0; | ||
815 | } | ||
816 | static inline bool nouveau_dsm_probe(struct drm_device *dev) | ||
817 | { | ||
818 | return false; | ||
819 | } | ||
820 | #endif | ||
821 | |||
822 | /* nouveau_backlight.c */ | ||
823 | #ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT | ||
824 | extern int nouveau_backlight_init(struct drm_device *); | ||
825 | extern void nouveau_backlight_exit(struct drm_device *); | ||
826 | #else | ||
827 | static inline int nouveau_backlight_init(struct drm_device *dev) | ||
828 | { | ||
829 | return 0; | ||
830 | } | ||
831 | |||
832 | static inline void nouveau_backlight_exit(struct drm_device *dev) { } | ||
833 | #endif | ||
834 | |||
835 | /* nouveau_bios.c */ | ||
836 | extern int nouveau_bios_init(struct drm_device *); | ||
837 | extern void nouveau_bios_takedown(struct drm_device *dev); | ||
838 | extern int nouveau_run_vbios_init(struct drm_device *); | ||
839 | extern void nouveau_bios_run_init_table(struct drm_device *, uint16_t table, | ||
840 | struct dcb_entry *); | ||
841 | extern struct dcb_gpio_entry *nouveau_bios_gpio_entry(struct drm_device *, | ||
842 | enum dcb_gpio_tag); | ||
843 | extern struct dcb_connector_table_entry * | ||
844 | nouveau_bios_connector_entry(struct drm_device *, int index); | ||
845 | extern int get_pll_limits(struct drm_device *, uint32_t limit_match, | ||
846 | struct pll_lims *); | ||
847 | extern int nouveau_bios_run_display_table(struct drm_device *, | ||
848 | struct dcb_entry *, | ||
849 | uint32_t script, int pxclk); | ||
850 | extern void *nouveau_bios_dp_table(struct drm_device *, struct dcb_entry *, | ||
851 | int *length); | ||
852 | extern bool nouveau_bios_fp_mode(struct drm_device *, struct drm_display_mode *); | ||
853 | extern uint8_t *nouveau_bios_embedded_edid(struct drm_device *); | ||
854 | extern int nouveau_bios_parse_lvds_table(struct drm_device *, int pxclk, | ||
855 | bool *dl, bool *if_is_24bit); | ||
856 | extern int run_tmds_table(struct drm_device *, struct dcb_entry *, | ||
857 | int head, int pxclk); | ||
858 | extern int call_lvds_script(struct drm_device *, struct dcb_entry *, int head, | ||
859 | enum LVDS_script, int pxclk); | ||
860 | |||
861 | /* nouveau_ttm.c */ | ||
862 | int nouveau_ttm_global_init(struct drm_nouveau_private *); | ||
863 | void nouveau_ttm_global_release(struct drm_nouveau_private *); | ||
864 | int nouveau_ttm_mmap(struct file *, struct vm_area_struct *); | ||
865 | |||
866 | /* nouveau_dp.c */ | ||
867 | int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr, | ||
868 | uint8_t *data, int data_nr); | ||
869 | bool nouveau_dp_detect(struct drm_encoder *); | ||
870 | bool nouveau_dp_link_train(struct drm_encoder *); | ||
871 | |||
872 | /* nv04_fb.c */ | ||
873 | extern int nv04_fb_init(struct drm_device *); | ||
874 | extern void nv04_fb_takedown(struct drm_device *); | ||
875 | |||
876 | /* nv10_fb.c */ | ||
877 | extern int nv10_fb_init(struct drm_device *); | ||
878 | extern void nv10_fb_takedown(struct drm_device *); | ||
879 | |||
880 | /* nv40_fb.c */ | ||
881 | extern int nv40_fb_init(struct drm_device *); | ||
882 | extern void nv40_fb_takedown(struct drm_device *); | ||
883 | |||
884 | /* nv04_fifo.c */ | ||
885 | extern int nv04_fifo_init(struct drm_device *); | ||
886 | extern void nv04_fifo_disable(struct drm_device *); | ||
887 | extern void nv04_fifo_enable(struct drm_device *); | ||
888 | extern bool nv04_fifo_reassign(struct drm_device *, bool); | ||
889 | extern int nv04_fifo_channel_id(struct drm_device *); | ||
890 | extern int nv04_fifo_create_context(struct nouveau_channel *); | ||
891 | extern void nv04_fifo_destroy_context(struct nouveau_channel *); | ||
892 | extern int nv04_fifo_load_context(struct nouveau_channel *); | ||
893 | extern int nv04_fifo_unload_context(struct drm_device *); | ||
894 | |||
895 | /* nv10_fifo.c */ | ||
896 | extern int nv10_fifo_init(struct drm_device *); | ||
897 | extern int nv10_fifo_channel_id(struct drm_device *); | ||
898 | extern int nv10_fifo_create_context(struct nouveau_channel *); | ||
899 | extern void nv10_fifo_destroy_context(struct nouveau_channel *); | ||
900 | extern int nv10_fifo_load_context(struct nouveau_channel *); | ||
901 | extern int nv10_fifo_unload_context(struct drm_device *); | ||
902 | |||
903 | /* nv40_fifo.c */ | ||
904 | extern int nv40_fifo_init(struct drm_device *); | ||
905 | extern int nv40_fifo_create_context(struct nouveau_channel *); | ||
906 | extern void nv40_fifo_destroy_context(struct nouveau_channel *); | ||
907 | extern int nv40_fifo_load_context(struct nouveau_channel *); | ||
908 | extern int nv40_fifo_unload_context(struct drm_device *); | ||
909 | |||
910 | /* nv50_fifo.c */ | ||
911 | extern int nv50_fifo_init(struct drm_device *); | ||
912 | extern void nv50_fifo_takedown(struct drm_device *); | ||
913 | extern int nv50_fifo_channel_id(struct drm_device *); | ||
914 | extern int nv50_fifo_create_context(struct nouveau_channel *); | ||
915 | extern void nv50_fifo_destroy_context(struct nouveau_channel *); | ||
916 | extern int nv50_fifo_load_context(struct nouveau_channel *); | ||
917 | extern int nv50_fifo_unload_context(struct drm_device *); | ||
918 | |||
919 | /* nv04_graph.c */ | ||
920 | extern struct nouveau_pgraph_object_class nv04_graph_grclass[]; | ||
921 | extern int nv04_graph_init(struct drm_device *); | ||
922 | extern void nv04_graph_takedown(struct drm_device *); | ||
923 | extern void nv04_graph_fifo_access(struct drm_device *, bool); | ||
924 | extern struct nouveau_channel *nv04_graph_channel(struct drm_device *); | ||
925 | extern int nv04_graph_create_context(struct nouveau_channel *); | ||
926 | extern void nv04_graph_destroy_context(struct nouveau_channel *); | ||
927 | extern int nv04_graph_load_context(struct nouveau_channel *); | ||
928 | extern int nv04_graph_unload_context(struct drm_device *); | ||
929 | extern void nv04_graph_context_switch(struct drm_device *); | ||
930 | |||
931 | /* nv10_graph.c */ | ||
932 | extern struct nouveau_pgraph_object_class nv10_graph_grclass[]; | ||
933 | extern int nv10_graph_init(struct drm_device *); | ||
934 | extern void nv10_graph_takedown(struct drm_device *); | ||
935 | extern struct nouveau_channel *nv10_graph_channel(struct drm_device *); | ||
936 | extern int nv10_graph_create_context(struct nouveau_channel *); | ||
937 | extern void nv10_graph_destroy_context(struct nouveau_channel *); | ||
938 | extern int nv10_graph_load_context(struct nouveau_channel *); | ||
939 | extern int nv10_graph_unload_context(struct drm_device *); | ||
940 | extern void nv10_graph_context_switch(struct drm_device *); | ||
941 | |||
942 | /* nv20_graph.c */ | ||
943 | extern struct nouveau_pgraph_object_class nv20_graph_grclass[]; | ||
944 | extern struct nouveau_pgraph_object_class nv30_graph_grclass[]; | ||
945 | extern int nv20_graph_create_context(struct nouveau_channel *); | ||
946 | extern void nv20_graph_destroy_context(struct nouveau_channel *); | ||
947 | extern int nv20_graph_load_context(struct nouveau_channel *); | ||
948 | extern int nv20_graph_unload_context(struct drm_device *); | ||
949 | extern int nv20_graph_init(struct drm_device *); | ||
950 | extern void nv20_graph_takedown(struct drm_device *); | ||
951 | extern int nv30_graph_init(struct drm_device *); | ||
952 | |||
953 | /* nv40_graph.c */ | ||
954 | extern struct nouveau_pgraph_object_class nv40_graph_grclass[]; | ||
955 | extern int nv40_graph_init(struct drm_device *); | ||
956 | extern void nv40_graph_takedown(struct drm_device *); | ||
957 | extern struct nouveau_channel *nv40_graph_channel(struct drm_device *); | ||
958 | extern int nv40_graph_create_context(struct nouveau_channel *); | ||
959 | extern void nv40_graph_destroy_context(struct nouveau_channel *); | ||
960 | extern int nv40_graph_load_context(struct nouveau_channel *); | ||
961 | extern int nv40_graph_unload_context(struct drm_device *); | ||
962 | extern int nv40_grctx_init(struct drm_device *); | ||
963 | extern void nv40_grctx_fini(struct drm_device *); | ||
964 | extern void nv40_grctx_vals_load(struct drm_device *, struct nouveau_gpuobj *); | ||
965 | |||
966 | /* nv50_graph.c */ | ||
967 | extern struct nouveau_pgraph_object_class nv50_graph_grclass[]; | ||
968 | extern int nv50_graph_init(struct drm_device *); | ||
969 | extern void nv50_graph_takedown(struct drm_device *); | ||
970 | extern void nv50_graph_fifo_access(struct drm_device *, bool); | ||
971 | extern struct nouveau_channel *nv50_graph_channel(struct drm_device *); | ||
972 | extern int nv50_graph_create_context(struct nouveau_channel *); | ||
973 | extern void nv50_graph_destroy_context(struct nouveau_channel *); | ||
974 | extern int nv50_graph_load_context(struct nouveau_channel *); | ||
975 | extern int nv50_graph_unload_context(struct drm_device *); | ||
976 | extern void nv50_graph_context_switch(struct drm_device *); | ||
977 | |||
978 | /* nv04_instmem.c */ | ||
979 | extern int nv04_instmem_init(struct drm_device *); | ||
980 | extern void nv04_instmem_takedown(struct drm_device *); | ||
981 | extern int nv04_instmem_suspend(struct drm_device *); | ||
982 | extern void nv04_instmem_resume(struct drm_device *); | ||
983 | extern int nv04_instmem_populate(struct drm_device *, struct nouveau_gpuobj *, | ||
984 | uint32_t *size); | ||
985 | extern void nv04_instmem_clear(struct drm_device *, struct nouveau_gpuobj *); | ||
986 | extern int nv04_instmem_bind(struct drm_device *, struct nouveau_gpuobj *); | ||
987 | extern int nv04_instmem_unbind(struct drm_device *, struct nouveau_gpuobj *); | ||
988 | extern void nv04_instmem_prepare_access(struct drm_device *, bool write); | ||
989 | extern void nv04_instmem_finish_access(struct drm_device *); | ||
990 | |||
991 | /* nv50_instmem.c */ | ||
992 | extern int nv50_instmem_init(struct drm_device *); | ||
993 | extern void nv50_instmem_takedown(struct drm_device *); | ||
994 | extern int nv50_instmem_suspend(struct drm_device *); | ||
995 | extern void nv50_instmem_resume(struct drm_device *); | ||
996 | extern int nv50_instmem_populate(struct drm_device *, struct nouveau_gpuobj *, | ||
997 | uint32_t *size); | ||
998 | extern void nv50_instmem_clear(struct drm_device *, struct nouveau_gpuobj *); | ||
999 | extern int nv50_instmem_bind(struct drm_device *, struct nouveau_gpuobj *); | ||
1000 | extern int nv50_instmem_unbind(struct drm_device *, struct nouveau_gpuobj *); | ||
1001 | extern void nv50_instmem_prepare_access(struct drm_device *, bool write); | ||
1002 | extern void nv50_instmem_finish_access(struct drm_device *); | ||
1003 | |||
1004 | /* nv04_mc.c */ | ||
1005 | extern int nv04_mc_init(struct drm_device *); | ||
1006 | extern void nv04_mc_takedown(struct drm_device *); | ||
1007 | |||
1008 | /* nv40_mc.c */ | ||
1009 | extern int nv40_mc_init(struct drm_device *); | ||
1010 | extern void nv40_mc_takedown(struct drm_device *); | ||
1011 | |||
1012 | /* nv50_mc.c */ | ||
1013 | extern int nv50_mc_init(struct drm_device *); | ||
1014 | extern void nv50_mc_takedown(struct drm_device *); | ||
1015 | |||
1016 | /* nv04_timer.c */ | ||
1017 | extern int nv04_timer_init(struct drm_device *); | ||
1018 | extern uint64_t nv04_timer_read(struct drm_device *); | ||
1019 | extern void nv04_timer_takedown(struct drm_device *); | ||
1020 | |||
1021 | extern long nouveau_compat_ioctl(struct file *file, unsigned int cmd, | ||
1022 | unsigned long arg); | ||
1023 | |||
1024 | /* nv04_dac.c */ | ||
1025 | extern int nv04_dac_create(struct drm_device *dev, struct dcb_entry *entry); | ||
1026 | extern enum drm_connector_status nv17_dac_detect(struct drm_encoder *encoder, | ||
1027 | struct drm_connector *connector); | ||
1028 | extern int nv04_dac_output_offset(struct drm_encoder *encoder); | ||
1029 | extern void nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable); | ||
1030 | |||
1031 | /* nv04_dfp.c */ | ||
1032 | extern int nv04_dfp_create(struct drm_device *dev, struct dcb_entry *entry); | ||
1033 | extern int nv04_dfp_get_bound_head(struct drm_device *dev, struct dcb_entry *dcbent); | ||
1034 | extern void nv04_dfp_bind_head(struct drm_device *dev, struct dcb_entry *dcbent, | ||
1035 | int head, bool dl); | ||
1036 | extern void nv04_dfp_disable(struct drm_device *dev, int head); | ||
1037 | extern void nv04_dfp_update_fp_control(struct drm_encoder *encoder, int mode); | ||
1038 | |||
1039 | /* nv04_tv.c */ | ||
1040 | extern int nv04_tv_identify(struct drm_device *dev, int i2c_index); | ||
1041 | extern int nv04_tv_create(struct drm_device *dev, struct dcb_entry *entry); | ||
1042 | |||
1043 | /* nv17_tv.c */ | ||
1044 | extern int nv17_tv_create(struct drm_device *dev, struct dcb_entry *entry); | ||
1045 | extern enum drm_connector_status nv17_tv_detect(struct drm_encoder *encoder, | ||
1046 | struct drm_connector *connector, | ||
1047 | uint32_t pin_mask); | ||
1048 | |||
1049 | /* nv04_display.c */ | ||
1050 | extern int nv04_display_create(struct drm_device *); | ||
1051 | extern void nv04_display_destroy(struct drm_device *); | ||
1052 | extern void nv04_display_restore(struct drm_device *); | ||
1053 | |||
1054 | /* nv04_crtc.c */ | ||
1055 | extern int nv04_crtc_create(struct drm_device *, int index); | ||
1056 | |||
1057 | /* nouveau_bo.c */ | ||
1058 | extern struct ttm_bo_driver nouveau_bo_driver; | ||
1059 | extern int nouveau_bo_new(struct drm_device *, struct nouveau_channel *, | ||
1060 | int size, int align, uint32_t flags, | ||
1061 | uint32_t tile_mode, uint32_t tile_flags, | ||
1062 | bool no_vm, bool mappable, struct nouveau_bo **); | ||
1063 | extern int nouveau_bo_pin(struct nouveau_bo *, uint32_t flags); | ||
1064 | extern int nouveau_bo_unpin(struct nouveau_bo *); | ||
1065 | extern int nouveau_bo_map(struct nouveau_bo *); | ||
1066 | extern void nouveau_bo_unmap(struct nouveau_bo *); | ||
1067 | extern void nouveau_bo_placement_set(struct nouveau_bo *, uint32_t memtype); | ||
1068 | extern u16 nouveau_bo_rd16(struct nouveau_bo *nvbo, unsigned index); | ||
1069 | extern void nouveau_bo_wr16(struct nouveau_bo *nvbo, unsigned index, u16 val); | ||
1070 | extern u32 nouveau_bo_rd32(struct nouveau_bo *nvbo, unsigned index); | ||
1071 | extern void nouveau_bo_wr32(struct nouveau_bo *nvbo, unsigned index, u32 val); | ||
1072 | |||
1073 | /* nouveau_fence.c */ | ||
1074 | struct nouveau_fence; | ||
1075 | extern int nouveau_fence_init(struct nouveau_channel *); | ||
1076 | extern void nouveau_fence_fini(struct nouveau_channel *); | ||
1077 | extern void nouveau_fence_update(struct nouveau_channel *); | ||
1078 | extern int nouveau_fence_new(struct nouveau_channel *, struct nouveau_fence **, | ||
1079 | bool emit); | ||
1080 | extern int nouveau_fence_emit(struct nouveau_fence *); | ||
1081 | struct nouveau_channel *nouveau_fence_channel(struct nouveau_fence *); | ||
1082 | extern bool nouveau_fence_signalled(void *obj, void *arg); | ||
1083 | extern int nouveau_fence_wait(void *obj, void *arg, bool lazy, bool intr); | ||
1084 | extern int nouveau_fence_flush(void *obj, void *arg); | ||
1085 | extern void nouveau_fence_unref(void **obj); | ||
1086 | extern void *nouveau_fence_ref(void *obj); | ||
1087 | extern void nouveau_fence_handler(struct drm_device *dev, int channel); | ||
1088 | |||
1089 | /* nouveau_gem.c */ | ||
1090 | extern int nouveau_gem_new(struct drm_device *, struct nouveau_channel *, | ||
1091 | int size, int align, uint32_t flags, | ||
1092 | uint32_t tile_mode, uint32_t tile_flags, | ||
1093 | bool no_vm, bool mappable, struct nouveau_bo **); | ||
1094 | extern int nouveau_gem_object_new(struct drm_gem_object *); | ||
1095 | extern void nouveau_gem_object_del(struct drm_gem_object *); | ||
1096 | extern int nouveau_gem_ioctl_new(struct drm_device *, void *, | ||
1097 | struct drm_file *); | ||
1098 | extern int nouveau_gem_ioctl_pushbuf(struct drm_device *, void *, | ||
1099 | struct drm_file *); | ||
1100 | extern int nouveau_gem_ioctl_pushbuf_call(struct drm_device *, void *, | ||
1101 | struct drm_file *); | ||
1102 | extern int nouveau_gem_ioctl_pushbuf_call2(struct drm_device *, void *, | ||
1103 | struct drm_file *); | ||
1104 | extern int nouveau_gem_ioctl_pin(struct drm_device *, void *, | ||
1105 | struct drm_file *); | ||
1106 | extern int nouveau_gem_ioctl_unpin(struct drm_device *, void *, | ||
1107 | struct drm_file *); | ||
1108 | extern int nouveau_gem_ioctl_tile(struct drm_device *, void *, | ||
1109 | struct drm_file *); | ||
1110 | extern int nouveau_gem_ioctl_cpu_prep(struct drm_device *, void *, | ||
1111 | struct drm_file *); | ||
1112 | extern int nouveau_gem_ioctl_cpu_fini(struct drm_device *, void *, | ||
1113 | struct drm_file *); | ||
1114 | extern int nouveau_gem_ioctl_info(struct drm_device *, void *, | ||
1115 | struct drm_file *); | ||
1116 | |||
1117 | /* nv17_gpio.c */ | ||
1118 | int nv17_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag); | ||
1119 | int nv17_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state); | ||
1120 | |||
1121 | #ifndef ioread32_native | ||
1122 | #ifdef __BIG_ENDIAN | ||
1123 | #define ioread16_native ioread16be | ||
1124 | #define iowrite16_native iowrite16be | ||
1125 | #define ioread32_native ioread32be | ||
1126 | #define iowrite32_native iowrite32be | ||
1127 | #else /* def __BIG_ENDIAN */ | ||
1128 | #define ioread16_native ioread16 | ||
1129 | #define iowrite16_native iowrite16 | ||
1130 | #define ioread32_native ioread32 | ||
1131 | #define iowrite32_native iowrite32 | ||
1132 | #endif /* def __BIG_ENDIAN else */ | ||
1133 | #endif /* !ioread32_native */ | ||
1134 | |||
1135 | /* channel control reg access */ | ||
1136 | static inline u32 nvchan_rd32(struct nouveau_channel *chan, unsigned reg) | ||
1137 | { | ||
1138 | return ioread32_native(chan->user + reg); | ||
1139 | } | ||
1140 | |||
1141 | static inline void nvchan_wr32(struct nouveau_channel *chan, | ||
1142 | unsigned reg, u32 val) | ||
1143 | { | ||
1144 | iowrite32_native(val, chan->user + reg); | ||
1145 | } | ||
1146 | |||
1147 | /* register access */ | ||
1148 | static inline u32 nv_rd32(struct drm_device *dev, unsigned reg) | ||
1149 | { | ||
1150 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
1151 | return ioread32_native(dev_priv->mmio + reg); | ||
1152 | } | ||
1153 | |||
1154 | static inline void nv_wr32(struct drm_device *dev, unsigned reg, u32 val) | ||
1155 | { | ||
1156 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
1157 | iowrite32_native(val, dev_priv->mmio + reg); | ||
1158 | } | ||
1159 | |||
1160 | static inline u8 nv_rd08(struct drm_device *dev, unsigned reg) | ||
1161 | { | ||
1162 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
1163 | return ioread8(dev_priv->mmio + reg); | ||
1164 | } | ||
1165 | |||
1166 | static inline void nv_wr08(struct drm_device *dev, unsigned reg, u8 val) | ||
1167 | { | ||
1168 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
1169 | iowrite8(val, dev_priv->mmio + reg); | ||
1170 | } | ||
1171 | |||
1172 | #define nv_wait(reg, mask, val) \ | ||
1173 | nouveau_wait_until(dev, 2000000000ULL, (reg), (mask), (val)) | ||
1174 | |||
1175 | /* PRAMIN access */ | ||
1176 | static inline u32 nv_ri32(struct drm_device *dev, unsigned offset) | ||
1177 | { | ||
1178 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
1179 | return ioread32_native(dev_priv->ramin + offset); | ||
1180 | } | ||
1181 | |||
1182 | static inline void nv_wi32(struct drm_device *dev, unsigned offset, u32 val) | ||
1183 | { | ||
1184 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
1185 | iowrite32_native(val, dev_priv->ramin + offset); | ||
1186 | } | ||
1187 | |||
1188 | /* object access */ | ||
1189 | static inline u32 nv_ro32(struct drm_device *dev, struct nouveau_gpuobj *obj, | ||
1190 | unsigned index) | ||
1191 | { | ||
1192 | return nv_ri32(dev, obj->im_pramin->start + index * 4); | ||
1193 | } | ||
1194 | |||
1195 | static inline void nv_wo32(struct drm_device *dev, struct nouveau_gpuobj *obj, | ||
1196 | unsigned index, u32 val) | ||
1197 | { | ||
1198 | nv_wi32(dev, obj->im_pramin->start + index * 4, val); | ||
1199 | } | ||
1200 | |||
1201 | /* | ||
1202 | * Logging | ||
1203 | * Argument d is (struct drm_device *). | ||
1204 | */ | ||
1205 | #define NV_PRINTK(level, d, fmt, arg...) \ | ||
1206 | printk(level "[" DRM_NAME "] " DRIVER_NAME " %s: " fmt, \ | ||
1207 | pci_name(d->pdev), ##arg) | ||
1208 | #ifndef NV_DEBUG_NOTRACE | ||
1209 | #define NV_DEBUG(d, fmt, arg...) do { \ | ||
1210 | if (drm_debug) { \ | ||
1211 | NV_PRINTK(KERN_DEBUG, d, "%s:%d - " fmt, __func__, \ | ||
1212 | __LINE__, ##arg); \ | ||
1213 | } \ | ||
1214 | } while (0) | ||
1215 | #else | ||
1216 | #define NV_DEBUG(d, fmt, arg...) do { \ | ||
1217 | if (drm_debug) \ | ||
1218 | NV_PRINTK(KERN_DEBUG, d, fmt, ##arg); \ | ||
1219 | } while (0) | ||
1220 | #endif | ||
1221 | #define NV_ERROR(d, fmt, arg...) NV_PRINTK(KERN_ERR, d, fmt, ##arg) | ||
1222 | #define NV_INFO(d, fmt, arg...) NV_PRINTK(KERN_INFO, d, fmt, ##arg) | ||
1223 | #define NV_TRACEWARN(d, fmt, arg...) NV_PRINTK(KERN_NOTICE, d, fmt, ##arg) | ||
1224 | #define NV_TRACE(d, fmt, arg...) NV_PRINTK(KERN_INFO, d, fmt, ##arg) | ||
1225 | #define NV_WARN(d, fmt, arg...) NV_PRINTK(KERN_WARNING, d, fmt, ##arg) | ||
1226 | |||
1227 | /* nouveau_reg_debug bitmask */ | ||
1228 | enum { | ||
1229 | NOUVEAU_REG_DEBUG_MC = 0x1, | ||
1230 | NOUVEAU_REG_DEBUG_VIDEO = 0x2, | ||
1231 | NOUVEAU_REG_DEBUG_FB = 0x4, | ||
1232 | NOUVEAU_REG_DEBUG_EXTDEV = 0x8, | ||
1233 | NOUVEAU_REG_DEBUG_CRTC = 0x10, | ||
1234 | NOUVEAU_REG_DEBUG_RAMDAC = 0x20, | ||
1235 | NOUVEAU_REG_DEBUG_VGACRTC = 0x40, | ||
1236 | NOUVEAU_REG_DEBUG_RMVIO = 0x80, | ||
1237 | NOUVEAU_REG_DEBUG_VGAATTR = 0x100, | ||
1238 | NOUVEAU_REG_DEBUG_EVO = 0x200, | ||
1239 | }; | ||
1240 | |||
1241 | #define NV_REG_DEBUG(type, dev, fmt, arg...) do { \ | ||
1242 | if (nouveau_reg_debug & NOUVEAU_REG_DEBUG_##type) \ | ||
1243 | NV_PRINTK(KERN_DEBUG, dev, "%s: " fmt, __func__, ##arg); \ | ||
1244 | } while (0) | ||
1245 | |||
1246 | static inline bool | ||
1247 | nv_two_heads(struct drm_device *dev) | ||
1248 | { | ||
1249 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
1250 | const int impl = dev->pci_device & 0x0ff0; | ||
1251 | |||
1252 | if (dev_priv->card_type >= NV_10 && impl != 0x0100 && | ||
1253 | impl != 0x0150 && impl != 0x01a0 && impl != 0x0200) | ||
1254 | return true; | ||
1255 | |||
1256 | return false; | ||
1257 | } | ||
1258 | |||
1259 | static inline bool | ||
1260 | nv_gf4_disp_arch(struct drm_device *dev) | ||
1261 | { | ||
1262 | return nv_two_heads(dev) && (dev->pci_device & 0x0ff0) != 0x0110; | ||
1263 | } | ||
1264 | |||
1265 | static inline bool | ||
1266 | nv_two_reg_pll(struct drm_device *dev) | ||
1267 | { | ||
1268 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
1269 | const int impl = dev->pci_device & 0x0ff0; | ||
1270 | |||
1271 | if (impl == 0x0310 || impl == 0x0340 || dev_priv->card_type >= NV_40) | ||
1272 | return true; | ||
1273 | return false; | ||
1274 | } | ||
1275 | |||
1276 | #define NV50_NVSW 0x0000506e | ||
1277 | #define NV50_NVSW_DMA_SEMAPHORE 0x00000060 | ||
1278 | #define NV50_NVSW_SEMAPHORE_OFFSET 0x00000064 | ||
1279 | #define NV50_NVSW_SEMAPHORE_ACQUIRE 0x00000068 | ||
1280 | #define NV50_NVSW_SEMAPHORE_RELEASE 0x0000006c | ||
1281 | #define NV50_NVSW_DMA_VBLSEM 0x0000018c | ||
1282 | #define NV50_NVSW_VBLSEM_OFFSET 0x00000400 | ||
1283 | #define NV50_NVSW_VBLSEM_RELEASE_VALUE 0x00000404 | ||
1284 | #define NV50_NVSW_VBLSEM_RELEASE 0x00000408 | ||
1285 | |||
1286 | #endif /* __NOUVEAU_DRV_H__ */ | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h new file mode 100644 index 000000000000..bc4a24029ed1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h | |||
@@ -0,0 +1,91 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2008 Maarten Maathuis. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #ifndef __NOUVEAU_ENCODER_H__ | ||
28 | #define __NOUVEAU_ENCODER_H__ | ||
29 | |||
30 | #include "drm_encoder_slave.h" | ||
31 | #include "nouveau_drv.h" | ||
32 | |||
33 | #define NV_DPMS_CLEARED 0x80 | ||
34 | |||
35 | struct nouveau_encoder { | ||
36 | struct drm_encoder_slave base; | ||
37 | |||
38 | struct dcb_entry *dcb; | ||
39 | int or; | ||
40 | |||
41 | struct drm_display_mode mode; | ||
42 | int last_dpms; | ||
43 | |||
44 | struct nv04_output_reg restore; | ||
45 | |||
46 | void (*disconnect)(struct nouveau_encoder *encoder); | ||
47 | |||
48 | union { | ||
49 | struct { | ||
50 | int dpcd_version; | ||
51 | int link_nr; | ||
52 | int link_bw; | ||
53 | } dp; | ||
54 | }; | ||
55 | }; | ||
56 | |||
57 | static inline struct nouveau_encoder *nouveau_encoder(struct drm_encoder *enc) | ||
58 | { | ||
59 | struct drm_encoder_slave *slave = to_encoder_slave(enc); | ||
60 | |||
61 | return container_of(slave, struct nouveau_encoder, base); | ||
62 | } | ||
63 | |||
64 | static inline struct drm_encoder *to_drm_encoder(struct nouveau_encoder *enc) | ||
65 | { | ||
66 | return &enc->base.base; | ||
67 | } | ||
68 | |||
69 | struct nouveau_connector * | ||
70 | nouveau_encoder_connector_get(struct nouveau_encoder *encoder); | ||
71 | int nv50_sor_create(struct drm_device *dev, struct dcb_entry *entry); | ||
72 | int nv50_dac_create(struct drm_device *dev, struct dcb_entry *entry); | ||
73 | |||
74 | struct bit_displayport_encoder_table { | ||
75 | uint32_t match; | ||
76 | uint8_t record_nr; | ||
77 | uint8_t unknown; | ||
78 | uint16_t script0; | ||
79 | uint16_t script1; | ||
80 | uint16_t unknown_table; | ||
81 | } __attribute__ ((packed)); | ||
82 | |||
83 | struct bit_displayport_encoder_table_entry { | ||
84 | uint8_t vs_level; | ||
85 | uint8_t pre_level; | ||
86 | uint8_t reg0; | ||
87 | uint8_t reg1; | ||
88 | uint8_t reg2; | ||
89 | } __attribute__ ((packed)); | ||
90 | |||
91 | #endif /* __NOUVEAU_ENCODER_H__ */ | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_fb.h b/drivers/gpu/drm/nouveau/nouveau_fb.h new file mode 100644 index 000000000000..4a3f31aa1949 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_fb.h | |||
@@ -0,0 +1,47 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2008 Maarten Maathuis. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #ifndef __NOUVEAU_FB_H__ | ||
28 | #define __NOUVEAU_FB_H__ | ||
29 | |||
30 | struct nouveau_framebuffer { | ||
31 | struct drm_framebuffer base; | ||
32 | struct nouveau_bo *nvbo; | ||
33 | }; | ||
34 | |||
35 | static inline struct nouveau_framebuffer * | ||
36 | nouveau_framebuffer(struct drm_framebuffer *fb) | ||
37 | { | ||
38 | return container_of(fb, struct nouveau_framebuffer, base); | ||
39 | } | ||
40 | |||
41 | extern const struct drm_mode_config_funcs nouveau_mode_config_funcs; | ||
42 | |||
43 | struct drm_framebuffer * | ||
44 | nouveau_framebuffer_create(struct drm_device *, struct nouveau_bo *, | ||
45 | struct drm_mode_fb_cmd *); | ||
46 | |||
47 | #endif /* __NOUVEAU_FB_H__ */ | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c new file mode 100644 index 000000000000..36e8c5e4503a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c | |||
@@ -0,0 +1,380 @@ | |||
1 | /* | ||
2 | * Copyright © 2007 David Airlie | ||
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 (including the next | ||
12 | * paragraph) shall be included in all copies or substantial portions of the | ||
13 | * Software. | ||
14 | * | ||
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
21 | * DEALINGS IN THE SOFTWARE. | ||
22 | * | ||
23 | * Authors: | ||
24 | * David Airlie | ||
25 | */ | ||
26 | |||
27 | #include <linux/module.h> | ||
28 | #include <linux/kernel.h> | ||
29 | #include <linux/errno.h> | ||
30 | #include <linux/string.h> | ||
31 | #include <linux/mm.h> | ||
32 | #include <linux/tty.h> | ||
33 | #include <linux/slab.h> | ||
34 | #include <linux/sysrq.h> | ||
35 | #include <linux/delay.h> | ||
36 | #include <linux/fb.h> | ||
37 | #include <linux/init.h> | ||
38 | #include <linux/screen_info.h> | ||
39 | |||
40 | #include "drmP.h" | ||
41 | #include "drm.h" | ||
42 | #include "drm_crtc.h" | ||
43 | #include "drm_crtc_helper.h" | ||
44 | #include "drm_fb_helper.h" | ||
45 | #include "nouveau_drv.h" | ||
46 | #include "nouveau_drm.h" | ||
47 | #include "nouveau_crtc.h" | ||
48 | #include "nouveau_fb.h" | ||
49 | #include "nouveau_fbcon.h" | ||
50 | #include "nouveau_dma.h" | ||
51 | |||
52 | static int | ||
53 | nouveau_fbcon_sync(struct fb_info *info) | ||
54 | { | ||
55 | struct nouveau_fbcon_par *par = info->par; | ||
56 | struct drm_device *dev = par->dev; | ||
57 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
58 | struct nouveau_channel *chan = dev_priv->channel; | ||
59 | int ret, i; | ||
60 | |||
61 | if (!chan->accel_done || | ||
62 | info->state != FBINFO_STATE_RUNNING || | ||
63 | info->flags & FBINFO_HWACCEL_DISABLED) | ||
64 | return 0; | ||
65 | |||
66 | if (RING_SPACE(chan, 4)) { | ||
67 | NV_ERROR(dev, "GPU lockup - switching to software fbcon\n"); | ||
68 | info->flags |= FBINFO_HWACCEL_DISABLED; | ||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | BEGIN_RING(chan, 0, 0x0104, 1); | ||
73 | OUT_RING(chan, 0); | ||
74 | BEGIN_RING(chan, 0, 0x0100, 1); | ||
75 | OUT_RING(chan, 0); | ||
76 | nouveau_bo_wr32(chan->notifier_bo, chan->m2mf_ntfy + 3, 0xffffffff); | ||
77 | FIRE_RING(chan); | ||
78 | |||
79 | ret = -EBUSY; | ||
80 | for (i = 0; i < 100000; i++) { | ||
81 | if (!nouveau_bo_rd32(chan->notifier_bo, chan->m2mf_ntfy + 3)) { | ||
82 | ret = 0; | ||
83 | break; | ||
84 | } | ||
85 | DRM_UDELAY(1); | ||
86 | } | ||
87 | |||
88 | if (ret) { | ||
89 | NV_ERROR(dev, "GPU lockup - switching to software fbcon\n"); | ||
90 | info->flags |= FBINFO_HWACCEL_DISABLED; | ||
91 | return 0; | ||
92 | } | ||
93 | |||
94 | chan->accel_done = false; | ||
95 | return 0; | ||
96 | } | ||
97 | |||
98 | static struct fb_ops nouveau_fbcon_ops = { | ||
99 | .owner = THIS_MODULE, | ||
100 | .fb_check_var = drm_fb_helper_check_var, | ||
101 | .fb_set_par = drm_fb_helper_set_par, | ||
102 | .fb_setcolreg = drm_fb_helper_setcolreg, | ||
103 | .fb_fillrect = cfb_fillrect, | ||
104 | .fb_copyarea = cfb_copyarea, | ||
105 | .fb_imageblit = cfb_imageblit, | ||
106 | .fb_sync = nouveau_fbcon_sync, | ||
107 | .fb_pan_display = drm_fb_helper_pan_display, | ||
108 | .fb_blank = drm_fb_helper_blank, | ||
109 | .fb_setcmap = drm_fb_helper_setcmap, | ||
110 | }; | ||
111 | |||
112 | static void nouveau_fbcon_gamma_set(struct drm_crtc *crtc, u16 red, u16 green, | ||
113 | u16 blue, int regno) | ||
114 | { | ||
115 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | ||
116 | |||
117 | nv_crtc->lut.r[regno] = red; | ||
118 | nv_crtc->lut.g[regno] = green; | ||
119 | nv_crtc->lut.b[regno] = blue; | ||
120 | } | ||
121 | |||
122 | static void nouveau_fbcon_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, | ||
123 | u16 *blue, int regno) | ||
124 | { | ||
125 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | ||
126 | |||
127 | *red = nv_crtc->lut.r[regno]; | ||
128 | *green = nv_crtc->lut.g[regno]; | ||
129 | *blue = nv_crtc->lut.b[regno]; | ||
130 | } | ||
131 | |||
132 | static struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = { | ||
133 | .gamma_set = nouveau_fbcon_gamma_set, | ||
134 | .gamma_get = nouveau_fbcon_gamma_get | ||
135 | }; | ||
136 | |||
137 | #if defined(__i386__) || defined(__x86_64__) | ||
138 | static bool | ||
139 | nouveau_fbcon_has_vesafb_or_efifb(struct drm_device *dev) | ||
140 | { | ||
141 | struct pci_dev *pdev = dev->pdev; | ||
142 | int ramin; | ||
143 | |||
144 | if (screen_info.orig_video_isVGA != VIDEO_TYPE_VLFB && | ||
145 | screen_info.orig_video_isVGA != VIDEO_TYPE_EFI) | ||
146 | return false; | ||
147 | |||
148 | if (screen_info.lfb_base < pci_resource_start(pdev, 1)) | ||
149 | goto not_fb; | ||
150 | |||
151 | if (screen_info.lfb_base + screen_info.lfb_size >= | ||
152 | pci_resource_start(pdev, 1) + pci_resource_len(pdev, 1)) | ||
153 | goto not_fb; | ||
154 | |||
155 | return true; | ||
156 | not_fb: | ||
157 | ramin = 2; | ||
158 | if (pci_resource_len(pdev, ramin) == 0) { | ||
159 | ramin = 3; | ||
160 | if (pci_resource_len(pdev, ramin) == 0) | ||
161 | return false; | ||
162 | } | ||
163 | |||
164 | if (screen_info.lfb_base < pci_resource_start(pdev, ramin)) | ||
165 | return false; | ||
166 | |||
167 | if (screen_info.lfb_base + screen_info.lfb_size >= | ||
168 | pci_resource_start(pdev, ramin) + pci_resource_len(pdev, ramin)) | ||
169 | return false; | ||
170 | |||
171 | return true; | ||
172 | } | ||
173 | #endif | ||
174 | |||
175 | void | ||
176 | nouveau_fbcon_zfill(struct drm_device *dev) | ||
177 | { | ||
178 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
179 | struct fb_info *info = dev_priv->fbdev_info; | ||
180 | struct fb_fillrect rect; | ||
181 | |||
182 | /* Clear the entire fbcon. The drm will program every connector | ||
183 | * with it's preferred mode. If the sizes differ, one display will | ||
184 | * quite likely have garbage around the console. | ||
185 | */ | ||
186 | rect.dx = rect.dy = 0; | ||
187 | rect.width = info->var.xres_virtual; | ||
188 | rect.height = info->var.yres_virtual; | ||
189 | rect.color = 0; | ||
190 | rect.rop = ROP_COPY; | ||
191 | info->fbops->fb_fillrect(info, &rect); | ||
192 | } | ||
193 | |||
194 | static int | ||
195 | nouveau_fbcon_create(struct drm_device *dev, uint32_t fb_width, | ||
196 | uint32_t fb_height, uint32_t surface_width, | ||
197 | uint32_t surface_height, uint32_t surface_depth, | ||
198 | uint32_t surface_bpp, struct drm_framebuffer **pfb) | ||
199 | { | ||
200 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
201 | struct fb_info *info; | ||
202 | struct nouveau_fbcon_par *par; | ||
203 | struct drm_framebuffer *fb; | ||
204 | struct nouveau_framebuffer *nouveau_fb; | ||
205 | struct nouveau_bo *nvbo; | ||
206 | struct drm_mode_fb_cmd mode_cmd; | ||
207 | struct device *device = &dev->pdev->dev; | ||
208 | int size, ret; | ||
209 | |||
210 | mode_cmd.width = surface_width; | ||
211 | mode_cmd.height = surface_height; | ||
212 | |||
213 | mode_cmd.bpp = surface_bpp; | ||
214 | mode_cmd.pitch = mode_cmd.width * (mode_cmd.bpp >> 3); | ||
215 | mode_cmd.pitch = ALIGN(mode_cmd.pitch, 256); | ||
216 | mode_cmd.depth = surface_depth; | ||
217 | |||
218 | size = mode_cmd.pitch * mode_cmd.height; | ||
219 | size = ALIGN(size, PAGE_SIZE); | ||
220 | |||
221 | ret = nouveau_gem_new(dev, dev_priv->channel, size, 0, TTM_PL_FLAG_VRAM, | ||
222 | 0, 0x0000, false, true, &nvbo); | ||
223 | if (ret) { | ||
224 | NV_ERROR(dev, "failed to allocate framebuffer\n"); | ||
225 | goto out; | ||
226 | } | ||
227 | |||
228 | ret = nouveau_bo_pin(nvbo, TTM_PL_FLAG_VRAM); | ||
229 | if (ret) { | ||
230 | NV_ERROR(dev, "failed to pin fb: %d\n", ret); | ||
231 | nouveau_bo_ref(NULL, &nvbo); | ||
232 | goto out; | ||
233 | } | ||
234 | |||
235 | ret = nouveau_bo_map(nvbo); | ||
236 | if (ret) { | ||
237 | NV_ERROR(dev, "failed to map fb: %d\n", ret); | ||
238 | nouveau_bo_unpin(nvbo); | ||
239 | nouveau_bo_ref(NULL, &nvbo); | ||
240 | goto out; | ||
241 | } | ||
242 | |||
243 | mutex_lock(&dev->struct_mutex); | ||
244 | |||
245 | fb = nouveau_framebuffer_create(dev, nvbo, &mode_cmd); | ||
246 | if (!fb) { | ||
247 | ret = -ENOMEM; | ||
248 | NV_ERROR(dev, "failed to allocate fb.\n"); | ||
249 | goto out_unref; | ||
250 | } | ||
251 | |||
252 | list_add(&fb->filp_head, &dev->mode_config.fb_kernel_list); | ||
253 | |||
254 | nouveau_fb = nouveau_framebuffer(fb); | ||
255 | *pfb = fb; | ||
256 | |||
257 | info = framebuffer_alloc(sizeof(struct nouveau_fbcon_par), device); | ||
258 | if (!info) { | ||
259 | ret = -ENOMEM; | ||
260 | goto out_unref; | ||
261 | } | ||
262 | |||
263 | par = info->par; | ||
264 | par->helper.funcs = &nouveau_fbcon_helper_funcs; | ||
265 | par->helper.dev = dev; | ||
266 | ret = drm_fb_helper_init_crtc_count(&par->helper, 2, 4); | ||
267 | if (ret) | ||
268 | goto out_unref; | ||
269 | dev_priv->fbdev_info = info; | ||
270 | |||
271 | strcpy(info->fix.id, "nouveaufb"); | ||
272 | info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA | | ||
273 | FBINFO_HWACCEL_FILLRECT | FBINFO_HWACCEL_IMAGEBLIT; | ||
274 | info->fbops = &nouveau_fbcon_ops; | ||
275 | info->fix.smem_start = dev->mode_config.fb_base + nvbo->bo.offset - | ||
276 | dev_priv->vm_vram_base; | ||
277 | info->fix.smem_len = size; | ||
278 | |||
279 | info->screen_base = nvbo_kmap_obj_iovirtual(nouveau_fb->nvbo); | ||
280 | info->screen_size = size; | ||
281 | |||
282 | drm_fb_helper_fill_fix(info, fb->pitch, fb->depth); | ||
283 | drm_fb_helper_fill_var(info, fb, fb_width, fb_height); | ||
284 | |||
285 | /* FIXME: we really shouldn't expose mmio space at all */ | ||
286 | info->fix.mmio_start = pci_resource_start(dev->pdev, 1); | ||
287 | info->fix.mmio_len = pci_resource_len(dev->pdev, 1); | ||
288 | |||
289 | /* Set aperture base/size for vesafb takeover */ | ||
290 | #if defined(__i386__) || defined(__x86_64__) | ||
291 | if (nouveau_fbcon_has_vesafb_or_efifb(dev)) { | ||
292 | /* Some NVIDIA VBIOS' are stupid and decide to put the | ||
293 | * framebuffer in the middle of the PRAMIN BAR for | ||
294 | * whatever reason. We need to know the exact lfb_base | ||
295 | * to get vesafb kicked off, and the only reliable way | ||
296 | * we have left is to find out lfb_base the same way | ||
297 | * vesafb did. | ||
298 | */ | ||
299 | info->aperture_base = screen_info.lfb_base; | ||
300 | info->aperture_size = screen_info.lfb_size; | ||
301 | if (screen_info.orig_video_isVGA == VIDEO_TYPE_VLFB) | ||
302 | info->aperture_size *= 65536; | ||
303 | } else | ||
304 | #endif | ||
305 | { | ||
306 | info->aperture_base = info->fix.mmio_start; | ||
307 | info->aperture_size = info->fix.mmio_len; | ||
308 | } | ||
309 | |||
310 | info->pixmap.size = 64*1024; | ||
311 | info->pixmap.buf_align = 8; | ||
312 | info->pixmap.access_align = 32; | ||
313 | info->pixmap.flags = FB_PIXMAP_SYSTEM; | ||
314 | info->pixmap.scan_align = 1; | ||
315 | |||
316 | fb->fbdev = info; | ||
317 | |||
318 | par->nouveau_fb = nouveau_fb; | ||
319 | par->dev = dev; | ||
320 | |||
321 | switch (dev_priv->card_type) { | ||
322 | case NV_50: | ||
323 | nv50_fbcon_accel_init(info); | ||
324 | break; | ||
325 | default: | ||
326 | nv04_fbcon_accel_init(info); | ||
327 | break; | ||
328 | }; | ||
329 | |||
330 | nouveau_fbcon_zfill(dev); | ||
331 | |||
332 | /* To allow resizeing without swapping buffers */ | ||
333 | NV_INFO(dev, "allocated %dx%d fb: 0x%lx, bo %p\n", | ||
334 | nouveau_fb->base.width, | ||
335 | nouveau_fb->base.height, | ||
336 | nvbo->bo.offset, nvbo); | ||
337 | |||
338 | mutex_unlock(&dev->struct_mutex); | ||
339 | return 0; | ||
340 | |||
341 | out_unref: | ||
342 | mutex_unlock(&dev->struct_mutex); | ||
343 | out: | ||
344 | return ret; | ||
345 | } | ||
346 | |||
347 | int | ||
348 | nouveau_fbcon_probe(struct drm_device *dev) | ||
349 | { | ||
350 | NV_DEBUG(dev, "\n"); | ||
351 | |||
352 | return drm_fb_helper_single_fb_probe(dev, 32, nouveau_fbcon_create); | ||
353 | } | ||
354 | |||
355 | int | ||
356 | nouveau_fbcon_remove(struct drm_device *dev, struct drm_framebuffer *fb) | ||
357 | { | ||
358 | struct nouveau_framebuffer *nouveau_fb = nouveau_framebuffer(fb); | ||
359 | struct fb_info *info; | ||
360 | |||
361 | if (!fb) | ||
362 | return -EINVAL; | ||
363 | |||
364 | info = fb->fbdev; | ||
365 | if (info) { | ||
366 | struct nouveau_fbcon_par *par = info->par; | ||
367 | |||
368 | unregister_framebuffer(info); | ||
369 | nouveau_bo_unmap(nouveau_fb->nvbo); | ||
370 | mutex_lock(&dev->struct_mutex); | ||
371 | drm_gem_object_unreference(nouveau_fb->nvbo->gem); | ||
372 | nouveau_fb->nvbo = NULL; | ||
373 | mutex_unlock(&dev->struct_mutex); | ||
374 | if (par) | ||
375 | drm_fb_helper_free(&par->helper); | ||
376 | framebuffer_release(info); | ||
377 | } | ||
378 | |||
379 | return 0; | ||
380 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.h b/drivers/gpu/drm/nouveau/nouveau_fbcon.h new file mode 100644 index 000000000000..8531140fedbc --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.h | |||
@@ -0,0 +1,47 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2008 Maarten Maathuis. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #ifndef __NOUVEAU_FBCON_H__ | ||
28 | #define __NOUVEAU_FBCON_H__ | ||
29 | |||
30 | #include "drm_fb_helper.h" | ||
31 | |||
32 | struct nouveau_fbcon_par { | ||
33 | struct drm_fb_helper helper; | ||
34 | struct drm_device *dev; | ||
35 | struct nouveau_framebuffer *nouveau_fb; | ||
36 | }; | ||
37 | |||
38 | int nouveau_fbcon_probe(struct drm_device *dev); | ||
39 | int nouveau_fbcon_remove(struct drm_device *dev, struct drm_framebuffer *fb); | ||
40 | void nouveau_fbcon_restore(void); | ||
41 | void nouveau_fbcon_zfill(struct drm_device *dev); | ||
42 | |||
43 | int nv04_fbcon_accel_init(struct fb_info *info); | ||
44 | int nv50_fbcon_accel_init(struct fb_info *info); | ||
45 | |||
46 | #endif /* __NV50_FBCON_H__ */ | ||
47 | |||
diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c new file mode 100644 index 000000000000..0cff7eb3690a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c | |||
@@ -0,0 +1,262 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007 Ben Skeggs. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include "drmP.h" | ||
28 | #include "drm.h" | ||
29 | |||
30 | #include "nouveau_drv.h" | ||
31 | #include "nouveau_dma.h" | ||
32 | |||
33 | #define USE_REFCNT (dev_priv->card_type >= NV_10) | ||
34 | |||
35 | struct nouveau_fence { | ||
36 | struct nouveau_channel *channel; | ||
37 | struct kref refcount; | ||
38 | struct list_head entry; | ||
39 | |||
40 | uint32_t sequence; | ||
41 | bool signalled; | ||
42 | }; | ||
43 | |||
44 | static inline struct nouveau_fence * | ||
45 | nouveau_fence(void *sync_obj) | ||
46 | { | ||
47 | return (struct nouveau_fence *)sync_obj; | ||
48 | } | ||
49 | |||
50 | static void | ||
51 | nouveau_fence_del(struct kref *ref) | ||
52 | { | ||
53 | struct nouveau_fence *fence = | ||
54 | container_of(ref, struct nouveau_fence, refcount); | ||
55 | |||
56 | kfree(fence); | ||
57 | } | ||
58 | |||
59 | void | ||
60 | nouveau_fence_update(struct nouveau_channel *chan) | ||
61 | { | ||
62 | struct drm_nouveau_private *dev_priv = chan->dev->dev_private; | ||
63 | struct list_head *entry, *tmp; | ||
64 | struct nouveau_fence *fence; | ||
65 | uint32_t sequence; | ||
66 | |||
67 | if (USE_REFCNT) | ||
68 | sequence = nvchan_rd32(chan, 0x48); | ||
69 | else | ||
70 | sequence = chan->fence.last_sequence_irq; | ||
71 | |||
72 | if (chan->fence.sequence_ack == sequence) | ||
73 | return; | ||
74 | chan->fence.sequence_ack = sequence; | ||
75 | |||
76 | list_for_each_safe(entry, tmp, &chan->fence.pending) { | ||
77 | fence = list_entry(entry, struct nouveau_fence, entry); | ||
78 | |||
79 | sequence = fence->sequence; | ||
80 | fence->signalled = true; | ||
81 | list_del(&fence->entry); | ||
82 | kref_put(&fence->refcount, nouveau_fence_del); | ||
83 | |||
84 | if (sequence == chan->fence.sequence_ack) | ||
85 | break; | ||
86 | } | ||
87 | } | ||
88 | |||
89 | int | ||
90 | nouveau_fence_new(struct nouveau_channel *chan, struct nouveau_fence **pfence, | ||
91 | bool emit) | ||
92 | { | ||
93 | struct nouveau_fence *fence; | ||
94 | int ret = 0; | ||
95 | |||
96 | fence = kzalloc(sizeof(*fence), GFP_KERNEL); | ||
97 | if (!fence) | ||
98 | return -ENOMEM; | ||
99 | kref_init(&fence->refcount); | ||
100 | fence->channel = chan; | ||
101 | |||
102 | if (emit) | ||
103 | ret = nouveau_fence_emit(fence); | ||
104 | |||
105 | if (ret) | ||
106 | nouveau_fence_unref((void *)&fence); | ||
107 | *pfence = fence; | ||
108 | return ret; | ||
109 | } | ||
110 | |||
111 | struct nouveau_channel * | ||
112 | nouveau_fence_channel(struct nouveau_fence *fence) | ||
113 | { | ||
114 | return fence ? fence->channel : NULL; | ||
115 | } | ||
116 | |||
117 | int | ||
118 | nouveau_fence_emit(struct nouveau_fence *fence) | ||
119 | { | ||
120 | struct drm_nouveau_private *dev_priv = fence->channel->dev->dev_private; | ||
121 | struct nouveau_channel *chan = fence->channel; | ||
122 | unsigned long flags; | ||
123 | int ret; | ||
124 | |||
125 | ret = RING_SPACE(chan, 2); | ||
126 | if (ret) | ||
127 | return ret; | ||
128 | |||
129 | if (unlikely(chan->fence.sequence == chan->fence.sequence_ack - 1)) { | ||
130 | spin_lock_irqsave(&chan->fence.lock, flags); | ||
131 | nouveau_fence_update(chan); | ||
132 | spin_unlock_irqrestore(&chan->fence.lock, flags); | ||
133 | |||
134 | BUG_ON(chan->fence.sequence == | ||
135 | chan->fence.sequence_ack - 1); | ||
136 | } | ||
137 | |||
138 | fence->sequence = ++chan->fence.sequence; | ||
139 | |||
140 | kref_get(&fence->refcount); | ||
141 | spin_lock_irqsave(&chan->fence.lock, flags); | ||
142 | list_add_tail(&fence->entry, &chan->fence.pending); | ||
143 | spin_unlock_irqrestore(&chan->fence.lock, flags); | ||
144 | |||
145 | BEGIN_RING(chan, NvSubM2MF, USE_REFCNT ? 0x0050 : 0x0150, 1); | ||
146 | OUT_RING(chan, fence->sequence); | ||
147 | FIRE_RING(chan); | ||
148 | |||
149 | return 0; | ||
150 | } | ||
151 | |||
152 | void | ||
153 | nouveau_fence_unref(void **sync_obj) | ||
154 | { | ||
155 | struct nouveau_fence *fence = nouveau_fence(*sync_obj); | ||
156 | |||
157 | if (fence) | ||
158 | kref_put(&fence->refcount, nouveau_fence_del); | ||
159 | *sync_obj = NULL; | ||
160 | } | ||
161 | |||
162 | void * | ||
163 | nouveau_fence_ref(void *sync_obj) | ||
164 | { | ||
165 | struct nouveau_fence *fence = nouveau_fence(sync_obj); | ||
166 | |||
167 | kref_get(&fence->refcount); | ||
168 | return sync_obj; | ||
169 | } | ||
170 | |||
171 | bool | ||
172 | nouveau_fence_signalled(void *sync_obj, void *sync_arg) | ||
173 | { | ||
174 | struct nouveau_fence *fence = nouveau_fence(sync_obj); | ||
175 | struct nouveau_channel *chan = fence->channel; | ||
176 | unsigned long flags; | ||
177 | |||
178 | if (fence->signalled) | ||
179 | return true; | ||
180 | |||
181 | spin_lock_irqsave(&chan->fence.lock, flags); | ||
182 | nouveau_fence_update(chan); | ||
183 | spin_unlock_irqrestore(&chan->fence.lock, flags); | ||
184 | return fence->signalled; | ||
185 | } | ||
186 | |||
187 | int | ||
188 | nouveau_fence_wait(void *sync_obj, void *sync_arg, bool lazy, bool intr) | ||
189 | { | ||
190 | unsigned long timeout = jiffies + (3 * DRM_HZ); | ||
191 | int ret = 0; | ||
192 | |||
193 | __set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); | ||
194 | |||
195 | while (1) { | ||
196 | if (nouveau_fence_signalled(sync_obj, sync_arg)) | ||
197 | break; | ||
198 | |||
199 | if (time_after_eq(jiffies, timeout)) { | ||
200 | ret = -EBUSY; | ||
201 | break; | ||
202 | } | ||
203 | |||
204 | if (lazy) | ||
205 | schedule_timeout(1); | ||
206 | |||
207 | if (intr && signal_pending(current)) { | ||
208 | ret = -ERESTART; | ||
209 | break; | ||
210 | } | ||
211 | } | ||
212 | |||
213 | __set_current_state(TASK_RUNNING); | ||
214 | |||
215 | return ret; | ||
216 | } | ||
217 | |||
218 | int | ||
219 | nouveau_fence_flush(void *sync_obj, void *sync_arg) | ||
220 | { | ||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | void | ||
225 | nouveau_fence_handler(struct drm_device *dev, int channel) | ||
226 | { | ||
227 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
228 | struct nouveau_channel *chan = NULL; | ||
229 | |||
230 | if (channel >= 0 && channel < dev_priv->engine.fifo.channels) | ||
231 | chan = dev_priv->fifos[channel]; | ||
232 | |||
233 | if (chan) { | ||
234 | spin_lock_irq(&chan->fence.lock); | ||
235 | nouveau_fence_update(chan); | ||
236 | spin_unlock_irq(&chan->fence.lock); | ||
237 | } | ||
238 | } | ||
239 | |||
240 | int | ||
241 | nouveau_fence_init(struct nouveau_channel *chan) | ||
242 | { | ||
243 | INIT_LIST_HEAD(&chan->fence.pending); | ||
244 | spin_lock_init(&chan->fence.lock); | ||
245 | return 0; | ||
246 | } | ||
247 | |||
248 | void | ||
249 | nouveau_fence_fini(struct nouveau_channel *chan) | ||
250 | { | ||
251 | struct list_head *entry, *tmp; | ||
252 | struct nouveau_fence *fence; | ||
253 | |||
254 | list_for_each_safe(entry, tmp, &chan->fence.pending) { | ||
255 | fence = list_entry(entry, struct nouveau_fence, entry); | ||
256 | |||
257 | fence->signalled = true; | ||
258 | list_del(&fence->entry); | ||
259 | kref_put(&fence->refcount, nouveau_fence_del); | ||
260 | } | ||
261 | } | ||
262 | |||
diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c new file mode 100644 index 000000000000..11f831f0ddc5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c | |||
@@ -0,0 +1,992 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2008 Ben Skeggs. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | #include "drmP.h" | ||
27 | #include "drm.h" | ||
28 | |||
29 | #include "nouveau_drv.h" | ||
30 | #include "nouveau_drm.h" | ||
31 | #include "nouveau_dma.h" | ||
32 | |||
33 | #define nouveau_gem_pushbuf_sync(chan) 0 | ||
34 | |||
35 | int | ||
36 | nouveau_gem_object_new(struct drm_gem_object *gem) | ||
37 | { | ||
38 | return 0; | ||
39 | } | ||
40 | |||
41 | void | ||
42 | nouveau_gem_object_del(struct drm_gem_object *gem) | ||
43 | { | ||
44 | struct nouveau_bo *nvbo = gem->driver_private; | ||
45 | struct ttm_buffer_object *bo = &nvbo->bo; | ||
46 | |||
47 | if (!nvbo) | ||
48 | return; | ||
49 | nvbo->gem = NULL; | ||
50 | |||
51 | if (unlikely(nvbo->cpu_filp)) | ||
52 | ttm_bo_synccpu_write_release(bo); | ||
53 | |||
54 | if (unlikely(nvbo->pin_refcnt)) { | ||
55 | nvbo->pin_refcnt = 1; | ||
56 | nouveau_bo_unpin(nvbo); | ||
57 | } | ||
58 | |||
59 | ttm_bo_unref(&bo); | ||
60 | } | ||
61 | |||
62 | int | ||
63 | nouveau_gem_new(struct drm_device *dev, struct nouveau_channel *chan, | ||
64 | int size, int align, uint32_t flags, uint32_t tile_mode, | ||
65 | uint32_t tile_flags, bool no_vm, bool mappable, | ||
66 | struct nouveau_bo **pnvbo) | ||
67 | { | ||
68 | struct nouveau_bo *nvbo; | ||
69 | int ret; | ||
70 | |||
71 | ret = nouveau_bo_new(dev, chan, size, align, flags, tile_mode, | ||
72 | tile_flags, no_vm, mappable, pnvbo); | ||
73 | if (ret) | ||
74 | return ret; | ||
75 | nvbo = *pnvbo; | ||
76 | |||
77 | nvbo->gem = drm_gem_object_alloc(dev, nvbo->bo.mem.size); | ||
78 | if (!nvbo->gem) { | ||
79 | nouveau_bo_ref(NULL, pnvbo); | ||
80 | return -ENOMEM; | ||
81 | } | ||
82 | |||
83 | nvbo->bo.persistant_swap_storage = nvbo->gem->filp; | ||
84 | nvbo->gem->driver_private = nvbo; | ||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | static int | ||
89 | nouveau_gem_info(struct drm_gem_object *gem, struct drm_nouveau_gem_info *rep) | ||
90 | { | ||
91 | struct nouveau_bo *nvbo = nouveau_gem_object(gem); | ||
92 | |||
93 | if (nvbo->bo.mem.mem_type == TTM_PL_TT) | ||
94 | rep->domain = NOUVEAU_GEM_DOMAIN_GART; | ||
95 | else | ||
96 | rep->domain = NOUVEAU_GEM_DOMAIN_VRAM; | ||
97 | |||
98 | rep->size = nvbo->bo.mem.num_pages << PAGE_SHIFT; | ||
99 | rep->offset = nvbo->bo.offset; | ||
100 | rep->map_handle = nvbo->mappable ? nvbo->bo.addr_space_offset : 0; | ||
101 | rep->tile_mode = nvbo->tile_mode; | ||
102 | rep->tile_flags = nvbo->tile_flags; | ||
103 | return 0; | ||
104 | } | ||
105 | |||
106 | static bool | ||
107 | nouveau_gem_tile_flags_valid(struct drm_device *dev, uint32_t tile_flags) { | ||
108 | switch (tile_flags) { | ||
109 | case 0x0000: | ||
110 | case 0x1800: | ||
111 | case 0x2800: | ||
112 | case 0x4800: | ||
113 | case 0x7000: | ||
114 | case 0x7400: | ||
115 | case 0x7a00: | ||
116 | case 0xe000: | ||
117 | break; | ||
118 | default: | ||
119 | NV_ERROR(dev, "bad page flags: 0x%08x\n", tile_flags); | ||
120 | return false; | ||
121 | } | ||
122 | |||
123 | return true; | ||
124 | } | ||
125 | |||
126 | int | ||
127 | nouveau_gem_ioctl_new(struct drm_device *dev, void *data, | ||
128 | struct drm_file *file_priv) | ||
129 | { | ||
130 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
131 | struct drm_nouveau_gem_new *req = data; | ||
132 | struct nouveau_bo *nvbo = NULL; | ||
133 | struct nouveau_channel *chan = NULL; | ||
134 | uint32_t flags = 0; | ||
135 | int ret = 0; | ||
136 | |||
137 | NOUVEAU_CHECK_INITIALISED_WITH_RETURN; | ||
138 | |||
139 | if (unlikely(dev_priv->ttm.bdev.dev_mapping == NULL)) | ||
140 | dev_priv->ttm.bdev.dev_mapping = dev_priv->dev->dev_mapping; | ||
141 | |||
142 | if (req->channel_hint) { | ||
143 | NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(req->channel_hint, | ||
144 | file_priv, chan); | ||
145 | } | ||
146 | |||
147 | if (req->info.domain & NOUVEAU_GEM_DOMAIN_VRAM) | ||
148 | flags |= TTM_PL_FLAG_VRAM; | ||
149 | if (req->info.domain & NOUVEAU_GEM_DOMAIN_GART) | ||
150 | flags |= TTM_PL_FLAG_TT; | ||
151 | if (!flags || req->info.domain & NOUVEAU_GEM_DOMAIN_CPU) | ||
152 | flags |= TTM_PL_FLAG_SYSTEM; | ||
153 | |||
154 | if (!nouveau_gem_tile_flags_valid(dev, req->info.tile_flags)) | ||
155 | return -EINVAL; | ||
156 | |||
157 | ret = nouveau_gem_new(dev, chan, req->info.size, req->align, flags, | ||
158 | req->info.tile_mode, req->info.tile_flags, false, | ||
159 | (req->info.domain & NOUVEAU_GEM_DOMAIN_MAPPABLE), | ||
160 | &nvbo); | ||
161 | if (ret) | ||
162 | return ret; | ||
163 | |||
164 | ret = nouveau_gem_info(nvbo->gem, &req->info); | ||
165 | if (ret) | ||
166 | goto out; | ||
167 | |||
168 | ret = drm_gem_handle_create(file_priv, nvbo->gem, &req->info.handle); | ||
169 | out: | ||
170 | mutex_lock(&dev->struct_mutex); | ||
171 | drm_gem_object_handle_unreference(nvbo->gem); | ||
172 | mutex_unlock(&dev->struct_mutex); | ||
173 | |||
174 | if (ret) | ||
175 | drm_gem_object_unreference(nvbo->gem); | ||
176 | return ret; | ||
177 | } | ||
178 | |||
179 | static int | ||
180 | nouveau_gem_set_domain(struct drm_gem_object *gem, uint32_t read_domains, | ||
181 | uint32_t write_domains, uint32_t valid_domains) | ||
182 | { | ||
183 | struct nouveau_bo *nvbo = gem->driver_private; | ||
184 | struct ttm_buffer_object *bo = &nvbo->bo; | ||
185 | uint64_t flags; | ||
186 | |||
187 | if (!valid_domains || (!read_domains && !write_domains)) | ||
188 | return -EINVAL; | ||
189 | |||
190 | if (write_domains) { | ||
191 | if ((valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) && | ||
192 | (write_domains & NOUVEAU_GEM_DOMAIN_VRAM)) | ||
193 | flags = TTM_PL_FLAG_VRAM; | ||
194 | else | ||
195 | if ((valid_domains & NOUVEAU_GEM_DOMAIN_GART) && | ||
196 | (write_domains & NOUVEAU_GEM_DOMAIN_GART)) | ||
197 | flags = TTM_PL_FLAG_TT; | ||
198 | else | ||
199 | return -EINVAL; | ||
200 | } else { | ||
201 | if ((valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) && | ||
202 | (read_domains & NOUVEAU_GEM_DOMAIN_VRAM) && | ||
203 | bo->mem.mem_type == TTM_PL_VRAM) | ||
204 | flags = TTM_PL_FLAG_VRAM; | ||
205 | else | ||
206 | if ((valid_domains & NOUVEAU_GEM_DOMAIN_GART) && | ||
207 | (read_domains & NOUVEAU_GEM_DOMAIN_GART) && | ||
208 | bo->mem.mem_type == TTM_PL_TT) | ||
209 | flags = TTM_PL_FLAG_TT; | ||
210 | else | ||
211 | if ((valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) && | ||
212 | (read_domains & NOUVEAU_GEM_DOMAIN_VRAM)) | ||
213 | flags = TTM_PL_FLAG_VRAM; | ||
214 | else | ||
215 | flags = TTM_PL_FLAG_TT; | ||
216 | } | ||
217 | |||
218 | nouveau_bo_placement_set(nvbo, flags); | ||
219 | return 0; | ||
220 | } | ||
221 | |||
222 | struct validate_op { | ||
223 | struct nouveau_fence *fence; | ||
224 | struct list_head vram_list; | ||
225 | struct list_head gart_list; | ||
226 | struct list_head both_list; | ||
227 | }; | ||
228 | |||
229 | static void | ||
230 | validate_fini_list(struct list_head *list, struct nouveau_fence *fence) | ||
231 | { | ||
232 | struct list_head *entry, *tmp; | ||
233 | struct nouveau_bo *nvbo; | ||
234 | |||
235 | list_for_each_safe(entry, tmp, list) { | ||
236 | nvbo = list_entry(entry, struct nouveau_bo, entry); | ||
237 | if (likely(fence)) { | ||
238 | struct nouveau_fence *prev_fence; | ||
239 | |||
240 | spin_lock(&nvbo->bo.lock); | ||
241 | prev_fence = nvbo->bo.sync_obj; | ||
242 | nvbo->bo.sync_obj = nouveau_fence_ref(fence); | ||
243 | spin_unlock(&nvbo->bo.lock); | ||
244 | nouveau_fence_unref((void *)&prev_fence); | ||
245 | } | ||
246 | |||
247 | list_del(&nvbo->entry); | ||
248 | nvbo->reserved_by = NULL; | ||
249 | ttm_bo_unreserve(&nvbo->bo); | ||
250 | drm_gem_object_unreference(nvbo->gem); | ||
251 | } | ||
252 | } | ||
253 | |||
254 | static void | ||
255 | validate_fini(struct validate_op *op, bool success) | ||
256 | { | ||
257 | struct nouveau_fence *fence = op->fence; | ||
258 | |||
259 | if (unlikely(!success)) | ||
260 | op->fence = NULL; | ||
261 | |||
262 | validate_fini_list(&op->vram_list, op->fence); | ||
263 | validate_fini_list(&op->gart_list, op->fence); | ||
264 | validate_fini_list(&op->both_list, op->fence); | ||
265 | nouveau_fence_unref((void *)&fence); | ||
266 | } | ||
267 | |||
268 | static int | ||
269 | validate_init(struct nouveau_channel *chan, struct drm_file *file_priv, | ||
270 | struct drm_nouveau_gem_pushbuf_bo *pbbo, | ||
271 | int nr_buffers, struct validate_op *op) | ||
272 | { | ||
273 | struct drm_device *dev = chan->dev; | ||
274 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
275 | uint32_t sequence; | ||
276 | int trycnt = 0; | ||
277 | int ret, i; | ||
278 | |||
279 | sequence = atomic_add_return(1, &dev_priv->ttm.validate_sequence); | ||
280 | retry: | ||
281 | if (++trycnt > 100000) { | ||
282 | NV_ERROR(dev, "%s failed and gave up.\n", __func__); | ||
283 | return -EINVAL; | ||
284 | } | ||
285 | |||
286 | for (i = 0; i < nr_buffers; i++) { | ||
287 | struct drm_nouveau_gem_pushbuf_bo *b = &pbbo[i]; | ||
288 | struct drm_gem_object *gem; | ||
289 | struct nouveau_bo *nvbo; | ||
290 | |||
291 | gem = drm_gem_object_lookup(dev, file_priv, b->handle); | ||
292 | if (!gem) { | ||
293 | NV_ERROR(dev, "Unknown handle 0x%08x\n", b->handle); | ||
294 | validate_fini(op, NULL); | ||
295 | return -EINVAL; | ||
296 | } | ||
297 | nvbo = gem->driver_private; | ||
298 | |||
299 | if (nvbo->reserved_by && nvbo->reserved_by == file_priv) { | ||
300 | NV_ERROR(dev, "multiple instances of buffer %d on " | ||
301 | "validation list\n", b->handle); | ||
302 | validate_fini(op, NULL); | ||
303 | return -EINVAL; | ||
304 | } | ||
305 | |||
306 | ret = ttm_bo_reserve(&nvbo->bo, false, false, true, sequence); | ||
307 | if (ret) { | ||
308 | validate_fini(op, NULL); | ||
309 | if (ret == -EAGAIN) | ||
310 | ret = ttm_bo_wait_unreserved(&nvbo->bo, false); | ||
311 | drm_gem_object_unreference(gem); | ||
312 | if (ret) | ||
313 | return ret; | ||
314 | goto retry; | ||
315 | } | ||
316 | |||
317 | nvbo->reserved_by = file_priv; | ||
318 | nvbo->pbbo_index = i; | ||
319 | if ((b->valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) && | ||
320 | (b->valid_domains & NOUVEAU_GEM_DOMAIN_GART)) | ||
321 | list_add_tail(&nvbo->entry, &op->both_list); | ||
322 | else | ||
323 | if (b->valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) | ||
324 | list_add_tail(&nvbo->entry, &op->vram_list); | ||
325 | else | ||
326 | if (b->valid_domains & NOUVEAU_GEM_DOMAIN_GART) | ||
327 | list_add_tail(&nvbo->entry, &op->gart_list); | ||
328 | else { | ||
329 | NV_ERROR(dev, "invalid valid domains: 0x%08x\n", | ||
330 | b->valid_domains); | ||
331 | validate_fini(op, NULL); | ||
332 | return -EINVAL; | ||
333 | } | ||
334 | |||
335 | if (unlikely(atomic_read(&nvbo->bo.cpu_writers) > 0)) { | ||
336 | validate_fini(op, NULL); | ||
337 | |||
338 | if (nvbo->cpu_filp == file_priv) { | ||
339 | NV_ERROR(dev, "bo %p mapped by process trying " | ||
340 | "to validate it!\n", nvbo); | ||
341 | return -EINVAL; | ||
342 | } | ||
343 | |||
344 | ret = ttm_bo_wait_cpu(&nvbo->bo, false); | ||
345 | if (ret == -ERESTART) | ||
346 | ret = -EAGAIN; | ||
347 | if (ret) | ||
348 | return ret; | ||
349 | goto retry; | ||
350 | } | ||
351 | } | ||
352 | |||
353 | return 0; | ||
354 | } | ||
355 | |||
356 | static int | ||
357 | validate_list(struct nouveau_channel *chan, struct list_head *list, | ||
358 | struct drm_nouveau_gem_pushbuf_bo *pbbo, uint64_t user_pbbo_ptr) | ||
359 | { | ||
360 | struct drm_nouveau_gem_pushbuf_bo __user *upbbo = | ||
361 | (void __force __user *)(uintptr_t)user_pbbo_ptr; | ||
362 | struct nouveau_bo *nvbo; | ||
363 | int ret, relocs = 0; | ||
364 | |||
365 | list_for_each_entry(nvbo, list, entry) { | ||
366 | struct drm_nouveau_gem_pushbuf_bo *b = &pbbo[nvbo->pbbo_index]; | ||
367 | struct nouveau_fence *prev_fence = nvbo->bo.sync_obj; | ||
368 | |||
369 | if (prev_fence && nouveau_fence_channel(prev_fence) != chan) { | ||
370 | spin_lock(&nvbo->bo.lock); | ||
371 | ret = ttm_bo_wait(&nvbo->bo, false, false, false); | ||
372 | spin_unlock(&nvbo->bo.lock); | ||
373 | if (unlikely(ret)) | ||
374 | return ret; | ||
375 | } | ||
376 | |||
377 | ret = nouveau_gem_set_domain(nvbo->gem, b->read_domains, | ||
378 | b->write_domains, | ||
379 | b->valid_domains); | ||
380 | if (unlikely(ret)) | ||
381 | return ret; | ||
382 | |||
383 | nvbo->channel = chan; | ||
384 | ret = ttm_bo_validate(&nvbo->bo, &nvbo->placement, | ||
385 | false, false); | ||
386 | nvbo->channel = NULL; | ||
387 | if (unlikely(ret)) | ||
388 | return ret; | ||
389 | |||
390 | if (nvbo->bo.offset == b->presumed_offset && | ||
391 | ((nvbo->bo.mem.mem_type == TTM_PL_VRAM && | ||
392 | b->presumed_domain & NOUVEAU_GEM_DOMAIN_VRAM) || | ||
393 | (nvbo->bo.mem.mem_type == TTM_PL_TT && | ||
394 | b->presumed_domain & NOUVEAU_GEM_DOMAIN_GART))) | ||
395 | continue; | ||
396 | |||
397 | if (nvbo->bo.mem.mem_type == TTM_PL_TT) | ||
398 | b->presumed_domain = NOUVEAU_GEM_DOMAIN_GART; | ||
399 | else | ||
400 | b->presumed_domain = NOUVEAU_GEM_DOMAIN_VRAM; | ||
401 | b->presumed_offset = nvbo->bo.offset; | ||
402 | b->presumed_ok = 0; | ||
403 | relocs++; | ||
404 | |||
405 | if (DRM_COPY_TO_USER(&upbbo[nvbo->pbbo_index], b, sizeof(*b))) | ||
406 | return -EFAULT; | ||
407 | } | ||
408 | |||
409 | return relocs; | ||
410 | } | ||
411 | |||
412 | static int | ||
413 | nouveau_gem_pushbuf_validate(struct nouveau_channel *chan, | ||
414 | struct drm_file *file_priv, | ||
415 | struct drm_nouveau_gem_pushbuf_bo *pbbo, | ||
416 | uint64_t user_buffers, int nr_buffers, | ||
417 | struct validate_op *op, int *apply_relocs) | ||
418 | { | ||
419 | int ret, relocs = 0; | ||
420 | |||
421 | INIT_LIST_HEAD(&op->vram_list); | ||
422 | INIT_LIST_HEAD(&op->gart_list); | ||
423 | INIT_LIST_HEAD(&op->both_list); | ||
424 | |||
425 | ret = nouveau_fence_new(chan, &op->fence, false); | ||
426 | if (ret) | ||
427 | return ret; | ||
428 | |||
429 | if (nr_buffers == 0) | ||
430 | return 0; | ||
431 | |||
432 | ret = validate_init(chan, file_priv, pbbo, nr_buffers, op); | ||
433 | if (unlikely(ret)) | ||
434 | return ret; | ||
435 | |||
436 | ret = validate_list(chan, &op->vram_list, pbbo, user_buffers); | ||
437 | if (unlikely(ret < 0)) { | ||
438 | validate_fini(op, NULL); | ||
439 | return ret; | ||
440 | } | ||
441 | relocs += ret; | ||
442 | |||
443 | ret = validate_list(chan, &op->gart_list, pbbo, user_buffers); | ||
444 | if (unlikely(ret < 0)) { | ||
445 | validate_fini(op, NULL); | ||
446 | return ret; | ||
447 | } | ||
448 | relocs += ret; | ||
449 | |||
450 | ret = validate_list(chan, &op->both_list, pbbo, user_buffers); | ||
451 | if (unlikely(ret < 0)) { | ||
452 | validate_fini(op, NULL); | ||
453 | return ret; | ||
454 | } | ||
455 | relocs += ret; | ||
456 | |||
457 | *apply_relocs = relocs; | ||
458 | return 0; | ||
459 | } | ||
460 | |||
461 | static inline void * | ||
462 | u_memcpya(uint64_t user, unsigned nmemb, unsigned size) | ||
463 | { | ||
464 | void *mem; | ||
465 | void __user *userptr = (void __force __user *)(uintptr_t)user; | ||
466 | |||
467 | mem = kmalloc(nmemb * size, GFP_KERNEL); | ||
468 | if (!mem) | ||
469 | return ERR_PTR(-ENOMEM); | ||
470 | |||
471 | if (DRM_COPY_FROM_USER(mem, userptr, nmemb * size)) { | ||
472 | kfree(mem); | ||
473 | return ERR_PTR(-EFAULT); | ||
474 | } | ||
475 | |||
476 | return mem; | ||
477 | } | ||
478 | |||
479 | static int | ||
480 | nouveau_gem_pushbuf_reloc_apply(struct nouveau_channel *chan, int nr_bo, | ||
481 | struct drm_nouveau_gem_pushbuf_bo *bo, | ||
482 | int nr_relocs, uint64_t ptr_relocs, | ||
483 | int nr_dwords, int first_dword, | ||
484 | uint32_t *pushbuf, bool is_iomem) | ||
485 | { | ||
486 | struct drm_nouveau_gem_pushbuf_reloc *reloc = NULL; | ||
487 | struct drm_device *dev = chan->dev; | ||
488 | int ret = 0, i; | ||
489 | |||
490 | reloc = u_memcpya(ptr_relocs, nr_relocs, sizeof(*reloc)); | ||
491 | if (IS_ERR(reloc)) | ||
492 | return PTR_ERR(reloc); | ||
493 | |||
494 | for (i = 0; i < nr_relocs; i++) { | ||
495 | struct drm_nouveau_gem_pushbuf_reloc *r = &reloc[i]; | ||
496 | struct drm_nouveau_gem_pushbuf_bo *b; | ||
497 | uint32_t data; | ||
498 | |||
499 | if (r->bo_index >= nr_bo || r->reloc_index < first_dword || | ||
500 | r->reloc_index >= first_dword + nr_dwords) { | ||
501 | NV_ERROR(dev, "Bad relocation %d\n", i); | ||
502 | NV_ERROR(dev, " bo: %d max %d\n", r->bo_index, nr_bo); | ||
503 | NV_ERROR(dev, " id: %d max %d\n", r->reloc_index, nr_dwords); | ||
504 | ret = -EINVAL; | ||
505 | break; | ||
506 | } | ||
507 | |||
508 | b = &bo[r->bo_index]; | ||
509 | if (b->presumed_ok) | ||
510 | continue; | ||
511 | |||
512 | if (r->flags & NOUVEAU_GEM_RELOC_LOW) | ||
513 | data = b->presumed_offset + r->data; | ||
514 | else | ||
515 | if (r->flags & NOUVEAU_GEM_RELOC_HIGH) | ||
516 | data = (b->presumed_offset + r->data) >> 32; | ||
517 | else | ||
518 | data = r->data; | ||
519 | |||
520 | if (r->flags & NOUVEAU_GEM_RELOC_OR) { | ||
521 | if (b->presumed_domain == NOUVEAU_GEM_DOMAIN_GART) | ||
522 | data |= r->tor; | ||
523 | else | ||
524 | data |= r->vor; | ||
525 | } | ||
526 | |||
527 | if (is_iomem) | ||
528 | iowrite32_native(data, (void __force __iomem *) | ||
529 | &pushbuf[r->reloc_index]); | ||
530 | else | ||
531 | pushbuf[r->reloc_index] = data; | ||
532 | } | ||
533 | |||
534 | kfree(reloc); | ||
535 | return ret; | ||
536 | } | ||
537 | |||
538 | int | ||
539 | nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data, | ||
540 | struct drm_file *file_priv) | ||
541 | { | ||
542 | struct drm_nouveau_gem_pushbuf *req = data; | ||
543 | struct drm_nouveau_gem_pushbuf_bo *bo = NULL; | ||
544 | struct nouveau_channel *chan; | ||
545 | struct validate_op op; | ||
546 | uint32_t *pushbuf = NULL; | ||
547 | int ret = 0, do_reloc = 0, i; | ||
548 | |||
549 | NOUVEAU_CHECK_INITIALISED_WITH_RETURN; | ||
550 | NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(req->channel, file_priv, chan); | ||
551 | |||
552 | if (req->nr_dwords >= chan->dma.max || | ||
553 | req->nr_buffers > NOUVEAU_GEM_MAX_BUFFERS || | ||
554 | req->nr_relocs > NOUVEAU_GEM_MAX_RELOCS) { | ||
555 | NV_ERROR(dev, "Pushbuf config exceeds limits:\n"); | ||
556 | NV_ERROR(dev, " dwords : %d max %d\n", req->nr_dwords, | ||
557 | chan->dma.max - 1); | ||
558 | NV_ERROR(dev, " buffers: %d max %d\n", req->nr_buffers, | ||
559 | NOUVEAU_GEM_MAX_BUFFERS); | ||
560 | NV_ERROR(dev, " relocs : %d max %d\n", req->nr_relocs, | ||
561 | NOUVEAU_GEM_MAX_RELOCS); | ||
562 | return -EINVAL; | ||
563 | } | ||
564 | |||
565 | pushbuf = u_memcpya(req->dwords, req->nr_dwords, sizeof(uint32_t)); | ||
566 | if (IS_ERR(pushbuf)) | ||
567 | return PTR_ERR(pushbuf); | ||
568 | |||
569 | bo = u_memcpya(req->buffers, req->nr_buffers, sizeof(*bo)); | ||
570 | if (IS_ERR(bo)) { | ||
571 | kfree(pushbuf); | ||
572 | return PTR_ERR(bo); | ||
573 | } | ||
574 | |||
575 | mutex_lock(&dev->struct_mutex); | ||
576 | |||
577 | /* Validate buffer list */ | ||
578 | ret = nouveau_gem_pushbuf_validate(chan, file_priv, bo, req->buffers, | ||
579 | req->nr_buffers, &op, &do_reloc); | ||
580 | if (ret) | ||
581 | goto out; | ||
582 | |||
583 | /* Apply any relocations that are required */ | ||
584 | if (do_reloc) { | ||
585 | ret = nouveau_gem_pushbuf_reloc_apply(chan, req->nr_buffers, | ||
586 | bo, req->nr_relocs, | ||
587 | req->relocs, | ||
588 | req->nr_dwords, 0, | ||
589 | pushbuf, false); | ||
590 | if (ret) | ||
591 | goto out; | ||
592 | } | ||
593 | |||
594 | /* Emit push buffer to the hw | ||
595 | */ | ||
596 | ret = RING_SPACE(chan, req->nr_dwords); | ||
597 | if (ret) | ||
598 | goto out; | ||
599 | |||
600 | OUT_RINGp(chan, pushbuf, req->nr_dwords); | ||
601 | |||
602 | ret = nouveau_fence_emit(op.fence); | ||
603 | if (ret) { | ||
604 | NV_ERROR(dev, "error fencing pushbuf: %d\n", ret); | ||
605 | WIND_RING(chan); | ||
606 | goto out; | ||
607 | } | ||
608 | |||
609 | if (nouveau_gem_pushbuf_sync(chan)) { | ||
610 | ret = nouveau_fence_wait(op.fence, NULL, false, false); | ||
611 | if (ret) { | ||
612 | for (i = 0; i < req->nr_dwords; i++) | ||
613 | NV_ERROR(dev, "0x%08x\n", pushbuf[i]); | ||
614 | NV_ERROR(dev, "^^ above push buffer is fail :(\n"); | ||
615 | } | ||
616 | } | ||
617 | |||
618 | out: | ||
619 | validate_fini(&op, ret == 0); | ||
620 | mutex_unlock(&dev->struct_mutex); | ||
621 | kfree(pushbuf); | ||
622 | kfree(bo); | ||
623 | return ret; | ||
624 | } | ||
625 | |||
626 | #define PUSHBUF_CAL (dev_priv->card_type >= NV_20) | ||
627 | |||
628 | int | ||
629 | nouveau_gem_ioctl_pushbuf_call(struct drm_device *dev, void *data, | ||
630 | struct drm_file *file_priv) | ||
631 | { | ||
632 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
633 | struct drm_nouveau_gem_pushbuf_call *req = data; | ||
634 | struct drm_nouveau_gem_pushbuf_bo *bo = NULL; | ||
635 | struct nouveau_channel *chan; | ||
636 | struct drm_gem_object *gem; | ||
637 | struct nouveau_bo *pbbo; | ||
638 | struct validate_op op; | ||
639 | int i, ret = 0, do_reloc = 0; | ||
640 | |||
641 | NOUVEAU_CHECK_INITIALISED_WITH_RETURN; | ||
642 | NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(req->channel, file_priv, chan); | ||
643 | |||
644 | if (unlikely(req->handle == 0)) | ||
645 | goto out_next; | ||
646 | |||
647 | if (req->nr_buffers > NOUVEAU_GEM_MAX_BUFFERS || | ||
648 | req->nr_relocs > NOUVEAU_GEM_MAX_RELOCS) { | ||
649 | NV_ERROR(dev, "Pushbuf config exceeds limits:\n"); | ||
650 | NV_ERROR(dev, " buffers: %d max %d\n", req->nr_buffers, | ||
651 | NOUVEAU_GEM_MAX_BUFFERS); | ||
652 | NV_ERROR(dev, " relocs : %d max %d\n", req->nr_relocs, | ||
653 | NOUVEAU_GEM_MAX_RELOCS); | ||
654 | return -EINVAL; | ||
655 | } | ||
656 | |||
657 | bo = u_memcpya(req->buffers, req->nr_buffers, sizeof(*bo)); | ||
658 | if (IS_ERR(bo)) | ||
659 | return PTR_ERR(bo); | ||
660 | |||
661 | mutex_lock(&dev->struct_mutex); | ||
662 | |||
663 | /* Validate buffer list */ | ||
664 | ret = nouveau_gem_pushbuf_validate(chan, file_priv, bo, req->buffers, | ||
665 | req->nr_buffers, &op, &do_reloc); | ||
666 | if (ret) { | ||
667 | NV_ERROR(dev, "validate: %d\n", ret); | ||
668 | goto out; | ||
669 | } | ||
670 | |||
671 | /* Validate DMA push buffer */ | ||
672 | gem = drm_gem_object_lookup(dev, file_priv, req->handle); | ||
673 | if (!gem) { | ||
674 | NV_ERROR(dev, "Unknown pb handle 0x%08x\n", req->handle); | ||
675 | ret = -EINVAL; | ||
676 | goto out; | ||
677 | } | ||
678 | pbbo = nouveau_gem_object(gem); | ||
679 | |||
680 | ret = ttm_bo_reserve(&pbbo->bo, false, false, true, | ||
681 | chan->fence.sequence); | ||
682 | if (ret) { | ||
683 | NV_ERROR(dev, "resv pb: %d\n", ret); | ||
684 | drm_gem_object_unreference(gem); | ||
685 | goto out; | ||
686 | } | ||
687 | |||
688 | nouveau_bo_placement_set(pbbo, 1 << chan->pushbuf_bo->bo.mem.mem_type); | ||
689 | ret = ttm_bo_validate(&pbbo->bo, &pbbo->placement, false, false); | ||
690 | if (ret) { | ||
691 | NV_ERROR(dev, "validate pb: %d\n", ret); | ||
692 | ttm_bo_unreserve(&pbbo->bo); | ||
693 | drm_gem_object_unreference(gem); | ||
694 | goto out; | ||
695 | } | ||
696 | |||
697 | list_add_tail(&pbbo->entry, &op.both_list); | ||
698 | |||
699 | /* If presumed return address doesn't match, we need to map the | ||
700 | * push buffer and fix it.. | ||
701 | */ | ||
702 | if (!PUSHBUF_CAL) { | ||
703 | uint32_t retaddy; | ||
704 | |||
705 | if (chan->dma.free < 4 + NOUVEAU_DMA_SKIPS) { | ||
706 | ret = nouveau_dma_wait(chan, 4 + NOUVEAU_DMA_SKIPS); | ||
707 | if (ret) { | ||
708 | NV_ERROR(dev, "jmp_space: %d\n", ret); | ||
709 | goto out; | ||
710 | } | ||
711 | } | ||
712 | |||
713 | retaddy = chan->pushbuf_base + ((chan->dma.cur + 2) << 2); | ||
714 | retaddy |= 0x20000000; | ||
715 | if (retaddy != req->suffix0) { | ||
716 | req->suffix0 = retaddy; | ||
717 | do_reloc = 1; | ||
718 | } | ||
719 | } | ||
720 | |||
721 | /* Apply any relocations that are required */ | ||
722 | if (do_reloc) { | ||
723 | void *pbvirt; | ||
724 | bool is_iomem; | ||
725 | ret = ttm_bo_kmap(&pbbo->bo, 0, pbbo->bo.mem.num_pages, | ||
726 | &pbbo->kmap); | ||
727 | if (ret) { | ||
728 | NV_ERROR(dev, "kmap pb: %d\n", ret); | ||
729 | goto out; | ||
730 | } | ||
731 | |||
732 | pbvirt = ttm_kmap_obj_virtual(&pbbo->kmap, &is_iomem); | ||
733 | ret = nouveau_gem_pushbuf_reloc_apply(chan, req->nr_buffers, bo, | ||
734 | req->nr_relocs, | ||
735 | req->relocs, | ||
736 | req->nr_dwords, | ||
737 | req->offset / 4, | ||
738 | pbvirt, is_iomem); | ||
739 | |||
740 | if (!PUSHBUF_CAL) { | ||
741 | nouveau_bo_wr32(pbbo, | ||
742 | req->offset / 4 + req->nr_dwords - 2, | ||
743 | req->suffix0); | ||
744 | } | ||
745 | |||
746 | ttm_bo_kunmap(&pbbo->kmap); | ||
747 | if (ret) { | ||
748 | NV_ERROR(dev, "reloc apply: %d\n", ret); | ||
749 | goto out; | ||
750 | } | ||
751 | } | ||
752 | |||
753 | if (PUSHBUF_CAL) { | ||
754 | ret = RING_SPACE(chan, 2); | ||
755 | if (ret) { | ||
756 | NV_ERROR(dev, "cal_space: %d\n", ret); | ||
757 | goto out; | ||
758 | } | ||
759 | OUT_RING(chan, ((pbbo->bo.mem.mm_node->start << PAGE_SHIFT) + | ||
760 | req->offset) | 2); | ||
761 | OUT_RING(chan, 0); | ||
762 | } else { | ||
763 | ret = RING_SPACE(chan, 2 + NOUVEAU_DMA_SKIPS); | ||
764 | if (ret) { | ||
765 | NV_ERROR(dev, "jmp_space: %d\n", ret); | ||
766 | goto out; | ||
767 | } | ||
768 | OUT_RING(chan, ((pbbo->bo.mem.mm_node->start << PAGE_SHIFT) + | ||
769 | req->offset) | 0x20000000); | ||
770 | OUT_RING(chan, 0); | ||
771 | |||
772 | /* Space the jumps apart with NOPs. */ | ||
773 | for (i = 0; i < NOUVEAU_DMA_SKIPS; i++) | ||
774 | OUT_RING(chan, 0); | ||
775 | } | ||
776 | |||
777 | ret = nouveau_fence_emit(op.fence); | ||
778 | if (ret) { | ||
779 | NV_ERROR(dev, "error fencing pushbuf: %d\n", ret); | ||
780 | WIND_RING(chan); | ||
781 | goto out; | ||
782 | } | ||
783 | |||
784 | out: | ||
785 | validate_fini(&op, ret == 0); | ||
786 | mutex_unlock(&dev->struct_mutex); | ||
787 | kfree(bo); | ||
788 | |||
789 | out_next: | ||
790 | if (PUSHBUF_CAL) { | ||
791 | req->suffix0 = 0x00020000; | ||
792 | req->suffix1 = 0x00000000; | ||
793 | } else { | ||
794 | req->suffix0 = 0x20000000 | | ||
795 | (chan->pushbuf_base + ((chan->dma.cur + 2) << 2)); | ||
796 | req->suffix1 = 0x00000000; | ||
797 | } | ||
798 | |||
799 | return ret; | ||
800 | } | ||
801 | |||
802 | int | ||
803 | nouveau_gem_ioctl_pushbuf_call2(struct drm_device *dev, void *data, | ||
804 | struct drm_file *file_priv) | ||
805 | { | ||
806 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
807 | struct drm_nouveau_gem_pushbuf_call *req = data; | ||
808 | |||
809 | req->vram_available = dev_priv->fb_aper_free; | ||
810 | req->gart_available = dev_priv->gart_info.aper_free; | ||
811 | |||
812 | return nouveau_gem_ioctl_pushbuf_call(dev, data, file_priv); | ||
813 | } | ||
814 | |||
815 | static inline uint32_t | ||
816 | domain_to_ttm(struct nouveau_bo *nvbo, uint32_t domain) | ||
817 | { | ||
818 | uint32_t flags = 0; | ||
819 | |||
820 | if (domain & NOUVEAU_GEM_DOMAIN_VRAM) | ||
821 | flags |= TTM_PL_FLAG_VRAM; | ||
822 | if (domain & NOUVEAU_GEM_DOMAIN_GART) | ||
823 | flags |= TTM_PL_FLAG_TT; | ||
824 | |||
825 | return flags; | ||
826 | } | ||
827 | |||
828 | int | ||
829 | nouveau_gem_ioctl_pin(struct drm_device *dev, void *data, | ||
830 | struct drm_file *file_priv) | ||
831 | { | ||
832 | struct drm_nouveau_gem_pin *req = data; | ||
833 | struct drm_gem_object *gem; | ||
834 | struct nouveau_bo *nvbo; | ||
835 | int ret = 0; | ||
836 | |||
837 | NOUVEAU_CHECK_INITIALISED_WITH_RETURN; | ||
838 | |||
839 | if (drm_core_check_feature(dev, DRIVER_MODESET)) { | ||
840 | NV_ERROR(dev, "pin only allowed without kernel modesetting\n"); | ||
841 | return -EINVAL; | ||
842 | } | ||
843 | |||
844 | if (!DRM_SUSER(DRM_CURPROC)) | ||
845 | return -EPERM; | ||
846 | |||
847 | gem = drm_gem_object_lookup(dev, file_priv, req->handle); | ||
848 | if (!gem) | ||
849 | return -EINVAL; | ||
850 | nvbo = nouveau_gem_object(gem); | ||
851 | |||
852 | ret = nouveau_bo_pin(nvbo, domain_to_ttm(nvbo, req->domain)); | ||
853 | if (ret) | ||
854 | goto out; | ||
855 | |||
856 | req->offset = nvbo->bo.offset; | ||
857 | if (nvbo->bo.mem.mem_type == TTM_PL_TT) | ||
858 | req->domain = NOUVEAU_GEM_DOMAIN_GART; | ||
859 | else | ||
860 | req->domain = NOUVEAU_GEM_DOMAIN_VRAM; | ||
861 | |||
862 | out: | ||
863 | mutex_lock(&dev->struct_mutex); | ||
864 | drm_gem_object_unreference(gem); | ||
865 | mutex_unlock(&dev->struct_mutex); | ||
866 | |||
867 | return ret; | ||
868 | } | ||
869 | |||
870 | int | ||
871 | nouveau_gem_ioctl_unpin(struct drm_device *dev, void *data, | ||
872 | struct drm_file *file_priv) | ||
873 | { | ||
874 | struct drm_nouveau_gem_pin *req = data; | ||
875 | struct drm_gem_object *gem; | ||
876 | int ret; | ||
877 | |||
878 | NOUVEAU_CHECK_INITIALISED_WITH_RETURN; | ||
879 | |||
880 | if (drm_core_check_feature(dev, DRIVER_MODESET)) | ||
881 | return -EINVAL; | ||
882 | |||
883 | gem = drm_gem_object_lookup(dev, file_priv, req->handle); | ||
884 | if (!gem) | ||
885 | return -EINVAL; | ||
886 | |||
887 | ret = nouveau_bo_unpin(nouveau_gem_object(gem)); | ||
888 | |||
889 | mutex_lock(&dev->struct_mutex); | ||
890 | drm_gem_object_unreference(gem); | ||
891 | mutex_unlock(&dev->struct_mutex); | ||
892 | |||
893 | return ret; | ||
894 | } | ||
895 | |||
896 | int | ||
897 | nouveau_gem_ioctl_cpu_prep(struct drm_device *dev, void *data, | ||
898 | struct drm_file *file_priv) | ||
899 | { | ||
900 | struct drm_nouveau_gem_cpu_prep *req = data; | ||
901 | struct drm_gem_object *gem; | ||
902 | struct nouveau_bo *nvbo; | ||
903 | bool no_wait = !!(req->flags & NOUVEAU_GEM_CPU_PREP_NOWAIT); | ||
904 | int ret = -EINVAL; | ||
905 | |||
906 | NOUVEAU_CHECK_INITIALISED_WITH_RETURN; | ||
907 | |||
908 | gem = drm_gem_object_lookup(dev, file_priv, req->handle); | ||
909 | if (!gem) | ||
910 | return ret; | ||
911 | nvbo = nouveau_gem_object(gem); | ||
912 | |||
913 | if (nvbo->cpu_filp) { | ||
914 | if (nvbo->cpu_filp == file_priv) | ||
915 | goto out; | ||
916 | |||
917 | ret = ttm_bo_wait_cpu(&nvbo->bo, no_wait); | ||
918 | if (ret == -ERESTART) | ||
919 | ret = -EAGAIN; | ||
920 | if (ret) | ||
921 | goto out; | ||
922 | } | ||
923 | |||
924 | if (req->flags & NOUVEAU_GEM_CPU_PREP_NOBLOCK) { | ||
925 | ret = ttm_bo_wait(&nvbo->bo, false, false, no_wait); | ||
926 | } else { | ||
927 | ret = ttm_bo_synccpu_write_grab(&nvbo->bo, no_wait); | ||
928 | if (ret == -ERESTART) | ||
929 | ret = -EAGAIN; | ||
930 | else | ||
931 | if (ret == 0) | ||
932 | nvbo->cpu_filp = file_priv; | ||
933 | } | ||
934 | |||
935 | out: | ||
936 | mutex_lock(&dev->struct_mutex); | ||
937 | drm_gem_object_unreference(gem); | ||
938 | mutex_unlock(&dev->struct_mutex); | ||
939 | return ret; | ||
940 | } | ||
941 | |||
942 | int | ||
943 | nouveau_gem_ioctl_cpu_fini(struct drm_device *dev, void *data, | ||
944 | struct drm_file *file_priv) | ||
945 | { | ||
946 | struct drm_nouveau_gem_cpu_prep *req = data; | ||
947 | struct drm_gem_object *gem; | ||
948 | struct nouveau_bo *nvbo; | ||
949 | int ret = -EINVAL; | ||
950 | |||
951 | NOUVEAU_CHECK_INITIALISED_WITH_RETURN; | ||
952 | |||
953 | gem = drm_gem_object_lookup(dev, file_priv, req->handle); | ||
954 | if (!gem) | ||
955 | return ret; | ||
956 | nvbo = nouveau_gem_object(gem); | ||
957 | |||
958 | if (nvbo->cpu_filp != file_priv) | ||
959 | goto out; | ||
960 | nvbo->cpu_filp = NULL; | ||
961 | |||
962 | ttm_bo_synccpu_write_release(&nvbo->bo); | ||
963 | ret = 0; | ||
964 | |||
965 | out: | ||
966 | mutex_lock(&dev->struct_mutex); | ||
967 | drm_gem_object_unreference(gem); | ||
968 | mutex_unlock(&dev->struct_mutex); | ||
969 | return ret; | ||
970 | } | ||
971 | |||
972 | int | ||
973 | nouveau_gem_ioctl_info(struct drm_device *dev, void *data, | ||
974 | struct drm_file *file_priv) | ||
975 | { | ||
976 | struct drm_nouveau_gem_info *req = data; | ||
977 | struct drm_gem_object *gem; | ||
978 | int ret; | ||
979 | |||
980 | NOUVEAU_CHECK_INITIALISED_WITH_RETURN; | ||
981 | |||
982 | gem = drm_gem_object_lookup(dev, file_priv, req->handle); | ||
983 | if (!gem) | ||
984 | return -EINVAL; | ||
985 | |||
986 | ret = nouveau_gem_info(gem, req); | ||
987 | mutex_lock(&dev->struct_mutex); | ||
988 | drm_gem_object_unreference(gem); | ||
989 | mutex_unlock(&dev->struct_mutex); | ||
990 | return ret; | ||
991 | } | ||
992 | |||
diff --git a/drivers/gpu/drm/nouveau/nouveau_hw.c b/drivers/gpu/drm/nouveau/nouveau_hw.c new file mode 100644 index 000000000000..dc46792a5c96 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_hw.c | |||
@@ -0,0 +1,1080 @@ | |||
1 | /* | ||
2 | * Copyright 2006 Dave Airlie | ||
3 | * Copyright 2007 Maarten Maathuis | ||
4 | * Copyright 2007-2009 Stuart Bennett | ||
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 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||
20 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF | ||
21 | * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
22 | * SOFTWARE. | ||
23 | */ | ||
24 | |||
25 | #include "drmP.h" | ||
26 | #include "nouveau_drv.h" | ||
27 | #include "nouveau_hw.h" | ||
28 | |||
29 | #define CHIPSET_NFORCE 0x01a0 | ||
30 | #define CHIPSET_NFORCE2 0x01f0 | ||
31 | |||
32 | /* | ||
33 | * misc hw access wrappers/control functions | ||
34 | */ | ||
35 | |||
36 | void | ||
37 | NVWriteVgaSeq(struct drm_device *dev, int head, uint8_t index, uint8_t value) | ||
38 | { | ||
39 | NVWritePRMVIO(dev, head, NV_PRMVIO_SRX, index); | ||
40 | NVWritePRMVIO(dev, head, NV_PRMVIO_SR, value); | ||
41 | } | ||
42 | |||
43 | uint8_t | ||
44 | NVReadVgaSeq(struct drm_device *dev, int head, uint8_t index) | ||
45 | { | ||
46 | NVWritePRMVIO(dev, head, NV_PRMVIO_SRX, index); | ||
47 | return NVReadPRMVIO(dev, head, NV_PRMVIO_SR); | ||
48 | } | ||
49 | |||
50 | void | ||
51 | NVWriteVgaGr(struct drm_device *dev, int head, uint8_t index, uint8_t value) | ||
52 | { | ||
53 | NVWritePRMVIO(dev, head, NV_PRMVIO_GRX, index); | ||
54 | NVWritePRMVIO(dev, head, NV_PRMVIO_GX, value); | ||
55 | } | ||
56 | |||
57 | uint8_t | ||
58 | NVReadVgaGr(struct drm_device *dev, int head, uint8_t index) | ||
59 | { | ||
60 | NVWritePRMVIO(dev, head, NV_PRMVIO_GRX, index); | ||
61 | return NVReadPRMVIO(dev, head, NV_PRMVIO_GX); | ||
62 | } | ||
63 | |||
64 | /* CR44 takes values 0 (head A), 3 (head B) and 4 (heads tied) | ||
65 | * it affects only the 8 bit vga io regs, which we access using mmio at | ||
66 | * 0xc{0,2}3c*, 0x60{1,3}3*, and 0x68{1,3}3d* | ||
67 | * in general, the set value of cr44 does not matter: reg access works as | ||
68 | * expected and values can be set for the appropriate head by using a 0x2000 | ||
69 | * offset as required | ||
70 | * however: | ||
71 | * a) pre nv40, the head B range of PRMVIO regs at 0xc23c* was not exposed and | ||
72 | * cr44 must be set to 0 or 3 for accessing values on the correct head | ||
73 | * through the common 0xc03c* addresses | ||
74 | * b) in tied mode (4) head B is programmed to the values set on head A, and | ||
75 | * access using the head B addresses can have strange results, ergo we leave | ||
76 | * tied mode in init once we know to what cr44 should be restored on exit | ||
77 | * | ||
78 | * the owner parameter is slightly abused: | ||
79 | * 0 and 1 are treated as head values and so the set value is (owner * 3) | ||
80 | * other values are treated as literal values to set | ||
81 | */ | ||
82 | void | ||
83 | NVSetOwner(struct drm_device *dev, int owner) | ||
84 | { | ||
85 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
86 | |||
87 | if (owner == 1) | ||
88 | owner *= 3; | ||
89 | |||
90 | if (dev_priv->chipset == 0x11) { | ||
91 | /* This might seem stupid, but the blob does it and | ||
92 | * omitting it often locks the system up. | ||
93 | */ | ||
94 | NVReadVgaCrtc(dev, 0, NV_CIO_SR_LOCK_INDEX); | ||
95 | NVReadVgaCrtc(dev, 1, NV_CIO_SR_LOCK_INDEX); | ||
96 | } | ||
97 | |||
98 | /* CR44 is always changed on CRTC0 */ | ||
99 | NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_44, owner); | ||
100 | |||
101 | if (dev_priv->chipset == 0x11) { /* set me harder */ | ||
102 | NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_2E, owner); | ||
103 | NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_2E, owner); | ||
104 | } | ||
105 | } | ||
106 | |||
107 | void | ||
108 | NVBlankScreen(struct drm_device *dev, int head, bool blank) | ||
109 | { | ||
110 | unsigned char seq1; | ||
111 | |||
112 | if (nv_two_heads(dev)) | ||
113 | NVSetOwner(dev, head); | ||
114 | |||
115 | seq1 = NVReadVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX); | ||
116 | |||
117 | NVVgaSeqReset(dev, head, true); | ||
118 | if (blank) | ||
119 | NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 | 0x20); | ||
120 | else | ||
121 | NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 & ~0x20); | ||
122 | NVVgaSeqReset(dev, head, false); | ||
123 | } | ||
124 | |||
125 | /* | ||
126 | * PLL setting | ||
127 | */ | ||
128 | |||
129 | static int | ||
130 | powerctrl_1_shift(int chip_version, int reg) | ||
131 | { | ||
132 | int shift = -4; | ||
133 | |||
134 | if (chip_version < 0x17 || chip_version == 0x1a || chip_version == 0x20) | ||
135 | return shift; | ||
136 | |||
137 | switch (reg) { | ||
138 | case NV_RAMDAC_VPLL2: | ||
139 | shift += 4; | ||
140 | case NV_PRAMDAC_VPLL_COEFF: | ||
141 | shift += 4; | ||
142 | case NV_PRAMDAC_MPLL_COEFF: | ||
143 | shift += 4; | ||
144 | case NV_PRAMDAC_NVPLL_COEFF: | ||
145 | shift += 4; | ||
146 | } | ||
147 | |||
148 | /* | ||
149 | * the shift for vpll regs is only used for nv3x chips with a single | ||
150 | * stage pll | ||
151 | */ | ||
152 | if (shift > 4 && (chip_version < 0x32 || chip_version == 0x35 || | ||
153 | chip_version == 0x36 || chip_version >= 0x40)) | ||
154 | shift = -4; | ||
155 | |||
156 | return shift; | ||
157 | } | ||
158 | |||
159 | static void | ||
160 | setPLL_single(struct drm_device *dev, uint32_t reg, struct nouveau_pll_vals *pv) | ||
161 | { | ||
162 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
163 | int chip_version = dev_priv->vbios->chip_version; | ||
164 | uint32_t oldpll = NVReadRAMDAC(dev, 0, reg); | ||
165 | int oldN = (oldpll >> 8) & 0xff, oldM = oldpll & 0xff; | ||
166 | uint32_t pll = (oldpll & 0xfff80000) | pv->log2P << 16 | pv->NM1; | ||
167 | uint32_t saved_powerctrl_1 = 0; | ||
168 | int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg); | ||
169 | |||
170 | if (oldpll == pll) | ||
171 | return; /* already set */ | ||
172 | |||
173 | if (shift_powerctrl_1 >= 0) { | ||
174 | saved_powerctrl_1 = nvReadMC(dev, NV_PBUS_POWERCTRL_1); | ||
175 | nvWriteMC(dev, NV_PBUS_POWERCTRL_1, | ||
176 | (saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) | | ||
177 | 1 << shift_powerctrl_1); | ||
178 | } | ||
179 | |||
180 | if (oldM && pv->M1 && (oldN / oldM < pv->N1 / pv->M1)) | ||
181 | /* upclock -- write new post divider first */ | ||
182 | NVWriteRAMDAC(dev, 0, reg, pv->log2P << 16 | (oldpll & 0xffff)); | ||
183 | else | ||
184 | /* downclock -- write new NM first */ | ||
185 | NVWriteRAMDAC(dev, 0, reg, (oldpll & 0xffff0000) | pv->NM1); | ||
186 | |||
187 | if (chip_version < 0x17 && chip_version != 0x11) | ||
188 | /* wait a bit on older chips */ | ||
189 | msleep(64); | ||
190 | NVReadRAMDAC(dev, 0, reg); | ||
191 | |||
192 | /* then write the other half as well */ | ||
193 | NVWriteRAMDAC(dev, 0, reg, pll); | ||
194 | |||
195 | if (shift_powerctrl_1 >= 0) | ||
196 | nvWriteMC(dev, NV_PBUS_POWERCTRL_1, saved_powerctrl_1); | ||
197 | } | ||
198 | |||
199 | static uint32_t | ||
200 | new_ramdac580(uint32_t reg1, bool ss, uint32_t ramdac580) | ||
201 | { | ||
202 | bool head_a = (reg1 == NV_PRAMDAC_VPLL_COEFF); | ||
203 | |||
204 | if (ss) /* single stage pll mode */ | ||
205 | ramdac580 |= head_a ? NV_RAMDAC_580_VPLL1_ACTIVE : | ||
206 | NV_RAMDAC_580_VPLL2_ACTIVE; | ||
207 | else | ||
208 | ramdac580 &= head_a ? ~NV_RAMDAC_580_VPLL1_ACTIVE : | ||
209 | ~NV_RAMDAC_580_VPLL2_ACTIVE; | ||
210 | |||
211 | return ramdac580; | ||
212 | } | ||
213 | |||
214 | static void | ||
215 | setPLL_double_highregs(struct drm_device *dev, uint32_t reg1, | ||
216 | struct nouveau_pll_vals *pv) | ||
217 | { | ||
218 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
219 | int chip_version = dev_priv->vbios->chip_version; | ||
220 | bool nv3035 = chip_version == 0x30 || chip_version == 0x35; | ||
221 | uint32_t reg2 = reg1 + ((reg1 == NV_RAMDAC_VPLL2) ? 0x5c : 0x70); | ||
222 | uint32_t oldpll1 = NVReadRAMDAC(dev, 0, reg1); | ||
223 | uint32_t oldpll2 = !nv3035 ? NVReadRAMDAC(dev, 0, reg2) : 0; | ||
224 | uint32_t pll1 = (oldpll1 & 0xfff80000) | pv->log2P << 16 | pv->NM1; | ||
225 | uint32_t pll2 = (oldpll2 & 0x7fff0000) | 1 << 31 | pv->NM2; | ||
226 | uint32_t oldramdac580 = 0, ramdac580 = 0; | ||
227 | bool single_stage = !pv->NM2 || pv->N2 == pv->M2; /* nv41+ only */ | ||
228 | uint32_t saved_powerctrl_1 = 0, savedc040 = 0; | ||
229 | int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg1); | ||
230 | |||
231 | /* model specific additions to generic pll1 and pll2 set up above */ | ||
232 | if (nv3035) { | ||
233 | pll1 = (pll1 & 0xfcc7ffff) | (pv->N2 & 0x18) << 21 | | ||
234 | (pv->N2 & 0x7) << 19 | 8 << 4 | (pv->M2 & 7) << 4; | ||
235 | pll2 = 0; | ||
236 | } | ||
237 | if (chip_version > 0x40 && reg1 >= NV_PRAMDAC_VPLL_COEFF) { /* !nv40 */ | ||
238 | oldramdac580 = NVReadRAMDAC(dev, 0, NV_PRAMDAC_580); | ||
239 | ramdac580 = new_ramdac580(reg1, single_stage, oldramdac580); | ||
240 | if (oldramdac580 != ramdac580) | ||
241 | oldpll1 = ~0; /* force mismatch */ | ||
242 | if (single_stage) | ||
243 | /* magic value used by nvidia in single stage mode */ | ||
244 | pll2 |= 0x011f; | ||
245 | } | ||
246 | if (chip_version > 0x70) | ||
247 | /* magic bits set by the blob (but not the bios) on g71-73 */ | ||
248 | pll1 = (pll1 & 0x7fffffff) | (single_stage ? 0x4 : 0xc) << 28; | ||
249 | |||
250 | if (oldpll1 == pll1 && oldpll2 == pll2) | ||
251 | return; /* already set */ | ||
252 | |||
253 | if (shift_powerctrl_1 >= 0) { | ||
254 | saved_powerctrl_1 = nvReadMC(dev, NV_PBUS_POWERCTRL_1); | ||
255 | nvWriteMC(dev, NV_PBUS_POWERCTRL_1, | ||
256 | (saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) | | ||
257 | 1 << shift_powerctrl_1); | ||
258 | } | ||
259 | |||
260 | if (chip_version >= 0x40) { | ||
261 | int shift_c040 = 14; | ||
262 | |||
263 | switch (reg1) { | ||
264 | case NV_PRAMDAC_MPLL_COEFF: | ||
265 | shift_c040 += 2; | ||
266 | case NV_PRAMDAC_NVPLL_COEFF: | ||
267 | shift_c040 += 2; | ||
268 | case NV_RAMDAC_VPLL2: | ||
269 | shift_c040 += 2; | ||
270 | case NV_PRAMDAC_VPLL_COEFF: | ||
271 | shift_c040 += 2; | ||
272 | } | ||
273 | |||
274 | savedc040 = nvReadMC(dev, 0xc040); | ||
275 | if (shift_c040 != 14) | ||
276 | nvWriteMC(dev, 0xc040, savedc040 & ~(3 << shift_c040)); | ||
277 | } | ||
278 | |||
279 | if (oldramdac580 != ramdac580) | ||
280 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_580, ramdac580); | ||
281 | |||
282 | if (!nv3035) | ||
283 | NVWriteRAMDAC(dev, 0, reg2, pll2); | ||
284 | NVWriteRAMDAC(dev, 0, reg1, pll1); | ||
285 | |||
286 | if (shift_powerctrl_1 >= 0) | ||
287 | nvWriteMC(dev, NV_PBUS_POWERCTRL_1, saved_powerctrl_1); | ||
288 | if (chip_version >= 0x40) | ||
289 | nvWriteMC(dev, 0xc040, savedc040); | ||
290 | } | ||
291 | |||
292 | static void | ||
293 | setPLL_double_lowregs(struct drm_device *dev, uint32_t NMNMreg, | ||
294 | struct nouveau_pll_vals *pv) | ||
295 | { | ||
296 | /* When setting PLLs, there is a merry game of disabling and enabling | ||
297 | * various bits of hardware during the process. This function is a | ||
298 | * synthesis of six nv4x traces, nearly each card doing a subtly | ||
299 | * different thing. With luck all the necessary bits for each card are | ||
300 | * combined herein. Without luck it deviates from each card's formula | ||
301 | * so as to not work on any :) | ||
302 | */ | ||
303 | |||
304 | uint32_t Preg = NMNMreg - 4; | ||
305 | bool mpll = Preg == 0x4020; | ||
306 | uint32_t oldPval = nvReadMC(dev, Preg); | ||
307 | uint32_t NMNM = pv->NM2 << 16 | pv->NM1; | ||
308 | uint32_t Pval = (oldPval & (mpll ? ~(0x11 << 16) : ~(1 << 16))) | | ||
309 | 0xc << 28 | pv->log2P << 16; | ||
310 | uint32_t saved4600 = 0; | ||
311 | /* some cards have different maskc040s */ | ||
312 | uint32_t maskc040 = ~(3 << 14), savedc040; | ||
313 | bool single_stage = !pv->NM2 || pv->N2 == pv->M2; | ||
314 | |||
315 | if (nvReadMC(dev, NMNMreg) == NMNM && (oldPval & 0xc0070000) == Pval) | ||
316 | return; | ||
317 | |||
318 | if (Preg == 0x4000) | ||
319 | maskc040 = ~0x333; | ||
320 | if (Preg == 0x4058) | ||
321 | maskc040 = ~(0xc << 24); | ||
322 | |||
323 | if (mpll) { | ||
324 | struct pll_lims pll_lim; | ||
325 | uint8_t Pval2; | ||
326 | |||
327 | if (get_pll_limits(dev, Preg, &pll_lim)) | ||
328 | return; | ||
329 | |||
330 | Pval2 = pv->log2P + pll_lim.log2p_bias; | ||
331 | if (Pval2 > pll_lim.max_log2p) | ||
332 | Pval2 = pll_lim.max_log2p; | ||
333 | Pval |= 1 << 28 | Pval2 << 20; | ||
334 | |||
335 | saved4600 = nvReadMC(dev, 0x4600); | ||
336 | nvWriteMC(dev, 0x4600, saved4600 | 8 << 28); | ||
337 | } | ||
338 | if (single_stage) | ||
339 | Pval |= mpll ? 1 << 12 : 1 << 8; | ||
340 | |||
341 | nvWriteMC(dev, Preg, oldPval | 1 << 28); | ||
342 | nvWriteMC(dev, Preg, Pval & ~(4 << 28)); | ||
343 | if (mpll) { | ||
344 | Pval |= 8 << 20; | ||
345 | nvWriteMC(dev, 0x4020, Pval & ~(0xc << 28)); | ||
346 | nvWriteMC(dev, 0x4038, Pval & ~(0xc << 28)); | ||
347 | } | ||
348 | |||
349 | savedc040 = nvReadMC(dev, 0xc040); | ||
350 | nvWriteMC(dev, 0xc040, savedc040 & maskc040); | ||
351 | |||
352 | nvWriteMC(dev, NMNMreg, NMNM); | ||
353 | if (NMNMreg == 0x4024) | ||
354 | nvWriteMC(dev, 0x403c, NMNM); | ||
355 | |||
356 | nvWriteMC(dev, Preg, Pval); | ||
357 | if (mpll) { | ||
358 | Pval &= ~(8 << 20); | ||
359 | nvWriteMC(dev, 0x4020, Pval); | ||
360 | nvWriteMC(dev, 0x4038, Pval); | ||
361 | nvWriteMC(dev, 0x4600, saved4600); | ||
362 | } | ||
363 | |||
364 | nvWriteMC(dev, 0xc040, savedc040); | ||
365 | |||
366 | if (mpll) { | ||
367 | nvWriteMC(dev, 0x4020, Pval & ~(1 << 28)); | ||
368 | nvWriteMC(dev, 0x4038, Pval & ~(1 << 28)); | ||
369 | } | ||
370 | } | ||
371 | |||
372 | void | ||
373 | nouveau_hw_setpll(struct drm_device *dev, uint32_t reg1, | ||
374 | struct nouveau_pll_vals *pv) | ||
375 | { | ||
376 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
377 | int cv = dev_priv->vbios->chip_version; | ||
378 | |||
379 | if (cv == 0x30 || cv == 0x31 || cv == 0x35 || cv == 0x36 || | ||
380 | cv >= 0x40) { | ||
381 | if (reg1 > 0x405c) | ||
382 | setPLL_double_highregs(dev, reg1, pv); | ||
383 | else | ||
384 | setPLL_double_lowregs(dev, reg1, pv); | ||
385 | } else | ||
386 | setPLL_single(dev, reg1, pv); | ||
387 | } | ||
388 | |||
389 | /* | ||
390 | * PLL getting | ||
391 | */ | ||
392 | |||
393 | static void | ||
394 | nouveau_hw_decode_pll(struct drm_device *dev, uint32_t reg1, uint32_t pll1, | ||
395 | uint32_t pll2, struct nouveau_pll_vals *pllvals) | ||
396 | { | ||
397 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
398 | |||
399 | /* to force parsing as single stage (i.e. nv40 vplls) pass pll2 as 0 */ | ||
400 | |||
401 | /* log2P is & 0x7 as never more than 7, and nv30/35 only uses 3 bits */ | ||
402 | pllvals->log2P = (pll1 >> 16) & 0x7; | ||
403 | pllvals->N2 = pllvals->M2 = 1; | ||
404 | |||
405 | if (reg1 <= 0x405c) { | ||
406 | pllvals->NM1 = pll2 & 0xffff; | ||
407 | /* single stage NVPLL and VPLLs use 1 << 8, MPLL uses 1 << 12 */ | ||
408 | if (!(pll1 & 0x1100)) | ||
409 | pllvals->NM2 = pll2 >> 16; | ||
410 | } else { | ||
411 | pllvals->NM1 = pll1 & 0xffff; | ||
412 | if (nv_two_reg_pll(dev) && pll2 & NV31_RAMDAC_ENABLE_VCO2) | ||
413 | pllvals->NM2 = pll2 & 0xffff; | ||
414 | else if (dev_priv->chipset == 0x30 || dev_priv->chipset == 0x35) { | ||
415 | pllvals->M1 &= 0xf; /* only 4 bits */ | ||
416 | if (pll1 & NV30_RAMDAC_ENABLE_VCO2) { | ||
417 | pllvals->M2 = (pll1 >> 4) & 0x7; | ||
418 | pllvals->N2 = ((pll1 >> 21) & 0x18) | | ||
419 | ((pll1 >> 19) & 0x7); | ||
420 | } | ||
421 | } | ||
422 | } | ||
423 | } | ||
424 | |||
425 | int | ||
426 | nouveau_hw_get_pllvals(struct drm_device *dev, enum pll_types plltype, | ||
427 | struct nouveau_pll_vals *pllvals) | ||
428 | { | ||
429 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
430 | const uint32_t nv04_regs[MAX_PLL_TYPES] = { NV_PRAMDAC_NVPLL_COEFF, | ||
431 | NV_PRAMDAC_MPLL_COEFF, | ||
432 | NV_PRAMDAC_VPLL_COEFF, | ||
433 | NV_RAMDAC_VPLL2 }; | ||
434 | const uint32_t nv40_regs[MAX_PLL_TYPES] = { 0x4000, | ||
435 | 0x4020, | ||
436 | NV_PRAMDAC_VPLL_COEFF, | ||
437 | NV_RAMDAC_VPLL2 }; | ||
438 | uint32_t reg1, pll1, pll2 = 0; | ||
439 | struct pll_lims pll_lim; | ||
440 | int ret; | ||
441 | |||
442 | if (dev_priv->card_type < NV_40) | ||
443 | reg1 = nv04_regs[plltype]; | ||
444 | else | ||
445 | reg1 = nv40_regs[plltype]; | ||
446 | |||
447 | pll1 = nvReadMC(dev, reg1); | ||
448 | |||
449 | if (reg1 <= 0x405c) | ||
450 | pll2 = nvReadMC(dev, reg1 + 4); | ||
451 | else if (nv_two_reg_pll(dev)) { | ||
452 | uint32_t reg2 = reg1 + (reg1 == NV_RAMDAC_VPLL2 ? 0x5c : 0x70); | ||
453 | |||
454 | pll2 = nvReadMC(dev, reg2); | ||
455 | } | ||
456 | |||
457 | if (dev_priv->card_type == 0x40 && reg1 >= NV_PRAMDAC_VPLL_COEFF) { | ||
458 | uint32_t ramdac580 = NVReadRAMDAC(dev, 0, NV_PRAMDAC_580); | ||
459 | |||
460 | /* check whether vpll has been forced into single stage mode */ | ||
461 | if (reg1 == NV_PRAMDAC_VPLL_COEFF) { | ||
462 | if (ramdac580 & NV_RAMDAC_580_VPLL1_ACTIVE) | ||
463 | pll2 = 0; | ||
464 | } else | ||
465 | if (ramdac580 & NV_RAMDAC_580_VPLL2_ACTIVE) | ||
466 | pll2 = 0; | ||
467 | } | ||
468 | |||
469 | nouveau_hw_decode_pll(dev, reg1, pll1, pll2, pllvals); | ||
470 | |||
471 | ret = get_pll_limits(dev, plltype, &pll_lim); | ||
472 | if (ret) | ||
473 | return ret; | ||
474 | |||
475 | pllvals->refclk = pll_lim.refclk; | ||
476 | |||
477 | return 0; | ||
478 | } | ||
479 | |||
480 | int | ||
481 | nouveau_hw_pllvals_to_clk(struct nouveau_pll_vals *pv) | ||
482 | { | ||
483 | /* Avoid divide by zero if called at an inappropriate time */ | ||
484 | if (!pv->M1 || !pv->M2) | ||
485 | return 0; | ||
486 | |||
487 | return pv->N1 * pv->N2 * pv->refclk / (pv->M1 * pv->M2) >> pv->log2P; | ||
488 | } | ||
489 | |||
490 | int | ||
491 | nouveau_hw_get_clock(struct drm_device *dev, enum pll_types plltype) | ||
492 | { | ||
493 | struct nouveau_pll_vals pllvals; | ||
494 | |||
495 | if (plltype == MPLL && (dev->pci_device & 0x0ff0) == CHIPSET_NFORCE) { | ||
496 | uint32_t mpllP; | ||
497 | |||
498 | pci_read_config_dword(pci_get_bus_and_slot(0, 3), 0x6c, &mpllP); | ||
499 | if (!mpllP) | ||
500 | mpllP = 4; | ||
501 | |||
502 | return 400000 / mpllP; | ||
503 | } else | ||
504 | if (plltype == MPLL && (dev->pci_device & 0xff0) == CHIPSET_NFORCE2) { | ||
505 | uint32_t clock; | ||
506 | |||
507 | pci_read_config_dword(pci_get_bus_and_slot(0, 5), 0x4c, &clock); | ||
508 | return clock; | ||
509 | } | ||
510 | |||
511 | nouveau_hw_get_pllvals(dev, plltype, &pllvals); | ||
512 | |||
513 | return nouveau_hw_pllvals_to_clk(&pllvals); | ||
514 | } | ||
515 | |||
516 | static void | ||
517 | nouveau_hw_fix_bad_vpll(struct drm_device *dev, int head) | ||
518 | { | ||
519 | /* the vpll on an unused head can come up with a random value, way | ||
520 | * beyond the pll limits. for some reason this causes the chip to | ||
521 | * lock up when reading the dac palette regs, so set a valid pll here | ||
522 | * when such a condition detected. only seen on nv11 to date | ||
523 | */ | ||
524 | |||
525 | struct pll_lims pll_lim; | ||
526 | struct nouveau_pll_vals pv; | ||
527 | uint32_t pllreg = head ? NV_RAMDAC_VPLL2 : NV_PRAMDAC_VPLL_COEFF; | ||
528 | |||
529 | if (get_pll_limits(dev, head ? VPLL2 : VPLL1, &pll_lim)) | ||
530 | return; | ||
531 | nouveau_hw_get_pllvals(dev, head ? VPLL2 : VPLL1, &pv); | ||
532 | |||
533 | if (pv.M1 >= pll_lim.vco1.min_m && pv.M1 <= pll_lim.vco1.max_m && | ||
534 | pv.N1 >= pll_lim.vco1.min_n && pv.N1 <= pll_lim.vco1.max_n && | ||
535 | pv.log2P <= pll_lim.max_log2p) | ||
536 | return; | ||
537 | |||
538 | NV_WARN(dev, "VPLL %d outwith limits, attempting to fix\n", head + 1); | ||
539 | |||
540 | /* set lowest clock within static limits */ | ||
541 | pv.M1 = pll_lim.vco1.max_m; | ||
542 | pv.N1 = pll_lim.vco1.min_n; | ||
543 | pv.log2P = pll_lim.max_usable_log2p; | ||
544 | nouveau_hw_setpll(dev, pllreg, &pv); | ||
545 | } | ||
546 | |||
547 | /* | ||
548 | * vga font save/restore | ||
549 | */ | ||
550 | |||
551 | static void nouveau_vga_font_io(struct drm_device *dev, | ||
552 | void __iomem *iovram, | ||
553 | bool save, unsigned plane) | ||
554 | { | ||
555 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
556 | unsigned i; | ||
557 | |||
558 | NVWriteVgaSeq(dev, 0, NV_VIO_SR_PLANE_MASK_INDEX, 1 << plane); | ||
559 | NVWriteVgaGr(dev, 0, NV_VIO_GX_READ_MAP_INDEX, plane); | ||
560 | for (i = 0; i < 16384; i++) { | ||
561 | if (save) { | ||
562 | dev_priv->saved_vga_font[plane][i] = | ||
563 | ioread32_native(iovram + i * 4); | ||
564 | } else { | ||
565 | iowrite32_native(dev_priv->saved_vga_font[plane][i], | ||
566 | iovram + i * 4); | ||
567 | } | ||
568 | } | ||
569 | } | ||
570 | |||
571 | void | ||
572 | nouveau_hw_save_vga_fonts(struct drm_device *dev, bool save) | ||
573 | { | ||
574 | uint8_t misc, gr4, gr5, gr6, seq2, seq4; | ||
575 | bool graphicsmode; | ||
576 | unsigned plane; | ||
577 | void __iomem *iovram; | ||
578 | |||
579 | if (nv_two_heads(dev)) | ||
580 | NVSetOwner(dev, 0); | ||
581 | |||
582 | NVSetEnablePalette(dev, 0, true); | ||
583 | graphicsmode = NVReadVgaAttr(dev, 0, NV_CIO_AR_MODE_INDEX) & 1; | ||
584 | NVSetEnablePalette(dev, 0, false); | ||
585 | |||
586 | if (graphicsmode) /* graphics mode => framebuffer => no need to save */ | ||
587 | return; | ||
588 | |||
589 | NV_INFO(dev, "%sing VGA fonts\n", save ? "Sav" : "Restor"); | ||
590 | |||
591 | /* map first 64KiB of VRAM, holds VGA fonts etc */ | ||
592 | iovram = ioremap(pci_resource_start(dev->pdev, 1), 65536); | ||
593 | if (!iovram) { | ||
594 | NV_ERROR(dev, "Failed to map VRAM, " | ||
595 | "cannot save/restore VGA fonts.\n"); | ||
596 | return; | ||
597 | } | ||
598 | |||
599 | if (nv_two_heads(dev)) | ||
600 | NVBlankScreen(dev, 1, true); | ||
601 | NVBlankScreen(dev, 0, true); | ||
602 | |||
603 | /* save control regs */ | ||
604 | misc = NVReadPRMVIO(dev, 0, NV_PRMVIO_MISC__READ); | ||
605 | seq2 = NVReadVgaSeq(dev, 0, NV_VIO_SR_PLANE_MASK_INDEX); | ||
606 | seq4 = NVReadVgaSeq(dev, 0, NV_VIO_SR_MEM_MODE_INDEX); | ||
607 | gr4 = NVReadVgaGr(dev, 0, NV_VIO_GX_READ_MAP_INDEX); | ||
608 | gr5 = NVReadVgaGr(dev, 0, NV_VIO_GX_MODE_INDEX); | ||
609 | gr6 = NVReadVgaGr(dev, 0, NV_VIO_GX_MISC_INDEX); | ||
610 | |||
611 | NVWritePRMVIO(dev, 0, NV_PRMVIO_MISC__WRITE, 0x67); | ||
612 | NVWriteVgaSeq(dev, 0, NV_VIO_SR_MEM_MODE_INDEX, 0x6); | ||
613 | NVWriteVgaGr(dev, 0, NV_VIO_GX_MODE_INDEX, 0x0); | ||
614 | NVWriteVgaGr(dev, 0, NV_VIO_GX_MISC_INDEX, 0x5); | ||
615 | |||
616 | /* store font in planes 0..3 */ | ||
617 | for (plane = 0; plane < 4; plane++) | ||
618 | nouveau_vga_font_io(dev, iovram, save, plane); | ||
619 | |||
620 | /* restore control regs */ | ||
621 | NVWritePRMVIO(dev, 0, NV_PRMVIO_MISC__WRITE, misc); | ||
622 | NVWriteVgaGr(dev, 0, NV_VIO_GX_READ_MAP_INDEX, gr4); | ||
623 | NVWriteVgaGr(dev, 0, NV_VIO_GX_MODE_INDEX, gr5); | ||
624 | NVWriteVgaGr(dev, 0, NV_VIO_GX_MISC_INDEX, gr6); | ||
625 | NVWriteVgaSeq(dev, 0, NV_VIO_SR_PLANE_MASK_INDEX, seq2); | ||
626 | NVWriteVgaSeq(dev, 0, NV_VIO_SR_MEM_MODE_INDEX, seq4); | ||
627 | |||
628 | if (nv_two_heads(dev)) | ||
629 | NVBlankScreen(dev, 1, false); | ||
630 | NVBlankScreen(dev, 0, false); | ||
631 | |||
632 | iounmap(iovram); | ||
633 | } | ||
634 | |||
635 | /* | ||
636 | * mode state save/load | ||
637 | */ | ||
638 | |||
639 | static void | ||
640 | rd_cio_state(struct drm_device *dev, int head, | ||
641 | struct nv04_crtc_reg *crtcstate, int index) | ||
642 | { | ||
643 | crtcstate->CRTC[index] = NVReadVgaCrtc(dev, head, index); | ||
644 | } | ||
645 | |||
646 | static void | ||
647 | wr_cio_state(struct drm_device *dev, int head, | ||
648 | struct nv04_crtc_reg *crtcstate, int index) | ||
649 | { | ||
650 | NVWriteVgaCrtc(dev, head, index, crtcstate->CRTC[index]); | ||
651 | } | ||
652 | |||
653 | static void | ||
654 | nv_save_state_ramdac(struct drm_device *dev, int head, | ||
655 | struct nv04_mode_state *state) | ||
656 | { | ||
657 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
658 | struct nv04_crtc_reg *regp = &state->crtc_reg[head]; | ||
659 | int i; | ||
660 | |||
661 | if (dev_priv->card_type >= NV_10) | ||
662 | regp->nv10_cursync = NVReadRAMDAC(dev, head, NV_RAMDAC_NV10_CURSYNC); | ||
663 | |||
664 | nouveau_hw_get_pllvals(dev, head ? VPLL2 : VPLL1, ®p->pllvals); | ||
665 | state->pllsel = NVReadRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT); | ||
666 | if (nv_two_heads(dev)) | ||
667 | state->sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK); | ||
668 | if (dev_priv->chipset == 0x11) | ||
669 | regp->dither = NVReadRAMDAC(dev, head, NV_RAMDAC_DITHER_NV11); | ||
670 | |||
671 | regp->ramdac_gen_ctrl = NVReadRAMDAC(dev, head, NV_PRAMDAC_GENERAL_CONTROL); | ||
672 | |||
673 | if (nv_gf4_disp_arch(dev)) | ||
674 | regp->ramdac_630 = NVReadRAMDAC(dev, head, NV_PRAMDAC_630); | ||
675 | if (dev_priv->chipset >= 0x30) | ||
676 | regp->ramdac_634 = NVReadRAMDAC(dev, head, NV_PRAMDAC_634); | ||
677 | |||
678 | regp->tv_setup = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP); | ||
679 | regp->tv_vtotal = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_VTOTAL); | ||
680 | regp->tv_vskew = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_VSKEW); | ||
681 | regp->tv_vsync_delay = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_VSYNC_DELAY); | ||
682 | regp->tv_htotal = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HTOTAL); | ||
683 | regp->tv_hskew = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HSKEW); | ||
684 | regp->tv_hsync_delay = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY); | ||
685 | regp->tv_hsync_delay2 = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY2); | ||
686 | |||
687 | for (i = 0; i < 7; i++) { | ||
688 | uint32_t ramdac_reg = NV_PRAMDAC_FP_VDISPLAY_END + (i * 4); | ||
689 | regp->fp_vert_regs[i] = NVReadRAMDAC(dev, head, ramdac_reg); | ||
690 | regp->fp_horiz_regs[i] = NVReadRAMDAC(dev, head, ramdac_reg + 0x20); | ||
691 | } | ||
692 | |||
693 | if (nv_gf4_disp_arch(dev)) { | ||
694 | regp->dither = NVReadRAMDAC(dev, head, NV_RAMDAC_FP_DITHER); | ||
695 | for (i = 0; i < 3; i++) { | ||
696 | regp->dither_regs[i] = NVReadRAMDAC(dev, head, NV_PRAMDAC_850 + i * 4); | ||
697 | regp->dither_regs[i + 3] = NVReadRAMDAC(dev, head, NV_PRAMDAC_85C + i * 4); | ||
698 | } | ||
699 | } | ||
700 | |||
701 | regp->fp_control = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL); | ||
702 | regp->fp_debug_0 = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_0); | ||
703 | if (!nv_gf4_disp_arch(dev) && head == 0) { | ||
704 | /* early chips don't allow access to PRAMDAC_TMDS_* without | ||
705 | * the head A FPCLK on (nv11 even locks up) */ | ||
706 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_FP_DEBUG_0, regp->fp_debug_0 & | ||
707 | ~NV_PRAMDAC_FP_DEBUG_0_PWRDOWN_FPCLK); | ||
708 | } | ||
709 | regp->fp_debug_1 = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_1); | ||
710 | regp->fp_debug_2 = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_2); | ||
711 | |||
712 | regp->fp_margin_color = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_MARGIN_COLOR); | ||
713 | |||
714 | if (nv_gf4_disp_arch(dev)) | ||
715 | regp->ramdac_8c0 = NVReadRAMDAC(dev, head, NV_PRAMDAC_8C0); | ||
716 | |||
717 | if (dev_priv->card_type == NV_40) { | ||
718 | regp->ramdac_a20 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A20); | ||
719 | regp->ramdac_a24 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A24); | ||
720 | regp->ramdac_a34 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A34); | ||
721 | |||
722 | for (i = 0; i < 38; i++) | ||
723 | regp->ctv_regs[i] = NVReadRAMDAC(dev, head, | ||
724 | NV_PRAMDAC_CTV + 4*i); | ||
725 | } | ||
726 | } | ||
727 | |||
728 | static void | ||
729 | nv_load_state_ramdac(struct drm_device *dev, int head, | ||
730 | struct nv04_mode_state *state) | ||
731 | { | ||
732 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
733 | struct nv04_crtc_reg *regp = &state->crtc_reg[head]; | ||
734 | uint32_t pllreg = head ? NV_RAMDAC_VPLL2 : NV_PRAMDAC_VPLL_COEFF; | ||
735 | int i; | ||
736 | |||
737 | if (dev_priv->card_type >= NV_10) | ||
738 | NVWriteRAMDAC(dev, head, NV_RAMDAC_NV10_CURSYNC, regp->nv10_cursync); | ||
739 | |||
740 | nouveau_hw_setpll(dev, pllreg, ®p->pllvals); | ||
741 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT, state->pllsel); | ||
742 | if (nv_two_heads(dev)) | ||
743 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, state->sel_clk); | ||
744 | if (dev_priv->chipset == 0x11) | ||
745 | NVWriteRAMDAC(dev, head, NV_RAMDAC_DITHER_NV11, regp->dither); | ||
746 | |||
747 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_GENERAL_CONTROL, regp->ramdac_gen_ctrl); | ||
748 | |||
749 | if (nv_gf4_disp_arch(dev)) | ||
750 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_630, regp->ramdac_630); | ||
751 | if (dev_priv->chipset >= 0x30) | ||
752 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_634, regp->ramdac_634); | ||
753 | |||
754 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP, regp->tv_setup); | ||
755 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_VTOTAL, regp->tv_vtotal); | ||
756 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_VSKEW, regp->tv_vskew); | ||
757 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_VSYNC_DELAY, regp->tv_vsync_delay); | ||
758 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HTOTAL, regp->tv_htotal); | ||
759 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HSKEW, regp->tv_hskew); | ||
760 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY, regp->tv_hsync_delay); | ||
761 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY2, regp->tv_hsync_delay2); | ||
762 | |||
763 | for (i = 0; i < 7; i++) { | ||
764 | uint32_t ramdac_reg = NV_PRAMDAC_FP_VDISPLAY_END + (i * 4); | ||
765 | |||
766 | NVWriteRAMDAC(dev, head, ramdac_reg, regp->fp_vert_regs[i]); | ||
767 | NVWriteRAMDAC(dev, head, ramdac_reg + 0x20, regp->fp_horiz_regs[i]); | ||
768 | } | ||
769 | |||
770 | if (nv_gf4_disp_arch(dev)) { | ||
771 | NVWriteRAMDAC(dev, head, NV_RAMDAC_FP_DITHER, regp->dither); | ||
772 | for (i = 0; i < 3; i++) { | ||
773 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_850 + i * 4, regp->dither_regs[i]); | ||
774 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_85C + i * 4, regp->dither_regs[i + 3]); | ||
775 | } | ||
776 | } | ||
777 | |||
778 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL, regp->fp_control); | ||
779 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_0, regp->fp_debug_0); | ||
780 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_1, regp->fp_debug_1); | ||
781 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_2, regp->fp_debug_2); | ||
782 | |||
783 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_MARGIN_COLOR, regp->fp_margin_color); | ||
784 | |||
785 | if (nv_gf4_disp_arch(dev)) | ||
786 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_8C0, regp->ramdac_8c0); | ||
787 | |||
788 | if (dev_priv->card_type == NV_40) { | ||
789 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_A20, regp->ramdac_a20); | ||
790 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_A24, regp->ramdac_a24); | ||
791 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_A34, regp->ramdac_a34); | ||
792 | |||
793 | for (i = 0; i < 38; i++) | ||
794 | NVWriteRAMDAC(dev, head, | ||
795 | NV_PRAMDAC_CTV + 4*i, regp->ctv_regs[i]); | ||
796 | } | ||
797 | } | ||
798 | |||
799 | static void | ||
800 | nv_save_state_vga(struct drm_device *dev, int head, | ||
801 | struct nv04_mode_state *state) | ||
802 | { | ||
803 | struct nv04_crtc_reg *regp = &state->crtc_reg[head]; | ||
804 | int i; | ||
805 | |||
806 | regp->MiscOutReg = NVReadPRMVIO(dev, head, NV_PRMVIO_MISC__READ); | ||
807 | |||
808 | for (i = 0; i < 25; i++) | ||
809 | rd_cio_state(dev, head, regp, i); | ||
810 | |||
811 | NVSetEnablePalette(dev, head, true); | ||
812 | for (i = 0; i < 21; i++) | ||
813 | regp->Attribute[i] = NVReadVgaAttr(dev, head, i); | ||
814 | NVSetEnablePalette(dev, head, false); | ||
815 | |||
816 | for (i = 0; i < 9; i++) | ||
817 | regp->Graphics[i] = NVReadVgaGr(dev, head, i); | ||
818 | |||
819 | for (i = 0; i < 5; i++) | ||
820 | regp->Sequencer[i] = NVReadVgaSeq(dev, head, i); | ||
821 | } | ||
822 | |||
823 | static void | ||
824 | nv_load_state_vga(struct drm_device *dev, int head, | ||
825 | struct nv04_mode_state *state) | ||
826 | { | ||
827 | struct nv04_crtc_reg *regp = &state->crtc_reg[head]; | ||
828 | int i; | ||
829 | |||
830 | NVWritePRMVIO(dev, head, NV_PRMVIO_MISC__WRITE, regp->MiscOutReg); | ||
831 | |||
832 | for (i = 0; i < 5; i++) | ||
833 | NVWriteVgaSeq(dev, head, i, regp->Sequencer[i]); | ||
834 | |||
835 | nv_lock_vga_crtc_base(dev, head, false); | ||
836 | for (i = 0; i < 25; i++) | ||
837 | wr_cio_state(dev, head, regp, i); | ||
838 | nv_lock_vga_crtc_base(dev, head, true); | ||
839 | |||
840 | for (i = 0; i < 9; i++) | ||
841 | NVWriteVgaGr(dev, head, i, regp->Graphics[i]); | ||
842 | |||
843 | NVSetEnablePalette(dev, head, true); | ||
844 | for (i = 0; i < 21; i++) | ||
845 | NVWriteVgaAttr(dev, head, i, regp->Attribute[i]); | ||
846 | NVSetEnablePalette(dev, head, false); | ||
847 | } | ||
848 | |||
849 | static void | ||
850 | nv_save_state_ext(struct drm_device *dev, int head, | ||
851 | struct nv04_mode_state *state) | ||
852 | { | ||
853 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
854 | struct nv04_crtc_reg *regp = &state->crtc_reg[head]; | ||
855 | int i; | ||
856 | |||
857 | rd_cio_state(dev, head, regp, NV_CIO_CRE_LCD__INDEX); | ||
858 | rd_cio_state(dev, head, regp, NV_CIO_CRE_RPC0_INDEX); | ||
859 | rd_cio_state(dev, head, regp, NV_CIO_CRE_RPC1_INDEX); | ||
860 | rd_cio_state(dev, head, regp, NV_CIO_CRE_LSR_INDEX); | ||
861 | rd_cio_state(dev, head, regp, NV_CIO_CRE_PIXEL_INDEX); | ||
862 | rd_cio_state(dev, head, regp, NV_CIO_CRE_HEB__INDEX); | ||
863 | rd_cio_state(dev, head, regp, NV_CIO_CRE_ENH_INDEX); | ||
864 | |||
865 | rd_cio_state(dev, head, regp, NV_CIO_CRE_FF_INDEX); | ||
866 | rd_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__INDEX); | ||
867 | rd_cio_state(dev, head, regp, NV_CIO_CRE_21); | ||
868 | if (dev_priv->card_type >= NV_30) | ||
869 | rd_cio_state(dev, head, regp, NV_CIO_CRE_47); | ||
870 | rd_cio_state(dev, head, regp, NV_CIO_CRE_49); | ||
871 | rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX); | ||
872 | rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX); | ||
873 | rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX); | ||
874 | rd_cio_state(dev, head, regp, NV_CIO_CRE_ILACE__INDEX); | ||
875 | |||
876 | if (dev_priv->card_type >= NV_10) { | ||
877 | regp->crtc_830 = NVReadCRTC(dev, head, NV_PCRTC_830); | ||
878 | regp->crtc_834 = NVReadCRTC(dev, head, NV_PCRTC_834); | ||
879 | |||
880 | if (dev_priv->card_type >= NV_30) | ||
881 | regp->gpio_ext = NVReadCRTC(dev, head, NV_PCRTC_GPIO_EXT); | ||
882 | |||
883 | if (dev_priv->card_type == NV_40) | ||
884 | regp->crtc_850 = NVReadCRTC(dev, head, NV_PCRTC_850); | ||
885 | |||
886 | if (nv_two_heads(dev)) | ||
887 | regp->crtc_eng_ctrl = NVReadCRTC(dev, head, NV_PCRTC_ENGINE_CTRL); | ||
888 | regp->cursor_cfg = NVReadCRTC(dev, head, NV_PCRTC_CURSOR_CONFIG); | ||
889 | } | ||
890 | |||
891 | regp->crtc_cfg = NVReadCRTC(dev, head, NV_PCRTC_CONFIG); | ||
892 | |||
893 | rd_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH3__INDEX); | ||
894 | rd_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH4__INDEX); | ||
895 | if (dev_priv->card_type >= NV_10) { | ||
896 | rd_cio_state(dev, head, regp, NV_CIO_CRE_EBR_INDEX); | ||
897 | rd_cio_state(dev, head, regp, NV_CIO_CRE_CSB); | ||
898 | rd_cio_state(dev, head, regp, NV_CIO_CRE_4B); | ||
899 | rd_cio_state(dev, head, regp, NV_CIO_CRE_TVOUT_LATENCY); | ||
900 | } | ||
901 | /* NV11 and NV20 don't have this, they stop at 0x52. */ | ||
902 | if (nv_gf4_disp_arch(dev)) { | ||
903 | rd_cio_state(dev, head, regp, NV_CIO_CRE_53); | ||
904 | rd_cio_state(dev, head, regp, NV_CIO_CRE_54); | ||
905 | |||
906 | for (i = 0; i < 0x10; i++) | ||
907 | regp->CR58[i] = NVReadVgaCrtc5758(dev, head, i); | ||
908 | rd_cio_state(dev, head, regp, NV_CIO_CRE_59); | ||
909 | rd_cio_state(dev, head, regp, NV_CIO_CRE_5B); | ||
910 | |||
911 | rd_cio_state(dev, head, regp, NV_CIO_CRE_85); | ||
912 | rd_cio_state(dev, head, regp, NV_CIO_CRE_86); | ||
913 | } | ||
914 | |||
915 | regp->fb_start = NVReadCRTC(dev, head, NV_PCRTC_START); | ||
916 | } | ||
917 | |||
918 | static void | ||
919 | nv_load_state_ext(struct drm_device *dev, int head, | ||
920 | struct nv04_mode_state *state) | ||
921 | { | ||
922 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
923 | struct nv04_crtc_reg *regp = &state->crtc_reg[head]; | ||
924 | uint32_t reg900; | ||
925 | int i; | ||
926 | |||
927 | if (dev_priv->card_type >= NV_10) { | ||
928 | if (nv_two_heads(dev)) | ||
929 | /* setting ENGINE_CTRL (EC) *must* come before | ||
930 | * CIO_CRE_LCD, as writing CRE_LCD sets bits 16 & 17 in | ||
931 | * EC that should not be overwritten by writing stale EC | ||
932 | */ | ||
933 | NVWriteCRTC(dev, head, NV_PCRTC_ENGINE_CTRL, regp->crtc_eng_ctrl); | ||
934 | |||
935 | nvWriteVIDEO(dev, NV_PVIDEO_STOP, 1); | ||
936 | nvWriteVIDEO(dev, NV_PVIDEO_INTR_EN, 0); | ||
937 | nvWriteVIDEO(dev, NV_PVIDEO_OFFSET_BUFF(0), 0); | ||
938 | nvWriteVIDEO(dev, NV_PVIDEO_OFFSET_BUFF(1), 0); | ||
939 | nvWriteVIDEO(dev, NV_PVIDEO_LIMIT(0), dev_priv->fb_available_size - 1); | ||
940 | nvWriteVIDEO(dev, NV_PVIDEO_LIMIT(1), dev_priv->fb_available_size - 1); | ||
941 | nvWriteVIDEO(dev, NV_PVIDEO_UVPLANE_LIMIT(0), dev_priv->fb_available_size - 1); | ||
942 | nvWriteVIDEO(dev, NV_PVIDEO_UVPLANE_LIMIT(1), dev_priv->fb_available_size - 1); | ||
943 | nvWriteMC(dev, NV_PBUS_POWERCTRL_2, 0); | ||
944 | |||
945 | NVWriteCRTC(dev, head, NV_PCRTC_CURSOR_CONFIG, regp->cursor_cfg); | ||
946 | NVWriteCRTC(dev, head, NV_PCRTC_830, regp->crtc_830); | ||
947 | NVWriteCRTC(dev, head, NV_PCRTC_834, regp->crtc_834); | ||
948 | |||
949 | if (dev_priv->card_type >= NV_30) | ||
950 | NVWriteCRTC(dev, head, NV_PCRTC_GPIO_EXT, regp->gpio_ext); | ||
951 | |||
952 | if (dev_priv->card_type == NV_40) { | ||
953 | NVWriteCRTC(dev, head, NV_PCRTC_850, regp->crtc_850); | ||
954 | |||
955 | reg900 = NVReadRAMDAC(dev, head, NV_PRAMDAC_900); | ||
956 | if (regp->crtc_cfg == NV_PCRTC_CONFIG_START_ADDRESS_HSYNC) | ||
957 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_900, reg900 | 0x10000); | ||
958 | else | ||
959 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_900, reg900 & ~0x10000); | ||
960 | } | ||
961 | } | ||
962 | |||
963 | NVWriteCRTC(dev, head, NV_PCRTC_CONFIG, regp->crtc_cfg); | ||
964 | |||
965 | wr_cio_state(dev, head, regp, NV_CIO_CRE_RPC0_INDEX); | ||
966 | wr_cio_state(dev, head, regp, NV_CIO_CRE_RPC1_INDEX); | ||
967 | wr_cio_state(dev, head, regp, NV_CIO_CRE_LSR_INDEX); | ||
968 | wr_cio_state(dev, head, regp, NV_CIO_CRE_PIXEL_INDEX); | ||
969 | wr_cio_state(dev, head, regp, NV_CIO_CRE_LCD__INDEX); | ||
970 | wr_cio_state(dev, head, regp, NV_CIO_CRE_HEB__INDEX); | ||
971 | wr_cio_state(dev, head, regp, NV_CIO_CRE_ENH_INDEX); | ||
972 | wr_cio_state(dev, head, regp, NV_CIO_CRE_FF_INDEX); | ||
973 | wr_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__INDEX); | ||
974 | if (dev_priv->card_type >= NV_30) | ||
975 | wr_cio_state(dev, head, regp, NV_CIO_CRE_47); | ||
976 | |||
977 | wr_cio_state(dev, head, regp, NV_CIO_CRE_49); | ||
978 | wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX); | ||
979 | wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX); | ||
980 | wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX); | ||
981 | if (dev_priv->card_type == NV_40) | ||
982 | nv_fix_nv40_hw_cursor(dev, head); | ||
983 | wr_cio_state(dev, head, regp, NV_CIO_CRE_ILACE__INDEX); | ||
984 | |||
985 | wr_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH3__INDEX); | ||
986 | wr_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH4__INDEX); | ||
987 | if (dev_priv->card_type >= NV_10) { | ||
988 | wr_cio_state(dev, head, regp, NV_CIO_CRE_EBR_INDEX); | ||
989 | wr_cio_state(dev, head, regp, NV_CIO_CRE_CSB); | ||
990 | wr_cio_state(dev, head, regp, NV_CIO_CRE_4B); | ||
991 | wr_cio_state(dev, head, regp, NV_CIO_CRE_TVOUT_LATENCY); | ||
992 | } | ||
993 | /* NV11 and NV20 stop at 0x52. */ | ||
994 | if (nv_gf4_disp_arch(dev)) { | ||
995 | if (dev_priv->card_type == NV_10) { | ||
996 | /* Not waiting for vertical retrace before modifying | ||
997 | CRE_53/CRE_54 causes lockups. */ | ||
998 | nouveau_wait_until(dev, 650000000, NV_PRMCIO_INP0__COLOR, 0x8, 0x8); | ||
999 | nouveau_wait_until(dev, 650000000, NV_PRMCIO_INP0__COLOR, 0x8, 0x0); | ||
1000 | } | ||
1001 | |||
1002 | wr_cio_state(dev, head, regp, NV_CIO_CRE_53); | ||
1003 | wr_cio_state(dev, head, regp, NV_CIO_CRE_54); | ||
1004 | |||
1005 | for (i = 0; i < 0x10; i++) | ||
1006 | NVWriteVgaCrtc5758(dev, head, i, regp->CR58[i]); | ||
1007 | wr_cio_state(dev, head, regp, NV_CIO_CRE_59); | ||
1008 | wr_cio_state(dev, head, regp, NV_CIO_CRE_5B); | ||
1009 | |||
1010 | wr_cio_state(dev, head, regp, NV_CIO_CRE_85); | ||
1011 | wr_cio_state(dev, head, regp, NV_CIO_CRE_86); | ||
1012 | } | ||
1013 | |||
1014 | NVWriteCRTC(dev, head, NV_PCRTC_START, regp->fb_start); | ||
1015 | |||
1016 | /* Setting 1 on this value gives you interrupts for every vblank period. */ | ||
1017 | NVWriteCRTC(dev, head, NV_PCRTC_INTR_EN_0, 0); | ||
1018 | NVWriteCRTC(dev, head, NV_PCRTC_INTR_0, NV_PCRTC_INTR_0_VBLANK); | ||
1019 | } | ||
1020 | |||
1021 | static void | ||
1022 | nv_save_state_palette(struct drm_device *dev, int head, | ||
1023 | struct nv04_mode_state *state) | ||
1024 | { | ||
1025 | int head_offset = head * NV_PRMDIO_SIZE, i; | ||
1026 | |||
1027 | nv_wr08(dev, NV_PRMDIO_PIXEL_MASK + head_offset, | ||
1028 | NV_PRMDIO_PIXEL_MASK_MASK); | ||
1029 | nv_wr08(dev, NV_PRMDIO_READ_MODE_ADDRESS + head_offset, 0x0); | ||
1030 | |||
1031 | for (i = 0; i < 768; i++) { | ||
1032 | state->crtc_reg[head].DAC[i] = nv_rd08(dev, | ||
1033 | NV_PRMDIO_PALETTE_DATA + head_offset); | ||
1034 | } | ||
1035 | |||
1036 | NVSetEnablePalette(dev, head, false); | ||
1037 | } | ||
1038 | |||
1039 | void | ||
1040 | nouveau_hw_load_state_palette(struct drm_device *dev, int head, | ||
1041 | struct nv04_mode_state *state) | ||
1042 | { | ||
1043 | int head_offset = head * NV_PRMDIO_SIZE, i; | ||
1044 | |||
1045 | nv_wr08(dev, NV_PRMDIO_PIXEL_MASK + head_offset, | ||
1046 | NV_PRMDIO_PIXEL_MASK_MASK); | ||
1047 | nv_wr08(dev, NV_PRMDIO_WRITE_MODE_ADDRESS + head_offset, 0x0); | ||
1048 | |||
1049 | for (i = 0; i < 768; i++) { | ||
1050 | nv_wr08(dev, NV_PRMDIO_PALETTE_DATA + head_offset, | ||
1051 | state->crtc_reg[head].DAC[i]); | ||
1052 | } | ||
1053 | |||
1054 | NVSetEnablePalette(dev, head, false); | ||
1055 | } | ||
1056 | |||
1057 | void nouveau_hw_save_state(struct drm_device *dev, int head, | ||
1058 | struct nv04_mode_state *state) | ||
1059 | { | ||
1060 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
1061 | |||
1062 | if (dev_priv->chipset == 0x11) | ||
1063 | /* NB: no attempt is made to restore the bad pll later on */ | ||
1064 | nouveau_hw_fix_bad_vpll(dev, head); | ||
1065 | nv_save_state_ramdac(dev, head, state); | ||
1066 | nv_save_state_vga(dev, head, state); | ||
1067 | nv_save_state_palette(dev, head, state); | ||
1068 | nv_save_state_ext(dev, head, state); | ||
1069 | } | ||
1070 | |||
1071 | void nouveau_hw_load_state(struct drm_device *dev, int head, | ||
1072 | struct nv04_mode_state *state) | ||
1073 | { | ||
1074 | NVVgaProtect(dev, head, true); | ||
1075 | nv_load_state_ramdac(dev, head, state); | ||
1076 | nv_load_state_ext(dev, head, state); | ||
1077 | nouveau_hw_load_state_palette(dev, head, state); | ||
1078 | nv_load_state_vga(dev, head, state); | ||
1079 | NVVgaProtect(dev, head, false); | ||
1080 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_hw.h b/drivers/gpu/drm/nouveau/nouveau_hw.h new file mode 100644 index 000000000000..869130f83602 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_hw.h | |||
@@ -0,0 +1,455 @@ | |||
1 | /* | ||
2 | * Copyright 2008 Stuart Bennett | ||
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 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||
18 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF | ||
19 | * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
20 | * SOFTWARE. | ||
21 | */ | ||
22 | |||
23 | #ifndef __NOUVEAU_HW_H__ | ||
24 | #define __NOUVEAU_HW_H__ | ||
25 | |||
26 | #include "drmP.h" | ||
27 | #include "nouveau_drv.h" | ||
28 | |||
29 | #define MASK(field) ( \ | ||
30 | (0xffffffff >> (31 - ((1 ? field) - (0 ? field)))) << (0 ? field)) | ||
31 | |||
32 | #define XLATE(src, srclowbit, outfield) ( \ | ||
33 | (((src) >> (srclowbit)) << (0 ? outfield)) & MASK(outfield)) | ||
34 | |||
35 | void NVWriteVgaSeq(struct drm_device *, int head, uint8_t index, uint8_t value); | ||
36 | uint8_t NVReadVgaSeq(struct drm_device *, int head, uint8_t index); | ||
37 | void NVWriteVgaGr(struct drm_device *, int head, uint8_t index, uint8_t value); | ||
38 | uint8_t NVReadVgaGr(struct drm_device *, int head, uint8_t index); | ||
39 | void NVSetOwner(struct drm_device *, int owner); | ||
40 | void NVBlankScreen(struct drm_device *, int head, bool blank); | ||
41 | void nouveau_hw_setpll(struct drm_device *, uint32_t reg1, | ||
42 | struct nouveau_pll_vals *pv); | ||
43 | int nouveau_hw_get_pllvals(struct drm_device *, enum pll_types plltype, | ||
44 | struct nouveau_pll_vals *pllvals); | ||
45 | int nouveau_hw_pllvals_to_clk(struct nouveau_pll_vals *pllvals); | ||
46 | int nouveau_hw_get_clock(struct drm_device *, enum pll_types plltype); | ||
47 | void nouveau_hw_save_vga_fonts(struct drm_device *, bool save); | ||
48 | void nouveau_hw_save_state(struct drm_device *, int head, | ||
49 | struct nv04_mode_state *state); | ||
50 | void nouveau_hw_load_state(struct drm_device *, int head, | ||
51 | struct nv04_mode_state *state); | ||
52 | void nouveau_hw_load_state_palette(struct drm_device *, int head, | ||
53 | struct nv04_mode_state *state); | ||
54 | |||
55 | /* nouveau_calc.c */ | ||
56 | extern void nouveau_calc_arb(struct drm_device *, int vclk, int bpp, | ||
57 | int *burst, int *lwm); | ||
58 | extern int nouveau_calc_pll_mnp(struct drm_device *, struct pll_lims *pll_lim, | ||
59 | int clk, struct nouveau_pll_vals *pv); | ||
60 | |||
61 | static inline uint32_t | ||
62 | nvReadMC(struct drm_device *dev, uint32_t reg) | ||
63 | { | ||
64 | uint32_t val = nv_rd32(dev, reg); | ||
65 | NV_REG_DEBUG(MC, dev, "reg %08x val %08x\n", reg, val); | ||
66 | return val; | ||
67 | } | ||
68 | |||
69 | static inline void | ||
70 | nvWriteMC(struct drm_device *dev, uint32_t reg, uint32_t val) | ||
71 | { | ||
72 | NV_REG_DEBUG(MC, dev, "reg %08x val %08x\n", reg, val); | ||
73 | nv_wr32(dev, reg, val); | ||
74 | } | ||
75 | |||
76 | static inline uint32_t | ||
77 | nvReadVIDEO(struct drm_device *dev, uint32_t reg) | ||
78 | { | ||
79 | uint32_t val = nv_rd32(dev, reg); | ||
80 | NV_REG_DEBUG(VIDEO, dev, "reg %08x val %08x\n", reg, val); | ||
81 | return val; | ||
82 | } | ||
83 | |||
84 | static inline void | ||
85 | nvWriteVIDEO(struct drm_device *dev, uint32_t reg, uint32_t val) | ||
86 | { | ||
87 | NV_REG_DEBUG(VIDEO, dev, "reg %08x val %08x\n", reg, val); | ||
88 | nv_wr32(dev, reg, val); | ||
89 | } | ||
90 | |||
91 | static inline uint32_t | ||
92 | nvReadFB(struct drm_device *dev, uint32_t reg) | ||
93 | { | ||
94 | uint32_t val = nv_rd32(dev, reg); | ||
95 | NV_REG_DEBUG(FB, dev, "reg %08x val %08x\n", reg, val); | ||
96 | return val; | ||
97 | } | ||
98 | |||
99 | static inline void | ||
100 | nvWriteFB(struct drm_device *dev, uint32_t reg, uint32_t val) | ||
101 | { | ||
102 | NV_REG_DEBUG(FB, dev, "reg %08x val %08x\n", reg, val); | ||
103 | nv_wr32(dev, reg, val); | ||
104 | } | ||
105 | |||
106 | static inline uint32_t | ||
107 | nvReadEXTDEV(struct drm_device *dev, uint32_t reg) | ||
108 | { | ||
109 | uint32_t val = nv_rd32(dev, reg); | ||
110 | NV_REG_DEBUG(EXTDEV, dev, "reg %08x val %08x\n", reg, val); | ||
111 | return val; | ||
112 | } | ||
113 | |||
114 | static inline void | ||
115 | nvWriteEXTDEV(struct drm_device *dev, uint32_t reg, uint32_t val) | ||
116 | { | ||
117 | NV_REG_DEBUG(EXTDEV, dev, "reg %08x val %08x\n", reg, val); | ||
118 | nv_wr32(dev, reg, val); | ||
119 | } | ||
120 | |||
121 | static inline uint32_t NVReadCRTC(struct drm_device *dev, | ||
122 | int head, uint32_t reg) | ||
123 | { | ||
124 | uint32_t val; | ||
125 | if (head) | ||
126 | reg += NV_PCRTC0_SIZE; | ||
127 | val = nv_rd32(dev, reg); | ||
128 | NV_REG_DEBUG(CRTC, dev, "head %d reg %08x val %08x\n", head, reg, val); | ||
129 | return val; | ||
130 | } | ||
131 | |||
132 | static inline void NVWriteCRTC(struct drm_device *dev, | ||
133 | int head, uint32_t reg, uint32_t val) | ||
134 | { | ||
135 | if (head) | ||
136 | reg += NV_PCRTC0_SIZE; | ||
137 | NV_REG_DEBUG(CRTC, dev, "head %d reg %08x val %08x\n", head, reg, val); | ||
138 | nv_wr32(dev, reg, val); | ||
139 | } | ||
140 | |||
141 | static inline uint32_t NVReadRAMDAC(struct drm_device *dev, | ||
142 | int head, uint32_t reg) | ||
143 | { | ||
144 | uint32_t val; | ||
145 | if (head) | ||
146 | reg += NV_PRAMDAC0_SIZE; | ||
147 | val = nv_rd32(dev, reg); | ||
148 | NV_REG_DEBUG(RAMDAC, dev, "head %d reg %08x val %08x\n", | ||
149 | head, reg, val); | ||
150 | return val; | ||
151 | } | ||
152 | |||
153 | static inline void NVWriteRAMDAC(struct drm_device *dev, | ||
154 | int head, uint32_t reg, uint32_t val) | ||
155 | { | ||
156 | if (head) | ||
157 | reg += NV_PRAMDAC0_SIZE; | ||
158 | NV_REG_DEBUG(RAMDAC, dev, "head %d reg %08x val %08x\n", | ||
159 | head, reg, val); | ||
160 | nv_wr32(dev, reg, val); | ||
161 | } | ||
162 | |||
163 | static inline uint8_t nv_read_tmds(struct drm_device *dev, | ||
164 | int or, int dl, uint8_t address) | ||
165 | { | ||
166 | int ramdac = (or & OUTPUT_C) >> 2; | ||
167 | |||
168 | NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL + dl * 8, | ||
169 | NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE | address); | ||
170 | return NVReadRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_DATA + dl * 8); | ||
171 | } | ||
172 | |||
173 | static inline void nv_write_tmds(struct drm_device *dev, | ||
174 | int or, int dl, uint8_t address, | ||
175 | uint8_t data) | ||
176 | { | ||
177 | int ramdac = (or & OUTPUT_C) >> 2; | ||
178 | |||
179 | NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_DATA + dl * 8, data); | ||
180 | NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL + dl * 8, address); | ||
181 | } | ||
182 | |||
183 | static inline void NVWriteVgaCrtc(struct drm_device *dev, | ||
184 | int head, uint8_t index, uint8_t value) | ||
185 | { | ||
186 | NV_REG_DEBUG(VGACRTC, dev, "head %d index 0x%02x data 0x%02x\n", | ||
187 | head, index, value); | ||
188 | nv_wr08(dev, NV_PRMCIO_CRX__COLOR + head * NV_PRMCIO_SIZE, index); | ||
189 | nv_wr08(dev, NV_PRMCIO_CR__COLOR + head * NV_PRMCIO_SIZE, value); | ||
190 | } | ||
191 | |||
192 | static inline uint8_t NVReadVgaCrtc(struct drm_device *dev, | ||
193 | int head, uint8_t index) | ||
194 | { | ||
195 | uint8_t val; | ||
196 | nv_wr08(dev, NV_PRMCIO_CRX__COLOR + head * NV_PRMCIO_SIZE, index); | ||
197 | val = nv_rd08(dev, NV_PRMCIO_CR__COLOR + head * NV_PRMCIO_SIZE); | ||
198 | NV_REG_DEBUG(VGACRTC, dev, "head %d index 0x%02x data 0x%02x\n", | ||
199 | head, index, val); | ||
200 | return val; | ||
201 | } | ||
202 | |||
203 | /* CR57 and CR58 are a fun pair of regs. CR57 provides an index (0-0xf) for CR58 | ||
204 | * I suspect they in fact do nothing, but are merely a way to carry useful | ||
205 | * per-head variables around | ||
206 | * | ||
207 | * Known uses: | ||
208 | * CR57 CR58 | ||
209 | * 0x00 index to the appropriate dcb entry (or 7f for inactive) | ||
210 | * 0x02 dcb entry's "or" value (or 00 for inactive) | ||
211 | * 0x03 bit0 set for dual link (LVDS, possibly elsewhere too) | ||
212 | * 0x08 or 0x09 pxclk in MHz | ||
213 | * 0x0f laptop panel info - low nibble for PEXTDEV_BOOT_0 strap | ||
214 | * high nibble for xlat strap value | ||
215 | */ | ||
216 | |||
217 | static inline void | ||
218 | NVWriteVgaCrtc5758(struct drm_device *dev, int head, uint8_t index, uint8_t value) | ||
219 | { | ||
220 | NVWriteVgaCrtc(dev, head, NV_CIO_CRE_57, index); | ||
221 | NVWriteVgaCrtc(dev, head, NV_CIO_CRE_58, value); | ||
222 | } | ||
223 | |||
224 | static inline uint8_t NVReadVgaCrtc5758(struct drm_device *dev, int head, uint8_t index) | ||
225 | { | ||
226 | NVWriteVgaCrtc(dev, head, NV_CIO_CRE_57, index); | ||
227 | return NVReadVgaCrtc(dev, head, NV_CIO_CRE_58); | ||
228 | } | ||
229 | |||
230 | static inline uint8_t NVReadPRMVIO(struct drm_device *dev, | ||
231 | int head, uint32_t reg) | ||
232 | { | ||
233 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
234 | uint8_t val; | ||
235 | |||
236 | /* Only NV4x have two pvio ranges; other twoHeads cards MUST call | ||
237 | * NVSetOwner for the relevant head to be programmed */ | ||
238 | if (head && dev_priv->card_type == NV_40) | ||
239 | reg += NV_PRMVIO_SIZE; | ||
240 | |||
241 | val = nv_rd08(dev, reg); | ||
242 | NV_REG_DEBUG(RMVIO, dev, "head %d reg %08x val %02x\n", head, reg, val); | ||
243 | return val; | ||
244 | } | ||
245 | |||
246 | static inline void NVWritePRMVIO(struct drm_device *dev, | ||
247 | int head, uint32_t reg, uint8_t value) | ||
248 | { | ||
249 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
250 | |||
251 | /* Only NV4x have two pvio ranges; other twoHeads cards MUST call | ||
252 | * NVSetOwner for the relevant head to be programmed */ | ||
253 | if (head && dev_priv->card_type == NV_40) | ||
254 | reg += NV_PRMVIO_SIZE; | ||
255 | |||
256 | NV_REG_DEBUG(RMVIO, dev, "head %d reg %08x val %02x\n", | ||
257 | head, reg, value); | ||
258 | nv_wr08(dev, reg, value); | ||
259 | } | ||
260 | |||
261 | static inline void NVSetEnablePalette(struct drm_device *dev, int head, bool enable) | ||
262 | { | ||
263 | nv_rd08(dev, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE); | ||
264 | nv_wr08(dev, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, enable ? 0 : 0x20); | ||
265 | } | ||
266 | |||
267 | static inline bool NVGetEnablePalette(struct drm_device *dev, int head) | ||
268 | { | ||
269 | nv_rd08(dev, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE); | ||
270 | return !(nv_rd08(dev, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE) & 0x20); | ||
271 | } | ||
272 | |||
273 | static inline void NVWriteVgaAttr(struct drm_device *dev, | ||
274 | int head, uint8_t index, uint8_t value) | ||
275 | { | ||
276 | if (NVGetEnablePalette(dev, head)) | ||
277 | index &= ~0x20; | ||
278 | else | ||
279 | index |= 0x20; | ||
280 | |||
281 | nv_rd08(dev, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE); | ||
282 | NV_REG_DEBUG(VGAATTR, dev, "head %d index 0x%02x data 0x%02x\n", | ||
283 | head, index, value); | ||
284 | nv_wr08(dev, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, index); | ||
285 | nv_wr08(dev, NV_PRMCIO_AR__WRITE + head * NV_PRMCIO_SIZE, value); | ||
286 | } | ||
287 | |||
288 | static inline uint8_t NVReadVgaAttr(struct drm_device *dev, | ||
289 | int head, uint8_t index) | ||
290 | { | ||
291 | uint8_t val; | ||
292 | if (NVGetEnablePalette(dev, head)) | ||
293 | index &= ~0x20; | ||
294 | else | ||
295 | index |= 0x20; | ||
296 | |||
297 | nv_rd08(dev, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE); | ||
298 | nv_wr08(dev, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, index); | ||
299 | val = nv_rd08(dev, NV_PRMCIO_AR__READ + head * NV_PRMCIO_SIZE); | ||
300 | NV_REG_DEBUG(VGAATTR, dev, "head %d index 0x%02x data 0x%02x\n", | ||
301 | head, index, val); | ||
302 | return val; | ||
303 | } | ||
304 | |||
305 | static inline void NVVgaSeqReset(struct drm_device *dev, int head, bool start) | ||
306 | { | ||
307 | NVWriteVgaSeq(dev, head, NV_VIO_SR_RESET_INDEX, start ? 0x1 : 0x3); | ||
308 | } | ||
309 | |||
310 | static inline void NVVgaProtect(struct drm_device *dev, int head, bool protect) | ||
311 | { | ||
312 | uint8_t seq1 = NVReadVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX); | ||
313 | |||
314 | if (protect) { | ||
315 | NVVgaSeqReset(dev, head, true); | ||
316 | NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 | 0x20); | ||
317 | } else { | ||
318 | /* Reenable sequencer, then turn on screen */ | ||
319 | NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 & ~0x20); /* reenable display */ | ||
320 | NVVgaSeqReset(dev, head, false); | ||
321 | } | ||
322 | NVSetEnablePalette(dev, head, protect); | ||
323 | } | ||
324 | |||
325 | static inline bool | ||
326 | nv_heads_tied(struct drm_device *dev) | ||
327 | { | ||
328 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
329 | |||
330 | if (dev_priv->chipset == 0x11) | ||
331 | return !!(nvReadMC(dev, NV_PBUS_DEBUG_1) & (1 << 28)); | ||
332 | |||
333 | return NVReadVgaCrtc(dev, 0, NV_CIO_CRE_44) & 0x4; | ||
334 | } | ||
335 | |||
336 | /* makes cr0-7 on the specified head read-only */ | ||
337 | static inline bool | ||
338 | nv_lock_vga_crtc_base(struct drm_device *dev, int head, bool lock) | ||
339 | { | ||
340 | uint8_t cr11 = NVReadVgaCrtc(dev, head, NV_CIO_CR_VRE_INDEX); | ||
341 | bool waslocked = cr11 & 0x80; | ||
342 | |||
343 | if (lock) | ||
344 | cr11 |= 0x80; | ||
345 | else | ||
346 | cr11 &= ~0x80; | ||
347 | NVWriteVgaCrtc(dev, head, NV_CIO_CR_VRE_INDEX, cr11); | ||
348 | |||
349 | return waslocked; | ||
350 | } | ||
351 | |||
352 | static inline void | ||
353 | nv_lock_vga_crtc_shadow(struct drm_device *dev, int head, int lock) | ||
354 | { | ||
355 | /* shadow lock: connects 0x60?3d? regs to "real" 0x3d? regs | ||
356 | * bit7: unlocks HDT, HBS, HBE, HRS, HRE, HEB | ||
357 | * bit6: seems to have some effect on CR09 (double scan, VBS_9) | ||
358 | * bit5: unlocks HDE | ||
359 | * bit4: unlocks VDE | ||
360 | * bit3: unlocks VDT, OVL, VRS, ?VRE?, VBS, VBE, LSR, EBR | ||
361 | * bit2: same as bit 1 of 0x60?804 | ||
362 | * bit0: same as bit 0 of 0x60?804 | ||
363 | */ | ||
364 | |||
365 | uint8_t cr21 = lock; | ||
366 | |||
367 | if (lock < 0) | ||
368 | /* 0xfa is generic "unlock all" mask */ | ||
369 | cr21 = NVReadVgaCrtc(dev, head, NV_CIO_CRE_21) | 0xfa; | ||
370 | |||
371 | NVWriteVgaCrtc(dev, head, NV_CIO_CRE_21, cr21); | ||
372 | } | ||
373 | |||
374 | /* renders the extended crtc regs (cr19+) on all crtcs impervious: | ||
375 | * immutable and unreadable | ||
376 | */ | ||
377 | static inline bool | ||
378 | NVLockVgaCrtcs(struct drm_device *dev, bool lock) | ||
379 | { | ||
380 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
381 | bool waslocked = !NVReadVgaCrtc(dev, 0, NV_CIO_SR_LOCK_INDEX); | ||
382 | |||
383 | NVWriteVgaCrtc(dev, 0, NV_CIO_SR_LOCK_INDEX, | ||
384 | lock ? NV_CIO_SR_LOCK_VALUE : NV_CIO_SR_UNLOCK_RW_VALUE); | ||
385 | /* NV11 has independently lockable extended crtcs, except when tied */ | ||
386 | if (dev_priv->chipset == 0x11 && !nv_heads_tied(dev)) | ||
387 | NVWriteVgaCrtc(dev, 1, NV_CIO_SR_LOCK_INDEX, | ||
388 | lock ? NV_CIO_SR_LOCK_VALUE : | ||
389 | NV_CIO_SR_UNLOCK_RW_VALUE); | ||
390 | |||
391 | return waslocked; | ||
392 | } | ||
393 | |||
394 | /* nv04 cursor max dimensions of 32x32 (A1R5G5B5) */ | ||
395 | #define NV04_CURSOR_SIZE 32 | ||
396 | /* limit nv10 cursors to 64x64 (ARGB8) (we could go to 64x255) */ | ||
397 | #define NV10_CURSOR_SIZE 64 | ||
398 | |||
399 | static inline int nv_cursor_width(struct drm_device *dev) | ||
400 | { | ||
401 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
402 | |||
403 | return dev_priv->card_type >= NV_10 ? NV10_CURSOR_SIZE : NV04_CURSOR_SIZE; | ||
404 | } | ||
405 | |||
406 | static inline void | ||
407 | nv_fix_nv40_hw_cursor(struct drm_device *dev, int head) | ||
408 | { | ||
409 | /* on some nv40 (such as the "true" (in the NV_PFB_BOOT_0 sense) nv40, | ||
410 | * the gf6800gt) a hardware bug requires a write to PRAMDAC_CURSOR_POS | ||
411 | * for changes to the CRTC CURCTL regs to take effect, whether changing | ||
412 | * the pixmap location, or just showing/hiding the cursor | ||
413 | */ | ||
414 | uint32_t curpos = NVReadRAMDAC(dev, head, NV_PRAMDAC_CU_START_POS); | ||
415 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_CU_START_POS, curpos); | ||
416 | } | ||
417 | |||
418 | static inline void | ||
419 | nv_show_cursor(struct drm_device *dev, int head, bool show) | ||
420 | { | ||
421 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
422 | uint8_t *curctl1 = | ||
423 | &dev_priv->mode_reg.crtc_reg[head].CRTC[NV_CIO_CRE_HCUR_ADDR1_INDEX]; | ||
424 | |||
425 | if (show) | ||
426 | *curctl1 |= MASK(NV_CIO_CRE_HCUR_ADDR1_ENABLE); | ||
427 | else | ||
428 | *curctl1 &= ~MASK(NV_CIO_CRE_HCUR_ADDR1_ENABLE); | ||
429 | NVWriteVgaCrtc(dev, head, NV_CIO_CRE_HCUR_ADDR1_INDEX, *curctl1); | ||
430 | |||
431 | if (dev_priv->card_type == NV_40) | ||
432 | nv_fix_nv40_hw_cursor(dev, head); | ||
433 | } | ||
434 | |||
435 | static inline uint32_t | ||
436 | nv_pitch_align(struct drm_device *dev, uint32_t width, int bpp) | ||
437 | { | ||
438 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
439 | int mask; | ||
440 | |||
441 | if (bpp == 15) | ||
442 | bpp = 16; | ||
443 | if (bpp == 24) | ||
444 | bpp = 8; | ||
445 | |||
446 | /* Alignment requirements taken from the Haiku driver */ | ||
447 | if (dev_priv->card_type == NV_04) | ||
448 | mask = 128 / bpp - 1; | ||
449 | else | ||
450 | mask = 512 / bpp - 1; | ||
451 | |||
452 | return (width + mask) & ~mask; | ||
453 | } | ||
454 | |||
455 | #endif /* __NOUVEAU_HW_H__ */ | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c new file mode 100644 index 000000000000..70e994d28122 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c | |||
@@ -0,0 +1,269 @@ | |||
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 | * Authors: Ben Skeggs | ||
23 | */ | ||
24 | |||
25 | #include "drmP.h" | ||
26 | #include "nouveau_drv.h" | ||
27 | #include "nouveau_i2c.h" | ||
28 | #include "nouveau_hw.h" | ||
29 | |||
30 | static void | ||
31 | nv04_i2c_setscl(void *data, int state) | ||
32 | { | ||
33 | struct nouveau_i2c_chan *i2c = data; | ||
34 | struct drm_device *dev = i2c->dev; | ||
35 | uint8_t val; | ||
36 | |||
37 | val = (NVReadVgaCrtc(dev, 0, i2c->wr) & 0xd0) | (state ? 0x20 : 0); | ||
38 | NVWriteVgaCrtc(dev, 0, i2c->wr, val | 0x01); | ||
39 | } | ||
40 | |||
41 | static void | ||
42 | nv04_i2c_setsda(void *data, int state) | ||
43 | { | ||
44 | struct nouveau_i2c_chan *i2c = data; | ||
45 | struct drm_device *dev = i2c->dev; | ||
46 | uint8_t val; | ||
47 | |||
48 | val = (NVReadVgaCrtc(dev, 0, i2c->wr) & 0xe0) | (state ? 0x10 : 0); | ||
49 | NVWriteVgaCrtc(dev, 0, i2c->wr, val | 0x01); | ||
50 | } | ||
51 | |||
52 | static int | ||
53 | nv04_i2c_getscl(void *data) | ||
54 | { | ||
55 | struct nouveau_i2c_chan *i2c = data; | ||
56 | struct drm_device *dev = i2c->dev; | ||
57 | |||
58 | return !!(NVReadVgaCrtc(dev, 0, i2c->rd) & 4); | ||
59 | } | ||
60 | |||
61 | static int | ||
62 | nv04_i2c_getsda(void *data) | ||
63 | { | ||
64 | struct nouveau_i2c_chan *i2c = data; | ||
65 | struct drm_device *dev = i2c->dev; | ||
66 | |||
67 | return !!(NVReadVgaCrtc(dev, 0, i2c->rd) & 8); | ||
68 | } | ||
69 | |||
70 | static void | ||
71 | nv4e_i2c_setscl(void *data, int state) | ||
72 | { | ||
73 | struct nouveau_i2c_chan *i2c = data; | ||
74 | struct drm_device *dev = i2c->dev; | ||
75 | uint8_t val; | ||
76 | |||
77 | val = (nv_rd32(dev, i2c->wr) & 0xd0) | (state ? 0x20 : 0); | ||
78 | nv_wr32(dev, i2c->wr, val | 0x01); | ||
79 | } | ||
80 | |||
81 | static void | ||
82 | nv4e_i2c_setsda(void *data, int state) | ||
83 | { | ||
84 | struct nouveau_i2c_chan *i2c = data; | ||
85 | struct drm_device *dev = i2c->dev; | ||
86 | uint8_t val; | ||
87 | |||
88 | val = (nv_rd32(dev, i2c->wr) & 0xe0) | (state ? 0x10 : 0); | ||
89 | nv_wr32(dev, i2c->wr, val | 0x01); | ||
90 | } | ||
91 | |||
92 | static int | ||
93 | nv4e_i2c_getscl(void *data) | ||
94 | { | ||
95 | struct nouveau_i2c_chan *i2c = data; | ||
96 | struct drm_device *dev = i2c->dev; | ||
97 | |||
98 | return !!((nv_rd32(dev, i2c->rd) >> 16) & 4); | ||
99 | } | ||
100 | |||
101 | static int | ||
102 | nv4e_i2c_getsda(void *data) | ||
103 | { | ||
104 | struct nouveau_i2c_chan *i2c = data; | ||
105 | struct drm_device *dev = i2c->dev; | ||
106 | |||
107 | return !!((nv_rd32(dev, i2c->rd) >> 16) & 8); | ||
108 | } | ||
109 | |||
110 | static int | ||
111 | nv50_i2c_getscl(void *data) | ||
112 | { | ||
113 | struct nouveau_i2c_chan *i2c = data; | ||
114 | struct drm_device *dev = i2c->dev; | ||
115 | |||
116 | return !!(nv_rd32(dev, i2c->rd) & 1); | ||
117 | } | ||
118 | |||
119 | |||
120 | static int | ||
121 | nv50_i2c_getsda(void *data) | ||
122 | { | ||
123 | struct nouveau_i2c_chan *i2c = data; | ||
124 | struct drm_device *dev = i2c->dev; | ||
125 | |||
126 | return !!(nv_rd32(dev, i2c->rd) & 2); | ||
127 | } | ||
128 | |||
129 | static void | ||
130 | nv50_i2c_setscl(void *data, int state) | ||
131 | { | ||
132 | struct nouveau_i2c_chan *i2c = data; | ||
133 | struct drm_device *dev = i2c->dev; | ||
134 | |||
135 | nv_wr32(dev, i2c->wr, 4 | (i2c->data ? 2 : 0) | (state ? 1 : 0)); | ||
136 | } | ||
137 | |||
138 | static void | ||
139 | nv50_i2c_setsda(void *data, int state) | ||
140 | { | ||
141 | struct nouveau_i2c_chan *i2c = data; | ||
142 | struct drm_device *dev = i2c->dev; | ||
143 | |||
144 | nv_wr32(dev, i2c->wr, | ||
145 | (nv_rd32(dev, i2c->rd) & 1) | 4 | (state ? 2 : 0)); | ||
146 | i2c->data = state; | ||
147 | } | ||
148 | |||
149 | static const uint32_t nv50_i2c_port[] = { | ||
150 | 0x00e138, 0x00e150, 0x00e168, 0x00e180, | ||
151 | 0x00e254, 0x00e274, 0x00e764, 0x00e780, | ||
152 | 0x00e79c, 0x00e7b8 | ||
153 | }; | ||
154 | #define NV50_I2C_PORTS ARRAY_SIZE(nv50_i2c_port) | ||
155 | |||
156 | int | ||
157 | nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index) | ||
158 | { | ||
159 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
160 | struct nouveau_i2c_chan *i2c; | ||
161 | int ret; | ||
162 | |||
163 | if (entry->chan) | ||
164 | return -EEXIST; | ||
165 | |||
166 | if (dev_priv->card_type == NV_50 && entry->read >= NV50_I2C_PORTS) { | ||
167 | NV_ERROR(dev, "unknown i2c port %d\n", entry->read); | ||
168 | return -EINVAL; | ||
169 | } | ||
170 | |||
171 | i2c = kzalloc(sizeof(*i2c), GFP_KERNEL); | ||
172 | if (i2c == NULL) | ||
173 | return -ENOMEM; | ||
174 | |||
175 | switch (entry->port_type) { | ||
176 | case 0: | ||
177 | i2c->algo.bit.setsda = nv04_i2c_setsda; | ||
178 | i2c->algo.bit.setscl = nv04_i2c_setscl; | ||
179 | i2c->algo.bit.getsda = nv04_i2c_getsda; | ||
180 | i2c->algo.bit.getscl = nv04_i2c_getscl; | ||
181 | i2c->rd = entry->read; | ||
182 | i2c->wr = entry->write; | ||
183 | break; | ||
184 | case 4: | ||
185 | i2c->algo.bit.setsda = nv4e_i2c_setsda; | ||
186 | i2c->algo.bit.setscl = nv4e_i2c_setscl; | ||
187 | i2c->algo.bit.getsda = nv4e_i2c_getsda; | ||
188 | i2c->algo.bit.getscl = nv4e_i2c_getscl; | ||
189 | i2c->rd = 0x600800 + entry->read; | ||
190 | i2c->wr = 0x600800 + entry->write; | ||
191 | break; | ||
192 | case 5: | ||
193 | i2c->algo.bit.setsda = nv50_i2c_setsda; | ||
194 | i2c->algo.bit.setscl = nv50_i2c_setscl; | ||
195 | i2c->algo.bit.getsda = nv50_i2c_getsda; | ||
196 | i2c->algo.bit.getscl = nv50_i2c_getscl; | ||
197 | i2c->rd = nv50_i2c_port[entry->read]; | ||
198 | i2c->wr = i2c->rd; | ||
199 | break; | ||
200 | case 6: | ||
201 | i2c->rd = entry->read; | ||
202 | i2c->wr = entry->write; | ||
203 | break; | ||
204 | default: | ||
205 | NV_ERROR(dev, "DCB I2C port type %d unknown\n", | ||
206 | entry->port_type); | ||
207 | kfree(i2c); | ||
208 | return -EINVAL; | ||
209 | } | ||
210 | |||
211 | snprintf(i2c->adapter.name, sizeof(i2c->adapter.name), | ||
212 | "nouveau-%s-%d", pci_name(dev->pdev), index); | ||
213 | i2c->adapter.owner = THIS_MODULE; | ||
214 | i2c->adapter.dev.parent = &dev->pdev->dev; | ||
215 | i2c->dev = dev; | ||
216 | i2c_set_adapdata(&i2c->adapter, i2c); | ||
217 | |||
218 | if (entry->port_type < 6) { | ||
219 | i2c->adapter.algo_data = &i2c->algo.bit; | ||
220 | i2c->algo.bit.udelay = 40; | ||
221 | i2c->algo.bit.timeout = usecs_to_jiffies(5000); | ||
222 | i2c->algo.bit.data = i2c; | ||
223 | ret = i2c_bit_add_bus(&i2c->adapter); | ||
224 | } else { | ||
225 | i2c->adapter.algo_data = &i2c->algo.dp; | ||
226 | i2c->algo.dp.running = false; | ||
227 | i2c->algo.dp.address = 0; | ||
228 | i2c->algo.dp.aux_ch = nouveau_dp_i2c_aux_ch; | ||
229 | ret = i2c_dp_aux_add_bus(&i2c->adapter); | ||
230 | } | ||
231 | |||
232 | if (ret) { | ||
233 | NV_ERROR(dev, "Failed to register i2c %d\n", index); | ||
234 | kfree(i2c); | ||
235 | return ret; | ||
236 | } | ||
237 | |||
238 | entry->chan = i2c; | ||
239 | return 0; | ||
240 | } | ||
241 | |||
242 | void | ||
243 | nouveau_i2c_fini(struct drm_device *dev, struct dcb_i2c_entry *entry) | ||
244 | { | ||
245 | if (!entry->chan) | ||
246 | return; | ||
247 | |||
248 | i2c_del_adapter(&entry->chan->adapter); | ||
249 | kfree(entry->chan); | ||
250 | entry->chan = NULL; | ||
251 | } | ||
252 | |||
253 | struct nouveau_i2c_chan * | ||
254 | nouveau_i2c_find(struct drm_device *dev, int index) | ||
255 | { | ||
256 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
257 | struct nvbios *bios = &dev_priv->VBIOS; | ||
258 | |||
259 | if (index > DCB_MAX_NUM_I2C_ENTRIES) | ||
260 | return NULL; | ||
261 | |||
262 | if (!bios->bdcb.dcb.i2c[index].chan) { | ||
263 | if (nouveau_i2c_init(dev, &bios->bdcb.dcb.i2c[index], index)) | ||
264 | return NULL; | ||
265 | } | ||
266 | |||
267 | return bios->bdcb.dcb.i2c[index].chan; | ||
268 | } | ||
269 | |||
diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.h b/drivers/gpu/drm/nouveau/nouveau_i2c.h new file mode 100644 index 000000000000..c8eaf7a9fcbb --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_i2c.h | |||
@@ -0,0 +1,52 @@ | |||
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 | |||
23 | #ifndef __NOUVEAU_I2C_H__ | ||
24 | #define __NOUVEAU_I2C_H__ | ||
25 | |||
26 | #include <linux/i2c.h> | ||
27 | #include <linux/i2c-id.h> | ||
28 | #include <linux/i2c-algo-bit.h> | ||
29 | #include "drm_dp_helper.h" | ||
30 | |||
31 | struct dcb_i2c_entry; | ||
32 | |||
33 | struct nouveau_i2c_chan { | ||
34 | struct i2c_adapter adapter; | ||
35 | struct drm_device *dev; | ||
36 | union { | ||
37 | struct i2c_algo_bit_data bit; | ||
38 | struct i2c_algo_dp_aux_data dp; | ||
39 | } algo; | ||
40 | unsigned rd; | ||
41 | unsigned wr; | ||
42 | unsigned data; | ||
43 | }; | ||
44 | |||
45 | int nouveau_i2c_init(struct drm_device *, struct dcb_i2c_entry *, int index); | ||
46 | void nouveau_i2c_fini(struct drm_device *, struct dcb_i2c_entry *); | ||
47 | struct nouveau_i2c_chan *nouveau_i2c_find(struct drm_device *, int index); | ||
48 | |||
49 | int nouveau_dp_i2c_aux_ch(struct i2c_adapter *, int mode, uint8_t write_byte, | ||
50 | uint8_t *read_byte); | ||
51 | |||
52 | #endif /* __NOUVEAU_I2C_H__ */ | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_ioc32.c b/drivers/gpu/drm/nouveau/nouveau_ioc32.c new file mode 100644 index 000000000000..a2c30f4611ba --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_ioc32.c | |||
@@ -0,0 +1,72 @@ | |||
1 | /** | ||
2 | * \file mga_ioc32.c | ||
3 | * | ||
4 | * 32-bit ioctl compatibility routines for the MGA DRM. | ||
5 | * | ||
6 | * \author Dave Airlie <airlied@linux.ie> with code from patches by Egbert Eich | ||
7 | * | ||
8 | * | ||
9 | * Copyright (C) Paul Mackerras 2005 | ||
10 | * Copyright (C) Egbert Eich 2003,2004 | ||
11 | * Copyright (C) Dave Airlie 2005 | ||
12 | * All Rights Reserved. | ||
13 | * | ||
14 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
15 | * copy of this software and associated documentation files (the "Software"), | ||
16 | * to deal in the Software without restriction, including without limitation | ||
17 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
18 | * and/or sell copies of the Software, and to permit persons to whom the | ||
19 | * Software is furnished to do so, subject to the following conditions: | ||
20 | * | ||
21 | * The above copyright notice and this permission notice (including the next | ||
22 | * paragraph) shall be included in all copies or substantial portions of the | ||
23 | * Software. | ||
24 | * | ||
25 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
26 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
27 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
28 | * THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||
29 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
30 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | ||
31 | * IN THE SOFTWARE. | ||
32 | */ | ||
33 | |||
34 | #include <linux/compat.h> | ||
35 | |||
36 | #include "drmP.h" | ||
37 | #include "drm.h" | ||
38 | |||
39 | #include "nouveau_drv.h" | ||
40 | |||
41 | /** | ||
42 | * Called whenever a 32-bit process running under a 64-bit kernel | ||
43 | * performs an ioctl on /dev/dri/card<n>. | ||
44 | * | ||
45 | * \param filp file pointer. | ||
46 | * \param cmd command. | ||
47 | * \param arg user argument. | ||
48 | * \return zero on success or negative number on failure. | ||
49 | */ | ||
50 | long nouveau_compat_ioctl(struct file *filp, unsigned int cmd, | ||
51 | unsigned long arg) | ||
52 | { | ||
53 | unsigned int nr = DRM_IOCTL_NR(cmd); | ||
54 | drm_ioctl_compat_t *fn = NULL; | ||
55 | int ret; | ||
56 | |||
57 | if (nr < DRM_COMMAND_BASE) | ||
58 | return drm_compat_ioctl(filp, cmd, arg); | ||
59 | |||
60 | #if 0 | ||
61 | if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(mga_compat_ioctls)) | ||
62 | fn = nouveau_compat_ioctls[nr - DRM_COMMAND_BASE]; | ||
63 | #endif | ||
64 | lock_kernel(); /* XXX for now */ | ||
65 | if (fn != NULL) | ||
66 | ret = (*fn)(filp, cmd, arg); | ||
67 | else | ||
68 | ret = drm_ioctl(filp->f_dentry->d_inode, filp, cmd, arg); | ||
69 | unlock_kernel(); | ||
70 | |||
71 | return ret; | ||
72 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_irq.c b/drivers/gpu/drm/nouveau/nouveau_irq.c new file mode 100644 index 000000000000..370c72c968d1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_irq.c | |||
@@ -0,0 +1,702 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2006 Ben Skeggs. | ||
3 | * | ||
4 | * All Rights Reserved. | ||
5 | * | ||
6 | * Permission is hereby granted, free of charge, to any person obtaining | ||
7 | * a copy of this software and associated documentation files (the | ||
8 | * "Software"), to deal in the Software without restriction, including | ||
9 | * without limitation the rights to use, copy, modify, merge, publish, | ||
10 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
11 | * permit persons to whom the Software is furnished to do so, subject to | ||
12 | * the following conditions: | ||
13 | * | ||
14 | * The above copyright notice and this permission notice (including the | ||
15 | * next paragraph) shall be included in all copies or substantial | ||
16 | * portions of the Software. | ||
17 | * | ||
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
20 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
21 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
22 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
23 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
24 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
25 | * | ||
26 | */ | ||
27 | |||
28 | /* | ||
29 | * Authors: | ||
30 | * Ben Skeggs <darktama@iinet.net.au> | ||
31 | */ | ||
32 | |||
33 | #include "drmP.h" | ||
34 | #include "drm.h" | ||
35 | #include "nouveau_drm.h" | ||
36 | #include "nouveau_drv.h" | ||
37 | #include "nouveau_reg.h" | ||
38 | #include <linux/ratelimit.h> | ||
39 | |||
40 | /* needed for hotplug irq */ | ||
41 | #include "nouveau_connector.h" | ||
42 | #include "nv50_display.h" | ||
43 | |||
44 | void | ||
45 | nouveau_irq_preinstall(struct drm_device *dev) | ||
46 | { | ||
47 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
48 | |||
49 | /* Master disable */ | ||
50 | nv_wr32(dev, NV03_PMC_INTR_EN_0, 0); | ||
51 | |||
52 | if (dev_priv->card_type == NV_50) { | ||
53 | INIT_WORK(&dev_priv->irq_work, nv50_display_irq_handler_bh); | ||
54 | INIT_LIST_HEAD(&dev_priv->vbl_waiting); | ||
55 | } | ||
56 | } | ||
57 | |||
58 | int | ||
59 | nouveau_irq_postinstall(struct drm_device *dev) | ||
60 | { | ||
61 | /* Master enable */ | ||
62 | nv_wr32(dev, NV03_PMC_INTR_EN_0, NV_PMC_INTR_EN_0_MASTER_ENABLE); | ||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | void | ||
67 | nouveau_irq_uninstall(struct drm_device *dev) | ||
68 | { | ||
69 | /* Master disable */ | ||
70 | nv_wr32(dev, NV03_PMC_INTR_EN_0, 0); | ||
71 | } | ||
72 | |||
73 | static int | ||
74 | nouveau_call_method(struct nouveau_channel *chan, int class, int mthd, int data) | ||
75 | { | ||
76 | struct drm_nouveau_private *dev_priv = chan->dev->dev_private; | ||
77 | struct nouveau_pgraph_object_method *grm; | ||
78 | struct nouveau_pgraph_object_class *grc; | ||
79 | |||
80 | grc = dev_priv->engine.graph.grclass; | ||
81 | while (grc->id) { | ||
82 | if (grc->id == class) | ||
83 | break; | ||
84 | grc++; | ||
85 | } | ||
86 | |||
87 | if (grc->id != class || !grc->methods) | ||
88 | return -ENOENT; | ||
89 | |||
90 | grm = grc->methods; | ||
91 | while (grm->id) { | ||
92 | if (grm->id == mthd) | ||
93 | return grm->exec(chan, class, mthd, data); | ||
94 | grm++; | ||
95 | } | ||
96 | |||
97 | return -ENOENT; | ||
98 | } | ||
99 | |||
100 | static bool | ||
101 | nouveau_fifo_swmthd(struct nouveau_channel *chan, uint32_t addr, uint32_t data) | ||
102 | { | ||
103 | struct drm_device *dev = chan->dev; | ||
104 | const int subc = (addr >> 13) & 0x7; | ||
105 | const int mthd = addr & 0x1ffc; | ||
106 | |||
107 | if (mthd == 0x0000) { | ||
108 | struct nouveau_gpuobj_ref *ref = NULL; | ||
109 | |||
110 | if (nouveau_gpuobj_ref_find(chan, data, &ref)) | ||
111 | return false; | ||
112 | |||
113 | if (ref->gpuobj->engine != NVOBJ_ENGINE_SW) | ||
114 | return false; | ||
115 | |||
116 | chan->sw_subchannel[subc] = ref->gpuobj->class; | ||
117 | nv_wr32(dev, NV04_PFIFO_CACHE1_ENGINE, nv_rd32(dev, | ||
118 | NV04_PFIFO_CACHE1_ENGINE) & ~(0xf << subc * 4)); | ||
119 | return true; | ||
120 | } | ||
121 | |||
122 | /* hw object */ | ||
123 | if (nv_rd32(dev, NV04_PFIFO_CACHE1_ENGINE) & (1 << (subc*4))) | ||
124 | return false; | ||
125 | |||
126 | if (nouveau_call_method(chan, chan->sw_subchannel[subc], mthd, data)) | ||
127 | return false; | ||
128 | |||
129 | return true; | ||
130 | } | ||
131 | |||
132 | static void | ||
133 | nouveau_fifo_irq_handler(struct drm_device *dev) | ||
134 | { | ||
135 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
136 | struct nouveau_engine *engine = &dev_priv->engine; | ||
137 | uint32_t status, reassign; | ||
138 | int cnt = 0; | ||
139 | |||
140 | reassign = nv_rd32(dev, NV03_PFIFO_CACHES) & 1; | ||
141 | while ((status = nv_rd32(dev, NV03_PFIFO_INTR_0)) && (cnt++ < 100)) { | ||
142 | struct nouveau_channel *chan = NULL; | ||
143 | uint32_t chid, get; | ||
144 | |||
145 | nv_wr32(dev, NV03_PFIFO_CACHES, 0); | ||
146 | |||
147 | chid = engine->fifo.channel_id(dev); | ||
148 | if (chid >= 0 && chid < engine->fifo.channels) | ||
149 | chan = dev_priv->fifos[chid]; | ||
150 | get = nv_rd32(dev, NV03_PFIFO_CACHE1_GET); | ||
151 | |||
152 | if (status & NV_PFIFO_INTR_CACHE_ERROR) { | ||
153 | uint32_t mthd, data; | ||
154 | int ptr; | ||
155 | |||
156 | /* NV_PFIFO_CACHE1_GET actually goes to 0xffc before | ||
157 | * wrapping on my G80 chips, but CACHE1 isn't big | ||
158 | * enough for this much data.. Tests show that it | ||
159 | * wraps around to the start at GET=0x800.. No clue | ||
160 | * as to why.. | ||
161 | */ | ||
162 | ptr = (get & 0x7ff) >> 2; | ||
163 | |||
164 | if (dev_priv->card_type < NV_40) { | ||
165 | mthd = nv_rd32(dev, | ||
166 | NV04_PFIFO_CACHE1_METHOD(ptr)); | ||
167 | data = nv_rd32(dev, | ||
168 | NV04_PFIFO_CACHE1_DATA(ptr)); | ||
169 | } else { | ||
170 | mthd = nv_rd32(dev, | ||
171 | NV40_PFIFO_CACHE1_METHOD(ptr)); | ||
172 | data = nv_rd32(dev, | ||
173 | NV40_PFIFO_CACHE1_DATA(ptr)); | ||
174 | } | ||
175 | |||
176 | if (!chan || !nouveau_fifo_swmthd(chan, mthd, data)) { | ||
177 | NV_INFO(dev, "PFIFO_CACHE_ERROR - Ch %d/%d " | ||
178 | "Mthd 0x%04x Data 0x%08x\n", | ||
179 | chid, (mthd >> 13) & 7, mthd & 0x1ffc, | ||
180 | data); | ||
181 | } | ||
182 | |||
183 | nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 0); | ||
184 | nv_wr32(dev, NV03_PFIFO_INTR_0, | ||
185 | NV_PFIFO_INTR_CACHE_ERROR); | ||
186 | |||
187 | nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0, | ||
188 | nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH0) & ~1); | ||
189 | nv_wr32(dev, NV03_PFIFO_CACHE1_GET, get + 4); | ||
190 | nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0, | ||
191 | nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH0) | 1); | ||
192 | nv_wr32(dev, NV04_PFIFO_CACHE1_HASH, 0); | ||
193 | |||
194 | nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH, | ||
195 | nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUSH) | 1); | ||
196 | nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, 1); | ||
197 | |||
198 | status &= ~NV_PFIFO_INTR_CACHE_ERROR; | ||
199 | } | ||
200 | |||
201 | if (status & NV_PFIFO_INTR_DMA_PUSHER) { | ||
202 | NV_INFO(dev, "PFIFO_DMA_PUSHER - Ch %d\n", chid); | ||
203 | |||
204 | status &= ~NV_PFIFO_INTR_DMA_PUSHER; | ||
205 | nv_wr32(dev, NV03_PFIFO_INTR_0, | ||
206 | NV_PFIFO_INTR_DMA_PUSHER); | ||
207 | |||
208 | nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_STATE, 0x00000000); | ||
209 | if (nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUT) != get) | ||
210 | nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_GET, | ||
211 | get + 4); | ||
212 | } | ||
213 | |||
214 | if (status) { | ||
215 | NV_INFO(dev, "PFIFO_INTR 0x%08x - Ch %d\n", | ||
216 | status, chid); | ||
217 | nv_wr32(dev, NV03_PFIFO_INTR_0, status); | ||
218 | status = 0; | ||
219 | } | ||
220 | |||
221 | nv_wr32(dev, NV03_PFIFO_CACHES, reassign); | ||
222 | } | ||
223 | |||
224 | if (status) { | ||
225 | NV_INFO(dev, "PFIFO still angry after %d spins, halt\n", cnt); | ||
226 | nv_wr32(dev, 0x2140, 0); | ||
227 | nv_wr32(dev, 0x140, 0); | ||
228 | } | ||
229 | |||
230 | nv_wr32(dev, NV03_PMC_INTR_0, NV_PMC_INTR_0_PFIFO_PENDING); | ||
231 | } | ||
232 | |||
233 | struct nouveau_bitfield_names { | ||
234 | uint32_t mask; | ||
235 | const char *name; | ||
236 | }; | ||
237 | |||
238 | static struct nouveau_bitfield_names nstatus_names[] = | ||
239 | { | ||
240 | { NV04_PGRAPH_NSTATUS_STATE_IN_USE, "STATE_IN_USE" }, | ||
241 | { NV04_PGRAPH_NSTATUS_INVALID_STATE, "INVALID_STATE" }, | ||
242 | { NV04_PGRAPH_NSTATUS_BAD_ARGUMENT, "BAD_ARGUMENT" }, | ||
243 | { NV04_PGRAPH_NSTATUS_PROTECTION_FAULT, "PROTECTION_FAULT" } | ||
244 | }; | ||
245 | |||
246 | static struct nouveau_bitfield_names nstatus_names_nv10[] = | ||
247 | { | ||
248 | { NV10_PGRAPH_NSTATUS_STATE_IN_USE, "STATE_IN_USE" }, | ||
249 | { NV10_PGRAPH_NSTATUS_INVALID_STATE, "INVALID_STATE" }, | ||
250 | { NV10_PGRAPH_NSTATUS_BAD_ARGUMENT, "BAD_ARGUMENT" }, | ||
251 | { NV10_PGRAPH_NSTATUS_PROTECTION_FAULT, "PROTECTION_FAULT" } | ||
252 | }; | ||
253 | |||
254 | static struct nouveau_bitfield_names nsource_names[] = | ||
255 | { | ||
256 | { NV03_PGRAPH_NSOURCE_NOTIFICATION, "NOTIFICATION" }, | ||
257 | { NV03_PGRAPH_NSOURCE_DATA_ERROR, "DATA_ERROR" }, | ||
258 | { NV03_PGRAPH_NSOURCE_PROTECTION_ERROR, "PROTECTION_ERROR" }, | ||
259 | { NV03_PGRAPH_NSOURCE_RANGE_EXCEPTION, "RANGE_EXCEPTION" }, | ||
260 | { NV03_PGRAPH_NSOURCE_LIMIT_COLOR, "LIMIT_COLOR" }, | ||
261 | { NV03_PGRAPH_NSOURCE_LIMIT_ZETA, "LIMIT_ZETA" }, | ||
262 | { NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD, "ILLEGAL_MTHD" }, | ||
263 | { NV03_PGRAPH_NSOURCE_DMA_R_PROTECTION, "DMA_R_PROTECTION" }, | ||
264 | { NV03_PGRAPH_NSOURCE_DMA_W_PROTECTION, "DMA_W_PROTECTION" }, | ||
265 | { NV03_PGRAPH_NSOURCE_FORMAT_EXCEPTION, "FORMAT_EXCEPTION" }, | ||
266 | { NV03_PGRAPH_NSOURCE_PATCH_EXCEPTION, "PATCH_EXCEPTION" }, | ||
267 | { NV03_PGRAPH_NSOURCE_STATE_INVALID, "STATE_INVALID" }, | ||
268 | { NV03_PGRAPH_NSOURCE_DOUBLE_NOTIFY, "DOUBLE_NOTIFY" }, | ||
269 | { NV03_PGRAPH_NSOURCE_NOTIFY_IN_USE, "NOTIFY_IN_USE" }, | ||
270 | { NV03_PGRAPH_NSOURCE_METHOD_CNT, "METHOD_CNT" }, | ||
271 | { NV03_PGRAPH_NSOURCE_BFR_NOTIFICATION, "BFR_NOTIFICATION" }, | ||
272 | { NV03_PGRAPH_NSOURCE_DMA_VTX_PROTECTION, "DMA_VTX_PROTECTION" }, | ||
273 | { NV03_PGRAPH_NSOURCE_DMA_WIDTH_A, "DMA_WIDTH_A" }, | ||
274 | { NV03_PGRAPH_NSOURCE_DMA_WIDTH_B, "DMA_WIDTH_B" }, | ||
275 | }; | ||
276 | |||
277 | static void | ||
278 | nouveau_print_bitfield_names_(uint32_t value, | ||
279 | const struct nouveau_bitfield_names *namelist, | ||
280 | const int namelist_len) | ||
281 | { | ||
282 | /* | ||
283 | * Caller must have already printed the KERN_* log level for us. | ||
284 | * Also the caller is responsible for adding the newline. | ||
285 | */ | ||
286 | int i; | ||
287 | for (i = 0; i < namelist_len; ++i) { | ||
288 | uint32_t mask = namelist[i].mask; | ||
289 | if (value & mask) { | ||
290 | printk(" %s", namelist[i].name); | ||
291 | value &= ~mask; | ||
292 | } | ||
293 | } | ||
294 | if (value) | ||
295 | printk(" (unknown bits 0x%08x)", value); | ||
296 | } | ||
297 | #define nouveau_print_bitfield_names(val, namelist) \ | ||
298 | nouveau_print_bitfield_names_((val), (namelist), ARRAY_SIZE(namelist)) | ||
299 | |||
300 | |||
301 | static int | ||
302 | nouveau_graph_chid_from_grctx(struct drm_device *dev) | ||
303 | { | ||
304 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
305 | uint32_t inst; | ||
306 | int i; | ||
307 | |||
308 | if (dev_priv->card_type < NV_40) | ||
309 | return dev_priv->engine.fifo.channels; | ||
310 | else | ||
311 | if (dev_priv->card_type < NV_50) { | ||
312 | inst = (nv_rd32(dev, 0x40032c) & 0xfffff) << 4; | ||
313 | |||
314 | for (i = 0; i < dev_priv->engine.fifo.channels; i++) { | ||
315 | struct nouveau_channel *chan = dev_priv->fifos[i]; | ||
316 | |||
317 | if (!chan || !chan->ramin_grctx) | ||
318 | continue; | ||
319 | |||
320 | if (inst == chan->ramin_grctx->instance) | ||
321 | break; | ||
322 | } | ||
323 | } else { | ||
324 | inst = (nv_rd32(dev, 0x40032c) & 0xfffff) << 12; | ||
325 | |||
326 | for (i = 0; i < dev_priv->engine.fifo.channels; i++) { | ||
327 | struct nouveau_channel *chan = dev_priv->fifos[i]; | ||
328 | |||
329 | if (!chan || !chan->ramin) | ||
330 | continue; | ||
331 | |||
332 | if (inst == chan->ramin->instance) | ||
333 | break; | ||
334 | } | ||
335 | } | ||
336 | |||
337 | |||
338 | return i; | ||
339 | } | ||
340 | |||
341 | static int | ||
342 | nouveau_graph_trapped_channel(struct drm_device *dev, int *channel_ret) | ||
343 | { | ||
344 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
345 | struct nouveau_engine *engine = &dev_priv->engine; | ||
346 | int channel; | ||
347 | |||
348 | if (dev_priv->card_type < NV_10) | ||
349 | channel = (nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR) >> 24) & 0xf; | ||
350 | else | ||
351 | if (dev_priv->card_type < NV_40) | ||
352 | channel = (nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR) >> 20) & 0x1f; | ||
353 | else | ||
354 | channel = nouveau_graph_chid_from_grctx(dev); | ||
355 | |||
356 | if (channel >= engine->fifo.channels || !dev_priv->fifos[channel]) { | ||
357 | NV_ERROR(dev, "AIII, invalid/inactive channel id %d\n", channel); | ||
358 | return -EINVAL; | ||
359 | } | ||
360 | |||
361 | *channel_ret = channel; | ||
362 | return 0; | ||
363 | } | ||
364 | |||
365 | struct nouveau_pgraph_trap { | ||
366 | int channel; | ||
367 | int class; | ||
368 | int subc, mthd, size; | ||
369 | uint32_t data, data2; | ||
370 | uint32_t nsource, nstatus; | ||
371 | }; | ||
372 | |||
373 | static void | ||
374 | nouveau_graph_trap_info(struct drm_device *dev, | ||
375 | struct nouveau_pgraph_trap *trap) | ||
376 | { | ||
377 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
378 | uint32_t address; | ||
379 | |||
380 | trap->nsource = trap->nstatus = 0; | ||
381 | if (dev_priv->card_type < NV_50) { | ||
382 | trap->nsource = nv_rd32(dev, NV03_PGRAPH_NSOURCE); | ||
383 | trap->nstatus = nv_rd32(dev, NV03_PGRAPH_NSTATUS); | ||
384 | } | ||
385 | |||
386 | if (nouveau_graph_trapped_channel(dev, &trap->channel)) | ||
387 | trap->channel = -1; | ||
388 | address = nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR); | ||
389 | |||
390 | trap->mthd = address & 0x1FFC; | ||
391 | trap->data = nv_rd32(dev, NV04_PGRAPH_TRAPPED_DATA); | ||
392 | if (dev_priv->card_type < NV_10) { | ||
393 | trap->subc = (address >> 13) & 0x7; | ||
394 | } else { | ||
395 | trap->subc = (address >> 16) & 0x7; | ||
396 | trap->data2 = nv_rd32(dev, NV10_PGRAPH_TRAPPED_DATA_HIGH); | ||
397 | } | ||
398 | |||
399 | if (dev_priv->card_type < NV_10) | ||
400 | trap->class = nv_rd32(dev, 0x400180 + trap->subc*4) & 0xFF; | ||
401 | else if (dev_priv->card_type < NV_40) | ||
402 | trap->class = nv_rd32(dev, 0x400160 + trap->subc*4) & 0xFFF; | ||
403 | else if (dev_priv->card_type < NV_50) | ||
404 | trap->class = nv_rd32(dev, 0x400160 + trap->subc*4) & 0xFFFF; | ||
405 | else | ||
406 | trap->class = nv_rd32(dev, 0x400814); | ||
407 | } | ||
408 | |||
409 | static void | ||
410 | nouveau_graph_dump_trap_info(struct drm_device *dev, const char *id, | ||
411 | struct nouveau_pgraph_trap *trap) | ||
412 | { | ||
413 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
414 | uint32_t nsource = trap->nsource, nstatus = trap->nstatus; | ||
415 | |||
416 | NV_INFO(dev, "%s - nSource:", id); | ||
417 | nouveau_print_bitfield_names(nsource, nsource_names); | ||
418 | printk(", nStatus:"); | ||
419 | if (dev_priv->card_type < NV_10) | ||
420 | nouveau_print_bitfield_names(nstatus, nstatus_names); | ||
421 | else | ||
422 | nouveau_print_bitfield_names(nstatus, nstatus_names_nv10); | ||
423 | printk("\n"); | ||
424 | |||
425 | NV_INFO(dev, "%s - Ch %d/%d Class 0x%04x Mthd 0x%04x " | ||
426 | "Data 0x%08x:0x%08x\n", | ||
427 | id, trap->channel, trap->subc, | ||
428 | trap->class, trap->mthd, | ||
429 | trap->data2, trap->data); | ||
430 | } | ||
431 | |||
432 | static int | ||
433 | nouveau_pgraph_intr_swmthd(struct drm_device *dev, | ||
434 | struct nouveau_pgraph_trap *trap) | ||
435 | { | ||
436 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
437 | |||
438 | if (trap->channel < 0 || | ||
439 | trap->channel >= dev_priv->engine.fifo.channels || | ||
440 | !dev_priv->fifos[trap->channel]) | ||
441 | return -ENODEV; | ||
442 | |||
443 | return nouveau_call_method(dev_priv->fifos[trap->channel], | ||
444 | trap->class, trap->mthd, trap->data); | ||
445 | } | ||
446 | |||
447 | static inline void | ||
448 | nouveau_pgraph_intr_notify(struct drm_device *dev, uint32_t nsource) | ||
449 | { | ||
450 | struct nouveau_pgraph_trap trap; | ||
451 | int unhandled = 0; | ||
452 | |||
453 | nouveau_graph_trap_info(dev, &trap); | ||
454 | |||
455 | if (nsource & NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD) { | ||
456 | if (nouveau_pgraph_intr_swmthd(dev, &trap)) | ||
457 | unhandled = 1; | ||
458 | } else { | ||
459 | unhandled = 1; | ||
460 | } | ||
461 | |||
462 | if (unhandled) | ||
463 | nouveau_graph_dump_trap_info(dev, "PGRAPH_NOTIFY", &trap); | ||
464 | } | ||
465 | |||
466 | static DEFINE_RATELIMIT_STATE(nouveau_ratelimit_state, 3 * HZ, 20); | ||
467 | |||
468 | static int nouveau_ratelimit(void) | ||
469 | { | ||
470 | return __ratelimit(&nouveau_ratelimit_state); | ||
471 | } | ||
472 | |||
473 | |||
474 | static inline void | ||
475 | nouveau_pgraph_intr_error(struct drm_device *dev, uint32_t nsource) | ||
476 | { | ||
477 | struct nouveau_pgraph_trap trap; | ||
478 | int unhandled = 0; | ||
479 | |||
480 | nouveau_graph_trap_info(dev, &trap); | ||
481 | trap.nsource = nsource; | ||
482 | |||
483 | if (nsource & NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD) { | ||
484 | if (nouveau_pgraph_intr_swmthd(dev, &trap)) | ||
485 | unhandled = 1; | ||
486 | } else { | ||
487 | unhandled = 1; | ||
488 | } | ||
489 | |||
490 | if (unhandled && nouveau_ratelimit()) | ||
491 | nouveau_graph_dump_trap_info(dev, "PGRAPH_ERROR", &trap); | ||
492 | } | ||
493 | |||
494 | static inline void | ||
495 | nouveau_pgraph_intr_context_switch(struct drm_device *dev) | ||
496 | { | ||
497 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
498 | struct nouveau_engine *engine = &dev_priv->engine; | ||
499 | uint32_t chid; | ||
500 | |||
501 | chid = engine->fifo.channel_id(dev); | ||
502 | NV_DEBUG(dev, "PGRAPH context switch interrupt channel %x\n", chid); | ||
503 | |||
504 | switch (dev_priv->card_type) { | ||
505 | case NV_04: | ||
506 | nv04_graph_context_switch(dev); | ||
507 | break; | ||
508 | case NV_10: | ||
509 | nv10_graph_context_switch(dev); | ||
510 | break; | ||
511 | default: | ||
512 | NV_ERROR(dev, "Context switch not implemented\n"); | ||
513 | break; | ||
514 | } | ||
515 | } | ||
516 | |||
517 | static void | ||
518 | nouveau_pgraph_irq_handler(struct drm_device *dev) | ||
519 | { | ||
520 | uint32_t status; | ||
521 | |||
522 | while ((status = nv_rd32(dev, NV03_PGRAPH_INTR))) { | ||
523 | uint32_t nsource = nv_rd32(dev, NV03_PGRAPH_NSOURCE); | ||
524 | |||
525 | if (status & NV_PGRAPH_INTR_NOTIFY) { | ||
526 | nouveau_pgraph_intr_notify(dev, nsource); | ||
527 | |||
528 | status &= ~NV_PGRAPH_INTR_NOTIFY; | ||
529 | nv_wr32(dev, NV03_PGRAPH_INTR, NV_PGRAPH_INTR_NOTIFY); | ||
530 | } | ||
531 | |||
532 | if (status & NV_PGRAPH_INTR_ERROR) { | ||
533 | nouveau_pgraph_intr_error(dev, nsource); | ||
534 | |||
535 | status &= ~NV_PGRAPH_INTR_ERROR; | ||
536 | nv_wr32(dev, NV03_PGRAPH_INTR, NV_PGRAPH_INTR_ERROR); | ||
537 | } | ||
538 | |||
539 | if (status & NV_PGRAPH_INTR_CONTEXT_SWITCH) { | ||
540 | nouveau_pgraph_intr_context_switch(dev); | ||
541 | |||
542 | status &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH; | ||
543 | nv_wr32(dev, NV03_PGRAPH_INTR, | ||
544 | NV_PGRAPH_INTR_CONTEXT_SWITCH); | ||
545 | } | ||
546 | |||
547 | if (status) { | ||
548 | NV_INFO(dev, "Unhandled PGRAPH_INTR - 0x%08x\n", status); | ||
549 | nv_wr32(dev, NV03_PGRAPH_INTR, status); | ||
550 | } | ||
551 | |||
552 | if ((nv_rd32(dev, NV04_PGRAPH_FIFO) & (1 << 0)) == 0) | ||
553 | nv_wr32(dev, NV04_PGRAPH_FIFO, 1); | ||
554 | } | ||
555 | |||
556 | nv_wr32(dev, NV03_PMC_INTR_0, NV_PMC_INTR_0_PGRAPH_PENDING); | ||
557 | } | ||
558 | |||
559 | static void | ||
560 | nv50_pgraph_irq_handler(struct drm_device *dev) | ||
561 | { | ||
562 | uint32_t status, nsource; | ||
563 | |||
564 | status = nv_rd32(dev, NV03_PGRAPH_INTR); | ||
565 | nsource = nv_rd32(dev, NV03_PGRAPH_NSOURCE); | ||
566 | |||
567 | if (status & 0x00000001) { | ||
568 | nouveau_pgraph_intr_notify(dev, nsource); | ||
569 | status &= ~0x00000001; | ||
570 | nv_wr32(dev, NV03_PGRAPH_INTR, 0x00000001); | ||
571 | } | ||
572 | |||
573 | if (status & 0x00000010) { | ||
574 | nouveau_pgraph_intr_error(dev, nsource | | ||
575 | NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD); | ||
576 | |||
577 | status &= ~0x00000010; | ||
578 | nv_wr32(dev, NV03_PGRAPH_INTR, 0x00000010); | ||
579 | } | ||
580 | |||
581 | if (status & 0x00001000) { | ||
582 | nv_wr32(dev, 0x400500, 0x00000000); | ||
583 | nv_wr32(dev, NV03_PGRAPH_INTR, NV_PGRAPH_INTR_CONTEXT_SWITCH); | ||
584 | nv_wr32(dev, NV40_PGRAPH_INTR_EN, nv_rd32(dev, | ||
585 | NV40_PGRAPH_INTR_EN) & ~NV_PGRAPH_INTR_CONTEXT_SWITCH); | ||
586 | nv_wr32(dev, 0x400500, 0x00010001); | ||
587 | |||
588 | nv50_graph_context_switch(dev); | ||
589 | |||
590 | status &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH; | ||
591 | } | ||
592 | |||
593 | if (status & 0x00100000) { | ||
594 | nouveau_pgraph_intr_error(dev, nsource | | ||
595 | NV03_PGRAPH_NSOURCE_DATA_ERROR); | ||
596 | |||
597 | status &= ~0x00100000; | ||
598 | nv_wr32(dev, NV03_PGRAPH_INTR, 0x00100000); | ||
599 | } | ||
600 | |||
601 | if (status & 0x00200000) { | ||
602 | int r; | ||
603 | |||
604 | nouveau_pgraph_intr_error(dev, nsource | | ||
605 | NV03_PGRAPH_NSOURCE_PROTECTION_ERROR); | ||
606 | |||
607 | NV_ERROR(dev, "magic set 1:\n"); | ||
608 | for (r = 0x408900; r <= 0x408910; r += 4) | ||
609 | NV_ERROR(dev, "\t0x%08x: 0x%08x\n", r, nv_rd32(dev, r)); | ||
610 | nv_wr32(dev, 0x408900, nv_rd32(dev, 0x408904) | 0xc0000000); | ||
611 | for (r = 0x408e08; r <= 0x408e24; r += 4) | ||
612 | NV_ERROR(dev, "\t0x%08x: 0x%08x\n", r, nv_rd32(dev, r)); | ||
613 | nv_wr32(dev, 0x408e08, nv_rd32(dev, 0x408e08) | 0xc0000000); | ||
614 | |||
615 | NV_ERROR(dev, "magic set 2:\n"); | ||
616 | for (r = 0x409900; r <= 0x409910; r += 4) | ||
617 | NV_ERROR(dev, "\t0x%08x: 0x%08x\n", r, nv_rd32(dev, r)); | ||
618 | nv_wr32(dev, 0x409900, nv_rd32(dev, 0x409904) | 0xc0000000); | ||
619 | for (r = 0x409e08; r <= 0x409e24; r += 4) | ||
620 | NV_ERROR(dev, "\t0x%08x: 0x%08x\n", r, nv_rd32(dev, r)); | ||
621 | nv_wr32(dev, 0x409e08, nv_rd32(dev, 0x409e08) | 0xc0000000); | ||
622 | |||
623 | status &= ~0x00200000; | ||
624 | nv_wr32(dev, NV03_PGRAPH_NSOURCE, nsource); | ||
625 | nv_wr32(dev, NV03_PGRAPH_INTR, 0x00200000); | ||
626 | } | ||
627 | |||
628 | if (status) { | ||
629 | NV_INFO(dev, "Unhandled PGRAPH_INTR - 0x%08x\n", status); | ||
630 | nv_wr32(dev, NV03_PGRAPH_INTR, status); | ||
631 | } | ||
632 | |||
633 | { | ||
634 | const int isb = (1 << 16) | (1 << 0); | ||
635 | |||
636 | if ((nv_rd32(dev, 0x400500) & isb) != isb) | ||
637 | nv_wr32(dev, 0x400500, nv_rd32(dev, 0x400500) | isb); | ||
638 | } | ||
639 | |||
640 | nv_wr32(dev, NV03_PMC_INTR_0, NV_PMC_INTR_0_PGRAPH_PENDING); | ||
641 | } | ||
642 | |||
643 | static void | ||
644 | nouveau_crtc_irq_handler(struct drm_device *dev, int crtc) | ||
645 | { | ||
646 | if (crtc & 1) | ||
647 | nv_wr32(dev, NV_CRTC0_INTSTAT, NV_CRTC_INTR_VBLANK); | ||
648 | |||
649 | if (crtc & 2) | ||
650 | nv_wr32(dev, NV_CRTC1_INTSTAT, NV_CRTC_INTR_VBLANK); | ||
651 | } | ||
652 | |||
653 | irqreturn_t | ||
654 | nouveau_irq_handler(DRM_IRQ_ARGS) | ||
655 | { | ||
656 | struct drm_device *dev = (struct drm_device *)arg; | ||
657 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
658 | uint32_t status, fbdev_flags = 0; | ||
659 | |||
660 | status = nv_rd32(dev, NV03_PMC_INTR_0); | ||
661 | if (!status) | ||
662 | return IRQ_NONE; | ||
663 | |||
664 | if (dev_priv->fbdev_info) { | ||
665 | fbdev_flags = dev_priv->fbdev_info->flags; | ||
666 | dev_priv->fbdev_info->flags |= FBINFO_HWACCEL_DISABLED; | ||
667 | } | ||
668 | |||
669 | if (status & NV_PMC_INTR_0_PFIFO_PENDING) { | ||
670 | nouveau_fifo_irq_handler(dev); | ||
671 | status &= ~NV_PMC_INTR_0_PFIFO_PENDING; | ||
672 | } | ||
673 | |||
674 | if (status & NV_PMC_INTR_0_PGRAPH_PENDING) { | ||
675 | if (dev_priv->card_type >= NV_50) | ||
676 | nv50_pgraph_irq_handler(dev); | ||
677 | else | ||
678 | nouveau_pgraph_irq_handler(dev); | ||
679 | |||
680 | status &= ~NV_PMC_INTR_0_PGRAPH_PENDING; | ||
681 | } | ||
682 | |||
683 | if (status & NV_PMC_INTR_0_CRTCn_PENDING) { | ||
684 | nouveau_crtc_irq_handler(dev, (status>>24)&3); | ||
685 | status &= ~NV_PMC_INTR_0_CRTCn_PENDING; | ||
686 | } | ||
687 | |||
688 | if (status & (NV_PMC_INTR_0_NV50_DISPLAY_PENDING | | ||
689 | NV_PMC_INTR_0_NV50_I2C_PENDING)) { | ||
690 | nv50_display_irq_handler(dev); | ||
691 | status &= ~(NV_PMC_INTR_0_NV50_DISPLAY_PENDING | | ||
692 | NV_PMC_INTR_0_NV50_I2C_PENDING); | ||
693 | } | ||
694 | |||
695 | if (status) | ||
696 | NV_ERROR(dev, "Unhandled PMC INTR status bits 0x%08x\n", status); | ||
697 | |||
698 | if (dev_priv->fbdev_info) | ||
699 | dev_priv->fbdev_info->flags = fbdev_flags; | ||
700 | |||
701 | return IRQ_HANDLED; | ||
702 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c new file mode 100644 index 000000000000..02755712ed3d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c | |||
@@ -0,0 +1,568 @@ | |||
1 | /* | ||
2 | * Copyright (C) The Weather Channel, Inc. 2002. All Rights Reserved. | ||
3 | * Copyright 2005 Stephane Marchesin | ||
4 | * | ||
5 | * The Weather Channel (TM) funded Tungsten Graphics to develop the | ||
6 | * initial release of the Radeon 8500 driver under the XFree86 license. | ||
7 | * This notice must be preserved. | ||
8 | * | ||
9 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
10 | * copy of this software and associated documentation files (the "Software"), | ||
11 | * to deal in the Software without restriction, including without limitation | ||
12 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
13 | * and/or sell copies of the Software, and to permit persons to whom the | ||
14 | * Software is furnished to do so, subject to the following conditions: | ||
15 | * | ||
16 | * The above copyright notice and this permission notice (including the next | ||
17 | * paragraph) shall be included in all copies or substantial portions of the | ||
18 | * Software. | ||
19 | * | ||
20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
23 | * THE AUTHORS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
24 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
25 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
26 | * DEALINGS IN THE SOFTWARE. | ||
27 | * | ||
28 | * Authors: | ||
29 | * Keith Whitwell <keith@tungstengraphics.com> | ||
30 | */ | ||
31 | |||
32 | |||
33 | #include "drmP.h" | ||
34 | #include "drm.h" | ||
35 | #include "drm_sarea.h" | ||
36 | #include "nouveau_drv.h" | ||
37 | |||
38 | static struct mem_block * | ||
39 | split_block(struct mem_block *p, uint64_t start, uint64_t size, | ||
40 | struct drm_file *file_priv) | ||
41 | { | ||
42 | /* Maybe cut off the start of an existing block */ | ||
43 | if (start > p->start) { | ||
44 | struct mem_block *newblock = | ||
45 | kmalloc(sizeof(*newblock), GFP_KERNEL); | ||
46 | if (!newblock) | ||
47 | goto out; | ||
48 | newblock->start = start; | ||
49 | newblock->size = p->size - (start - p->start); | ||
50 | newblock->file_priv = NULL; | ||
51 | newblock->next = p->next; | ||
52 | newblock->prev = p; | ||
53 | p->next->prev = newblock; | ||
54 | p->next = newblock; | ||
55 | p->size -= newblock->size; | ||
56 | p = newblock; | ||
57 | } | ||
58 | |||
59 | /* Maybe cut off the end of an existing block */ | ||
60 | if (size < p->size) { | ||
61 | struct mem_block *newblock = | ||
62 | kmalloc(sizeof(*newblock), GFP_KERNEL); | ||
63 | if (!newblock) | ||
64 | goto out; | ||
65 | newblock->start = start + size; | ||
66 | newblock->size = p->size - size; | ||
67 | newblock->file_priv = NULL; | ||
68 | newblock->next = p->next; | ||
69 | newblock->prev = p; | ||
70 | p->next->prev = newblock; | ||
71 | p->next = newblock; | ||
72 | p->size = size; | ||
73 | } | ||
74 | |||
75 | out: | ||
76 | /* Our block is in the middle */ | ||
77 | p->file_priv = file_priv; | ||
78 | return p; | ||
79 | } | ||
80 | |||
81 | struct mem_block * | ||
82 | nouveau_mem_alloc_block(struct mem_block *heap, uint64_t size, | ||
83 | int align2, struct drm_file *file_priv, int tail) | ||
84 | { | ||
85 | struct mem_block *p; | ||
86 | uint64_t mask = (1 << align2) - 1; | ||
87 | |||
88 | if (!heap) | ||
89 | return NULL; | ||
90 | |||
91 | if (tail) { | ||
92 | list_for_each_prev(p, heap) { | ||
93 | uint64_t start = ((p->start + p->size) - size) & ~mask; | ||
94 | |||
95 | if (p->file_priv == NULL && start >= p->start && | ||
96 | start + size <= p->start + p->size) | ||
97 | return split_block(p, start, size, file_priv); | ||
98 | } | ||
99 | } else { | ||
100 | list_for_each(p, heap) { | ||
101 | uint64_t start = (p->start + mask) & ~mask; | ||
102 | |||
103 | if (p->file_priv == NULL && | ||
104 | start + size <= p->start + p->size) | ||
105 | return split_block(p, start, size, file_priv); | ||
106 | } | ||
107 | } | ||
108 | |||
109 | return NULL; | ||
110 | } | ||
111 | |||
112 | void nouveau_mem_free_block(struct mem_block *p) | ||
113 | { | ||
114 | p->file_priv = NULL; | ||
115 | |||
116 | /* Assumes a single contiguous range. Needs a special file_priv in | ||
117 | * 'heap' to stop it being subsumed. | ||
118 | */ | ||
119 | if (p->next->file_priv == NULL) { | ||
120 | struct mem_block *q = p->next; | ||
121 | p->size += q->size; | ||
122 | p->next = q->next; | ||
123 | p->next->prev = p; | ||
124 | kfree(q); | ||
125 | } | ||
126 | |||
127 | if (p->prev->file_priv == NULL) { | ||
128 | struct mem_block *q = p->prev; | ||
129 | q->size += p->size; | ||
130 | q->next = p->next; | ||
131 | q->next->prev = q; | ||
132 | kfree(p); | ||
133 | } | ||
134 | } | ||
135 | |||
136 | /* Initialize. How to check for an uninitialized heap? | ||
137 | */ | ||
138 | int nouveau_mem_init_heap(struct mem_block **heap, uint64_t start, | ||
139 | uint64_t size) | ||
140 | { | ||
141 | struct mem_block *blocks = kmalloc(sizeof(*blocks), GFP_KERNEL); | ||
142 | |||
143 | if (!blocks) | ||
144 | return -ENOMEM; | ||
145 | |||
146 | *heap = kmalloc(sizeof(**heap), GFP_KERNEL); | ||
147 | if (!*heap) { | ||
148 | kfree(blocks); | ||
149 | return -ENOMEM; | ||
150 | } | ||
151 | |||
152 | blocks->start = start; | ||
153 | blocks->size = size; | ||
154 | blocks->file_priv = NULL; | ||
155 | blocks->next = blocks->prev = *heap; | ||
156 | |||
157 | memset(*heap, 0, sizeof(**heap)); | ||
158 | (*heap)->file_priv = (struct drm_file *) -1; | ||
159 | (*heap)->next = (*heap)->prev = blocks; | ||
160 | return 0; | ||
161 | } | ||
162 | |||
163 | /* | ||
164 | * Free all blocks associated with the releasing file_priv | ||
165 | */ | ||
166 | void nouveau_mem_release(struct drm_file *file_priv, struct mem_block *heap) | ||
167 | { | ||
168 | struct mem_block *p; | ||
169 | |||
170 | if (!heap || !heap->next) | ||
171 | return; | ||
172 | |||
173 | list_for_each(p, heap) { | ||
174 | if (p->file_priv == file_priv) | ||
175 | p->file_priv = NULL; | ||
176 | } | ||
177 | |||
178 | /* Assumes a single contiguous range. Needs a special file_priv in | ||
179 | * 'heap' to stop it being subsumed. | ||
180 | */ | ||
181 | list_for_each(p, heap) { | ||
182 | while ((p->file_priv == NULL) && | ||
183 | (p->next->file_priv == NULL) && | ||
184 | (p->next != heap)) { | ||
185 | struct mem_block *q = p->next; | ||
186 | p->size += q->size; | ||
187 | p->next = q->next; | ||
188 | p->next->prev = p; | ||
189 | kfree(q); | ||
190 | } | ||
191 | } | ||
192 | } | ||
193 | |||
194 | /* | ||
195 | * NV50 VM helpers | ||
196 | */ | ||
197 | int | ||
198 | nv50_mem_vm_bind_linear(struct drm_device *dev, uint64_t virt, uint32_t size, | ||
199 | uint32_t flags, uint64_t phys) | ||
200 | { | ||
201 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
202 | struct nouveau_gpuobj **pgt; | ||
203 | unsigned psz, pfl, pages; | ||
204 | |||
205 | if (virt >= dev_priv->vm_gart_base && | ||
206 | (virt + size) < (dev_priv->vm_gart_base + dev_priv->vm_gart_size)) { | ||
207 | psz = 12; | ||
208 | pgt = &dev_priv->gart_info.sg_ctxdma; | ||
209 | pfl = 0x21; | ||
210 | virt -= dev_priv->vm_gart_base; | ||
211 | } else | ||
212 | if (virt >= dev_priv->vm_vram_base && | ||
213 | (virt + size) < (dev_priv->vm_vram_base + dev_priv->vm_vram_size)) { | ||
214 | psz = 16; | ||
215 | pgt = dev_priv->vm_vram_pt; | ||
216 | pfl = 0x01; | ||
217 | virt -= dev_priv->vm_vram_base; | ||
218 | } else { | ||
219 | NV_ERROR(dev, "Invalid address: 0x%16llx-0x%16llx\n", | ||
220 | virt, virt + size - 1); | ||
221 | return -EINVAL; | ||
222 | } | ||
223 | |||
224 | pages = size >> psz; | ||
225 | |||
226 | dev_priv->engine.instmem.prepare_access(dev, true); | ||
227 | if (flags & 0x80000000) { | ||
228 | while (pages--) { | ||
229 | struct nouveau_gpuobj *pt = pgt[virt >> 29]; | ||
230 | unsigned pte = ((virt & 0x1fffffffULL) >> psz) << 1; | ||
231 | |||
232 | nv_wo32(dev, pt, pte++, 0x00000000); | ||
233 | nv_wo32(dev, pt, pte++, 0x00000000); | ||
234 | |||
235 | virt += (1 << psz); | ||
236 | } | ||
237 | } else { | ||
238 | while (pages--) { | ||
239 | struct nouveau_gpuobj *pt = pgt[virt >> 29]; | ||
240 | unsigned pte = ((virt & 0x1fffffffULL) >> psz) << 1; | ||
241 | unsigned offset_h = upper_32_bits(phys) & 0xff; | ||
242 | unsigned offset_l = lower_32_bits(phys); | ||
243 | |||
244 | nv_wo32(dev, pt, pte++, offset_l | pfl); | ||
245 | nv_wo32(dev, pt, pte++, offset_h | flags); | ||
246 | |||
247 | phys += (1 << psz); | ||
248 | virt += (1 << psz); | ||
249 | } | ||
250 | } | ||
251 | dev_priv->engine.instmem.finish_access(dev); | ||
252 | |||
253 | nv_wr32(dev, 0x100c80, 0x00050001); | ||
254 | if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) { | ||
255 | NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n"); | ||
256 | NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80)); | ||
257 | return -EBUSY; | ||
258 | } | ||
259 | |||
260 | nv_wr32(dev, 0x100c80, 0x00000001); | ||
261 | if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) { | ||
262 | NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n"); | ||
263 | NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80)); | ||
264 | return -EBUSY; | ||
265 | } | ||
266 | |||
267 | return 0; | ||
268 | } | ||
269 | |||
270 | void | ||
271 | nv50_mem_vm_unbind(struct drm_device *dev, uint64_t virt, uint32_t size) | ||
272 | { | ||
273 | nv50_mem_vm_bind_linear(dev, virt, size, 0x80000000, 0); | ||
274 | } | ||
275 | |||
276 | /* | ||
277 | * Cleanup everything | ||
278 | */ | ||
279 | void nouveau_mem_takedown(struct mem_block **heap) | ||
280 | { | ||
281 | struct mem_block *p; | ||
282 | |||
283 | if (!*heap) | ||
284 | return; | ||
285 | |||
286 | for (p = (*heap)->next; p != *heap;) { | ||
287 | struct mem_block *q = p; | ||
288 | p = p->next; | ||
289 | kfree(q); | ||
290 | } | ||
291 | |||
292 | kfree(*heap); | ||
293 | *heap = NULL; | ||
294 | } | ||
295 | |||
296 | void nouveau_mem_close(struct drm_device *dev) | ||
297 | { | ||
298 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
299 | |||
300 | if (dev_priv->ttm.bdev.man[TTM_PL_PRIV0].has_type) | ||
301 | ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_PRIV0); | ||
302 | ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_VRAM); | ||
303 | |||
304 | ttm_bo_device_release(&dev_priv->ttm.bdev); | ||
305 | |||
306 | nouveau_ttm_global_release(dev_priv); | ||
307 | |||
308 | if (drm_core_has_AGP(dev) && dev->agp && | ||
309 | drm_core_check_feature(dev, DRIVER_MODESET)) { | ||
310 | struct drm_agp_mem *entry, *tempe; | ||
311 | |||
312 | /* Remove AGP resources, but leave dev->agp | ||
313 | intact until drv_cleanup is called. */ | ||
314 | list_for_each_entry_safe(entry, tempe, &dev->agp->memory, head) { | ||
315 | if (entry->bound) | ||
316 | drm_unbind_agp(entry->memory); | ||
317 | drm_free_agp(entry->memory, entry->pages); | ||
318 | kfree(entry); | ||
319 | } | ||
320 | INIT_LIST_HEAD(&dev->agp->memory); | ||
321 | |||
322 | if (dev->agp->acquired) | ||
323 | drm_agp_release(dev); | ||
324 | |||
325 | dev->agp->acquired = 0; | ||
326 | dev->agp->enabled = 0; | ||
327 | } | ||
328 | |||
329 | if (dev_priv->fb_mtrr) { | ||
330 | drm_mtrr_del(dev_priv->fb_mtrr, drm_get_resource_start(dev, 1), | ||
331 | drm_get_resource_len(dev, 1), DRM_MTRR_WC); | ||
332 | dev_priv->fb_mtrr = 0; | ||
333 | } | ||
334 | } | ||
335 | |||
336 | /*XXX won't work on BSD because of pci_read_config_dword */ | ||
337 | static uint32_t | ||
338 | nouveau_mem_fb_amount_igp(struct drm_device *dev) | ||
339 | { | ||
340 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
341 | struct pci_dev *bridge; | ||
342 | uint32_t mem; | ||
343 | |||
344 | bridge = pci_get_bus_and_slot(0, PCI_DEVFN(0, 1)); | ||
345 | if (!bridge) { | ||
346 | NV_ERROR(dev, "no bridge device\n"); | ||
347 | return 0; | ||
348 | } | ||
349 | |||
350 | if (dev_priv->flags&NV_NFORCE) { | ||
351 | pci_read_config_dword(bridge, 0x7C, &mem); | ||
352 | return (uint64_t)(((mem >> 6) & 31) + 1)*1024*1024; | ||
353 | } else | ||
354 | if (dev_priv->flags&NV_NFORCE2) { | ||
355 | pci_read_config_dword(bridge, 0x84, &mem); | ||
356 | return (uint64_t)(((mem >> 4) & 127) + 1)*1024*1024; | ||
357 | } | ||
358 | |||
359 | NV_ERROR(dev, "impossible!\n"); | ||
360 | return 0; | ||
361 | } | ||
362 | |||
363 | /* returns the amount of FB ram in bytes */ | ||
364 | uint64_t nouveau_mem_fb_amount(struct drm_device *dev) | ||
365 | { | ||
366 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
367 | uint32_t boot0; | ||
368 | |||
369 | switch (dev_priv->card_type) { | ||
370 | case NV_04: | ||
371 | boot0 = nv_rd32(dev, NV03_BOOT_0); | ||
372 | if (boot0 & 0x00000100) | ||
373 | return (((boot0 >> 12) & 0xf) * 2 + 2) * 1024 * 1024; | ||
374 | |||
375 | switch (boot0 & NV03_BOOT_0_RAM_AMOUNT) { | ||
376 | case NV04_BOOT_0_RAM_AMOUNT_32MB: | ||
377 | return 32 * 1024 * 1024; | ||
378 | case NV04_BOOT_0_RAM_AMOUNT_16MB: | ||
379 | return 16 * 1024 * 1024; | ||
380 | case NV04_BOOT_0_RAM_AMOUNT_8MB: | ||
381 | return 8 * 1024 * 1024; | ||
382 | case NV04_BOOT_0_RAM_AMOUNT_4MB: | ||
383 | return 4 * 1024 * 1024; | ||
384 | } | ||
385 | break; | ||
386 | case NV_10: | ||
387 | case NV_20: | ||
388 | case NV_30: | ||
389 | case NV_40: | ||
390 | case NV_50: | ||
391 | default: | ||
392 | if (dev_priv->flags & (NV_NFORCE | NV_NFORCE2)) { | ||
393 | return nouveau_mem_fb_amount_igp(dev); | ||
394 | } else { | ||
395 | uint64_t mem; | ||
396 | mem = (nv_rd32(dev, NV04_FIFO_DATA) & | ||
397 | NV10_FIFO_DATA_RAM_AMOUNT_MB_MASK) >> | ||
398 | NV10_FIFO_DATA_RAM_AMOUNT_MB_SHIFT; | ||
399 | return mem * 1024 * 1024; | ||
400 | } | ||
401 | break; | ||
402 | } | ||
403 | |||
404 | NV_ERROR(dev, | ||
405 | "Unable to detect video ram size. Please report your setup to " | ||
406 | DRIVER_EMAIL "\n"); | ||
407 | return 0; | ||
408 | } | ||
409 | |||
410 | static void nouveau_mem_reset_agp(struct drm_device *dev) | ||
411 | { | ||
412 | uint32_t saved_pci_nv_1, saved_pci_nv_19, pmc_enable; | ||
413 | |||
414 | saved_pci_nv_1 = nv_rd32(dev, NV04_PBUS_PCI_NV_1); | ||
415 | saved_pci_nv_19 = nv_rd32(dev, NV04_PBUS_PCI_NV_19); | ||
416 | |||
417 | /* clear busmaster bit */ | ||
418 | nv_wr32(dev, NV04_PBUS_PCI_NV_1, saved_pci_nv_1 & ~0x4); | ||
419 | /* clear SBA and AGP bits */ | ||
420 | nv_wr32(dev, NV04_PBUS_PCI_NV_19, saved_pci_nv_19 & 0xfffff0ff); | ||
421 | |||
422 | /* power cycle pgraph, if enabled */ | ||
423 | pmc_enable = nv_rd32(dev, NV03_PMC_ENABLE); | ||
424 | if (pmc_enable & NV_PMC_ENABLE_PGRAPH) { | ||
425 | nv_wr32(dev, NV03_PMC_ENABLE, | ||
426 | pmc_enable & ~NV_PMC_ENABLE_PGRAPH); | ||
427 | nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) | | ||
428 | NV_PMC_ENABLE_PGRAPH); | ||
429 | } | ||
430 | |||
431 | /* and restore (gives effect of resetting AGP) */ | ||
432 | nv_wr32(dev, NV04_PBUS_PCI_NV_19, saved_pci_nv_19); | ||
433 | nv_wr32(dev, NV04_PBUS_PCI_NV_1, saved_pci_nv_1); | ||
434 | } | ||
435 | |||
436 | int | ||
437 | nouveau_mem_init_agp(struct drm_device *dev) | ||
438 | { | ||
439 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
440 | struct drm_agp_info info; | ||
441 | struct drm_agp_mode mode; | ||
442 | int ret; | ||
443 | |||
444 | if (nouveau_noagp) | ||
445 | return 0; | ||
446 | |||
447 | nouveau_mem_reset_agp(dev); | ||
448 | |||
449 | if (!dev->agp->acquired) { | ||
450 | ret = drm_agp_acquire(dev); | ||
451 | if (ret) { | ||
452 | NV_ERROR(dev, "Unable to acquire AGP: %d\n", ret); | ||
453 | return ret; | ||
454 | } | ||
455 | } | ||
456 | |||
457 | ret = drm_agp_info(dev, &info); | ||
458 | if (ret) { | ||
459 | NV_ERROR(dev, "Unable to get AGP info: %d\n", ret); | ||
460 | return ret; | ||
461 | } | ||
462 | |||
463 | /* see agp.h for the AGPSTAT_* modes available */ | ||
464 | mode.mode = info.mode; | ||
465 | ret = drm_agp_enable(dev, mode); | ||
466 | if (ret) { | ||
467 | NV_ERROR(dev, "Unable to enable AGP: %d\n", ret); | ||
468 | return ret; | ||
469 | } | ||
470 | |||
471 | dev_priv->gart_info.type = NOUVEAU_GART_AGP; | ||
472 | dev_priv->gart_info.aper_base = info.aperture_base; | ||
473 | dev_priv->gart_info.aper_size = info.aperture_size; | ||
474 | return 0; | ||
475 | } | ||
476 | |||
477 | int | ||
478 | nouveau_mem_init(struct drm_device *dev) | ||
479 | { | ||
480 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
481 | struct ttm_bo_device *bdev = &dev_priv->ttm.bdev; | ||
482 | int ret, dma_bits = 32; | ||
483 | |||
484 | dev_priv->fb_phys = drm_get_resource_start(dev, 1); | ||
485 | dev_priv->gart_info.type = NOUVEAU_GART_NONE; | ||
486 | |||
487 | if (dev_priv->card_type >= NV_50 && | ||
488 | pci_dma_supported(dev->pdev, DMA_BIT_MASK(40))) | ||
489 | dma_bits = 40; | ||
490 | |||
491 | ret = pci_set_dma_mask(dev->pdev, DMA_BIT_MASK(dma_bits)); | ||
492 | if (ret) { | ||
493 | NV_ERROR(dev, "Error setting DMA mask: %d\n", ret); | ||
494 | return ret; | ||
495 | } | ||
496 | |||
497 | ret = nouveau_ttm_global_init(dev_priv); | ||
498 | if (ret) | ||
499 | return ret; | ||
500 | |||
501 | ret = ttm_bo_device_init(&dev_priv->ttm.bdev, | ||
502 | dev_priv->ttm.bo_global_ref.ref.object, | ||
503 | &nouveau_bo_driver, DRM_FILE_PAGE_OFFSET, | ||
504 | dma_bits <= 32 ? true : false); | ||
505 | if (ret) { | ||
506 | NV_ERROR(dev, "Error initialising bo driver: %d\n", ret); | ||
507 | return ret; | ||
508 | } | ||
509 | |||
510 | INIT_LIST_HEAD(&dev_priv->ttm.bo_list); | ||
511 | spin_lock_init(&dev_priv->ttm.bo_list_lock); | ||
512 | |||
513 | dev_priv->fb_available_size = nouveau_mem_fb_amount(dev); | ||
514 | |||
515 | dev_priv->fb_mappable_pages = dev_priv->fb_available_size; | ||
516 | if (dev_priv->fb_mappable_pages > drm_get_resource_len(dev, 1)) | ||
517 | dev_priv->fb_mappable_pages = drm_get_resource_len(dev, 1); | ||
518 | dev_priv->fb_mappable_pages >>= PAGE_SHIFT; | ||
519 | |||
520 | NV_INFO(dev, "%d MiB VRAM\n", (int)(dev_priv->fb_available_size >> 20)); | ||
521 | |||
522 | /* remove reserved space at end of vram from available amount */ | ||
523 | dev_priv->fb_available_size -= dev_priv->ramin_rsvd_vram; | ||
524 | dev_priv->fb_aper_free = dev_priv->fb_available_size; | ||
525 | |||
526 | /* mappable vram */ | ||
527 | ret = ttm_bo_init_mm(bdev, TTM_PL_VRAM, | ||
528 | dev_priv->fb_available_size >> PAGE_SHIFT); | ||
529 | if (ret) { | ||
530 | NV_ERROR(dev, "Failed VRAM mm init: %d\n", ret); | ||
531 | return ret; | ||
532 | } | ||
533 | |||
534 | /* GART */ | ||
535 | #if !defined(__powerpc__) && !defined(__ia64__) | ||
536 | if (drm_device_is_agp(dev) && dev->agp) { | ||
537 | ret = nouveau_mem_init_agp(dev); | ||
538 | if (ret) | ||
539 | NV_ERROR(dev, "Error initialising AGP: %d\n", ret); | ||
540 | } | ||
541 | #endif | ||
542 | |||
543 | if (dev_priv->gart_info.type == NOUVEAU_GART_NONE) { | ||
544 | ret = nouveau_sgdma_init(dev); | ||
545 | if (ret) { | ||
546 | NV_ERROR(dev, "Error initialising PCI(E): %d\n", ret); | ||
547 | return ret; | ||
548 | } | ||
549 | } | ||
550 | |||
551 | NV_INFO(dev, "%d MiB GART (aperture)\n", | ||
552 | (int)(dev_priv->gart_info.aper_size >> 20)); | ||
553 | dev_priv->gart_info.aper_free = dev_priv->gart_info.aper_size; | ||
554 | |||
555 | ret = ttm_bo_init_mm(bdev, TTM_PL_TT, | ||
556 | dev_priv->gart_info.aper_size >> PAGE_SHIFT); | ||
557 | if (ret) { | ||
558 | NV_ERROR(dev, "Failed TT mm init: %d\n", ret); | ||
559 | return ret; | ||
560 | } | ||
561 | |||
562 | dev_priv->fb_mtrr = drm_mtrr_add(drm_get_resource_start(dev, 1), | ||
563 | drm_get_resource_len(dev, 1), | ||
564 | DRM_MTRR_WC); | ||
565 | return 0; | ||
566 | } | ||
567 | |||
568 | |||
diff --git a/drivers/gpu/drm/nouveau/nouveau_notifier.c b/drivers/gpu/drm/nouveau/nouveau_notifier.c new file mode 100644 index 000000000000..6c66a34b6345 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_notifier.c | |||
@@ -0,0 +1,196 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007 Ben Skeggs. | ||
3 | * | ||
4 | * All Rights Reserved. | ||
5 | * | ||
6 | * Permission is hereby granted, free of charge, to any person obtaining | ||
7 | * a copy of this software and associated documentation files (the | ||
8 | * "Software"), to deal in the Software without restriction, including | ||
9 | * without limitation the rights to use, copy, modify, merge, publish, | ||
10 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
11 | * permit persons to whom the Software is furnished to do so, subject to | ||
12 | * the following conditions: | ||
13 | * | ||
14 | * The above copyright notice and this permission notice (including the | ||
15 | * next paragraph) shall be included in all copies or substantial | ||
16 | * portions of the Software. | ||
17 | * | ||
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
20 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
21 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
22 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
23 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
24 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
25 | * | ||
26 | */ | ||
27 | |||
28 | #include "drmP.h" | ||
29 | #include "drm.h" | ||
30 | #include "nouveau_drv.h" | ||
31 | |||
32 | int | ||
33 | nouveau_notifier_init_channel(struct nouveau_channel *chan) | ||
34 | { | ||
35 | struct drm_device *dev = chan->dev; | ||
36 | struct nouveau_bo *ntfy = NULL; | ||
37 | int ret; | ||
38 | |||
39 | ret = nouveau_gem_new(dev, NULL, PAGE_SIZE, 0, nouveau_vram_notify ? | ||
40 | TTM_PL_FLAG_VRAM : TTM_PL_FLAG_TT, | ||
41 | 0, 0x0000, false, true, &ntfy); | ||
42 | if (ret) | ||
43 | return ret; | ||
44 | |||
45 | ret = nouveau_bo_pin(ntfy, TTM_PL_FLAG_VRAM); | ||
46 | if (ret) | ||
47 | goto out_err; | ||
48 | |||
49 | ret = nouveau_bo_map(ntfy); | ||
50 | if (ret) | ||
51 | goto out_err; | ||
52 | |||
53 | ret = nouveau_mem_init_heap(&chan->notifier_heap, 0, ntfy->bo.mem.size); | ||
54 | if (ret) | ||
55 | goto out_err; | ||
56 | |||
57 | chan->notifier_bo = ntfy; | ||
58 | out_err: | ||
59 | if (ret) { | ||
60 | mutex_lock(&dev->struct_mutex); | ||
61 | drm_gem_object_unreference(ntfy->gem); | ||
62 | mutex_unlock(&dev->struct_mutex); | ||
63 | } | ||
64 | |||
65 | return ret; | ||
66 | } | ||
67 | |||
68 | void | ||
69 | nouveau_notifier_takedown_channel(struct nouveau_channel *chan) | ||
70 | { | ||
71 | struct drm_device *dev = chan->dev; | ||
72 | |||
73 | if (!chan->notifier_bo) | ||
74 | return; | ||
75 | |||
76 | nouveau_bo_unmap(chan->notifier_bo); | ||
77 | mutex_lock(&dev->struct_mutex); | ||
78 | nouveau_bo_unpin(chan->notifier_bo); | ||
79 | drm_gem_object_unreference(chan->notifier_bo->gem); | ||
80 | mutex_unlock(&dev->struct_mutex); | ||
81 | nouveau_mem_takedown(&chan->notifier_heap); | ||
82 | } | ||
83 | |||
84 | static void | ||
85 | nouveau_notifier_gpuobj_dtor(struct drm_device *dev, | ||
86 | struct nouveau_gpuobj *gpuobj) | ||
87 | { | ||
88 | NV_DEBUG(dev, "\n"); | ||
89 | |||
90 | if (gpuobj->priv) | ||
91 | nouveau_mem_free_block(gpuobj->priv); | ||
92 | } | ||
93 | |||
94 | int | ||
95 | nouveau_notifier_alloc(struct nouveau_channel *chan, uint32_t handle, | ||
96 | int size, uint32_t *b_offset) | ||
97 | { | ||
98 | struct drm_device *dev = chan->dev; | ||
99 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
100 | struct nouveau_gpuobj *nobj = NULL; | ||
101 | struct mem_block *mem; | ||
102 | uint32_t offset; | ||
103 | int target, ret; | ||
104 | |||
105 | if (!chan->notifier_heap) { | ||
106 | NV_ERROR(dev, "Channel %d doesn't have a notifier heap!\n", | ||
107 | chan->id); | ||
108 | return -EINVAL; | ||
109 | } | ||
110 | |||
111 | mem = nouveau_mem_alloc_block(chan->notifier_heap, size, 0, | ||
112 | (struct drm_file *)-2, 0); | ||
113 | if (!mem) { | ||
114 | NV_ERROR(dev, "Channel %d notifier block full\n", chan->id); | ||
115 | return -ENOMEM; | ||
116 | } | ||
117 | |||
118 | offset = chan->notifier_bo->bo.mem.mm_node->start << PAGE_SHIFT; | ||
119 | if (chan->notifier_bo->bo.mem.mem_type == TTM_PL_VRAM) { | ||
120 | target = NV_DMA_TARGET_VIDMEM; | ||
121 | } else | ||
122 | if (chan->notifier_bo->bo.mem.mem_type == TTM_PL_TT) { | ||
123 | if (dev_priv->gart_info.type == NOUVEAU_GART_SGDMA && | ||
124 | dev_priv->card_type < NV_50) { | ||
125 | ret = nouveau_sgdma_get_page(dev, offset, &offset); | ||
126 | if (ret) | ||
127 | return ret; | ||
128 | target = NV_DMA_TARGET_PCI; | ||
129 | } else { | ||
130 | target = NV_DMA_TARGET_AGP; | ||
131 | } | ||
132 | } else { | ||
133 | NV_ERROR(dev, "Bad DMA target, mem_type %d!\n", | ||
134 | chan->notifier_bo->bo.mem.mem_type); | ||
135 | return -EINVAL; | ||
136 | } | ||
137 | offset += mem->start; | ||
138 | |||
139 | ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, offset, | ||
140 | mem->size, NV_DMA_ACCESS_RW, target, | ||
141 | &nobj); | ||
142 | if (ret) { | ||
143 | nouveau_mem_free_block(mem); | ||
144 | NV_ERROR(dev, "Error creating notifier ctxdma: %d\n", ret); | ||
145 | return ret; | ||
146 | } | ||
147 | nobj->dtor = nouveau_notifier_gpuobj_dtor; | ||
148 | nobj->priv = mem; | ||
149 | |||
150 | ret = nouveau_gpuobj_ref_add(dev, chan, handle, nobj, NULL); | ||
151 | if (ret) { | ||
152 | nouveau_gpuobj_del(dev, &nobj); | ||
153 | nouveau_mem_free_block(mem); | ||
154 | NV_ERROR(dev, "Error referencing notifier ctxdma: %d\n", ret); | ||
155 | return ret; | ||
156 | } | ||
157 | |||
158 | *b_offset = mem->start; | ||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | int | ||
163 | nouveau_notifier_offset(struct nouveau_gpuobj *nobj, uint32_t *poffset) | ||
164 | { | ||
165 | if (!nobj || nobj->dtor != nouveau_notifier_gpuobj_dtor) | ||
166 | return -EINVAL; | ||
167 | |||
168 | if (poffset) { | ||
169 | struct mem_block *mem = nobj->priv; | ||
170 | |||
171 | if (*poffset >= mem->size) | ||
172 | return false; | ||
173 | |||
174 | *poffset += mem->start; | ||
175 | } | ||
176 | |||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | int | ||
181 | nouveau_ioctl_notifier_alloc(struct drm_device *dev, void *data, | ||
182 | struct drm_file *file_priv) | ||
183 | { | ||
184 | struct drm_nouveau_notifierobj_alloc *na = data; | ||
185 | struct nouveau_channel *chan; | ||
186 | int ret; | ||
187 | |||
188 | NOUVEAU_CHECK_INITIALISED_WITH_RETURN; | ||
189 | NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(na->channel, file_priv, chan); | ||
190 | |||
191 | ret = nouveau_notifier_alloc(chan, na->handle, na->size, &na->offset); | ||
192 | if (ret) | ||
193 | return ret; | ||
194 | |||
195 | return 0; | ||
196 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_object.c b/drivers/gpu/drm/nouveau/nouveau_object.c new file mode 100644 index 000000000000..93379bb81bea --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_object.c | |||
@@ -0,0 +1,1294 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2006 Ben Skeggs. | ||
3 | * | ||
4 | * All Rights Reserved. | ||
5 | * | ||
6 | * Permission is hereby granted, free of charge, to any person obtaining | ||
7 | * a copy of this software and associated documentation files (the | ||
8 | * "Software"), to deal in the Software without restriction, including | ||
9 | * without limitation the rights to use, copy, modify, merge, publish, | ||
10 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
11 | * permit persons to whom the Software is furnished to do so, subject to | ||
12 | * the following conditions: | ||
13 | * | ||
14 | * The above copyright notice and this permission notice (including the | ||
15 | * next paragraph) shall be included in all copies or substantial | ||
16 | * portions of the Software. | ||
17 | * | ||
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
20 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
21 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
22 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
23 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
24 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
25 | * | ||
26 | */ | ||
27 | |||
28 | /* | ||
29 | * Authors: | ||
30 | * Ben Skeggs <darktama@iinet.net.au> | ||
31 | */ | ||
32 | |||
33 | #include "drmP.h" | ||
34 | #include "drm.h" | ||
35 | #include "nouveau_drv.h" | ||
36 | #include "nouveau_drm.h" | ||
37 | |||
38 | /* NVidia uses context objects to drive drawing operations. | ||
39 | |||
40 | Context objects can be selected into 8 subchannels in the FIFO, | ||
41 | and then used via DMA command buffers. | ||
42 | |||
43 | A context object is referenced by a user defined handle (CARD32). The HW | ||
44 | looks up graphics objects in a hash table in the instance RAM. | ||
45 | |||
46 | An entry in the hash table consists of 2 CARD32. The first CARD32 contains | ||
47 | the handle, the second one a bitfield, that contains the address of the | ||
48 | object in instance RAM. | ||
49 | |||
50 | The format of the second CARD32 seems to be: | ||
51 | |||
52 | NV4 to NV30: | ||
53 | |||
54 | 15: 0 instance_addr >> 4 | ||
55 | 17:16 engine (here uses 1 = graphics) | ||
56 | 28:24 channel id (here uses 0) | ||
57 | 31 valid (use 1) | ||
58 | |||
59 | NV40: | ||
60 | |||
61 | 15: 0 instance_addr >> 4 (maybe 19-0) | ||
62 | 21:20 engine (here uses 1 = graphics) | ||
63 | I'm unsure about the other bits, but using 0 seems to work. | ||
64 | |||
65 | The key into the hash table depends on the object handle and channel id and | ||
66 | is given as: | ||
67 | */ | ||
68 | static uint32_t | ||
69 | nouveau_ramht_hash_handle(struct drm_device *dev, int channel, uint32_t handle) | ||
70 | { | ||
71 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
72 | uint32_t hash = 0; | ||
73 | int i; | ||
74 | |||
75 | NV_DEBUG(dev, "ch%d handle=0x%08x\n", channel, handle); | ||
76 | |||
77 | for (i = 32; i > 0; i -= dev_priv->ramht_bits) { | ||
78 | hash ^= (handle & ((1 << dev_priv->ramht_bits) - 1)); | ||
79 | handle >>= dev_priv->ramht_bits; | ||
80 | } | ||
81 | |||
82 | if (dev_priv->card_type < NV_50) | ||
83 | hash ^= channel << (dev_priv->ramht_bits - 4); | ||
84 | hash <<= 3; | ||
85 | |||
86 | NV_DEBUG(dev, "hash=0x%08x\n", hash); | ||
87 | return hash; | ||
88 | } | ||
89 | |||
90 | static int | ||
91 | nouveau_ramht_entry_valid(struct drm_device *dev, struct nouveau_gpuobj *ramht, | ||
92 | uint32_t offset) | ||
93 | { | ||
94 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
95 | uint32_t ctx = nv_ro32(dev, ramht, (offset + 4)/4); | ||
96 | |||
97 | if (dev_priv->card_type < NV_40) | ||
98 | return ((ctx & NV_RAMHT_CONTEXT_VALID) != 0); | ||
99 | return (ctx != 0); | ||
100 | } | ||
101 | |||
102 | static int | ||
103 | nouveau_ramht_insert(struct drm_device *dev, struct nouveau_gpuobj_ref *ref) | ||
104 | { | ||
105 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
106 | struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem; | ||
107 | struct nouveau_channel *chan = ref->channel; | ||
108 | struct nouveau_gpuobj *ramht = chan->ramht ? chan->ramht->gpuobj : NULL; | ||
109 | uint32_t ctx, co, ho; | ||
110 | |||
111 | if (!ramht) { | ||
112 | NV_ERROR(dev, "No hash table!\n"); | ||
113 | return -EINVAL; | ||
114 | } | ||
115 | |||
116 | if (dev_priv->card_type < NV_40) { | ||
117 | ctx = NV_RAMHT_CONTEXT_VALID | (ref->instance >> 4) | | ||
118 | (chan->id << NV_RAMHT_CONTEXT_CHANNEL_SHIFT) | | ||
119 | (ref->gpuobj->engine << NV_RAMHT_CONTEXT_ENGINE_SHIFT); | ||
120 | } else | ||
121 | if (dev_priv->card_type < NV_50) { | ||
122 | ctx = (ref->instance >> 4) | | ||
123 | (chan->id << NV40_RAMHT_CONTEXT_CHANNEL_SHIFT) | | ||
124 | (ref->gpuobj->engine << NV40_RAMHT_CONTEXT_ENGINE_SHIFT); | ||
125 | } else { | ||
126 | if (ref->gpuobj->engine == NVOBJ_ENGINE_DISPLAY) { | ||
127 | ctx = (ref->instance << 10) | 2; | ||
128 | } else { | ||
129 | ctx = (ref->instance >> 4) | | ||
130 | ((ref->gpuobj->engine << | ||
131 | NV40_RAMHT_CONTEXT_ENGINE_SHIFT)); | ||
132 | } | ||
133 | } | ||
134 | |||
135 | instmem->prepare_access(dev, true); | ||
136 | co = ho = nouveau_ramht_hash_handle(dev, chan->id, ref->handle); | ||
137 | do { | ||
138 | if (!nouveau_ramht_entry_valid(dev, ramht, co)) { | ||
139 | NV_DEBUG(dev, | ||
140 | "insert ch%d 0x%08x: h=0x%08x, c=0x%08x\n", | ||
141 | chan->id, co, ref->handle, ctx); | ||
142 | nv_wo32(dev, ramht, (co + 0)/4, ref->handle); | ||
143 | nv_wo32(dev, ramht, (co + 4)/4, ctx); | ||
144 | |||
145 | list_add_tail(&ref->list, &chan->ramht_refs); | ||
146 | instmem->finish_access(dev); | ||
147 | return 0; | ||
148 | } | ||
149 | NV_DEBUG(dev, "collision ch%d 0x%08x: h=0x%08x\n", | ||
150 | chan->id, co, nv_ro32(dev, ramht, co/4)); | ||
151 | |||
152 | co += 8; | ||
153 | if (co >= dev_priv->ramht_size) | ||
154 | co = 0; | ||
155 | } while (co != ho); | ||
156 | instmem->finish_access(dev); | ||
157 | |||
158 | NV_ERROR(dev, "RAMHT space exhausted. ch=%d\n", chan->id); | ||
159 | return -ENOMEM; | ||
160 | } | ||
161 | |||
162 | static void | ||
163 | nouveau_ramht_remove(struct drm_device *dev, struct nouveau_gpuobj_ref *ref) | ||
164 | { | ||
165 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
166 | struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem; | ||
167 | struct nouveau_channel *chan = ref->channel; | ||
168 | struct nouveau_gpuobj *ramht = chan->ramht ? chan->ramht->gpuobj : NULL; | ||
169 | uint32_t co, ho; | ||
170 | |||
171 | if (!ramht) { | ||
172 | NV_ERROR(dev, "No hash table!\n"); | ||
173 | return; | ||
174 | } | ||
175 | |||
176 | instmem->prepare_access(dev, true); | ||
177 | co = ho = nouveau_ramht_hash_handle(dev, chan->id, ref->handle); | ||
178 | do { | ||
179 | if (nouveau_ramht_entry_valid(dev, ramht, co) && | ||
180 | (ref->handle == nv_ro32(dev, ramht, (co/4)))) { | ||
181 | NV_DEBUG(dev, | ||
182 | "remove ch%d 0x%08x: h=0x%08x, c=0x%08x\n", | ||
183 | chan->id, co, ref->handle, | ||
184 | nv_ro32(dev, ramht, (co + 4))); | ||
185 | nv_wo32(dev, ramht, (co + 0)/4, 0x00000000); | ||
186 | nv_wo32(dev, ramht, (co + 4)/4, 0x00000000); | ||
187 | |||
188 | list_del(&ref->list); | ||
189 | instmem->finish_access(dev); | ||
190 | return; | ||
191 | } | ||
192 | |||
193 | co += 8; | ||
194 | if (co >= dev_priv->ramht_size) | ||
195 | co = 0; | ||
196 | } while (co != ho); | ||
197 | list_del(&ref->list); | ||
198 | instmem->finish_access(dev); | ||
199 | |||
200 | NV_ERROR(dev, "RAMHT entry not found. ch=%d, handle=0x%08x\n", | ||
201 | chan->id, ref->handle); | ||
202 | } | ||
203 | |||
204 | int | ||
205 | nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan, | ||
206 | uint32_t size, int align, uint32_t flags, | ||
207 | struct nouveau_gpuobj **gpuobj_ret) | ||
208 | { | ||
209 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
210 | struct nouveau_engine *engine = &dev_priv->engine; | ||
211 | struct nouveau_gpuobj *gpuobj; | ||
212 | struct mem_block *pramin = NULL; | ||
213 | int ret; | ||
214 | |||
215 | NV_DEBUG(dev, "ch%d size=%u align=%d flags=0x%08x\n", | ||
216 | chan ? chan->id : -1, size, align, flags); | ||
217 | |||
218 | if (!dev_priv || !gpuobj_ret || *gpuobj_ret != NULL) | ||
219 | return -EINVAL; | ||
220 | |||
221 | gpuobj = kzalloc(sizeof(*gpuobj), GFP_KERNEL); | ||
222 | if (!gpuobj) | ||
223 | return -ENOMEM; | ||
224 | NV_DEBUG(dev, "gpuobj %p\n", gpuobj); | ||
225 | gpuobj->flags = flags; | ||
226 | gpuobj->im_channel = chan; | ||
227 | |||
228 | list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list); | ||
229 | |||
230 | /* Choose between global instmem heap, and per-channel private | ||
231 | * instmem heap. On <NV50 allow requests for private instmem | ||
232 | * to be satisfied from global heap if no per-channel area | ||
233 | * available. | ||
234 | */ | ||
235 | if (chan) { | ||
236 | if (chan->ramin_heap) { | ||
237 | NV_DEBUG(dev, "private heap\n"); | ||
238 | pramin = chan->ramin_heap; | ||
239 | } else | ||
240 | if (dev_priv->card_type < NV_50) { | ||
241 | NV_DEBUG(dev, "global heap fallback\n"); | ||
242 | pramin = dev_priv->ramin_heap; | ||
243 | } | ||
244 | } else { | ||
245 | NV_DEBUG(dev, "global heap\n"); | ||
246 | pramin = dev_priv->ramin_heap; | ||
247 | } | ||
248 | |||
249 | if (!pramin) { | ||
250 | NV_ERROR(dev, "No PRAMIN heap!\n"); | ||
251 | return -EINVAL; | ||
252 | } | ||
253 | |||
254 | if (!chan) { | ||
255 | ret = engine->instmem.populate(dev, gpuobj, &size); | ||
256 | if (ret) { | ||
257 | nouveau_gpuobj_del(dev, &gpuobj); | ||
258 | return ret; | ||
259 | } | ||
260 | } | ||
261 | |||
262 | /* Allocate a chunk of the PRAMIN aperture */ | ||
263 | gpuobj->im_pramin = nouveau_mem_alloc_block(pramin, size, | ||
264 | drm_order(align), | ||
265 | (struct drm_file *)-2, 0); | ||
266 | if (!gpuobj->im_pramin) { | ||
267 | nouveau_gpuobj_del(dev, &gpuobj); | ||
268 | return -ENOMEM; | ||
269 | } | ||
270 | |||
271 | if (!chan) { | ||
272 | ret = engine->instmem.bind(dev, gpuobj); | ||
273 | if (ret) { | ||
274 | nouveau_gpuobj_del(dev, &gpuobj); | ||
275 | return ret; | ||
276 | } | ||
277 | } | ||
278 | |||
279 | if (gpuobj->flags & NVOBJ_FLAG_ZERO_ALLOC) { | ||
280 | int i; | ||
281 | |||
282 | engine->instmem.prepare_access(dev, true); | ||
283 | for (i = 0; i < gpuobj->im_pramin->size; i += 4) | ||
284 | nv_wo32(dev, gpuobj, i/4, 0); | ||
285 | engine->instmem.finish_access(dev); | ||
286 | } | ||
287 | |||
288 | *gpuobj_ret = gpuobj; | ||
289 | return 0; | ||
290 | } | ||
291 | |||
292 | int | ||
293 | nouveau_gpuobj_early_init(struct drm_device *dev) | ||
294 | { | ||
295 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
296 | |||
297 | NV_DEBUG(dev, "\n"); | ||
298 | |||
299 | INIT_LIST_HEAD(&dev_priv->gpuobj_list); | ||
300 | |||
301 | return 0; | ||
302 | } | ||
303 | |||
304 | int | ||
305 | nouveau_gpuobj_init(struct drm_device *dev) | ||
306 | { | ||
307 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
308 | int ret; | ||
309 | |||
310 | NV_DEBUG(dev, "\n"); | ||
311 | |||
312 | if (dev_priv->card_type < NV_50) { | ||
313 | ret = nouveau_gpuobj_new_fake(dev, | ||
314 | dev_priv->ramht_offset, ~0, dev_priv->ramht_size, | ||
315 | NVOBJ_FLAG_ZERO_ALLOC | NVOBJ_FLAG_ALLOW_NO_REFS, | ||
316 | &dev_priv->ramht, NULL); | ||
317 | if (ret) | ||
318 | return ret; | ||
319 | } | ||
320 | |||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | void | ||
325 | nouveau_gpuobj_takedown(struct drm_device *dev) | ||
326 | { | ||
327 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
328 | |||
329 | NV_DEBUG(dev, "\n"); | ||
330 | |||
331 | nouveau_gpuobj_del(dev, &dev_priv->ramht); | ||
332 | } | ||
333 | |||
334 | void | ||
335 | nouveau_gpuobj_late_takedown(struct drm_device *dev) | ||
336 | { | ||
337 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
338 | struct nouveau_gpuobj *gpuobj = NULL; | ||
339 | struct list_head *entry, *tmp; | ||
340 | |||
341 | NV_DEBUG(dev, "\n"); | ||
342 | |||
343 | list_for_each_safe(entry, tmp, &dev_priv->gpuobj_list) { | ||
344 | gpuobj = list_entry(entry, struct nouveau_gpuobj, list); | ||
345 | |||
346 | NV_ERROR(dev, "gpuobj %p still exists at takedown, refs=%d\n", | ||
347 | gpuobj, gpuobj->refcount); | ||
348 | gpuobj->refcount = 0; | ||
349 | nouveau_gpuobj_del(dev, &gpuobj); | ||
350 | } | ||
351 | } | ||
352 | |||
353 | int | ||
354 | nouveau_gpuobj_del(struct drm_device *dev, struct nouveau_gpuobj **pgpuobj) | ||
355 | { | ||
356 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
357 | struct nouveau_engine *engine = &dev_priv->engine; | ||
358 | struct nouveau_gpuobj *gpuobj; | ||
359 | int i; | ||
360 | |||
361 | NV_DEBUG(dev, "gpuobj %p\n", pgpuobj ? *pgpuobj : NULL); | ||
362 | |||
363 | if (!dev_priv || !pgpuobj || !(*pgpuobj)) | ||
364 | return -EINVAL; | ||
365 | gpuobj = *pgpuobj; | ||
366 | |||
367 | if (gpuobj->refcount != 0) { | ||
368 | NV_ERROR(dev, "gpuobj refcount is %d\n", gpuobj->refcount); | ||
369 | return -EINVAL; | ||
370 | } | ||
371 | |||
372 | if (gpuobj->im_pramin && (gpuobj->flags & NVOBJ_FLAG_ZERO_FREE)) { | ||
373 | engine->instmem.prepare_access(dev, true); | ||
374 | for (i = 0; i < gpuobj->im_pramin->size; i += 4) | ||
375 | nv_wo32(dev, gpuobj, i/4, 0); | ||
376 | engine->instmem.finish_access(dev); | ||
377 | } | ||
378 | |||
379 | if (gpuobj->dtor) | ||
380 | gpuobj->dtor(dev, gpuobj); | ||
381 | |||
382 | if (gpuobj->im_backing && !(gpuobj->flags & NVOBJ_FLAG_FAKE)) | ||
383 | engine->instmem.clear(dev, gpuobj); | ||
384 | |||
385 | if (gpuobj->im_pramin) { | ||
386 | if (gpuobj->flags & NVOBJ_FLAG_FAKE) | ||
387 | kfree(gpuobj->im_pramin); | ||
388 | else | ||
389 | nouveau_mem_free_block(gpuobj->im_pramin); | ||
390 | } | ||
391 | |||
392 | list_del(&gpuobj->list); | ||
393 | |||
394 | *pgpuobj = NULL; | ||
395 | kfree(gpuobj); | ||
396 | return 0; | ||
397 | } | ||
398 | |||
399 | static int | ||
400 | nouveau_gpuobj_instance_get(struct drm_device *dev, | ||
401 | struct nouveau_channel *chan, | ||
402 | struct nouveau_gpuobj *gpuobj, uint32_t *inst) | ||
403 | { | ||
404 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
405 | struct nouveau_gpuobj *cpramin; | ||
406 | |||
407 | /* <NV50 use PRAMIN address everywhere */ | ||
408 | if (dev_priv->card_type < NV_50) { | ||
409 | *inst = gpuobj->im_pramin->start; | ||
410 | return 0; | ||
411 | } | ||
412 | |||
413 | if (chan && gpuobj->im_channel != chan) { | ||
414 | NV_ERROR(dev, "Channel mismatch: obj %d, ref %d\n", | ||
415 | gpuobj->im_channel->id, chan->id); | ||
416 | return -EINVAL; | ||
417 | } | ||
418 | |||
419 | /* NV50 channel-local instance */ | ||
420 | if (chan) { | ||
421 | cpramin = chan->ramin->gpuobj; | ||
422 | *inst = gpuobj->im_pramin->start - cpramin->im_pramin->start; | ||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | /* NV50 global (VRAM) instance */ | ||
427 | if (!gpuobj->im_channel) { | ||
428 | /* ...from global heap */ | ||
429 | if (!gpuobj->im_backing) { | ||
430 | NV_ERROR(dev, "AII, no VRAM backing gpuobj\n"); | ||
431 | return -EINVAL; | ||
432 | } | ||
433 | *inst = gpuobj->im_backing_start; | ||
434 | return 0; | ||
435 | } else { | ||
436 | /* ...from local heap */ | ||
437 | cpramin = gpuobj->im_channel->ramin->gpuobj; | ||
438 | *inst = cpramin->im_backing_start + | ||
439 | (gpuobj->im_pramin->start - cpramin->im_pramin->start); | ||
440 | return 0; | ||
441 | } | ||
442 | |||
443 | return -EINVAL; | ||
444 | } | ||
445 | |||
446 | int | ||
447 | nouveau_gpuobj_ref_add(struct drm_device *dev, struct nouveau_channel *chan, | ||
448 | uint32_t handle, struct nouveau_gpuobj *gpuobj, | ||
449 | struct nouveau_gpuobj_ref **ref_ret) | ||
450 | { | ||
451 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
452 | struct nouveau_gpuobj_ref *ref; | ||
453 | uint32_t instance; | ||
454 | int ret; | ||
455 | |||
456 | NV_DEBUG(dev, "ch%d h=0x%08x gpuobj=%p\n", | ||
457 | chan ? chan->id : -1, handle, gpuobj); | ||
458 | |||
459 | if (!dev_priv || !gpuobj || (ref_ret && *ref_ret != NULL)) | ||
460 | return -EINVAL; | ||
461 | |||
462 | if (!chan && !ref_ret) | ||
463 | return -EINVAL; | ||
464 | |||
465 | if (gpuobj->engine == NVOBJ_ENGINE_SW && !gpuobj->im_pramin) { | ||
466 | /* sw object */ | ||
467 | instance = 0x40; | ||
468 | } else { | ||
469 | ret = nouveau_gpuobj_instance_get(dev, chan, gpuobj, &instance); | ||
470 | if (ret) | ||
471 | return ret; | ||
472 | } | ||
473 | |||
474 | ref = kzalloc(sizeof(*ref), GFP_KERNEL); | ||
475 | if (!ref) | ||
476 | return -ENOMEM; | ||
477 | INIT_LIST_HEAD(&ref->list); | ||
478 | ref->gpuobj = gpuobj; | ||
479 | ref->channel = chan; | ||
480 | ref->instance = instance; | ||
481 | |||
482 | if (!ref_ret) { | ||
483 | ref->handle = handle; | ||
484 | |||
485 | ret = nouveau_ramht_insert(dev, ref); | ||
486 | if (ret) { | ||
487 | kfree(ref); | ||
488 | return ret; | ||
489 | } | ||
490 | } else { | ||
491 | ref->handle = ~0; | ||
492 | *ref_ret = ref; | ||
493 | } | ||
494 | |||
495 | ref->gpuobj->refcount++; | ||
496 | return 0; | ||
497 | } | ||
498 | |||
499 | int nouveau_gpuobj_ref_del(struct drm_device *dev, struct nouveau_gpuobj_ref **pref) | ||
500 | { | ||
501 | struct nouveau_gpuobj_ref *ref; | ||
502 | |||
503 | NV_DEBUG(dev, "ref %p\n", pref ? *pref : NULL); | ||
504 | |||
505 | if (!dev || !pref || *pref == NULL) | ||
506 | return -EINVAL; | ||
507 | ref = *pref; | ||
508 | |||
509 | if (ref->handle != ~0) | ||
510 | nouveau_ramht_remove(dev, ref); | ||
511 | |||
512 | if (ref->gpuobj) { | ||
513 | ref->gpuobj->refcount--; | ||
514 | |||
515 | if (ref->gpuobj->refcount == 0) { | ||
516 | if (!(ref->gpuobj->flags & NVOBJ_FLAG_ALLOW_NO_REFS)) | ||
517 | nouveau_gpuobj_del(dev, &ref->gpuobj); | ||
518 | } | ||
519 | } | ||
520 | |||
521 | *pref = NULL; | ||
522 | kfree(ref); | ||
523 | return 0; | ||
524 | } | ||
525 | |||
526 | int | ||
527 | nouveau_gpuobj_new_ref(struct drm_device *dev, | ||
528 | struct nouveau_channel *oc, struct nouveau_channel *rc, | ||
529 | uint32_t handle, uint32_t size, int align, | ||
530 | uint32_t flags, struct nouveau_gpuobj_ref **ref) | ||
531 | { | ||
532 | struct nouveau_gpuobj *gpuobj = NULL; | ||
533 | int ret; | ||
534 | |||
535 | ret = nouveau_gpuobj_new(dev, oc, size, align, flags, &gpuobj); | ||
536 | if (ret) | ||
537 | return ret; | ||
538 | |||
539 | ret = nouveau_gpuobj_ref_add(dev, rc, handle, gpuobj, ref); | ||
540 | if (ret) { | ||
541 | nouveau_gpuobj_del(dev, &gpuobj); | ||
542 | return ret; | ||
543 | } | ||
544 | |||
545 | return 0; | ||
546 | } | ||
547 | |||
548 | int | ||
549 | nouveau_gpuobj_ref_find(struct nouveau_channel *chan, uint32_t handle, | ||
550 | struct nouveau_gpuobj_ref **ref_ret) | ||
551 | { | ||
552 | struct nouveau_gpuobj_ref *ref; | ||
553 | struct list_head *entry, *tmp; | ||
554 | |||
555 | list_for_each_safe(entry, tmp, &chan->ramht_refs) { | ||
556 | ref = list_entry(entry, struct nouveau_gpuobj_ref, list); | ||
557 | |||
558 | if (ref->handle == handle) { | ||
559 | if (ref_ret) | ||
560 | *ref_ret = ref; | ||
561 | return 0; | ||
562 | } | ||
563 | } | ||
564 | |||
565 | return -EINVAL; | ||
566 | } | ||
567 | |||
568 | int | ||
569 | nouveau_gpuobj_new_fake(struct drm_device *dev, uint32_t p_offset, | ||
570 | uint32_t b_offset, uint32_t size, | ||
571 | uint32_t flags, struct nouveau_gpuobj **pgpuobj, | ||
572 | struct nouveau_gpuobj_ref **pref) | ||
573 | { | ||
574 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
575 | struct nouveau_gpuobj *gpuobj = NULL; | ||
576 | int i; | ||
577 | |||
578 | NV_DEBUG(dev, | ||
579 | "p_offset=0x%08x b_offset=0x%08x size=0x%08x flags=0x%08x\n", | ||
580 | p_offset, b_offset, size, flags); | ||
581 | |||
582 | gpuobj = kzalloc(sizeof(*gpuobj), GFP_KERNEL); | ||
583 | if (!gpuobj) | ||
584 | return -ENOMEM; | ||
585 | NV_DEBUG(dev, "gpuobj %p\n", gpuobj); | ||
586 | gpuobj->im_channel = NULL; | ||
587 | gpuobj->flags = flags | NVOBJ_FLAG_FAKE; | ||
588 | |||
589 | list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list); | ||
590 | |||
591 | if (p_offset != ~0) { | ||
592 | gpuobj->im_pramin = kzalloc(sizeof(struct mem_block), | ||
593 | GFP_KERNEL); | ||
594 | if (!gpuobj->im_pramin) { | ||
595 | nouveau_gpuobj_del(dev, &gpuobj); | ||
596 | return -ENOMEM; | ||
597 | } | ||
598 | gpuobj->im_pramin->start = p_offset; | ||
599 | gpuobj->im_pramin->size = size; | ||
600 | } | ||
601 | |||
602 | if (b_offset != ~0) { | ||
603 | gpuobj->im_backing = (struct nouveau_bo *)-1; | ||
604 | gpuobj->im_backing_start = b_offset; | ||
605 | } | ||
606 | |||
607 | if (gpuobj->flags & NVOBJ_FLAG_ZERO_ALLOC) { | ||
608 | dev_priv->engine.instmem.prepare_access(dev, true); | ||
609 | for (i = 0; i < gpuobj->im_pramin->size; i += 4) | ||
610 | nv_wo32(dev, gpuobj, i/4, 0); | ||
611 | dev_priv->engine.instmem.finish_access(dev); | ||
612 | } | ||
613 | |||
614 | if (pref) { | ||
615 | i = nouveau_gpuobj_ref_add(dev, NULL, 0, gpuobj, pref); | ||
616 | if (i) { | ||
617 | nouveau_gpuobj_del(dev, &gpuobj); | ||
618 | return i; | ||
619 | } | ||
620 | } | ||
621 | |||
622 | if (pgpuobj) | ||
623 | *pgpuobj = gpuobj; | ||
624 | return 0; | ||
625 | } | ||
626 | |||
627 | |||
628 | static uint32_t | ||
629 | nouveau_gpuobj_class_instmem_size(struct drm_device *dev, int class) | ||
630 | { | ||
631 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
632 | |||
633 | /*XXX: dodgy hack for now */ | ||
634 | if (dev_priv->card_type >= NV_50) | ||
635 | return 24; | ||
636 | if (dev_priv->card_type >= NV_40) | ||
637 | return 32; | ||
638 | return 16; | ||
639 | } | ||
640 | |||
641 | /* | ||
642 | DMA objects are used to reference a piece of memory in the | ||
643 | framebuffer, PCI or AGP address space. Each object is 16 bytes big | ||
644 | and looks as follows: | ||
645 | |||
646 | entry[0] | ||
647 | 11:0 class (seems like I can always use 0 here) | ||
648 | 12 page table present? | ||
649 | 13 page entry linear? | ||
650 | 15:14 access: 0 rw, 1 ro, 2 wo | ||
651 | 17:16 target: 0 NV memory, 1 NV memory tiled, 2 PCI, 3 AGP | ||
652 | 31:20 dma adjust (bits 0-11 of the address) | ||
653 | entry[1] | ||
654 | dma limit (size of transfer) | ||
655 | entry[X] | ||
656 | 1 0 readonly, 1 readwrite | ||
657 | 31:12 dma frame address of the page (bits 12-31 of the address) | ||
658 | entry[N] | ||
659 | page table terminator, same value as the first pte, as does nvidia | ||
660 | rivatv uses 0xffffffff | ||
661 | |||
662 | Non linear page tables need a list of frame addresses afterwards, | ||
663 | the rivatv project has some info on this. | ||
664 | |||
665 | The method below creates a DMA object in instance RAM and returns a handle | ||
666 | to it that can be used to set up context objects. | ||
667 | */ | ||
668 | int | ||
669 | nouveau_gpuobj_dma_new(struct nouveau_channel *chan, int class, | ||
670 | uint64_t offset, uint64_t size, int access, | ||
671 | int target, struct nouveau_gpuobj **gpuobj) | ||
672 | { | ||
673 | struct drm_device *dev = chan->dev; | ||
674 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
675 | struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem; | ||
676 | int ret; | ||
677 | |||
678 | NV_DEBUG(dev, "ch%d class=0x%04x offset=0x%llx size=0x%llx\n", | ||
679 | chan->id, class, offset, size); | ||
680 | NV_DEBUG(dev, "access=%d target=%d\n", access, target); | ||
681 | |||
682 | switch (target) { | ||
683 | case NV_DMA_TARGET_AGP: | ||
684 | offset += dev_priv->gart_info.aper_base; | ||
685 | break; | ||
686 | default: | ||
687 | break; | ||
688 | } | ||
689 | |||
690 | ret = nouveau_gpuobj_new(dev, chan, | ||
691 | nouveau_gpuobj_class_instmem_size(dev, class), | ||
692 | 16, NVOBJ_FLAG_ZERO_ALLOC | | ||
693 | NVOBJ_FLAG_ZERO_FREE, gpuobj); | ||
694 | if (ret) { | ||
695 | NV_ERROR(dev, "Error creating gpuobj: %d\n", ret); | ||
696 | return ret; | ||
697 | } | ||
698 | |||
699 | instmem->prepare_access(dev, true); | ||
700 | |||
701 | if (dev_priv->card_type < NV_50) { | ||
702 | uint32_t frame, adjust, pte_flags = 0; | ||
703 | |||
704 | if (access != NV_DMA_ACCESS_RO) | ||
705 | pte_flags |= (1<<1); | ||
706 | adjust = offset & 0x00000fff; | ||
707 | frame = offset & ~0x00000fff; | ||
708 | |||
709 | nv_wo32(dev, *gpuobj, 0, ((1<<12) | (1<<13) | | ||
710 | (adjust << 20) | | ||
711 | (access << 14) | | ||
712 | (target << 16) | | ||
713 | class)); | ||
714 | nv_wo32(dev, *gpuobj, 1, size - 1); | ||
715 | nv_wo32(dev, *gpuobj, 2, frame | pte_flags); | ||
716 | nv_wo32(dev, *gpuobj, 3, frame | pte_flags); | ||
717 | } else { | ||
718 | uint64_t limit = offset + size - 1; | ||
719 | uint32_t flags0, flags5; | ||
720 | |||
721 | if (target == NV_DMA_TARGET_VIDMEM) { | ||
722 | flags0 = 0x00190000; | ||
723 | flags5 = 0x00010000; | ||
724 | } else { | ||
725 | flags0 = 0x7fc00000; | ||
726 | flags5 = 0x00080000; | ||
727 | } | ||
728 | |||
729 | nv_wo32(dev, *gpuobj, 0, flags0 | class); | ||
730 | nv_wo32(dev, *gpuobj, 1, lower_32_bits(limit)); | ||
731 | nv_wo32(dev, *gpuobj, 2, lower_32_bits(offset)); | ||
732 | nv_wo32(dev, *gpuobj, 3, ((upper_32_bits(limit) & 0xff) << 24) | | ||
733 | (upper_32_bits(offset) & 0xff)); | ||
734 | nv_wo32(dev, *gpuobj, 5, flags5); | ||
735 | } | ||
736 | |||
737 | instmem->finish_access(dev); | ||
738 | |||
739 | (*gpuobj)->engine = NVOBJ_ENGINE_SW; | ||
740 | (*gpuobj)->class = class; | ||
741 | return 0; | ||
742 | } | ||
743 | |||
744 | int | ||
745 | nouveau_gpuobj_gart_dma_new(struct nouveau_channel *chan, | ||
746 | uint64_t offset, uint64_t size, int access, | ||
747 | struct nouveau_gpuobj **gpuobj, | ||
748 | uint32_t *o_ret) | ||
749 | { | ||
750 | struct drm_device *dev = chan->dev; | ||
751 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
752 | int ret; | ||
753 | |||
754 | if (dev_priv->gart_info.type == NOUVEAU_GART_AGP || | ||
755 | (dev_priv->card_type >= NV_50 && | ||
756 | dev_priv->gart_info.type == NOUVEAU_GART_SGDMA)) { | ||
757 | ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, | ||
758 | offset + dev_priv->vm_gart_base, | ||
759 | size, access, NV_DMA_TARGET_AGP, | ||
760 | gpuobj); | ||
761 | if (o_ret) | ||
762 | *o_ret = 0; | ||
763 | } else | ||
764 | if (dev_priv->gart_info.type == NOUVEAU_GART_SGDMA) { | ||
765 | *gpuobj = dev_priv->gart_info.sg_ctxdma; | ||
766 | if (offset & ~0xffffffffULL) { | ||
767 | NV_ERROR(dev, "obj offset exceeds 32-bits\n"); | ||
768 | return -EINVAL; | ||
769 | } | ||
770 | if (o_ret) | ||
771 | *o_ret = (uint32_t)offset; | ||
772 | ret = (*gpuobj != NULL) ? 0 : -EINVAL; | ||
773 | } else { | ||
774 | NV_ERROR(dev, "Invalid GART type %d\n", dev_priv->gart_info.type); | ||
775 | return -EINVAL; | ||
776 | } | ||
777 | |||
778 | return ret; | ||
779 | } | ||
780 | |||
781 | /* Context objects in the instance RAM have the following structure. | ||
782 | * On NV40 they are 32 byte long, on NV30 and smaller 16 bytes. | ||
783 | |||
784 | NV4 - NV30: | ||
785 | |||
786 | entry[0] | ||
787 | 11:0 class | ||
788 | 12 chroma key enable | ||
789 | 13 user clip enable | ||
790 | 14 swizzle enable | ||
791 | 17:15 patch config: | ||
792 | scrcopy_and, rop_and, blend_and, scrcopy, srccopy_pre, blend_pre | ||
793 | 18 synchronize enable | ||
794 | 19 endian: 1 big, 0 little | ||
795 | 21:20 dither mode | ||
796 | 23 single step enable | ||
797 | 24 patch status: 0 invalid, 1 valid | ||
798 | 25 context_surface 0: 1 valid | ||
799 | 26 context surface 1: 1 valid | ||
800 | 27 context pattern: 1 valid | ||
801 | 28 context rop: 1 valid | ||
802 | 29,30 context beta, beta4 | ||
803 | entry[1] | ||
804 | 7:0 mono format | ||
805 | 15:8 color format | ||
806 | 31:16 notify instance address | ||
807 | entry[2] | ||
808 | 15:0 dma 0 instance address | ||
809 | 31:16 dma 1 instance address | ||
810 | entry[3] | ||
811 | dma method traps | ||
812 | |||
813 | NV40: | ||
814 | No idea what the exact format is. Here's what can be deducted: | ||
815 | |||
816 | entry[0]: | ||
817 | 11:0 class (maybe uses more bits here?) | ||
818 | 17 user clip enable | ||
819 | 21:19 patch config | ||
820 | 25 patch status valid ? | ||
821 | entry[1]: | ||
822 | 15:0 DMA notifier (maybe 20:0) | ||
823 | entry[2]: | ||
824 | 15:0 DMA 0 instance (maybe 20:0) | ||
825 | 24 big endian | ||
826 | entry[3]: | ||
827 | 15:0 DMA 1 instance (maybe 20:0) | ||
828 | entry[4]: | ||
829 | entry[5]: | ||
830 | set to 0? | ||
831 | */ | ||
832 | int | ||
833 | nouveau_gpuobj_gr_new(struct nouveau_channel *chan, int class, | ||
834 | struct nouveau_gpuobj **gpuobj) | ||
835 | { | ||
836 | struct drm_device *dev = chan->dev; | ||
837 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
838 | int ret; | ||
839 | |||
840 | NV_DEBUG(dev, "ch%d class=0x%04x\n", chan->id, class); | ||
841 | |||
842 | ret = nouveau_gpuobj_new(dev, chan, | ||
843 | nouveau_gpuobj_class_instmem_size(dev, class), | ||
844 | 16, | ||
845 | NVOBJ_FLAG_ZERO_ALLOC | NVOBJ_FLAG_ZERO_FREE, | ||
846 | gpuobj); | ||
847 | if (ret) { | ||
848 | NV_ERROR(dev, "Error creating gpuobj: %d\n", ret); | ||
849 | return ret; | ||
850 | } | ||
851 | |||
852 | dev_priv->engine.instmem.prepare_access(dev, true); | ||
853 | if (dev_priv->card_type >= NV_50) { | ||
854 | nv_wo32(dev, *gpuobj, 0, class); | ||
855 | nv_wo32(dev, *gpuobj, 5, 0x00010000); | ||
856 | } else { | ||
857 | switch (class) { | ||
858 | case NV_CLASS_NULL: | ||
859 | nv_wo32(dev, *gpuobj, 0, 0x00001030); | ||
860 | nv_wo32(dev, *gpuobj, 1, 0xFFFFFFFF); | ||
861 | break; | ||
862 | default: | ||
863 | if (dev_priv->card_type >= NV_40) { | ||
864 | nv_wo32(dev, *gpuobj, 0, class); | ||
865 | #ifdef __BIG_ENDIAN | ||
866 | nv_wo32(dev, *gpuobj, 2, 0x01000000); | ||
867 | #endif | ||
868 | } else { | ||
869 | #ifdef __BIG_ENDIAN | ||
870 | nv_wo32(dev, *gpuobj, 0, class | 0x00080000); | ||
871 | #else | ||
872 | nv_wo32(dev, *gpuobj, 0, class); | ||
873 | #endif | ||
874 | } | ||
875 | } | ||
876 | } | ||
877 | dev_priv->engine.instmem.finish_access(dev); | ||
878 | |||
879 | (*gpuobj)->engine = NVOBJ_ENGINE_GR; | ||
880 | (*gpuobj)->class = class; | ||
881 | return 0; | ||
882 | } | ||
883 | |||
884 | static int | ||
885 | nouveau_gpuobj_sw_new(struct nouveau_channel *chan, int class, | ||
886 | struct nouveau_gpuobj **gpuobj_ret) | ||
887 | { | ||
888 | struct drm_nouveau_private *dev_priv = chan->dev->dev_private; | ||
889 | struct nouveau_gpuobj *gpuobj; | ||
890 | |||
891 | if (!chan || !gpuobj_ret || *gpuobj_ret != NULL) | ||
892 | return -EINVAL; | ||
893 | |||
894 | gpuobj = kzalloc(sizeof(*gpuobj), GFP_KERNEL); | ||
895 | if (!gpuobj) | ||
896 | return -ENOMEM; | ||
897 | gpuobj->engine = NVOBJ_ENGINE_SW; | ||
898 | gpuobj->class = class; | ||
899 | |||
900 | list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list); | ||
901 | *gpuobj_ret = gpuobj; | ||
902 | return 0; | ||
903 | } | ||
904 | |||
905 | static int | ||
906 | nouveau_gpuobj_channel_init_pramin(struct nouveau_channel *chan) | ||
907 | { | ||
908 | struct drm_device *dev = chan->dev; | ||
909 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
910 | struct nouveau_gpuobj *pramin = NULL; | ||
911 | uint32_t size; | ||
912 | uint32_t base; | ||
913 | int ret; | ||
914 | |||
915 | NV_DEBUG(dev, "ch%d\n", chan->id); | ||
916 | |||
917 | /* Base amount for object storage (4KiB enough?) */ | ||
918 | size = 0x1000; | ||
919 | base = 0; | ||
920 | |||
921 | /* PGRAPH context */ | ||
922 | |||
923 | if (dev_priv->card_type == NV_50) { | ||
924 | /* Various fixed table thingos */ | ||
925 | size += 0x1400; /* mostly unknown stuff */ | ||
926 | size += 0x4000; /* vm pd */ | ||
927 | base = 0x6000; | ||
928 | /* RAMHT, not sure about setting size yet, 32KiB to be safe */ | ||
929 | size += 0x8000; | ||
930 | /* RAMFC */ | ||
931 | size += 0x1000; | ||
932 | /* PGRAPH context */ | ||
933 | size += 0x70000; | ||
934 | } | ||
935 | |||
936 | NV_DEBUG(dev, "ch%d PRAMIN size: 0x%08x bytes, base alloc=0x%08x\n", | ||
937 | chan->id, size, base); | ||
938 | ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, size, 0x1000, 0, | ||
939 | &chan->ramin); | ||
940 | if (ret) { | ||
941 | NV_ERROR(dev, "Error allocating channel PRAMIN: %d\n", ret); | ||
942 | return ret; | ||
943 | } | ||
944 | pramin = chan->ramin->gpuobj; | ||
945 | |||
946 | ret = nouveau_mem_init_heap(&chan->ramin_heap, | ||
947 | pramin->im_pramin->start + base, size); | ||
948 | if (ret) { | ||
949 | NV_ERROR(dev, "Error creating PRAMIN heap: %d\n", ret); | ||
950 | nouveau_gpuobj_ref_del(dev, &chan->ramin); | ||
951 | return ret; | ||
952 | } | ||
953 | |||
954 | return 0; | ||
955 | } | ||
956 | |||
957 | int | ||
958 | nouveau_gpuobj_channel_init(struct nouveau_channel *chan, | ||
959 | uint32_t vram_h, uint32_t tt_h) | ||
960 | { | ||
961 | struct drm_device *dev = chan->dev; | ||
962 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
963 | struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem; | ||
964 | struct nouveau_gpuobj *vram = NULL, *tt = NULL; | ||
965 | int ret, i; | ||
966 | |||
967 | INIT_LIST_HEAD(&chan->ramht_refs); | ||
968 | |||
969 | NV_DEBUG(dev, "ch%d vram=0x%08x tt=0x%08x\n", chan->id, vram_h, tt_h); | ||
970 | |||
971 | /* Reserve a block of PRAMIN for the channel | ||
972 | *XXX: maybe on <NV50 too at some point | ||
973 | */ | ||
974 | if (0 || dev_priv->card_type == NV_50) { | ||
975 | ret = nouveau_gpuobj_channel_init_pramin(chan); | ||
976 | if (ret) { | ||
977 | NV_ERROR(dev, "init pramin\n"); | ||
978 | return ret; | ||
979 | } | ||
980 | } | ||
981 | |||
982 | /* NV50 VM | ||
983 | * - Allocate per-channel page-directory | ||
984 | * - Map GART and VRAM into the channel's address space at the | ||
985 | * locations determined during init. | ||
986 | */ | ||
987 | if (dev_priv->card_type >= NV_50) { | ||
988 | uint32_t vm_offset, pde; | ||
989 | |||
990 | instmem->prepare_access(dev, true); | ||
991 | |||
992 | vm_offset = (dev_priv->chipset & 0xf0) == 0x50 ? 0x1400 : 0x200; | ||
993 | vm_offset += chan->ramin->gpuobj->im_pramin->start; | ||
994 | |||
995 | ret = nouveau_gpuobj_new_fake(dev, vm_offset, ~0, 0x4000, | ||
996 | 0, &chan->vm_pd, NULL); | ||
997 | if (ret) { | ||
998 | instmem->finish_access(dev); | ||
999 | return ret; | ||
1000 | } | ||
1001 | for (i = 0; i < 0x4000; i += 8) { | ||
1002 | nv_wo32(dev, chan->vm_pd, (i+0)/4, 0x00000000); | ||
1003 | nv_wo32(dev, chan->vm_pd, (i+4)/4, 0xdeadcafe); | ||
1004 | } | ||
1005 | |||
1006 | pde = (dev_priv->vm_gart_base / (512*1024*1024)) * 2; | ||
1007 | ret = nouveau_gpuobj_ref_add(dev, NULL, 0, | ||
1008 | dev_priv->gart_info.sg_ctxdma, | ||
1009 | &chan->vm_gart_pt); | ||
1010 | if (ret) { | ||
1011 | instmem->finish_access(dev); | ||
1012 | return ret; | ||
1013 | } | ||
1014 | nv_wo32(dev, chan->vm_pd, pde++, | ||
1015 | chan->vm_gart_pt->instance | 0x03); | ||
1016 | nv_wo32(dev, chan->vm_pd, pde++, 0x00000000); | ||
1017 | |||
1018 | pde = (dev_priv->vm_vram_base / (512*1024*1024)) * 2; | ||
1019 | for (i = 0; i < dev_priv->vm_vram_pt_nr; i++) { | ||
1020 | ret = nouveau_gpuobj_ref_add(dev, NULL, 0, | ||
1021 | dev_priv->vm_vram_pt[i], | ||
1022 | &chan->vm_vram_pt[i]); | ||
1023 | if (ret) { | ||
1024 | instmem->finish_access(dev); | ||
1025 | return ret; | ||
1026 | } | ||
1027 | |||
1028 | nv_wo32(dev, chan->vm_pd, pde++, | ||
1029 | chan->vm_vram_pt[i]->instance | 0x61); | ||
1030 | nv_wo32(dev, chan->vm_pd, pde++, 0x00000000); | ||
1031 | } | ||
1032 | |||
1033 | instmem->finish_access(dev); | ||
1034 | } | ||
1035 | |||
1036 | /* RAMHT */ | ||
1037 | if (dev_priv->card_type < NV_50) { | ||
1038 | ret = nouveau_gpuobj_ref_add(dev, NULL, 0, dev_priv->ramht, | ||
1039 | &chan->ramht); | ||
1040 | if (ret) | ||
1041 | return ret; | ||
1042 | } else { | ||
1043 | ret = nouveau_gpuobj_new_ref(dev, chan, chan, 0, | ||
1044 | 0x8000, 16, | ||
1045 | NVOBJ_FLAG_ZERO_ALLOC, | ||
1046 | &chan->ramht); | ||
1047 | if (ret) | ||
1048 | return ret; | ||
1049 | } | ||
1050 | |||
1051 | /* VRAM ctxdma */ | ||
1052 | if (dev_priv->card_type >= NV_50) { | ||
1053 | ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, | ||
1054 | 0, dev_priv->vm_end, | ||
1055 | NV_DMA_ACCESS_RW, | ||
1056 | NV_DMA_TARGET_AGP, &vram); | ||
1057 | if (ret) { | ||
1058 | NV_ERROR(dev, "Error creating VRAM ctxdma: %d\n", ret); | ||
1059 | return ret; | ||
1060 | } | ||
1061 | } else { | ||
1062 | ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, | ||
1063 | 0, dev_priv->fb_available_size, | ||
1064 | NV_DMA_ACCESS_RW, | ||
1065 | NV_DMA_TARGET_VIDMEM, &vram); | ||
1066 | if (ret) { | ||
1067 | NV_ERROR(dev, "Error creating VRAM ctxdma: %d\n", ret); | ||
1068 | return ret; | ||
1069 | } | ||
1070 | } | ||
1071 | |||
1072 | ret = nouveau_gpuobj_ref_add(dev, chan, vram_h, vram, NULL); | ||
1073 | if (ret) { | ||
1074 | NV_ERROR(dev, "Error referencing VRAM ctxdma: %d\n", ret); | ||
1075 | return ret; | ||
1076 | } | ||
1077 | |||
1078 | /* TT memory ctxdma */ | ||
1079 | if (dev_priv->card_type >= NV_50) { | ||
1080 | tt = vram; | ||
1081 | } else | ||
1082 | if (dev_priv->gart_info.type != NOUVEAU_GART_NONE) { | ||
1083 | ret = nouveau_gpuobj_gart_dma_new(chan, 0, | ||
1084 | dev_priv->gart_info.aper_size, | ||
1085 | NV_DMA_ACCESS_RW, &tt, NULL); | ||
1086 | } else { | ||
1087 | NV_ERROR(dev, "Invalid GART type %d\n", dev_priv->gart_info.type); | ||
1088 | ret = -EINVAL; | ||
1089 | } | ||
1090 | |||
1091 | if (ret) { | ||
1092 | NV_ERROR(dev, "Error creating TT ctxdma: %d\n", ret); | ||
1093 | return ret; | ||
1094 | } | ||
1095 | |||
1096 | ret = nouveau_gpuobj_ref_add(dev, chan, tt_h, tt, NULL); | ||
1097 | if (ret) { | ||
1098 | NV_ERROR(dev, "Error referencing TT ctxdma: %d\n", ret); | ||
1099 | return ret; | ||
1100 | } | ||
1101 | |||
1102 | return 0; | ||
1103 | } | ||
1104 | |||
1105 | void | ||
1106 | nouveau_gpuobj_channel_takedown(struct nouveau_channel *chan) | ||
1107 | { | ||
1108 | struct drm_nouveau_private *dev_priv = chan->dev->dev_private; | ||
1109 | struct drm_device *dev = chan->dev; | ||
1110 | struct list_head *entry, *tmp; | ||
1111 | struct nouveau_gpuobj_ref *ref; | ||
1112 | int i; | ||
1113 | |||
1114 | NV_DEBUG(dev, "ch%d\n", chan->id); | ||
1115 | |||
1116 | if (!chan->ramht_refs.next) | ||
1117 | return; | ||
1118 | |||
1119 | list_for_each_safe(entry, tmp, &chan->ramht_refs) { | ||
1120 | ref = list_entry(entry, struct nouveau_gpuobj_ref, list); | ||
1121 | |||
1122 | nouveau_gpuobj_ref_del(dev, &ref); | ||
1123 | } | ||
1124 | |||
1125 | nouveau_gpuobj_ref_del(dev, &chan->ramht); | ||
1126 | |||
1127 | nouveau_gpuobj_del(dev, &chan->vm_pd); | ||
1128 | nouveau_gpuobj_ref_del(dev, &chan->vm_gart_pt); | ||
1129 | for (i = 0; i < dev_priv->vm_vram_pt_nr; i++) | ||
1130 | nouveau_gpuobj_ref_del(dev, &chan->vm_vram_pt[i]); | ||
1131 | |||
1132 | if (chan->ramin_heap) | ||
1133 | nouveau_mem_takedown(&chan->ramin_heap); | ||
1134 | if (chan->ramin) | ||
1135 | nouveau_gpuobj_ref_del(dev, &chan->ramin); | ||
1136 | |||
1137 | } | ||
1138 | |||
1139 | int | ||
1140 | nouveau_gpuobj_suspend(struct drm_device *dev) | ||
1141 | { | ||
1142 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
1143 | struct nouveau_gpuobj *gpuobj; | ||
1144 | int i; | ||
1145 | |||
1146 | if (dev_priv->card_type < NV_50) { | ||
1147 | dev_priv->susres.ramin_copy = vmalloc(dev_priv->ramin_rsvd_vram); | ||
1148 | if (!dev_priv->susres.ramin_copy) | ||
1149 | return -ENOMEM; | ||
1150 | |||
1151 | for (i = 0; i < dev_priv->ramin_rsvd_vram; i += 4) | ||
1152 | dev_priv->susres.ramin_copy[i/4] = nv_ri32(dev, i); | ||
1153 | return 0; | ||
1154 | } | ||
1155 | |||
1156 | list_for_each_entry(gpuobj, &dev_priv->gpuobj_list, list) { | ||
1157 | if (!gpuobj->im_backing || (gpuobj->flags & NVOBJ_FLAG_FAKE)) | ||
1158 | continue; | ||
1159 | |||
1160 | gpuobj->im_backing_suspend = vmalloc(gpuobj->im_pramin->size); | ||
1161 | if (!gpuobj->im_backing_suspend) { | ||
1162 | nouveau_gpuobj_resume(dev); | ||
1163 | return -ENOMEM; | ||
1164 | } | ||
1165 | |||
1166 | dev_priv->engine.instmem.prepare_access(dev, false); | ||
1167 | for (i = 0; i < gpuobj->im_pramin->size / 4; i++) | ||
1168 | gpuobj->im_backing_suspend[i] = nv_ro32(dev, gpuobj, i); | ||
1169 | dev_priv->engine.instmem.finish_access(dev); | ||
1170 | } | ||
1171 | |||
1172 | return 0; | ||
1173 | } | ||
1174 | |||
1175 | void | ||
1176 | nouveau_gpuobj_suspend_cleanup(struct drm_device *dev) | ||
1177 | { | ||
1178 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
1179 | struct nouveau_gpuobj *gpuobj; | ||
1180 | |||
1181 | if (dev_priv->card_type < NV_50) { | ||
1182 | vfree(dev_priv->susres.ramin_copy); | ||
1183 | dev_priv->susres.ramin_copy = NULL; | ||
1184 | return; | ||
1185 | } | ||
1186 | |||
1187 | list_for_each_entry(gpuobj, &dev_priv->gpuobj_list, list) { | ||
1188 | if (!gpuobj->im_backing_suspend) | ||
1189 | continue; | ||
1190 | |||
1191 | vfree(gpuobj->im_backing_suspend); | ||
1192 | gpuobj->im_backing_suspend = NULL; | ||
1193 | } | ||
1194 | } | ||
1195 | |||
1196 | void | ||
1197 | nouveau_gpuobj_resume(struct drm_device *dev) | ||
1198 | { | ||
1199 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
1200 | struct nouveau_gpuobj *gpuobj; | ||
1201 | int i; | ||
1202 | |||
1203 | if (dev_priv->card_type < NV_50) { | ||
1204 | for (i = 0; i < dev_priv->ramin_rsvd_vram; i += 4) | ||
1205 | nv_wi32(dev, i, dev_priv->susres.ramin_copy[i/4]); | ||
1206 | nouveau_gpuobj_suspend_cleanup(dev); | ||
1207 | return; | ||
1208 | } | ||
1209 | |||
1210 | list_for_each_entry(gpuobj, &dev_priv->gpuobj_list, list) { | ||
1211 | if (!gpuobj->im_backing_suspend) | ||
1212 | continue; | ||
1213 | |||
1214 | dev_priv->engine.instmem.prepare_access(dev, true); | ||
1215 | for (i = 0; i < gpuobj->im_pramin->size / 4; i++) | ||
1216 | nv_wo32(dev, gpuobj, i, gpuobj->im_backing_suspend[i]); | ||
1217 | dev_priv->engine.instmem.finish_access(dev); | ||
1218 | } | ||
1219 | |||
1220 | nouveau_gpuobj_suspend_cleanup(dev); | ||
1221 | } | ||
1222 | |||
1223 | int nouveau_ioctl_grobj_alloc(struct drm_device *dev, void *data, | ||
1224 | struct drm_file *file_priv) | ||
1225 | { | ||
1226 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
1227 | struct drm_nouveau_grobj_alloc *init = data; | ||
1228 | struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; | ||
1229 | struct nouveau_pgraph_object_class *grc; | ||
1230 | struct nouveau_gpuobj *gr = NULL; | ||
1231 | struct nouveau_channel *chan; | ||
1232 | int ret; | ||
1233 | |||
1234 | NOUVEAU_CHECK_INITIALISED_WITH_RETURN; | ||
1235 | NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(init->channel, file_priv, chan); | ||
1236 | |||
1237 | if (init->handle == ~0) | ||
1238 | return -EINVAL; | ||
1239 | |||
1240 | grc = pgraph->grclass; | ||
1241 | while (grc->id) { | ||
1242 | if (grc->id == init->class) | ||
1243 | break; | ||
1244 | grc++; | ||
1245 | } | ||
1246 | |||
1247 | if (!grc->id) { | ||
1248 | NV_ERROR(dev, "Illegal object class: 0x%x\n", init->class); | ||
1249 | return -EPERM; | ||
1250 | } | ||
1251 | |||
1252 | if (nouveau_gpuobj_ref_find(chan, init->handle, NULL) == 0) | ||
1253 | return -EEXIST; | ||
1254 | |||
1255 | if (!grc->software) | ||
1256 | ret = nouveau_gpuobj_gr_new(chan, grc->id, &gr); | ||
1257 | else | ||
1258 | ret = nouveau_gpuobj_sw_new(chan, grc->id, &gr); | ||
1259 | |||
1260 | if (ret) { | ||
1261 | NV_ERROR(dev, "Error creating object: %d (%d/0x%08x)\n", | ||
1262 | ret, init->channel, init->handle); | ||
1263 | return ret; | ||
1264 | } | ||
1265 | |||
1266 | ret = nouveau_gpuobj_ref_add(dev, chan, init->handle, gr, NULL); | ||
1267 | if (ret) { | ||
1268 | NV_ERROR(dev, "Error referencing object: %d (%d/0x%08x)\n", | ||
1269 | ret, init->channel, init->handle); | ||
1270 | nouveau_gpuobj_del(dev, &gr); | ||
1271 | return ret; | ||
1272 | } | ||
1273 | |||
1274 | return 0; | ||
1275 | } | ||
1276 | |||
1277 | int nouveau_ioctl_gpuobj_free(struct drm_device *dev, void *data, | ||
1278 | struct drm_file *file_priv) | ||
1279 | { | ||
1280 | struct drm_nouveau_gpuobj_free *objfree = data; | ||
1281 | struct nouveau_gpuobj_ref *ref; | ||
1282 | struct nouveau_channel *chan; | ||
1283 | int ret; | ||
1284 | |||
1285 | NOUVEAU_CHECK_INITIALISED_WITH_RETURN; | ||
1286 | NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(objfree->channel, file_priv, chan); | ||
1287 | |||
1288 | ret = nouveau_gpuobj_ref_find(chan, objfree->handle, &ref); | ||
1289 | if (ret) | ||
1290 | return ret; | ||
1291 | nouveau_gpuobj_ref_del(dev, &ref); | ||
1292 | |||
1293 | return 0; | ||
1294 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_reg.h b/drivers/gpu/drm/nouveau/nouveau_reg.h new file mode 100644 index 000000000000..fa1b0e7165b9 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_reg.h | |||
@@ -0,0 +1,836 @@ | |||
1 | |||
2 | |||
3 | #define NV03_BOOT_0 0x00100000 | ||
4 | # define NV03_BOOT_0_RAM_AMOUNT 0x00000003 | ||
5 | # define NV03_BOOT_0_RAM_AMOUNT_8MB 0x00000000 | ||
6 | # define NV03_BOOT_0_RAM_AMOUNT_2MB 0x00000001 | ||
7 | # define NV03_BOOT_0_RAM_AMOUNT_4MB 0x00000002 | ||
8 | # define NV03_BOOT_0_RAM_AMOUNT_8MB_SDRAM 0x00000003 | ||
9 | # define NV04_BOOT_0_RAM_AMOUNT_32MB 0x00000000 | ||
10 | # define NV04_BOOT_0_RAM_AMOUNT_4MB 0x00000001 | ||
11 | # define NV04_BOOT_0_RAM_AMOUNT_8MB 0x00000002 | ||
12 | # define NV04_BOOT_0_RAM_AMOUNT_16MB 0x00000003 | ||
13 | |||
14 | #define NV04_FIFO_DATA 0x0010020c | ||
15 | # define NV10_FIFO_DATA_RAM_AMOUNT_MB_MASK 0xfff00000 | ||
16 | # define NV10_FIFO_DATA_RAM_AMOUNT_MB_SHIFT 20 | ||
17 | |||
18 | #define NV_RAMIN 0x00700000 | ||
19 | |||
20 | #define NV_RAMHT_HANDLE_OFFSET 0 | ||
21 | #define NV_RAMHT_CONTEXT_OFFSET 4 | ||
22 | # define NV_RAMHT_CONTEXT_VALID (1<<31) | ||
23 | # define NV_RAMHT_CONTEXT_CHANNEL_SHIFT 24 | ||
24 | # define NV_RAMHT_CONTEXT_ENGINE_SHIFT 16 | ||
25 | # define NV_RAMHT_CONTEXT_ENGINE_SOFTWARE 0 | ||
26 | # define NV_RAMHT_CONTEXT_ENGINE_GRAPHICS 1 | ||
27 | # define NV_RAMHT_CONTEXT_INSTANCE_SHIFT 0 | ||
28 | # define NV40_RAMHT_CONTEXT_CHANNEL_SHIFT 23 | ||
29 | # define NV40_RAMHT_CONTEXT_ENGINE_SHIFT 20 | ||
30 | # define NV40_RAMHT_CONTEXT_INSTANCE_SHIFT 0 | ||
31 | |||
32 | /* DMA object defines */ | ||
33 | #define NV_DMA_ACCESS_RW 0 | ||
34 | #define NV_DMA_ACCESS_RO 1 | ||
35 | #define NV_DMA_ACCESS_WO 2 | ||
36 | #define NV_DMA_TARGET_VIDMEM 0 | ||
37 | #define NV_DMA_TARGET_PCI 2 | ||
38 | #define NV_DMA_TARGET_AGP 3 | ||
39 | /* The following is not a real value used by the card, it's changed by | ||
40 | * nouveau_object_dma_create */ | ||
41 | #define NV_DMA_TARGET_PCI_NONLINEAR 8 | ||
42 | |||
43 | /* Some object classes we care about in the drm */ | ||
44 | #define NV_CLASS_DMA_FROM_MEMORY 0x00000002 | ||
45 | #define NV_CLASS_DMA_TO_MEMORY 0x00000003 | ||
46 | #define NV_CLASS_NULL 0x00000030 | ||
47 | #define NV_CLASS_DMA_IN_MEMORY 0x0000003D | ||
48 | |||
49 | #define NV03_USER(i) (0x00800000+(i*NV03_USER_SIZE)) | ||
50 | #define NV03_USER__SIZE 16 | ||
51 | #define NV10_USER__SIZE 32 | ||
52 | #define NV03_USER_SIZE 0x00010000 | ||
53 | #define NV03_USER_DMA_PUT(i) (0x00800040+(i*NV03_USER_SIZE)) | ||
54 | #define NV03_USER_DMA_PUT__SIZE 16 | ||
55 | #define NV10_USER_DMA_PUT__SIZE 32 | ||
56 | #define NV03_USER_DMA_GET(i) (0x00800044+(i*NV03_USER_SIZE)) | ||
57 | #define NV03_USER_DMA_GET__SIZE 16 | ||
58 | #define NV10_USER_DMA_GET__SIZE 32 | ||
59 | #define NV03_USER_REF_CNT(i) (0x00800048+(i*NV03_USER_SIZE)) | ||
60 | #define NV03_USER_REF_CNT__SIZE 16 | ||
61 | #define NV10_USER_REF_CNT__SIZE 32 | ||
62 | |||
63 | #define NV40_USER(i) (0x00c00000+(i*NV40_USER_SIZE)) | ||
64 | #define NV40_USER_SIZE 0x00001000 | ||
65 | #define NV40_USER_DMA_PUT(i) (0x00c00040+(i*NV40_USER_SIZE)) | ||
66 | #define NV40_USER_DMA_PUT__SIZE 32 | ||
67 | #define NV40_USER_DMA_GET(i) (0x00c00044+(i*NV40_USER_SIZE)) | ||
68 | #define NV40_USER_DMA_GET__SIZE 32 | ||
69 | #define NV40_USER_REF_CNT(i) (0x00c00048+(i*NV40_USER_SIZE)) | ||
70 | #define NV40_USER_REF_CNT__SIZE 32 | ||
71 | |||
72 | #define NV50_USER(i) (0x00c00000+(i*NV50_USER_SIZE)) | ||
73 | #define NV50_USER_SIZE 0x00002000 | ||
74 | #define NV50_USER_DMA_PUT(i) (0x00c00040+(i*NV50_USER_SIZE)) | ||
75 | #define NV50_USER_DMA_PUT__SIZE 128 | ||
76 | #define NV50_USER_DMA_GET(i) (0x00c00044+(i*NV50_USER_SIZE)) | ||
77 | #define NV50_USER_DMA_GET__SIZE 128 | ||
78 | #define NV50_USER_REF_CNT(i) (0x00c00048+(i*NV50_USER_SIZE)) | ||
79 | #define NV50_USER_REF_CNT__SIZE 128 | ||
80 | |||
81 | #define NV03_FIFO_SIZE 0x8000UL | ||
82 | |||
83 | #define NV03_PMC_BOOT_0 0x00000000 | ||
84 | #define NV03_PMC_BOOT_1 0x00000004 | ||
85 | #define NV03_PMC_INTR_0 0x00000100 | ||
86 | # define NV_PMC_INTR_0_PFIFO_PENDING (1<<8) | ||
87 | # define NV_PMC_INTR_0_PGRAPH_PENDING (1<<12) | ||
88 | # define NV_PMC_INTR_0_NV50_I2C_PENDING (1<<21) | ||
89 | # define NV_PMC_INTR_0_CRTC0_PENDING (1<<24) | ||
90 | # define NV_PMC_INTR_0_CRTC1_PENDING (1<<25) | ||
91 | # define NV_PMC_INTR_0_NV50_DISPLAY_PENDING (1<<26) | ||
92 | # define NV_PMC_INTR_0_CRTCn_PENDING (3<<24) | ||
93 | #define NV03_PMC_INTR_EN_0 0x00000140 | ||
94 | # define NV_PMC_INTR_EN_0_MASTER_ENABLE (1<<0) | ||
95 | #define NV03_PMC_ENABLE 0x00000200 | ||
96 | # define NV_PMC_ENABLE_PFIFO (1<<8) | ||
97 | # define NV_PMC_ENABLE_PGRAPH (1<<12) | ||
98 | /* Disabling the below bit breaks newer (G7X only?) mobile chipsets, | ||
99 | * the card will hang early on in the X init process. | ||
100 | */ | ||
101 | # define NV_PMC_ENABLE_UNK13 (1<<13) | ||
102 | #define NV40_PMC_BACKLIGHT 0x000015f0 | ||
103 | # define NV40_PMC_BACKLIGHT_MASK 0x001f0000 | ||
104 | #define NV40_PMC_1700 0x00001700 | ||
105 | #define NV40_PMC_1704 0x00001704 | ||
106 | #define NV40_PMC_1708 0x00001708 | ||
107 | #define NV40_PMC_170C 0x0000170C | ||
108 | |||
109 | /* probably PMC ? */ | ||
110 | #define NV50_PUNK_BAR0_PRAMIN 0x00001700 | ||
111 | #define NV50_PUNK_BAR_CFG_BASE 0x00001704 | ||
112 | #define NV50_PUNK_BAR_CFG_BASE_VALID (1<<30) | ||
113 | #define NV50_PUNK_BAR1_CTXDMA 0x00001708 | ||
114 | #define NV50_PUNK_BAR1_CTXDMA_VALID (1<<31) | ||
115 | #define NV50_PUNK_BAR3_CTXDMA 0x0000170C | ||
116 | #define NV50_PUNK_BAR3_CTXDMA_VALID (1<<31) | ||
117 | #define NV50_PUNK_UNK1710 0x00001710 | ||
118 | |||
119 | #define NV04_PBUS_PCI_NV_1 0x00001804 | ||
120 | #define NV04_PBUS_PCI_NV_19 0x0000184C | ||
121 | #define NV04_PBUS_PCI_NV_20 0x00001850 | ||
122 | # define NV04_PBUS_PCI_NV_20_ROM_SHADOW_DISABLED (0 << 0) | ||
123 | # define NV04_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED (1 << 0) | ||
124 | |||
125 | #define NV04_PTIMER_INTR_0 0x00009100 | ||
126 | #define NV04_PTIMER_INTR_EN_0 0x00009140 | ||
127 | #define NV04_PTIMER_NUMERATOR 0x00009200 | ||
128 | #define NV04_PTIMER_DENOMINATOR 0x00009210 | ||
129 | #define NV04_PTIMER_TIME_0 0x00009400 | ||
130 | #define NV04_PTIMER_TIME_1 0x00009410 | ||
131 | #define NV04_PTIMER_ALARM_0 0x00009420 | ||
132 | |||
133 | #define NV04_PFB_CFG0 0x00100200 | ||
134 | #define NV04_PFB_CFG1 0x00100204 | ||
135 | #define NV40_PFB_020C 0x0010020C | ||
136 | #define NV10_PFB_TILE(i) (0x00100240 + (i*16)) | ||
137 | #define NV10_PFB_TILE__SIZE 8 | ||
138 | #define NV10_PFB_TLIMIT(i) (0x00100244 + (i*16)) | ||
139 | #define NV10_PFB_TSIZE(i) (0x00100248 + (i*16)) | ||
140 | #define NV10_PFB_TSTATUS(i) (0x0010024C + (i*16)) | ||
141 | #define NV10_PFB_CLOSE_PAGE2 0x0010033C | ||
142 | #define NV40_PFB_TILE(i) (0x00100600 + (i*16)) | ||
143 | #define NV40_PFB_TILE__SIZE_0 12 | ||
144 | #define NV40_PFB_TILE__SIZE_1 15 | ||
145 | #define NV40_PFB_TLIMIT(i) (0x00100604 + (i*16)) | ||
146 | #define NV40_PFB_TSIZE(i) (0x00100608 + (i*16)) | ||
147 | #define NV40_PFB_TSTATUS(i) (0x0010060C + (i*16)) | ||
148 | #define NV40_PFB_UNK_800 0x00100800 | ||
149 | |||
150 | #define NV04_PGRAPH_DEBUG_0 0x00400080 | ||
151 | #define NV04_PGRAPH_DEBUG_1 0x00400084 | ||
152 | #define NV04_PGRAPH_DEBUG_2 0x00400088 | ||
153 | #define NV04_PGRAPH_DEBUG_3 0x0040008c | ||
154 | #define NV10_PGRAPH_DEBUG_4 0x00400090 | ||
155 | #define NV03_PGRAPH_INTR 0x00400100 | ||
156 | #define NV03_PGRAPH_NSTATUS 0x00400104 | ||
157 | # define NV04_PGRAPH_NSTATUS_STATE_IN_USE (1<<11) | ||
158 | # define NV04_PGRAPH_NSTATUS_INVALID_STATE (1<<12) | ||
159 | # define NV04_PGRAPH_NSTATUS_BAD_ARGUMENT (1<<13) | ||
160 | # define NV04_PGRAPH_NSTATUS_PROTECTION_FAULT (1<<14) | ||
161 | # define NV10_PGRAPH_NSTATUS_STATE_IN_USE (1<<23) | ||
162 | # define NV10_PGRAPH_NSTATUS_INVALID_STATE (1<<24) | ||
163 | # define NV10_PGRAPH_NSTATUS_BAD_ARGUMENT (1<<25) | ||
164 | # define NV10_PGRAPH_NSTATUS_PROTECTION_FAULT (1<<26) | ||
165 | #define NV03_PGRAPH_NSOURCE 0x00400108 | ||
166 | # define NV03_PGRAPH_NSOURCE_NOTIFICATION (1<<0) | ||
167 | # define NV03_PGRAPH_NSOURCE_DATA_ERROR (1<<1) | ||
168 | # define NV03_PGRAPH_NSOURCE_PROTECTION_ERROR (1<<2) | ||
169 | # define NV03_PGRAPH_NSOURCE_RANGE_EXCEPTION (1<<3) | ||
170 | # define NV03_PGRAPH_NSOURCE_LIMIT_COLOR (1<<4) | ||
171 | # define NV03_PGRAPH_NSOURCE_LIMIT_ZETA (1<<5) | ||
172 | # define NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD (1<<6) | ||
173 | # define NV03_PGRAPH_NSOURCE_DMA_R_PROTECTION (1<<7) | ||
174 | # define NV03_PGRAPH_NSOURCE_DMA_W_PROTECTION (1<<8) | ||
175 | # define NV03_PGRAPH_NSOURCE_FORMAT_EXCEPTION (1<<9) | ||
176 | # define NV03_PGRAPH_NSOURCE_PATCH_EXCEPTION (1<<10) | ||
177 | # define NV03_PGRAPH_NSOURCE_STATE_INVALID (1<<11) | ||
178 | # define NV03_PGRAPH_NSOURCE_DOUBLE_NOTIFY (1<<12) | ||
179 | # define NV03_PGRAPH_NSOURCE_NOTIFY_IN_USE (1<<13) | ||
180 | # define NV03_PGRAPH_NSOURCE_METHOD_CNT (1<<14) | ||
181 | # define NV03_PGRAPH_NSOURCE_BFR_NOTIFICATION (1<<15) | ||
182 | # define NV03_PGRAPH_NSOURCE_DMA_VTX_PROTECTION (1<<16) | ||
183 | # define NV03_PGRAPH_NSOURCE_DMA_WIDTH_A (1<<17) | ||
184 | # define NV03_PGRAPH_NSOURCE_DMA_WIDTH_B (1<<18) | ||
185 | #define NV03_PGRAPH_INTR_EN 0x00400140 | ||
186 | #define NV40_PGRAPH_INTR_EN 0x0040013C | ||
187 | # define NV_PGRAPH_INTR_NOTIFY (1<<0) | ||
188 | # define NV_PGRAPH_INTR_MISSING_HW (1<<4) | ||
189 | # define NV_PGRAPH_INTR_CONTEXT_SWITCH (1<<12) | ||
190 | # define NV_PGRAPH_INTR_BUFFER_NOTIFY (1<<16) | ||
191 | # define NV_PGRAPH_INTR_ERROR (1<<20) | ||
192 | #define NV10_PGRAPH_CTX_CONTROL 0x00400144 | ||
193 | #define NV10_PGRAPH_CTX_USER 0x00400148 | ||
194 | #define NV10_PGRAPH_CTX_SWITCH1 0x0040014C | ||
195 | #define NV10_PGRAPH_CTX_SWITCH2 0x00400150 | ||
196 | #define NV10_PGRAPH_CTX_SWITCH3 0x00400154 | ||
197 | #define NV10_PGRAPH_CTX_SWITCH4 0x00400158 | ||
198 | #define NV10_PGRAPH_CTX_SWITCH5 0x0040015C | ||
199 | #define NV04_PGRAPH_CTX_SWITCH1 0x00400160 | ||
200 | #define NV10_PGRAPH_CTX_CACHE1 0x00400160 | ||
201 | #define NV04_PGRAPH_CTX_SWITCH2 0x00400164 | ||
202 | #define NV04_PGRAPH_CTX_SWITCH3 0x00400168 | ||
203 | #define NV04_PGRAPH_CTX_SWITCH4 0x0040016C | ||
204 | #define NV04_PGRAPH_CTX_CONTROL 0x00400170 | ||
205 | #define NV04_PGRAPH_CTX_USER 0x00400174 | ||
206 | #define NV04_PGRAPH_CTX_CACHE1 0x00400180 | ||
207 | #define NV10_PGRAPH_CTX_CACHE2 0x00400180 | ||
208 | #define NV03_PGRAPH_CTX_CONTROL 0x00400190 | ||
209 | #define NV03_PGRAPH_CTX_USER 0x00400194 | ||
210 | #define NV04_PGRAPH_CTX_CACHE2 0x004001A0 | ||
211 | #define NV10_PGRAPH_CTX_CACHE3 0x004001A0 | ||
212 | #define NV04_PGRAPH_CTX_CACHE3 0x004001C0 | ||
213 | #define NV10_PGRAPH_CTX_CACHE4 0x004001C0 | ||
214 | #define NV04_PGRAPH_CTX_CACHE4 0x004001E0 | ||
215 | #define NV10_PGRAPH_CTX_CACHE5 0x004001E0 | ||
216 | #define NV40_PGRAPH_CTXCTL_0304 0x00400304 | ||
217 | #define NV40_PGRAPH_CTXCTL_0304_XFER_CTX 0x00000001 | ||
218 | #define NV40_PGRAPH_CTXCTL_UCODE_STAT 0x00400308 | ||
219 | #define NV40_PGRAPH_CTXCTL_UCODE_STAT_IP_MASK 0xff000000 | ||
220 | #define NV40_PGRAPH_CTXCTL_UCODE_STAT_IP_SHIFT 24 | ||
221 | #define NV40_PGRAPH_CTXCTL_UCODE_STAT_OP_MASK 0x00ffffff | ||
222 | #define NV40_PGRAPH_CTXCTL_0310 0x00400310 | ||
223 | #define NV40_PGRAPH_CTXCTL_0310_XFER_SAVE 0x00000020 | ||
224 | #define NV40_PGRAPH_CTXCTL_0310_XFER_LOAD 0x00000040 | ||
225 | #define NV40_PGRAPH_CTXCTL_030C 0x0040030c | ||
226 | #define NV40_PGRAPH_CTXCTL_UCODE_INDEX 0x00400324 | ||
227 | #define NV40_PGRAPH_CTXCTL_UCODE_DATA 0x00400328 | ||
228 | #define NV40_PGRAPH_CTXCTL_CUR 0x0040032c | ||
229 | #define NV40_PGRAPH_CTXCTL_CUR_LOADED 0x01000000 | ||
230 | #define NV40_PGRAPH_CTXCTL_CUR_INSTANCE 0x000FFFFF | ||
231 | #define NV40_PGRAPH_CTXCTL_NEXT 0x00400330 | ||
232 | #define NV40_PGRAPH_CTXCTL_NEXT_INSTANCE 0x000fffff | ||
233 | #define NV50_PGRAPH_CTXCTL_CUR 0x0040032c | ||
234 | #define NV50_PGRAPH_CTXCTL_CUR_LOADED 0x80000000 | ||
235 | #define NV50_PGRAPH_CTXCTL_CUR_INSTANCE 0x00ffffff | ||
236 | #define NV50_PGRAPH_CTXCTL_NEXT 0x00400330 | ||
237 | #define NV50_PGRAPH_CTXCTL_NEXT_INSTANCE 0x00ffffff | ||
238 | #define NV03_PGRAPH_ABS_X_RAM 0x00400400 | ||
239 | #define NV03_PGRAPH_ABS_Y_RAM 0x00400480 | ||
240 | #define NV03_PGRAPH_X_MISC 0x00400500 | ||
241 | #define NV03_PGRAPH_Y_MISC 0x00400504 | ||
242 | #define NV04_PGRAPH_VALID1 0x00400508 | ||
243 | #define NV04_PGRAPH_SOURCE_COLOR 0x0040050C | ||
244 | #define NV04_PGRAPH_MISC24_0 0x00400510 | ||
245 | #define NV03_PGRAPH_XY_LOGIC_MISC0 0x00400514 | ||
246 | #define NV03_PGRAPH_XY_LOGIC_MISC1 0x00400518 | ||
247 | #define NV03_PGRAPH_XY_LOGIC_MISC2 0x0040051C | ||
248 | #define NV03_PGRAPH_XY_LOGIC_MISC3 0x00400520 | ||
249 | #define NV03_PGRAPH_CLIPX_0 0x00400524 | ||
250 | #define NV03_PGRAPH_CLIPX_1 0x00400528 | ||
251 | #define NV03_PGRAPH_CLIPY_0 0x0040052C | ||
252 | #define NV03_PGRAPH_CLIPY_1 0x00400530 | ||
253 | #define NV03_PGRAPH_ABS_ICLIP_XMAX 0x00400534 | ||
254 | #define NV03_PGRAPH_ABS_ICLIP_YMAX 0x00400538 | ||
255 | #define NV03_PGRAPH_ABS_UCLIP_XMIN 0x0040053C | ||
256 | #define NV03_PGRAPH_ABS_UCLIP_YMIN 0x00400540 | ||
257 | #define NV03_PGRAPH_ABS_UCLIP_XMAX 0x00400544 | ||
258 | #define NV03_PGRAPH_ABS_UCLIP_YMAX 0x00400548 | ||
259 | #define NV03_PGRAPH_ABS_UCLIPA_XMIN 0x00400560 | ||
260 | #define NV03_PGRAPH_ABS_UCLIPA_YMIN 0x00400564 | ||
261 | #define NV03_PGRAPH_ABS_UCLIPA_XMAX 0x00400568 | ||
262 | #define NV03_PGRAPH_ABS_UCLIPA_YMAX 0x0040056C | ||
263 | #define NV04_PGRAPH_MISC24_1 0x00400570 | ||
264 | #define NV04_PGRAPH_MISC24_2 0x00400574 | ||
265 | #define NV04_PGRAPH_VALID2 0x00400578 | ||
266 | #define NV04_PGRAPH_PASSTHRU_0 0x0040057C | ||
267 | #define NV04_PGRAPH_PASSTHRU_1 0x00400580 | ||
268 | #define NV04_PGRAPH_PASSTHRU_2 0x00400584 | ||
269 | #define NV10_PGRAPH_DIMX_TEXTURE 0x00400588 | ||
270 | #define NV10_PGRAPH_WDIMX_TEXTURE 0x0040058C | ||
271 | #define NV04_PGRAPH_COMBINE_0_ALPHA 0x00400590 | ||
272 | #define NV04_PGRAPH_COMBINE_0_COLOR 0x00400594 | ||
273 | #define NV04_PGRAPH_COMBINE_1_ALPHA 0x00400598 | ||
274 | #define NV04_PGRAPH_COMBINE_1_COLOR 0x0040059C | ||
275 | #define NV04_PGRAPH_FORMAT_0 0x004005A8 | ||
276 | #define NV04_PGRAPH_FORMAT_1 0x004005AC | ||
277 | #define NV04_PGRAPH_FILTER_0 0x004005B0 | ||
278 | #define NV04_PGRAPH_FILTER_1 0x004005B4 | ||
279 | #define NV03_PGRAPH_MONO_COLOR0 0x00400600 | ||
280 | #define NV04_PGRAPH_ROP3 0x00400604 | ||
281 | #define NV04_PGRAPH_BETA_AND 0x00400608 | ||
282 | #define NV04_PGRAPH_BETA_PREMULT 0x0040060C | ||
283 | #define NV04_PGRAPH_LIMIT_VIOL_PIX 0x00400610 | ||
284 | #define NV04_PGRAPH_FORMATS 0x00400618 | ||
285 | #define NV10_PGRAPH_DEBUG_2 0x00400620 | ||
286 | #define NV04_PGRAPH_BOFFSET0 0x00400640 | ||
287 | #define NV04_PGRAPH_BOFFSET1 0x00400644 | ||
288 | #define NV04_PGRAPH_BOFFSET2 0x00400648 | ||
289 | #define NV04_PGRAPH_BOFFSET3 0x0040064C | ||
290 | #define NV04_PGRAPH_BOFFSET4 0x00400650 | ||
291 | #define NV04_PGRAPH_BOFFSET5 0x00400654 | ||
292 | #define NV04_PGRAPH_BBASE0 0x00400658 | ||
293 | #define NV04_PGRAPH_BBASE1 0x0040065C | ||
294 | #define NV04_PGRAPH_BBASE2 0x00400660 | ||
295 | #define NV04_PGRAPH_BBASE3 0x00400664 | ||
296 | #define NV04_PGRAPH_BBASE4 0x00400668 | ||
297 | #define NV04_PGRAPH_BBASE5 0x0040066C | ||
298 | #define NV04_PGRAPH_BPITCH0 0x00400670 | ||
299 | #define NV04_PGRAPH_BPITCH1 0x00400674 | ||
300 | #define NV04_PGRAPH_BPITCH2 0x00400678 | ||
301 | #define NV04_PGRAPH_BPITCH3 0x0040067C | ||
302 | #define NV04_PGRAPH_BPITCH4 0x00400680 | ||
303 | #define NV04_PGRAPH_BLIMIT0 0x00400684 | ||
304 | #define NV04_PGRAPH_BLIMIT1 0x00400688 | ||
305 | #define NV04_PGRAPH_BLIMIT2 0x0040068C | ||
306 | #define NV04_PGRAPH_BLIMIT3 0x00400690 | ||
307 | #define NV04_PGRAPH_BLIMIT4 0x00400694 | ||
308 | #define NV04_PGRAPH_BLIMIT5 0x00400698 | ||
309 | #define NV04_PGRAPH_BSWIZZLE2 0x0040069C | ||
310 | #define NV04_PGRAPH_BSWIZZLE5 0x004006A0 | ||
311 | #define NV03_PGRAPH_STATUS 0x004006B0 | ||
312 | #define NV04_PGRAPH_STATUS 0x00400700 | ||
313 | #define NV04_PGRAPH_TRAPPED_ADDR 0x00400704 | ||
314 | #define NV04_PGRAPH_TRAPPED_DATA 0x00400708 | ||
315 | #define NV04_PGRAPH_SURFACE 0x0040070C | ||
316 | #define NV10_PGRAPH_TRAPPED_DATA_HIGH 0x0040070C | ||
317 | #define NV04_PGRAPH_STATE 0x00400710 | ||
318 | #define NV10_PGRAPH_SURFACE 0x00400710 | ||
319 | #define NV04_PGRAPH_NOTIFY 0x00400714 | ||
320 | #define NV10_PGRAPH_STATE 0x00400714 | ||
321 | #define NV10_PGRAPH_NOTIFY 0x00400718 | ||
322 | |||
323 | #define NV04_PGRAPH_FIFO 0x00400720 | ||
324 | |||
325 | #define NV04_PGRAPH_BPIXEL 0x00400724 | ||
326 | #define NV10_PGRAPH_RDI_INDEX 0x00400750 | ||
327 | #define NV04_PGRAPH_FFINTFC_ST2 0x00400754 | ||
328 | #define NV10_PGRAPH_RDI_DATA 0x00400754 | ||
329 | #define NV04_PGRAPH_DMA_PITCH 0x00400760 | ||
330 | #define NV10_PGRAPH_FFINTFC_ST2 0x00400764 | ||
331 | #define NV04_PGRAPH_DVD_COLORFMT 0x00400764 | ||
332 | #define NV04_PGRAPH_SCALED_FORMAT 0x00400768 | ||
333 | #define NV10_PGRAPH_DMA_PITCH 0x00400770 | ||
334 | #define NV10_PGRAPH_DVD_COLORFMT 0x00400774 | ||
335 | #define NV10_PGRAPH_SCALED_FORMAT 0x00400778 | ||
336 | #define NV20_PGRAPH_CHANNEL_CTX_TABLE 0x00400780 | ||
337 | #define NV20_PGRAPH_CHANNEL_CTX_POINTER 0x00400784 | ||
338 | #define NV20_PGRAPH_CHANNEL_CTX_XFER 0x00400788 | ||
339 | #define NV20_PGRAPH_CHANNEL_CTX_XFER_LOAD 0x00000001 | ||
340 | #define NV20_PGRAPH_CHANNEL_CTX_XFER_SAVE 0x00000002 | ||
341 | #define NV04_PGRAPH_PATT_COLOR0 0x00400800 | ||
342 | #define NV04_PGRAPH_PATT_COLOR1 0x00400804 | ||
343 | #define NV04_PGRAPH_PATTERN 0x00400808 | ||
344 | #define NV04_PGRAPH_PATTERN_SHAPE 0x00400810 | ||
345 | #define NV04_PGRAPH_CHROMA 0x00400814 | ||
346 | #define NV04_PGRAPH_CONTROL0 0x00400818 | ||
347 | #define NV04_PGRAPH_CONTROL1 0x0040081C | ||
348 | #define NV04_PGRAPH_CONTROL2 0x00400820 | ||
349 | #define NV04_PGRAPH_BLEND 0x00400824 | ||
350 | #define NV04_PGRAPH_STORED_FMT 0x00400830 | ||
351 | #define NV04_PGRAPH_PATT_COLORRAM 0x00400900 | ||
352 | #define NV40_PGRAPH_TILE0(i) (0x00400900 + (i*16)) | ||
353 | #define NV40_PGRAPH_TLIMIT0(i) (0x00400904 + (i*16)) | ||
354 | #define NV40_PGRAPH_TSIZE0(i) (0x00400908 + (i*16)) | ||
355 | #define NV40_PGRAPH_TSTATUS0(i) (0x0040090C + (i*16)) | ||
356 | #define NV10_PGRAPH_TILE(i) (0x00400B00 + (i*16)) | ||
357 | #define NV10_PGRAPH_TLIMIT(i) (0x00400B04 + (i*16)) | ||
358 | #define NV10_PGRAPH_TSIZE(i) (0x00400B08 + (i*16)) | ||
359 | #define NV10_PGRAPH_TSTATUS(i) (0x00400B0C + (i*16)) | ||
360 | #define NV04_PGRAPH_U_RAM 0x00400D00 | ||
361 | #define NV47_PGRAPH_TILE0(i) (0x00400D00 + (i*16)) | ||
362 | #define NV47_PGRAPH_TLIMIT0(i) (0x00400D04 + (i*16)) | ||
363 | #define NV47_PGRAPH_TSIZE0(i) (0x00400D08 + (i*16)) | ||
364 | #define NV47_PGRAPH_TSTATUS0(i) (0x00400D0C + (i*16)) | ||
365 | #define NV04_PGRAPH_V_RAM 0x00400D40 | ||
366 | #define NV04_PGRAPH_W_RAM 0x00400D80 | ||
367 | #define NV10_PGRAPH_COMBINER0_IN_ALPHA 0x00400E40 | ||
368 | #define NV10_PGRAPH_COMBINER1_IN_ALPHA 0x00400E44 | ||
369 | #define NV10_PGRAPH_COMBINER0_IN_RGB 0x00400E48 | ||
370 | #define NV10_PGRAPH_COMBINER1_IN_RGB 0x00400E4C | ||
371 | #define NV10_PGRAPH_COMBINER_COLOR0 0x00400E50 | ||
372 | #define NV10_PGRAPH_COMBINER_COLOR1 0x00400E54 | ||
373 | #define NV10_PGRAPH_COMBINER0_OUT_ALPHA 0x00400E58 | ||
374 | #define NV10_PGRAPH_COMBINER1_OUT_ALPHA 0x00400E5C | ||
375 | #define NV10_PGRAPH_COMBINER0_OUT_RGB 0x00400E60 | ||
376 | #define NV10_PGRAPH_COMBINER1_OUT_RGB 0x00400E64 | ||
377 | #define NV10_PGRAPH_COMBINER_FINAL0 0x00400E68 | ||
378 | #define NV10_PGRAPH_COMBINER_FINAL1 0x00400E6C | ||
379 | #define NV10_PGRAPH_WINDOWCLIP_HORIZONTAL 0x00400F00 | ||
380 | #define NV10_PGRAPH_WINDOWCLIP_VERTICAL 0x00400F20 | ||
381 | #define NV10_PGRAPH_XFMODE0 0x00400F40 | ||
382 | #define NV10_PGRAPH_XFMODE1 0x00400F44 | ||
383 | #define NV10_PGRAPH_GLOBALSTATE0 0x00400F48 | ||
384 | #define NV10_PGRAPH_GLOBALSTATE1 0x00400F4C | ||
385 | #define NV10_PGRAPH_PIPE_ADDRESS 0x00400F50 | ||
386 | #define NV10_PGRAPH_PIPE_DATA 0x00400F54 | ||
387 | #define NV04_PGRAPH_DMA_START_0 0x00401000 | ||
388 | #define NV04_PGRAPH_DMA_START_1 0x00401004 | ||
389 | #define NV04_PGRAPH_DMA_LENGTH 0x00401008 | ||
390 | #define NV04_PGRAPH_DMA_MISC 0x0040100C | ||
391 | #define NV04_PGRAPH_DMA_DATA_0 0x00401020 | ||
392 | #define NV04_PGRAPH_DMA_DATA_1 0x00401024 | ||
393 | #define NV04_PGRAPH_DMA_RM 0x00401030 | ||
394 | #define NV04_PGRAPH_DMA_A_XLATE_INST 0x00401040 | ||
395 | #define NV04_PGRAPH_DMA_A_CONTROL 0x00401044 | ||
396 | #define NV04_PGRAPH_DMA_A_LIMIT 0x00401048 | ||
397 | #define NV04_PGRAPH_DMA_A_TLB_PTE 0x0040104C | ||
398 | #define NV04_PGRAPH_DMA_A_TLB_TAG 0x00401050 | ||
399 | #define NV04_PGRAPH_DMA_A_ADJ_OFFSET 0x00401054 | ||
400 | #define NV04_PGRAPH_DMA_A_OFFSET 0x00401058 | ||
401 | #define NV04_PGRAPH_DMA_A_SIZE 0x0040105C | ||
402 | #define NV04_PGRAPH_DMA_A_Y_SIZE 0x00401060 | ||
403 | #define NV04_PGRAPH_DMA_B_XLATE_INST 0x00401080 | ||
404 | #define NV04_PGRAPH_DMA_B_CONTROL 0x00401084 | ||
405 | #define NV04_PGRAPH_DMA_B_LIMIT 0x00401088 | ||
406 | #define NV04_PGRAPH_DMA_B_TLB_PTE 0x0040108C | ||
407 | #define NV04_PGRAPH_DMA_B_TLB_TAG 0x00401090 | ||
408 | #define NV04_PGRAPH_DMA_B_ADJ_OFFSET 0x00401094 | ||
409 | #define NV04_PGRAPH_DMA_B_OFFSET 0x00401098 | ||
410 | #define NV04_PGRAPH_DMA_B_SIZE 0x0040109C | ||
411 | #define NV04_PGRAPH_DMA_B_Y_SIZE 0x004010A0 | ||
412 | #define NV40_PGRAPH_TILE1(i) (0x00406900 + (i*16)) | ||
413 | #define NV40_PGRAPH_TLIMIT1(i) (0x00406904 + (i*16)) | ||
414 | #define NV40_PGRAPH_TSIZE1(i) (0x00406908 + (i*16)) | ||
415 | #define NV40_PGRAPH_TSTATUS1(i) (0x0040690C + (i*16)) | ||
416 | |||
417 | |||
418 | /* It's a guess that this works on NV03. Confirmed on NV04, though */ | ||
419 | #define NV04_PFIFO_DELAY_0 0x00002040 | ||
420 | #define NV04_PFIFO_DMA_TIMESLICE 0x00002044 | ||
421 | #define NV04_PFIFO_NEXT_CHANNEL 0x00002050 | ||
422 | #define NV03_PFIFO_INTR_0 0x00002100 | ||
423 | #define NV03_PFIFO_INTR_EN_0 0x00002140 | ||
424 | # define NV_PFIFO_INTR_CACHE_ERROR (1<<0) | ||
425 | # define NV_PFIFO_INTR_RUNOUT (1<<4) | ||
426 | # define NV_PFIFO_INTR_RUNOUT_OVERFLOW (1<<8) | ||
427 | # define NV_PFIFO_INTR_DMA_PUSHER (1<<12) | ||
428 | # define NV_PFIFO_INTR_DMA_PT (1<<16) | ||
429 | # define NV_PFIFO_INTR_SEMAPHORE (1<<20) | ||
430 | # define NV_PFIFO_INTR_ACQUIRE_TIMEOUT (1<<24) | ||
431 | #define NV03_PFIFO_RAMHT 0x00002210 | ||
432 | #define NV03_PFIFO_RAMFC 0x00002214 | ||
433 | #define NV03_PFIFO_RAMRO 0x00002218 | ||
434 | #define NV40_PFIFO_RAMFC 0x00002220 | ||
435 | #define NV03_PFIFO_CACHES 0x00002500 | ||
436 | #define NV04_PFIFO_MODE 0x00002504 | ||
437 | #define NV04_PFIFO_DMA 0x00002508 | ||
438 | #define NV04_PFIFO_SIZE 0x0000250c | ||
439 | #define NV50_PFIFO_CTX_TABLE(c) (0x2600+(c)*4) | ||
440 | #define NV50_PFIFO_CTX_TABLE__SIZE 128 | ||
441 | #define NV50_PFIFO_CTX_TABLE_CHANNEL_ENABLED (1<<31) | ||
442 | #define NV50_PFIFO_CTX_TABLE_UNK30_BAD (1<<30) | ||
443 | #define NV50_PFIFO_CTX_TABLE_INSTANCE_MASK_G80 0x0FFFFFFF | ||
444 | #define NV50_PFIFO_CTX_TABLE_INSTANCE_MASK_G84 0x00FFFFFF | ||
445 | #define NV03_PFIFO_CACHE0_PUSH0 0x00003000 | ||
446 | #define NV03_PFIFO_CACHE0_PULL0 0x00003040 | ||
447 | #define NV04_PFIFO_CACHE0_PULL0 0x00003050 | ||
448 | #define NV04_PFIFO_CACHE0_PULL1 0x00003054 | ||
449 | #define NV03_PFIFO_CACHE1_PUSH0 0x00003200 | ||
450 | #define NV03_PFIFO_CACHE1_PUSH1 0x00003204 | ||
451 | #define NV03_PFIFO_CACHE1_PUSH1_DMA (1<<8) | ||
452 | #define NV40_PFIFO_CACHE1_PUSH1_DMA (1<<16) | ||
453 | #define NV03_PFIFO_CACHE1_PUSH1_CHID_MASK 0x0000000f | ||
454 | #define NV10_PFIFO_CACHE1_PUSH1_CHID_MASK 0x0000001f | ||
455 | #define NV50_PFIFO_CACHE1_PUSH1_CHID_MASK 0x0000007f | ||
456 | #define NV03_PFIFO_CACHE1_PUT 0x00003210 | ||
457 | #define NV04_PFIFO_CACHE1_DMA_PUSH 0x00003220 | ||
458 | #define NV04_PFIFO_CACHE1_DMA_FETCH 0x00003224 | ||
459 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_8_BYTES 0x00000000 | ||
460 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_16_BYTES 0x00000008 | ||
461 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_24_BYTES 0x00000010 | ||
462 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_32_BYTES 0x00000018 | ||
463 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_40_BYTES 0x00000020 | ||
464 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_48_BYTES 0x00000028 | ||
465 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_56_BYTES 0x00000030 | ||
466 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_64_BYTES 0x00000038 | ||
467 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_72_BYTES 0x00000040 | ||
468 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_80_BYTES 0x00000048 | ||
469 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_88_BYTES 0x00000050 | ||
470 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_96_BYTES 0x00000058 | ||
471 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_104_BYTES 0x00000060 | ||
472 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_112_BYTES 0x00000068 | ||
473 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_120_BYTES 0x00000070 | ||
474 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES 0x00000078 | ||
475 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_136_BYTES 0x00000080 | ||
476 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_144_BYTES 0x00000088 | ||
477 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_152_BYTES 0x00000090 | ||
478 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_160_BYTES 0x00000098 | ||
479 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_168_BYTES 0x000000A0 | ||
480 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_176_BYTES 0x000000A8 | ||
481 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_184_BYTES 0x000000B0 | ||
482 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_192_BYTES 0x000000B8 | ||
483 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_200_BYTES 0x000000C0 | ||
484 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_208_BYTES 0x000000C8 | ||
485 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_216_BYTES 0x000000D0 | ||
486 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_224_BYTES 0x000000D8 | ||
487 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_232_BYTES 0x000000E0 | ||
488 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_240_BYTES 0x000000E8 | ||
489 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_248_BYTES 0x000000F0 | ||
490 | # define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_256_BYTES 0x000000F8 | ||
491 | # define NV_PFIFO_CACHE1_DMA_FETCH_SIZE 0x0000E000 | ||
492 | # define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_32_BYTES 0x00000000 | ||
493 | # define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_64_BYTES 0x00002000 | ||
494 | # define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_96_BYTES 0x00004000 | ||
495 | # define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES 0x00006000 | ||
496 | # define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_160_BYTES 0x00008000 | ||
497 | # define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_192_BYTES 0x0000A000 | ||
498 | # define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_224_BYTES 0x0000C000 | ||
499 | # define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_256_BYTES 0x0000E000 | ||
500 | # define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS 0x001F0000 | ||
501 | # define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_0 0x00000000 | ||
502 | # define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_1 0x00010000 | ||
503 | # define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_2 0x00020000 | ||
504 | # define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_3 0x00030000 | ||
505 | # define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_4 0x00040000 | ||
506 | # define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_5 0x00050000 | ||
507 | # define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_6 0x00060000 | ||
508 | # define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_7 0x00070000 | ||
509 | # define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8 0x00080000 | ||
510 | # define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_9 0x00090000 | ||
511 | # define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_10 0x000A0000 | ||
512 | # define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_11 0x000B0000 | ||
513 | # define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_12 0x000C0000 | ||
514 | # define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_13 0x000D0000 | ||
515 | # define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_14 0x000E0000 | ||
516 | # define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_15 0x000F0000 | ||
517 | # define NV_PFIFO_CACHE1_ENDIAN 0x80000000 | ||
518 | # define NV_PFIFO_CACHE1_LITTLE_ENDIAN 0x7FFFFFFF | ||
519 | # define NV_PFIFO_CACHE1_BIG_ENDIAN 0x80000000 | ||
520 | #define NV04_PFIFO_CACHE1_DMA_STATE 0x00003228 | ||
521 | #define NV04_PFIFO_CACHE1_DMA_INSTANCE 0x0000322c | ||
522 | #define NV04_PFIFO_CACHE1_DMA_CTL 0x00003230 | ||
523 | #define NV04_PFIFO_CACHE1_DMA_PUT 0x00003240 | ||
524 | #define NV04_PFIFO_CACHE1_DMA_GET 0x00003244 | ||
525 | #define NV10_PFIFO_CACHE1_REF_CNT 0x00003248 | ||
526 | #define NV10_PFIFO_CACHE1_DMA_SUBROUTINE 0x0000324C | ||
527 | #define NV03_PFIFO_CACHE1_PULL0 0x00003240 | ||
528 | #define NV04_PFIFO_CACHE1_PULL0 0x00003250 | ||
529 | #define NV03_PFIFO_CACHE1_PULL1 0x00003250 | ||
530 | #define NV04_PFIFO_CACHE1_PULL1 0x00003254 | ||
531 | #define NV04_PFIFO_CACHE1_HASH 0x00003258 | ||
532 | #define NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT 0x00003260 | ||
533 | #define NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP 0x00003264 | ||
534 | #define NV10_PFIFO_CACHE1_ACQUIRE_VALUE 0x00003268 | ||
535 | #define NV10_PFIFO_CACHE1_SEMAPHORE 0x0000326C | ||
536 | #define NV03_PFIFO_CACHE1_GET 0x00003270 | ||
537 | #define NV04_PFIFO_CACHE1_ENGINE 0x00003280 | ||
538 | #define NV04_PFIFO_CACHE1_DMA_DCOUNT 0x000032A0 | ||
539 | #define NV40_PFIFO_GRCTX_INSTANCE 0x000032E0 | ||
540 | #define NV40_PFIFO_UNK32E4 0x000032E4 | ||
541 | #define NV04_PFIFO_CACHE1_METHOD(i) (0x00003800+(i*8)) | ||
542 | #define NV04_PFIFO_CACHE1_DATA(i) (0x00003804+(i*8)) | ||
543 | #define NV40_PFIFO_CACHE1_METHOD(i) (0x00090000+(i*8)) | ||
544 | #define NV40_PFIFO_CACHE1_DATA(i) (0x00090004+(i*8)) | ||
545 | |||
546 | #define NV_CRTC0_INTSTAT 0x00600100 | ||
547 | #define NV_CRTC0_INTEN 0x00600140 | ||
548 | #define NV_CRTC1_INTSTAT 0x00602100 | ||
549 | #define NV_CRTC1_INTEN 0x00602140 | ||
550 | # define NV_CRTC_INTR_VBLANK (1<<0) | ||
551 | |||
552 | #define NV04_PRAMIN 0x00700000 | ||
553 | |||
554 | /* Fifo commands. These are not regs, neither masks */ | ||
555 | #define NV03_FIFO_CMD_JUMP 0x20000000 | ||
556 | #define NV03_FIFO_CMD_JUMP_OFFSET_MASK 0x1ffffffc | ||
557 | #define NV03_FIFO_CMD_REWIND (NV03_FIFO_CMD_JUMP | (0 & NV03_FIFO_CMD_JUMP_OFFSET_MASK)) | ||
558 | |||
559 | /* This is a partial import from rules-ng, a few things may be duplicated. | ||
560 | * Eventually we should completely import everything from rules-ng. | ||
561 | * For the moment check rules-ng for docs. | ||
562 | */ | ||
563 | |||
564 | #define NV50_PMC 0x00000000 | ||
565 | #define NV50_PMC__LEN 0x1 | ||
566 | #define NV50_PMC__ESIZE 0x2000 | ||
567 | # define NV50_PMC_BOOT_0 0x00000000 | ||
568 | # define NV50_PMC_BOOT_0_REVISION 0x000000ff | ||
569 | # define NV50_PMC_BOOT_0_REVISION__SHIFT 0 | ||
570 | # define NV50_PMC_BOOT_0_ARCH 0x0ff00000 | ||
571 | # define NV50_PMC_BOOT_0_ARCH__SHIFT 20 | ||
572 | # define NV50_PMC_INTR_0 0x00000100 | ||
573 | # define NV50_PMC_INTR_0_PFIFO (1<<8) | ||
574 | # define NV50_PMC_INTR_0_PGRAPH (1<<12) | ||
575 | # define NV50_PMC_INTR_0_PTIMER (1<<20) | ||
576 | # define NV50_PMC_INTR_0_HOTPLUG (1<<21) | ||
577 | # define NV50_PMC_INTR_0_DISPLAY (1<<26) | ||
578 | # define NV50_PMC_INTR_EN_0 0x00000140 | ||
579 | # define NV50_PMC_INTR_EN_0_MASTER (1<<0) | ||
580 | # define NV50_PMC_INTR_EN_0_MASTER_DISABLED (0<<0) | ||
581 | # define NV50_PMC_INTR_EN_0_MASTER_ENABLED (1<<0) | ||
582 | # define NV50_PMC_ENABLE 0x00000200 | ||
583 | # define NV50_PMC_ENABLE_PFIFO (1<<8) | ||
584 | # define NV50_PMC_ENABLE_PGRAPH (1<<12) | ||
585 | |||
586 | #define NV50_PCONNECTOR 0x0000e000 | ||
587 | #define NV50_PCONNECTOR__LEN 0x1 | ||
588 | #define NV50_PCONNECTOR__ESIZE 0x1000 | ||
589 | # define NV50_PCONNECTOR_HOTPLUG_INTR 0x0000e050 | ||
590 | # define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C0 (1<<0) | ||
591 | # define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C1 (1<<1) | ||
592 | # define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C2 (1<<2) | ||
593 | # define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C3 (1<<3) | ||
594 | # define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C0 (1<<16) | ||
595 | # define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C1 (1<<17) | ||
596 | # define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C2 (1<<18) | ||
597 | # define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C3 (1<<19) | ||
598 | # define NV50_PCONNECTOR_HOTPLUG_CTRL 0x0000e054 | ||
599 | # define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C0 (1<<0) | ||
600 | # define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C1 (1<<1) | ||
601 | # define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C2 (1<<2) | ||
602 | # define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C3 (1<<3) | ||
603 | # define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C0 (1<<16) | ||
604 | # define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C1 (1<<17) | ||
605 | # define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C2 (1<<18) | ||
606 | # define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C3 (1<<19) | ||
607 | # define NV50_PCONNECTOR_HOTPLUG_STATE 0x0000e104 | ||
608 | # define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C0 (1<<2) | ||
609 | # define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C1 (1<<6) | ||
610 | # define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C2 (1<<10) | ||
611 | # define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C3 (1<<14) | ||
612 | # define NV50_PCONNECTOR_I2C_PORT_0 0x0000e138 | ||
613 | # define NV50_PCONNECTOR_I2C_PORT_1 0x0000e150 | ||
614 | # define NV50_PCONNECTOR_I2C_PORT_2 0x0000e168 | ||
615 | # define NV50_PCONNECTOR_I2C_PORT_3 0x0000e180 | ||
616 | # define NV50_PCONNECTOR_I2C_PORT_4 0x0000e240 | ||
617 | # define NV50_PCONNECTOR_I2C_PORT_5 0x0000e258 | ||
618 | |||
619 | #define NV50_AUXCH_DATA_OUT(i,n) ((n) * 4 + (i) * 0x50 + 0x0000e4c0) | ||
620 | #define NV50_AUXCH_DATA_OUT__SIZE 4 | ||
621 | #define NV50_AUXCH_DATA_IN(i,n) ((n) * 4 + (i) * 0x50 + 0x0000e4d0) | ||
622 | #define NV50_AUXCH_DATA_IN__SIZE 4 | ||
623 | #define NV50_AUXCH_ADDR(i) ((i) * 0x50 + 0x0000e4e0) | ||
624 | #define NV50_AUXCH_CTRL(i) ((i) * 0x50 + 0x0000e4e4) | ||
625 | #define NV50_AUXCH_CTRL_LINKSTAT 0x01000000 | ||
626 | #define NV50_AUXCH_CTRL_LINKSTAT_NOT_READY 0x00000000 | ||
627 | #define NV50_AUXCH_CTRL_LINKSTAT_READY 0x01000000 | ||
628 | #define NV50_AUXCH_CTRL_LINKEN 0x00100000 | ||
629 | #define NV50_AUXCH_CTRL_LINKEN_DISABLED 0x00000000 | ||
630 | #define NV50_AUXCH_CTRL_LINKEN_ENABLED 0x00100000 | ||
631 | #define NV50_AUXCH_CTRL_EXEC 0x00010000 | ||
632 | #define NV50_AUXCH_CTRL_EXEC_COMPLETE 0x00000000 | ||
633 | #define NV50_AUXCH_CTRL_EXEC_IN_PROCESS 0x00010000 | ||
634 | #define NV50_AUXCH_CTRL_CMD 0x0000f000 | ||
635 | #define NV50_AUXCH_CTRL_CMD_SHIFT 12 | ||
636 | #define NV50_AUXCH_CTRL_LEN 0x0000000f | ||
637 | #define NV50_AUXCH_CTRL_LEN_SHIFT 0 | ||
638 | #define NV50_AUXCH_STAT(i) ((i) * 0x50 + 0x0000e4e8) | ||
639 | #define NV50_AUXCH_STAT_STATE 0x10000000 | ||
640 | #define NV50_AUXCH_STAT_STATE_NOT_READY 0x00000000 | ||
641 | #define NV50_AUXCH_STAT_STATE_READY 0x10000000 | ||
642 | #define NV50_AUXCH_STAT_REPLY 0x000f0000 | ||
643 | #define NV50_AUXCH_STAT_REPLY_AUX 0x00030000 | ||
644 | #define NV50_AUXCH_STAT_REPLY_AUX_ACK 0x00000000 | ||
645 | #define NV50_AUXCH_STAT_REPLY_AUX_NACK 0x00010000 | ||
646 | #define NV50_AUXCH_STAT_REPLY_AUX_DEFER 0x00020000 | ||
647 | #define NV50_AUXCH_STAT_REPLY_I2C 0x000c0000 | ||
648 | #define NV50_AUXCH_STAT_REPLY_I2C_ACK 0x00000000 | ||
649 | #define NV50_AUXCH_STAT_REPLY_I2C_NACK 0x00040000 | ||
650 | #define NV50_AUXCH_STAT_REPLY_I2C_DEFER 0x00080000 | ||
651 | #define NV50_AUXCH_STAT_COUNT 0x0000001f | ||
652 | |||
653 | #define NV50_PBUS 0x00088000 | ||
654 | #define NV50_PBUS__LEN 0x1 | ||
655 | #define NV50_PBUS__ESIZE 0x1000 | ||
656 | # define NV50_PBUS_PCI_ID 0x00088000 | ||
657 | # define NV50_PBUS_PCI_ID_VENDOR_ID 0x0000ffff | ||
658 | # define NV50_PBUS_PCI_ID_VENDOR_ID__SHIFT 0 | ||
659 | # define NV50_PBUS_PCI_ID_DEVICE_ID 0xffff0000 | ||
660 | # define NV50_PBUS_PCI_ID_DEVICE_ID__SHIFT 16 | ||
661 | |||
662 | #define NV50_PFB 0x00100000 | ||
663 | #define NV50_PFB__LEN 0x1 | ||
664 | #define NV50_PFB__ESIZE 0x1000 | ||
665 | |||
666 | #define NV50_PEXTDEV 0x00101000 | ||
667 | #define NV50_PEXTDEV__LEN 0x1 | ||
668 | #define NV50_PEXTDEV__ESIZE 0x1000 | ||
669 | |||
670 | #define NV50_PROM 0x00300000 | ||
671 | #define NV50_PROM__LEN 0x1 | ||
672 | #define NV50_PROM__ESIZE 0x10000 | ||
673 | |||
674 | #define NV50_PGRAPH 0x00400000 | ||
675 | #define NV50_PGRAPH__LEN 0x1 | ||
676 | #define NV50_PGRAPH__ESIZE 0x10000 | ||
677 | |||
678 | #define NV50_PDISPLAY 0x00610000 | ||
679 | #define NV50_PDISPLAY_OBJECTS 0x00610010 | ||
680 | #define NV50_PDISPLAY_INTR_0 0x00610020 | ||
681 | #define NV50_PDISPLAY_INTR_1 0x00610024 | ||
682 | #define NV50_PDISPLAY_INTR_1_VBLANK_CRTC 0x0000000c | ||
683 | #define NV50_PDISPLAY_INTR_1_VBLANK_CRTC_SHIFT 2 | ||
684 | #define NV50_PDISPLAY_INTR_1_VBLANK_CRTC_(n) (1 << ((n) + 2)) | ||
685 | #define NV50_PDISPLAY_INTR_1_VBLANK_CRTC_0 0x00000004 | ||
686 | #define NV50_PDISPLAY_INTR_1_VBLANK_CRTC_1 0x00000008 | ||
687 | #define NV50_PDISPLAY_INTR_1_CLK_UNK10 0x00000010 | ||
688 | #define NV50_PDISPLAY_INTR_1_CLK_UNK20 0x00000020 | ||
689 | #define NV50_PDISPLAY_INTR_1_CLK_UNK40 0x00000040 | ||
690 | #define NV50_PDISPLAY_INTR_EN 0x0061002c | ||
691 | #define NV50_PDISPLAY_INTR_EN_VBLANK_CRTC 0x0000000c | ||
692 | #define NV50_PDISPLAY_INTR_EN_VBLANK_CRTC_(n) (1 << ((n) + 2)) | ||
693 | #define NV50_PDISPLAY_INTR_EN_VBLANK_CRTC_0 0x00000004 | ||
694 | #define NV50_PDISPLAY_INTR_EN_VBLANK_CRTC_1 0x00000008 | ||
695 | #define NV50_PDISPLAY_INTR_EN_CLK_UNK10 0x00000010 | ||
696 | #define NV50_PDISPLAY_INTR_EN_CLK_UNK20 0x00000020 | ||
697 | #define NV50_PDISPLAY_INTR_EN_CLK_UNK40 0x00000040 | ||
698 | #define NV50_PDISPLAY_UNK30_CTRL 0x00610030 | ||
699 | #define NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK0 0x00000200 | ||
700 | #define NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK1 0x00000400 | ||
701 | #define NV50_PDISPLAY_UNK30_CTRL_PENDING 0x80000000 | ||
702 | #define NV50_PDISPLAY_TRAPPED_ADDR 0x00610080 | ||
703 | #define NV50_PDISPLAY_TRAPPED_DATA 0x00610084 | ||
704 | #define NV50_PDISPLAY_CHANNEL_STAT(i) ((i) * 0x10 + 0x00610200) | ||
705 | #define NV50_PDISPLAY_CHANNEL_STAT_DMA 0x00000010 | ||
706 | #define NV50_PDISPLAY_CHANNEL_STAT_DMA_DISABLED 0x00000000 | ||
707 | #define NV50_PDISPLAY_CHANNEL_STAT_DMA_ENABLED 0x00000010 | ||
708 | #define NV50_PDISPLAY_CHANNEL_DMA_CB(i) ((i) * 0x10 + 0x00610204) | ||
709 | #define NV50_PDISPLAY_CHANNEL_DMA_CB_LOCATION 0x00000002 | ||
710 | #define NV50_PDISPLAY_CHANNEL_DMA_CB_LOCATION_VRAM 0x00000000 | ||
711 | #define NV50_PDISPLAY_CHANNEL_DMA_CB_LOCATION_SYSTEM 0x00000002 | ||
712 | #define NV50_PDISPLAY_CHANNEL_DMA_CB_VALID 0x00000001 | ||
713 | #define NV50_PDISPLAY_CHANNEL_UNK2(i) ((i) * 0x10 + 0x00610208) | ||
714 | #define NV50_PDISPLAY_CHANNEL_UNK3(i) ((i) * 0x10 + 0x0061020c) | ||
715 | |||
716 | #define NV50_PDISPLAY_CURSOR 0x00610270 | ||
717 | #define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i) ((i) * 0x10 + 0x00610270) | ||
718 | #define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_ON 0x00000001 | ||
719 | #define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS 0x00030000 | ||
720 | #define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_ACTIVE 0x00010000 | ||
721 | |||
722 | #define NV50_PDISPLAY_CTRL_STATE 0x00610300 | ||
723 | #define NV50_PDISPLAY_CTRL_STATE_PENDING 0x80000000 | ||
724 | #define NV50_PDISPLAY_CTRL_STATE_METHOD 0x00001ffc | ||
725 | #define NV50_PDISPLAY_CTRL_STATE_ENABLE 0x00000001 | ||
726 | #define NV50_PDISPLAY_CTRL_VAL 0x00610304 | ||
727 | #define NV50_PDISPLAY_UNK_380 0x00610380 | ||
728 | #define NV50_PDISPLAY_RAM_AMOUNT 0x00610384 | ||
729 | #define NV50_PDISPLAY_UNK_388 0x00610388 | ||
730 | #define NV50_PDISPLAY_UNK_38C 0x0061038c | ||
731 | |||
732 | #define NV50_PDISPLAY_CRTC_P(i, r) ((i) * 0x540 + NV50_PDISPLAY_CRTC_##r) | ||
733 | #define NV50_PDISPLAY_CRTC_C(i, r) (4 + (i) * 0x540 + NV50_PDISPLAY_CRTC_##r) | ||
734 | #define NV50_PDISPLAY_CRTC_UNK_0A18 /* mthd 0x0900 */ 0x00610a18 | ||
735 | #define NV50_PDISPLAY_CRTC_CLUT_MODE 0x00610a24 | ||
736 | #define NV50_PDISPLAY_CRTC_INTERLACE 0x00610a48 | ||
737 | #define NV50_PDISPLAY_CRTC_SCALE_CTRL 0x00610a50 | ||
738 | #define NV50_PDISPLAY_CRTC_CURSOR_CTRL 0x00610a58 | ||
739 | #define NV50_PDISPLAY_CRTC_UNK0A78 /* mthd 0x0904 */ 0x00610a78 | ||
740 | #define NV50_PDISPLAY_CRTC_UNK0AB8 0x00610ab8 | ||
741 | #define NV50_PDISPLAY_CRTC_DEPTH 0x00610ac8 | ||
742 | #define NV50_PDISPLAY_CRTC_CLOCK 0x00610ad0 | ||
743 | #define NV50_PDISPLAY_CRTC_COLOR_CTRL 0x00610ae0 | ||
744 | #define NV50_PDISPLAY_CRTC_SYNC_START_TO_BLANK_END 0x00610ae8 | ||
745 | #define NV50_PDISPLAY_CRTC_MODE_UNK1 0x00610af0 | ||
746 | #define NV50_PDISPLAY_CRTC_DISPLAY_TOTAL 0x00610af8 | ||
747 | #define NV50_PDISPLAY_CRTC_SYNC_DURATION 0x00610b00 | ||
748 | #define NV50_PDISPLAY_CRTC_MODE_UNK2 0x00610b08 | ||
749 | #define NV50_PDISPLAY_CRTC_UNK_0B10 /* mthd 0x0828 */ 0x00610b10 | ||
750 | #define NV50_PDISPLAY_CRTC_FB_SIZE 0x00610b18 | ||
751 | #define NV50_PDISPLAY_CRTC_FB_PITCH 0x00610b20 | ||
752 | #define NV50_PDISPLAY_CRTC_FB_PITCH_LINEAR 0x00100000 | ||
753 | #define NV50_PDISPLAY_CRTC_FB_POS 0x00610b28 | ||
754 | #define NV50_PDISPLAY_CRTC_SCALE_CENTER_OFFSET 0x00610b38 | ||
755 | #define NV50_PDISPLAY_CRTC_REAL_RES 0x00610b40 | ||
756 | #define NV50_PDISPLAY_CRTC_SCALE_RES1 0x00610b48 | ||
757 | #define NV50_PDISPLAY_CRTC_SCALE_RES2 0x00610b50 | ||
758 | |||
759 | #define NV50_PDISPLAY_DAC_MODE_CTRL_P(i) (0x00610b58 + (i) * 0x8) | ||
760 | #define NV50_PDISPLAY_DAC_MODE_CTRL_C(i) (0x00610b5c + (i) * 0x8) | ||
761 | #define NV50_PDISPLAY_SOR_MODE_CTRL_P(i) (0x00610b70 + (i) * 0x8) | ||
762 | #define NV50_PDISPLAY_SOR_MODE_CTRL_C(i) (0x00610b74 + (i) * 0x8) | ||
763 | #define NV50_PDISPLAY_DAC_MODE_CTRL2_P(i) (0x00610bdc + (i) * 0x8) | ||
764 | #define NV50_PDISPLAY_DAC_MODE_CTRL2_C(i) (0x00610be0 + (i) * 0x8) | ||
765 | |||
766 | #define NV90_PDISPLAY_SOR_MODE_CTRL_P(i) (0x00610794 + (i) * 0x8) | ||
767 | #define NV90_PDISPLAY_SOR_MODE_CTRL_C(i) (0x00610798 + (i) * 0x8) | ||
768 | #define NV90_PDISPLAY_DAC_MODE_CTRL_P(i) (0x00610b58 + (i) * 0x8) | ||
769 | #define NV90_PDISPLAY_DAC_MODE_CTRL_C(i) (0x00610b5c + (i) * 0x8) | ||
770 | #define NV90_PDISPLAY_DAC_MODE_CTRL2_P(i) (0x00610b80 + (i) * 0x8) | ||
771 | #define NV90_PDISPLAY_DAC_MODE_CTRL2_C(i) (0x00610b84 + (i) * 0x8) | ||
772 | |||
773 | #define NV50_PDISPLAY_CRTC_CLK 0x00614000 | ||
774 | #define NV50_PDISPLAY_CRTC_CLK_CTRL1(i) ((i) * 0x800 + 0x614100) | ||
775 | #define NV50_PDISPLAY_CRTC_CLK_CTRL1_CONNECTED 0x00000600 | ||
776 | #define NV50_PDISPLAY_CRTC_CLK_VPLL_A(i) ((i) * 0x800 + 0x614104) | ||
777 | #define NV50_PDISPLAY_CRTC_CLK_VPLL_B(i) ((i) * 0x800 + 0x614108) | ||
778 | #define NV50_PDISPLAY_CRTC_CLK_CTRL2(i) ((i) * 0x800 + 0x614200) | ||
779 | |||
780 | #define NV50_PDISPLAY_DAC_CLK 0x00614000 | ||
781 | #define NV50_PDISPLAY_DAC_CLK_CTRL2(i) ((i) * 0x800 + 0x614280) | ||
782 | |||
783 | #define NV50_PDISPLAY_SOR_CLK 0x00614000 | ||
784 | #define NV50_PDISPLAY_SOR_CLK_CTRL2(i) ((i) * 0x800 + 0x614300) | ||
785 | |||
786 | #define NV50_PDISPLAY_VGACRTC(r) ((r) + 0x619400) | ||
787 | |||
788 | #define NV50_PDISPLAY_DAC 0x0061a000 | ||
789 | #define NV50_PDISPLAY_DAC_DPMS_CTRL(i) (0x0061a004 + (i) * 0x800) | ||
790 | #define NV50_PDISPLAY_DAC_DPMS_CTRL_HSYNC_OFF 0x00000001 | ||
791 | #define NV50_PDISPLAY_DAC_DPMS_CTRL_VSYNC_OFF 0x00000004 | ||
792 | #define NV50_PDISPLAY_DAC_DPMS_CTRL_BLANKED 0x00000010 | ||
793 | #define NV50_PDISPLAY_DAC_DPMS_CTRL_OFF 0x00000040 | ||
794 | #define NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING 0x80000000 | ||
795 | #define NV50_PDISPLAY_DAC_LOAD_CTRL(i) (0x0061a00c + (i) * 0x800) | ||
796 | #define NV50_PDISPLAY_DAC_LOAD_CTRL_ACTIVE 0x00100000 | ||
797 | #define NV50_PDISPLAY_DAC_LOAD_CTRL_PRESENT 0x38000000 | ||
798 | #define NV50_PDISPLAY_DAC_LOAD_CTRL_DONE 0x80000000 | ||
799 | #define NV50_PDISPLAY_DAC_CLK_CTRL1(i) (0x0061a010 + (i) * 0x800) | ||
800 | #define NV50_PDISPLAY_DAC_CLK_CTRL1_CONNECTED 0x00000600 | ||
801 | |||
802 | #define NV50_PDISPLAY_SOR 0x0061c000 | ||
803 | #define NV50_PDISPLAY_SOR_DPMS_CTRL(i) (0x0061c004 + (i) * 0x800) | ||
804 | #define NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING 0x80000000 | ||
805 | #define NV50_PDISPLAY_SOR_DPMS_CTRL_ON 0x00000001 | ||
806 | #define NV50_PDISPLAY_SOR_CLK_CTRL1(i) (0x0061c008 + (i) * 0x800) | ||
807 | #define NV50_PDISPLAY_SOR_CLK_CTRL1_CONNECTED 0x00000600 | ||
808 | #define NV50_PDISPLAY_SOR_DPMS_STATE(i) (0x0061c030 + (i) * 0x800) | ||
809 | #define NV50_PDISPLAY_SOR_DPMS_STATE_ACTIVE 0x00030000 | ||
810 | #define NV50_PDISPLAY_SOR_DPMS_STATE_BLANKED 0x00080000 | ||
811 | #define NV50_PDISPLAY_SOR_DPMS_STATE_WAIT 0x10000000 | ||
812 | #define NV50_PDISPLAY_SOR_BACKLIGHT 0x0061c084 | ||
813 | #define NV50_PDISPLAY_SOR_BACKLIGHT_ENABLE 0x80000000 | ||
814 | #define NV50_PDISPLAY_SOR_BACKLIGHT_LEVEL 0x00000fff | ||
815 | #define NV50_SOR_DP_CTRL(i,l) (0x0061c10c + (i) * 0x800 + (l) * 0x80) | ||
816 | #define NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED 0x00004000 | ||
817 | #define NV50_SOR_DP_CTRL_LANE_MASK 0x001f0000 | ||
818 | #define NV50_SOR_DP_CTRL_LANE_0_ENABLED 0x00010000 | ||
819 | #define NV50_SOR_DP_CTRL_LANE_1_ENABLED 0x00020000 | ||
820 | #define NV50_SOR_DP_CTRL_LANE_2_ENABLED 0x00040000 | ||
821 | #define NV50_SOR_DP_CTRL_LANE_3_ENABLED 0x00080000 | ||
822 | #define NV50_SOR_DP_CTRL_TRAINING_PATTERN 0x0f000000 | ||
823 | #define NV50_SOR_DP_CTRL_TRAINING_PATTERN_DISABLED 0x00000000 | ||
824 | #define NV50_SOR_DP_CTRL_TRAINING_PATTERN_1 0x01000000 | ||
825 | #define NV50_SOR_DP_CTRL_TRAINING_PATTERN_2 0x02000000 | ||
826 | #define NV50_SOR_DP_UNK118(i,l) (0x0061c118 + (i) * 0x800 + (l) * 0x80) | ||
827 | #define NV50_SOR_DP_UNK120(i,l) (0x0061c120 + (i) * 0x800 + (l) * 0x80) | ||
828 | #define NV50_SOR_DP_UNK130(i,l) (0x0061c130 + (i) * 0x800 + (l) * 0x80) | ||
829 | |||
830 | #define NV50_PDISPLAY_USER(i) ((i) * 0x1000 + 0x00640000) | ||
831 | #define NV50_PDISPLAY_USER_PUT(i) ((i) * 0x1000 + 0x00640000) | ||
832 | #define NV50_PDISPLAY_USER_GET(i) ((i) * 0x1000 + 0x00640004) | ||
833 | |||
834 | #define NV50_PDISPLAY_CURSOR_USER 0x00647000 | ||
835 | #define NV50_PDISPLAY_CURSOR_USER_POS_CTRL(i) ((i) * 0x1000 + 0x00647080) | ||
836 | #define NV50_PDISPLAY_CURSOR_USER_POS(i) ((i) * 0x1000 + 0x00647084) | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c new file mode 100644 index 000000000000..4c7f1e403e80 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_sgdma.c | |||
@@ -0,0 +1,321 @@ | |||
1 | #include "drmP.h" | ||
2 | #include "nouveau_drv.h" | ||
3 | #include <linux/pagemap.h> | ||
4 | |||
5 | #define NV_CTXDMA_PAGE_SHIFT 12 | ||
6 | #define NV_CTXDMA_PAGE_SIZE (1 << NV_CTXDMA_PAGE_SHIFT) | ||
7 | #define NV_CTXDMA_PAGE_MASK (NV_CTXDMA_PAGE_SIZE - 1) | ||
8 | |||
9 | struct nouveau_sgdma_be { | ||
10 | struct ttm_backend backend; | ||
11 | struct drm_device *dev; | ||
12 | |||
13 | dma_addr_t *pages; | ||
14 | unsigned nr_pages; | ||
15 | |||
16 | unsigned pte_start; | ||
17 | bool bound; | ||
18 | }; | ||
19 | |||
20 | static int | ||
21 | nouveau_sgdma_populate(struct ttm_backend *be, unsigned long num_pages, | ||
22 | struct page **pages, struct page *dummy_read_page) | ||
23 | { | ||
24 | struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be; | ||
25 | struct drm_device *dev = nvbe->dev; | ||
26 | |||
27 | NV_DEBUG(nvbe->dev, "num_pages = %ld\n", num_pages); | ||
28 | |||
29 | if (nvbe->pages) | ||
30 | return -EINVAL; | ||
31 | |||
32 | nvbe->pages = kmalloc(sizeof(dma_addr_t) * num_pages, GFP_KERNEL); | ||
33 | if (!nvbe->pages) | ||
34 | return -ENOMEM; | ||
35 | |||
36 | nvbe->nr_pages = 0; | ||
37 | while (num_pages--) { | ||
38 | nvbe->pages[nvbe->nr_pages] = | ||
39 | pci_map_page(dev->pdev, pages[nvbe->nr_pages], 0, | ||
40 | PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); | ||
41 | if (pci_dma_mapping_error(dev->pdev, | ||
42 | nvbe->pages[nvbe->nr_pages])) { | ||
43 | be->func->clear(be); | ||
44 | return -EFAULT; | ||
45 | } | ||
46 | |||
47 | nvbe->nr_pages++; | ||
48 | } | ||
49 | |||
50 | return 0; | ||
51 | } | ||
52 | |||
53 | static void | ||
54 | nouveau_sgdma_clear(struct ttm_backend *be) | ||
55 | { | ||
56 | struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be; | ||
57 | struct drm_device *dev = nvbe->dev; | ||
58 | |||
59 | NV_DEBUG(nvbe->dev, "\n"); | ||
60 | |||
61 | if (nvbe && nvbe->pages) { | ||
62 | if (nvbe->bound) | ||
63 | be->func->unbind(be); | ||
64 | |||
65 | while (nvbe->nr_pages--) { | ||
66 | pci_unmap_page(dev->pdev, nvbe->pages[nvbe->nr_pages], | ||
67 | PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); | ||
68 | } | ||
69 | kfree(nvbe->pages); | ||
70 | nvbe->pages = NULL; | ||
71 | nvbe->nr_pages = 0; | ||
72 | } | ||
73 | } | ||
74 | |||
75 | static inline unsigned | ||
76 | nouveau_sgdma_pte(struct drm_device *dev, uint64_t offset) | ||
77 | { | ||
78 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
79 | unsigned pte = (offset >> NV_CTXDMA_PAGE_SHIFT); | ||
80 | |||
81 | if (dev_priv->card_type < NV_50) | ||
82 | return pte + 2; | ||
83 | |||
84 | return pte << 1; | ||
85 | } | ||
86 | |||
87 | static int | ||
88 | nouveau_sgdma_bind(struct ttm_backend *be, struct ttm_mem_reg *mem) | ||
89 | { | ||
90 | struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be; | ||
91 | struct drm_device *dev = nvbe->dev; | ||
92 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
93 | struct nouveau_gpuobj *gpuobj = dev_priv->gart_info.sg_ctxdma; | ||
94 | unsigned i, j, pte; | ||
95 | |||
96 | NV_DEBUG(dev, "pg=0x%lx\n", mem->mm_node->start); | ||
97 | |||
98 | dev_priv->engine.instmem.prepare_access(nvbe->dev, true); | ||
99 | pte = nouveau_sgdma_pte(nvbe->dev, mem->mm_node->start << PAGE_SHIFT); | ||
100 | nvbe->pte_start = pte; | ||
101 | for (i = 0; i < nvbe->nr_pages; i++) { | ||
102 | dma_addr_t dma_offset = nvbe->pages[i]; | ||
103 | uint32_t offset_l = lower_32_bits(dma_offset); | ||
104 | uint32_t offset_h = upper_32_bits(dma_offset); | ||
105 | |||
106 | for (j = 0; j < PAGE_SIZE / NV_CTXDMA_PAGE_SIZE; j++) { | ||
107 | if (dev_priv->card_type < NV_50) | ||
108 | nv_wo32(dev, gpuobj, pte++, offset_l | 3); | ||
109 | else { | ||
110 | nv_wo32(dev, gpuobj, pte++, offset_l | 0x21); | ||
111 | nv_wo32(dev, gpuobj, pte++, offset_h & 0xff); | ||
112 | } | ||
113 | |||
114 | dma_offset += NV_CTXDMA_PAGE_SIZE; | ||
115 | } | ||
116 | } | ||
117 | dev_priv->engine.instmem.finish_access(nvbe->dev); | ||
118 | |||
119 | if (dev_priv->card_type == NV_50) { | ||
120 | nv_wr32(dev, 0x100c80, 0x00050001); | ||
121 | if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) { | ||
122 | NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n"); | ||
123 | NV_ERROR(dev, "0x100c80 = 0x%08x\n", | ||
124 | nv_rd32(dev, 0x100c80)); | ||
125 | return -EBUSY; | ||
126 | } | ||
127 | |||
128 | nv_wr32(dev, 0x100c80, 0x00000001); | ||
129 | if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) { | ||
130 | NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n"); | ||
131 | NV_ERROR(dev, "0x100c80 = 0x%08x\n", | ||
132 | nv_rd32(dev, 0x100c80)); | ||
133 | return -EBUSY; | ||
134 | } | ||
135 | } | ||
136 | |||
137 | nvbe->bound = true; | ||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | static int | ||
142 | nouveau_sgdma_unbind(struct ttm_backend *be) | ||
143 | { | ||
144 | struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be; | ||
145 | struct drm_device *dev = nvbe->dev; | ||
146 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
147 | struct nouveau_gpuobj *gpuobj = dev_priv->gart_info.sg_ctxdma; | ||
148 | unsigned i, j, pte; | ||
149 | |||
150 | NV_DEBUG(dev, "\n"); | ||
151 | |||
152 | if (!nvbe->bound) | ||
153 | return 0; | ||
154 | |||
155 | dev_priv->engine.instmem.prepare_access(nvbe->dev, true); | ||
156 | pte = nvbe->pte_start; | ||
157 | for (i = 0; i < nvbe->nr_pages; i++) { | ||
158 | dma_addr_t dma_offset = dev_priv->gart_info.sg_dummy_bus; | ||
159 | |||
160 | for (j = 0; j < PAGE_SIZE / NV_CTXDMA_PAGE_SIZE; j++) { | ||
161 | if (dev_priv->card_type < NV_50) | ||
162 | nv_wo32(dev, gpuobj, pte++, dma_offset | 3); | ||
163 | else { | ||
164 | nv_wo32(dev, gpuobj, pte++, dma_offset | 0x21); | ||
165 | nv_wo32(dev, gpuobj, pte++, 0x00000000); | ||
166 | } | ||
167 | |||
168 | dma_offset += NV_CTXDMA_PAGE_SIZE; | ||
169 | } | ||
170 | } | ||
171 | dev_priv->engine.instmem.finish_access(nvbe->dev); | ||
172 | |||
173 | nvbe->bound = false; | ||
174 | return 0; | ||
175 | } | ||
176 | |||
177 | static void | ||
178 | nouveau_sgdma_destroy(struct ttm_backend *be) | ||
179 | { | ||
180 | struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be; | ||
181 | |||
182 | if (be) { | ||
183 | NV_DEBUG(nvbe->dev, "\n"); | ||
184 | |||
185 | if (nvbe) { | ||
186 | if (nvbe->pages) | ||
187 | be->func->clear(be); | ||
188 | kfree(nvbe); | ||
189 | } | ||
190 | } | ||
191 | } | ||
192 | |||
193 | static struct ttm_backend_func nouveau_sgdma_backend = { | ||
194 | .populate = nouveau_sgdma_populate, | ||
195 | .clear = nouveau_sgdma_clear, | ||
196 | .bind = nouveau_sgdma_bind, | ||
197 | .unbind = nouveau_sgdma_unbind, | ||
198 | .destroy = nouveau_sgdma_destroy | ||
199 | }; | ||
200 | |||
201 | struct ttm_backend * | ||
202 | nouveau_sgdma_init_ttm(struct drm_device *dev) | ||
203 | { | ||
204 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
205 | struct nouveau_sgdma_be *nvbe; | ||
206 | |||
207 | if (!dev_priv->gart_info.sg_ctxdma) | ||
208 | return NULL; | ||
209 | |||
210 | nvbe = kzalloc(sizeof(*nvbe), GFP_KERNEL); | ||
211 | if (!nvbe) | ||
212 | return NULL; | ||
213 | |||
214 | nvbe->dev = dev; | ||
215 | |||
216 | nvbe->backend.func = &nouveau_sgdma_backend; | ||
217 | |||
218 | return &nvbe->backend; | ||
219 | } | ||
220 | |||
221 | int | ||
222 | nouveau_sgdma_init(struct drm_device *dev) | ||
223 | { | ||
224 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
225 | struct nouveau_gpuobj *gpuobj = NULL; | ||
226 | uint32_t aper_size, obj_size; | ||
227 | int i, ret; | ||
228 | |||
229 | if (dev_priv->card_type < NV_50) { | ||
230 | aper_size = (64 * 1024 * 1024); | ||
231 | obj_size = (aper_size >> NV_CTXDMA_PAGE_SHIFT) * 4; | ||
232 | obj_size += 8; /* ctxdma header */ | ||
233 | } else { | ||
234 | /* 1 entire VM page table */ | ||
235 | aper_size = (512 * 1024 * 1024); | ||
236 | obj_size = (aper_size >> NV_CTXDMA_PAGE_SHIFT) * 8; | ||
237 | } | ||
238 | |||
239 | ret = nouveau_gpuobj_new(dev, NULL, obj_size, 16, | ||
240 | NVOBJ_FLAG_ALLOW_NO_REFS | | ||
241 | NVOBJ_FLAG_ZERO_ALLOC | | ||
242 | NVOBJ_FLAG_ZERO_FREE, &gpuobj); | ||
243 | if (ret) { | ||
244 | NV_ERROR(dev, "Error creating sgdma object: %d\n", ret); | ||
245 | return ret; | ||
246 | } | ||
247 | |||
248 | dev_priv->gart_info.sg_dummy_page = | ||
249 | alloc_page(GFP_KERNEL|__GFP_DMA32); | ||
250 | set_bit(PG_locked, &dev_priv->gart_info.sg_dummy_page->flags); | ||
251 | dev_priv->gart_info.sg_dummy_bus = | ||
252 | pci_map_page(dev->pdev, dev_priv->gart_info.sg_dummy_page, 0, | ||
253 | PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); | ||
254 | |||
255 | dev_priv->engine.instmem.prepare_access(dev, true); | ||
256 | if (dev_priv->card_type < NV_50) { | ||
257 | /* Maybe use NV_DMA_TARGET_AGP for PCIE? NVIDIA do this, and | ||
258 | * confirmed to work on c51. Perhaps means NV_DMA_TARGET_PCIE | ||
259 | * on those cards? */ | ||
260 | nv_wo32(dev, gpuobj, 0, NV_CLASS_DMA_IN_MEMORY | | ||
261 | (1 << 12) /* PT present */ | | ||
262 | (0 << 13) /* PT *not* linear */ | | ||
263 | (NV_DMA_ACCESS_RW << 14) | | ||
264 | (NV_DMA_TARGET_PCI << 16)); | ||
265 | nv_wo32(dev, gpuobj, 1, aper_size - 1); | ||
266 | for (i = 2; i < 2 + (aper_size >> 12); i++) { | ||
267 | nv_wo32(dev, gpuobj, i, | ||
268 | dev_priv->gart_info.sg_dummy_bus | 3); | ||
269 | } | ||
270 | } else { | ||
271 | for (i = 0; i < obj_size; i += 8) { | ||
272 | nv_wo32(dev, gpuobj, (i+0)/4, | ||
273 | dev_priv->gart_info.sg_dummy_bus | 0x21); | ||
274 | nv_wo32(dev, gpuobj, (i+4)/4, 0); | ||
275 | } | ||
276 | } | ||
277 | dev_priv->engine.instmem.finish_access(dev); | ||
278 | |||
279 | dev_priv->gart_info.type = NOUVEAU_GART_SGDMA; | ||
280 | dev_priv->gart_info.aper_base = 0; | ||
281 | dev_priv->gart_info.aper_size = aper_size; | ||
282 | dev_priv->gart_info.sg_ctxdma = gpuobj; | ||
283 | return 0; | ||
284 | } | ||
285 | |||
286 | void | ||
287 | nouveau_sgdma_takedown(struct drm_device *dev) | ||
288 | { | ||
289 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
290 | |||
291 | if (dev_priv->gart_info.sg_dummy_page) { | ||
292 | pci_unmap_page(dev->pdev, dev_priv->gart_info.sg_dummy_bus, | ||
293 | NV_CTXDMA_PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); | ||
294 | unlock_page(dev_priv->gart_info.sg_dummy_page); | ||
295 | __free_page(dev_priv->gart_info.sg_dummy_page); | ||
296 | dev_priv->gart_info.sg_dummy_page = NULL; | ||
297 | dev_priv->gart_info.sg_dummy_bus = 0; | ||
298 | } | ||
299 | |||
300 | nouveau_gpuobj_del(dev, &dev_priv->gart_info.sg_ctxdma); | ||
301 | } | ||
302 | |||
303 | int | ||
304 | nouveau_sgdma_get_page(struct drm_device *dev, uint32_t offset, uint32_t *page) | ||
305 | { | ||
306 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
307 | struct nouveau_gpuobj *gpuobj = dev_priv->gart_info.sg_ctxdma; | ||
308 | struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem; | ||
309 | int pte; | ||
310 | |||
311 | pte = (offset >> NV_CTXDMA_PAGE_SHIFT); | ||
312 | if (dev_priv->card_type < NV_50) { | ||
313 | instmem->prepare_access(dev, false); | ||
314 | *page = nv_ro32(dev, gpuobj, (pte + 2)) & ~NV_CTXDMA_PAGE_MASK; | ||
315 | instmem->finish_access(dev); | ||
316 | return 0; | ||
317 | } | ||
318 | |||
319 | NV_ERROR(dev, "Unimplemented on NV50\n"); | ||
320 | return -EINVAL; | ||
321 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c new file mode 100644 index 000000000000..2ed41d339f6a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_state.c | |||
@@ -0,0 +1,811 @@ | |||
1 | /* | ||
2 | * Copyright 2005 Stephane Marchesin | ||
3 | * Copyright 2008 Stuart Bennett | ||
4 | * All Rights Reserved. | ||
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 (including the next | ||
14 | * paragraph) shall be included in all copies or substantial portions of the | ||
15 | * Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
20 | * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | ||
21 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | ||
22 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
23 | * DEALINGS IN THE SOFTWARE. | ||
24 | */ | ||
25 | |||
26 | #include <linux/swab.h> | ||
27 | #include "drmP.h" | ||
28 | #include "drm.h" | ||
29 | #include "drm_sarea.h" | ||
30 | #include "drm_crtc_helper.h" | ||
31 | #include <linux/vgaarb.h> | ||
32 | |||
33 | #include "nouveau_drv.h" | ||
34 | #include "nouveau_drm.h" | ||
35 | #include "nv50_display.h" | ||
36 | |||
37 | static int nouveau_stub_init(struct drm_device *dev) { return 0; } | ||
38 | static void nouveau_stub_takedown(struct drm_device *dev) {} | ||
39 | |||
40 | static int nouveau_init_engine_ptrs(struct drm_device *dev) | ||
41 | { | ||
42 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
43 | struct nouveau_engine *engine = &dev_priv->engine; | ||
44 | |||
45 | switch (dev_priv->chipset & 0xf0) { | ||
46 | case 0x00: | ||
47 | engine->instmem.init = nv04_instmem_init; | ||
48 | engine->instmem.takedown = nv04_instmem_takedown; | ||
49 | engine->instmem.suspend = nv04_instmem_suspend; | ||
50 | engine->instmem.resume = nv04_instmem_resume; | ||
51 | engine->instmem.populate = nv04_instmem_populate; | ||
52 | engine->instmem.clear = nv04_instmem_clear; | ||
53 | engine->instmem.bind = nv04_instmem_bind; | ||
54 | engine->instmem.unbind = nv04_instmem_unbind; | ||
55 | engine->instmem.prepare_access = nv04_instmem_prepare_access; | ||
56 | engine->instmem.finish_access = nv04_instmem_finish_access; | ||
57 | engine->mc.init = nv04_mc_init; | ||
58 | engine->mc.takedown = nv04_mc_takedown; | ||
59 | engine->timer.init = nv04_timer_init; | ||
60 | engine->timer.read = nv04_timer_read; | ||
61 | engine->timer.takedown = nv04_timer_takedown; | ||
62 | engine->fb.init = nv04_fb_init; | ||
63 | engine->fb.takedown = nv04_fb_takedown; | ||
64 | engine->graph.grclass = nv04_graph_grclass; | ||
65 | engine->graph.init = nv04_graph_init; | ||
66 | engine->graph.takedown = nv04_graph_takedown; | ||
67 | engine->graph.fifo_access = nv04_graph_fifo_access; | ||
68 | engine->graph.channel = nv04_graph_channel; | ||
69 | engine->graph.create_context = nv04_graph_create_context; | ||
70 | engine->graph.destroy_context = nv04_graph_destroy_context; | ||
71 | engine->graph.load_context = nv04_graph_load_context; | ||
72 | engine->graph.unload_context = nv04_graph_unload_context; | ||
73 | engine->fifo.channels = 16; | ||
74 | engine->fifo.init = nv04_fifo_init; | ||
75 | engine->fifo.takedown = nouveau_stub_takedown; | ||
76 | engine->fifo.disable = nv04_fifo_disable; | ||
77 | engine->fifo.enable = nv04_fifo_enable; | ||
78 | engine->fifo.reassign = nv04_fifo_reassign; | ||
79 | engine->fifo.channel_id = nv04_fifo_channel_id; | ||
80 | engine->fifo.create_context = nv04_fifo_create_context; | ||
81 | engine->fifo.destroy_context = nv04_fifo_destroy_context; | ||
82 | engine->fifo.load_context = nv04_fifo_load_context; | ||
83 | engine->fifo.unload_context = nv04_fifo_unload_context; | ||
84 | break; | ||
85 | case 0x10: | ||
86 | engine->instmem.init = nv04_instmem_init; | ||
87 | engine->instmem.takedown = nv04_instmem_takedown; | ||
88 | engine->instmem.suspend = nv04_instmem_suspend; | ||
89 | engine->instmem.resume = nv04_instmem_resume; | ||
90 | engine->instmem.populate = nv04_instmem_populate; | ||
91 | engine->instmem.clear = nv04_instmem_clear; | ||
92 | engine->instmem.bind = nv04_instmem_bind; | ||
93 | engine->instmem.unbind = nv04_instmem_unbind; | ||
94 | engine->instmem.prepare_access = nv04_instmem_prepare_access; | ||
95 | engine->instmem.finish_access = nv04_instmem_finish_access; | ||
96 | engine->mc.init = nv04_mc_init; | ||
97 | engine->mc.takedown = nv04_mc_takedown; | ||
98 | engine->timer.init = nv04_timer_init; | ||
99 | engine->timer.read = nv04_timer_read; | ||
100 | engine->timer.takedown = nv04_timer_takedown; | ||
101 | engine->fb.init = nv10_fb_init; | ||
102 | engine->fb.takedown = nv10_fb_takedown; | ||
103 | engine->graph.grclass = nv10_graph_grclass; | ||
104 | engine->graph.init = nv10_graph_init; | ||
105 | engine->graph.takedown = nv10_graph_takedown; | ||
106 | engine->graph.channel = nv10_graph_channel; | ||
107 | engine->graph.create_context = nv10_graph_create_context; | ||
108 | engine->graph.destroy_context = nv10_graph_destroy_context; | ||
109 | engine->graph.fifo_access = nv04_graph_fifo_access; | ||
110 | engine->graph.load_context = nv10_graph_load_context; | ||
111 | engine->graph.unload_context = nv10_graph_unload_context; | ||
112 | engine->fifo.channels = 32; | ||
113 | engine->fifo.init = nv10_fifo_init; | ||
114 | engine->fifo.takedown = nouveau_stub_takedown; | ||
115 | engine->fifo.disable = nv04_fifo_disable; | ||
116 | engine->fifo.enable = nv04_fifo_enable; | ||
117 | engine->fifo.reassign = nv04_fifo_reassign; | ||
118 | engine->fifo.channel_id = nv10_fifo_channel_id; | ||
119 | engine->fifo.create_context = nv10_fifo_create_context; | ||
120 | engine->fifo.destroy_context = nv10_fifo_destroy_context; | ||
121 | engine->fifo.load_context = nv10_fifo_load_context; | ||
122 | engine->fifo.unload_context = nv10_fifo_unload_context; | ||
123 | break; | ||
124 | case 0x20: | ||
125 | engine->instmem.init = nv04_instmem_init; | ||
126 | engine->instmem.takedown = nv04_instmem_takedown; | ||
127 | engine->instmem.suspend = nv04_instmem_suspend; | ||
128 | engine->instmem.resume = nv04_instmem_resume; | ||
129 | engine->instmem.populate = nv04_instmem_populate; | ||
130 | engine->instmem.clear = nv04_instmem_clear; | ||
131 | engine->instmem.bind = nv04_instmem_bind; | ||
132 | engine->instmem.unbind = nv04_instmem_unbind; | ||
133 | engine->instmem.prepare_access = nv04_instmem_prepare_access; | ||
134 | engine->instmem.finish_access = nv04_instmem_finish_access; | ||
135 | engine->mc.init = nv04_mc_init; | ||
136 | engine->mc.takedown = nv04_mc_takedown; | ||
137 | engine->timer.init = nv04_timer_init; | ||
138 | engine->timer.read = nv04_timer_read; | ||
139 | engine->timer.takedown = nv04_timer_takedown; | ||
140 | engine->fb.init = nv10_fb_init; | ||
141 | engine->fb.takedown = nv10_fb_takedown; | ||
142 | engine->graph.grclass = nv20_graph_grclass; | ||
143 | engine->graph.init = nv20_graph_init; | ||
144 | engine->graph.takedown = nv20_graph_takedown; | ||
145 | engine->graph.channel = nv10_graph_channel; | ||
146 | engine->graph.create_context = nv20_graph_create_context; | ||
147 | engine->graph.destroy_context = nv20_graph_destroy_context; | ||
148 | engine->graph.fifo_access = nv04_graph_fifo_access; | ||
149 | engine->graph.load_context = nv20_graph_load_context; | ||
150 | engine->graph.unload_context = nv20_graph_unload_context; | ||
151 | engine->fifo.channels = 32; | ||
152 | engine->fifo.init = nv10_fifo_init; | ||
153 | engine->fifo.takedown = nouveau_stub_takedown; | ||
154 | engine->fifo.disable = nv04_fifo_disable; | ||
155 | engine->fifo.enable = nv04_fifo_enable; | ||
156 | engine->fifo.reassign = nv04_fifo_reassign; | ||
157 | engine->fifo.channel_id = nv10_fifo_channel_id; | ||
158 | engine->fifo.create_context = nv10_fifo_create_context; | ||
159 | engine->fifo.destroy_context = nv10_fifo_destroy_context; | ||
160 | engine->fifo.load_context = nv10_fifo_load_context; | ||
161 | engine->fifo.unload_context = nv10_fifo_unload_context; | ||
162 | break; | ||
163 | case 0x30: | ||
164 | engine->instmem.init = nv04_instmem_init; | ||
165 | engine->instmem.takedown = nv04_instmem_takedown; | ||
166 | engine->instmem.suspend = nv04_instmem_suspend; | ||
167 | engine->instmem.resume = nv04_instmem_resume; | ||
168 | engine->instmem.populate = nv04_instmem_populate; | ||
169 | engine->instmem.clear = nv04_instmem_clear; | ||
170 | engine->instmem.bind = nv04_instmem_bind; | ||
171 | engine->instmem.unbind = nv04_instmem_unbind; | ||
172 | engine->instmem.prepare_access = nv04_instmem_prepare_access; | ||
173 | engine->instmem.finish_access = nv04_instmem_finish_access; | ||
174 | engine->mc.init = nv04_mc_init; | ||
175 | engine->mc.takedown = nv04_mc_takedown; | ||
176 | engine->timer.init = nv04_timer_init; | ||
177 | engine->timer.read = nv04_timer_read; | ||
178 | engine->timer.takedown = nv04_timer_takedown; | ||
179 | engine->fb.init = nv10_fb_init; | ||
180 | engine->fb.takedown = nv10_fb_takedown; | ||
181 | engine->graph.grclass = nv30_graph_grclass; | ||
182 | engine->graph.init = nv30_graph_init; | ||
183 | engine->graph.takedown = nv20_graph_takedown; | ||
184 | engine->graph.fifo_access = nv04_graph_fifo_access; | ||
185 | engine->graph.channel = nv10_graph_channel; | ||
186 | engine->graph.create_context = nv20_graph_create_context; | ||
187 | engine->graph.destroy_context = nv20_graph_destroy_context; | ||
188 | engine->graph.load_context = nv20_graph_load_context; | ||
189 | engine->graph.unload_context = nv20_graph_unload_context; | ||
190 | engine->fifo.channels = 32; | ||
191 | engine->fifo.init = nv10_fifo_init; | ||
192 | engine->fifo.takedown = nouveau_stub_takedown; | ||
193 | engine->fifo.disable = nv04_fifo_disable; | ||
194 | engine->fifo.enable = nv04_fifo_enable; | ||
195 | engine->fifo.reassign = nv04_fifo_reassign; | ||
196 | engine->fifo.channel_id = nv10_fifo_channel_id; | ||
197 | engine->fifo.create_context = nv10_fifo_create_context; | ||
198 | engine->fifo.destroy_context = nv10_fifo_destroy_context; | ||
199 | engine->fifo.load_context = nv10_fifo_load_context; | ||
200 | engine->fifo.unload_context = nv10_fifo_unload_context; | ||
201 | break; | ||
202 | case 0x40: | ||
203 | case 0x60: | ||
204 | engine->instmem.init = nv04_instmem_init; | ||
205 | engine->instmem.takedown = nv04_instmem_takedown; | ||
206 | engine->instmem.suspend = nv04_instmem_suspend; | ||
207 | engine->instmem.resume = nv04_instmem_resume; | ||
208 | engine->instmem.populate = nv04_instmem_populate; | ||
209 | engine->instmem.clear = nv04_instmem_clear; | ||
210 | engine->instmem.bind = nv04_instmem_bind; | ||
211 | engine->instmem.unbind = nv04_instmem_unbind; | ||
212 | engine->instmem.prepare_access = nv04_instmem_prepare_access; | ||
213 | engine->instmem.finish_access = nv04_instmem_finish_access; | ||
214 | engine->mc.init = nv40_mc_init; | ||
215 | engine->mc.takedown = nv40_mc_takedown; | ||
216 | engine->timer.init = nv04_timer_init; | ||
217 | engine->timer.read = nv04_timer_read; | ||
218 | engine->timer.takedown = nv04_timer_takedown; | ||
219 | engine->fb.init = nv40_fb_init; | ||
220 | engine->fb.takedown = nv40_fb_takedown; | ||
221 | engine->graph.grclass = nv40_graph_grclass; | ||
222 | engine->graph.init = nv40_graph_init; | ||
223 | engine->graph.takedown = nv40_graph_takedown; | ||
224 | engine->graph.fifo_access = nv04_graph_fifo_access; | ||
225 | engine->graph.channel = nv40_graph_channel; | ||
226 | engine->graph.create_context = nv40_graph_create_context; | ||
227 | engine->graph.destroy_context = nv40_graph_destroy_context; | ||
228 | engine->graph.load_context = nv40_graph_load_context; | ||
229 | engine->graph.unload_context = nv40_graph_unload_context; | ||
230 | engine->fifo.channels = 32; | ||
231 | engine->fifo.init = nv40_fifo_init; | ||
232 | engine->fifo.takedown = nouveau_stub_takedown; | ||
233 | engine->fifo.disable = nv04_fifo_disable; | ||
234 | engine->fifo.enable = nv04_fifo_enable; | ||
235 | engine->fifo.reassign = nv04_fifo_reassign; | ||
236 | engine->fifo.channel_id = nv10_fifo_channel_id; | ||
237 | engine->fifo.create_context = nv40_fifo_create_context; | ||
238 | engine->fifo.destroy_context = nv40_fifo_destroy_context; | ||
239 | engine->fifo.load_context = nv40_fifo_load_context; | ||
240 | engine->fifo.unload_context = nv40_fifo_unload_context; | ||
241 | break; | ||
242 | case 0x50: | ||
243 | case 0x80: /* gotta love NVIDIA's consistency.. */ | ||
244 | case 0x90: | ||
245 | case 0xA0: | ||
246 | engine->instmem.init = nv50_instmem_init; | ||
247 | engine->instmem.takedown = nv50_instmem_takedown; | ||
248 | engine->instmem.suspend = nv50_instmem_suspend; | ||
249 | engine->instmem.resume = nv50_instmem_resume; | ||
250 | engine->instmem.populate = nv50_instmem_populate; | ||
251 | engine->instmem.clear = nv50_instmem_clear; | ||
252 | engine->instmem.bind = nv50_instmem_bind; | ||
253 | engine->instmem.unbind = nv50_instmem_unbind; | ||
254 | engine->instmem.prepare_access = nv50_instmem_prepare_access; | ||
255 | engine->instmem.finish_access = nv50_instmem_finish_access; | ||
256 | engine->mc.init = nv50_mc_init; | ||
257 | engine->mc.takedown = nv50_mc_takedown; | ||
258 | engine->timer.init = nv04_timer_init; | ||
259 | engine->timer.read = nv04_timer_read; | ||
260 | engine->timer.takedown = nv04_timer_takedown; | ||
261 | engine->fb.init = nouveau_stub_init; | ||
262 | engine->fb.takedown = nouveau_stub_takedown; | ||
263 | engine->graph.grclass = nv50_graph_grclass; | ||
264 | engine->graph.init = nv50_graph_init; | ||
265 | engine->graph.takedown = nv50_graph_takedown; | ||
266 | engine->graph.fifo_access = nv50_graph_fifo_access; | ||
267 | engine->graph.channel = nv50_graph_channel; | ||
268 | engine->graph.create_context = nv50_graph_create_context; | ||
269 | engine->graph.destroy_context = nv50_graph_destroy_context; | ||
270 | engine->graph.load_context = nv50_graph_load_context; | ||
271 | engine->graph.unload_context = nv50_graph_unload_context; | ||
272 | engine->fifo.channels = 128; | ||
273 | engine->fifo.init = nv50_fifo_init; | ||
274 | engine->fifo.takedown = nv50_fifo_takedown; | ||
275 | engine->fifo.disable = nv04_fifo_disable; | ||
276 | engine->fifo.enable = nv04_fifo_enable; | ||
277 | engine->fifo.reassign = nv04_fifo_reassign; | ||
278 | engine->fifo.channel_id = nv50_fifo_channel_id; | ||
279 | engine->fifo.create_context = nv50_fifo_create_context; | ||
280 | engine->fifo.destroy_context = nv50_fifo_destroy_context; | ||
281 | engine->fifo.load_context = nv50_fifo_load_context; | ||
282 | engine->fifo.unload_context = nv50_fifo_unload_context; | ||
283 | break; | ||
284 | default: | ||
285 | NV_ERROR(dev, "NV%02x unsupported\n", dev_priv->chipset); | ||
286 | return 1; | ||
287 | } | ||
288 | |||
289 | return 0; | ||
290 | } | ||
291 | |||
292 | static unsigned int | ||
293 | nouveau_vga_set_decode(void *priv, bool state) | ||
294 | { | ||
295 | if (state) | ||
296 | return VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM | | ||
297 | VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; | ||
298 | else | ||
299 | return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; | ||
300 | } | ||
301 | |||
302 | int | ||
303 | nouveau_card_init(struct drm_device *dev) | ||
304 | { | ||
305 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
306 | struct nouveau_engine *engine; | ||
307 | struct nouveau_gpuobj *gpuobj; | ||
308 | int ret; | ||
309 | |||
310 | NV_DEBUG(dev, "prev state = %d\n", dev_priv->init_state); | ||
311 | |||
312 | if (dev_priv->init_state == NOUVEAU_CARD_INIT_DONE) | ||
313 | return 0; | ||
314 | |||
315 | vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode); | ||
316 | |||
317 | /* Initialise internal driver API hooks */ | ||
318 | ret = nouveau_init_engine_ptrs(dev); | ||
319 | if (ret) | ||
320 | return ret; | ||
321 | engine = &dev_priv->engine; | ||
322 | dev_priv->init_state = NOUVEAU_CARD_INIT_FAILED; | ||
323 | |||
324 | /* Parse BIOS tables / Run init tables if card not POSTed */ | ||
325 | if (drm_core_check_feature(dev, DRIVER_MODESET)) { | ||
326 | ret = nouveau_bios_init(dev); | ||
327 | if (ret) | ||
328 | return ret; | ||
329 | } | ||
330 | |||
331 | ret = nouveau_gpuobj_early_init(dev); | ||
332 | if (ret) | ||
333 | return ret; | ||
334 | |||
335 | /* Initialise instance memory, must happen before mem_init so we | ||
336 | * know exactly how much VRAM we're able to use for "normal" | ||
337 | * purposes. | ||
338 | */ | ||
339 | ret = engine->instmem.init(dev); | ||
340 | if (ret) | ||
341 | return ret; | ||
342 | |||
343 | /* Setup the memory manager */ | ||
344 | ret = nouveau_mem_init(dev); | ||
345 | if (ret) | ||
346 | return ret; | ||
347 | |||
348 | ret = nouveau_gpuobj_init(dev); | ||
349 | if (ret) | ||
350 | return ret; | ||
351 | |||
352 | /* PMC */ | ||
353 | ret = engine->mc.init(dev); | ||
354 | if (ret) | ||
355 | return ret; | ||
356 | |||
357 | /* PTIMER */ | ||
358 | ret = engine->timer.init(dev); | ||
359 | if (ret) | ||
360 | return ret; | ||
361 | |||
362 | /* PFB */ | ||
363 | ret = engine->fb.init(dev); | ||
364 | if (ret) | ||
365 | return ret; | ||
366 | |||
367 | /* PGRAPH */ | ||
368 | ret = engine->graph.init(dev); | ||
369 | if (ret) | ||
370 | return ret; | ||
371 | |||
372 | /* PFIFO */ | ||
373 | ret = engine->fifo.init(dev); | ||
374 | if (ret) | ||
375 | return ret; | ||
376 | |||
377 | /* this call irq_preinstall, register irq handler and | ||
378 | * call irq_postinstall | ||
379 | */ | ||
380 | ret = drm_irq_install(dev); | ||
381 | if (ret) | ||
382 | return ret; | ||
383 | |||
384 | ret = drm_vblank_init(dev, 0); | ||
385 | if (ret) | ||
386 | return ret; | ||
387 | |||
388 | /* what about PVIDEO/PCRTC/PRAMDAC etc? */ | ||
389 | |||
390 | ret = nouveau_channel_alloc(dev, &dev_priv->channel, | ||
391 | (struct drm_file *)-2, | ||
392 | NvDmaFB, NvDmaTT); | ||
393 | if (ret) | ||
394 | return ret; | ||
395 | |||
396 | gpuobj = NULL; | ||
397 | ret = nouveau_gpuobj_dma_new(dev_priv->channel, NV_CLASS_DMA_IN_MEMORY, | ||
398 | 0, nouveau_mem_fb_amount(dev), | ||
399 | NV_DMA_ACCESS_RW, NV_DMA_TARGET_VIDMEM, | ||
400 | &gpuobj); | ||
401 | if (ret) | ||
402 | return ret; | ||
403 | |||
404 | ret = nouveau_gpuobj_ref_add(dev, dev_priv->channel, NvDmaVRAM, | ||
405 | gpuobj, NULL); | ||
406 | if (ret) { | ||
407 | nouveau_gpuobj_del(dev, &gpuobj); | ||
408 | return ret; | ||
409 | } | ||
410 | |||
411 | gpuobj = NULL; | ||
412 | ret = nouveau_gpuobj_gart_dma_new(dev_priv->channel, 0, | ||
413 | dev_priv->gart_info.aper_size, | ||
414 | NV_DMA_ACCESS_RW, &gpuobj, NULL); | ||
415 | if (ret) | ||
416 | return ret; | ||
417 | |||
418 | ret = nouveau_gpuobj_ref_add(dev, dev_priv->channel, NvDmaGART, | ||
419 | gpuobj, NULL); | ||
420 | if (ret) { | ||
421 | nouveau_gpuobj_del(dev, &gpuobj); | ||
422 | return ret; | ||
423 | } | ||
424 | |||
425 | if (drm_core_check_feature(dev, DRIVER_MODESET)) { | ||
426 | if (dev_priv->card_type >= NV_50) { | ||
427 | ret = nv50_display_create(dev); | ||
428 | if (ret) | ||
429 | return ret; | ||
430 | } else { | ||
431 | ret = nv04_display_create(dev); | ||
432 | if (ret) | ||
433 | return ret; | ||
434 | } | ||
435 | } | ||
436 | |||
437 | ret = nouveau_backlight_init(dev); | ||
438 | if (ret) | ||
439 | NV_ERROR(dev, "Error %d registering backlight\n", ret); | ||
440 | |||
441 | dev_priv->init_state = NOUVEAU_CARD_INIT_DONE; | ||
442 | |||
443 | if (drm_core_check_feature(dev, DRIVER_MODESET)) | ||
444 | drm_helper_initial_config(dev); | ||
445 | |||
446 | return 0; | ||
447 | } | ||
448 | |||
449 | static void nouveau_card_takedown(struct drm_device *dev) | ||
450 | { | ||
451 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
452 | struct nouveau_engine *engine = &dev_priv->engine; | ||
453 | |||
454 | NV_DEBUG(dev, "prev state = %d\n", dev_priv->init_state); | ||
455 | |||
456 | if (dev_priv->init_state != NOUVEAU_CARD_INIT_DOWN) { | ||
457 | nouveau_backlight_exit(dev); | ||
458 | |||
459 | if (dev_priv->channel) { | ||
460 | nouveau_channel_free(dev_priv->channel); | ||
461 | dev_priv->channel = NULL; | ||
462 | } | ||
463 | |||
464 | engine->fifo.takedown(dev); | ||
465 | engine->graph.takedown(dev); | ||
466 | engine->fb.takedown(dev); | ||
467 | engine->timer.takedown(dev); | ||
468 | engine->mc.takedown(dev); | ||
469 | |||
470 | mutex_lock(&dev->struct_mutex); | ||
471 | ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_TT); | ||
472 | mutex_unlock(&dev->struct_mutex); | ||
473 | nouveau_sgdma_takedown(dev); | ||
474 | |||
475 | nouveau_gpuobj_takedown(dev); | ||
476 | nouveau_mem_close(dev); | ||
477 | engine->instmem.takedown(dev); | ||
478 | |||
479 | if (drm_core_check_feature(dev, DRIVER_MODESET)) | ||
480 | drm_irq_uninstall(dev); | ||
481 | |||
482 | nouveau_gpuobj_late_takedown(dev); | ||
483 | nouveau_bios_takedown(dev); | ||
484 | |||
485 | vga_client_register(dev->pdev, NULL, NULL, NULL); | ||
486 | |||
487 | dev_priv->init_state = NOUVEAU_CARD_INIT_DOWN; | ||
488 | } | ||
489 | } | ||
490 | |||
491 | /* here a client dies, release the stuff that was allocated for its | ||
492 | * file_priv */ | ||
493 | void nouveau_preclose(struct drm_device *dev, struct drm_file *file_priv) | ||
494 | { | ||
495 | nouveau_channel_cleanup(dev, file_priv); | ||
496 | } | ||
497 | |||
498 | /* first module load, setup the mmio/fb mapping */ | ||
499 | /* KMS: we need mmio at load time, not when the first drm client opens. */ | ||
500 | int nouveau_firstopen(struct drm_device *dev) | ||
501 | { | ||
502 | return 0; | ||
503 | } | ||
504 | |||
505 | /* if we have an OF card, copy vbios to RAMIN */ | ||
506 | static void nouveau_OF_copy_vbios_to_ramin(struct drm_device *dev) | ||
507 | { | ||
508 | #if defined(__powerpc__) | ||
509 | int size, i; | ||
510 | const uint32_t *bios; | ||
511 | struct device_node *dn = pci_device_to_OF_node(dev->pdev); | ||
512 | if (!dn) { | ||
513 | NV_INFO(dev, "Unable to get the OF node\n"); | ||
514 | return; | ||
515 | } | ||
516 | |||
517 | bios = of_get_property(dn, "NVDA,BMP", &size); | ||
518 | if (bios) { | ||
519 | for (i = 0; i < size; i += 4) | ||
520 | nv_wi32(dev, i, bios[i/4]); | ||
521 | NV_INFO(dev, "OF bios successfully copied (%d bytes)\n", size); | ||
522 | } else { | ||
523 | NV_INFO(dev, "Unable to get the OF bios\n"); | ||
524 | } | ||
525 | #endif | ||
526 | } | ||
527 | |||
528 | int nouveau_load(struct drm_device *dev, unsigned long flags) | ||
529 | { | ||
530 | struct drm_nouveau_private *dev_priv; | ||
531 | uint32_t reg0; | ||
532 | resource_size_t mmio_start_offs; | ||
533 | |||
534 | dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL); | ||
535 | if (!dev_priv) | ||
536 | return -ENOMEM; | ||
537 | dev->dev_private = dev_priv; | ||
538 | dev_priv->dev = dev; | ||
539 | |||
540 | dev_priv->flags = flags & NOUVEAU_FLAGS; | ||
541 | dev_priv->init_state = NOUVEAU_CARD_INIT_DOWN; | ||
542 | |||
543 | NV_DEBUG(dev, "vendor: 0x%X device: 0x%X class: 0x%X\n", | ||
544 | dev->pci_vendor, dev->pci_device, dev->pdev->class); | ||
545 | |||
546 | dev_priv->acpi_dsm = nouveau_dsm_probe(dev); | ||
547 | |||
548 | if (dev_priv->acpi_dsm) | ||
549 | nouveau_hybrid_setup(dev); | ||
550 | |||
551 | dev_priv->wq = create_workqueue("nouveau"); | ||
552 | if (!dev_priv->wq) | ||
553 | return -EINVAL; | ||
554 | |||
555 | /* resource 0 is mmio regs */ | ||
556 | /* resource 1 is linear FB */ | ||
557 | /* resource 2 is RAMIN (mmio regs + 0x1000000) */ | ||
558 | /* resource 6 is bios */ | ||
559 | |||
560 | /* map the mmio regs */ | ||
561 | mmio_start_offs = pci_resource_start(dev->pdev, 0); | ||
562 | dev_priv->mmio = ioremap(mmio_start_offs, 0x00800000); | ||
563 | if (!dev_priv->mmio) { | ||
564 | NV_ERROR(dev, "Unable to initialize the mmio mapping. " | ||
565 | "Please report your setup to " DRIVER_EMAIL "\n"); | ||
566 | return -EINVAL; | ||
567 | } | ||
568 | NV_DEBUG(dev, "regs mapped ok at 0x%llx\n", | ||
569 | (unsigned long long)mmio_start_offs); | ||
570 | |||
571 | #ifdef __BIG_ENDIAN | ||
572 | /* Put the card in BE mode if it's not */ | ||
573 | if (nv_rd32(dev, NV03_PMC_BOOT_1)) | ||
574 | nv_wr32(dev, NV03_PMC_BOOT_1, 0x00000001); | ||
575 | |||
576 | DRM_MEMORYBARRIER(); | ||
577 | #endif | ||
578 | |||
579 | /* Time to determine the card architecture */ | ||
580 | reg0 = nv_rd32(dev, NV03_PMC_BOOT_0); | ||
581 | |||
582 | /* We're dealing with >=NV10 */ | ||
583 | if ((reg0 & 0x0f000000) > 0) { | ||
584 | /* Bit 27-20 contain the architecture in hex */ | ||
585 | dev_priv->chipset = (reg0 & 0xff00000) >> 20; | ||
586 | /* NV04 or NV05 */ | ||
587 | } else if ((reg0 & 0xff00fff0) == 0x20004000) { | ||
588 | dev_priv->chipset = 0x04; | ||
589 | } else | ||
590 | dev_priv->chipset = 0xff; | ||
591 | |||
592 | switch (dev_priv->chipset & 0xf0) { | ||
593 | case 0x00: | ||
594 | case 0x10: | ||
595 | case 0x20: | ||
596 | case 0x30: | ||
597 | dev_priv->card_type = dev_priv->chipset & 0xf0; | ||
598 | break; | ||
599 | case 0x40: | ||
600 | case 0x60: | ||
601 | dev_priv->card_type = NV_40; | ||
602 | break; | ||
603 | case 0x50: | ||
604 | case 0x80: | ||
605 | case 0x90: | ||
606 | case 0xa0: | ||
607 | dev_priv->card_type = NV_50; | ||
608 | break; | ||
609 | default: | ||
610 | NV_INFO(dev, "Unsupported chipset 0x%08x\n", reg0); | ||
611 | return -EINVAL; | ||
612 | } | ||
613 | |||
614 | NV_INFO(dev, "Detected an NV%2x generation card (0x%08x)\n", | ||
615 | dev_priv->card_type, reg0); | ||
616 | |||
617 | /* map larger RAMIN aperture on NV40 cards */ | ||
618 | dev_priv->ramin = NULL; | ||
619 | if (dev_priv->card_type >= NV_40) { | ||
620 | int ramin_bar = 2; | ||
621 | if (pci_resource_len(dev->pdev, ramin_bar) == 0) | ||
622 | ramin_bar = 3; | ||
623 | |||
624 | dev_priv->ramin_size = pci_resource_len(dev->pdev, ramin_bar); | ||
625 | dev_priv->ramin = ioremap( | ||
626 | pci_resource_start(dev->pdev, ramin_bar), | ||
627 | dev_priv->ramin_size); | ||
628 | if (!dev_priv->ramin) { | ||
629 | NV_ERROR(dev, "Failed to init RAMIN mapping, " | ||
630 | "limited instance memory available\n"); | ||
631 | } | ||
632 | } | ||
633 | |||
634 | /* On older cards (or if the above failed), create a map covering | ||
635 | * the BAR0 PRAMIN aperture */ | ||
636 | if (!dev_priv->ramin) { | ||
637 | dev_priv->ramin_size = 1 * 1024 * 1024; | ||
638 | dev_priv->ramin = ioremap(mmio_start_offs + NV_RAMIN, | ||
639 | dev_priv->ramin_size); | ||
640 | if (!dev_priv->ramin) { | ||
641 | NV_ERROR(dev, "Failed to map BAR0 PRAMIN.\n"); | ||
642 | return -ENOMEM; | ||
643 | } | ||
644 | } | ||
645 | |||
646 | nouveau_OF_copy_vbios_to_ramin(dev); | ||
647 | |||
648 | /* Special flags */ | ||
649 | if (dev->pci_device == 0x01a0) | ||
650 | dev_priv->flags |= NV_NFORCE; | ||
651 | else if (dev->pci_device == 0x01f0) | ||
652 | dev_priv->flags |= NV_NFORCE2; | ||
653 | |||
654 | /* For kernel modesetting, init card now and bring up fbcon */ | ||
655 | if (drm_core_check_feature(dev, DRIVER_MODESET)) { | ||
656 | int ret = nouveau_card_init(dev); | ||
657 | if (ret) | ||
658 | return ret; | ||
659 | } | ||
660 | |||
661 | return 0; | ||
662 | } | ||
663 | |||
664 | static void nouveau_close(struct drm_device *dev) | ||
665 | { | ||
666 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
667 | |||
668 | /* In the case of an error dev_priv may not be be allocated yet */ | ||
669 | if (dev_priv && dev_priv->card_type) | ||
670 | nouveau_card_takedown(dev); | ||
671 | } | ||
672 | |||
673 | /* KMS: we need mmio at load time, not when the first drm client opens. */ | ||
674 | void nouveau_lastclose(struct drm_device *dev) | ||
675 | { | ||
676 | if (drm_core_check_feature(dev, DRIVER_MODESET)) | ||
677 | return; | ||
678 | |||
679 | nouveau_close(dev); | ||
680 | } | ||
681 | |||
682 | int nouveau_unload(struct drm_device *dev) | ||
683 | { | ||
684 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
685 | |||
686 | if (drm_core_check_feature(dev, DRIVER_MODESET)) { | ||
687 | if (dev_priv->card_type >= NV_50) | ||
688 | nv50_display_destroy(dev); | ||
689 | else | ||
690 | nv04_display_destroy(dev); | ||
691 | nouveau_close(dev); | ||
692 | } | ||
693 | |||
694 | iounmap(dev_priv->mmio); | ||
695 | iounmap(dev_priv->ramin); | ||
696 | |||
697 | kfree(dev_priv); | ||
698 | dev->dev_private = NULL; | ||
699 | return 0; | ||
700 | } | ||
701 | |||
702 | int | ||
703 | nouveau_ioctl_card_init(struct drm_device *dev, void *data, | ||
704 | struct drm_file *file_priv) | ||
705 | { | ||
706 | return nouveau_card_init(dev); | ||
707 | } | ||
708 | |||
709 | int nouveau_ioctl_getparam(struct drm_device *dev, void *data, | ||
710 | struct drm_file *file_priv) | ||
711 | { | ||
712 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
713 | struct drm_nouveau_getparam *getparam = data; | ||
714 | |||
715 | NOUVEAU_CHECK_INITIALISED_WITH_RETURN; | ||
716 | |||
717 | switch (getparam->param) { | ||
718 | case NOUVEAU_GETPARAM_CHIPSET_ID: | ||
719 | getparam->value = dev_priv->chipset; | ||
720 | break; | ||
721 | case NOUVEAU_GETPARAM_PCI_VENDOR: | ||
722 | getparam->value = dev->pci_vendor; | ||
723 | break; | ||
724 | case NOUVEAU_GETPARAM_PCI_DEVICE: | ||
725 | getparam->value = dev->pci_device; | ||
726 | break; | ||
727 | case NOUVEAU_GETPARAM_BUS_TYPE: | ||
728 | if (drm_device_is_agp(dev)) | ||
729 | getparam->value = NV_AGP; | ||
730 | else if (drm_device_is_pcie(dev)) | ||
731 | getparam->value = NV_PCIE; | ||
732 | else | ||
733 | getparam->value = NV_PCI; | ||
734 | break; | ||
735 | case NOUVEAU_GETPARAM_FB_PHYSICAL: | ||
736 | getparam->value = dev_priv->fb_phys; | ||
737 | break; | ||
738 | case NOUVEAU_GETPARAM_AGP_PHYSICAL: | ||
739 | getparam->value = dev_priv->gart_info.aper_base; | ||
740 | break; | ||
741 | case NOUVEAU_GETPARAM_PCI_PHYSICAL: | ||
742 | if (dev->sg) { | ||
743 | getparam->value = (unsigned long)dev->sg->virtual; | ||
744 | } else { | ||
745 | NV_ERROR(dev, "Requested PCIGART address, " | ||
746 | "while no PCIGART was created\n"); | ||
747 | return -EINVAL; | ||
748 | } | ||
749 | break; | ||
750 | case NOUVEAU_GETPARAM_FB_SIZE: | ||
751 | getparam->value = dev_priv->fb_available_size; | ||
752 | break; | ||
753 | case NOUVEAU_GETPARAM_AGP_SIZE: | ||
754 | getparam->value = dev_priv->gart_info.aper_size; | ||
755 | break; | ||
756 | case NOUVEAU_GETPARAM_VM_VRAM_BASE: | ||
757 | getparam->value = dev_priv->vm_vram_base; | ||
758 | break; | ||
759 | default: | ||
760 | NV_ERROR(dev, "unknown parameter %lld\n", getparam->param); | ||
761 | return -EINVAL; | ||
762 | } | ||
763 | |||
764 | return 0; | ||
765 | } | ||
766 | |||
767 | int | ||
768 | nouveau_ioctl_setparam(struct drm_device *dev, void *data, | ||
769 | struct drm_file *file_priv) | ||
770 | { | ||
771 | struct drm_nouveau_setparam *setparam = data; | ||
772 | |||
773 | NOUVEAU_CHECK_INITIALISED_WITH_RETURN; | ||
774 | |||
775 | switch (setparam->param) { | ||
776 | default: | ||
777 | NV_ERROR(dev, "unknown parameter %lld\n", setparam->param); | ||
778 | return -EINVAL; | ||
779 | } | ||
780 | |||
781 | return 0; | ||
782 | } | ||
783 | |||
784 | /* Wait until (value(reg) & mask) == val, up until timeout has hit */ | ||
785 | bool nouveau_wait_until(struct drm_device *dev, uint64_t timeout, | ||
786 | uint32_t reg, uint32_t mask, uint32_t val) | ||
787 | { | ||
788 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
789 | struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer; | ||
790 | uint64_t start = ptimer->read(dev); | ||
791 | |||
792 | do { | ||
793 | if ((nv_rd32(dev, reg) & mask) == val) | ||
794 | return true; | ||
795 | } while (ptimer->read(dev) - start < timeout); | ||
796 | |||
797 | return false; | ||
798 | } | ||
799 | |||
800 | /* Waits for PGRAPH to go completely idle */ | ||
801 | bool nouveau_wait_for_idle(struct drm_device *dev) | ||
802 | { | ||
803 | if (!nv_wait(NV04_PGRAPH_STATUS, 0xffffffff, 0x00000000)) { | ||
804 | NV_ERROR(dev, "PGRAPH idle timed out with status 0x%08x\n", | ||
805 | nv_rd32(dev, NV04_PGRAPH_STATUS)); | ||
806 | return false; | ||
807 | } | ||
808 | |||
809 | return true; | ||
810 | } | ||
811 | |||
diff --git a/drivers/gpu/drm/nouveau/nouveau_ttm.c b/drivers/gpu/drm/nouveau/nouveau_ttm.c new file mode 100644 index 000000000000..187eb84e4da5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_ttm.c | |||
@@ -0,0 +1,131 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2007-2008 Tungsten Graphics, Inc., Cedar Park, TX., USA, | ||
3 | * All Rights Reserved. | ||
4 | * Copyright (c) 2009 VMware, Inc., Palo Alto, CA., USA, | ||
5 | * All Rights Reserved. | ||
6 | * | ||
7 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
8 | * copy of this software and associated documentation files (the "Software"), | ||
9 | * to deal in the Software without restriction, including without limitation | ||
10 | * the rights to use, copy, modify, merge, publish, distribute, sub license, | ||
11 | * and/or sell copies of the Software, and to permit persons to whom the | ||
12 | * Software is furnished to do so, subject to the following conditions: | ||
13 | * | ||
14 | * The above copyright notice and this permission notice (including the | ||
15 | * next paragraph) shall be included in all copies or substantial portions | ||
16 | * of the Software. | ||
17 | * | ||
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
20 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL | ||
21 | * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, | ||
22 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | ||
23 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | ||
24 | * USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
25 | */ | ||
26 | |||
27 | #include "drmP.h" | ||
28 | |||
29 | #include "nouveau_drv.h" | ||
30 | |||
31 | static struct vm_operations_struct nouveau_ttm_vm_ops; | ||
32 | static const struct vm_operations_struct *ttm_vm_ops; | ||
33 | |||
34 | static int | ||
35 | nouveau_ttm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) | ||
36 | { | ||
37 | struct ttm_buffer_object *bo = vma->vm_private_data; | ||
38 | int ret; | ||
39 | |||
40 | if (unlikely(bo == NULL)) | ||
41 | return VM_FAULT_NOPAGE; | ||
42 | |||
43 | ret = ttm_vm_ops->fault(vma, vmf); | ||
44 | return ret; | ||
45 | } | ||
46 | |||
47 | int | ||
48 | nouveau_ttm_mmap(struct file *filp, struct vm_area_struct *vma) | ||
49 | { | ||
50 | struct drm_file *file_priv = filp->private_data; | ||
51 | struct drm_nouveau_private *dev_priv = | ||
52 | file_priv->minor->dev->dev_private; | ||
53 | int ret; | ||
54 | |||
55 | if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET)) | ||
56 | return drm_mmap(filp, vma); | ||
57 | |||
58 | ret = ttm_bo_mmap(filp, vma, &dev_priv->ttm.bdev); | ||
59 | if (unlikely(ret != 0)) | ||
60 | return ret; | ||
61 | |||
62 | if (unlikely(ttm_vm_ops == NULL)) { | ||
63 | ttm_vm_ops = vma->vm_ops; | ||
64 | nouveau_ttm_vm_ops = *ttm_vm_ops; | ||
65 | nouveau_ttm_vm_ops.fault = &nouveau_ttm_fault; | ||
66 | } | ||
67 | |||
68 | vma->vm_ops = &nouveau_ttm_vm_ops; | ||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | static int | ||
73 | nouveau_ttm_mem_global_init(struct ttm_global_reference *ref) | ||
74 | { | ||
75 | return ttm_mem_global_init(ref->object); | ||
76 | } | ||
77 | |||
78 | static void | ||
79 | nouveau_ttm_mem_global_release(struct ttm_global_reference *ref) | ||
80 | { | ||
81 | ttm_mem_global_release(ref->object); | ||
82 | } | ||
83 | |||
84 | int | ||
85 | nouveau_ttm_global_init(struct drm_nouveau_private *dev_priv) | ||
86 | { | ||
87 | struct ttm_global_reference *global_ref; | ||
88 | int ret; | ||
89 | |||
90 | global_ref = &dev_priv->ttm.mem_global_ref; | ||
91 | global_ref->global_type = TTM_GLOBAL_TTM_MEM; | ||
92 | global_ref->size = sizeof(struct ttm_mem_global); | ||
93 | global_ref->init = &nouveau_ttm_mem_global_init; | ||
94 | global_ref->release = &nouveau_ttm_mem_global_release; | ||
95 | |||
96 | ret = ttm_global_item_ref(global_ref); | ||
97 | if (unlikely(ret != 0)) { | ||
98 | DRM_ERROR("Failed setting up TTM memory accounting\n"); | ||
99 | dev_priv->ttm.mem_global_ref.release = NULL; | ||
100 | return ret; | ||
101 | } | ||
102 | |||
103 | dev_priv->ttm.bo_global_ref.mem_glob = global_ref->object; | ||
104 | global_ref = &dev_priv->ttm.bo_global_ref.ref; | ||
105 | global_ref->global_type = TTM_GLOBAL_TTM_BO; | ||
106 | global_ref->size = sizeof(struct ttm_bo_global); | ||
107 | global_ref->init = &ttm_bo_global_init; | ||
108 | global_ref->release = &ttm_bo_global_release; | ||
109 | |||
110 | ret = ttm_global_item_ref(global_ref); | ||
111 | if (unlikely(ret != 0)) { | ||
112 | DRM_ERROR("Failed setting up TTM BO subsystem\n"); | ||
113 | ttm_global_item_unref(&dev_priv->ttm.mem_global_ref); | ||
114 | dev_priv->ttm.mem_global_ref.release = NULL; | ||
115 | return ret; | ||
116 | } | ||
117 | |||
118 | return 0; | ||
119 | } | ||
120 | |||
121 | void | ||
122 | nouveau_ttm_global_release(struct drm_nouveau_private *dev_priv) | ||
123 | { | ||
124 | if (dev_priv->ttm.mem_global_ref.release == NULL) | ||
125 | return; | ||
126 | |||
127 | ttm_global_item_unref(&dev_priv->ttm.bo_global_ref.ref); | ||
128 | ttm_global_item_unref(&dev_priv->ttm.mem_global_ref); | ||
129 | dev_priv->ttm.mem_global_ref.release = NULL; | ||
130 | } | ||
131 | |||
diff --git a/drivers/gpu/drm/nouveau/nv04_crtc.c b/drivers/gpu/drm/nouveau/nv04_crtc.c new file mode 100644 index 000000000000..b91363606055 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv04_crtc.c | |||
@@ -0,0 +1,1002 @@ | |||
1 | /* | ||
2 | * Copyright 1993-2003 NVIDIA, Corporation | ||
3 | * Copyright 2006 Dave Airlie | ||
4 | * Copyright 2007 Maarten Maathuis | ||
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 (including the next | ||
14 | * paragraph) shall be included in all copies or substantial portions of the | ||
15 | * Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
22 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
23 | * DEALINGS IN THE SOFTWARE. | ||
24 | */ | ||
25 | |||
26 | #include "drmP.h" | ||
27 | #include "drm_crtc_helper.h" | ||
28 | |||
29 | #include "nouveau_drv.h" | ||
30 | #include "nouveau_encoder.h" | ||
31 | #include "nouveau_connector.h" | ||
32 | #include "nouveau_crtc.h" | ||
33 | #include "nouveau_fb.h" | ||
34 | #include "nouveau_hw.h" | ||
35 | #include "nvreg.h" | ||
36 | |||
37 | static int | ||
38 | nv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, | ||
39 | struct drm_framebuffer *old_fb); | ||
40 | |||
41 | static void | ||
42 | crtc_wr_cio_state(struct drm_crtc *crtc, struct nv04_crtc_reg *crtcstate, int index) | ||
43 | { | ||
44 | NVWriteVgaCrtc(crtc->dev, nouveau_crtc(crtc)->index, index, | ||
45 | crtcstate->CRTC[index]); | ||
46 | } | ||
47 | |||
48 | static void nv_crtc_set_digital_vibrance(struct drm_crtc *crtc, int level) | ||
49 | { | ||
50 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | ||
51 | struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; | ||
52 | struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index]; | ||
53 | |||
54 | regp->CRTC[NV_CIO_CRE_CSB] = nv_crtc->saturation = level; | ||
55 | if (nv_crtc->saturation && nv_gf4_disp_arch(crtc->dev)) { | ||
56 | regp->CRTC[NV_CIO_CRE_CSB] = 0x80; | ||
57 | regp->CRTC[NV_CIO_CRE_5B] = nv_crtc->saturation << 2; | ||
58 | crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_5B); | ||
59 | } | ||
60 | crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_CSB); | ||
61 | } | ||
62 | |||
63 | static void nv_crtc_set_image_sharpening(struct drm_crtc *crtc, int level) | ||
64 | { | ||
65 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | ||
66 | struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; | ||
67 | struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index]; | ||
68 | |||
69 | nv_crtc->sharpness = level; | ||
70 | if (level < 0) /* blur is in hw range 0x3f -> 0x20 */ | ||
71 | level += 0x40; | ||
72 | regp->ramdac_634 = level; | ||
73 | NVWriteRAMDAC(crtc->dev, nv_crtc->index, NV_PRAMDAC_634, regp->ramdac_634); | ||
74 | } | ||
75 | |||
76 | #define PLLSEL_VPLL1_MASK \ | ||
77 | (NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_VPLL \ | ||
78 | | NV_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO_DB2) | ||
79 | #define PLLSEL_VPLL2_MASK \ | ||
80 | (NV_PRAMDAC_PLL_COEFF_SELECT_PLL_SOURCE_VPLL2 \ | ||
81 | | NV_PRAMDAC_PLL_COEFF_SELECT_VCLK2_RATIO_DB2) | ||
82 | #define PLLSEL_TV_MASK \ | ||
83 | (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 \ | ||
84 | | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1 \ | ||
85 | | NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2 \ | ||
86 | | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2) | ||
87 | |||
88 | /* NV4x 0x40.. pll notes: | ||
89 | * gpu pll: 0x4000 + 0x4004 | ||
90 | * ?gpu? pll: 0x4008 + 0x400c | ||
91 | * vpll1: 0x4010 + 0x4014 | ||
92 | * vpll2: 0x4018 + 0x401c | ||
93 | * mpll: 0x4020 + 0x4024 | ||
94 | * mpll: 0x4038 + 0x403c | ||
95 | * | ||
96 | * the first register of each pair has some unknown details: | ||
97 | * bits 0-7: redirected values from elsewhere? (similar to PLL_SETUP_CONTROL?) | ||
98 | * bits 20-23: (mpll) something to do with post divider? | ||
99 | * bits 28-31: related to single stage mode? (bit 8/12) | ||
100 | */ | ||
101 | |||
102 | static void nv_crtc_calc_state_ext(struct drm_crtc *crtc, struct drm_display_mode * mode, int dot_clock) | ||
103 | { | ||
104 | struct drm_device *dev = crtc->dev; | ||
105 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
106 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | ||
107 | struct nv04_mode_state *state = &dev_priv->mode_reg; | ||
108 | struct nv04_crtc_reg *regp = &state->crtc_reg[nv_crtc->index]; | ||
109 | struct nouveau_pll_vals *pv = ®p->pllvals; | ||
110 | struct pll_lims pll_lim; | ||
111 | |||
112 | if (get_pll_limits(dev, nv_crtc->index ? VPLL2 : VPLL1, &pll_lim)) | ||
113 | return; | ||
114 | |||
115 | /* NM2 == 0 is used to determine single stage mode on two stage plls */ | ||
116 | pv->NM2 = 0; | ||
117 | |||
118 | /* for newer nv4x the blob uses only the first stage of the vpll below a | ||
119 | * certain clock. for a certain nv4b this is 150MHz. since the max | ||
120 | * output frequency of the first stage for this card is 300MHz, it is | ||
121 | * assumed the threshold is given by vco1 maxfreq/2 | ||
122 | */ | ||
123 | /* for early nv4x, specifically nv40 and *some* nv43 (devids 0 and 6, | ||
124 | * not 8, others unknown), the blob always uses both plls. no problem | ||
125 | * has yet been observed in allowing the use a single stage pll on all | ||
126 | * nv43 however. the behaviour of single stage use is untested on nv40 | ||
127 | */ | ||
128 | if (dev_priv->chipset > 0x40 && dot_clock <= (pll_lim.vco1.maxfreq / 2)) | ||
129 | memset(&pll_lim.vco2, 0, sizeof(pll_lim.vco2)); | ||
130 | |||
131 | if (!nouveau_calc_pll_mnp(dev, &pll_lim, dot_clock, pv)) | ||
132 | return; | ||
133 | |||
134 | state->pllsel &= PLLSEL_VPLL1_MASK | PLLSEL_VPLL2_MASK | PLLSEL_TV_MASK; | ||
135 | |||
136 | /* The blob uses this always, so let's do the same */ | ||
137 | if (dev_priv->card_type == NV_40) | ||
138 | state->pllsel |= NV_PRAMDAC_PLL_COEFF_SELECT_USE_VPLL2_TRUE; | ||
139 | /* again nv40 and some nv43 act more like nv3x as described above */ | ||
140 | if (dev_priv->chipset < 0x41) | ||
141 | state->pllsel |= NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_MPLL | | ||
142 | NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_NVPLL; | ||
143 | state->pllsel |= nv_crtc->index ? PLLSEL_VPLL2_MASK : PLLSEL_VPLL1_MASK; | ||
144 | |||
145 | if (pv->NM2) | ||
146 | NV_TRACE(dev, "vpll: n1 %d n2 %d m1 %d m2 %d log2p %d\n", | ||
147 | pv->N1, pv->N2, pv->M1, pv->M2, pv->log2P); | ||
148 | else | ||
149 | NV_TRACE(dev, "vpll: n %d m %d log2p %d\n", | ||
150 | pv->N1, pv->M1, pv->log2P); | ||
151 | |||
152 | nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.offset); | ||
153 | } | ||
154 | |||
155 | static void | ||
156 | nv_crtc_dpms(struct drm_crtc *crtc, int mode) | ||
157 | { | ||
158 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | ||
159 | struct drm_device *dev = crtc->dev; | ||
160 | unsigned char seq1 = 0, crtc17 = 0; | ||
161 | unsigned char crtc1A; | ||
162 | |||
163 | NV_TRACE(dev, "Setting dpms mode %d on CRTC %d\n", mode, | ||
164 | nv_crtc->index); | ||
165 | |||
166 | if (nv_crtc->last_dpms == mode) /* Don't do unnecesary mode changes. */ | ||
167 | return; | ||
168 | |||
169 | nv_crtc->last_dpms = mode; | ||
170 | |||
171 | if (nv_two_heads(dev)) | ||
172 | NVSetOwner(dev, nv_crtc->index); | ||
173 | |||
174 | /* nv4ref indicates these two RPC1 bits inhibit h/v sync */ | ||
175 | crtc1A = NVReadVgaCrtc(dev, nv_crtc->index, | ||
176 | NV_CIO_CRE_RPC1_INDEX) & ~0xC0; | ||
177 | switch (mode) { | ||
178 | case DRM_MODE_DPMS_STANDBY: | ||
179 | /* Screen: Off; HSync: Off, VSync: On -- Not Supported */ | ||
180 | seq1 = 0x20; | ||
181 | crtc17 = 0x80; | ||
182 | crtc1A |= 0x80; | ||
183 | break; | ||
184 | case DRM_MODE_DPMS_SUSPEND: | ||
185 | /* Screen: Off; HSync: On, VSync: Off -- Not Supported */ | ||
186 | seq1 = 0x20; | ||
187 | crtc17 = 0x80; | ||
188 | crtc1A |= 0x40; | ||
189 | break; | ||
190 | case DRM_MODE_DPMS_OFF: | ||
191 | /* Screen: Off; HSync: Off, VSync: Off */ | ||
192 | seq1 = 0x20; | ||
193 | crtc17 = 0x00; | ||
194 | crtc1A |= 0xC0; | ||
195 | break; | ||
196 | case DRM_MODE_DPMS_ON: | ||
197 | default: | ||
198 | /* Screen: On; HSync: On, VSync: On */ | ||
199 | seq1 = 0x00; | ||
200 | crtc17 = 0x80; | ||
201 | break; | ||
202 | } | ||
203 | |||
204 | NVVgaSeqReset(dev, nv_crtc->index, true); | ||
205 | /* Each head has it's own sequencer, so we can turn it off when we want */ | ||
206 | seq1 |= (NVReadVgaSeq(dev, nv_crtc->index, NV_VIO_SR_CLOCK_INDEX) & ~0x20); | ||
207 | NVWriteVgaSeq(dev, nv_crtc->index, NV_VIO_SR_CLOCK_INDEX, seq1); | ||
208 | crtc17 |= (NVReadVgaCrtc(dev, nv_crtc->index, NV_CIO_CR_MODE_INDEX) & ~0x80); | ||
209 | mdelay(10); | ||
210 | NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CR_MODE_INDEX, crtc17); | ||
211 | NVVgaSeqReset(dev, nv_crtc->index, false); | ||
212 | |||
213 | NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RPC1_INDEX, crtc1A); | ||
214 | } | ||
215 | |||
216 | static bool | ||
217 | nv_crtc_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode *mode, | ||
218 | struct drm_display_mode *adjusted_mode) | ||
219 | { | ||
220 | return true; | ||
221 | } | ||
222 | |||
223 | static void | ||
224 | nv_crtc_mode_set_vga(struct drm_crtc *crtc, struct drm_display_mode *mode) | ||
225 | { | ||
226 | struct drm_device *dev = crtc->dev; | ||
227 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
228 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | ||
229 | struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index]; | ||
230 | struct drm_framebuffer *fb = crtc->fb; | ||
231 | |||
232 | /* Calculate our timings */ | ||
233 | int horizDisplay = (mode->crtc_hdisplay >> 3) - 1; | ||
234 | int horizStart = (mode->crtc_hsync_start >> 3) - 1; | ||
235 | int horizEnd = (mode->crtc_hsync_end >> 3) - 1; | ||
236 | int horizTotal = (mode->crtc_htotal >> 3) - 5; | ||
237 | int horizBlankStart = (mode->crtc_hdisplay >> 3) - 1; | ||
238 | int horizBlankEnd = (mode->crtc_htotal >> 3) - 1; | ||
239 | int vertDisplay = mode->crtc_vdisplay - 1; | ||
240 | int vertStart = mode->crtc_vsync_start - 1; | ||
241 | int vertEnd = mode->crtc_vsync_end - 1; | ||
242 | int vertTotal = mode->crtc_vtotal - 2; | ||
243 | int vertBlankStart = mode->crtc_vdisplay - 1; | ||
244 | int vertBlankEnd = mode->crtc_vtotal - 1; | ||
245 | |||
246 | struct drm_encoder *encoder; | ||
247 | bool fp_output = false; | ||
248 | |||
249 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | ||
250 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
251 | |||
252 | if (encoder->crtc == crtc && | ||
253 | (nv_encoder->dcb->type == OUTPUT_LVDS || | ||
254 | nv_encoder->dcb->type == OUTPUT_TMDS)) | ||
255 | fp_output = true; | ||
256 | } | ||
257 | |||
258 | if (fp_output) { | ||
259 | vertStart = vertTotal - 3; | ||
260 | vertEnd = vertTotal - 2; | ||
261 | vertBlankStart = vertStart; | ||
262 | horizStart = horizTotal - 5; | ||
263 | horizEnd = horizTotal - 2; | ||
264 | horizBlankEnd = horizTotal + 4; | ||
265 | #if 0 | ||
266 | if (dev->overlayAdaptor && dev_priv->card_type >= NV_10) | ||
267 | /* This reportedly works around some video overlay bandwidth problems */ | ||
268 | horizTotal += 2; | ||
269 | #endif | ||
270 | } | ||
271 | |||
272 | if (mode->flags & DRM_MODE_FLAG_INTERLACE) | ||
273 | vertTotal |= 1; | ||
274 | |||
275 | #if 0 | ||
276 | ErrorF("horizDisplay: 0x%X \n", horizDisplay); | ||
277 | ErrorF("horizStart: 0x%X \n", horizStart); | ||
278 | ErrorF("horizEnd: 0x%X \n", horizEnd); | ||
279 | ErrorF("horizTotal: 0x%X \n", horizTotal); | ||
280 | ErrorF("horizBlankStart: 0x%X \n", horizBlankStart); | ||
281 | ErrorF("horizBlankEnd: 0x%X \n", horizBlankEnd); | ||
282 | ErrorF("vertDisplay: 0x%X \n", vertDisplay); | ||
283 | ErrorF("vertStart: 0x%X \n", vertStart); | ||
284 | ErrorF("vertEnd: 0x%X \n", vertEnd); | ||
285 | ErrorF("vertTotal: 0x%X \n", vertTotal); | ||
286 | ErrorF("vertBlankStart: 0x%X \n", vertBlankStart); | ||
287 | ErrorF("vertBlankEnd: 0x%X \n", vertBlankEnd); | ||
288 | #endif | ||
289 | |||
290 | /* | ||
291 | * compute correct Hsync & Vsync polarity | ||
292 | */ | ||
293 | if ((mode->flags & (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NHSYNC)) | ||
294 | && (mode->flags & (DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC))) { | ||
295 | |||
296 | regp->MiscOutReg = 0x23; | ||
297 | if (mode->flags & DRM_MODE_FLAG_NHSYNC) | ||
298 | regp->MiscOutReg |= 0x40; | ||
299 | if (mode->flags & DRM_MODE_FLAG_NVSYNC) | ||
300 | regp->MiscOutReg |= 0x80; | ||
301 | } else { | ||
302 | int vdisplay = mode->vdisplay; | ||
303 | if (mode->flags & DRM_MODE_FLAG_DBLSCAN) | ||
304 | vdisplay *= 2; | ||
305 | if (mode->vscan > 1) | ||
306 | vdisplay *= mode->vscan; | ||
307 | if (vdisplay < 400) | ||
308 | regp->MiscOutReg = 0xA3; /* +hsync -vsync */ | ||
309 | else if (vdisplay < 480) | ||
310 | regp->MiscOutReg = 0x63; /* -hsync +vsync */ | ||
311 | else if (vdisplay < 768) | ||
312 | regp->MiscOutReg = 0xE3; /* -hsync -vsync */ | ||
313 | else | ||
314 | regp->MiscOutReg = 0x23; /* +hsync +vsync */ | ||
315 | } | ||
316 | |||
317 | regp->MiscOutReg |= (mode->clock_index & 0x03) << 2; | ||
318 | |||
319 | /* | ||
320 | * Time Sequencer | ||
321 | */ | ||
322 | regp->Sequencer[NV_VIO_SR_RESET_INDEX] = 0x00; | ||
323 | /* 0x20 disables the sequencer */ | ||
324 | if (mode->flags & DRM_MODE_FLAG_CLKDIV2) | ||
325 | regp->Sequencer[NV_VIO_SR_CLOCK_INDEX] = 0x29; | ||
326 | else | ||
327 | regp->Sequencer[NV_VIO_SR_CLOCK_INDEX] = 0x21; | ||
328 | regp->Sequencer[NV_VIO_SR_PLANE_MASK_INDEX] = 0x0F; | ||
329 | regp->Sequencer[NV_VIO_SR_CHAR_MAP_INDEX] = 0x00; | ||
330 | regp->Sequencer[NV_VIO_SR_MEM_MODE_INDEX] = 0x0E; | ||
331 | |||
332 | /* | ||
333 | * CRTC | ||
334 | */ | ||
335 | regp->CRTC[NV_CIO_CR_HDT_INDEX] = horizTotal; | ||
336 | regp->CRTC[NV_CIO_CR_HDE_INDEX] = horizDisplay; | ||
337 | regp->CRTC[NV_CIO_CR_HBS_INDEX] = horizBlankStart; | ||
338 | regp->CRTC[NV_CIO_CR_HBE_INDEX] = (1 << 7) | | ||
339 | XLATE(horizBlankEnd, 0, NV_CIO_CR_HBE_4_0); | ||
340 | regp->CRTC[NV_CIO_CR_HRS_INDEX] = horizStart; | ||
341 | regp->CRTC[NV_CIO_CR_HRE_INDEX] = XLATE(horizBlankEnd, 5, NV_CIO_CR_HRE_HBE_5) | | ||
342 | XLATE(horizEnd, 0, NV_CIO_CR_HRE_4_0); | ||
343 | regp->CRTC[NV_CIO_CR_VDT_INDEX] = vertTotal; | ||
344 | regp->CRTC[NV_CIO_CR_OVL_INDEX] = XLATE(vertStart, 9, NV_CIO_CR_OVL_VRS_9) | | ||
345 | XLATE(vertDisplay, 9, NV_CIO_CR_OVL_VDE_9) | | ||
346 | XLATE(vertTotal, 9, NV_CIO_CR_OVL_VDT_9) | | ||
347 | (1 << 4) | | ||
348 | XLATE(vertBlankStart, 8, NV_CIO_CR_OVL_VBS_8) | | ||
349 | XLATE(vertStart, 8, NV_CIO_CR_OVL_VRS_8) | | ||
350 | XLATE(vertDisplay, 8, NV_CIO_CR_OVL_VDE_8) | | ||
351 | XLATE(vertTotal, 8, NV_CIO_CR_OVL_VDT_8); | ||
352 | regp->CRTC[NV_CIO_CR_RSAL_INDEX] = 0x00; | ||
353 | regp->CRTC[NV_CIO_CR_CELL_HT_INDEX] = ((mode->flags & DRM_MODE_FLAG_DBLSCAN) ? MASK(NV_CIO_CR_CELL_HT_SCANDBL) : 0) | | ||
354 | 1 << 6 | | ||
355 | XLATE(vertBlankStart, 9, NV_CIO_CR_CELL_HT_VBS_9); | ||
356 | regp->CRTC[NV_CIO_CR_CURS_ST_INDEX] = 0x00; | ||
357 | regp->CRTC[NV_CIO_CR_CURS_END_INDEX] = 0x00; | ||
358 | regp->CRTC[NV_CIO_CR_SA_HI_INDEX] = 0x00; | ||
359 | regp->CRTC[NV_CIO_CR_SA_LO_INDEX] = 0x00; | ||
360 | regp->CRTC[NV_CIO_CR_TCOFF_HI_INDEX] = 0x00; | ||
361 | regp->CRTC[NV_CIO_CR_TCOFF_LO_INDEX] = 0x00; | ||
362 | regp->CRTC[NV_CIO_CR_VRS_INDEX] = vertStart; | ||
363 | regp->CRTC[NV_CIO_CR_VRE_INDEX] = 1 << 5 | XLATE(vertEnd, 0, NV_CIO_CR_VRE_3_0); | ||
364 | regp->CRTC[NV_CIO_CR_VDE_INDEX] = vertDisplay; | ||
365 | /* framebuffer can be larger than crtc scanout area. */ | ||
366 | regp->CRTC[NV_CIO_CR_OFFSET_INDEX] = fb->pitch / 8; | ||
367 | regp->CRTC[NV_CIO_CR_ULINE_INDEX] = 0x00; | ||
368 | regp->CRTC[NV_CIO_CR_VBS_INDEX] = vertBlankStart; | ||
369 | regp->CRTC[NV_CIO_CR_VBE_INDEX] = vertBlankEnd; | ||
370 | regp->CRTC[NV_CIO_CR_MODE_INDEX] = 0x43; | ||
371 | regp->CRTC[NV_CIO_CR_LCOMP_INDEX] = 0xff; | ||
372 | |||
373 | /* | ||
374 | * Some extended CRTC registers (they are not saved with the rest of the vga regs). | ||
375 | */ | ||
376 | |||
377 | /* framebuffer can be larger than crtc scanout area. */ | ||
378 | regp->CRTC[NV_CIO_CRE_RPC0_INDEX] = XLATE(fb->pitch / 8, 8, NV_CIO_CRE_RPC0_OFFSET_10_8); | ||
379 | regp->CRTC[NV_CIO_CRE_RPC1_INDEX] = mode->crtc_hdisplay < 1280 ? | ||
380 | MASK(NV_CIO_CRE_RPC1_LARGE) : 0x00; | ||
381 | regp->CRTC[NV_CIO_CRE_LSR_INDEX] = XLATE(horizBlankEnd, 6, NV_CIO_CRE_LSR_HBE_6) | | ||
382 | XLATE(vertBlankStart, 10, NV_CIO_CRE_LSR_VBS_10) | | ||
383 | XLATE(vertStart, 10, NV_CIO_CRE_LSR_VRS_10) | | ||
384 | XLATE(vertDisplay, 10, NV_CIO_CRE_LSR_VDE_10) | | ||
385 | XLATE(vertTotal, 10, NV_CIO_CRE_LSR_VDT_10); | ||
386 | regp->CRTC[NV_CIO_CRE_HEB__INDEX] = XLATE(horizStart, 8, NV_CIO_CRE_HEB_HRS_8) | | ||
387 | XLATE(horizBlankStart, 8, NV_CIO_CRE_HEB_HBS_8) | | ||
388 | XLATE(horizDisplay, 8, NV_CIO_CRE_HEB_HDE_8) | | ||
389 | XLATE(horizTotal, 8, NV_CIO_CRE_HEB_HDT_8); | ||
390 | regp->CRTC[NV_CIO_CRE_EBR_INDEX] = XLATE(vertBlankStart, 11, NV_CIO_CRE_EBR_VBS_11) | | ||
391 | XLATE(vertStart, 11, NV_CIO_CRE_EBR_VRS_11) | | ||
392 | XLATE(vertDisplay, 11, NV_CIO_CRE_EBR_VDE_11) | | ||
393 | XLATE(vertTotal, 11, NV_CIO_CRE_EBR_VDT_11); | ||
394 | |||
395 | if (mode->flags & DRM_MODE_FLAG_INTERLACE) { | ||
396 | horizTotal = (horizTotal >> 1) & ~1; | ||
397 | regp->CRTC[NV_CIO_CRE_ILACE__INDEX] = horizTotal; | ||
398 | regp->CRTC[NV_CIO_CRE_HEB__INDEX] |= XLATE(horizTotal, 8, NV_CIO_CRE_HEB_ILC_8); | ||
399 | } else | ||
400 | regp->CRTC[NV_CIO_CRE_ILACE__INDEX] = 0xff; /* interlace off */ | ||
401 | |||
402 | /* | ||
403 | * Graphics Display Controller | ||
404 | */ | ||
405 | regp->Graphics[NV_VIO_GX_SR_INDEX] = 0x00; | ||
406 | regp->Graphics[NV_VIO_GX_SREN_INDEX] = 0x00; | ||
407 | regp->Graphics[NV_VIO_GX_CCOMP_INDEX] = 0x00; | ||
408 | regp->Graphics[NV_VIO_GX_ROP_INDEX] = 0x00; | ||
409 | regp->Graphics[NV_VIO_GX_READ_MAP_INDEX] = 0x00; | ||
410 | regp->Graphics[NV_VIO_GX_MODE_INDEX] = 0x40; /* 256 color mode */ | ||
411 | regp->Graphics[NV_VIO_GX_MISC_INDEX] = 0x05; /* map 64k mem + graphic mode */ | ||
412 | regp->Graphics[NV_VIO_GX_DONT_CARE_INDEX] = 0x0F; | ||
413 | regp->Graphics[NV_VIO_GX_BIT_MASK_INDEX] = 0xFF; | ||
414 | |||
415 | regp->Attribute[0] = 0x00; /* standard colormap translation */ | ||
416 | regp->Attribute[1] = 0x01; | ||
417 | regp->Attribute[2] = 0x02; | ||
418 | regp->Attribute[3] = 0x03; | ||
419 | regp->Attribute[4] = 0x04; | ||
420 | regp->Attribute[5] = 0x05; | ||
421 | regp->Attribute[6] = 0x06; | ||
422 | regp->Attribute[7] = 0x07; | ||
423 | regp->Attribute[8] = 0x08; | ||
424 | regp->Attribute[9] = 0x09; | ||
425 | regp->Attribute[10] = 0x0A; | ||
426 | regp->Attribute[11] = 0x0B; | ||
427 | regp->Attribute[12] = 0x0C; | ||
428 | regp->Attribute[13] = 0x0D; | ||
429 | regp->Attribute[14] = 0x0E; | ||
430 | regp->Attribute[15] = 0x0F; | ||
431 | regp->Attribute[NV_CIO_AR_MODE_INDEX] = 0x01; /* Enable graphic mode */ | ||
432 | /* Non-vga */ | ||
433 | regp->Attribute[NV_CIO_AR_OSCAN_INDEX] = 0x00; | ||
434 | regp->Attribute[NV_CIO_AR_PLANE_INDEX] = 0x0F; /* enable all color planes */ | ||
435 | regp->Attribute[NV_CIO_AR_HPP_INDEX] = 0x00; | ||
436 | regp->Attribute[NV_CIO_AR_CSEL_INDEX] = 0x00; | ||
437 | } | ||
438 | |||
439 | /** | ||
440 | * Sets up registers for the given mode/adjusted_mode pair. | ||
441 | * | ||
442 | * The clocks, CRTCs and outputs attached to this CRTC must be off. | ||
443 | * | ||
444 | * This shouldn't enable any clocks, CRTCs, or outputs, but they should | ||
445 | * be easily turned on/off after this. | ||
446 | */ | ||
447 | static void | ||
448 | nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode) | ||
449 | { | ||
450 | struct drm_device *dev = crtc->dev; | ||
451 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
452 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | ||
453 | struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index]; | ||
454 | struct nv04_crtc_reg *savep = &dev_priv->saved_reg.crtc_reg[nv_crtc->index]; | ||
455 | struct drm_encoder *encoder; | ||
456 | bool lvds_output = false, tmds_output = false, tv_output = false, | ||
457 | off_chip_digital = false; | ||
458 | |||
459 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | ||
460 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
461 | bool digital = false; | ||
462 | |||
463 | if (encoder->crtc != crtc) | ||
464 | continue; | ||
465 | |||
466 | if (nv_encoder->dcb->type == OUTPUT_LVDS) | ||
467 | digital = lvds_output = true; | ||
468 | if (nv_encoder->dcb->type == OUTPUT_TV) | ||
469 | tv_output = true; | ||
470 | if (nv_encoder->dcb->type == OUTPUT_TMDS) | ||
471 | digital = tmds_output = true; | ||
472 | if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP && digital) | ||
473 | off_chip_digital = true; | ||
474 | } | ||
475 | |||
476 | /* Registers not directly related to the (s)vga mode */ | ||
477 | |||
478 | /* What is the meaning of this register? */ | ||
479 | /* A few popular values are 0x18, 0x1c, 0x38, 0x3c */ | ||
480 | regp->CRTC[NV_CIO_CRE_ENH_INDEX] = savep->CRTC[NV_CIO_CRE_ENH_INDEX] & ~(1<<5); | ||
481 | |||
482 | regp->crtc_eng_ctrl = 0; | ||
483 | /* Except for rare conditions I2C is enabled on the primary crtc */ | ||
484 | if (nv_crtc->index == 0) | ||
485 | regp->crtc_eng_ctrl |= NV_CRTC_FSEL_I2C; | ||
486 | #if 0 | ||
487 | /* Set overlay to desired crtc. */ | ||
488 | if (dev->overlayAdaptor) { | ||
489 | NVPortPrivPtr pPriv = GET_OVERLAY_PRIVATE(dev); | ||
490 | if (pPriv->overlayCRTC == nv_crtc->index) | ||
491 | regp->crtc_eng_ctrl |= NV_CRTC_FSEL_OVERLAY; | ||
492 | } | ||
493 | #endif | ||
494 | |||
495 | /* ADDRESS_SPACE_PNVM is the same as setting HCUR_ASI */ | ||
496 | regp->cursor_cfg = NV_PCRTC_CURSOR_CONFIG_CUR_LINES_64 | | ||
497 | NV_PCRTC_CURSOR_CONFIG_CUR_PIXELS_64 | | ||
498 | NV_PCRTC_CURSOR_CONFIG_ADDRESS_SPACE_PNVM; | ||
499 | if (dev_priv->chipset >= 0x11) | ||
500 | regp->cursor_cfg |= NV_PCRTC_CURSOR_CONFIG_CUR_BPP_32; | ||
501 | if (mode->flags & DRM_MODE_FLAG_DBLSCAN) | ||
502 | regp->cursor_cfg |= NV_PCRTC_CURSOR_CONFIG_DOUBLE_SCAN_ENABLE; | ||
503 | |||
504 | /* Unblock some timings */ | ||
505 | regp->CRTC[NV_CIO_CRE_53] = 0; | ||
506 | regp->CRTC[NV_CIO_CRE_54] = 0; | ||
507 | |||
508 | /* 0x00 is disabled, 0x11 is lvds, 0x22 crt and 0x88 tmds */ | ||
509 | if (lvds_output) | ||
510 | regp->CRTC[NV_CIO_CRE_SCRATCH3__INDEX] = 0x11; | ||
511 | else if (tmds_output) | ||
512 | regp->CRTC[NV_CIO_CRE_SCRATCH3__INDEX] = 0x88; | ||
513 | else | ||
514 | regp->CRTC[NV_CIO_CRE_SCRATCH3__INDEX] = 0x22; | ||
515 | |||
516 | /* These values seem to vary */ | ||
517 | /* This register seems to be used by the bios to make certain decisions on some G70 cards? */ | ||
518 | regp->CRTC[NV_CIO_CRE_SCRATCH4__INDEX] = savep->CRTC[NV_CIO_CRE_SCRATCH4__INDEX]; | ||
519 | |||
520 | nv_crtc_set_digital_vibrance(crtc, nv_crtc->saturation); | ||
521 | |||
522 | /* probably a scratch reg, but kept for cargo-cult purposes: | ||
523 | * bit0: crtc0?, head A | ||
524 | * bit6: lvds, head A | ||
525 | * bit7: (only in X), head A | ||
526 | */ | ||
527 | if (nv_crtc->index == 0) | ||
528 | regp->CRTC[NV_CIO_CRE_4B] = savep->CRTC[NV_CIO_CRE_4B] | 0x80; | ||
529 | |||
530 | /* The blob seems to take the current value from crtc 0, add 4 to that | ||
531 | * and reuse the old value for crtc 1 */ | ||
532 | regp->CRTC[NV_CIO_CRE_TVOUT_LATENCY] = dev_priv->saved_reg.crtc_reg[0].CRTC[NV_CIO_CRE_TVOUT_LATENCY]; | ||
533 | if (!nv_crtc->index) | ||
534 | regp->CRTC[NV_CIO_CRE_TVOUT_LATENCY] += 4; | ||
535 | |||
536 | /* the blob sometimes sets |= 0x10 (which is the same as setting |= | ||
537 | * 1 << 30 on 0x60.830), for no apparent reason */ | ||
538 | regp->CRTC[NV_CIO_CRE_59] = off_chip_digital; | ||
539 | |||
540 | regp->crtc_830 = mode->crtc_vdisplay - 3; | ||
541 | regp->crtc_834 = mode->crtc_vdisplay - 1; | ||
542 | |||
543 | if (dev_priv->card_type == NV_40) | ||
544 | /* This is what the blob does */ | ||
545 | regp->crtc_850 = NVReadCRTC(dev, 0, NV_PCRTC_850); | ||
546 | |||
547 | if (dev_priv->card_type >= NV_30) | ||
548 | regp->gpio_ext = NVReadCRTC(dev, 0, NV_PCRTC_GPIO_EXT); | ||
549 | |||
550 | regp->crtc_cfg = NV_PCRTC_CONFIG_START_ADDRESS_HSYNC; | ||
551 | |||
552 | /* Some misc regs */ | ||
553 | if (dev_priv->card_type == NV_40) { | ||
554 | regp->CRTC[NV_CIO_CRE_85] = 0xFF; | ||
555 | regp->CRTC[NV_CIO_CRE_86] = 0x1; | ||
556 | } | ||
557 | |||
558 | regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] = (crtc->fb->depth + 1) / 8; | ||
559 | /* Enable slaved mode (called MODE_TV in nv4ref.h) */ | ||
560 | if (lvds_output || tmds_output || tv_output) | ||
561 | regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] |= (1 << 7); | ||
562 | |||
563 | /* Generic PRAMDAC regs */ | ||
564 | |||
565 | if (dev_priv->card_type >= NV_10) | ||
566 | /* Only bit that bios and blob set. */ | ||
567 | regp->nv10_cursync = (1 << 25); | ||
568 | |||
569 | regp->ramdac_gen_ctrl = NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS | | ||
570 | NV_PRAMDAC_GENERAL_CONTROL_VGA_STATE_SEL | | ||
571 | NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON; | ||
572 | if (crtc->fb->depth == 16) | ||
573 | regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL; | ||
574 | if (dev_priv->chipset >= 0x11) | ||
575 | regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_PIPE_LONG; | ||
576 | |||
577 | regp->ramdac_630 = 0; /* turn off green mode (tv test pattern?) */ | ||
578 | regp->tv_setup = 0; | ||
579 | |||
580 | nv_crtc_set_image_sharpening(crtc, nv_crtc->sharpness); | ||
581 | |||
582 | /* Some values the blob sets */ | ||
583 | regp->ramdac_8c0 = 0x100; | ||
584 | regp->ramdac_a20 = 0x0; | ||
585 | regp->ramdac_a24 = 0xfffff; | ||
586 | regp->ramdac_a34 = 0x1; | ||
587 | } | ||
588 | |||
589 | /** | ||
590 | * Sets up registers for the given mode/adjusted_mode pair. | ||
591 | * | ||
592 | * The clocks, CRTCs and outputs attached to this CRTC must be off. | ||
593 | * | ||
594 | * This shouldn't enable any clocks, CRTCs, or outputs, but they should | ||
595 | * be easily turned on/off after this. | ||
596 | */ | ||
597 | static int | ||
598 | nv_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, | ||
599 | struct drm_display_mode *adjusted_mode, | ||
600 | int x, int y, struct drm_framebuffer *old_fb) | ||
601 | { | ||
602 | struct drm_device *dev = crtc->dev; | ||
603 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | ||
604 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
605 | |||
606 | NV_DEBUG(dev, "CTRC mode on CRTC %d:\n", nv_crtc->index); | ||
607 | drm_mode_debug_printmodeline(adjusted_mode); | ||
608 | |||
609 | /* unlock must come after turning off FP_TG_CONTROL in output_prepare */ | ||
610 | nv_lock_vga_crtc_shadow(dev, nv_crtc->index, -1); | ||
611 | |||
612 | nv_crtc_mode_set_vga(crtc, adjusted_mode); | ||
613 | /* calculated in nv04_dfp_prepare, nv40 needs it written before calculating PLLs */ | ||
614 | if (dev_priv->card_type == NV_40) | ||
615 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, dev_priv->mode_reg.sel_clk); | ||
616 | nv_crtc_mode_set_regs(crtc, adjusted_mode); | ||
617 | nv_crtc_calc_state_ext(crtc, mode, adjusted_mode->clock); | ||
618 | return 0; | ||
619 | } | ||
620 | |||
621 | static void nv_crtc_save(struct drm_crtc *crtc) | ||
622 | { | ||
623 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | ||
624 | struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; | ||
625 | struct nv04_mode_state *state = &dev_priv->mode_reg; | ||
626 | struct nv04_crtc_reg *crtc_state = &state->crtc_reg[nv_crtc->index]; | ||
627 | struct nv04_mode_state *saved = &dev_priv->saved_reg; | ||
628 | struct nv04_crtc_reg *crtc_saved = &saved->crtc_reg[nv_crtc->index]; | ||
629 | |||
630 | if (nv_two_heads(crtc->dev)) | ||
631 | NVSetOwner(crtc->dev, nv_crtc->index); | ||
632 | |||
633 | nouveau_hw_save_state(crtc->dev, nv_crtc->index, saved); | ||
634 | |||
635 | /* init some state to saved value */ | ||
636 | state->sel_clk = saved->sel_clk & ~(0x5 << 16); | ||
637 | crtc_state->CRTC[NV_CIO_CRE_LCD__INDEX] = crtc_saved->CRTC[NV_CIO_CRE_LCD__INDEX]; | ||
638 | state->pllsel = saved->pllsel & ~(PLLSEL_VPLL1_MASK | PLLSEL_VPLL2_MASK | PLLSEL_TV_MASK); | ||
639 | crtc_state->gpio_ext = crtc_saved->gpio_ext; | ||
640 | } | ||
641 | |||
642 | static void nv_crtc_restore(struct drm_crtc *crtc) | ||
643 | { | ||
644 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | ||
645 | struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; | ||
646 | int head = nv_crtc->index; | ||
647 | uint8_t saved_cr21 = dev_priv->saved_reg.crtc_reg[head].CRTC[NV_CIO_CRE_21]; | ||
648 | |||
649 | if (nv_two_heads(crtc->dev)) | ||
650 | NVSetOwner(crtc->dev, head); | ||
651 | |||
652 | nouveau_hw_load_state(crtc->dev, head, &dev_priv->saved_reg); | ||
653 | nv_lock_vga_crtc_shadow(crtc->dev, head, saved_cr21); | ||
654 | |||
655 | nv_crtc->last_dpms = NV_DPMS_CLEARED; | ||
656 | } | ||
657 | |||
658 | static void nv_crtc_prepare(struct drm_crtc *crtc) | ||
659 | { | ||
660 | struct drm_device *dev = crtc->dev; | ||
661 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
662 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | ||
663 | struct drm_crtc_helper_funcs *funcs = crtc->helper_private; | ||
664 | |||
665 | if (nv_two_heads(dev)) | ||
666 | NVSetOwner(dev, nv_crtc->index); | ||
667 | |||
668 | funcs->dpms(crtc, DRM_MODE_DPMS_OFF); | ||
669 | |||
670 | NVBlankScreen(dev, nv_crtc->index, true); | ||
671 | |||
672 | /* Some more preperation. */ | ||
673 | NVWriteCRTC(dev, nv_crtc->index, NV_PCRTC_CONFIG, NV_PCRTC_CONFIG_START_ADDRESS_NON_VGA); | ||
674 | if (dev_priv->card_type == NV_40) { | ||
675 | uint32_t reg900 = NVReadRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_900); | ||
676 | NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_900, reg900 & ~0x10000); | ||
677 | } | ||
678 | } | ||
679 | |||
680 | static void nv_crtc_commit(struct drm_crtc *crtc) | ||
681 | { | ||
682 | struct drm_device *dev = crtc->dev; | ||
683 | struct drm_crtc_helper_funcs *funcs = crtc->helper_private; | ||
684 | struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; | ||
685 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | ||
686 | |||
687 | nouveau_hw_load_state(dev, nv_crtc->index, &dev_priv->mode_reg); | ||
688 | nv04_crtc_mode_set_base(crtc, crtc->x, crtc->y, NULL); | ||
689 | |||
690 | #ifdef __BIG_ENDIAN | ||
691 | /* turn on LFB swapping */ | ||
692 | { | ||
693 | uint8_t tmp = NVReadVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RCR); | ||
694 | tmp |= MASK(NV_CIO_CRE_RCR_ENDIAN_BIG); | ||
695 | NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RCR, tmp); | ||
696 | } | ||
697 | #endif | ||
698 | |||
699 | funcs->dpms(crtc, DRM_MODE_DPMS_ON); | ||
700 | } | ||
701 | |||
702 | static void nv_crtc_destroy(struct drm_crtc *crtc) | ||
703 | { | ||
704 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | ||
705 | |||
706 | NV_DEBUG(crtc->dev, "\n"); | ||
707 | |||
708 | if (!nv_crtc) | ||
709 | return; | ||
710 | |||
711 | drm_crtc_cleanup(crtc); | ||
712 | |||
713 | nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); | ||
714 | kfree(nv_crtc); | ||
715 | } | ||
716 | |||
717 | static void | ||
718 | nv_crtc_gamma_load(struct drm_crtc *crtc) | ||
719 | { | ||
720 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | ||
721 | struct drm_device *dev = nv_crtc->base.dev; | ||
722 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
723 | struct rgb { uint8_t r, g, b; } __attribute__((packed)) *rgbs; | ||
724 | int i; | ||
725 | |||
726 | rgbs = (struct rgb *)dev_priv->mode_reg.crtc_reg[nv_crtc->index].DAC; | ||
727 | for (i = 0; i < 256; i++) { | ||
728 | rgbs[i].r = nv_crtc->lut.r[i] >> 8; | ||
729 | rgbs[i].g = nv_crtc->lut.g[i] >> 8; | ||
730 | rgbs[i].b = nv_crtc->lut.b[i] >> 8; | ||
731 | } | ||
732 | |||
733 | nouveau_hw_load_state_palette(dev, nv_crtc->index, &dev_priv->mode_reg); | ||
734 | } | ||
735 | |||
736 | static void | ||
737 | nv_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, uint32_t size) | ||
738 | { | ||
739 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | ||
740 | int i; | ||
741 | |||
742 | if (size != 256) | ||
743 | return; | ||
744 | |||
745 | for (i = 0; i < 256; i++) { | ||
746 | nv_crtc->lut.r[i] = r[i]; | ||
747 | nv_crtc->lut.g[i] = g[i]; | ||
748 | nv_crtc->lut.b[i] = b[i]; | ||
749 | } | ||
750 | |||
751 | /* We need to know the depth before we upload, but it's possible to | ||
752 | * get called before a framebuffer is bound. If this is the case, | ||
753 | * mark the lut values as dirty by setting depth==0, and it'll be | ||
754 | * uploaded on the first mode_set_base() | ||
755 | */ | ||
756 | if (!nv_crtc->base.fb) { | ||
757 | nv_crtc->lut.depth = 0; | ||
758 | return; | ||
759 | } | ||
760 | |||
761 | nv_crtc_gamma_load(crtc); | ||
762 | } | ||
763 | |||
764 | static int | ||
765 | nv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, | ||
766 | struct drm_framebuffer *old_fb) | ||
767 | { | ||
768 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | ||
769 | struct drm_device *dev = crtc->dev; | ||
770 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
771 | struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index]; | ||
772 | struct drm_framebuffer *drm_fb = nv_crtc->base.fb; | ||
773 | struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb); | ||
774 | int arb_burst, arb_lwm; | ||
775 | int ret; | ||
776 | |||
777 | ret = nouveau_bo_pin(fb->nvbo, TTM_PL_FLAG_VRAM); | ||
778 | if (ret) | ||
779 | return ret; | ||
780 | |||
781 | if (old_fb) { | ||
782 | struct nouveau_framebuffer *ofb = nouveau_framebuffer(old_fb); | ||
783 | nouveau_bo_unpin(ofb->nvbo); | ||
784 | } | ||
785 | |||
786 | nv_crtc->fb.offset = fb->nvbo->bo.offset; | ||
787 | |||
788 | if (nv_crtc->lut.depth != drm_fb->depth) { | ||
789 | nv_crtc->lut.depth = drm_fb->depth; | ||
790 | nv_crtc_gamma_load(crtc); | ||
791 | } | ||
792 | |||
793 | /* Update the framebuffer format. */ | ||
794 | regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] &= ~3; | ||
795 | regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] |= (crtc->fb->depth + 1) / 8; | ||
796 | regp->ramdac_gen_ctrl &= ~NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL; | ||
797 | if (crtc->fb->depth == 16) | ||
798 | regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL; | ||
799 | crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_PIXEL_INDEX); | ||
800 | NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_GENERAL_CONTROL, | ||
801 | regp->ramdac_gen_ctrl); | ||
802 | |||
803 | regp->CRTC[NV_CIO_CR_OFFSET_INDEX] = drm_fb->pitch >> 3; | ||
804 | regp->CRTC[NV_CIO_CRE_RPC0_INDEX] = | ||
805 | XLATE(drm_fb->pitch >> 3, 8, NV_CIO_CRE_RPC0_OFFSET_10_8); | ||
806 | crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_RPC0_INDEX); | ||
807 | crtc_wr_cio_state(crtc, regp, NV_CIO_CR_OFFSET_INDEX); | ||
808 | |||
809 | /* Update the framebuffer location. */ | ||
810 | regp->fb_start = nv_crtc->fb.offset & ~3; | ||
811 | regp->fb_start += (y * drm_fb->pitch) + (x * drm_fb->bits_per_pixel / 8); | ||
812 | NVWriteCRTC(dev, nv_crtc->index, NV_PCRTC_START, regp->fb_start); | ||
813 | |||
814 | /* Update the arbitration parameters. */ | ||
815 | nouveau_calc_arb(dev, crtc->mode.clock, drm_fb->bits_per_pixel, | ||
816 | &arb_burst, &arb_lwm); | ||
817 | |||
818 | regp->CRTC[NV_CIO_CRE_FF_INDEX] = arb_burst; | ||
819 | regp->CRTC[NV_CIO_CRE_FFLWM__INDEX] = arb_lwm & 0xff; | ||
820 | crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_FF_INDEX); | ||
821 | crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_FFLWM__INDEX); | ||
822 | |||
823 | if (dev_priv->card_type >= NV_30) { | ||
824 | regp->CRTC[NV_CIO_CRE_47] = arb_lwm >> 8; | ||
825 | crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_47); | ||
826 | } | ||
827 | |||
828 | return 0; | ||
829 | } | ||
830 | |||
831 | static void nv04_cursor_upload(struct drm_device *dev, struct nouveau_bo *src, | ||
832 | struct nouveau_bo *dst) | ||
833 | { | ||
834 | int width = nv_cursor_width(dev); | ||
835 | uint32_t pixel; | ||
836 | int i, j; | ||
837 | |||
838 | for (i = 0; i < width; i++) { | ||
839 | for (j = 0; j < width; j++) { | ||
840 | pixel = nouveau_bo_rd32(src, i*64 + j); | ||
841 | |||
842 | nouveau_bo_wr16(dst, i*width + j, (pixel & 0x80000000) >> 16 | ||
843 | | (pixel & 0xf80000) >> 9 | ||
844 | | (pixel & 0xf800) >> 6 | ||
845 | | (pixel & 0xf8) >> 3); | ||
846 | } | ||
847 | } | ||
848 | } | ||
849 | |||
850 | static void nv11_cursor_upload(struct drm_device *dev, struct nouveau_bo *src, | ||
851 | struct nouveau_bo *dst) | ||
852 | { | ||
853 | uint32_t pixel; | ||
854 | int alpha, i; | ||
855 | |||
856 | /* nv11+ supports premultiplied (PM), or non-premultiplied (NPM) alpha | ||
857 | * cursors (though NPM in combination with fp dithering may not work on | ||
858 | * nv11, from "nv" driver history) | ||
859 | * NPM mode needs NV_PCRTC_CURSOR_CONFIG_ALPHA_BLEND set and is what the | ||
860 | * blob uses, however we get given PM cursors so we use PM mode | ||
861 | */ | ||
862 | for (i = 0; i < 64 * 64; i++) { | ||
863 | pixel = nouveau_bo_rd32(src, i); | ||
864 | |||
865 | /* hw gets unhappy if alpha <= rgb values. for a PM image "less | ||
866 | * than" shouldn't happen; fix "equal to" case by adding one to | ||
867 | * alpha channel (slightly inaccurate, but so is attempting to | ||
868 | * get back to NPM images, due to limits of integer precision) | ||
869 | */ | ||
870 | alpha = pixel >> 24; | ||
871 | if (alpha > 0 && alpha < 255) | ||
872 | pixel = (pixel & 0x00ffffff) | ((alpha + 1) << 24); | ||
873 | |||
874 | #ifdef __BIG_ENDIAN | ||
875 | { | ||
876 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
877 | |||
878 | if (dev_priv->chipset == 0x11) { | ||
879 | pixel = ((pixel & 0x000000ff) << 24) | | ||
880 | ((pixel & 0x0000ff00) << 8) | | ||
881 | ((pixel & 0x00ff0000) >> 8) | | ||
882 | ((pixel & 0xff000000) >> 24); | ||
883 | } | ||
884 | } | ||
885 | #endif | ||
886 | |||
887 | nouveau_bo_wr32(dst, i, pixel); | ||
888 | } | ||
889 | } | ||
890 | |||
891 | static int | ||
892 | nv04_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, | ||
893 | uint32_t buffer_handle, uint32_t width, uint32_t height) | ||
894 | { | ||
895 | struct drm_nouveau_private *dev_priv = crtc->dev->dev_private; | ||
896 | struct drm_device *dev = dev_priv->dev; | ||
897 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | ||
898 | struct nouveau_bo *cursor = NULL; | ||
899 | struct drm_gem_object *gem; | ||
900 | int ret = 0; | ||
901 | |||
902 | if (width != 64 || height != 64) | ||
903 | return -EINVAL; | ||
904 | |||
905 | if (!buffer_handle) { | ||
906 | nv_crtc->cursor.hide(nv_crtc, true); | ||
907 | return 0; | ||
908 | } | ||
909 | |||
910 | gem = drm_gem_object_lookup(dev, file_priv, buffer_handle); | ||
911 | if (!gem) | ||
912 | return -EINVAL; | ||
913 | cursor = nouveau_gem_object(gem); | ||
914 | |||
915 | ret = nouveau_bo_map(cursor); | ||
916 | if (ret) | ||
917 | goto out; | ||
918 | |||
919 | if (dev_priv->chipset >= 0x11) | ||
920 | nv11_cursor_upload(dev, cursor, nv_crtc->cursor.nvbo); | ||
921 | else | ||
922 | nv04_cursor_upload(dev, cursor, nv_crtc->cursor.nvbo); | ||
923 | |||
924 | nouveau_bo_unmap(cursor); | ||
925 | nv_crtc->cursor.offset = nv_crtc->cursor.nvbo->bo.offset; | ||
926 | nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.offset); | ||
927 | nv_crtc->cursor.show(nv_crtc, true); | ||
928 | out: | ||
929 | mutex_lock(&dev->struct_mutex); | ||
930 | drm_gem_object_unreference(gem); | ||
931 | mutex_unlock(&dev->struct_mutex); | ||
932 | return ret; | ||
933 | } | ||
934 | |||
935 | static int | ||
936 | nv04_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) | ||
937 | { | ||
938 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | ||
939 | |||
940 | nv_crtc->cursor.set_pos(nv_crtc, x, y); | ||
941 | return 0; | ||
942 | } | ||
943 | |||
944 | static const struct drm_crtc_funcs nv04_crtc_funcs = { | ||
945 | .save = nv_crtc_save, | ||
946 | .restore = nv_crtc_restore, | ||
947 | .cursor_set = nv04_crtc_cursor_set, | ||
948 | .cursor_move = nv04_crtc_cursor_move, | ||
949 | .gamma_set = nv_crtc_gamma_set, | ||
950 | .set_config = drm_crtc_helper_set_config, | ||
951 | .destroy = nv_crtc_destroy, | ||
952 | }; | ||
953 | |||
954 | static const struct drm_crtc_helper_funcs nv04_crtc_helper_funcs = { | ||
955 | .dpms = nv_crtc_dpms, | ||
956 | .prepare = nv_crtc_prepare, | ||
957 | .commit = nv_crtc_commit, | ||
958 | .mode_fixup = nv_crtc_mode_fixup, | ||
959 | .mode_set = nv_crtc_mode_set, | ||
960 | .mode_set_base = nv04_crtc_mode_set_base, | ||
961 | .load_lut = nv_crtc_gamma_load, | ||
962 | }; | ||
963 | |||
964 | int | ||
965 | nv04_crtc_create(struct drm_device *dev, int crtc_num) | ||
966 | { | ||
967 | struct nouveau_crtc *nv_crtc; | ||
968 | int ret, i; | ||
969 | |||
970 | nv_crtc = kzalloc(sizeof(*nv_crtc), GFP_KERNEL); | ||
971 | if (!nv_crtc) | ||
972 | return -ENOMEM; | ||
973 | |||
974 | for (i = 0; i < 256; i++) { | ||
975 | nv_crtc->lut.r[i] = i << 8; | ||
976 | nv_crtc->lut.g[i] = i << 8; | ||
977 | nv_crtc->lut.b[i] = i << 8; | ||
978 | } | ||
979 | nv_crtc->lut.depth = 0; | ||
980 | |||
981 | nv_crtc->index = crtc_num; | ||
982 | nv_crtc->last_dpms = NV_DPMS_CLEARED; | ||
983 | |||
984 | drm_crtc_init(dev, &nv_crtc->base, &nv04_crtc_funcs); | ||
985 | drm_crtc_helper_add(&nv_crtc->base, &nv04_crtc_helper_funcs); | ||
986 | drm_mode_crtc_set_gamma_size(&nv_crtc->base, 256); | ||
987 | |||
988 | ret = nouveau_bo_new(dev, NULL, 64*64*4, 0x100, TTM_PL_FLAG_VRAM, | ||
989 | 0, 0x0000, false, true, &nv_crtc->cursor.nvbo); | ||
990 | if (!ret) { | ||
991 | ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM); | ||
992 | if (!ret) | ||
993 | ret = nouveau_bo_map(nv_crtc->cursor.nvbo); | ||
994 | if (ret) | ||
995 | nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); | ||
996 | } | ||
997 | |||
998 | nv04_cursor_init(nv_crtc); | ||
999 | |||
1000 | return 0; | ||
1001 | } | ||
1002 | |||
diff --git a/drivers/gpu/drm/nouveau/nv04_cursor.c b/drivers/gpu/drm/nouveau/nv04_cursor.c new file mode 100644 index 000000000000..89a91b9d8b25 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv04_cursor.c | |||
@@ -0,0 +1,70 @@ | |||
1 | #include "drmP.h" | ||
2 | #include "drm_mode.h" | ||
3 | #include "nouveau_reg.h" | ||
4 | #include "nouveau_drv.h" | ||
5 | #include "nouveau_crtc.h" | ||
6 | #include "nouveau_hw.h" | ||
7 | |||
8 | static void | ||
9 | nv04_cursor_show(struct nouveau_crtc *nv_crtc, bool update) | ||
10 | { | ||
11 | nv_show_cursor(nv_crtc->base.dev, nv_crtc->index, true); | ||
12 | } | ||
13 | |||
14 | static void | ||
15 | nv04_cursor_hide(struct nouveau_crtc *nv_crtc, bool update) | ||
16 | { | ||
17 | nv_show_cursor(nv_crtc->base.dev, nv_crtc->index, false); | ||
18 | } | ||
19 | |||
20 | static void | ||
21 | nv04_cursor_set_pos(struct nouveau_crtc *nv_crtc, int x, int y) | ||
22 | { | ||
23 | NVWriteRAMDAC(nv_crtc->base.dev, nv_crtc->index, | ||
24 | NV_PRAMDAC_CU_START_POS, | ||
25 | XLATE(y, 0, NV_PRAMDAC_CU_START_POS_Y) | | ||
26 | XLATE(x, 0, NV_PRAMDAC_CU_START_POS_X)); | ||
27 | } | ||
28 | |||
29 | static void | ||
30 | crtc_wr_cio_state(struct drm_crtc *crtc, struct nv04_crtc_reg *crtcstate, int index) | ||
31 | { | ||
32 | NVWriteVgaCrtc(crtc->dev, nouveau_crtc(crtc)->index, index, | ||
33 | crtcstate->CRTC[index]); | ||
34 | } | ||
35 | |||
36 | static void | ||
37 | nv04_cursor_set_offset(struct nouveau_crtc *nv_crtc, uint32_t offset) | ||
38 | { | ||
39 | struct drm_device *dev = nv_crtc->base.dev; | ||
40 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
41 | struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index]; | ||
42 | struct drm_crtc *crtc = &nv_crtc->base; | ||
43 | |||
44 | regp->CRTC[NV_CIO_CRE_HCUR_ADDR0_INDEX] = | ||
45 | MASK(NV_CIO_CRE_HCUR_ASI) | | ||
46 | XLATE(offset, 17, NV_CIO_CRE_HCUR_ADDR0_ADR); | ||
47 | regp->CRTC[NV_CIO_CRE_HCUR_ADDR1_INDEX] = | ||
48 | XLATE(offset, 11, NV_CIO_CRE_HCUR_ADDR1_ADR); | ||
49 | if (crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN) | ||
50 | regp->CRTC[NV_CIO_CRE_HCUR_ADDR1_INDEX] |= | ||
51 | MASK(NV_CIO_CRE_HCUR_ADDR1_CUR_DBL); | ||
52 | regp->CRTC[NV_CIO_CRE_HCUR_ADDR2_INDEX] = offset >> 24; | ||
53 | |||
54 | crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX); | ||
55 | crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX); | ||
56 | crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX); | ||
57 | if (dev_priv->card_type == NV_40) | ||
58 | nv_fix_nv40_hw_cursor(dev, nv_crtc->index); | ||
59 | } | ||
60 | |||
61 | int | ||
62 | nv04_cursor_init(struct nouveau_crtc *crtc) | ||
63 | { | ||
64 | crtc->cursor.set_offset = nv04_cursor_set_offset; | ||
65 | crtc->cursor.set_pos = nv04_cursor_set_pos; | ||
66 | crtc->cursor.hide = nv04_cursor_hide; | ||
67 | crtc->cursor.show = nv04_cursor_show; | ||
68 | return 0; | ||
69 | } | ||
70 | |||
diff --git a/drivers/gpu/drm/nouveau/nv04_dac.c b/drivers/gpu/drm/nouveau/nv04_dac.c new file mode 100644 index 000000000000..a5fa51714e87 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv04_dac.c | |||
@@ -0,0 +1,528 @@ | |||
1 | /* | ||
2 | * Copyright 2003 NVIDIA, Corporation | ||
3 | * Copyright 2006 Dave Airlie | ||
4 | * Copyright 2007 Maarten Maathuis | ||
5 | * Copyright 2007-2009 Stuart Bennett | ||
6 | * | ||
7 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
8 | * copy of this software and associated documentation files (the "Software"), | ||
9 | * to deal in the Software without restriction, including without limitation | ||
10 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
11 | * and/or sell copies of the Software, and to permit persons to whom the | ||
12 | * Software is furnished to do so, subject to the following conditions: | ||
13 | * | ||
14 | * The above copyright notice and this permission notice (including the next | ||
15 | * paragraph) shall be included in all copies or substantial portions of the | ||
16 | * Software. | ||
17 | * | ||
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
21 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
24 | * DEALINGS IN THE SOFTWARE. | ||
25 | */ | ||
26 | |||
27 | #include "drmP.h" | ||
28 | #include "drm_crtc_helper.h" | ||
29 | |||
30 | #include "nouveau_drv.h" | ||
31 | #include "nouveau_encoder.h" | ||
32 | #include "nouveau_connector.h" | ||
33 | #include "nouveau_crtc.h" | ||
34 | #include "nouveau_hw.h" | ||
35 | #include "nvreg.h" | ||
36 | |||
37 | int nv04_dac_output_offset(struct drm_encoder *encoder) | ||
38 | { | ||
39 | struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb; | ||
40 | int offset = 0; | ||
41 | |||
42 | if (dcb->or & (8 | OUTPUT_C)) | ||
43 | offset += 0x68; | ||
44 | if (dcb->or & (8 | OUTPUT_B)) | ||
45 | offset += 0x2000; | ||
46 | |||
47 | return offset; | ||
48 | } | ||
49 | |||
50 | /* | ||
51 | * arbitrary limit to number of sense oscillations tolerated in one sample | ||
52 | * period (observed to be at least 13 in "nvidia") | ||
53 | */ | ||
54 | #define MAX_HBLANK_OSC 20 | ||
55 | |||
56 | /* | ||
57 | * arbitrary limit to number of conflicting sample pairs to tolerate at a | ||
58 | * voltage step (observed to be at least 5 in "nvidia") | ||
59 | */ | ||
60 | #define MAX_SAMPLE_PAIRS 10 | ||
61 | |||
62 | static int sample_load_twice(struct drm_device *dev, bool sense[2]) | ||
63 | { | ||
64 | int i; | ||
65 | |||
66 | for (i = 0; i < 2; i++) { | ||
67 | bool sense_a, sense_b, sense_b_prime; | ||
68 | int j = 0; | ||
69 | |||
70 | /* | ||
71 | * wait for bit 0 clear -- out of hblank -- (say reg value 0x4), | ||
72 | * then wait for transition 0x4->0x5->0x4: enter hblank, leave | ||
73 | * hblank again | ||
74 | * use a 10ms timeout (guards against crtc being inactive, in | ||
75 | * which case blank state would never change) | ||
76 | */ | ||
77 | if (!nouveau_wait_until(dev, 10000000, NV_PRMCIO_INP0__COLOR, | ||
78 | 0x00000001, 0x00000000)) | ||
79 | return -EBUSY; | ||
80 | if (!nouveau_wait_until(dev, 10000000, NV_PRMCIO_INP0__COLOR, | ||
81 | 0x00000001, 0x00000001)) | ||
82 | return -EBUSY; | ||
83 | if (!nouveau_wait_until(dev, 10000000, NV_PRMCIO_INP0__COLOR, | ||
84 | 0x00000001, 0x00000000)) | ||
85 | return -EBUSY; | ||
86 | |||
87 | udelay(100); | ||
88 | /* when level triggers, sense is _LO_ */ | ||
89 | sense_a = nv_rd08(dev, NV_PRMCIO_INP0) & 0x10; | ||
90 | |||
91 | /* take another reading until it agrees with sense_a... */ | ||
92 | do { | ||
93 | udelay(100); | ||
94 | sense_b = nv_rd08(dev, NV_PRMCIO_INP0) & 0x10; | ||
95 | if (sense_a != sense_b) { | ||
96 | sense_b_prime = | ||
97 | nv_rd08(dev, NV_PRMCIO_INP0) & 0x10; | ||
98 | if (sense_b == sense_b_prime) { | ||
99 | /* ... unless two consecutive subsequent | ||
100 | * samples agree; sense_a is replaced */ | ||
101 | sense_a = sense_b; | ||
102 | /* force mis-match so we loop */ | ||
103 | sense_b = !sense_a; | ||
104 | } | ||
105 | } | ||
106 | } while ((sense_a != sense_b) && ++j < MAX_HBLANK_OSC); | ||
107 | |||
108 | if (j == MAX_HBLANK_OSC) | ||
109 | /* with so much oscillation, default to sense:LO */ | ||
110 | sense[i] = false; | ||
111 | else | ||
112 | sense[i] = sense_a; | ||
113 | } | ||
114 | |||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | static enum drm_connector_status nv04_dac_detect(struct drm_encoder *encoder, | ||
119 | struct drm_connector *connector) | ||
120 | { | ||
121 | struct drm_device *dev = encoder->dev; | ||
122 | uint8_t saved_seq1, saved_pi, saved_rpc1; | ||
123 | uint8_t saved_palette0[3], saved_palette_mask; | ||
124 | uint32_t saved_rtest_ctrl, saved_rgen_ctrl; | ||
125 | int i; | ||
126 | uint8_t blue; | ||
127 | bool sense = true; | ||
128 | |||
129 | /* | ||
130 | * for this detection to work, there needs to be a mode set up on the | ||
131 | * CRTC. this is presumed to be the case | ||
132 | */ | ||
133 | |||
134 | if (nv_two_heads(dev)) | ||
135 | /* only implemented for head A for now */ | ||
136 | NVSetOwner(dev, 0); | ||
137 | |||
138 | saved_seq1 = NVReadVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX); | ||
139 | NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1 & ~0x20); | ||
140 | |||
141 | saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL); | ||
142 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL, | ||
143 | saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF); | ||
144 | |||
145 | msleep(10); | ||
146 | |||
147 | saved_pi = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX); | ||
148 | NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, | ||
149 | saved_pi & ~(0x80 | MASK(NV_CIO_CRE_PIXEL_FORMAT))); | ||
150 | saved_rpc1 = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX); | ||
151 | NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1 & ~0xc0); | ||
152 | |||
153 | nv_wr08(dev, NV_PRMDIO_READ_MODE_ADDRESS, 0x0); | ||
154 | for (i = 0; i < 3; i++) | ||
155 | saved_palette0[i] = nv_rd08(dev, NV_PRMDIO_PALETTE_DATA); | ||
156 | saved_palette_mask = nv_rd08(dev, NV_PRMDIO_PIXEL_MASK); | ||
157 | nv_wr08(dev, NV_PRMDIO_PIXEL_MASK, 0); | ||
158 | |||
159 | saved_rgen_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL); | ||
160 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL, | ||
161 | (saved_rgen_ctrl & ~(NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS | | ||
162 | NV_PRAMDAC_GENERAL_CONTROL_TERMINATION_75OHM)) | | ||
163 | NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON); | ||
164 | |||
165 | blue = 8; /* start of test range */ | ||
166 | |||
167 | do { | ||
168 | bool sense_pair[2]; | ||
169 | |||
170 | nv_wr08(dev, NV_PRMDIO_WRITE_MODE_ADDRESS, 0); | ||
171 | nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, 0); | ||
172 | nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, 0); | ||
173 | /* testing blue won't find monochrome monitors. I don't care */ | ||
174 | nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, blue); | ||
175 | |||
176 | i = 0; | ||
177 | /* take sample pairs until both samples in the pair agree */ | ||
178 | do { | ||
179 | if (sample_load_twice(dev, sense_pair)) | ||
180 | goto out; | ||
181 | } while ((sense_pair[0] != sense_pair[1]) && | ||
182 | ++i < MAX_SAMPLE_PAIRS); | ||
183 | |||
184 | if (i == MAX_SAMPLE_PAIRS) | ||
185 | /* too much oscillation defaults to LO */ | ||
186 | sense = false; | ||
187 | else | ||
188 | sense = sense_pair[0]; | ||
189 | |||
190 | /* | ||
191 | * if sense goes LO before blue ramps to 0x18, monitor is not connected. | ||
192 | * ergo, if blue gets to 0x18, monitor must be connected | ||
193 | */ | ||
194 | } while (++blue < 0x18 && sense); | ||
195 | |||
196 | out: | ||
197 | nv_wr08(dev, NV_PRMDIO_PIXEL_MASK, saved_palette_mask); | ||
198 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL, saved_rgen_ctrl); | ||
199 | nv_wr08(dev, NV_PRMDIO_WRITE_MODE_ADDRESS, 0); | ||
200 | for (i = 0; i < 3; i++) | ||
201 | nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, saved_palette0[i]); | ||
202 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL, saved_rtest_ctrl); | ||
203 | NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, saved_pi); | ||
204 | NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1); | ||
205 | NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1); | ||
206 | |||
207 | if (blue == 0x18) { | ||
208 | NV_TRACE(dev, "Load detected on head A\n"); | ||
209 | return connector_status_connected; | ||
210 | } | ||
211 | |||
212 | return connector_status_disconnected; | ||
213 | } | ||
214 | |||
215 | enum drm_connector_status nv17_dac_detect(struct drm_encoder *encoder, | ||
216 | struct drm_connector *connector) | ||
217 | { | ||
218 | struct drm_device *dev = encoder->dev; | ||
219 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
220 | struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb; | ||
221 | uint32_t testval, regoffset = nv04_dac_output_offset(encoder); | ||
222 | uint32_t saved_powerctrl_2 = 0, saved_powerctrl_4 = 0, saved_routput, | ||
223 | saved_rtest_ctrl, saved_gpio0, saved_gpio1, temp, routput; | ||
224 | int head, present = 0; | ||
225 | |||
226 | #define RGB_TEST_DATA(r, g, b) (r << 0 | g << 10 | b << 20) | ||
227 | if (dcb->type == OUTPUT_TV) { | ||
228 | testval = RGB_TEST_DATA(0xa0, 0xa0, 0xa0); | ||
229 | |||
230 | if (dev_priv->vbios->tvdactestval) | ||
231 | testval = dev_priv->vbios->tvdactestval; | ||
232 | } else { | ||
233 | testval = RGB_TEST_DATA(0x140, 0x140, 0x140); /* 0x94050140 */ | ||
234 | |||
235 | if (dev_priv->vbios->dactestval) | ||
236 | testval = dev_priv->vbios->dactestval; | ||
237 | } | ||
238 | |||
239 | saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); | ||
240 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, | ||
241 | saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF); | ||
242 | |||
243 | saved_powerctrl_2 = nvReadMC(dev, NV_PBUS_POWERCTRL_2); | ||
244 | |||
245 | nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2 & 0xd7ffffff); | ||
246 | if (regoffset == 0x68) { | ||
247 | saved_powerctrl_4 = nvReadMC(dev, NV_PBUS_POWERCTRL_4); | ||
248 | nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4 & 0xffffffcf); | ||
249 | } | ||
250 | |||
251 | saved_gpio1 = nv17_gpio_get(dev, DCB_GPIO_TVDAC1); | ||
252 | saved_gpio0 = nv17_gpio_get(dev, DCB_GPIO_TVDAC0); | ||
253 | |||
254 | nv17_gpio_set(dev, DCB_GPIO_TVDAC1, dcb->type == OUTPUT_TV); | ||
255 | nv17_gpio_set(dev, DCB_GPIO_TVDAC0, dcb->type == OUTPUT_TV); | ||
256 | |||
257 | msleep(4); | ||
258 | |||
259 | saved_routput = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset); | ||
260 | head = (saved_routput & 0x100) >> 8; | ||
261 | #if 0 | ||
262 | /* if there's a spare crtc, using it will minimise flicker for the case | ||
263 | * where the in-use crtc is in use by an off-chip tmds encoder */ | ||
264 | if (xf86_config->crtc[head]->enabled && !xf86_config->crtc[head ^ 1]->enabled) | ||
265 | head ^= 1; | ||
266 | #endif | ||
267 | /* nv driver and nv31 use 0xfffffeee, nv34 and 6600 use 0xfffffece */ | ||
268 | routput = (saved_routput & 0xfffffece) | head << 8; | ||
269 | |||
270 | if (dev_priv->card_type >= NV_40) { | ||
271 | if (dcb->type == OUTPUT_TV) | ||
272 | routput |= 0x1a << 16; | ||
273 | else | ||
274 | routput &= ~(0x1a << 16); | ||
275 | } | ||
276 | |||
277 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, routput); | ||
278 | msleep(1); | ||
279 | |||
280 | temp = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset); | ||
281 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, temp | 1); | ||
282 | |||
283 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA, | ||
284 | NV_PRAMDAC_TESTPOINT_DATA_NOTBLANK | testval); | ||
285 | temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL); | ||
286 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL, | ||
287 | temp | NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED); | ||
288 | msleep(5); | ||
289 | |||
290 | temp = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); | ||
291 | |||
292 | if (dcb->type == OUTPUT_TV) | ||
293 | present = (nv17_tv_detect(encoder, connector, temp) | ||
294 | == connector_status_connected); | ||
295 | else | ||
296 | present = temp & NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI; | ||
297 | |||
298 | temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL); | ||
299 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL, | ||
300 | temp & ~NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED); | ||
301 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA, 0); | ||
302 | |||
303 | /* bios does something more complex for restoring, but I think this is good enough */ | ||
304 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, saved_routput); | ||
305 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, saved_rtest_ctrl); | ||
306 | if (regoffset == 0x68) | ||
307 | nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4); | ||
308 | nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2); | ||
309 | |||
310 | nv17_gpio_set(dev, DCB_GPIO_TVDAC1, saved_gpio1); | ||
311 | nv17_gpio_set(dev, DCB_GPIO_TVDAC0, saved_gpio0); | ||
312 | |||
313 | if (present) { | ||
314 | NV_INFO(dev, "Load detected on output %c\n", '@' + ffs(dcb->or)); | ||
315 | return connector_status_connected; | ||
316 | } | ||
317 | |||
318 | return connector_status_disconnected; | ||
319 | } | ||
320 | |||
321 | |||
322 | static bool nv04_dac_mode_fixup(struct drm_encoder *encoder, | ||
323 | struct drm_display_mode *mode, | ||
324 | struct drm_display_mode *adjusted_mode) | ||
325 | { | ||
326 | return true; | ||
327 | } | ||
328 | |||
329 | static void nv04_dac_prepare(struct drm_encoder *encoder) | ||
330 | { | ||
331 | struct drm_encoder_helper_funcs *helper = encoder->helper_private; | ||
332 | struct drm_device *dev = encoder->dev; | ||
333 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
334 | int head = nouveau_crtc(encoder->crtc)->index; | ||
335 | struct nv04_crtc_reg *crtcstate = dev_priv->mode_reg.crtc_reg; | ||
336 | |||
337 | helper->dpms(encoder, DRM_MODE_DPMS_OFF); | ||
338 | |||
339 | nv04_dfp_disable(dev, head); | ||
340 | |||
341 | /* Some NV4x have unknown values (0x3f, 0x50, 0x54, 0x6b, 0x79, 0x7f) | ||
342 | * at LCD__INDEX which we don't alter | ||
343 | */ | ||
344 | if (!(crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX] & 0x44)) | ||
345 | crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX] = 0; | ||
346 | } | ||
347 | |||
348 | |||
349 | static void nv04_dac_mode_set(struct drm_encoder *encoder, | ||
350 | struct drm_display_mode *mode, | ||
351 | struct drm_display_mode *adjusted_mode) | ||
352 | { | ||
353 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
354 | struct drm_device *dev = encoder->dev; | ||
355 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
356 | int head = nouveau_crtc(encoder->crtc)->index; | ||
357 | |||
358 | NV_TRACE(dev, "%s called for encoder %d\n", __func__, | ||
359 | nv_encoder->dcb->index); | ||
360 | |||
361 | if (nv_gf4_disp_arch(dev)) { | ||
362 | struct drm_encoder *rebind; | ||
363 | uint32_t dac_offset = nv04_dac_output_offset(encoder); | ||
364 | uint32_t otherdac; | ||
365 | |||
366 | /* bit 16-19 are bits that are set on some G70 cards, | ||
367 | * but don't seem to have much effect */ | ||
368 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset, | ||
369 | head << 8 | NV_PRAMDAC_DACCLK_SEL_DACCLK); | ||
370 | /* force any other vga encoders to bind to the other crtc */ | ||
371 | list_for_each_entry(rebind, &dev->mode_config.encoder_list, head) { | ||
372 | if (rebind == encoder | ||
373 | || nouveau_encoder(rebind)->dcb->type != OUTPUT_ANALOG) | ||
374 | continue; | ||
375 | |||
376 | dac_offset = nv04_dac_output_offset(rebind); | ||
377 | otherdac = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset); | ||
378 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset, | ||
379 | (otherdac & ~0x0100) | (head ^ 1) << 8); | ||
380 | } | ||
381 | } | ||
382 | |||
383 | /* This could use refinement for flatpanels, but it should work this way */ | ||
384 | if (dev_priv->chipset < 0x44) | ||
385 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0xf0000000); | ||
386 | else | ||
387 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000); | ||
388 | } | ||
389 | |||
390 | static void nv04_dac_commit(struct drm_encoder *encoder) | ||
391 | { | ||
392 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
393 | struct drm_device *dev = encoder->dev; | ||
394 | struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); | ||
395 | struct drm_encoder_helper_funcs *helper = encoder->helper_private; | ||
396 | |||
397 | helper->dpms(encoder, DRM_MODE_DPMS_ON); | ||
398 | |||
399 | NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n", | ||
400 | drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base), | ||
401 | nv_crtc->index, '@' + ffs(nv_encoder->dcb->or)); | ||
402 | } | ||
403 | |||
404 | void nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable) | ||
405 | { | ||
406 | struct drm_device *dev = encoder->dev; | ||
407 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
408 | struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb; | ||
409 | |||
410 | if (nv_gf4_disp_arch(dev)) { | ||
411 | uint32_t *dac_users = &dev_priv->dac_users[ffs(dcb->or) - 1]; | ||
412 | int dacclk_off = NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder); | ||
413 | uint32_t dacclk = NVReadRAMDAC(dev, 0, dacclk_off); | ||
414 | |||
415 | if (enable) { | ||
416 | *dac_users |= 1 << dcb->index; | ||
417 | NVWriteRAMDAC(dev, 0, dacclk_off, dacclk | NV_PRAMDAC_DACCLK_SEL_DACCLK); | ||
418 | |||
419 | } else { | ||
420 | *dac_users &= ~(1 << dcb->index); | ||
421 | if (!*dac_users) | ||
422 | NVWriteRAMDAC(dev, 0, dacclk_off, | ||
423 | dacclk & ~NV_PRAMDAC_DACCLK_SEL_DACCLK); | ||
424 | } | ||
425 | } | ||
426 | } | ||
427 | |||
428 | static void nv04_dac_dpms(struct drm_encoder *encoder, int mode) | ||
429 | { | ||
430 | struct drm_device *dev = encoder->dev; | ||
431 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
432 | |||
433 | if (nv_encoder->last_dpms == mode) | ||
434 | return; | ||
435 | nv_encoder->last_dpms = mode; | ||
436 | |||
437 | NV_INFO(dev, "Setting dpms mode %d on vga encoder (output %d)\n", | ||
438 | mode, nv_encoder->dcb->index); | ||
439 | |||
440 | nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON); | ||
441 | } | ||
442 | |||
443 | static void nv04_dac_save(struct drm_encoder *encoder) | ||
444 | { | ||
445 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
446 | struct drm_device *dev = encoder->dev; | ||
447 | |||
448 | if (nv_gf4_disp_arch(dev)) | ||
449 | nv_encoder->restore.output = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + | ||
450 | nv04_dac_output_offset(encoder)); | ||
451 | } | ||
452 | |||
453 | static void nv04_dac_restore(struct drm_encoder *encoder) | ||
454 | { | ||
455 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
456 | struct drm_device *dev = encoder->dev; | ||
457 | |||
458 | if (nv_gf4_disp_arch(dev)) | ||
459 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder), | ||
460 | nv_encoder->restore.output); | ||
461 | |||
462 | nv_encoder->last_dpms = NV_DPMS_CLEARED; | ||
463 | } | ||
464 | |||
465 | static void nv04_dac_destroy(struct drm_encoder *encoder) | ||
466 | { | ||
467 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
468 | |||
469 | NV_DEBUG(encoder->dev, "\n"); | ||
470 | |||
471 | drm_encoder_cleanup(encoder); | ||
472 | kfree(nv_encoder); | ||
473 | } | ||
474 | |||
475 | static const struct drm_encoder_helper_funcs nv04_dac_helper_funcs = { | ||
476 | .dpms = nv04_dac_dpms, | ||
477 | .save = nv04_dac_save, | ||
478 | .restore = nv04_dac_restore, | ||
479 | .mode_fixup = nv04_dac_mode_fixup, | ||
480 | .prepare = nv04_dac_prepare, | ||
481 | .commit = nv04_dac_commit, | ||
482 | .mode_set = nv04_dac_mode_set, | ||
483 | .detect = nv04_dac_detect | ||
484 | }; | ||
485 | |||
486 | static const struct drm_encoder_helper_funcs nv17_dac_helper_funcs = { | ||
487 | .dpms = nv04_dac_dpms, | ||
488 | .save = nv04_dac_save, | ||
489 | .restore = nv04_dac_restore, | ||
490 | .mode_fixup = nv04_dac_mode_fixup, | ||
491 | .prepare = nv04_dac_prepare, | ||
492 | .commit = nv04_dac_commit, | ||
493 | .mode_set = nv04_dac_mode_set, | ||
494 | .detect = nv17_dac_detect | ||
495 | }; | ||
496 | |||
497 | static const struct drm_encoder_funcs nv04_dac_funcs = { | ||
498 | .destroy = nv04_dac_destroy, | ||
499 | }; | ||
500 | |||
501 | int nv04_dac_create(struct drm_device *dev, struct dcb_entry *entry) | ||
502 | { | ||
503 | const struct drm_encoder_helper_funcs *helper; | ||
504 | struct drm_encoder *encoder; | ||
505 | struct nouveau_encoder *nv_encoder = NULL; | ||
506 | |||
507 | nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); | ||
508 | if (!nv_encoder) | ||
509 | return -ENOMEM; | ||
510 | |||
511 | encoder = to_drm_encoder(nv_encoder); | ||
512 | |||
513 | nv_encoder->dcb = entry; | ||
514 | nv_encoder->or = ffs(entry->or) - 1; | ||
515 | |||
516 | if (nv_gf4_disp_arch(dev)) | ||
517 | helper = &nv17_dac_helper_funcs; | ||
518 | else | ||
519 | helper = &nv04_dac_helper_funcs; | ||
520 | |||
521 | drm_encoder_init(dev, encoder, &nv04_dac_funcs, DRM_MODE_ENCODER_DAC); | ||
522 | drm_encoder_helper_add(encoder, helper); | ||
523 | |||
524 | encoder->possible_crtcs = entry->heads; | ||
525 | encoder->possible_clones = 0; | ||
526 | |||
527 | return 0; | ||
528 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nv04_dfp.c b/drivers/gpu/drm/nouveau/nv04_dfp.c new file mode 100644 index 000000000000..e5b33339d595 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv04_dfp.c | |||
@@ -0,0 +1,621 @@ | |||
1 | /* | ||
2 | * Copyright 2003 NVIDIA, Corporation | ||
3 | * Copyright 2006 Dave Airlie | ||
4 | * Copyright 2007 Maarten Maathuis | ||
5 | * Copyright 2007-2009 Stuart Bennett | ||
6 | * | ||
7 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
8 | * copy of this software and associated documentation files (the "Software"), | ||
9 | * to deal in the Software without restriction, including without limitation | ||
10 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
11 | * and/or sell copies of the Software, and to permit persons to whom the | ||
12 | * Software is furnished to do so, subject to the following conditions: | ||
13 | * | ||
14 | * The above copyright notice and this permission notice (including the next | ||
15 | * paragraph) shall be included in all copies or substantial portions of the | ||
16 | * Software. | ||
17 | * | ||
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
21 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
24 | * DEALINGS IN THE SOFTWARE. | ||
25 | */ | ||
26 | |||
27 | #include "drmP.h" | ||
28 | #include "drm_crtc_helper.h" | ||
29 | |||
30 | #include "nouveau_drv.h" | ||
31 | #include "nouveau_encoder.h" | ||
32 | #include "nouveau_connector.h" | ||
33 | #include "nouveau_crtc.h" | ||
34 | #include "nouveau_hw.h" | ||
35 | #include "nvreg.h" | ||
36 | |||
37 | #define FP_TG_CONTROL_ON (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS | \ | ||
38 | NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS | \ | ||
39 | NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS) | ||
40 | #define FP_TG_CONTROL_OFF (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_DISABLE | \ | ||
41 | NV_PRAMDAC_FP_TG_CONTROL_HSYNC_DISABLE | \ | ||
42 | NV_PRAMDAC_FP_TG_CONTROL_VSYNC_DISABLE) | ||
43 | |||
44 | static inline bool is_fpc_off(uint32_t fpc) | ||
45 | { | ||
46 | return ((fpc & (FP_TG_CONTROL_ON | FP_TG_CONTROL_OFF)) == | ||
47 | FP_TG_CONTROL_OFF); | ||
48 | } | ||
49 | |||
50 | int nv04_dfp_get_bound_head(struct drm_device *dev, struct dcb_entry *dcbent) | ||
51 | { | ||
52 | /* special case of nv_read_tmds to find crtc associated with an output. | ||
53 | * this does not give a correct answer for off-chip dvi, but there's no | ||
54 | * use for such an answer anyway | ||
55 | */ | ||
56 | int ramdac = (dcbent->or & OUTPUT_C) >> 2; | ||
57 | |||
58 | NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL, | ||
59 | NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE | 0x4); | ||
60 | return ((NVReadRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_DATA) & 0x8) >> 3) ^ ramdac; | ||
61 | } | ||
62 | |||
63 | void nv04_dfp_bind_head(struct drm_device *dev, struct dcb_entry *dcbent, | ||
64 | int head, bool dl) | ||
65 | { | ||
66 | /* The BIOS scripts don't do this for us, sadly | ||
67 | * Luckily we do know the values ;-) | ||
68 | * | ||
69 | * head < 0 indicates we wish to force a setting with the overrideval | ||
70 | * (for VT restore etc.) | ||
71 | */ | ||
72 | |||
73 | int ramdac = (dcbent->or & OUTPUT_C) >> 2; | ||
74 | uint8_t tmds04 = 0x80; | ||
75 | |||
76 | if (head != ramdac) | ||
77 | tmds04 = 0x88; | ||
78 | |||
79 | if (dcbent->type == OUTPUT_LVDS) | ||
80 | tmds04 |= 0x01; | ||
81 | |||
82 | nv_write_tmds(dev, dcbent->or, 0, 0x04, tmds04); | ||
83 | |||
84 | if (dl) /* dual link */ | ||
85 | nv_write_tmds(dev, dcbent->or, 1, 0x04, tmds04 ^ 0x08); | ||
86 | } | ||
87 | |||
88 | void nv04_dfp_disable(struct drm_device *dev, int head) | ||
89 | { | ||
90 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
91 | struct nv04_crtc_reg *crtcstate = dev_priv->mode_reg.crtc_reg; | ||
92 | |||
93 | if (NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL) & | ||
94 | FP_TG_CONTROL_ON) { | ||
95 | /* digital remnants must be cleaned before new crtc | ||
96 | * values programmed. delay is time for the vga stuff | ||
97 | * to realise it's in control again | ||
98 | */ | ||
99 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL, | ||
100 | FP_TG_CONTROL_OFF); | ||
101 | msleep(50); | ||
102 | } | ||
103 | /* don't inadvertently turn it on when state written later */ | ||
104 | crtcstate[head].fp_control = FP_TG_CONTROL_OFF; | ||
105 | } | ||
106 | |||
107 | void nv04_dfp_update_fp_control(struct drm_encoder *encoder, int mode) | ||
108 | { | ||
109 | struct drm_device *dev = encoder->dev; | ||
110 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
111 | struct drm_crtc *crtc; | ||
112 | struct nouveau_crtc *nv_crtc; | ||
113 | uint32_t *fpc; | ||
114 | |||
115 | if (mode == DRM_MODE_DPMS_ON) { | ||
116 | nv_crtc = nouveau_crtc(encoder->crtc); | ||
117 | fpc = &dev_priv->mode_reg.crtc_reg[nv_crtc->index].fp_control; | ||
118 | |||
119 | if (is_fpc_off(*fpc)) { | ||
120 | /* using saved value is ok, as (is_digital && dpms_on && | ||
121 | * fp_control==OFF) is (at present) *only* true when | ||
122 | * fpc's most recent change was by below "off" code | ||
123 | */ | ||
124 | *fpc = nv_crtc->dpms_saved_fp_control; | ||
125 | } | ||
126 | |||
127 | nv_crtc->fp_users |= 1 << nouveau_encoder(encoder)->dcb->index; | ||
128 | NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_FP_TG_CONTROL, *fpc); | ||
129 | } else { | ||
130 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | ||
131 | nv_crtc = nouveau_crtc(crtc); | ||
132 | fpc = &dev_priv->mode_reg.crtc_reg[nv_crtc->index].fp_control; | ||
133 | |||
134 | nv_crtc->fp_users &= ~(1 << nouveau_encoder(encoder)->dcb->index); | ||
135 | if (!is_fpc_off(*fpc) && !nv_crtc->fp_users) { | ||
136 | nv_crtc->dpms_saved_fp_control = *fpc; | ||
137 | /* cut the FP output */ | ||
138 | *fpc &= ~FP_TG_CONTROL_ON; | ||
139 | *fpc |= FP_TG_CONTROL_OFF; | ||
140 | NVWriteRAMDAC(dev, nv_crtc->index, | ||
141 | NV_PRAMDAC_FP_TG_CONTROL, *fpc); | ||
142 | } | ||
143 | } | ||
144 | } | ||
145 | } | ||
146 | |||
147 | static bool nv04_dfp_mode_fixup(struct drm_encoder *encoder, | ||
148 | struct drm_display_mode *mode, | ||
149 | struct drm_display_mode *adjusted_mode) | ||
150 | { | ||
151 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
152 | struct nouveau_connector *nv_connector = nouveau_encoder_connector_get(nv_encoder); | ||
153 | |||
154 | /* For internal panels and gpu scaling on DVI we need the native mode */ | ||
155 | if (nv_connector->scaling_mode != DRM_MODE_SCALE_NONE) { | ||
156 | if (!nv_connector->native_mode) | ||
157 | return false; | ||
158 | nv_encoder->mode = *nv_connector->native_mode; | ||
159 | adjusted_mode->clock = nv_connector->native_mode->clock; | ||
160 | } else { | ||
161 | nv_encoder->mode = *adjusted_mode; | ||
162 | } | ||
163 | |||
164 | return true; | ||
165 | } | ||
166 | |||
167 | static void nv04_dfp_prepare_sel_clk(struct drm_device *dev, | ||
168 | struct nouveau_encoder *nv_encoder, int head) | ||
169 | { | ||
170 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
171 | struct nv04_mode_state *state = &dev_priv->mode_reg; | ||
172 | uint32_t bits1618 = nv_encoder->dcb->or & OUTPUT_A ? 0x10000 : 0x40000; | ||
173 | |||
174 | if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP) | ||
175 | return; | ||
176 | |||
177 | /* SEL_CLK is only used on the primary ramdac | ||
178 | * It toggles spread spectrum PLL output and sets the bindings of PLLs | ||
179 | * to heads on digital outputs | ||
180 | */ | ||
181 | if (head) | ||
182 | state->sel_clk |= bits1618; | ||
183 | else | ||
184 | state->sel_clk &= ~bits1618; | ||
185 | |||
186 | /* nv30: | ||
187 | * bit 0 NVClk spread spectrum on/off | ||
188 | * bit 2 MemClk spread spectrum on/off | ||
189 | * bit 4 PixClk1 spread spectrum on/off toggle | ||
190 | * bit 6 PixClk2 spread spectrum on/off toggle | ||
191 | * | ||
192 | * nv40 (observations from bios behaviour and mmio traces): | ||
193 | * bits 4&6 as for nv30 | ||
194 | * bits 5&7 head dependent as for bits 4&6, but do not appear with 4&6; | ||
195 | * maybe a different spread mode | ||
196 | * bits 8&10 seen on dual-link dvi outputs, purpose unknown (set by POST scripts) | ||
197 | * The logic behind turning spread spectrum on/off in the first place, | ||
198 | * and which bit-pair to use, is unclear on nv40 (for earlier cards, the fp table | ||
199 | * entry has the necessary info) | ||
200 | */ | ||
201 | if (nv_encoder->dcb->type == OUTPUT_LVDS && dev_priv->saved_reg.sel_clk & 0xf0) { | ||
202 | int shift = (dev_priv->saved_reg.sel_clk & 0x50) ? 0 : 1; | ||
203 | |||
204 | state->sel_clk &= ~0xf0; | ||
205 | state->sel_clk |= (head ? 0x40 : 0x10) << shift; | ||
206 | } | ||
207 | } | ||
208 | |||
209 | static void nv04_dfp_prepare(struct drm_encoder *encoder) | ||
210 | { | ||
211 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
212 | struct drm_encoder_helper_funcs *helper = encoder->helper_private; | ||
213 | struct drm_device *dev = encoder->dev; | ||
214 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
215 | int head = nouveau_crtc(encoder->crtc)->index; | ||
216 | struct nv04_crtc_reg *crtcstate = dev_priv->mode_reg.crtc_reg; | ||
217 | uint8_t *cr_lcd = &crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX]; | ||
218 | uint8_t *cr_lcd_oth = &crtcstate[head ^ 1].CRTC[NV_CIO_CRE_LCD__INDEX]; | ||
219 | |||
220 | helper->dpms(encoder, DRM_MODE_DPMS_OFF); | ||
221 | |||
222 | nv04_dfp_prepare_sel_clk(dev, nv_encoder, head); | ||
223 | |||
224 | /* Some NV4x have unknown values (0x3f, 0x50, 0x54, 0x6b, 0x79, 0x7f) | ||
225 | * at LCD__INDEX which we don't alter | ||
226 | */ | ||
227 | if (!(*cr_lcd & 0x44)) { | ||
228 | *cr_lcd = 0x3; | ||
229 | |||
230 | if (nv_two_heads(dev)) { | ||
231 | if (nv_encoder->dcb->location == DCB_LOC_ON_CHIP) | ||
232 | *cr_lcd |= head ? 0x0 : 0x8; | ||
233 | else { | ||
234 | *cr_lcd |= (nv_encoder->dcb->or << 4) & 0x30; | ||
235 | if (nv_encoder->dcb->type == OUTPUT_LVDS) | ||
236 | *cr_lcd |= 0x30; | ||
237 | if ((*cr_lcd & 0x30) == (*cr_lcd_oth & 0x30)) { | ||
238 | /* avoid being connected to both crtcs */ | ||
239 | *cr_lcd_oth &= ~0x30; | ||
240 | NVWriteVgaCrtc(dev, head ^ 1, | ||
241 | NV_CIO_CRE_LCD__INDEX, | ||
242 | *cr_lcd_oth); | ||
243 | } | ||
244 | } | ||
245 | } | ||
246 | } | ||
247 | } | ||
248 | |||
249 | |||
250 | static void nv04_dfp_mode_set(struct drm_encoder *encoder, | ||
251 | struct drm_display_mode *mode, | ||
252 | struct drm_display_mode *adjusted_mode) | ||
253 | { | ||
254 | struct drm_device *dev = encoder->dev; | ||
255 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
256 | struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); | ||
257 | struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index]; | ||
258 | struct nv04_crtc_reg *savep = &dev_priv->saved_reg.crtc_reg[nv_crtc->index]; | ||
259 | struct nouveau_connector *nv_connector = nouveau_crtc_connector_get(nv_crtc); | ||
260 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
261 | struct drm_display_mode *output_mode = &nv_encoder->mode; | ||
262 | uint32_t mode_ratio, panel_ratio; | ||
263 | |||
264 | NV_DEBUG(dev, "Output mode on CRTC %d:\n", nv_crtc->index); | ||
265 | drm_mode_debug_printmodeline(output_mode); | ||
266 | |||
267 | /* Initialize the FP registers in this CRTC. */ | ||
268 | regp->fp_horiz_regs[FP_DISPLAY_END] = output_mode->hdisplay - 1; | ||
269 | regp->fp_horiz_regs[FP_TOTAL] = output_mode->htotal - 1; | ||
270 | if (!nv_gf4_disp_arch(dev) || | ||
271 | (output_mode->hsync_start - output_mode->hdisplay) >= | ||
272 | dev_priv->vbios->digital_min_front_porch) | ||
273 | regp->fp_horiz_regs[FP_CRTC] = output_mode->hdisplay; | ||
274 | else | ||
275 | regp->fp_horiz_regs[FP_CRTC] = output_mode->hsync_start - dev_priv->vbios->digital_min_front_porch - 1; | ||
276 | regp->fp_horiz_regs[FP_SYNC_START] = output_mode->hsync_start - 1; | ||
277 | regp->fp_horiz_regs[FP_SYNC_END] = output_mode->hsync_end - 1; | ||
278 | regp->fp_horiz_regs[FP_VALID_START] = output_mode->hskew; | ||
279 | regp->fp_horiz_regs[FP_VALID_END] = output_mode->hdisplay - 1; | ||
280 | |||
281 | regp->fp_vert_regs[FP_DISPLAY_END] = output_mode->vdisplay - 1; | ||
282 | regp->fp_vert_regs[FP_TOTAL] = output_mode->vtotal - 1; | ||
283 | regp->fp_vert_regs[FP_CRTC] = output_mode->vtotal - 5 - 1; | ||
284 | regp->fp_vert_regs[FP_SYNC_START] = output_mode->vsync_start - 1; | ||
285 | regp->fp_vert_regs[FP_SYNC_END] = output_mode->vsync_end - 1; | ||
286 | regp->fp_vert_regs[FP_VALID_START] = 0; | ||
287 | regp->fp_vert_regs[FP_VALID_END] = output_mode->vdisplay - 1; | ||
288 | |||
289 | /* bit26: a bit seen on some g7x, no as yet discernable purpose */ | ||
290 | regp->fp_control = NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS | | ||
291 | (savep->fp_control & (1 << 26 | NV_PRAMDAC_FP_TG_CONTROL_READ_PROG)); | ||
292 | /* Deal with vsync/hsync polarity */ | ||
293 | /* LVDS screens do set this, but modes with +ve syncs are very rare */ | ||
294 | if (output_mode->flags & DRM_MODE_FLAG_PVSYNC) | ||
295 | regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS; | ||
296 | if (output_mode->flags & DRM_MODE_FLAG_PHSYNC) | ||
297 | regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS; | ||
298 | /* panel scaling first, as native would get set otherwise */ | ||
299 | if (nv_connector->scaling_mode == DRM_MODE_SCALE_NONE || | ||
300 | nv_connector->scaling_mode == DRM_MODE_SCALE_CENTER) /* panel handles it */ | ||
301 | regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_CENTER; | ||
302 | else if (adjusted_mode->hdisplay == output_mode->hdisplay && | ||
303 | adjusted_mode->vdisplay == output_mode->vdisplay) /* native mode */ | ||
304 | regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_NATIVE; | ||
305 | else /* gpu needs to scale */ | ||
306 | regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_SCALE; | ||
307 | if (nvReadEXTDEV(dev, NV_PEXTDEV_BOOT_0) & NV_PEXTDEV_BOOT_0_STRAP_FP_IFACE_12BIT) | ||
308 | regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12; | ||
309 | if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP && | ||
310 | output_mode->clock > 165000) | ||
311 | regp->fp_control |= (2 << 24); | ||
312 | if (nv_encoder->dcb->type == OUTPUT_LVDS) { | ||
313 | bool duallink, dummy; | ||
314 | |||
315 | nouveau_bios_parse_lvds_table(dev, nv_connector->native_mode-> | ||
316 | clock, &duallink, &dummy); | ||
317 | if (duallink) | ||
318 | regp->fp_control |= (8 << 28); | ||
319 | } else | ||
320 | if (output_mode->clock > 165000) | ||
321 | regp->fp_control |= (8 << 28); | ||
322 | |||
323 | regp->fp_debug_0 = NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND | | ||
324 | NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND | | ||
325 | NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR | | ||
326 | NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR | | ||
327 | NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED | | ||
328 | NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE | | ||
329 | NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE; | ||
330 | |||
331 | /* We want automatic scaling */ | ||
332 | regp->fp_debug_1 = 0; | ||
333 | /* This can override HTOTAL and VTOTAL */ | ||
334 | regp->fp_debug_2 = 0; | ||
335 | |||
336 | /* Use 20.12 fixed point format to avoid floats */ | ||
337 | mode_ratio = (1 << 12) * adjusted_mode->hdisplay / adjusted_mode->vdisplay; | ||
338 | panel_ratio = (1 << 12) * output_mode->hdisplay / output_mode->vdisplay; | ||
339 | /* if ratios are equal, SCALE_ASPECT will automatically (and correctly) | ||
340 | * get treated the same as SCALE_FULLSCREEN */ | ||
341 | if (nv_connector->scaling_mode == DRM_MODE_SCALE_ASPECT && | ||
342 | mode_ratio != panel_ratio) { | ||
343 | uint32_t diff, scale; | ||
344 | bool divide_by_2 = nv_gf4_disp_arch(dev); | ||
345 | |||
346 | if (mode_ratio < panel_ratio) { | ||
347 | /* vertical needs to expand to glass size (automatic) | ||
348 | * horizontal needs to be scaled at vertical scale factor | ||
349 | * to maintain aspect */ | ||
350 | |||
351 | scale = (1 << 12) * adjusted_mode->vdisplay / output_mode->vdisplay; | ||
352 | regp->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE | | ||
353 | XLATE(scale, divide_by_2, NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE); | ||
354 | |||
355 | /* restrict area of screen used, horizontally */ | ||
356 | diff = output_mode->hdisplay - | ||
357 | output_mode->vdisplay * mode_ratio / (1 << 12); | ||
358 | regp->fp_horiz_regs[FP_VALID_START] += diff / 2; | ||
359 | regp->fp_horiz_regs[FP_VALID_END] -= diff / 2; | ||
360 | } | ||
361 | |||
362 | if (mode_ratio > panel_ratio) { | ||
363 | /* horizontal needs to expand to glass size (automatic) | ||
364 | * vertical needs to be scaled at horizontal scale factor | ||
365 | * to maintain aspect */ | ||
366 | |||
367 | scale = (1 << 12) * adjusted_mode->hdisplay / output_mode->hdisplay; | ||
368 | regp->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE | | ||
369 | XLATE(scale, divide_by_2, NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE); | ||
370 | |||
371 | /* restrict area of screen used, vertically */ | ||
372 | diff = output_mode->vdisplay - | ||
373 | (1 << 12) * output_mode->hdisplay / mode_ratio; | ||
374 | regp->fp_vert_regs[FP_VALID_START] += diff / 2; | ||
375 | regp->fp_vert_regs[FP_VALID_END] -= diff / 2; | ||
376 | } | ||
377 | } | ||
378 | |||
379 | /* Output property. */ | ||
380 | if (nv_connector->use_dithering) { | ||
381 | if (dev_priv->chipset == 0x11) | ||
382 | regp->dither = savep->dither | 0x00010000; | ||
383 | else { | ||
384 | int i; | ||
385 | regp->dither = savep->dither | 0x00000001; | ||
386 | for (i = 0; i < 3; i++) { | ||
387 | regp->dither_regs[i] = 0xe4e4e4e4; | ||
388 | regp->dither_regs[i + 3] = 0x44444444; | ||
389 | } | ||
390 | } | ||
391 | } else { | ||
392 | if (dev_priv->chipset != 0x11) { | ||
393 | /* reset them */ | ||
394 | int i; | ||
395 | for (i = 0; i < 3; i++) { | ||
396 | regp->dither_regs[i] = savep->dither_regs[i]; | ||
397 | regp->dither_regs[i + 3] = savep->dither_regs[i + 3]; | ||
398 | } | ||
399 | } | ||
400 | regp->dither = savep->dither; | ||
401 | } | ||
402 | |||
403 | regp->fp_margin_color = 0; | ||
404 | } | ||
405 | |||
406 | static void nv04_dfp_commit(struct drm_encoder *encoder) | ||
407 | { | ||
408 | struct drm_device *dev = encoder->dev; | ||
409 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
410 | struct drm_encoder_helper_funcs *helper = encoder->helper_private; | ||
411 | struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); | ||
412 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
413 | struct dcb_entry *dcbe = nv_encoder->dcb; | ||
414 | int head = nouveau_crtc(encoder->crtc)->index; | ||
415 | |||
416 | NV_TRACE(dev, "%s called for encoder %d\n", __func__, nv_encoder->dcb->index); | ||
417 | |||
418 | if (dcbe->type == OUTPUT_TMDS) | ||
419 | run_tmds_table(dev, dcbe, head, nv_encoder->mode.clock); | ||
420 | else if (dcbe->type == OUTPUT_LVDS) | ||
421 | call_lvds_script(dev, dcbe, head, LVDS_RESET, nv_encoder->mode.clock); | ||
422 | |||
423 | /* update fp_control state for any changes made by scripts, | ||
424 | * so correct value is written at DPMS on */ | ||
425 | dev_priv->mode_reg.crtc_reg[head].fp_control = | ||
426 | NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL); | ||
427 | |||
428 | /* This could use refinement for flatpanels, but it should work this way */ | ||
429 | if (dev_priv->chipset < 0x44) | ||
430 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0xf0000000); | ||
431 | else | ||
432 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000); | ||
433 | |||
434 | helper->dpms(encoder, DRM_MODE_DPMS_ON); | ||
435 | |||
436 | NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n", | ||
437 | drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base), | ||
438 | nv_crtc->index, '@' + ffs(nv_encoder->dcb->or)); | ||
439 | } | ||
440 | |||
441 | static inline bool is_powersaving_dpms(int mode) | ||
442 | { | ||
443 | return (mode != DRM_MODE_DPMS_ON); | ||
444 | } | ||
445 | |||
446 | static void nv04_lvds_dpms(struct drm_encoder *encoder, int mode) | ||
447 | { | ||
448 | struct drm_device *dev = encoder->dev; | ||
449 | struct drm_crtc *crtc = encoder->crtc; | ||
450 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
451 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
452 | bool was_powersaving = is_powersaving_dpms(nv_encoder->last_dpms); | ||
453 | |||
454 | if (nv_encoder->last_dpms == mode) | ||
455 | return; | ||
456 | nv_encoder->last_dpms = mode; | ||
457 | |||
458 | NV_INFO(dev, "Setting dpms mode %d on lvds encoder (output %d)\n", | ||
459 | mode, nv_encoder->dcb->index); | ||
460 | |||
461 | if (was_powersaving && is_powersaving_dpms(mode)) | ||
462 | return; | ||
463 | |||
464 | if (nv_encoder->dcb->lvdsconf.use_power_scripts) { | ||
465 | struct nouveau_connector *nv_connector = nouveau_encoder_connector_get(nv_encoder); | ||
466 | |||
467 | /* when removing an output, crtc may not be set, but PANEL_OFF | ||
468 | * must still be run | ||
469 | */ | ||
470 | int head = crtc ? nouveau_crtc(crtc)->index : | ||
471 | nv04_dfp_get_bound_head(dev, nv_encoder->dcb); | ||
472 | |||
473 | if (mode == DRM_MODE_DPMS_ON) { | ||
474 | if (!nv_connector->native_mode) { | ||
475 | NV_ERROR(dev, "Not turning on LVDS without native mode\n"); | ||
476 | return; | ||
477 | } | ||
478 | call_lvds_script(dev, nv_encoder->dcb, head, | ||
479 | LVDS_PANEL_ON, nv_connector->native_mode->clock); | ||
480 | } else | ||
481 | /* pxclk of 0 is fine for PANEL_OFF, and for a | ||
482 | * disconnected LVDS encoder there is no native_mode | ||
483 | */ | ||
484 | call_lvds_script(dev, nv_encoder->dcb, head, | ||
485 | LVDS_PANEL_OFF, 0); | ||
486 | } | ||
487 | |||
488 | nv04_dfp_update_fp_control(encoder, mode); | ||
489 | |||
490 | if (mode == DRM_MODE_DPMS_ON) | ||
491 | nv04_dfp_prepare_sel_clk(dev, nv_encoder, nouveau_crtc(crtc)->index); | ||
492 | else { | ||
493 | dev_priv->mode_reg.sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK); | ||
494 | dev_priv->mode_reg.sel_clk &= ~0xf0; | ||
495 | } | ||
496 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, dev_priv->mode_reg.sel_clk); | ||
497 | } | ||
498 | |||
499 | static void nv04_tmds_dpms(struct drm_encoder *encoder, int mode) | ||
500 | { | ||
501 | struct drm_device *dev = encoder->dev; | ||
502 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
503 | |||
504 | if (nv_encoder->last_dpms == mode) | ||
505 | return; | ||
506 | nv_encoder->last_dpms = mode; | ||
507 | |||
508 | NV_INFO(dev, "Setting dpms mode %d on tmds encoder (output %d)\n", | ||
509 | mode, nv_encoder->dcb->index); | ||
510 | |||
511 | nv04_dfp_update_fp_control(encoder, mode); | ||
512 | } | ||
513 | |||
514 | static void nv04_dfp_save(struct drm_encoder *encoder) | ||
515 | { | ||
516 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
517 | struct drm_device *dev = encoder->dev; | ||
518 | |||
519 | if (nv_two_heads(dev)) | ||
520 | nv_encoder->restore.head = | ||
521 | nv04_dfp_get_bound_head(dev, nv_encoder->dcb); | ||
522 | } | ||
523 | |||
524 | static void nv04_dfp_restore(struct drm_encoder *encoder) | ||
525 | { | ||
526 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
527 | struct drm_device *dev = encoder->dev; | ||
528 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
529 | int head = nv_encoder->restore.head; | ||
530 | |||
531 | if (nv_encoder->dcb->type == OUTPUT_LVDS) { | ||
532 | struct drm_display_mode *native_mode = nouveau_encoder_connector_get(nv_encoder)->native_mode; | ||
533 | if (native_mode) | ||
534 | call_lvds_script(dev, nv_encoder->dcb, head, LVDS_PANEL_ON, | ||
535 | native_mode->clock); | ||
536 | else | ||
537 | NV_ERROR(dev, "Not restoring LVDS without native mode\n"); | ||
538 | |||
539 | } else if (nv_encoder->dcb->type == OUTPUT_TMDS) { | ||
540 | int clock = nouveau_hw_pllvals_to_clk | ||
541 | (&dev_priv->saved_reg.crtc_reg[head].pllvals); | ||
542 | |||
543 | run_tmds_table(dev, nv_encoder->dcb, head, clock); | ||
544 | } | ||
545 | |||
546 | nv_encoder->last_dpms = NV_DPMS_CLEARED; | ||
547 | } | ||
548 | |||
549 | static void nv04_dfp_destroy(struct drm_encoder *encoder) | ||
550 | { | ||
551 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
552 | |||
553 | NV_DEBUG(encoder->dev, "\n"); | ||
554 | |||
555 | drm_encoder_cleanup(encoder); | ||
556 | kfree(nv_encoder); | ||
557 | } | ||
558 | |||
559 | static const struct drm_encoder_helper_funcs nv04_lvds_helper_funcs = { | ||
560 | .dpms = nv04_lvds_dpms, | ||
561 | .save = nv04_dfp_save, | ||
562 | .restore = nv04_dfp_restore, | ||
563 | .mode_fixup = nv04_dfp_mode_fixup, | ||
564 | .prepare = nv04_dfp_prepare, | ||
565 | .commit = nv04_dfp_commit, | ||
566 | .mode_set = nv04_dfp_mode_set, | ||
567 | .detect = NULL, | ||
568 | }; | ||
569 | |||
570 | static const struct drm_encoder_helper_funcs nv04_tmds_helper_funcs = { | ||
571 | .dpms = nv04_tmds_dpms, | ||
572 | .save = nv04_dfp_save, | ||
573 | .restore = nv04_dfp_restore, | ||
574 | .mode_fixup = nv04_dfp_mode_fixup, | ||
575 | .prepare = nv04_dfp_prepare, | ||
576 | .commit = nv04_dfp_commit, | ||
577 | .mode_set = nv04_dfp_mode_set, | ||
578 | .detect = NULL, | ||
579 | }; | ||
580 | |||
581 | static const struct drm_encoder_funcs nv04_dfp_funcs = { | ||
582 | .destroy = nv04_dfp_destroy, | ||
583 | }; | ||
584 | |||
585 | int nv04_dfp_create(struct drm_device *dev, struct dcb_entry *entry) | ||
586 | { | ||
587 | const struct drm_encoder_helper_funcs *helper; | ||
588 | struct drm_encoder *encoder; | ||
589 | struct nouveau_encoder *nv_encoder = NULL; | ||
590 | int type; | ||
591 | |||
592 | switch (entry->type) { | ||
593 | case OUTPUT_TMDS: | ||
594 | type = DRM_MODE_ENCODER_TMDS; | ||
595 | helper = &nv04_tmds_helper_funcs; | ||
596 | break; | ||
597 | case OUTPUT_LVDS: | ||
598 | type = DRM_MODE_ENCODER_LVDS; | ||
599 | helper = &nv04_lvds_helper_funcs; | ||
600 | break; | ||
601 | default: | ||
602 | return -EINVAL; | ||
603 | } | ||
604 | |||
605 | nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); | ||
606 | if (!nv_encoder) | ||
607 | return -ENOMEM; | ||
608 | |||
609 | encoder = to_drm_encoder(nv_encoder); | ||
610 | |||
611 | nv_encoder->dcb = entry; | ||
612 | nv_encoder->or = ffs(entry->or) - 1; | ||
613 | |||
614 | drm_encoder_init(dev, encoder, &nv04_dfp_funcs, type); | ||
615 | drm_encoder_helper_add(encoder, helper); | ||
616 | |||
617 | encoder->possible_crtcs = entry->heads; | ||
618 | encoder->possible_clones = 0; | ||
619 | |||
620 | return 0; | ||
621 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nv04_display.c b/drivers/gpu/drm/nouveau/nv04_display.c new file mode 100644 index 000000000000..b47c757ff48b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv04_display.c | |||
@@ -0,0 +1,288 @@ | |||
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 | |||
25 | #include "drmP.h" | ||
26 | #include "drm.h" | ||
27 | #include "drm_crtc_helper.h" | ||
28 | |||
29 | #include "nouveau_drv.h" | ||
30 | #include "nouveau_fb.h" | ||
31 | #include "nouveau_hw.h" | ||
32 | #include "nouveau_encoder.h" | ||
33 | #include "nouveau_connector.h" | ||
34 | |||
35 | #define MULTIPLE_ENCODERS(e) (e & (e - 1)) | ||
36 | |||
37 | static void | ||
38 | nv04_display_store_initial_head_owner(struct drm_device *dev) | ||
39 | { | ||
40 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
41 | |||
42 | if (dev_priv->chipset != 0x11) { | ||
43 | dev_priv->crtc_owner = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_44); | ||
44 | goto ownerknown; | ||
45 | } | ||
46 | |||
47 | /* reading CR44 is broken on nv11, so we attempt to infer it */ | ||
48 | if (nvReadMC(dev, NV_PBUS_DEBUG_1) & (1 << 28)) /* heads tied, restore both */ | ||
49 | dev_priv->crtc_owner = 0x4; | ||
50 | else { | ||
51 | uint8_t slaved_on_A, slaved_on_B; | ||
52 | bool tvA = false; | ||
53 | bool tvB = false; | ||
54 | |||
55 | NVLockVgaCrtcs(dev, false); | ||
56 | |||
57 | slaved_on_B = NVReadVgaCrtc(dev, 1, NV_CIO_CRE_PIXEL_INDEX) & | ||
58 | 0x80; | ||
59 | if (slaved_on_B) | ||
60 | tvB = !(NVReadVgaCrtc(dev, 1, NV_CIO_CRE_LCD__INDEX) & | ||
61 | MASK(NV_CIO_CRE_LCD_LCD_SELECT)); | ||
62 | |||
63 | slaved_on_A = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX) & | ||
64 | 0x80; | ||
65 | if (slaved_on_A) | ||
66 | tvA = !(NVReadVgaCrtc(dev, 0, NV_CIO_CRE_LCD__INDEX) & | ||
67 | MASK(NV_CIO_CRE_LCD_LCD_SELECT)); | ||
68 | |||
69 | NVLockVgaCrtcs(dev, true); | ||
70 | |||
71 | if (slaved_on_A && !tvA) | ||
72 | dev_priv->crtc_owner = 0x0; | ||
73 | else if (slaved_on_B && !tvB) | ||
74 | dev_priv->crtc_owner = 0x3; | ||
75 | else if (slaved_on_A) | ||
76 | dev_priv->crtc_owner = 0x0; | ||
77 | else if (slaved_on_B) | ||
78 | dev_priv->crtc_owner = 0x3; | ||
79 | else | ||
80 | dev_priv->crtc_owner = 0x0; | ||
81 | } | ||
82 | |||
83 | ownerknown: | ||
84 | NV_INFO(dev, "Initial CRTC_OWNER is %d\n", dev_priv->crtc_owner); | ||
85 | |||
86 | /* we need to ensure the heads are not tied henceforth, or reading any | ||
87 | * 8 bit reg on head B will fail | ||
88 | * setting a single arbitrary head solves that */ | ||
89 | NVSetOwner(dev, 0); | ||
90 | } | ||
91 | |||
92 | int | ||
93 | nv04_display_create(struct drm_device *dev) | ||
94 | { | ||
95 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
96 | struct parsed_dcb *dcb = dev_priv->vbios->dcb; | ||
97 | struct drm_encoder *encoder; | ||
98 | struct drm_crtc *crtc; | ||
99 | uint16_t connector[16] = { 0 }; | ||
100 | int i, ret; | ||
101 | |||
102 | NV_DEBUG(dev, "\n"); | ||
103 | |||
104 | if (nv_two_heads(dev)) | ||
105 | nv04_display_store_initial_head_owner(dev); | ||
106 | |||
107 | drm_mode_config_init(dev); | ||
108 | drm_mode_create_scaling_mode_property(dev); | ||
109 | drm_mode_create_dithering_property(dev); | ||
110 | |||
111 | dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs; | ||
112 | |||
113 | dev->mode_config.min_width = 0; | ||
114 | dev->mode_config.min_height = 0; | ||
115 | switch (dev_priv->card_type) { | ||
116 | case NV_04: | ||
117 | dev->mode_config.max_width = 2048; | ||
118 | dev->mode_config.max_height = 2048; | ||
119 | break; | ||
120 | default: | ||
121 | dev->mode_config.max_width = 4096; | ||
122 | dev->mode_config.max_height = 4096; | ||
123 | break; | ||
124 | } | ||
125 | |||
126 | dev->mode_config.fb_base = dev_priv->fb_phys; | ||
127 | |||
128 | nv04_crtc_create(dev, 0); | ||
129 | if (nv_two_heads(dev)) | ||
130 | nv04_crtc_create(dev, 1); | ||
131 | |||
132 | for (i = 0; i < dcb->entries; i++) { | ||
133 | struct dcb_entry *dcbent = &dcb->entry[i]; | ||
134 | |||
135 | switch (dcbent->type) { | ||
136 | case OUTPUT_ANALOG: | ||
137 | ret = nv04_dac_create(dev, dcbent); | ||
138 | break; | ||
139 | case OUTPUT_LVDS: | ||
140 | case OUTPUT_TMDS: | ||
141 | ret = nv04_dfp_create(dev, dcbent); | ||
142 | break; | ||
143 | case OUTPUT_TV: | ||
144 | if (dcbent->location == DCB_LOC_ON_CHIP) | ||
145 | ret = nv17_tv_create(dev, dcbent); | ||
146 | else | ||
147 | ret = nv04_tv_create(dev, dcbent); | ||
148 | break; | ||
149 | default: | ||
150 | NV_WARN(dev, "DCB type %d not known\n", dcbent->type); | ||
151 | continue; | ||
152 | } | ||
153 | |||
154 | if (ret) | ||
155 | continue; | ||
156 | |||
157 | connector[dcbent->connector] |= (1 << dcbent->type); | ||
158 | } | ||
159 | |||
160 | for (i = 0; i < dcb->entries; i++) { | ||
161 | struct dcb_entry *dcbent = &dcb->entry[i]; | ||
162 | uint16_t encoders; | ||
163 | int type; | ||
164 | |||
165 | encoders = connector[dcbent->connector]; | ||
166 | if (!(encoders & (1 << dcbent->type))) | ||
167 | continue; | ||
168 | connector[dcbent->connector] = 0; | ||
169 | |||
170 | switch (dcbent->type) { | ||
171 | case OUTPUT_ANALOG: | ||
172 | if (!MULTIPLE_ENCODERS(encoders)) | ||
173 | type = DRM_MODE_CONNECTOR_VGA; | ||
174 | else | ||
175 | type = DRM_MODE_CONNECTOR_DVII; | ||
176 | break; | ||
177 | case OUTPUT_TMDS: | ||
178 | if (!MULTIPLE_ENCODERS(encoders)) | ||
179 | type = DRM_MODE_CONNECTOR_DVID; | ||
180 | else | ||
181 | type = DRM_MODE_CONNECTOR_DVII; | ||
182 | break; | ||
183 | case OUTPUT_LVDS: | ||
184 | type = DRM_MODE_CONNECTOR_LVDS; | ||
185 | #if 0 | ||
186 | /* don't create i2c adapter when lvds ddc not allowed */ | ||
187 | if (dcbent->lvdsconf.use_straps_for_mode || | ||
188 | dev_priv->vbios->fp_no_ddc) | ||
189 | i2c_index = 0xf; | ||
190 | #endif | ||
191 | break; | ||
192 | case OUTPUT_TV: | ||
193 | type = DRM_MODE_CONNECTOR_TV; | ||
194 | break; | ||
195 | default: | ||
196 | type = DRM_MODE_CONNECTOR_Unknown; | ||
197 | continue; | ||
198 | } | ||
199 | |||
200 | nouveau_connector_create(dev, dcbent->connector, type); | ||
201 | } | ||
202 | |||
203 | /* Save previous state */ | ||
204 | NVLockVgaCrtcs(dev, false); | ||
205 | |||
206 | nouveau_hw_save_vga_fonts(dev, 1); | ||
207 | |||
208 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) | ||
209 | crtc->funcs->save(crtc); | ||
210 | |||
211 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | ||
212 | struct drm_encoder_helper_funcs *func = encoder->helper_private; | ||
213 | |||
214 | func->save(encoder); | ||
215 | } | ||
216 | |||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | void | ||
221 | nv04_display_destroy(struct drm_device *dev) | ||
222 | { | ||
223 | struct drm_encoder *encoder; | ||
224 | struct drm_crtc *crtc; | ||
225 | |||
226 | NV_DEBUG(dev, "\n"); | ||
227 | |||
228 | /* Turn every CRTC off. */ | ||
229 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | ||
230 | struct drm_mode_set modeset = { | ||
231 | .crtc = crtc, | ||
232 | }; | ||
233 | |||
234 | crtc->funcs->set_config(&modeset); | ||
235 | } | ||
236 | |||
237 | /* Restore state */ | ||
238 | NVLockVgaCrtcs(dev, false); | ||
239 | |||
240 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | ||
241 | struct drm_encoder_helper_funcs *func = encoder->helper_private; | ||
242 | |||
243 | func->restore(encoder); | ||
244 | } | ||
245 | |||
246 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) | ||
247 | crtc->funcs->restore(crtc); | ||
248 | |||
249 | nouveau_hw_save_vga_fonts(dev, 0); | ||
250 | |||
251 | drm_mode_config_cleanup(dev); | ||
252 | } | ||
253 | |||
254 | void | ||
255 | nv04_display_restore(struct drm_device *dev) | ||
256 | { | ||
257 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
258 | struct drm_encoder *encoder; | ||
259 | struct drm_crtc *crtc; | ||
260 | |||
261 | NVLockVgaCrtcs(dev, false); | ||
262 | |||
263 | /* meh.. modeset apparently doesn't setup all the regs and depends | ||
264 | * on pre-existing state, for now load the state of the card *before* | ||
265 | * nouveau was loaded, and then do a modeset. | ||
266 | * | ||
267 | * best thing to do probably is to make save/restore routines not | ||
268 | * save/restore "pre-load" state, but more general so we can save | ||
269 | * on suspend too. | ||
270 | */ | ||
271 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | ||
272 | struct drm_encoder_helper_funcs *func = encoder->helper_private; | ||
273 | |||
274 | func->restore(encoder); | ||
275 | } | ||
276 | |||
277 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) | ||
278 | crtc->funcs->restore(crtc); | ||
279 | |||
280 | if (nv_two_heads(dev)) { | ||
281 | NV_INFO(dev, "Restoring CRTC_OWNER to %d.\n", | ||
282 | dev_priv->crtc_owner); | ||
283 | NVSetOwner(dev, dev_priv->crtc_owner); | ||
284 | } | ||
285 | |||
286 | NVLockVgaCrtcs(dev, true); | ||
287 | } | ||
288 | |||
diff --git a/drivers/gpu/drm/nouveau/nv04_fb.c b/drivers/gpu/drm/nouveau/nv04_fb.c new file mode 100644 index 000000000000..638cf601c427 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv04_fb.c | |||
@@ -0,0 +1,21 @@ | |||
1 | #include "drmP.h" | ||
2 | #include "drm.h" | ||
3 | #include "nouveau_drv.h" | ||
4 | #include "nouveau_drm.h" | ||
5 | |||
6 | int | ||
7 | nv04_fb_init(struct drm_device *dev) | ||
8 | { | ||
9 | /* This is what the DDX did for NV_ARCH_04, but a mmio-trace shows | ||
10 | * nvidia reading PFB_CFG_0, then writing back its original value. | ||
11 | * (which was 0x701114 in this case) | ||
12 | */ | ||
13 | |||
14 | nv_wr32(dev, NV04_PFB_CFG0, 0x1114); | ||
15 | return 0; | ||
16 | } | ||
17 | |||
18 | void | ||
19 | nv04_fb_takedown(struct drm_device *dev) | ||
20 | { | ||
21 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nv04_fbcon.c b/drivers/gpu/drm/nouveau/nv04_fbcon.c new file mode 100644 index 000000000000..09a31071ee58 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv04_fbcon.c | |||
@@ -0,0 +1,316 @@ | |||
1 | /* | ||
2 | * Copyright 2009 Ben Skeggs | ||
3 | * Copyright 2008 Stuart Bennett | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
6 | * copy of this software and associated documentation files (the "Software"), | ||
7 | * to deal in the Software without restriction, including without limitation | ||
8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
9 | * and/or sell copies of the Software, and to permit persons to whom the | ||
10 | * Software is furnished to do so, subject to the following conditions: | ||
11 | * | ||
12 | * The above copyright notice and this permission notice (including the next | ||
13 | * paragraph) shall be included in all copies or substantial portions of the | ||
14 | * 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
22 | * DEALINGS IN THE SOFTWARE. | ||
23 | */ | ||
24 | |||
25 | #include "drmP.h" | ||
26 | #include "nouveau_drv.h" | ||
27 | #include "nouveau_dma.h" | ||
28 | #include "nouveau_fbcon.h" | ||
29 | |||
30 | static void | ||
31 | nv04_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region) | ||
32 | { | ||
33 | struct nouveau_fbcon_par *par = info->par; | ||
34 | struct drm_device *dev = par->dev; | ||
35 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
36 | struct nouveau_channel *chan = dev_priv->channel; | ||
37 | |||
38 | if (info->state != FBINFO_STATE_RUNNING) | ||
39 | return; | ||
40 | |||
41 | if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 4)) { | ||
42 | NV_ERROR(dev, "GPU lockup - switching to software fbcon\n"); | ||
43 | info->flags |= FBINFO_HWACCEL_DISABLED; | ||
44 | } | ||
45 | |||
46 | if (info->flags & FBINFO_HWACCEL_DISABLED) { | ||
47 | cfb_copyarea(info, region); | ||
48 | return; | ||
49 | } | ||
50 | |||
51 | BEGIN_RING(chan, NvSubImageBlit, 0x0300, 3); | ||
52 | OUT_RING(chan, (region->sy << 16) | region->sx); | ||
53 | OUT_RING(chan, (region->dy << 16) | region->dx); | ||
54 | OUT_RING(chan, (region->height << 16) | region->width); | ||
55 | FIRE_RING(chan); | ||
56 | } | ||
57 | |||
58 | static void | ||
59 | nv04_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect) | ||
60 | { | ||
61 | struct nouveau_fbcon_par *par = info->par; | ||
62 | struct drm_device *dev = par->dev; | ||
63 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
64 | struct nouveau_channel *chan = dev_priv->channel; | ||
65 | uint32_t color = ((uint32_t *) info->pseudo_palette)[rect->color]; | ||
66 | |||
67 | if (info->state != FBINFO_STATE_RUNNING) | ||
68 | return; | ||
69 | |||
70 | if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 7)) { | ||
71 | NV_ERROR(dev, "GPU lockup - switching to software fbcon\n"); | ||
72 | info->flags |= FBINFO_HWACCEL_DISABLED; | ||
73 | } | ||
74 | |||
75 | if (info->flags & FBINFO_HWACCEL_DISABLED) { | ||
76 | cfb_fillrect(info, rect); | ||
77 | return; | ||
78 | } | ||
79 | |||
80 | BEGIN_RING(chan, NvSubGdiRect, 0x02fc, 1); | ||
81 | OUT_RING(chan, (rect->rop != ROP_COPY) ? 1 : 3); | ||
82 | BEGIN_RING(chan, NvSubGdiRect, 0x03fc, 1); | ||
83 | OUT_RING(chan, color); | ||
84 | BEGIN_RING(chan, NvSubGdiRect, 0x0400, 2); | ||
85 | OUT_RING(chan, (rect->dx << 16) | rect->dy); | ||
86 | OUT_RING(chan, (rect->width << 16) | rect->height); | ||
87 | FIRE_RING(chan); | ||
88 | } | ||
89 | |||
90 | static void | ||
91 | nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image) | ||
92 | { | ||
93 | struct nouveau_fbcon_par *par = info->par; | ||
94 | struct drm_device *dev = par->dev; | ||
95 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
96 | struct nouveau_channel *chan = dev_priv->channel; | ||
97 | uint32_t fg; | ||
98 | uint32_t bg; | ||
99 | uint32_t dsize; | ||
100 | uint32_t width; | ||
101 | uint32_t *data = (uint32_t *)image->data; | ||
102 | |||
103 | if (info->state != FBINFO_STATE_RUNNING) | ||
104 | return; | ||
105 | |||
106 | if (image->depth != 1) { | ||
107 | cfb_imageblit(info, image); | ||
108 | return; | ||
109 | } | ||
110 | |||
111 | if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 8)) { | ||
112 | NV_ERROR(dev, "GPU lockup - switching to software fbcon\n"); | ||
113 | info->flags |= FBINFO_HWACCEL_DISABLED; | ||
114 | } | ||
115 | |||
116 | if (info->flags & FBINFO_HWACCEL_DISABLED) { | ||
117 | cfb_imageblit(info, image); | ||
118 | return; | ||
119 | } | ||
120 | |||
121 | width = (image->width + 31) & ~31; | ||
122 | dsize = (width * image->height) >> 5; | ||
123 | |||
124 | if (info->fix.visual == FB_VISUAL_TRUECOLOR || | ||
125 | info->fix.visual == FB_VISUAL_DIRECTCOLOR) { | ||
126 | fg = ((uint32_t *) info->pseudo_palette)[image->fg_color]; | ||
127 | bg = ((uint32_t *) info->pseudo_palette)[image->bg_color]; | ||
128 | } else { | ||
129 | fg = image->fg_color; | ||
130 | bg = image->bg_color; | ||
131 | } | ||
132 | |||
133 | BEGIN_RING(chan, NvSubGdiRect, 0x0be4, 7); | ||
134 | OUT_RING(chan, (image->dy << 16) | (image->dx & 0xffff)); | ||
135 | OUT_RING(chan, ((image->dy + image->height) << 16) | | ||
136 | ((image->dx + image->width) & 0xffff)); | ||
137 | OUT_RING(chan, bg); | ||
138 | OUT_RING(chan, fg); | ||
139 | OUT_RING(chan, (image->height << 16) | image->width); | ||
140 | OUT_RING(chan, (image->height << 16) | width); | ||
141 | OUT_RING(chan, (image->dy << 16) | (image->dx & 0xffff)); | ||
142 | |||
143 | while (dsize) { | ||
144 | int iter_len = dsize > 128 ? 128 : dsize; | ||
145 | |||
146 | if (RING_SPACE(chan, iter_len + 1)) { | ||
147 | NV_ERROR(dev, "GPU lockup - switching to software fbcon\n"); | ||
148 | info->flags |= FBINFO_HWACCEL_DISABLED; | ||
149 | cfb_imageblit(info, image); | ||
150 | return; | ||
151 | } | ||
152 | |||
153 | BEGIN_RING(chan, NvSubGdiRect, 0x0c00, iter_len); | ||
154 | OUT_RINGp(chan, data, iter_len); | ||
155 | data += iter_len; | ||
156 | dsize -= iter_len; | ||
157 | } | ||
158 | |||
159 | FIRE_RING(chan); | ||
160 | } | ||
161 | |||
162 | static int | ||
163 | nv04_fbcon_grobj_new(struct drm_device *dev, int class, uint32_t handle) | ||
164 | { | ||
165 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
166 | struct nouveau_gpuobj *obj = NULL; | ||
167 | int ret; | ||
168 | |||
169 | ret = nouveau_gpuobj_gr_new(dev_priv->channel, class, &obj); | ||
170 | if (ret) | ||
171 | return ret; | ||
172 | |||
173 | ret = nouveau_gpuobj_ref_add(dev, dev_priv->channel, handle, obj, NULL); | ||
174 | if (ret) | ||
175 | return ret; | ||
176 | |||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | int | ||
181 | nv04_fbcon_accel_init(struct fb_info *info) | ||
182 | { | ||
183 | struct nouveau_fbcon_par *par = info->par; | ||
184 | struct drm_device *dev = par->dev; | ||
185 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
186 | struct nouveau_channel *chan = dev_priv->channel; | ||
187 | int surface_fmt, pattern_fmt, rect_fmt; | ||
188 | int ret; | ||
189 | |||
190 | switch (info->var.bits_per_pixel) { | ||
191 | case 8: | ||
192 | surface_fmt = 1; | ||
193 | pattern_fmt = 3; | ||
194 | rect_fmt = 3; | ||
195 | break; | ||
196 | case 16: | ||
197 | surface_fmt = 4; | ||
198 | pattern_fmt = 1; | ||
199 | rect_fmt = 1; | ||
200 | break; | ||
201 | case 32: | ||
202 | switch (info->var.transp.length) { | ||
203 | case 0: /* depth 24 */ | ||
204 | case 8: /* depth 32 */ | ||
205 | break; | ||
206 | default: | ||
207 | return -EINVAL; | ||
208 | } | ||
209 | |||
210 | surface_fmt = 6; | ||
211 | pattern_fmt = 3; | ||
212 | rect_fmt = 3; | ||
213 | break; | ||
214 | default: | ||
215 | return -EINVAL; | ||
216 | } | ||
217 | |||
218 | ret = nv04_fbcon_grobj_new(dev, dev_priv->card_type >= NV_10 ? | ||
219 | 0x0062 : 0x0042, NvCtxSurf2D); | ||
220 | if (ret) | ||
221 | return ret; | ||
222 | |||
223 | ret = nv04_fbcon_grobj_new(dev, 0x0019, NvClipRect); | ||
224 | if (ret) | ||
225 | return ret; | ||
226 | |||
227 | ret = nv04_fbcon_grobj_new(dev, 0x0043, NvRop); | ||
228 | if (ret) | ||
229 | return ret; | ||
230 | |||
231 | ret = nv04_fbcon_grobj_new(dev, 0x0044, NvImagePatt); | ||
232 | if (ret) | ||
233 | return ret; | ||
234 | |||
235 | ret = nv04_fbcon_grobj_new(dev, 0x004a, NvGdiRect); | ||
236 | if (ret) | ||
237 | return ret; | ||
238 | |||
239 | ret = nv04_fbcon_grobj_new(dev, dev_priv->card_type >= NV_10 ? | ||
240 | 0x009f : 0x005f, NvImageBlit); | ||
241 | if (ret) | ||
242 | return ret; | ||
243 | |||
244 | if (RING_SPACE(chan, 49)) { | ||
245 | NV_ERROR(dev, "GPU lockup - switching to software fbcon\n"); | ||
246 | info->flags |= FBINFO_HWACCEL_DISABLED; | ||
247 | return 0; | ||
248 | } | ||
249 | |||
250 | BEGIN_RING(chan, 1, 0x0000, 1); | ||
251 | OUT_RING(chan, NvCtxSurf2D); | ||
252 | BEGIN_RING(chan, 1, 0x0184, 2); | ||
253 | OUT_RING(chan, NvDmaFB); | ||
254 | OUT_RING(chan, NvDmaFB); | ||
255 | BEGIN_RING(chan, 1, 0x0300, 4); | ||
256 | OUT_RING(chan, surface_fmt); | ||
257 | OUT_RING(chan, info->fix.line_length | (info->fix.line_length << 16)); | ||
258 | OUT_RING(chan, info->fix.smem_start - dev->mode_config.fb_base); | ||
259 | OUT_RING(chan, info->fix.smem_start - dev->mode_config.fb_base); | ||
260 | |||
261 | BEGIN_RING(chan, 1, 0x0000, 1); | ||
262 | OUT_RING(chan, NvRop); | ||
263 | BEGIN_RING(chan, 1, 0x0300, 1); | ||
264 | OUT_RING(chan, 0x55); | ||
265 | |||
266 | BEGIN_RING(chan, 1, 0x0000, 1); | ||
267 | OUT_RING(chan, NvImagePatt); | ||
268 | BEGIN_RING(chan, 1, 0x0300, 8); | ||
269 | OUT_RING(chan, pattern_fmt); | ||
270 | #ifdef __BIG_ENDIAN | ||
271 | OUT_RING(chan, 2); | ||
272 | #else | ||
273 | OUT_RING(chan, 1); | ||
274 | #endif | ||
275 | OUT_RING(chan, 0); | ||
276 | OUT_RING(chan, 1); | ||
277 | OUT_RING(chan, ~0); | ||
278 | OUT_RING(chan, ~0); | ||
279 | OUT_RING(chan, ~0); | ||
280 | OUT_RING(chan, ~0); | ||
281 | |||
282 | BEGIN_RING(chan, 1, 0x0000, 1); | ||
283 | OUT_RING(chan, NvClipRect); | ||
284 | BEGIN_RING(chan, 1, 0x0300, 2); | ||
285 | OUT_RING(chan, 0); | ||
286 | OUT_RING(chan, (info->var.yres_virtual << 16) | info->var.xres_virtual); | ||
287 | |||
288 | BEGIN_RING(chan, NvSubImageBlit, 0x0000, 1); | ||
289 | OUT_RING(chan, NvImageBlit); | ||
290 | BEGIN_RING(chan, NvSubImageBlit, 0x019c, 1); | ||
291 | OUT_RING(chan, NvCtxSurf2D); | ||
292 | BEGIN_RING(chan, NvSubImageBlit, 0x02fc, 1); | ||
293 | OUT_RING(chan, 3); | ||
294 | |||
295 | BEGIN_RING(chan, NvSubGdiRect, 0x0000, 1); | ||
296 | OUT_RING(chan, NvGdiRect); | ||
297 | BEGIN_RING(chan, NvSubGdiRect, 0x0198, 1); | ||
298 | OUT_RING(chan, NvCtxSurf2D); | ||
299 | BEGIN_RING(chan, NvSubGdiRect, 0x0188, 2); | ||
300 | OUT_RING(chan, NvImagePatt); | ||
301 | OUT_RING(chan, NvRop); | ||
302 | BEGIN_RING(chan, NvSubGdiRect, 0x0304, 1); | ||
303 | OUT_RING(chan, 1); | ||
304 | BEGIN_RING(chan, NvSubGdiRect, 0x0300, 1); | ||
305 | OUT_RING(chan, rect_fmt); | ||
306 | BEGIN_RING(chan, NvSubGdiRect, 0x02fc, 1); | ||
307 | OUT_RING(chan, 3); | ||
308 | |||
309 | FIRE_RING(chan); | ||
310 | |||
311 | info->fbops->fb_fillrect = nv04_fbcon_fillrect; | ||
312 | info->fbops->fb_copyarea = nv04_fbcon_copyarea; | ||
313 | info->fbops->fb_imageblit = nv04_fbcon_imageblit; | ||
314 | return 0; | ||
315 | } | ||
316 | |||
diff --git a/drivers/gpu/drm/nouveau/nv04_fifo.c b/drivers/gpu/drm/nouveau/nv04_fifo.c new file mode 100644 index 000000000000..0c3cd53c7313 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv04_fifo.c | |||
@@ -0,0 +1,271 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007 Ben Skeggs. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include "drmP.h" | ||
28 | #include "drm.h" | ||
29 | #include "nouveau_drv.h" | ||
30 | |||
31 | #define NV04_RAMFC(c) (dev_priv->ramfc_offset + ((c) * NV04_RAMFC__SIZE)) | ||
32 | #define NV04_RAMFC__SIZE 32 | ||
33 | #define NV04_RAMFC_DMA_PUT 0x00 | ||
34 | #define NV04_RAMFC_DMA_GET 0x04 | ||
35 | #define NV04_RAMFC_DMA_INSTANCE 0x08 | ||
36 | #define NV04_RAMFC_DMA_STATE 0x0C | ||
37 | #define NV04_RAMFC_DMA_FETCH 0x10 | ||
38 | #define NV04_RAMFC_ENGINE 0x14 | ||
39 | #define NV04_RAMFC_PULL1_ENGINE 0x18 | ||
40 | |||
41 | #define RAMFC_WR(offset, val) nv_wo32(dev, chan->ramfc->gpuobj, \ | ||
42 | NV04_RAMFC_##offset/4, (val)) | ||
43 | #define RAMFC_RD(offset) nv_ro32(dev, chan->ramfc->gpuobj, \ | ||
44 | NV04_RAMFC_##offset/4) | ||
45 | |||
46 | void | ||
47 | nv04_fifo_disable(struct drm_device *dev) | ||
48 | { | ||
49 | uint32_t tmp; | ||
50 | |||
51 | tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUSH); | ||
52 | nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH, tmp & ~1); | ||
53 | nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0, 0); | ||
54 | tmp = nv_rd32(dev, NV03_PFIFO_CACHE1_PULL1); | ||
55 | nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, tmp & ~1); | ||
56 | } | ||
57 | |||
58 | void | ||
59 | nv04_fifo_enable(struct drm_device *dev) | ||
60 | { | ||
61 | nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0, 1); | ||
62 | nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, 1); | ||
63 | } | ||
64 | |||
65 | bool | ||
66 | nv04_fifo_reassign(struct drm_device *dev, bool enable) | ||
67 | { | ||
68 | uint32_t reassign = nv_rd32(dev, NV03_PFIFO_CACHES); | ||
69 | |||
70 | nv_wr32(dev, NV03_PFIFO_CACHES, enable ? 1 : 0); | ||
71 | return (reassign == 1); | ||
72 | } | ||
73 | |||
74 | int | ||
75 | nv04_fifo_channel_id(struct drm_device *dev) | ||
76 | { | ||
77 | return nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH1) & | ||
78 | NV03_PFIFO_CACHE1_PUSH1_CHID_MASK; | ||
79 | } | ||
80 | |||
81 | int | ||
82 | nv04_fifo_create_context(struct nouveau_channel *chan) | ||
83 | { | ||
84 | struct drm_device *dev = chan->dev; | ||
85 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
86 | int ret; | ||
87 | |||
88 | ret = nouveau_gpuobj_new_fake(dev, NV04_RAMFC(chan->id), ~0, | ||
89 | NV04_RAMFC__SIZE, | ||
90 | NVOBJ_FLAG_ZERO_ALLOC | | ||
91 | NVOBJ_FLAG_ZERO_FREE, | ||
92 | NULL, &chan->ramfc); | ||
93 | if (ret) | ||
94 | return ret; | ||
95 | |||
96 | /* Setup initial state */ | ||
97 | dev_priv->engine.instmem.prepare_access(dev, true); | ||
98 | RAMFC_WR(DMA_PUT, chan->pushbuf_base); | ||
99 | RAMFC_WR(DMA_GET, chan->pushbuf_base); | ||
100 | RAMFC_WR(DMA_INSTANCE, chan->pushbuf->instance >> 4); | ||
101 | RAMFC_WR(DMA_FETCH, (NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES | | ||
102 | NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES | | ||
103 | NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8 | | ||
104 | #ifdef __BIG_ENDIAN | ||
105 | NV_PFIFO_CACHE1_BIG_ENDIAN | | ||
106 | #endif | ||
107 | 0)); | ||
108 | dev_priv->engine.instmem.finish_access(dev); | ||
109 | |||
110 | /* enable the fifo dma operation */ | ||
111 | nv_wr32(dev, NV04_PFIFO_MODE, | ||
112 | nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id)); | ||
113 | return 0; | ||
114 | } | ||
115 | |||
116 | void | ||
117 | nv04_fifo_destroy_context(struct nouveau_channel *chan) | ||
118 | { | ||
119 | struct drm_device *dev = chan->dev; | ||
120 | |||
121 | nv_wr32(dev, NV04_PFIFO_MODE, | ||
122 | nv_rd32(dev, NV04_PFIFO_MODE) & ~(1 << chan->id)); | ||
123 | |||
124 | nouveau_gpuobj_ref_del(dev, &chan->ramfc); | ||
125 | } | ||
126 | |||
127 | static void | ||
128 | nv04_fifo_do_load_context(struct drm_device *dev, int chid) | ||
129 | { | ||
130 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
131 | uint32_t fc = NV04_RAMFC(chid), tmp; | ||
132 | |||
133 | dev_priv->engine.instmem.prepare_access(dev, false); | ||
134 | |||
135 | nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUT, nv_ri32(dev, fc + 0)); | ||
136 | nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_GET, nv_ri32(dev, fc + 4)); | ||
137 | tmp = nv_ri32(dev, fc + 8); | ||
138 | nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_INSTANCE, tmp & 0xFFFF); | ||
139 | nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT, tmp >> 16); | ||
140 | nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_STATE, nv_ri32(dev, fc + 12)); | ||
141 | nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_FETCH, nv_ri32(dev, fc + 16)); | ||
142 | nv_wr32(dev, NV04_PFIFO_CACHE1_ENGINE, nv_ri32(dev, fc + 20)); | ||
143 | nv_wr32(dev, NV04_PFIFO_CACHE1_PULL1, nv_ri32(dev, fc + 24)); | ||
144 | |||
145 | dev_priv->engine.instmem.finish_access(dev); | ||
146 | |||
147 | nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0); | ||
148 | nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, 0); | ||
149 | } | ||
150 | |||
151 | int | ||
152 | nv04_fifo_load_context(struct nouveau_channel *chan) | ||
153 | { | ||
154 | uint32_t tmp; | ||
155 | |||
156 | nv_wr32(chan->dev, NV03_PFIFO_CACHE1_PUSH1, | ||
157 | NV03_PFIFO_CACHE1_PUSH1_DMA | chan->id); | ||
158 | nv04_fifo_do_load_context(chan->dev, chan->id); | ||
159 | nv_wr32(chan->dev, NV04_PFIFO_CACHE1_DMA_PUSH, 1); | ||
160 | |||
161 | /* Reset NV04_PFIFO_CACHE1_DMA_CTL_AT_INFO to INVALID */ | ||
162 | tmp = nv_rd32(chan->dev, NV04_PFIFO_CACHE1_DMA_CTL) & ~(1 << 31); | ||
163 | nv_wr32(chan->dev, NV04_PFIFO_CACHE1_DMA_CTL, tmp); | ||
164 | |||
165 | return 0; | ||
166 | } | ||
167 | |||
168 | int | ||
169 | nv04_fifo_unload_context(struct drm_device *dev) | ||
170 | { | ||
171 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
172 | struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; | ||
173 | struct nouveau_channel *chan = NULL; | ||
174 | uint32_t tmp; | ||
175 | int chid; | ||
176 | |||
177 | chid = pfifo->channel_id(dev); | ||
178 | if (chid < 0 || chid >= dev_priv->engine.fifo.channels) | ||
179 | return 0; | ||
180 | |||
181 | chan = dev_priv->fifos[chid]; | ||
182 | if (!chan) { | ||
183 | NV_ERROR(dev, "Inactive channel on PFIFO: %d\n", chid); | ||
184 | return -EINVAL; | ||
185 | } | ||
186 | |||
187 | dev_priv->engine.instmem.prepare_access(dev, true); | ||
188 | RAMFC_WR(DMA_PUT, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUT)); | ||
189 | RAMFC_WR(DMA_GET, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET)); | ||
190 | tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT) << 16; | ||
191 | tmp |= nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_INSTANCE); | ||
192 | RAMFC_WR(DMA_INSTANCE, tmp); | ||
193 | RAMFC_WR(DMA_STATE, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_STATE)); | ||
194 | RAMFC_WR(DMA_FETCH, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_FETCH)); | ||
195 | RAMFC_WR(ENGINE, nv_rd32(dev, NV04_PFIFO_CACHE1_ENGINE)); | ||
196 | RAMFC_WR(PULL1_ENGINE, nv_rd32(dev, NV04_PFIFO_CACHE1_PULL1)); | ||
197 | dev_priv->engine.instmem.finish_access(dev); | ||
198 | |||
199 | nv04_fifo_do_load_context(dev, pfifo->channels - 1); | ||
200 | nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, pfifo->channels - 1); | ||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | static void | ||
205 | nv04_fifo_init_reset(struct drm_device *dev) | ||
206 | { | ||
207 | nv_wr32(dev, NV03_PMC_ENABLE, | ||
208 | nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PFIFO); | ||
209 | nv_wr32(dev, NV03_PMC_ENABLE, | ||
210 | nv_rd32(dev, NV03_PMC_ENABLE) | NV_PMC_ENABLE_PFIFO); | ||
211 | |||
212 | nv_wr32(dev, 0x003224, 0x000f0078); | ||
213 | nv_wr32(dev, 0x002044, 0x0101ffff); | ||
214 | nv_wr32(dev, 0x002040, 0x000000ff); | ||
215 | nv_wr32(dev, 0x002500, 0x00000000); | ||
216 | nv_wr32(dev, 0x003000, 0x00000000); | ||
217 | nv_wr32(dev, 0x003050, 0x00000000); | ||
218 | nv_wr32(dev, 0x003200, 0x00000000); | ||
219 | nv_wr32(dev, 0x003250, 0x00000000); | ||
220 | nv_wr32(dev, 0x003220, 0x00000000); | ||
221 | |||
222 | nv_wr32(dev, 0x003250, 0x00000000); | ||
223 | nv_wr32(dev, 0x003270, 0x00000000); | ||
224 | nv_wr32(dev, 0x003210, 0x00000000); | ||
225 | } | ||
226 | |||
227 | static void | ||
228 | nv04_fifo_init_ramxx(struct drm_device *dev) | ||
229 | { | ||
230 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
231 | |||
232 | nv_wr32(dev, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ | | ||
233 | ((dev_priv->ramht_bits - 9) << 16) | | ||
234 | (dev_priv->ramht_offset >> 8)); | ||
235 | nv_wr32(dev, NV03_PFIFO_RAMRO, dev_priv->ramro_offset>>8); | ||
236 | nv_wr32(dev, NV03_PFIFO_RAMFC, dev_priv->ramfc_offset >> 8); | ||
237 | } | ||
238 | |||
239 | static void | ||
240 | nv04_fifo_init_intr(struct drm_device *dev) | ||
241 | { | ||
242 | nv_wr32(dev, 0x002100, 0xffffffff); | ||
243 | nv_wr32(dev, 0x002140, 0xffffffff); | ||
244 | } | ||
245 | |||
246 | int | ||
247 | nv04_fifo_init(struct drm_device *dev) | ||
248 | { | ||
249 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
250 | struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; | ||
251 | int i; | ||
252 | |||
253 | nv04_fifo_init_reset(dev); | ||
254 | nv04_fifo_init_ramxx(dev); | ||
255 | |||
256 | nv04_fifo_do_load_context(dev, pfifo->channels - 1); | ||
257 | nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, pfifo->channels - 1); | ||
258 | |||
259 | nv04_fifo_init_intr(dev); | ||
260 | pfifo->enable(dev); | ||
261 | |||
262 | for (i = 0; i < dev_priv->engine.fifo.channels; i++) { | ||
263 | if (dev_priv->fifos[i]) { | ||
264 | uint32_t mode = nv_rd32(dev, NV04_PFIFO_MODE); | ||
265 | nv_wr32(dev, NV04_PFIFO_MODE, mode | (1 << i)); | ||
266 | } | ||
267 | } | ||
268 | |||
269 | return 0; | ||
270 | } | ||
271 | |||
diff --git a/drivers/gpu/drm/nouveau/nv04_graph.c b/drivers/gpu/drm/nouveau/nv04_graph.c new file mode 100644 index 000000000000..396ee92118f6 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv04_graph.c | |||
@@ -0,0 +1,579 @@ | |||
1 | /* | ||
2 | * Copyright 2007 Stephane Marchesin | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
6 | * copy of this software and associated documentation files (the "Software"), | ||
7 | * to deal in the Software without restriction, including without limitation | ||
8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
9 | * and/or sell copies of the Software, and to permit persons to whom the | ||
10 | * Software is furnished to do so, subject to the following conditions: | ||
11 | * | ||
12 | * The above copyright notice and this permission notice (including the next | ||
13 | * paragraph) shall be included in all copies or substantial portions of the | ||
14 | * 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 | * PRECISION INSIGHT AND/OR ITS SUPPLIERS 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 OTHER | ||
22 | * DEALINGS IN THE SOFTWARE. | ||
23 | */ | ||
24 | |||
25 | #include "drmP.h" | ||
26 | #include "drm.h" | ||
27 | #include "nouveau_drm.h" | ||
28 | #include "nouveau_drv.h" | ||
29 | |||
30 | static uint32_t nv04_graph_ctx_regs[] = { | ||
31 | NV04_PGRAPH_CTX_SWITCH1, | ||
32 | NV04_PGRAPH_CTX_SWITCH2, | ||
33 | NV04_PGRAPH_CTX_SWITCH3, | ||
34 | NV04_PGRAPH_CTX_SWITCH4, | ||
35 | NV04_PGRAPH_CTX_CACHE1, | ||
36 | NV04_PGRAPH_CTX_CACHE2, | ||
37 | NV04_PGRAPH_CTX_CACHE3, | ||
38 | NV04_PGRAPH_CTX_CACHE4, | ||
39 | 0x00400184, | ||
40 | 0x004001a4, | ||
41 | 0x004001c4, | ||
42 | 0x004001e4, | ||
43 | 0x00400188, | ||
44 | 0x004001a8, | ||
45 | 0x004001c8, | ||
46 | 0x004001e8, | ||
47 | 0x0040018c, | ||
48 | 0x004001ac, | ||
49 | 0x004001cc, | ||
50 | 0x004001ec, | ||
51 | 0x00400190, | ||
52 | 0x004001b0, | ||
53 | 0x004001d0, | ||
54 | 0x004001f0, | ||
55 | 0x00400194, | ||
56 | 0x004001b4, | ||
57 | 0x004001d4, | ||
58 | 0x004001f4, | ||
59 | 0x00400198, | ||
60 | 0x004001b8, | ||
61 | 0x004001d8, | ||
62 | 0x004001f8, | ||
63 | 0x0040019c, | ||
64 | 0x004001bc, | ||
65 | 0x004001dc, | ||
66 | 0x004001fc, | ||
67 | 0x00400174, | ||
68 | NV04_PGRAPH_DMA_START_0, | ||
69 | NV04_PGRAPH_DMA_START_1, | ||
70 | NV04_PGRAPH_DMA_LENGTH, | ||
71 | NV04_PGRAPH_DMA_MISC, | ||
72 | NV04_PGRAPH_DMA_PITCH, | ||
73 | NV04_PGRAPH_BOFFSET0, | ||
74 | NV04_PGRAPH_BBASE0, | ||
75 | NV04_PGRAPH_BLIMIT0, | ||
76 | NV04_PGRAPH_BOFFSET1, | ||
77 | NV04_PGRAPH_BBASE1, | ||
78 | NV04_PGRAPH_BLIMIT1, | ||
79 | NV04_PGRAPH_BOFFSET2, | ||
80 | NV04_PGRAPH_BBASE2, | ||
81 | NV04_PGRAPH_BLIMIT2, | ||
82 | NV04_PGRAPH_BOFFSET3, | ||
83 | NV04_PGRAPH_BBASE3, | ||
84 | NV04_PGRAPH_BLIMIT3, | ||
85 | NV04_PGRAPH_BOFFSET4, | ||
86 | NV04_PGRAPH_BBASE4, | ||
87 | NV04_PGRAPH_BLIMIT4, | ||
88 | NV04_PGRAPH_BOFFSET5, | ||
89 | NV04_PGRAPH_BBASE5, | ||
90 | NV04_PGRAPH_BLIMIT5, | ||
91 | NV04_PGRAPH_BPITCH0, | ||
92 | NV04_PGRAPH_BPITCH1, | ||
93 | NV04_PGRAPH_BPITCH2, | ||
94 | NV04_PGRAPH_BPITCH3, | ||
95 | NV04_PGRAPH_BPITCH4, | ||
96 | NV04_PGRAPH_SURFACE, | ||
97 | NV04_PGRAPH_STATE, | ||
98 | NV04_PGRAPH_BSWIZZLE2, | ||
99 | NV04_PGRAPH_BSWIZZLE5, | ||
100 | NV04_PGRAPH_BPIXEL, | ||
101 | NV04_PGRAPH_NOTIFY, | ||
102 | NV04_PGRAPH_PATT_COLOR0, | ||
103 | NV04_PGRAPH_PATT_COLOR1, | ||
104 | NV04_PGRAPH_PATT_COLORRAM+0x00, | ||
105 | NV04_PGRAPH_PATT_COLORRAM+0x01, | ||
106 | NV04_PGRAPH_PATT_COLORRAM+0x02, | ||
107 | NV04_PGRAPH_PATT_COLORRAM+0x03, | ||
108 | NV04_PGRAPH_PATT_COLORRAM+0x04, | ||
109 | NV04_PGRAPH_PATT_COLORRAM+0x05, | ||
110 | NV04_PGRAPH_PATT_COLORRAM+0x06, | ||
111 | NV04_PGRAPH_PATT_COLORRAM+0x07, | ||
112 | NV04_PGRAPH_PATT_COLORRAM+0x08, | ||
113 | NV04_PGRAPH_PATT_COLORRAM+0x09, | ||
114 | NV04_PGRAPH_PATT_COLORRAM+0x0A, | ||
115 | NV04_PGRAPH_PATT_COLORRAM+0x0B, | ||
116 | NV04_PGRAPH_PATT_COLORRAM+0x0C, | ||
117 | NV04_PGRAPH_PATT_COLORRAM+0x0D, | ||
118 | NV04_PGRAPH_PATT_COLORRAM+0x0E, | ||
119 | NV04_PGRAPH_PATT_COLORRAM+0x0F, | ||
120 | NV04_PGRAPH_PATT_COLORRAM+0x10, | ||
121 | NV04_PGRAPH_PATT_COLORRAM+0x11, | ||
122 | NV04_PGRAPH_PATT_COLORRAM+0x12, | ||
123 | NV04_PGRAPH_PATT_COLORRAM+0x13, | ||
124 | NV04_PGRAPH_PATT_COLORRAM+0x14, | ||
125 | NV04_PGRAPH_PATT_COLORRAM+0x15, | ||
126 | NV04_PGRAPH_PATT_COLORRAM+0x16, | ||
127 | NV04_PGRAPH_PATT_COLORRAM+0x17, | ||
128 | NV04_PGRAPH_PATT_COLORRAM+0x18, | ||
129 | NV04_PGRAPH_PATT_COLORRAM+0x19, | ||
130 | NV04_PGRAPH_PATT_COLORRAM+0x1A, | ||
131 | NV04_PGRAPH_PATT_COLORRAM+0x1B, | ||
132 | NV04_PGRAPH_PATT_COLORRAM+0x1C, | ||
133 | NV04_PGRAPH_PATT_COLORRAM+0x1D, | ||
134 | NV04_PGRAPH_PATT_COLORRAM+0x1E, | ||
135 | NV04_PGRAPH_PATT_COLORRAM+0x1F, | ||
136 | NV04_PGRAPH_PATT_COLORRAM+0x20, | ||
137 | NV04_PGRAPH_PATT_COLORRAM+0x21, | ||
138 | NV04_PGRAPH_PATT_COLORRAM+0x22, | ||
139 | NV04_PGRAPH_PATT_COLORRAM+0x23, | ||
140 | NV04_PGRAPH_PATT_COLORRAM+0x24, | ||
141 | NV04_PGRAPH_PATT_COLORRAM+0x25, | ||
142 | NV04_PGRAPH_PATT_COLORRAM+0x26, | ||
143 | NV04_PGRAPH_PATT_COLORRAM+0x27, | ||
144 | NV04_PGRAPH_PATT_COLORRAM+0x28, | ||
145 | NV04_PGRAPH_PATT_COLORRAM+0x29, | ||
146 | NV04_PGRAPH_PATT_COLORRAM+0x2A, | ||
147 | NV04_PGRAPH_PATT_COLORRAM+0x2B, | ||
148 | NV04_PGRAPH_PATT_COLORRAM+0x2C, | ||
149 | NV04_PGRAPH_PATT_COLORRAM+0x2D, | ||
150 | NV04_PGRAPH_PATT_COLORRAM+0x2E, | ||
151 | NV04_PGRAPH_PATT_COLORRAM+0x2F, | ||
152 | NV04_PGRAPH_PATT_COLORRAM+0x30, | ||
153 | NV04_PGRAPH_PATT_COLORRAM+0x31, | ||
154 | NV04_PGRAPH_PATT_COLORRAM+0x32, | ||
155 | NV04_PGRAPH_PATT_COLORRAM+0x33, | ||
156 | NV04_PGRAPH_PATT_COLORRAM+0x34, | ||
157 | NV04_PGRAPH_PATT_COLORRAM+0x35, | ||
158 | NV04_PGRAPH_PATT_COLORRAM+0x36, | ||
159 | NV04_PGRAPH_PATT_COLORRAM+0x37, | ||
160 | NV04_PGRAPH_PATT_COLORRAM+0x38, | ||
161 | NV04_PGRAPH_PATT_COLORRAM+0x39, | ||
162 | NV04_PGRAPH_PATT_COLORRAM+0x3A, | ||
163 | NV04_PGRAPH_PATT_COLORRAM+0x3B, | ||
164 | NV04_PGRAPH_PATT_COLORRAM+0x3C, | ||
165 | NV04_PGRAPH_PATT_COLORRAM+0x3D, | ||
166 | NV04_PGRAPH_PATT_COLORRAM+0x3E, | ||
167 | NV04_PGRAPH_PATT_COLORRAM+0x3F, | ||
168 | NV04_PGRAPH_PATTERN, | ||
169 | 0x0040080c, | ||
170 | NV04_PGRAPH_PATTERN_SHAPE, | ||
171 | 0x00400600, | ||
172 | NV04_PGRAPH_ROP3, | ||
173 | NV04_PGRAPH_CHROMA, | ||
174 | NV04_PGRAPH_BETA_AND, | ||
175 | NV04_PGRAPH_BETA_PREMULT, | ||
176 | NV04_PGRAPH_CONTROL0, | ||
177 | NV04_PGRAPH_CONTROL1, | ||
178 | NV04_PGRAPH_CONTROL2, | ||
179 | NV04_PGRAPH_BLEND, | ||
180 | NV04_PGRAPH_STORED_FMT, | ||
181 | NV04_PGRAPH_SOURCE_COLOR, | ||
182 | 0x00400560, | ||
183 | 0x00400568, | ||
184 | 0x00400564, | ||
185 | 0x0040056c, | ||
186 | 0x00400400, | ||
187 | 0x00400480, | ||
188 | 0x00400404, | ||
189 | 0x00400484, | ||
190 | 0x00400408, | ||
191 | 0x00400488, | ||
192 | 0x0040040c, | ||
193 | 0x0040048c, | ||
194 | 0x00400410, | ||
195 | 0x00400490, | ||
196 | 0x00400414, | ||
197 | 0x00400494, | ||
198 | 0x00400418, | ||
199 | 0x00400498, | ||
200 | 0x0040041c, | ||
201 | 0x0040049c, | ||
202 | 0x00400420, | ||
203 | 0x004004a0, | ||
204 | 0x00400424, | ||
205 | 0x004004a4, | ||
206 | 0x00400428, | ||
207 | 0x004004a8, | ||
208 | 0x0040042c, | ||
209 | 0x004004ac, | ||
210 | 0x00400430, | ||
211 | 0x004004b0, | ||
212 | 0x00400434, | ||
213 | 0x004004b4, | ||
214 | 0x00400438, | ||
215 | 0x004004b8, | ||
216 | 0x0040043c, | ||
217 | 0x004004bc, | ||
218 | 0x00400440, | ||
219 | 0x004004c0, | ||
220 | 0x00400444, | ||
221 | 0x004004c4, | ||
222 | 0x00400448, | ||
223 | 0x004004c8, | ||
224 | 0x0040044c, | ||
225 | 0x004004cc, | ||
226 | 0x00400450, | ||
227 | 0x004004d0, | ||
228 | 0x00400454, | ||
229 | 0x004004d4, | ||
230 | 0x00400458, | ||
231 | 0x004004d8, | ||
232 | 0x0040045c, | ||
233 | 0x004004dc, | ||
234 | 0x00400460, | ||
235 | 0x004004e0, | ||
236 | 0x00400464, | ||
237 | 0x004004e4, | ||
238 | 0x00400468, | ||
239 | 0x004004e8, | ||
240 | 0x0040046c, | ||
241 | 0x004004ec, | ||
242 | 0x00400470, | ||
243 | 0x004004f0, | ||
244 | 0x00400474, | ||
245 | 0x004004f4, | ||
246 | 0x00400478, | ||
247 | 0x004004f8, | ||
248 | 0x0040047c, | ||
249 | 0x004004fc, | ||
250 | 0x0040053c, | ||
251 | 0x00400544, | ||
252 | 0x00400540, | ||
253 | 0x00400548, | ||
254 | 0x00400560, | ||
255 | 0x00400568, | ||
256 | 0x00400564, | ||
257 | 0x0040056c, | ||
258 | 0x00400534, | ||
259 | 0x00400538, | ||
260 | 0x00400514, | ||
261 | 0x00400518, | ||
262 | 0x0040051c, | ||
263 | 0x00400520, | ||
264 | 0x00400524, | ||
265 | 0x00400528, | ||
266 | 0x0040052c, | ||
267 | 0x00400530, | ||
268 | 0x00400d00, | ||
269 | 0x00400d40, | ||
270 | 0x00400d80, | ||
271 | 0x00400d04, | ||
272 | 0x00400d44, | ||
273 | 0x00400d84, | ||
274 | 0x00400d08, | ||
275 | 0x00400d48, | ||
276 | 0x00400d88, | ||
277 | 0x00400d0c, | ||
278 | 0x00400d4c, | ||
279 | 0x00400d8c, | ||
280 | 0x00400d10, | ||
281 | 0x00400d50, | ||
282 | 0x00400d90, | ||
283 | 0x00400d14, | ||
284 | 0x00400d54, | ||
285 | 0x00400d94, | ||
286 | 0x00400d18, | ||
287 | 0x00400d58, | ||
288 | 0x00400d98, | ||
289 | 0x00400d1c, | ||
290 | 0x00400d5c, | ||
291 | 0x00400d9c, | ||
292 | 0x00400d20, | ||
293 | 0x00400d60, | ||
294 | 0x00400da0, | ||
295 | 0x00400d24, | ||
296 | 0x00400d64, | ||
297 | 0x00400da4, | ||
298 | 0x00400d28, | ||
299 | 0x00400d68, | ||
300 | 0x00400da8, | ||
301 | 0x00400d2c, | ||
302 | 0x00400d6c, | ||
303 | 0x00400dac, | ||
304 | 0x00400d30, | ||
305 | 0x00400d70, | ||
306 | 0x00400db0, | ||
307 | 0x00400d34, | ||
308 | 0x00400d74, | ||
309 | 0x00400db4, | ||
310 | 0x00400d38, | ||
311 | 0x00400d78, | ||
312 | 0x00400db8, | ||
313 | 0x00400d3c, | ||
314 | 0x00400d7c, | ||
315 | 0x00400dbc, | ||
316 | 0x00400590, | ||
317 | 0x00400594, | ||
318 | 0x00400598, | ||
319 | 0x0040059c, | ||
320 | 0x004005a8, | ||
321 | 0x004005ac, | ||
322 | 0x004005b0, | ||
323 | 0x004005b4, | ||
324 | 0x004005c0, | ||
325 | 0x004005c4, | ||
326 | 0x004005c8, | ||
327 | 0x004005cc, | ||
328 | 0x004005d0, | ||
329 | 0x004005d4, | ||
330 | 0x004005d8, | ||
331 | 0x004005dc, | ||
332 | 0x004005e0, | ||
333 | NV04_PGRAPH_PASSTHRU_0, | ||
334 | NV04_PGRAPH_PASSTHRU_1, | ||
335 | NV04_PGRAPH_PASSTHRU_2, | ||
336 | NV04_PGRAPH_DVD_COLORFMT, | ||
337 | NV04_PGRAPH_SCALED_FORMAT, | ||
338 | NV04_PGRAPH_MISC24_0, | ||
339 | NV04_PGRAPH_MISC24_1, | ||
340 | NV04_PGRAPH_MISC24_2, | ||
341 | 0x00400500, | ||
342 | 0x00400504, | ||
343 | NV04_PGRAPH_VALID1, | ||
344 | NV04_PGRAPH_VALID2 | ||
345 | |||
346 | |||
347 | }; | ||
348 | |||
349 | struct graph_state { | ||
350 | int nv04[ARRAY_SIZE(nv04_graph_ctx_regs)]; | ||
351 | }; | ||
352 | |||
353 | struct nouveau_channel * | ||
354 | nv04_graph_channel(struct drm_device *dev) | ||
355 | { | ||
356 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
357 | int chid = dev_priv->engine.fifo.channels; | ||
358 | |||
359 | if (nv_rd32(dev, NV04_PGRAPH_CTX_CONTROL) & 0x00010000) | ||
360 | chid = nv_rd32(dev, NV04_PGRAPH_CTX_USER) >> 24; | ||
361 | |||
362 | if (chid >= dev_priv->engine.fifo.channels) | ||
363 | return NULL; | ||
364 | |||
365 | return dev_priv->fifos[chid]; | ||
366 | } | ||
367 | |||
368 | void | ||
369 | nv04_graph_context_switch(struct drm_device *dev) | ||
370 | { | ||
371 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
372 | struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; | ||
373 | struct nouveau_channel *chan = NULL; | ||
374 | int chid; | ||
375 | |||
376 | pgraph->fifo_access(dev, false); | ||
377 | nouveau_wait_for_idle(dev); | ||
378 | |||
379 | /* If previous context is valid, we need to save it */ | ||
380 | pgraph->unload_context(dev); | ||
381 | |||
382 | /* Load context for next channel */ | ||
383 | chid = dev_priv->engine.fifo.channel_id(dev); | ||
384 | chan = dev_priv->fifos[chid]; | ||
385 | if (chan) | ||
386 | nv04_graph_load_context(chan); | ||
387 | |||
388 | pgraph->fifo_access(dev, true); | ||
389 | } | ||
390 | |||
391 | int nv04_graph_create_context(struct nouveau_channel *chan) | ||
392 | { | ||
393 | struct graph_state *pgraph_ctx; | ||
394 | NV_DEBUG(chan->dev, "nv04_graph_context_create %d\n", chan->id); | ||
395 | |||
396 | chan->pgraph_ctx = pgraph_ctx = kzalloc(sizeof(*pgraph_ctx), | ||
397 | GFP_KERNEL); | ||
398 | if (pgraph_ctx == NULL) | ||
399 | return -ENOMEM; | ||
400 | |||
401 | /* dev_priv->fifos[channel].pgraph_ctx_user = channel << 24; */ | ||
402 | pgraph_ctx->nv04[0] = 0x0001ffff; | ||
403 | /* is it really needed ??? */ | ||
404 | #if 0 | ||
405 | dev_priv->fifos[channel].pgraph_ctx[1] = | ||
406 | nv_rd32(dev, NV_PGRAPH_DEBUG_4); | ||
407 | dev_priv->fifos[channel].pgraph_ctx[2] = | ||
408 | nv_rd32(dev, 0x004006b0); | ||
409 | #endif | ||
410 | return 0; | ||
411 | } | ||
412 | |||
413 | void nv04_graph_destroy_context(struct nouveau_channel *chan) | ||
414 | { | ||
415 | struct graph_state *pgraph_ctx = chan->pgraph_ctx; | ||
416 | |||
417 | kfree(pgraph_ctx); | ||
418 | chan->pgraph_ctx = NULL; | ||
419 | } | ||
420 | |||
421 | int nv04_graph_load_context(struct nouveau_channel *chan) | ||
422 | { | ||
423 | struct drm_device *dev = chan->dev; | ||
424 | struct graph_state *pgraph_ctx = chan->pgraph_ctx; | ||
425 | uint32_t tmp; | ||
426 | int i; | ||
427 | |||
428 | for (i = 0; i < ARRAY_SIZE(nv04_graph_ctx_regs); i++) | ||
429 | nv_wr32(dev, nv04_graph_ctx_regs[i], pgraph_ctx->nv04[i]); | ||
430 | |||
431 | nv_wr32(dev, NV04_PGRAPH_CTX_CONTROL, 0x10010100); | ||
432 | nv_wr32(dev, NV04_PGRAPH_CTX_USER, chan->id << 24); | ||
433 | tmp = nv_rd32(dev, NV04_PGRAPH_FFINTFC_ST2); | ||
434 | nv_wr32(dev, NV04_PGRAPH_FFINTFC_ST2, tmp & 0x000fffff); | ||
435 | return 0; | ||
436 | } | ||
437 | |||
438 | int | ||
439 | nv04_graph_unload_context(struct drm_device *dev) | ||
440 | { | ||
441 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
442 | struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; | ||
443 | struct nouveau_channel *chan = NULL; | ||
444 | struct graph_state *ctx; | ||
445 | uint32_t tmp; | ||
446 | int i; | ||
447 | |||
448 | chan = pgraph->channel(dev); | ||
449 | if (!chan) | ||
450 | return 0; | ||
451 | ctx = chan->pgraph_ctx; | ||
452 | |||
453 | for (i = 0; i < ARRAY_SIZE(nv04_graph_ctx_regs); i++) | ||
454 | ctx->nv04[i] = nv_rd32(dev, nv04_graph_ctx_regs[i]); | ||
455 | |||
456 | nv_wr32(dev, NV04_PGRAPH_CTX_CONTROL, 0x10000000); | ||
457 | tmp = nv_rd32(dev, NV04_PGRAPH_CTX_USER) & 0x00ffffff; | ||
458 | tmp |= (dev_priv->engine.fifo.channels - 1) << 24; | ||
459 | nv_wr32(dev, NV04_PGRAPH_CTX_USER, tmp); | ||
460 | return 0; | ||
461 | } | ||
462 | |||
463 | int nv04_graph_init(struct drm_device *dev) | ||
464 | { | ||
465 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
466 | uint32_t tmp; | ||
467 | |||
468 | nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) & | ||
469 | ~NV_PMC_ENABLE_PGRAPH); | ||
470 | nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) | | ||
471 | NV_PMC_ENABLE_PGRAPH); | ||
472 | |||
473 | /* Enable PGRAPH interrupts */ | ||
474 | nv_wr32(dev, NV03_PGRAPH_INTR, 0xFFFFFFFF); | ||
475 | nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF); | ||
476 | |||
477 | nv_wr32(dev, NV04_PGRAPH_VALID1, 0); | ||
478 | nv_wr32(dev, NV04_PGRAPH_VALID2, 0); | ||
479 | /*nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x000001FF); | ||
480 | nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x001FFFFF);*/ | ||
481 | nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x1231c000); | ||
482 | /*1231C000 blob, 001 haiku*/ | ||
483 | //*V_WRITE(NV04_PGRAPH_DEBUG_1, 0xf2d91100);*/ | ||
484 | nv_wr32(dev, NV04_PGRAPH_DEBUG_1, 0x72111100); | ||
485 | /*0x72111100 blob , 01 haiku*/ | ||
486 | /*nv_wr32(dev, NV04_PGRAPH_DEBUG_2, 0x11d5f870);*/ | ||
487 | nv_wr32(dev, NV04_PGRAPH_DEBUG_2, 0x11d5f071); | ||
488 | /*haiku same*/ | ||
489 | |||
490 | /*nv_wr32(dev, NV04_PGRAPH_DEBUG_3, 0xfad4ff31);*/ | ||
491 | nv_wr32(dev, NV04_PGRAPH_DEBUG_3, 0xf0d4ff31); | ||
492 | /*haiku and blob 10d4*/ | ||
493 | |||
494 | nv_wr32(dev, NV04_PGRAPH_STATE , 0xFFFFFFFF); | ||
495 | nv_wr32(dev, NV04_PGRAPH_CTX_CONTROL , 0x10000100); | ||
496 | tmp = nv_rd32(dev, NV04_PGRAPH_CTX_USER) & 0x00ffffff; | ||
497 | tmp |= dev_priv->engine.fifo.channels << 24; | ||
498 | nv_wr32(dev, NV04_PGRAPH_CTX_USER, tmp); | ||
499 | |||
500 | /* These don't belong here, they're part of a per-channel context */ | ||
501 | nv_wr32(dev, NV04_PGRAPH_PATTERN_SHAPE, 0x00000000); | ||
502 | nv_wr32(dev, NV04_PGRAPH_BETA_AND , 0xFFFFFFFF); | ||
503 | |||
504 | return 0; | ||
505 | } | ||
506 | |||
507 | void nv04_graph_takedown(struct drm_device *dev) | ||
508 | { | ||
509 | } | ||
510 | |||
511 | void | ||
512 | nv04_graph_fifo_access(struct drm_device *dev, bool enabled) | ||
513 | { | ||
514 | if (enabled) | ||
515 | nv_wr32(dev, NV04_PGRAPH_FIFO, | ||
516 | nv_rd32(dev, NV04_PGRAPH_FIFO) | 1); | ||
517 | else | ||
518 | nv_wr32(dev, NV04_PGRAPH_FIFO, | ||
519 | nv_rd32(dev, NV04_PGRAPH_FIFO) & ~1); | ||
520 | } | ||
521 | |||
522 | static int | ||
523 | nv04_graph_mthd_set_ref(struct nouveau_channel *chan, int grclass, | ||
524 | int mthd, uint32_t data) | ||
525 | { | ||
526 | chan->fence.last_sequence_irq = data; | ||
527 | nouveau_fence_handler(chan->dev, chan->id); | ||
528 | return 0; | ||
529 | } | ||
530 | |||
531 | static int | ||
532 | nv04_graph_mthd_set_operation(struct nouveau_channel *chan, int grclass, | ||
533 | int mthd, uint32_t data) | ||
534 | { | ||
535 | struct drm_device *dev = chan->dev; | ||
536 | uint32_t instance = nv_rd32(dev, NV04_PGRAPH_CTX_SWITCH4) & 0xffff; | ||
537 | int subc = (nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR) >> 13) & 0x7; | ||
538 | uint32_t tmp; | ||
539 | |||
540 | tmp = nv_ri32(dev, instance); | ||
541 | tmp &= ~0x00038000; | ||
542 | tmp |= ((data & 7) << 15); | ||
543 | |||
544 | nv_wi32(dev, instance, tmp); | ||
545 | nv_wr32(dev, NV04_PGRAPH_CTX_SWITCH1, tmp); | ||
546 | nv_wr32(dev, NV04_PGRAPH_CTX_CACHE1 + subc, tmp); | ||
547 | return 0; | ||
548 | } | ||
549 | |||
550 | static struct nouveau_pgraph_object_method nv04_graph_mthds_m2mf[] = { | ||
551 | { 0x0150, nv04_graph_mthd_set_ref }, | ||
552 | {} | ||
553 | }; | ||
554 | |||
555 | static struct nouveau_pgraph_object_method nv04_graph_mthds_set_operation[] = { | ||
556 | { 0x02fc, nv04_graph_mthd_set_operation }, | ||
557 | {}, | ||
558 | }; | ||
559 | |||
560 | struct nouveau_pgraph_object_class nv04_graph_grclass[] = { | ||
561 | { 0x0039, false, nv04_graph_mthds_m2mf }, | ||
562 | { 0x004a, false, nv04_graph_mthds_set_operation }, /* gdirect */ | ||
563 | { 0x005f, false, nv04_graph_mthds_set_operation }, /* imageblit */ | ||
564 | { 0x0061, false, nv04_graph_mthds_set_operation }, /* ifc */ | ||
565 | { 0x0077, false, nv04_graph_mthds_set_operation }, /* sifm */ | ||
566 | { 0x0030, false, NULL }, /* null */ | ||
567 | { 0x0042, false, NULL }, /* surf2d */ | ||
568 | { 0x0043, false, NULL }, /* rop */ | ||
569 | { 0x0012, false, NULL }, /* beta1 */ | ||
570 | { 0x0072, false, NULL }, /* beta4 */ | ||
571 | { 0x0019, false, NULL }, /* cliprect */ | ||
572 | { 0x0044, false, NULL }, /* pattern */ | ||
573 | { 0x0052, false, NULL }, /* swzsurf */ | ||
574 | { 0x0053, false, NULL }, /* surf3d */ | ||
575 | { 0x0054, false, NULL }, /* tex_tri */ | ||
576 | { 0x0055, false, NULL }, /* multitex_tri */ | ||
577 | {} | ||
578 | }; | ||
579 | |||
diff --git a/drivers/gpu/drm/nouveau/nv04_instmem.c b/drivers/gpu/drm/nouveau/nv04_instmem.c new file mode 100644 index 000000000000..a20c206625a2 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv04_instmem.c | |||
@@ -0,0 +1,208 @@ | |||
1 | #include "drmP.h" | ||
2 | #include "drm.h" | ||
3 | #include "nouveau_drv.h" | ||
4 | |||
5 | /* returns the size of fifo context */ | ||
6 | static int | ||
7 | nouveau_fifo_ctx_size(struct drm_device *dev) | ||
8 | { | ||
9 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
10 | |||
11 | if (dev_priv->chipset >= 0x40) | ||
12 | return 128; | ||
13 | else | ||
14 | if (dev_priv->chipset >= 0x17) | ||
15 | return 64; | ||
16 | |||
17 | return 32; | ||
18 | } | ||
19 | |||
20 | static void | ||
21 | nv04_instmem_determine_amount(struct drm_device *dev) | ||
22 | { | ||
23 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
24 | int i; | ||
25 | |||
26 | /* Figure out how much instance memory we need */ | ||
27 | if (dev_priv->card_type >= NV_40) { | ||
28 | /* We'll want more instance memory than this on some NV4x cards. | ||
29 | * There's a 16MB aperture to play with that maps onto the end | ||
30 | * of vram. For now, only reserve a small piece until we know | ||
31 | * more about what each chipset requires. | ||
32 | */ | ||
33 | switch (dev_priv->chipset & 0xf0) { | ||
34 | case 0x40: | ||
35 | case 0x47: | ||
36 | case 0x49: | ||
37 | case 0x4b: | ||
38 | dev_priv->ramin_rsvd_vram = (2 * 1024 * 1024); | ||
39 | break; | ||
40 | default: | ||
41 | dev_priv->ramin_rsvd_vram = (1 * 1024 * 1024); | ||
42 | break; | ||
43 | } | ||
44 | } else { | ||
45 | /*XXX: what *are* the limits on <NV40 cards? | ||
46 | */ | ||
47 | dev_priv->ramin_rsvd_vram = (512 * 1024); | ||
48 | } | ||
49 | NV_DEBUG(dev, "RAMIN size: %dKiB\n", dev_priv->ramin_rsvd_vram >> 10); | ||
50 | |||
51 | /* Clear all of it, except the BIOS image that's in the first 64KiB */ | ||
52 | dev_priv->engine.instmem.prepare_access(dev, true); | ||
53 | for (i = 64 * 1024; i < dev_priv->ramin_rsvd_vram; i += 4) | ||
54 | nv_wi32(dev, i, 0x00000000); | ||
55 | dev_priv->engine.instmem.finish_access(dev); | ||
56 | } | ||
57 | |||
58 | static void | ||
59 | nv04_instmem_configure_fixed_tables(struct drm_device *dev) | ||
60 | { | ||
61 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
62 | struct nouveau_engine *engine = &dev_priv->engine; | ||
63 | |||
64 | /* FIFO hash table (RAMHT) | ||
65 | * use 4k hash table at RAMIN+0x10000 | ||
66 | * TODO: extend the hash table | ||
67 | */ | ||
68 | dev_priv->ramht_offset = 0x10000; | ||
69 | dev_priv->ramht_bits = 9; | ||
70 | dev_priv->ramht_size = (1 << dev_priv->ramht_bits); /* nr entries */ | ||
71 | dev_priv->ramht_size *= 8; /* 2 32-bit values per entry in RAMHT */ | ||
72 | NV_DEBUG(dev, "RAMHT offset=0x%x, size=%d\n", dev_priv->ramht_offset, | ||
73 | dev_priv->ramht_size); | ||
74 | |||
75 | /* FIFO runout table (RAMRO) - 512k at 0x11200 */ | ||
76 | dev_priv->ramro_offset = 0x11200; | ||
77 | dev_priv->ramro_size = 512; | ||
78 | NV_DEBUG(dev, "RAMRO offset=0x%x, size=%d\n", dev_priv->ramro_offset, | ||
79 | dev_priv->ramro_size); | ||
80 | |||
81 | /* FIFO context table (RAMFC) | ||
82 | * NV40 : Not sure exactly how to position RAMFC on some cards, | ||
83 | * 0x30002 seems to position it at RAMIN+0x20000 on these | ||
84 | * cards. RAMFC is 4kb (32 fifos, 128byte entries). | ||
85 | * Others: Position RAMFC at RAMIN+0x11400 | ||
86 | */ | ||
87 | dev_priv->ramfc_size = engine->fifo.channels * | ||
88 | nouveau_fifo_ctx_size(dev); | ||
89 | switch (dev_priv->card_type) { | ||
90 | case NV_40: | ||
91 | dev_priv->ramfc_offset = 0x20000; | ||
92 | break; | ||
93 | case NV_30: | ||
94 | case NV_20: | ||
95 | case NV_10: | ||
96 | case NV_04: | ||
97 | default: | ||
98 | dev_priv->ramfc_offset = 0x11400; | ||
99 | break; | ||
100 | } | ||
101 | NV_DEBUG(dev, "RAMFC offset=0x%x, size=%d\n", dev_priv->ramfc_offset, | ||
102 | dev_priv->ramfc_size); | ||
103 | } | ||
104 | |||
105 | int nv04_instmem_init(struct drm_device *dev) | ||
106 | { | ||
107 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
108 | uint32_t offset; | ||
109 | int ret = 0; | ||
110 | |||
111 | nv04_instmem_determine_amount(dev); | ||
112 | nv04_instmem_configure_fixed_tables(dev); | ||
113 | |||
114 | /* Create a heap to manage RAMIN allocations, we don't allocate | ||
115 | * the space that was reserved for RAMHT/FC/RO. | ||
116 | */ | ||
117 | offset = dev_priv->ramfc_offset + dev_priv->ramfc_size; | ||
118 | |||
119 | /* It appears RAMRO (or something?) is controlled by 0x2220/0x2230 | ||
120 | * on certain NV4x chipsets as well as RAMFC. When 0x2230 == 0 | ||
121 | * ("new style" control) the upper 16-bits of 0x2220 points at this | ||
122 | * other mysterious table that's clobbering important things. | ||
123 | * | ||
124 | * We're now pointing this at RAMIN+0x30000 to avoid RAMFC getting | ||
125 | * smashed to pieces on us, so reserve 0x30000-0x40000 too.. | ||
126 | */ | ||
127 | if (dev_priv->card_type >= NV_40) { | ||
128 | if (offset < 0x40000) | ||
129 | offset = 0x40000; | ||
130 | } | ||
131 | |||
132 | ret = nouveau_mem_init_heap(&dev_priv->ramin_heap, | ||
133 | offset, dev_priv->ramin_rsvd_vram - offset); | ||
134 | if (ret) { | ||
135 | dev_priv->ramin_heap = NULL; | ||
136 | NV_ERROR(dev, "Failed to init RAMIN heap\n"); | ||
137 | } | ||
138 | |||
139 | return ret; | ||
140 | } | ||
141 | |||
142 | void | ||
143 | nv04_instmem_takedown(struct drm_device *dev) | ||
144 | { | ||
145 | } | ||
146 | |||
147 | int | ||
148 | nv04_instmem_populate(struct drm_device *dev, struct nouveau_gpuobj *gpuobj, uint32_t *sz) | ||
149 | { | ||
150 | if (gpuobj->im_backing) | ||
151 | return -EINVAL; | ||
152 | |||
153 | return 0; | ||
154 | } | ||
155 | |||
156 | void | ||
157 | nv04_instmem_clear(struct drm_device *dev, struct nouveau_gpuobj *gpuobj) | ||
158 | { | ||
159 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
160 | |||
161 | if (gpuobj && gpuobj->im_backing) { | ||
162 | if (gpuobj->im_bound) | ||
163 | dev_priv->engine.instmem.unbind(dev, gpuobj); | ||
164 | gpuobj->im_backing = NULL; | ||
165 | } | ||
166 | } | ||
167 | |||
168 | int | ||
169 | nv04_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj) | ||
170 | { | ||
171 | if (!gpuobj->im_pramin || gpuobj->im_bound) | ||
172 | return -EINVAL; | ||
173 | |||
174 | gpuobj->im_bound = 1; | ||
175 | return 0; | ||
176 | } | ||
177 | |||
178 | int | ||
179 | nv04_instmem_unbind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj) | ||
180 | { | ||
181 | if (gpuobj->im_bound == 0) | ||
182 | return -EINVAL; | ||
183 | |||
184 | gpuobj->im_bound = 0; | ||
185 | return 0; | ||
186 | } | ||
187 | |||
188 | void | ||
189 | nv04_instmem_prepare_access(struct drm_device *dev, bool write) | ||
190 | { | ||
191 | } | ||
192 | |||
193 | void | ||
194 | nv04_instmem_finish_access(struct drm_device *dev) | ||
195 | { | ||
196 | } | ||
197 | |||
198 | int | ||
199 | nv04_instmem_suspend(struct drm_device *dev) | ||
200 | { | ||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | void | ||
205 | nv04_instmem_resume(struct drm_device *dev) | ||
206 | { | ||
207 | } | ||
208 | |||
diff --git a/drivers/gpu/drm/nouveau/nv04_mc.c b/drivers/gpu/drm/nouveau/nv04_mc.c new file mode 100644 index 000000000000..617ed1e05269 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv04_mc.c | |||
@@ -0,0 +1,20 @@ | |||
1 | #include "drmP.h" | ||
2 | #include "drm.h" | ||
3 | #include "nouveau_drv.h" | ||
4 | #include "nouveau_drm.h" | ||
5 | |||
6 | int | ||
7 | nv04_mc_init(struct drm_device *dev) | ||
8 | { | ||
9 | /* Power up everything, resetting each individual unit will | ||
10 | * be done later if needed. | ||
11 | */ | ||
12 | |||
13 | nv_wr32(dev, NV03_PMC_ENABLE, 0xFFFFFFFF); | ||
14 | return 0; | ||
15 | } | ||
16 | |||
17 | void | ||
18 | nv04_mc_takedown(struct drm_device *dev) | ||
19 | { | ||
20 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nv04_timer.c b/drivers/gpu/drm/nouveau/nv04_timer.c new file mode 100644 index 000000000000..1d09ddd57399 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv04_timer.c | |||
@@ -0,0 +1,51 @@ | |||
1 | #include "drmP.h" | ||
2 | #include "drm.h" | ||
3 | #include "nouveau_drv.h" | ||
4 | #include "nouveau_drm.h" | ||
5 | |||
6 | int | ||
7 | nv04_timer_init(struct drm_device *dev) | ||
8 | { | ||
9 | nv_wr32(dev, NV04_PTIMER_INTR_EN_0, 0x00000000); | ||
10 | nv_wr32(dev, NV04_PTIMER_INTR_0, 0xFFFFFFFF); | ||
11 | |||
12 | /* Just use the pre-existing values when possible for now; these regs | ||
13 | * are not written in nv (driver writer missed a /4 on the address), and | ||
14 | * writing 8 and 3 to the correct regs breaks the timings on the LVDS | ||
15 | * hardware sequencing microcode. | ||
16 | * A correct solution (involving calculations with the GPU PLL) can | ||
17 | * be done when kernel modesetting lands | ||
18 | */ | ||
19 | if (!nv_rd32(dev, NV04_PTIMER_NUMERATOR) || | ||
20 | !nv_rd32(dev, NV04_PTIMER_DENOMINATOR)) { | ||
21 | nv_wr32(dev, NV04_PTIMER_NUMERATOR, 0x00000008); | ||
22 | nv_wr32(dev, NV04_PTIMER_DENOMINATOR, 0x00000003); | ||
23 | } | ||
24 | |||
25 | return 0; | ||
26 | } | ||
27 | |||
28 | uint64_t | ||
29 | nv04_timer_read(struct drm_device *dev) | ||
30 | { | ||
31 | uint32_t low; | ||
32 | /* From kmmio dumps on nv28 this looks like how the blob does this. | ||
33 | * It reads the high dword twice, before and after. | ||
34 | * The only explanation seems to be that the 64-bit timer counter | ||
35 | * advances between high and low dword reads and may corrupt the | ||
36 | * result. Not confirmed. | ||
37 | */ | ||
38 | uint32_t high2 = nv_rd32(dev, NV04_PTIMER_TIME_1); | ||
39 | uint32_t high1; | ||
40 | do { | ||
41 | high1 = high2; | ||
42 | low = nv_rd32(dev, NV04_PTIMER_TIME_0); | ||
43 | high2 = nv_rd32(dev, NV04_PTIMER_TIME_1); | ||
44 | } while (high1 != high2); | ||
45 | return (((uint64_t)high2) << 32) | (uint64_t)low; | ||
46 | } | ||
47 | |||
48 | void | ||
49 | nv04_timer_takedown(struct drm_device *dev) | ||
50 | { | ||
51 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nv04_tv.c b/drivers/gpu/drm/nouveau/nv04_tv.c new file mode 100644 index 000000000000..9c63099e9c42 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv04_tv.c | |||
@@ -0,0 +1,305 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009 Francisco Jerez. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include "drmP.h" | ||
28 | #include "nouveau_drv.h" | ||
29 | #include "nouveau_encoder.h" | ||
30 | #include "nouveau_connector.h" | ||
31 | #include "nouveau_crtc.h" | ||
32 | #include "nouveau_hw.h" | ||
33 | #include "drm_crtc_helper.h" | ||
34 | |||
35 | #include "i2c/ch7006.h" | ||
36 | |||
37 | static struct { | ||
38 | struct i2c_board_info board_info; | ||
39 | struct drm_encoder_funcs funcs; | ||
40 | struct drm_encoder_helper_funcs hfuncs; | ||
41 | void *params; | ||
42 | |||
43 | } nv04_tv_encoder_info[] = { | ||
44 | { | ||
45 | .board_info = { I2C_BOARD_INFO("ch7006", 0x75) }, | ||
46 | .params = &(struct ch7006_encoder_params) { | ||
47 | CH7006_FORMAT_RGB24m12I, CH7006_CLOCK_MASTER, | ||
48 | 0, 0, 0, | ||
49 | CH7006_SYNC_SLAVE, CH7006_SYNC_SEPARATED, | ||
50 | CH7006_POUT_3_3V, CH7006_ACTIVE_HSYNC | ||
51 | }, | ||
52 | }, | ||
53 | }; | ||
54 | |||
55 | static bool probe_i2c_addr(struct i2c_adapter *adapter, int addr) | ||
56 | { | ||
57 | struct i2c_msg msg = { | ||
58 | .addr = addr, | ||
59 | .len = 0, | ||
60 | }; | ||
61 | |||
62 | return i2c_transfer(adapter, &msg, 1) == 1; | ||
63 | } | ||
64 | |||
65 | int nv04_tv_identify(struct drm_device *dev, int i2c_index) | ||
66 | { | ||
67 | struct nouveau_i2c_chan *i2c; | ||
68 | bool was_locked; | ||
69 | int i, ret; | ||
70 | |||
71 | NV_TRACE(dev, "Probing TV encoders on I2C bus: %d\n", i2c_index); | ||
72 | |||
73 | i2c = nouveau_i2c_find(dev, i2c_index); | ||
74 | if (!i2c) | ||
75 | return -ENODEV; | ||
76 | |||
77 | was_locked = NVLockVgaCrtcs(dev, false); | ||
78 | |||
79 | for (i = 0; i < ARRAY_SIZE(nv04_tv_encoder_info); i++) { | ||
80 | if (probe_i2c_addr(&i2c->adapter, | ||
81 | nv04_tv_encoder_info[i].board_info.addr)) { | ||
82 | ret = i; | ||
83 | break; | ||
84 | } | ||
85 | } | ||
86 | |||
87 | if (i < ARRAY_SIZE(nv04_tv_encoder_info)) { | ||
88 | NV_TRACE(dev, "Detected TV encoder: %s\n", | ||
89 | nv04_tv_encoder_info[i].board_info.type); | ||
90 | |||
91 | } else { | ||
92 | NV_TRACE(dev, "No TV encoders found.\n"); | ||
93 | i = -ENODEV; | ||
94 | } | ||
95 | |||
96 | NVLockVgaCrtcs(dev, was_locked); | ||
97 | return i; | ||
98 | } | ||
99 | |||
100 | #define PLLSEL_TV_CRTC1_MASK \ | ||
101 | (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 \ | ||
102 | | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1) | ||
103 | #define PLLSEL_TV_CRTC2_MASK \ | ||
104 | (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2 \ | ||
105 | | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2) | ||
106 | |||
107 | static void nv04_tv_dpms(struct drm_encoder *encoder, int mode) | ||
108 | { | ||
109 | struct drm_device *dev = encoder->dev; | ||
110 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
111 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
112 | struct nv04_mode_state *state = &dev_priv->mode_reg; | ||
113 | uint8_t crtc1A; | ||
114 | |||
115 | NV_INFO(dev, "Setting dpms mode %d on TV encoder (output %d)\n", | ||
116 | mode, nv_encoder->dcb->index); | ||
117 | |||
118 | state->pllsel &= ~(PLLSEL_TV_CRTC1_MASK | PLLSEL_TV_CRTC2_MASK); | ||
119 | |||
120 | if (mode == DRM_MODE_DPMS_ON) { | ||
121 | int head = nouveau_crtc(encoder->crtc)->index; | ||
122 | crtc1A = NVReadVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX); | ||
123 | |||
124 | state->pllsel |= head ? PLLSEL_TV_CRTC2_MASK : | ||
125 | PLLSEL_TV_CRTC1_MASK; | ||
126 | |||
127 | /* Inhibit hsync */ | ||
128 | crtc1A |= 0x80; | ||
129 | |||
130 | NVWriteVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX, crtc1A); | ||
131 | } | ||
132 | |||
133 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT, state->pllsel); | ||
134 | |||
135 | to_encoder_slave(encoder)->slave_funcs->dpms(encoder, mode); | ||
136 | } | ||
137 | |||
138 | static void nv04_tv_bind(struct drm_device *dev, int head, bool bind) | ||
139 | { | ||
140 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
141 | struct nv04_crtc_reg *state = &dev_priv->mode_reg.crtc_reg[head]; | ||
142 | |||
143 | state->tv_setup = 0; | ||
144 | |||
145 | if (bind) { | ||
146 | state->CRTC[NV_CIO_CRE_LCD__INDEX] = 0; | ||
147 | state->CRTC[NV_CIO_CRE_49] |= 0x10; | ||
148 | } else { | ||
149 | state->CRTC[NV_CIO_CRE_49] &= ~0x10; | ||
150 | } | ||
151 | |||
152 | NVWriteVgaCrtc(dev, head, NV_CIO_CRE_LCD__INDEX, | ||
153 | state->CRTC[NV_CIO_CRE_LCD__INDEX]); | ||
154 | NVWriteVgaCrtc(dev, head, NV_CIO_CRE_49, | ||
155 | state->CRTC[NV_CIO_CRE_49]); | ||
156 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP, | ||
157 | state->tv_setup); | ||
158 | } | ||
159 | |||
160 | static void nv04_tv_prepare(struct drm_encoder *encoder) | ||
161 | { | ||
162 | struct drm_device *dev = encoder->dev; | ||
163 | int head = nouveau_crtc(encoder->crtc)->index; | ||
164 | struct drm_encoder_helper_funcs *helper = encoder->helper_private; | ||
165 | |||
166 | helper->dpms(encoder, DRM_MODE_DPMS_OFF); | ||
167 | |||
168 | nv04_dfp_disable(dev, head); | ||
169 | |||
170 | if (nv_two_heads(dev)) | ||
171 | nv04_tv_bind(dev, head ^ 1, false); | ||
172 | |||
173 | nv04_tv_bind(dev, head, true); | ||
174 | } | ||
175 | |||
176 | static void nv04_tv_mode_set(struct drm_encoder *encoder, | ||
177 | struct drm_display_mode *mode, | ||
178 | struct drm_display_mode *adjusted_mode) | ||
179 | { | ||
180 | struct drm_device *dev = encoder->dev; | ||
181 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
182 | struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); | ||
183 | struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index]; | ||
184 | |||
185 | regp->tv_htotal = adjusted_mode->htotal; | ||
186 | regp->tv_vtotal = adjusted_mode->vtotal; | ||
187 | |||
188 | /* These delay the TV signals with respect to the VGA port, | ||
189 | * they might be useful if we ever allow a CRTC to drive | ||
190 | * multiple outputs. | ||
191 | */ | ||
192 | regp->tv_hskew = 1; | ||
193 | regp->tv_hsync_delay = 1; | ||
194 | regp->tv_hsync_delay2 = 64; | ||
195 | regp->tv_vskew = 1; | ||
196 | regp->tv_vsync_delay = 1; | ||
197 | |||
198 | to_encoder_slave(encoder)->slave_funcs->mode_set(encoder, mode, adjusted_mode); | ||
199 | } | ||
200 | |||
201 | static void nv04_tv_commit(struct drm_encoder *encoder) | ||
202 | { | ||
203 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
204 | struct drm_device *dev = encoder->dev; | ||
205 | struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); | ||
206 | struct drm_encoder_helper_funcs *helper = encoder->helper_private; | ||
207 | |||
208 | helper->dpms(encoder, DRM_MODE_DPMS_ON); | ||
209 | |||
210 | NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n", | ||
211 | drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base), nv_crtc->index, | ||
212 | '@' + ffs(nv_encoder->dcb->or)); | ||
213 | } | ||
214 | |||
215 | static void nv04_tv_destroy(struct drm_encoder *encoder) | ||
216 | { | ||
217 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
218 | |||
219 | to_encoder_slave(encoder)->slave_funcs->destroy(encoder); | ||
220 | |||
221 | drm_encoder_cleanup(encoder); | ||
222 | |||
223 | kfree(nv_encoder); | ||
224 | } | ||
225 | |||
226 | int nv04_tv_create(struct drm_device *dev, struct dcb_entry *entry) | ||
227 | { | ||
228 | struct nouveau_encoder *nv_encoder; | ||
229 | struct drm_encoder *encoder; | ||
230 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
231 | struct i2c_adapter *adap; | ||
232 | struct drm_encoder_funcs *funcs = NULL; | ||
233 | struct drm_encoder_helper_funcs *hfuncs = NULL; | ||
234 | struct drm_encoder_slave_funcs *sfuncs = NULL; | ||
235 | int i2c_index = entry->i2c_index; | ||
236 | int type, ret; | ||
237 | bool was_locked; | ||
238 | |||
239 | /* Ensure that we can talk to this encoder */ | ||
240 | type = nv04_tv_identify(dev, i2c_index); | ||
241 | if (type < 0) | ||
242 | return type; | ||
243 | |||
244 | /* Allocate the necessary memory */ | ||
245 | nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); | ||
246 | if (!nv_encoder) | ||
247 | return -ENOMEM; | ||
248 | |||
249 | /* Initialize the common members */ | ||
250 | encoder = to_drm_encoder(nv_encoder); | ||
251 | |||
252 | funcs = &nv04_tv_encoder_info[type].funcs; | ||
253 | hfuncs = &nv04_tv_encoder_info[type].hfuncs; | ||
254 | |||
255 | drm_encoder_init(dev, encoder, funcs, DRM_MODE_ENCODER_TVDAC); | ||
256 | drm_encoder_helper_add(encoder, hfuncs); | ||
257 | |||
258 | encoder->possible_crtcs = entry->heads; | ||
259 | encoder->possible_clones = 0; | ||
260 | |||
261 | nv_encoder->dcb = entry; | ||
262 | nv_encoder->or = ffs(entry->or) - 1; | ||
263 | |||
264 | /* Run the slave-specific initialization */ | ||
265 | adap = &dev_priv->vbios->dcb->i2c[i2c_index].chan->adapter; | ||
266 | |||
267 | was_locked = NVLockVgaCrtcs(dev, false); | ||
268 | |||
269 | ret = drm_i2c_encoder_init(encoder->dev, to_encoder_slave(encoder), adap, | ||
270 | &nv04_tv_encoder_info[type].board_info); | ||
271 | |||
272 | NVLockVgaCrtcs(dev, was_locked); | ||
273 | |||
274 | if (ret < 0) | ||
275 | goto fail; | ||
276 | |||
277 | /* Fill the function pointers */ | ||
278 | sfuncs = to_encoder_slave(encoder)->slave_funcs; | ||
279 | |||
280 | *funcs = (struct drm_encoder_funcs) { | ||
281 | .destroy = nv04_tv_destroy, | ||
282 | }; | ||
283 | |||
284 | *hfuncs = (struct drm_encoder_helper_funcs) { | ||
285 | .dpms = nv04_tv_dpms, | ||
286 | .save = sfuncs->save, | ||
287 | .restore = sfuncs->restore, | ||
288 | .mode_fixup = sfuncs->mode_fixup, | ||
289 | .prepare = nv04_tv_prepare, | ||
290 | .commit = nv04_tv_commit, | ||
291 | .mode_set = nv04_tv_mode_set, | ||
292 | .detect = sfuncs->detect, | ||
293 | }; | ||
294 | |||
295 | /* Set the slave encoder configuration */ | ||
296 | sfuncs->set_config(encoder, nv04_tv_encoder_info[type].params); | ||
297 | |||
298 | return 0; | ||
299 | |||
300 | fail: | ||
301 | drm_encoder_cleanup(encoder); | ||
302 | |||
303 | kfree(nv_encoder); | ||
304 | return ret; | ||
305 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nv10_fb.c b/drivers/gpu/drm/nouveau/nv10_fb.c new file mode 100644 index 000000000000..79e2d104d70a --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv10_fb.c | |||
@@ -0,0 +1,24 @@ | |||
1 | #include "drmP.h" | ||
2 | #include "drm.h" | ||
3 | #include "nouveau_drv.h" | ||
4 | #include "nouveau_drm.h" | ||
5 | |||
6 | int | ||
7 | nv10_fb_init(struct drm_device *dev) | ||
8 | { | ||
9 | uint32_t fb_bar_size; | ||
10 | int i; | ||
11 | |||
12 | fb_bar_size = drm_get_resource_len(dev, 0) - 1; | ||
13 | for (i = 0; i < NV10_PFB_TILE__SIZE; i++) { | ||
14 | nv_wr32(dev, NV10_PFB_TILE(i), 0); | ||
15 | nv_wr32(dev, NV10_PFB_TLIMIT(i), fb_bar_size); | ||
16 | } | ||
17 | |||
18 | return 0; | ||
19 | } | ||
20 | |||
21 | void | ||
22 | nv10_fb_takedown(struct drm_device *dev) | ||
23 | { | ||
24 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nv10_fifo.c b/drivers/gpu/drm/nouveau/nv10_fifo.c new file mode 100644 index 000000000000..7aeabf262bc0 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv10_fifo.c | |||
@@ -0,0 +1,260 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007 Ben Skeggs. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include "drmP.h" | ||
28 | #include "drm.h" | ||
29 | #include "nouveau_drv.h" | ||
30 | |||
31 | #define NV10_RAMFC(c) (dev_priv->ramfc_offset + ((c) * NV10_RAMFC__SIZE)) | ||
32 | #define NV10_RAMFC__SIZE ((dev_priv->chipset) >= 0x17 ? 64 : 32) | ||
33 | |||
34 | int | ||
35 | nv10_fifo_channel_id(struct drm_device *dev) | ||
36 | { | ||
37 | return nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH1) & | ||
38 | NV10_PFIFO_CACHE1_PUSH1_CHID_MASK; | ||
39 | } | ||
40 | |||
41 | int | ||
42 | nv10_fifo_create_context(struct nouveau_channel *chan) | ||
43 | { | ||
44 | struct drm_nouveau_private *dev_priv = chan->dev->dev_private; | ||
45 | struct drm_device *dev = chan->dev; | ||
46 | uint32_t fc = NV10_RAMFC(chan->id); | ||
47 | int ret; | ||
48 | |||
49 | ret = nouveau_gpuobj_new_fake(dev, NV10_RAMFC(chan->id), ~0, | ||
50 | NV10_RAMFC__SIZE, NVOBJ_FLAG_ZERO_ALLOC | | ||
51 | NVOBJ_FLAG_ZERO_FREE, NULL, &chan->ramfc); | ||
52 | if (ret) | ||
53 | return ret; | ||
54 | |||
55 | /* Fill entries that are seen filled in dumps of nvidia driver just | ||
56 | * after channel's is put into DMA mode | ||
57 | */ | ||
58 | dev_priv->engine.instmem.prepare_access(dev, true); | ||
59 | nv_wi32(dev, fc + 0, chan->pushbuf_base); | ||
60 | nv_wi32(dev, fc + 4, chan->pushbuf_base); | ||
61 | nv_wi32(dev, fc + 12, chan->pushbuf->instance >> 4); | ||
62 | nv_wi32(dev, fc + 20, NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES | | ||
63 | NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES | | ||
64 | NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8 | | ||
65 | #ifdef __BIG_ENDIAN | ||
66 | NV_PFIFO_CACHE1_BIG_ENDIAN | | ||
67 | #endif | ||
68 | 0); | ||
69 | dev_priv->engine.instmem.finish_access(dev); | ||
70 | |||
71 | /* enable the fifo dma operation */ | ||
72 | nv_wr32(dev, NV04_PFIFO_MODE, | ||
73 | nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id)); | ||
74 | return 0; | ||
75 | } | ||
76 | |||
77 | void | ||
78 | nv10_fifo_destroy_context(struct nouveau_channel *chan) | ||
79 | { | ||
80 | struct drm_device *dev = chan->dev; | ||
81 | |||
82 | nv_wr32(dev, NV04_PFIFO_MODE, | ||
83 | nv_rd32(dev, NV04_PFIFO_MODE) & ~(1 << chan->id)); | ||
84 | |||
85 | nouveau_gpuobj_ref_del(dev, &chan->ramfc); | ||
86 | } | ||
87 | |||
88 | static void | ||
89 | nv10_fifo_do_load_context(struct drm_device *dev, int chid) | ||
90 | { | ||
91 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
92 | uint32_t fc = NV10_RAMFC(chid), tmp; | ||
93 | |||
94 | dev_priv->engine.instmem.prepare_access(dev, false); | ||
95 | |||
96 | nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUT, nv_ri32(dev, fc + 0)); | ||
97 | nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_GET, nv_ri32(dev, fc + 4)); | ||
98 | nv_wr32(dev, NV10_PFIFO_CACHE1_REF_CNT, nv_ri32(dev, fc + 8)); | ||
99 | |||
100 | tmp = nv_ri32(dev, fc + 12); | ||
101 | nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_INSTANCE, tmp & 0xFFFF); | ||
102 | nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT, tmp >> 16); | ||
103 | |||
104 | nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_STATE, nv_ri32(dev, fc + 16)); | ||
105 | nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_FETCH, nv_ri32(dev, fc + 20)); | ||
106 | nv_wr32(dev, NV04_PFIFO_CACHE1_ENGINE, nv_ri32(dev, fc + 24)); | ||
107 | nv_wr32(dev, NV04_PFIFO_CACHE1_PULL1, nv_ri32(dev, fc + 28)); | ||
108 | |||
109 | if (dev_priv->chipset < 0x17) | ||
110 | goto out; | ||
111 | |||
112 | nv_wr32(dev, NV10_PFIFO_CACHE1_ACQUIRE_VALUE, nv_ri32(dev, fc + 32)); | ||
113 | tmp = nv_ri32(dev, fc + 36); | ||
114 | nv_wr32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP, tmp); | ||
115 | nv_wr32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT, nv_ri32(dev, fc + 40)); | ||
116 | nv_wr32(dev, NV10_PFIFO_CACHE1_SEMAPHORE, nv_ri32(dev, fc + 44)); | ||
117 | nv_wr32(dev, NV10_PFIFO_CACHE1_DMA_SUBROUTINE, nv_ri32(dev, fc + 48)); | ||
118 | |||
119 | out: | ||
120 | dev_priv->engine.instmem.finish_access(dev); | ||
121 | |||
122 | nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0); | ||
123 | nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, 0); | ||
124 | } | ||
125 | |||
126 | int | ||
127 | nv10_fifo_load_context(struct nouveau_channel *chan) | ||
128 | { | ||
129 | struct drm_device *dev = chan->dev; | ||
130 | uint32_t tmp; | ||
131 | |||
132 | nv10_fifo_do_load_context(dev, chan->id); | ||
133 | |||
134 | nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, | ||
135 | NV03_PFIFO_CACHE1_PUSH1_DMA | chan->id); | ||
136 | nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 1); | ||
137 | |||
138 | /* Reset NV04_PFIFO_CACHE1_DMA_CTL_AT_INFO to INVALID */ | ||
139 | tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_CTL) & ~(1 << 31); | ||
140 | nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_CTL, tmp); | ||
141 | |||
142 | return 0; | ||
143 | } | ||
144 | |||
145 | int | ||
146 | nv10_fifo_unload_context(struct drm_device *dev) | ||
147 | { | ||
148 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
149 | struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; | ||
150 | uint32_t fc, tmp; | ||
151 | int chid; | ||
152 | |||
153 | chid = pfifo->channel_id(dev); | ||
154 | if (chid < 0 || chid >= dev_priv->engine.fifo.channels) | ||
155 | return 0; | ||
156 | fc = NV10_RAMFC(chid); | ||
157 | |||
158 | dev_priv->engine.instmem.prepare_access(dev, true); | ||
159 | |||
160 | nv_wi32(dev, fc + 0, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUT)); | ||
161 | nv_wi32(dev, fc + 4, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET)); | ||
162 | nv_wi32(dev, fc + 8, nv_rd32(dev, NV10_PFIFO_CACHE1_REF_CNT)); | ||
163 | tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_INSTANCE) & 0xFFFF; | ||
164 | tmp |= (nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT) << 16); | ||
165 | nv_wi32(dev, fc + 12, tmp); | ||
166 | nv_wi32(dev, fc + 16, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_STATE)); | ||
167 | nv_wi32(dev, fc + 20, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_FETCH)); | ||
168 | nv_wi32(dev, fc + 24, nv_rd32(dev, NV04_PFIFO_CACHE1_ENGINE)); | ||
169 | nv_wi32(dev, fc + 28, nv_rd32(dev, NV04_PFIFO_CACHE1_PULL1)); | ||
170 | |||
171 | if (dev_priv->chipset < 0x17) | ||
172 | goto out; | ||
173 | |||
174 | nv_wi32(dev, fc + 32, nv_rd32(dev, NV10_PFIFO_CACHE1_ACQUIRE_VALUE)); | ||
175 | tmp = nv_rd32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP); | ||
176 | nv_wi32(dev, fc + 36, tmp); | ||
177 | nv_wi32(dev, fc + 40, nv_rd32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT)); | ||
178 | nv_wi32(dev, fc + 44, nv_rd32(dev, NV10_PFIFO_CACHE1_SEMAPHORE)); | ||
179 | nv_wi32(dev, fc + 48, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET)); | ||
180 | |||
181 | out: | ||
182 | dev_priv->engine.instmem.finish_access(dev); | ||
183 | |||
184 | nv10_fifo_do_load_context(dev, pfifo->channels - 1); | ||
185 | nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, pfifo->channels - 1); | ||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | static void | ||
190 | nv10_fifo_init_reset(struct drm_device *dev) | ||
191 | { | ||
192 | nv_wr32(dev, NV03_PMC_ENABLE, | ||
193 | nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PFIFO); | ||
194 | nv_wr32(dev, NV03_PMC_ENABLE, | ||
195 | nv_rd32(dev, NV03_PMC_ENABLE) | NV_PMC_ENABLE_PFIFO); | ||
196 | |||
197 | nv_wr32(dev, 0x003224, 0x000f0078); | ||
198 | nv_wr32(dev, 0x002044, 0x0101ffff); | ||
199 | nv_wr32(dev, 0x002040, 0x000000ff); | ||
200 | nv_wr32(dev, 0x002500, 0x00000000); | ||
201 | nv_wr32(dev, 0x003000, 0x00000000); | ||
202 | nv_wr32(dev, 0x003050, 0x00000000); | ||
203 | |||
204 | nv_wr32(dev, 0x003258, 0x00000000); | ||
205 | nv_wr32(dev, 0x003210, 0x00000000); | ||
206 | nv_wr32(dev, 0x003270, 0x00000000); | ||
207 | } | ||
208 | |||
209 | static void | ||
210 | nv10_fifo_init_ramxx(struct drm_device *dev) | ||
211 | { | ||
212 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
213 | |||
214 | nv_wr32(dev, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ | | ||
215 | ((dev_priv->ramht_bits - 9) << 16) | | ||
216 | (dev_priv->ramht_offset >> 8)); | ||
217 | nv_wr32(dev, NV03_PFIFO_RAMRO, dev_priv->ramro_offset>>8); | ||
218 | |||
219 | if (dev_priv->chipset < 0x17) { | ||
220 | nv_wr32(dev, NV03_PFIFO_RAMFC, dev_priv->ramfc_offset >> 8); | ||
221 | } else { | ||
222 | nv_wr32(dev, NV03_PFIFO_RAMFC, (dev_priv->ramfc_offset >> 8) | | ||
223 | (1 << 16) /* 64 Bytes entry*/); | ||
224 | /* XXX nvidia blob set bit 18, 21,23 for nv20 & nv30 */ | ||
225 | } | ||
226 | } | ||
227 | |||
228 | static void | ||
229 | nv10_fifo_init_intr(struct drm_device *dev) | ||
230 | { | ||
231 | nv_wr32(dev, 0x002100, 0xffffffff); | ||
232 | nv_wr32(dev, 0x002140, 0xffffffff); | ||
233 | } | ||
234 | |||
235 | int | ||
236 | nv10_fifo_init(struct drm_device *dev) | ||
237 | { | ||
238 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
239 | struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; | ||
240 | int i; | ||
241 | |||
242 | nv10_fifo_init_reset(dev); | ||
243 | nv10_fifo_init_ramxx(dev); | ||
244 | |||
245 | nv10_fifo_do_load_context(dev, pfifo->channels - 1); | ||
246 | nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, pfifo->channels - 1); | ||
247 | |||
248 | nv10_fifo_init_intr(dev); | ||
249 | pfifo->enable(dev); | ||
250 | pfifo->reassign(dev, true); | ||
251 | |||
252 | for (i = 0; i < dev_priv->engine.fifo.channels; i++) { | ||
253 | if (dev_priv->fifos[i]) { | ||
254 | uint32_t mode = nv_rd32(dev, NV04_PFIFO_MODE); | ||
255 | nv_wr32(dev, NV04_PFIFO_MODE, mode | (1 << i)); | ||
256 | } | ||
257 | } | ||
258 | |||
259 | return 0; | ||
260 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nv10_graph.c b/drivers/gpu/drm/nouveau/nv10_graph.c new file mode 100644 index 000000000000..6bf6804bb0ef --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv10_graph.c | |||
@@ -0,0 +1,892 @@ | |||
1 | /* | ||
2 | * Copyright 2007 Matthieu CASTET <castet.matthieu@free.fr> | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
6 | * copy of this software and associated documentation files (the "Software"), | ||
7 | * to deal in the Software without restriction, including without limitation | ||
8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
9 | * and/or sell copies of the Software, and to permit persons to whom the | ||
10 | * Software is furnished to do so, subject to the following conditions: | ||
11 | * | ||
12 | * The above copyright notice and this permission notice (including the next | ||
13 | * paragraph) shall be included in all copies or substantial portions of the | ||
14 | * 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 | * PRECISION INSIGHT AND/OR ITS SUPPLIERS 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 OTHER | ||
22 | * DEALINGS IN THE SOFTWARE. | ||
23 | */ | ||
24 | |||
25 | #include "drmP.h" | ||
26 | #include "drm.h" | ||
27 | #include "nouveau_drm.h" | ||
28 | #include "nouveau_drv.h" | ||
29 | |||
30 | #define NV10_FIFO_NUMBER 32 | ||
31 | |||
32 | struct pipe_state { | ||
33 | uint32_t pipe_0x0000[0x040/4]; | ||
34 | uint32_t pipe_0x0040[0x010/4]; | ||
35 | uint32_t pipe_0x0200[0x0c0/4]; | ||
36 | uint32_t pipe_0x4400[0x080/4]; | ||
37 | uint32_t pipe_0x6400[0x3b0/4]; | ||
38 | uint32_t pipe_0x6800[0x2f0/4]; | ||
39 | uint32_t pipe_0x6c00[0x030/4]; | ||
40 | uint32_t pipe_0x7000[0x130/4]; | ||
41 | uint32_t pipe_0x7400[0x0c0/4]; | ||
42 | uint32_t pipe_0x7800[0x0c0/4]; | ||
43 | }; | ||
44 | |||
45 | static int nv10_graph_ctx_regs[] = { | ||
46 | NV10_PGRAPH_CTX_SWITCH1, | ||
47 | NV10_PGRAPH_CTX_SWITCH2, | ||
48 | NV10_PGRAPH_CTX_SWITCH3, | ||
49 | NV10_PGRAPH_CTX_SWITCH4, | ||
50 | NV10_PGRAPH_CTX_SWITCH5, | ||
51 | NV10_PGRAPH_CTX_CACHE1, /* 8 values from 0x400160 to 0x40017c */ | ||
52 | NV10_PGRAPH_CTX_CACHE2, /* 8 values from 0x400180 to 0x40019c */ | ||
53 | NV10_PGRAPH_CTX_CACHE3, /* 8 values from 0x4001a0 to 0x4001bc */ | ||
54 | NV10_PGRAPH_CTX_CACHE4, /* 8 values from 0x4001c0 to 0x4001dc */ | ||
55 | NV10_PGRAPH_CTX_CACHE5, /* 8 values from 0x4001e0 to 0x4001fc */ | ||
56 | 0x00400164, | ||
57 | 0x00400184, | ||
58 | 0x004001a4, | ||
59 | 0x004001c4, | ||
60 | 0x004001e4, | ||
61 | 0x00400168, | ||
62 | 0x00400188, | ||
63 | 0x004001a8, | ||
64 | 0x004001c8, | ||
65 | 0x004001e8, | ||
66 | 0x0040016c, | ||
67 | 0x0040018c, | ||
68 | 0x004001ac, | ||
69 | 0x004001cc, | ||
70 | 0x004001ec, | ||
71 | 0x00400170, | ||
72 | 0x00400190, | ||
73 | 0x004001b0, | ||
74 | 0x004001d0, | ||
75 | 0x004001f0, | ||
76 | 0x00400174, | ||
77 | 0x00400194, | ||
78 | 0x004001b4, | ||
79 | 0x004001d4, | ||
80 | 0x004001f4, | ||
81 | 0x00400178, | ||
82 | 0x00400198, | ||
83 | 0x004001b8, | ||
84 | 0x004001d8, | ||
85 | 0x004001f8, | ||
86 | 0x0040017c, | ||
87 | 0x0040019c, | ||
88 | 0x004001bc, | ||
89 | 0x004001dc, | ||
90 | 0x004001fc, | ||
91 | NV10_PGRAPH_CTX_USER, | ||
92 | NV04_PGRAPH_DMA_START_0, | ||
93 | NV04_PGRAPH_DMA_START_1, | ||
94 | NV04_PGRAPH_DMA_LENGTH, | ||
95 | NV04_PGRAPH_DMA_MISC, | ||
96 | NV10_PGRAPH_DMA_PITCH, | ||
97 | NV04_PGRAPH_BOFFSET0, | ||
98 | NV04_PGRAPH_BBASE0, | ||
99 | NV04_PGRAPH_BLIMIT0, | ||
100 | NV04_PGRAPH_BOFFSET1, | ||
101 | NV04_PGRAPH_BBASE1, | ||
102 | NV04_PGRAPH_BLIMIT1, | ||
103 | NV04_PGRAPH_BOFFSET2, | ||
104 | NV04_PGRAPH_BBASE2, | ||
105 | NV04_PGRAPH_BLIMIT2, | ||
106 | NV04_PGRAPH_BOFFSET3, | ||
107 | NV04_PGRAPH_BBASE3, | ||
108 | NV04_PGRAPH_BLIMIT3, | ||
109 | NV04_PGRAPH_BOFFSET4, | ||
110 | NV04_PGRAPH_BBASE4, | ||
111 | NV04_PGRAPH_BLIMIT4, | ||
112 | NV04_PGRAPH_BOFFSET5, | ||
113 | NV04_PGRAPH_BBASE5, | ||
114 | NV04_PGRAPH_BLIMIT5, | ||
115 | NV04_PGRAPH_BPITCH0, | ||
116 | NV04_PGRAPH_BPITCH1, | ||
117 | NV04_PGRAPH_BPITCH2, | ||
118 | NV04_PGRAPH_BPITCH3, | ||
119 | NV04_PGRAPH_BPITCH4, | ||
120 | NV10_PGRAPH_SURFACE, | ||
121 | NV10_PGRAPH_STATE, | ||
122 | NV04_PGRAPH_BSWIZZLE2, | ||
123 | NV04_PGRAPH_BSWIZZLE5, | ||
124 | NV04_PGRAPH_BPIXEL, | ||
125 | NV10_PGRAPH_NOTIFY, | ||
126 | NV04_PGRAPH_PATT_COLOR0, | ||
127 | NV04_PGRAPH_PATT_COLOR1, | ||
128 | NV04_PGRAPH_PATT_COLORRAM, /* 64 values from 0x400900 to 0x4009fc */ | ||
129 | 0x00400904, | ||
130 | 0x00400908, | ||
131 | 0x0040090c, | ||
132 | 0x00400910, | ||
133 | 0x00400914, | ||
134 | 0x00400918, | ||
135 | 0x0040091c, | ||
136 | 0x00400920, | ||
137 | 0x00400924, | ||
138 | 0x00400928, | ||
139 | 0x0040092c, | ||
140 | 0x00400930, | ||
141 | 0x00400934, | ||
142 | 0x00400938, | ||
143 | 0x0040093c, | ||
144 | 0x00400940, | ||
145 | 0x00400944, | ||
146 | 0x00400948, | ||
147 | 0x0040094c, | ||
148 | 0x00400950, | ||
149 | 0x00400954, | ||
150 | 0x00400958, | ||
151 | 0x0040095c, | ||
152 | 0x00400960, | ||
153 | 0x00400964, | ||
154 | 0x00400968, | ||
155 | 0x0040096c, | ||
156 | 0x00400970, | ||
157 | 0x00400974, | ||
158 | 0x00400978, | ||
159 | 0x0040097c, | ||
160 | 0x00400980, | ||
161 | 0x00400984, | ||
162 | 0x00400988, | ||
163 | 0x0040098c, | ||
164 | 0x00400990, | ||
165 | 0x00400994, | ||
166 | 0x00400998, | ||
167 | 0x0040099c, | ||
168 | 0x004009a0, | ||
169 | 0x004009a4, | ||
170 | 0x004009a8, | ||
171 | 0x004009ac, | ||
172 | 0x004009b0, | ||
173 | 0x004009b4, | ||
174 | 0x004009b8, | ||
175 | 0x004009bc, | ||
176 | 0x004009c0, | ||
177 | 0x004009c4, | ||
178 | 0x004009c8, | ||
179 | 0x004009cc, | ||
180 | 0x004009d0, | ||
181 | 0x004009d4, | ||
182 | 0x004009d8, | ||
183 | 0x004009dc, | ||
184 | 0x004009e0, | ||
185 | 0x004009e4, | ||
186 | 0x004009e8, | ||
187 | 0x004009ec, | ||
188 | 0x004009f0, | ||
189 | 0x004009f4, | ||
190 | 0x004009f8, | ||
191 | 0x004009fc, | ||
192 | NV04_PGRAPH_PATTERN, /* 2 values from 0x400808 to 0x40080c */ | ||
193 | 0x0040080c, | ||
194 | NV04_PGRAPH_PATTERN_SHAPE, | ||
195 | NV03_PGRAPH_MONO_COLOR0, | ||
196 | NV04_PGRAPH_ROP3, | ||
197 | NV04_PGRAPH_CHROMA, | ||
198 | NV04_PGRAPH_BETA_AND, | ||
199 | NV04_PGRAPH_BETA_PREMULT, | ||
200 | 0x00400e70, | ||
201 | 0x00400e74, | ||
202 | 0x00400e78, | ||
203 | 0x00400e7c, | ||
204 | 0x00400e80, | ||
205 | 0x00400e84, | ||
206 | 0x00400e88, | ||
207 | 0x00400e8c, | ||
208 | 0x00400ea0, | ||
209 | 0x00400ea4, | ||
210 | 0x00400ea8, | ||
211 | 0x00400e90, | ||
212 | 0x00400e94, | ||
213 | 0x00400e98, | ||
214 | 0x00400e9c, | ||
215 | NV10_PGRAPH_WINDOWCLIP_HORIZONTAL, /* 8 values from 0x400f00-0x400f1c */ | ||
216 | NV10_PGRAPH_WINDOWCLIP_VERTICAL, /* 8 values from 0x400f20-0x400f3c */ | ||
217 | 0x00400f04, | ||
218 | 0x00400f24, | ||
219 | 0x00400f08, | ||
220 | 0x00400f28, | ||
221 | 0x00400f0c, | ||
222 | 0x00400f2c, | ||
223 | 0x00400f10, | ||
224 | 0x00400f30, | ||
225 | 0x00400f14, | ||
226 | 0x00400f34, | ||
227 | 0x00400f18, | ||
228 | 0x00400f38, | ||
229 | 0x00400f1c, | ||
230 | 0x00400f3c, | ||
231 | NV10_PGRAPH_XFMODE0, | ||
232 | NV10_PGRAPH_XFMODE1, | ||
233 | NV10_PGRAPH_GLOBALSTATE0, | ||
234 | NV10_PGRAPH_GLOBALSTATE1, | ||
235 | NV04_PGRAPH_STORED_FMT, | ||
236 | NV04_PGRAPH_SOURCE_COLOR, | ||
237 | NV03_PGRAPH_ABS_X_RAM, /* 32 values from 0x400400 to 0x40047c */ | ||
238 | NV03_PGRAPH_ABS_Y_RAM, /* 32 values from 0x400480 to 0x4004fc */ | ||
239 | 0x00400404, | ||
240 | 0x00400484, | ||
241 | 0x00400408, | ||
242 | 0x00400488, | ||
243 | 0x0040040c, | ||
244 | 0x0040048c, | ||
245 | 0x00400410, | ||
246 | 0x00400490, | ||
247 | 0x00400414, | ||
248 | 0x00400494, | ||
249 | 0x00400418, | ||
250 | 0x00400498, | ||
251 | 0x0040041c, | ||
252 | 0x0040049c, | ||
253 | 0x00400420, | ||
254 | 0x004004a0, | ||
255 | 0x00400424, | ||
256 | 0x004004a4, | ||
257 | 0x00400428, | ||
258 | 0x004004a8, | ||
259 | 0x0040042c, | ||
260 | 0x004004ac, | ||
261 | 0x00400430, | ||
262 | 0x004004b0, | ||
263 | 0x00400434, | ||
264 | 0x004004b4, | ||
265 | 0x00400438, | ||
266 | 0x004004b8, | ||
267 | 0x0040043c, | ||
268 | 0x004004bc, | ||
269 | 0x00400440, | ||
270 | 0x004004c0, | ||
271 | 0x00400444, | ||
272 | 0x004004c4, | ||
273 | 0x00400448, | ||
274 | 0x004004c8, | ||
275 | 0x0040044c, | ||
276 | 0x004004cc, | ||
277 | 0x00400450, | ||
278 | 0x004004d0, | ||
279 | 0x00400454, | ||
280 | 0x004004d4, | ||
281 | 0x00400458, | ||
282 | 0x004004d8, | ||
283 | 0x0040045c, | ||
284 | 0x004004dc, | ||
285 | 0x00400460, | ||
286 | 0x004004e0, | ||
287 | 0x00400464, | ||
288 | 0x004004e4, | ||
289 | 0x00400468, | ||
290 | 0x004004e8, | ||
291 | 0x0040046c, | ||
292 | 0x004004ec, | ||
293 | 0x00400470, | ||
294 | 0x004004f0, | ||
295 | 0x00400474, | ||
296 | 0x004004f4, | ||
297 | 0x00400478, | ||
298 | 0x004004f8, | ||
299 | 0x0040047c, | ||
300 | 0x004004fc, | ||
301 | NV03_PGRAPH_ABS_UCLIP_XMIN, | ||
302 | NV03_PGRAPH_ABS_UCLIP_XMAX, | ||
303 | NV03_PGRAPH_ABS_UCLIP_YMIN, | ||
304 | NV03_PGRAPH_ABS_UCLIP_YMAX, | ||
305 | 0x00400550, | ||
306 | 0x00400558, | ||
307 | 0x00400554, | ||
308 | 0x0040055c, | ||
309 | NV03_PGRAPH_ABS_UCLIPA_XMIN, | ||
310 | NV03_PGRAPH_ABS_UCLIPA_XMAX, | ||
311 | NV03_PGRAPH_ABS_UCLIPA_YMIN, | ||
312 | NV03_PGRAPH_ABS_UCLIPA_YMAX, | ||
313 | NV03_PGRAPH_ABS_ICLIP_XMAX, | ||
314 | NV03_PGRAPH_ABS_ICLIP_YMAX, | ||
315 | NV03_PGRAPH_XY_LOGIC_MISC0, | ||
316 | NV03_PGRAPH_XY_LOGIC_MISC1, | ||
317 | NV03_PGRAPH_XY_LOGIC_MISC2, | ||
318 | NV03_PGRAPH_XY_LOGIC_MISC3, | ||
319 | NV03_PGRAPH_CLIPX_0, | ||
320 | NV03_PGRAPH_CLIPX_1, | ||
321 | NV03_PGRAPH_CLIPY_0, | ||
322 | NV03_PGRAPH_CLIPY_1, | ||
323 | NV10_PGRAPH_COMBINER0_IN_ALPHA, | ||
324 | NV10_PGRAPH_COMBINER1_IN_ALPHA, | ||
325 | NV10_PGRAPH_COMBINER0_IN_RGB, | ||
326 | NV10_PGRAPH_COMBINER1_IN_RGB, | ||
327 | NV10_PGRAPH_COMBINER_COLOR0, | ||
328 | NV10_PGRAPH_COMBINER_COLOR1, | ||
329 | NV10_PGRAPH_COMBINER0_OUT_ALPHA, | ||
330 | NV10_PGRAPH_COMBINER1_OUT_ALPHA, | ||
331 | NV10_PGRAPH_COMBINER0_OUT_RGB, | ||
332 | NV10_PGRAPH_COMBINER1_OUT_RGB, | ||
333 | NV10_PGRAPH_COMBINER_FINAL0, | ||
334 | NV10_PGRAPH_COMBINER_FINAL1, | ||
335 | 0x00400e00, | ||
336 | 0x00400e04, | ||
337 | 0x00400e08, | ||
338 | 0x00400e0c, | ||
339 | 0x00400e10, | ||
340 | 0x00400e14, | ||
341 | 0x00400e18, | ||
342 | 0x00400e1c, | ||
343 | 0x00400e20, | ||
344 | 0x00400e24, | ||
345 | 0x00400e28, | ||
346 | 0x00400e2c, | ||
347 | 0x00400e30, | ||
348 | 0x00400e34, | ||
349 | 0x00400e38, | ||
350 | 0x00400e3c, | ||
351 | NV04_PGRAPH_PASSTHRU_0, | ||
352 | NV04_PGRAPH_PASSTHRU_1, | ||
353 | NV04_PGRAPH_PASSTHRU_2, | ||
354 | NV10_PGRAPH_DIMX_TEXTURE, | ||
355 | NV10_PGRAPH_WDIMX_TEXTURE, | ||
356 | NV10_PGRAPH_DVD_COLORFMT, | ||
357 | NV10_PGRAPH_SCALED_FORMAT, | ||
358 | NV04_PGRAPH_MISC24_0, | ||
359 | NV04_PGRAPH_MISC24_1, | ||
360 | NV04_PGRAPH_MISC24_2, | ||
361 | NV03_PGRAPH_X_MISC, | ||
362 | NV03_PGRAPH_Y_MISC, | ||
363 | NV04_PGRAPH_VALID1, | ||
364 | NV04_PGRAPH_VALID2, | ||
365 | }; | ||
366 | |||
367 | static int nv17_graph_ctx_regs[] = { | ||
368 | NV10_PGRAPH_DEBUG_4, | ||
369 | 0x004006b0, | ||
370 | 0x00400eac, | ||
371 | 0x00400eb0, | ||
372 | 0x00400eb4, | ||
373 | 0x00400eb8, | ||
374 | 0x00400ebc, | ||
375 | 0x00400ec0, | ||
376 | 0x00400ec4, | ||
377 | 0x00400ec8, | ||
378 | 0x00400ecc, | ||
379 | 0x00400ed0, | ||
380 | 0x00400ed4, | ||
381 | 0x00400ed8, | ||
382 | 0x00400edc, | ||
383 | 0x00400ee0, | ||
384 | 0x00400a00, | ||
385 | 0x00400a04, | ||
386 | }; | ||
387 | |||
388 | struct graph_state { | ||
389 | int nv10[ARRAY_SIZE(nv10_graph_ctx_regs)]; | ||
390 | int nv17[ARRAY_SIZE(nv17_graph_ctx_regs)]; | ||
391 | struct pipe_state pipe_state; | ||
392 | }; | ||
393 | |||
394 | static void nv10_graph_save_pipe(struct nouveau_channel *chan) | ||
395 | { | ||
396 | struct drm_device *dev = chan->dev; | ||
397 | struct graph_state *pgraph_ctx = chan->pgraph_ctx; | ||
398 | struct pipe_state *fifo_pipe_state = &pgraph_ctx->pipe_state; | ||
399 | int i; | ||
400 | #define PIPE_SAVE(addr) \ | ||
401 | do { \ | ||
402 | nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, addr); \ | ||
403 | for (i = 0; i < ARRAY_SIZE(fifo_pipe_state->pipe_##addr); i++) \ | ||
404 | fifo_pipe_state->pipe_##addr[i] = nv_rd32(dev, NV10_PGRAPH_PIPE_DATA); \ | ||
405 | } while (0) | ||
406 | |||
407 | PIPE_SAVE(0x4400); | ||
408 | PIPE_SAVE(0x0200); | ||
409 | PIPE_SAVE(0x6400); | ||
410 | PIPE_SAVE(0x6800); | ||
411 | PIPE_SAVE(0x6c00); | ||
412 | PIPE_SAVE(0x7000); | ||
413 | PIPE_SAVE(0x7400); | ||
414 | PIPE_SAVE(0x7800); | ||
415 | PIPE_SAVE(0x0040); | ||
416 | PIPE_SAVE(0x0000); | ||
417 | |||
418 | #undef PIPE_SAVE | ||
419 | } | ||
420 | |||
421 | static void nv10_graph_load_pipe(struct nouveau_channel *chan) | ||
422 | { | ||
423 | struct drm_device *dev = chan->dev; | ||
424 | struct graph_state *pgraph_ctx = chan->pgraph_ctx; | ||
425 | struct pipe_state *fifo_pipe_state = &pgraph_ctx->pipe_state; | ||
426 | int i; | ||
427 | uint32_t xfmode0, xfmode1; | ||
428 | #define PIPE_RESTORE(addr) \ | ||
429 | do { \ | ||
430 | nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, addr); \ | ||
431 | for (i = 0; i < ARRAY_SIZE(fifo_pipe_state->pipe_##addr); i++) \ | ||
432 | nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, fifo_pipe_state->pipe_##addr[i]); \ | ||
433 | } while (0) | ||
434 | |||
435 | |||
436 | nouveau_wait_for_idle(dev); | ||
437 | /* XXX check haiku comments */ | ||
438 | xfmode0 = nv_rd32(dev, NV10_PGRAPH_XFMODE0); | ||
439 | xfmode1 = nv_rd32(dev, NV10_PGRAPH_XFMODE1); | ||
440 | nv_wr32(dev, NV10_PGRAPH_XFMODE0, 0x10000000); | ||
441 | nv_wr32(dev, NV10_PGRAPH_XFMODE1, 0x00000000); | ||
442 | nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, 0x000064c0); | ||
443 | for (i = 0; i < 4; i++) | ||
444 | nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x3f800000); | ||
445 | for (i = 0; i < 4; i++) | ||
446 | nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x00000000); | ||
447 | |||
448 | nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, 0x00006ab0); | ||
449 | for (i = 0; i < 3; i++) | ||
450 | nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x3f800000); | ||
451 | |||
452 | nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, 0x00006a80); | ||
453 | for (i = 0; i < 3; i++) | ||
454 | nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x00000000); | ||
455 | |||
456 | nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, 0x00000040); | ||
457 | nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x00000008); | ||
458 | |||
459 | |||
460 | PIPE_RESTORE(0x0200); | ||
461 | nouveau_wait_for_idle(dev); | ||
462 | |||
463 | /* restore XFMODE */ | ||
464 | nv_wr32(dev, NV10_PGRAPH_XFMODE0, xfmode0); | ||
465 | nv_wr32(dev, NV10_PGRAPH_XFMODE1, xfmode1); | ||
466 | PIPE_RESTORE(0x6400); | ||
467 | PIPE_RESTORE(0x6800); | ||
468 | PIPE_RESTORE(0x6c00); | ||
469 | PIPE_RESTORE(0x7000); | ||
470 | PIPE_RESTORE(0x7400); | ||
471 | PIPE_RESTORE(0x7800); | ||
472 | PIPE_RESTORE(0x4400); | ||
473 | PIPE_RESTORE(0x0000); | ||
474 | PIPE_RESTORE(0x0040); | ||
475 | nouveau_wait_for_idle(dev); | ||
476 | |||
477 | #undef PIPE_RESTORE | ||
478 | } | ||
479 | |||
480 | static void nv10_graph_create_pipe(struct nouveau_channel *chan) | ||
481 | { | ||
482 | struct drm_device *dev = chan->dev; | ||
483 | struct graph_state *pgraph_ctx = chan->pgraph_ctx; | ||
484 | struct pipe_state *fifo_pipe_state = &pgraph_ctx->pipe_state; | ||
485 | uint32_t *fifo_pipe_state_addr; | ||
486 | int i; | ||
487 | #define PIPE_INIT(addr) \ | ||
488 | do { \ | ||
489 | fifo_pipe_state_addr = fifo_pipe_state->pipe_##addr; \ | ||
490 | } while (0) | ||
491 | #define PIPE_INIT_END(addr) \ | ||
492 | do { \ | ||
493 | uint32_t *__end_addr = fifo_pipe_state->pipe_##addr + \ | ||
494 | ARRAY_SIZE(fifo_pipe_state->pipe_##addr); \ | ||
495 | if (fifo_pipe_state_addr != __end_addr) \ | ||
496 | NV_ERROR(dev, "incomplete pipe init for 0x%x : %p/%p\n", \ | ||
497 | addr, fifo_pipe_state_addr, __end_addr); \ | ||
498 | } while (0) | ||
499 | #define NV_WRITE_PIPE_INIT(value) *(fifo_pipe_state_addr++) = value | ||
500 | |||
501 | PIPE_INIT(0x0200); | ||
502 | for (i = 0; i < 48; i++) | ||
503 | NV_WRITE_PIPE_INIT(0x00000000); | ||
504 | PIPE_INIT_END(0x0200); | ||
505 | |||
506 | PIPE_INIT(0x6400); | ||
507 | for (i = 0; i < 211; i++) | ||
508 | NV_WRITE_PIPE_INIT(0x00000000); | ||
509 | NV_WRITE_PIPE_INIT(0x3f800000); | ||
510 | NV_WRITE_PIPE_INIT(0x40000000); | ||
511 | NV_WRITE_PIPE_INIT(0x40000000); | ||
512 | NV_WRITE_PIPE_INIT(0x40000000); | ||
513 | NV_WRITE_PIPE_INIT(0x40000000); | ||
514 | NV_WRITE_PIPE_INIT(0x00000000); | ||
515 | NV_WRITE_PIPE_INIT(0x00000000); | ||
516 | NV_WRITE_PIPE_INIT(0x3f800000); | ||
517 | NV_WRITE_PIPE_INIT(0x00000000); | ||
518 | NV_WRITE_PIPE_INIT(0x3f000000); | ||
519 | NV_WRITE_PIPE_INIT(0x3f000000); | ||
520 | NV_WRITE_PIPE_INIT(0x00000000); | ||
521 | NV_WRITE_PIPE_INIT(0x00000000); | ||
522 | NV_WRITE_PIPE_INIT(0x00000000); | ||
523 | NV_WRITE_PIPE_INIT(0x00000000); | ||
524 | NV_WRITE_PIPE_INIT(0x3f800000); | ||
525 | NV_WRITE_PIPE_INIT(0x00000000); | ||
526 | NV_WRITE_PIPE_INIT(0x00000000); | ||
527 | NV_WRITE_PIPE_INIT(0x00000000); | ||
528 | NV_WRITE_PIPE_INIT(0x00000000); | ||
529 | NV_WRITE_PIPE_INIT(0x00000000); | ||
530 | NV_WRITE_PIPE_INIT(0x3f800000); | ||
531 | NV_WRITE_PIPE_INIT(0x3f800000); | ||
532 | NV_WRITE_PIPE_INIT(0x3f800000); | ||
533 | NV_WRITE_PIPE_INIT(0x3f800000); | ||
534 | PIPE_INIT_END(0x6400); | ||
535 | |||
536 | PIPE_INIT(0x6800); | ||
537 | for (i = 0; i < 162; i++) | ||
538 | NV_WRITE_PIPE_INIT(0x00000000); | ||
539 | NV_WRITE_PIPE_INIT(0x3f800000); | ||
540 | for (i = 0; i < 25; i++) | ||
541 | NV_WRITE_PIPE_INIT(0x00000000); | ||
542 | PIPE_INIT_END(0x6800); | ||
543 | |||
544 | PIPE_INIT(0x6c00); | ||
545 | NV_WRITE_PIPE_INIT(0x00000000); | ||
546 | NV_WRITE_PIPE_INIT(0x00000000); | ||
547 | NV_WRITE_PIPE_INIT(0x00000000); | ||
548 | NV_WRITE_PIPE_INIT(0x00000000); | ||
549 | NV_WRITE_PIPE_INIT(0xbf800000); | ||
550 | NV_WRITE_PIPE_INIT(0x00000000); | ||
551 | NV_WRITE_PIPE_INIT(0x00000000); | ||
552 | NV_WRITE_PIPE_INIT(0x00000000); | ||
553 | NV_WRITE_PIPE_INIT(0x00000000); | ||
554 | NV_WRITE_PIPE_INIT(0x00000000); | ||
555 | NV_WRITE_PIPE_INIT(0x00000000); | ||
556 | NV_WRITE_PIPE_INIT(0x00000000); | ||
557 | PIPE_INIT_END(0x6c00); | ||
558 | |||
559 | PIPE_INIT(0x7000); | ||
560 | NV_WRITE_PIPE_INIT(0x00000000); | ||
561 | NV_WRITE_PIPE_INIT(0x00000000); | ||
562 | NV_WRITE_PIPE_INIT(0x00000000); | ||
563 | NV_WRITE_PIPE_INIT(0x00000000); | ||
564 | NV_WRITE_PIPE_INIT(0x00000000); | ||
565 | NV_WRITE_PIPE_INIT(0x00000000); | ||
566 | NV_WRITE_PIPE_INIT(0x00000000); | ||
567 | NV_WRITE_PIPE_INIT(0x00000000); | ||
568 | NV_WRITE_PIPE_INIT(0x00000000); | ||
569 | NV_WRITE_PIPE_INIT(0x00000000); | ||
570 | NV_WRITE_PIPE_INIT(0x00000000); | ||
571 | NV_WRITE_PIPE_INIT(0x00000000); | ||
572 | NV_WRITE_PIPE_INIT(0x7149f2ca); | ||
573 | NV_WRITE_PIPE_INIT(0x00000000); | ||
574 | NV_WRITE_PIPE_INIT(0x00000000); | ||
575 | NV_WRITE_PIPE_INIT(0x00000000); | ||
576 | NV_WRITE_PIPE_INIT(0x7149f2ca); | ||
577 | NV_WRITE_PIPE_INIT(0x00000000); | ||
578 | NV_WRITE_PIPE_INIT(0x00000000); | ||
579 | NV_WRITE_PIPE_INIT(0x00000000); | ||
580 | NV_WRITE_PIPE_INIT(0x7149f2ca); | ||
581 | NV_WRITE_PIPE_INIT(0x00000000); | ||
582 | NV_WRITE_PIPE_INIT(0x00000000); | ||
583 | NV_WRITE_PIPE_INIT(0x00000000); | ||
584 | NV_WRITE_PIPE_INIT(0x7149f2ca); | ||
585 | NV_WRITE_PIPE_INIT(0x00000000); | ||
586 | NV_WRITE_PIPE_INIT(0x00000000); | ||
587 | NV_WRITE_PIPE_INIT(0x00000000); | ||
588 | NV_WRITE_PIPE_INIT(0x7149f2ca); | ||
589 | NV_WRITE_PIPE_INIT(0x00000000); | ||
590 | NV_WRITE_PIPE_INIT(0x00000000); | ||
591 | NV_WRITE_PIPE_INIT(0x00000000); | ||
592 | NV_WRITE_PIPE_INIT(0x7149f2ca); | ||
593 | NV_WRITE_PIPE_INIT(0x00000000); | ||
594 | NV_WRITE_PIPE_INIT(0x00000000); | ||
595 | NV_WRITE_PIPE_INIT(0x00000000); | ||
596 | NV_WRITE_PIPE_INIT(0x7149f2ca); | ||
597 | NV_WRITE_PIPE_INIT(0x00000000); | ||
598 | NV_WRITE_PIPE_INIT(0x00000000); | ||
599 | NV_WRITE_PIPE_INIT(0x00000000); | ||
600 | NV_WRITE_PIPE_INIT(0x7149f2ca); | ||
601 | for (i = 0; i < 35; i++) | ||
602 | NV_WRITE_PIPE_INIT(0x00000000); | ||
603 | PIPE_INIT_END(0x7000); | ||
604 | |||
605 | PIPE_INIT(0x7400); | ||
606 | for (i = 0; i < 48; i++) | ||
607 | NV_WRITE_PIPE_INIT(0x00000000); | ||
608 | PIPE_INIT_END(0x7400); | ||
609 | |||
610 | PIPE_INIT(0x7800); | ||
611 | for (i = 0; i < 48; i++) | ||
612 | NV_WRITE_PIPE_INIT(0x00000000); | ||
613 | PIPE_INIT_END(0x7800); | ||
614 | |||
615 | PIPE_INIT(0x4400); | ||
616 | for (i = 0; i < 32; i++) | ||
617 | NV_WRITE_PIPE_INIT(0x00000000); | ||
618 | PIPE_INIT_END(0x4400); | ||
619 | |||
620 | PIPE_INIT(0x0000); | ||
621 | for (i = 0; i < 16; i++) | ||
622 | NV_WRITE_PIPE_INIT(0x00000000); | ||
623 | PIPE_INIT_END(0x0000); | ||
624 | |||
625 | PIPE_INIT(0x0040); | ||
626 | for (i = 0; i < 4; i++) | ||
627 | NV_WRITE_PIPE_INIT(0x00000000); | ||
628 | PIPE_INIT_END(0x0040); | ||
629 | |||
630 | #undef PIPE_INIT | ||
631 | #undef PIPE_INIT_END | ||
632 | #undef NV_WRITE_PIPE_INIT | ||
633 | } | ||
634 | |||
635 | static int nv10_graph_ctx_regs_find_offset(struct drm_device *dev, int reg) | ||
636 | { | ||
637 | int i; | ||
638 | for (i = 0; i < ARRAY_SIZE(nv10_graph_ctx_regs); i++) { | ||
639 | if (nv10_graph_ctx_regs[i] == reg) | ||
640 | return i; | ||
641 | } | ||
642 | NV_ERROR(dev, "unknow offset nv10_ctx_regs %d\n", reg); | ||
643 | return -1; | ||
644 | } | ||
645 | |||
646 | static int nv17_graph_ctx_regs_find_offset(struct drm_device *dev, int reg) | ||
647 | { | ||
648 | int i; | ||
649 | for (i = 0; i < ARRAY_SIZE(nv17_graph_ctx_regs); i++) { | ||
650 | if (nv17_graph_ctx_regs[i] == reg) | ||
651 | return i; | ||
652 | } | ||
653 | NV_ERROR(dev, "unknow offset nv17_ctx_regs %d\n", reg); | ||
654 | return -1; | ||
655 | } | ||
656 | |||
657 | int nv10_graph_load_context(struct nouveau_channel *chan) | ||
658 | { | ||
659 | struct drm_device *dev = chan->dev; | ||
660 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
661 | struct graph_state *pgraph_ctx = chan->pgraph_ctx; | ||
662 | uint32_t tmp; | ||
663 | int i; | ||
664 | |||
665 | for (i = 0; i < ARRAY_SIZE(nv10_graph_ctx_regs); i++) | ||
666 | nv_wr32(dev, nv10_graph_ctx_regs[i], pgraph_ctx->nv10[i]); | ||
667 | if (dev_priv->chipset >= 0x17) { | ||
668 | for (i = 0; i < ARRAY_SIZE(nv17_graph_ctx_regs); i++) | ||
669 | nv_wr32(dev, nv17_graph_ctx_regs[i], | ||
670 | pgraph_ctx->nv17[i]); | ||
671 | } | ||
672 | |||
673 | nv10_graph_load_pipe(chan); | ||
674 | |||
675 | nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10010100); | ||
676 | tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER); | ||
677 | nv_wr32(dev, NV10_PGRAPH_CTX_USER, (tmp & 0xffffff) | chan->id << 24); | ||
678 | tmp = nv_rd32(dev, NV10_PGRAPH_FFINTFC_ST2); | ||
679 | nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2, tmp & 0xcfffffff); | ||
680 | return 0; | ||
681 | } | ||
682 | |||
683 | int | ||
684 | nv10_graph_unload_context(struct drm_device *dev) | ||
685 | { | ||
686 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
687 | struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; | ||
688 | struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; | ||
689 | struct nouveau_channel *chan; | ||
690 | struct graph_state *ctx; | ||
691 | uint32_t tmp; | ||
692 | int i; | ||
693 | |||
694 | chan = pgraph->channel(dev); | ||
695 | if (!chan) | ||
696 | return 0; | ||
697 | ctx = chan->pgraph_ctx; | ||
698 | |||
699 | for (i = 0; i < ARRAY_SIZE(nv10_graph_ctx_regs); i++) | ||
700 | ctx->nv10[i] = nv_rd32(dev, nv10_graph_ctx_regs[i]); | ||
701 | |||
702 | if (dev_priv->chipset >= 0x17) { | ||
703 | for (i = 0; i < ARRAY_SIZE(nv17_graph_ctx_regs); i++) | ||
704 | ctx->nv17[i] = nv_rd32(dev, nv17_graph_ctx_regs[i]); | ||
705 | } | ||
706 | |||
707 | nv10_graph_save_pipe(chan); | ||
708 | |||
709 | nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000000); | ||
710 | tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER) & 0x00ffffff; | ||
711 | tmp |= (pfifo->channels - 1) << 24; | ||
712 | nv_wr32(dev, NV10_PGRAPH_CTX_USER, tmp); | ||
713 | return 0; | ||
714 | } | ||
715 | |||
716 | void | ||
717 | nv10_graph_context_switch(struct drm_device *dev) | ||
718 | { | ||
719 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
720 | struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; | ||
721 | struct nouveau_channel *chan = NULL; | ||
722 | int chid; | ||
723 | |||
724 | pgraph->fifo_access(dev, false); | ||
725 | nouveau_wait_for_idle(dev); | ||
726 | |||
727 | /* If previous context is valid, we need to save it */ | ||
728 | nv10_graph_unload_context(dev); | ||
729 | |||
730 | /* Load context for next channel */ | ||
731 | chid = (nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR) >> 20) & 0x1f; | ||
732 | chan = dev_priv->fifos[chid]; | ||
733 | if (chan) | ||
734 | nv10_graph_load_context(chan); | ||
735 | |||
736 | pgraph->fifo_access(dev, true); | ||
737 | } | ||
738 | |||
739 | #define NV_WRITE_CTX(reg, val) do { \ | ||
740 | int offset = nv10_graph_ctx_regs_find_offset(dev, reg); \ | ||
741 | if (offset > 0) \ | ||
742 | pgraph_ctx->nv10[offset] = val; \ | ||
743 | } while (0) | ||
744 | |||
745 | #define NV17_WRITE_CTX(reg, val) do { \ | ||
746 | int offset = nv17_graph_ctx_regs_find_offset(dev, reg); \ | ||
747 | if (offset > 0) \ | ||
748 | pgraph_ctx->nv17[offset] = val; \ | ||
749 | } while (0) | ||
750 | |||
751 | struct nouveau_channel * | ||
752 | nv10_graph_channel(struct drm_device *dev) | ||
753 | { | ||
754 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
755 | int chid = dev_priv->engine.fifo.channels; | ||
756 | |||
757 | if (nv_rd32(dev, NV10_PGRAPH_CTX_CONTROL) & 0x00010000) | ||
758 | chid = nv_rd32(dev, NV10_PGRAPH_CTX_USER) >> 24; | ||
759 | |||
760 | if (chid >= dev_priv->engine.fifo.channels) | ||
761 | return NULL; | ||
762 | |||
763 | return dev_priv->fifos[chid]; | ||
764 | } | ||
765 | |||
766 | int nv10_graph_create_context(struct nouveau_channel *chan) | ||
767 | { | ||
768 | struct drm_device *dev = chan->dev; | ||
769 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
770 | struct graph_state *pgraph_ctx; | ||
771 | |||
772 | NV_DEBUG(dev, "nv10_graph_context_create %d\n", chan->id); | ||
773 | |||
774 | chan->pgraph_ctx = pgraph_ctx = kzalloc(sizeof(*pgraph_ctx), | ||
775 | GFP_KERNEL); | ||
776 | if (pgraph_ctx == NULL) | ||
777 | return -ENOMEM; | ||
778 | |||
779 | |||
780 | NV_WRITE_CTX(0x00400e88, 0x08000000); | ||
781 | NV_WRITE_CTX(0x00400e9c, 0x4b7fffff); | ||
782 | NV_WRITE_CTX(NV03_PGRAPH_XY_LOGIC_MISC0, 0x0001ffff); | ||
783 | NV_WRITE_CTX(0x00400e10, 0x00001000); | ||
784 | NV_WRITE_CTX(0x00400e14, 0x00001000); | ||
785 | NV_WRITE_CTX(0x00400e30, 0x00080008); | ||
786 | NV_WRITE_CTX(0x00400e34, 0x00080008); | ||
787 | if (dev_priv->chipset >= 0x17) { | ||
788 | /* is it really needed ??? */ | ||
789 | NV17_WRITE_CTX(NV10_PGRAPH_DEBUG_4, | ||
790 | nv_rd32(dev, NV10_PGRAPH_DEBUG_4)); | ||
791 | NV17_WRITE_CTX(0x004006b0, nv_rd32(dev, 0x004006b0)); | ||
792 | NV17_WRITE_CTX(0x00400eac, 0x0fff0000); | ||
793 | NV17_WRITE_CTX(0x00400eb0, 0x0fff0000); | ||
794 | NV17_WRITE_CTX(0x00400ec0, 0x00000080); | ||
795 | NV17_WRITE_CTX(0x00400ed0, 0x00000080); | ||
796 | } | ||
797 | NV_WRITE_CTX(NV10_PGRAPH_CTX_USER, chan->id << 24); | ||
798 | |||
799 | nv10_graph_create_pipe(chan); | ||
800 | return 0; | ||
801 | } | ||
802 | |||
803 | void nv10_graph_destroy_context(struct nouveau_channel *chan) | ||
804 | { | ||
805 | struct graph_state *pgraph_ctx = chan->pgraph_ctx; | ||
806 | |||
807 | kfree(pgraph_ctx); | ||
808 | chan->pgraph_ctx = NULL; | ||
809 | } | ||
810 | |||
811 | int nv10_graph_init(struct drm_device *dev) | ||
812 | { | ||
813 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
814 | uint32_t tmp; | ||
815 | int i; | ||
816 | |||
817 | nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) & | ||
818 | ~NV_PMC_ENABLE_PGRAPH); | ||
819 | nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) | | ||
820 | NV_PMC_ENABLE_PGRAPH); | ||
821 | |||
822 | nv_wr32(dev, NV03_PGRAPH_INTR , 0xFFFFFFFF); | ||
823 | nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF); | ||
824 | |||
825 | nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0xFFFFFFFF); | ||
826 | nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x00000000); | ||
827 | nv_wr32(dev, NV04_PGRAPH_DEBUG_1, 0x00118700); | ||
828 | /* nv_wr32(dev, NV04_PGRAPH_DEBUG_2, 0x24E00810); */ /* 0x25f92ad9 */ | ||
829 | nv_wr32(dev, NV04_PGRAPH_DEBUG_2, 0x25f92ad9); | ||
830 | nv_wr32(dev, NV04_PGRAPH_DEBUG_3, 0x55DE0830 | | ||
831 | (1<<29) | | ||
832 | (1<<31)); | ||
833 | if (dev_priv->chipset >= 0x17) { | ||
834 | nv_wr32(dev, NV10_PGRAPH_DEBUG_4, 0x1f000000); | ||
835 | nv_wr32(dev, 0x004006b0, 0x40000020); | ||
836 | } else | ||
837 | nv_wr32(dev, NV10_PGRAPH_DEBUG_4, 0x00000000); | ||
838 | |||
839 | /* copy tile info from PFB */ | ||
840 | for (i = 0; i < NV10_PFB_TILE__SIZE; i++) { | ||
841 | nv_wr32(dev, NV10_PGRAPH_TILE(i), | ||
842 | nv_rd32(dev, NV10_PFB_TILE(i))); | ||
843 | nv_wr32(dev, NV10_PGRAPH_TLIMIT(i), | ||
844 | nv_rd32(dev, NV10_PFB_TLIMIT(i))); | ||
845 | nv_wr32(dev, NV10_PGRAPH_TSIZE(i), | ||
846 | nv_rd32(dev, NV10_PFB_TSIZE(i))); | ||
847 | nv_wr32(dev, NV10_PGRAPH_TSTATUS(i), | ||
848 | nv_rd32(dev, NV10_PFB_TSTATUS(i))); | ||
849 | } | ||
850 | |||
851 | nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH1, 0x00000000); | ||
852 | nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH2, 0x00000000); | ||
853 | nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH3, 0x00000000); | ||
854 | nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH4, 0x00000000); | ||
855 | nv_wr32(dev, NV10_PGRAPH_STATE , 0xFFFFFFFF); | ||
856 | |||
857 | tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER) & 0x00ffffff; | ||
858 | tmp |= (dev_priv->engine.fifo.channels - 1) << 24; | ||
859 | nv_wr32(dev, NV10_PGRAPH_CTX_USER, tmp); | ||
860 | nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000100); | ||
861 | nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2, 0x08000000); | ||
862 | |||
863 | return 0; | ||
864 | } | ||
865 | |||
866 | void nv10_graph_takedown(struct drm_device *dev) | ||
867 | { | ||
868 | } | ||
869 | |||
870 | struct nouveau_pgraph_object_class nv10_graph_grclass[] = { | ||
871 | { 0x0030, false, NULL }, /* null */ | ||
872 | { 0x0039, false, NULL }, /* m2mf */ | ||
873 | { 0x004a, false, NULL }, /* gdirect */ | ||
874 | { 0x005f, false, NULL }, /* imageblit */ | ||
875 | { 0x009f, false, NULL }, /* imageblit (nv12) */ | ||
876 | { 0x008a, false, NULL }, /* ifc */ | ||
877 | { 0x0089, false, NULL }, /* sifm */ | ||
878 | { 0x0062, false, NULL }, /* surf2d */ | ||
879 | { 0x0043, false, NULL }, /* rop */ | ||
880 | { 0x0012, false, NULL }, /* beta1 */ | ||
881 | { 0x0072, false, NULL }, /* beta4 */ | ||
882 | { 0x0019, false, NULL }, /* cliprect */ | ||
883 | { 0x0044, false, NULL }, /* pattern */ | ||
884 | { 0x0052, false, NULL }, /* swzsurf */ | ||
885 | { 0x0093, false, NULL }, /* surf3d */ | ||
886 | { 0x0094, false, NULL }, /* tex_tri */ | ||
887 | { 0x0095, false, NULL }, /* multitex_tri */ | ||
888 | { 0x0056, false, NULL }, /* celcius (nv10) */ | ||
889 | { 0x0096, false, NULL }, /* celcius (nv11) */ | ||
890 | { 0x0099, false, NULL }, /* celcius (nv17) */ | ||
891 | {} | ||
892 | }; | ||
diff --git a/drivers/gpu/drm/nouveau/nv17_gpio.c b/drivers/gpu/drm/nouveau/nv17_gpio.c new file mode 100644 index 000000000000..2e58c331e9b7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv17_gpio.c | |||
@@ -0,0 +1,92 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009 Francisco Jerez. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include "drmP.h" | ||
28 | #include "nouveau_drv.h" | ||
29 | #include "nouveau_hw.h" | ||
30 | |||
31 | static bool | ||
32 | get_gpio_location(struct dcb_gpio_entry *ent, uint32_t *reg, uint32_t *shift, | ||
33 | uint32_t *mask) | ||
34 | { | ||
35 | if (ent->line < 2) { | ||
36 | *reg = NV_PCRTC_GPIO; | ||
37 | *shift = ent->line * 16; | ||
38 | *mask = 0x11; | ||
39 | |||
40 | } else if (ent->line < 10) { | ||
41 | *reg = NV_PCRTC_GPIO_EXT; | ||
42 | *shift = (ent->line - 2) * 4; | ||
43 | *mask = 0x3; | ||
44 | |||
45 | } else if (ent->line < 14) { | ||
46 | *reg = NV_PCRTC_850; | ||
47 | *shift = (ent->line - 10) * 4; | ||
48 | *mask = 0x3; | ||
49 | |||
50 | } else { | ||
51 | return false; | ||
52 | } | ||
53 | |||
54 | return true; | ||
55 | } | ||
56 | |||
57 | int | ||
58 | nv17_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag) | ||
59 | { | ||
60 | struct dcb_gpio_entry *ent = nouveau_bios_gpio_entry(dev, tag); | ||
61 | uint32_t reg, shift, mask, value; | ||
62 | |||
63 | if (!ent) | ||
64 | return -ENODEV; | ||
65 | |||
66 | if (!get_gpio_location(ent, ®, &shift, &mask)) | ||
67 | return -ENODEV; | ||
68 | |||
69 | value = NVReadCRTC(dev, 0, reg) >> shift; | ||
70 | |||
71 | return (ent->invert ? 1 : 0) ^ (value & 1); | ||
72 | } | ||
73 | |||
74 | int | ||
75 | nv17_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state) | ||
76 | { | ||
77 | struct dcb_gpio_entry *ent = nouveau_bios_gpio_entry(dev, tag); | ||
78 | uint32_t reg, shift, mask, value; | ||
79 | |||
80 | if (!ent) | ||
81 | return -ENODEV; | ||
82 | |||
83 | if (!get_gpio_location(ent, ®, &shift, &mask)) | ||
84 | return -ENODEV; | ||
85 | |||
86 | value = ((ent->invert ? 1 : 0) ^ (state ? 1 : 0)) << shift; | ||
87 | mask = ~(mask << shift); | ||
88 | |||
89 | NVWriteCRTC(dev, 0, reg, value | (NVReadCRTC(dev, 0, reg) & mask)); | ||
90 | |||
91 | return 0; | ||
92 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nv17_tv.c b/drivers/gpu/drm/nouveau/nv17_tv.c new file mode 100644 index 000000000000..46cfd9c60478 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv17_tv.c | |||
@@ -0,0 +1,681 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009 Francisco Jerez. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include "drmP.h" | ||
28 | #include "drm_crtc_helper.h" | ||
29 | #include "nouveau_drv.h" | ||
30 | #include "nouveau_encoder.h" | ||
31 | #include "nouveau_connector.h" | ||
32 | #include "nouveau_crtc.h" | ||
33 | #include "nouveau_hw.h" | ||
34 | #include "nv17_tv.h" | ||
35 | |||
36 | enum drm_connector_status nv17_tv_detect(struct drm_encoder *encoder, | ||
37 | struct drm_connector *connector, | ||
38 | uint32_t pin_mask) | ||
39 | { | ||
40 | struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); | ||
41 | |||
42 | tv_enc->pin_mask = pin_mask >> 28 & 0xe; | ||
43 | |||
44 | switch (tv_enc->pin_mask) { | ||
45 | case 0x2: | ||
46 | case 0x4: | ||
47 | tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Composite; | ||
48 | break; | ||
49 | case 0xc: | ||
50 | tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_SVIDEO; | ||
51 | break; | ||
52 | case 0xe: | ||
53 | if (nouveau_encoder(encoder)->dcb->tvconf.has_component_output) | ||
54 | tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Component; | ||
55 | else | ||
56 | tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_SCART; | ||
57 | break; | ||
58 | default: | ||
59 | tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Unknown; | ||
60 | break; | ||
61 | } | ||
62 | |||
63 | drm_connector_property_set_value(connector, | ||
64 | encoder->dev->mode_config.tv_subconnector_property, | ||
65 | tv_enc->subconnector); | ||
66 | |||
67 | return tv_enc->subconnector ? connector_status_connected : | ||
68 | connector_status_disconnected; | ||
69 | } | ||
70 | |||
71 | static const struct { | ||
72 | int hdisplay; | ||
73 | int vdisplay; | ||
74 | } modes[] = { | ||
75 | { 640, 400 }, | ||
76 | { 640, 480 }, | ||
77 | { 720, 480 }, | ||
78 | { 720, 576 }, | ||
79 | { 800, 600 }, | ||
80 | { 1024, 768 }, | ||
81 | { 1280, 720 }, | ||
82 | { 1280, 1024 }, | ||
83 | { 1920, 1080 } | ||
84 | }; | ||
85 | |||
86 | static int nv17_tv_get_modes(struct drm_encoder *encoder, | ||
87 | struct drm_connector *connector) | ||
88 | { | ||
89 | struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); | ||
90 | struct drm_display_mode *mode; | ||
91 | struct drm_display_mode *output_mode; | ||
92 | int n = 0; | ||
93 | int i; | ||
94 | |||
95 | if (tv_norm->kind != CTV_ENC_MODE) { | ||
96 | struct drm_display_mode *tv_mode; | ||
97 | |||
98 | for (tv_mode = nv17_tv_modes; tv_mode->hdisplay; tv_mode++) { | ||
99 | mode = drm_mode_duplicate(encoder->dev, tv_mode); | ||
100 | |||
101 | mode->clock = tv_norm->tv_enc_mode.vrefresh * | ||
102 | mode->htotal / 1000 * | ||
103 | mode->vtotal / 1000; | ||
104 | |||
105 | if (mode->flags & DRM_MODE_FLAG_DBLSCAN) | ||
106 | mode->clock *= 2; | ||
107 | |||
108 | if (mode->hdisplay == tv_norm->tv_enc_mode.hdisplay && | ||
109 | mode->vdisplay == tv_norm->tv_enc_mode.vdisplay) | ||
110 | mode->type |= DRM_MODE_TYPE_PREFERRED; | ||
111 | |||
112 | drm_mode_probed_add(connector, mode); | ||
113 | n++; | ||
114 | } | ||
115 | return n; | ||
116 | } | ||
117 | |||
118 | /* tv_norm->kind == CTV_ENC_MODE */ | ||
119 | output_mode = &tv_norm->ctv_enc_mode.mode; | ||
120 | for (i = 0; i < ARRAY_SIZE(modes); i++) { | ||
121 | if (modes[i].hdisplay > output_mode->hdisplay || | ||
122 | modes[i].vdisplay > output_mode->vdisplay) | ||
123 | continue; | ||
124 | |||
125 | if (modes[i].hdisplay == output_mode->hdisplay && | ||
126 | modes[i].vdisplay == output_mode->vdisplay) { | ||
127 | mode = drm_mode_duplicate(encoder->dev, output_mode); | ||
128 | mode->type |= DRM_MODE_TYPE_PREFERRED; | ||
129 | } else { | ||
130 | mode = drm_cvt_mode(encoder->dev, modes[i].hdisplay, | ||
131 | modes[i].vdisplay, 60, false, | ||
132 | output_mode->flags & DRM_MODE_FLAG_INTERLACE, | ||
133 | false); | ||
134 | } | ||
135 | |||
136 | /* CVT modes are sometimes unsuitable... */ | ||
137 | if (output_mode->hdisplay <= 720 | ||
138 | || output_mode->hdisplay >= 1920) { | ||
139 | mode->htotal = output_mode->htotal; | ||
140 | mode->hsync_start = (mode->hdisplay + (mode->htotal | ||
141 | - mode->hdisplay) * 9 / 10) & ~7; | ||
142 | mode->hsync_end = mode->hsync_start + 8; | ||
143 | } | ||
144 | if (output_mode->vdisplay >= 1024) { | ||
145 | mode->vtotal = output_mode->vtotal; | ||
146 | mode->vsync_start = output_mode->vsync_start; | ||
147 | mode->vsync_end = output_mode->vsync_end; | ||
148 | } | ||
149 | |||
150 | mode->type |= DRM_MODE_TYPE_DRIVER; | ||
151 | drm_mode_probed_add(connector, mode); | ||
152 | n++; | ||
153 | } | ||
154 | return n; | ||
155 | } | ||
156 | |||
157 | static int nv17_tv_mode_valid(struct drm_encoder *encoder, | ||
158 | struct drm_display_mode *mode) | ||
159 | { | ||
160 | struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); | ||
161 | |||
162 | if (tv_norm->kind == CTV_ENC_MODE) { | ||
163 | struct drm_display_mode *output_mode = | ||
164 | &tv_norm->ctv_enc_mode.mode; | ||
165 | |||
166 | if (mode->clock > 400000) | ||
167 | return MODE_CLOCK_HIGH; | ||
168 | |||
169 | if (mode->hdisplay > output_mode->hdisplay || | ||
170 | mode->vdisplay > output_mode->vdisplay) | ||
171 | return MODE_BAD; | ||
172 | |||
173 | if ((mode->flags & DRM_MODE_FLAG_INTERLACE) != | ||
174 | (output_mode->flags & DRM_MODE_FLAG_INTERLACE)) | ||
175 | return MODE_NO_INTERLACE; | ||
176 | |||
177 | if (mode->flags & DRM_MODE_FLAG_DBLSCAN) | ||
178 | return MODE_NO_DBLESCAN; | ||
179 | |||
180 | } else { | ||
181 | const int vsync_tolerance = 600; | ||
182 | |||
183 | if (mode->clock > 70000) | ||
184 | return MODE_CLOCK_HIGH; | ||
185 | |||
186 | if (abs(drm_mode_vrefresh(mode) * 1000 - | ||
187 | tv_norm->tv_enc_mode.vrefresh) > vsync_tolerance) | ||
188 | return MODE_VSYNC; | ||
189 | |||
190 | /* The encoder takes care of the actual interlacing */ | ||
191 | if (mode->flags & DRM_MODE_FLAG_INTERLACE) | ||
192 | return MODE_NO_INTERLACE; | ||
193 | } | ||
194 | |||
195 | return MODE_OK; | ||
196 | } | ||
197 | |||
198 | static bool nv17_tv_mode_fixup(struct drm_encoder *encoder, | ||
199 | struct drm_display_mode *mode, | ||
200 | struct drm_display_mode *adjusted_mode) | ||
201 | { | ||
202 | struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); | ||
203 | |||
204 | if (tv_norm->kind == CTV_ENC_MODE) | ||
205 | adjusted_mode->clock = tv_norm->ctv_enc_mode.mode.clock; | ||
206 | else | ||
207 | adjusted_mode->clock = 90000; | ||
208 | |||
209 | return true; | ||
210 | } | ||
211 | |||
212 | static void nv17_tv_dpms(struct drm_encoder *encoder, int mode) | ||
213 | { | ||
214 | struct drm_device *dev = encoder->dev; | ||
215 | struct nv17_tv_state *regs = &to_tv_enc(encoder)->state; | ||
216 | struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); | ||
217 | |||
218 | if (nouveau_encoder(encoder)->last_dpms == mode) | ||
219 | return; | ||
220 | nouveau_encoder(encoder)->last_dpms = mode; | ||
221 | |||
222 | NV_TRACE(dev, "Setting dpms mode %d on TV encoder (output %d)\n", | ||
223 | mode, nouveau_encoder(encoder)->dcb->index); | ||
224 | |||
225 | regs->ptv_200 &= ~1; | ||
226 | |||
227 | if (tv_norm->kind == CTV_ENC_MODE) { | ||
228 | nv04_dfp_update_fp_control(encoder, mode); | ||
229 | |||
230 | } else { | ||
231 | nv04_dfp_update_fp_control(encoder, DRM_MODE_DPMS_OFF); | ||
232 | |||
233 | if (mode == DRM_MODE_DPMS_ON) | ||
234 | regs->ptv_200 |= 1; | ||
235 | } | ||
236 | |||
237 | nv_load_ptv(dev, regs, 200); | ||
238 | |||
239 | nv17_gpio_set(dev, DCB_GPIO_TVDAC1, mode == DRM_MODE_DPMS_ON); | ||
240 | nv17_gpio_set(dev, DCB_GPIO_TVDAC0, mode == DRM_MODE_DPMS_ON); | ||
241 | |||
242 | nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON); | ||
243 | } | ||
244 | |||
245 | static void nv17_tv_prepare(struct drm_encoder *encoder) | ||
246 | { | ||
247 | struct drm_device *dev = encoder->dev; | ||
248 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
249 | struct drm_encoder_helper_funcs *helper = encoder->helper_private; | ||
250 | struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); | ||
251 | int head = nouveau_crtc(encoder->crtc)->index; | ||
252 | uint8_t *cr_lcd = &dev_priv->mode_reg.crtc_reg[head].CRTC[ | ||
253 | NV_CIO_CRE_LCD__INDEX]; | ||
254 | uint32_t dacclk_off = NV_PRAMDAC_DACCLK + | ||
255 | nv04_dac_output_offset(encoder); | ||
256 | uint32_t dacclk; | ||
257 | |||
258 | helper->dpms(encoder, DRM_MODE_DPMS_OFF); | ||
259 | |||
260 | nv04_dfp_disable(dev, head); | ||
261 | |||
262 | /* Unbind any FP encoders from this head if we need the FP | ||
263 | * stuff enabled. */ | ||
264 | if (tv_norm->kind == CTV_ENC_MODE) { | ||
265 | struct drm_encoder *enc; | ||
266 | |||
267 | list_for_each_entry(enc, &dev->mode_config.encoder_list, head) { | ||
268 | struct dcb_entry *dcb = nouveau_encoder(enc)->dcb; | ||
269 | |||
270 | if ((dcb->type == OUTPUT_TMDS || | ||
271 | dcb->type == OUTPUT_LVDS) && | ||
272 | !enc->crtc && | ||
273 | nv04_dfp_get_bound_head(dev, dcb) == head) { | ||
274 | nv04_dfp_bind_head(dev, dcb, head ^ 1, | ||
275 | dev_priv->VBIOS.fp.dual_link); | ||
276 | } | ||
277 | } | ||
278 | |||
279 | } | ||
280 | |||
281 | /* Some NV4x have unknown values (0x3f, 0x50, 0x54, 0x6b, 0x79, 0x7f) | ||
282 | * at LCD__INDEX which we don't alter | ||
283 | */ | ||
284 | if (!(*cr_lcd & 0x44)) { | ||
285 | if (tv_norm->kind == CTV_ENC_MODE) | ||
286 | *cr_lcd = 0x1 | (head ? 0x0 : 0x8); | ||
287 | else | ||
288 | *cr_lcd = 0; | ||
289 | } | ||
290 | |||
291 | /* Set the DACCLK register */ | ||
292 | dacclk = (NVReadRAMDAC(dev, 0, dacclk_off) & ~0x30) | 0x1; | ||
293 | |||
294 | if (dev_priv->card_type == NV_40) | ||
295 | dacclk |= 0x1a << 16; | ||
296 | |||
297 | if (tv_norm->kind == CTV_ENC_MODE) { | ||
298 | dacclk |= 0x20; | ||
299 | |||
300 | if (head) | ||
301 | dacclk |= 0x100; | ||
302 | else | ||
303 | dacclk &= ~0x100; | ||
304 | |||
305 | } else { | ||
306 | dacclk |= 0x10; | ||
307 | |||
308 | } | ||
309 | |||
310 | NVWriteRAMDAC(dev, 0, dacclk_off, dacclk); | ||
311 | } | ||
312 | |||
313 | static void nv17_tv_mode_set(struct drm_encoder *encoder, | ||
314 | struct drm_display_mode *drm_mode, | ||
315 | struct drm_display_mode *adjusted_mode) | ||
316 | { | ||
317 | struct drm_device *dev = encoder->dev; | ||
318 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
319 | int head = nouveau_crtc(encoder->crtc)->index; | ||
320 | struct nv04_crtc_reg *regs = &dev_priv->mode_reg.crtc_reg[head]; | ||
321 | struct nv17_tv_state *tv_regs = &to_tv_enc(encoder)->state; | ||
322 | struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); | ||
323 | int i; | ||
324 | |||
325 | regs->CRTC[NV_CIO_CRE_53] = 0x40; /* FP_HTIMING */ | ||
326 | regs->CRTC[NV_CIO_CRE_54] = 0; /* FP_VTIMING */ | ||
327 | regs->ramdac_630 = 0x2; /* turn off green mode (tv test pattern?) */ | ||
328 | regs->tv_setup = 1; | ||
329 | regs->ramdac_8c0 = 0x0; | ||
330 | |||
331 | if (tv_norm->kind == TV_ENC_MODE) { | ||
332 | tv_regs->ptv_200 = 0x13111100; | ||
333 | if (head) | ||
334 | tv_regs->ptv_200 |= 0x10; | ||
335 | |||
336 | tv_regs->ptv_20c = 0x808010; | ||
337 | tv_regs->ptv_304 = 0x2d00000; | ||
338 | tv_regs->ptv_600 = 0x0; | ||
339 | tv_regs->ptv_60c = 0x0; | ||
340 | tv_regs->ptv_610 = 0x1e00000; | ||
341 | |||
342 | if (tv_norm->tv_enc_mode.vdisplay == 576) { | ||
343 | tv_regs->ptv_508 = 0x1200000; | ||
344 | tv_regs->ptv_614 = 0x33; | ||
345 | |||
346 | } else if (tv_norm->tv_enc_mode.vdisplay == 480) { | ||
347 | tv_regs->ptv_508 = 0xf00000; | ||
348 | tv_regs->ptv_614 = 0x13; | ||
349 | } | ||
350 | |||
351 | if (dev_priv->card_type >= NV_30) { | ||
352 | tv_regs->ptv_500 = 0xe8e0; | ||
353 | tv_regs->ptv_504 = 0x1710; | ||
354 | tv_regs->ptv_604 = 0x0; | ||
355 | tv_regs->ptv_608 = 0x0; | ||
356 | } else { | ||
357 | if (tv_norm->tv_enc_mode.vdisplay == 576) { | ||
358 | tv_regs->ptv_604 = 0x20; | ||
359 | tv_regs->ptv_608 = 0x10; | ||
360 | tv_regs->ptv_500 = 0x19710; | ||
361 | tv_regs->ptv_504 = 0x68f0; | ||
362 | |||
363 | } else if (tv_norm->tv_enc_mode.vdisplay == 480) { | ||
364 | tv_regs->ptv_604 = 0x10; | ||
365 | tv_regs->ptv_608 = 0x20; | ||
366 | tv_regs->ptv_500 = 0x4b90; | ||
367 | tv_regs->ptv_504 = 0x1b480; | ||
368 | } | ||
369 | } | ||
370 | |||
371 | for (i = 0; i < 0x40; i++) | ||
372 | tv_regs->tv_enc[i] = tv_norm->tv_enc_mode.tv_enc[i]; | ||
373 | |||
374 | } else { | ||
375 | struct drm_display_mode *output_mode = | ||
376 | &tv_norm->ctv_enc_mode.mode; | ||
377 | |||
378 | /* The registers in PRAMDAC+0xc00 control some timings and CSC | ||
379 | * parameters for the CTV encoder (It's only used for "HD" TV | ||
380 | * modes, I don't think I have enough working to guess what | ||
381 | * they exactly mean...), it's probably connected at the | ||
382 | * output of the FP encoder, but it also needs the analog | ||
383 | * encoder in its OR enabled and routed to the head it's | ||
384 | * using. It's enabled with the DACCLK register, bits [5:4]. | ||
385 | */ | ||
386 | for (i = 0; i < 38; i++) | ||
387 | regs->ctv_regs[i] = tv_norm->ctv_enc_mode.ctv_regs[i]; | ||
388 | |||
389 | regs->fp_horiz_regs[FP_DISPLAY_END] = output_mode->hdisplay - 1; | ||
390 | regs->fp_horiz_regs[FP_TOTAL] = output_mode->htotal - 1; | ||
391 | regs->fp_horiz_regs[FP_SYNC_START] = | ||
392 | output_mode->hsync_start - 1; | ||
393 | regs->fp_horiz_regs[FP_SYNC_END] = output_mode->hsync_end - 1; | ||
394 | regs->fp_horiz_regs[FP_CRTC] = output_mode->hdisplay + | ||
395 | max((output_mode->hdisplay-600)/40 - 1, 1); | ||
396 | |||
397 | regs->fp_vert_regs[FP_DISPLAY_END] = output_mode->vdisplay - 1; | ||
398 | regs->fp_vert_regs[FP_TOTAL] = output_mode->vtotal - 1; | ||
399 | regs->fp_vert_regs[FP_SYNC_START] = | ||
400 | output_mode->vsync_start - 1; | ||
401 | regs->fp_vert_regs[FP_SYNC_END] = output_mode->vsync_end - 1; | ||
402 | regs->fp_vert_regs[FP_CRTC] = output_mode->vdisplay - 1; | ||
403 | |||
404 | regs->fp_control = NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS | | ||
405 | NV_PRAMDAC_FP_TG_CONTROL_READ_PROG | | ||
406 | NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12; | ||
407 | |||
408 | if (output_mode->flags & DRM_MODE_FLAG_PVSYNC) | ||
409 | regs->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS; | ||
410 | if (output_mode->flags & DRM_MODE_FLAG_PHSYNC) | ||
411 | regs->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS; | ||
412 | |||
413 | regs->fp_debug_0 = NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND | | ||
414 | NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND | | ||
415 | NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR | | ||
416 | NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR | | ||
417 | NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED | | ||
418 | NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE | | ||
419 | NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE; | ||
420 | |||
421 | regs->fp_debug_2 = 0; | ||
422 | |||
423 | regs->fp_margin_color = 0x801080; | ||
424 | |||
425 | } | ||
426 | } | ||
427 | |||
428 | static void nv17_tv_commit(struct drm_encoder *encoder) | ||
429 | { | ||
430 | struct drm_device *dev = encoder->dev; | ||
431 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
432 | struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); | ||
433 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
434 | struct drm_encoder_helper_funcs *helper = encoder->helper_private; | ||
435 | |||
436 | if (get_tv_norm(encoder)->kind == TV_ENC_MODE) { | ||
437 | nv17_tv_update_rescaler(encoder); | ||
438 | nv17_tv_update_properties(encoder); | ||
439 | } else { | ||
440 | nv17_ctv_update_rescaler(encoder); | ||
441 | } | ||
442 | |||
443 | nv17_tv_state_load(dev, &to_tv_enc(encoder)->state); | ||
444 | |||
445 | /* This could use refinement for flatpanels, but it should work */ | ||
446 | if (dev_priv->chipset < 0x44) | ||
447 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + | ||
448 | nv04_dac_output_offset(encoder), | ||
449 | 0xf0000000); | ||
450 | else | ||
451 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + | ||
452 | nv04_dac_output_offset(encoder), | ||
453 | 0x00100000); | ||
454 | |||
455 | helper->dpms(encoder, DRM_MODE_DPMS_ON); | ||
456 | |||
457 | NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n", | ||
458 | drm_get_connector_name( | ||
459 | &nouveau_encoder_connector_get(nv_encoder)->base), | ||
460 | nv_crtc->index, '@' + ffs(nv_encoder->dcb->or)); | ||
461 | } | ||
462 | |||
463 | static void nv17_tv_save(struct drm_encoder *encoder) | ||
464 | { | ||
465 | struct drm_device *dev = encoder->dev; | ||
466 | struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); | ||
467 | |||
468 | nouveau_encoder(encoder)->restore.output = | ||
469 | NVReadRAMDAC(dev, 0, | ||
470 | NV_PRAMDAC_DACCLK + | ||
471 | nv04_dac_output_offset(encoder)); | ||
472 | |||
473 | nv17_tv_state_save(dev, &tv_enc->saved_state); | ||
474 | |||
475 | tv_enc->state.ptv_200 = tv_enc->saved_state.ptv_200; | ||
476 | } | ||
477 | |||
478 | static void nv17_tv_restore(struct drm_encoder *encoder) | ||
479 | { | ||
480 | struct drm_device *dev = encoder->dev; | ||
481 | |||
482 | NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + | ||
483 | nv04_dac_output_offset(encoder), | ||
484 | nouveau_encoder(encoder)->restore.output); | ||
485 | |||
486 | nv17_tv_state_load(dev, &to_tv_enc(encoder)->saved_state); | ||
487 | } | ||
488 | |||
489 | static int nv17_tv_create_resources(struct drm_encoder *encoder, | ||
490 | struct drm_connector *connector) | ||
491 | { | ||
492 | struct drm_device *dev = encoder->dev; | ||
493 | struct drm_mode_config *conf = &dev->mode_config; | ||
494 | struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); | ||
495 | struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb; | ||
496 | int num_tv_norms = dcb->tvconf.has_component_output ? NUM_TV_NORMS : | ||
497 | NUM_LD_TV_NORMS; | ||
498 | int i; | ||
499 | |||
500 | if (nouveau_tv_norm) { | ||
501 | for (i = 0; i < num_tv_norms; i++) { | ||
502 | if (!strcmp(nv17_tv_norm_names[i], nouveau_tv_norm)) { | ||
503 | tv_enc->tv_norm = i; | ||
504 | break; | ||
505 | } | ||
506 | } | ||
507 | |||
508 | if (i == num_tv_norms) | ||
509 | NV_WARN(dev, "Invalid TV norm setting \"%s\"\n", | ||
510 | nouveau_tv_norm); | ||
511 | } | ||
512 | |||
513 | drm_mode_create_tv_properties(dev, num_tv_norms, nv17_tv_norm_names); | ||
514 | |||
515 | drm_connector_attach_property(connector, | ||
516 | conf->tv_select_subconnector_property, | ||
517 | tv_enc->select_subconnector); | ||
518 | drm_connector_attach_property(connector, | ||
519 | conf->tv_subconnector_property, | ||
520 | tv_enc->subconnector); | ||
521 | drm_connector_attach_property(connector, | ||
522 | conf->tv_mode_property, | ||
523 | tv_enc->tv_norm); | ||
524 | drm_connector_attach_property(connector, | ||
525 | conf->tv_flicker_reduction_property, | ||
526 | tv_enc->flicker); | ||
527 | drm_connector_attach_property(connector, | ||
528 | conf->tv_saturation_property, | ||
529 | tv_enc->saturation); | ||
530 | drm_connector_attach_property(connector, | ||
531 | conf->tv_hue_property, | ||
532 | tv_enc->hue); | ||
533 | drm_connector_attach_property(connector, | ||
534 | conf->tv_overscan_property, | ||
535 | tv_enc->overscan); | ||
536 | |||
537 | return 0; | ||
538 | } | ||
539 | |||
540 | static int nv17_tv_set_property(struct drm_encoder *encoder, | ||
541 | struct drm_connector *connector, | ||
542 | struct drm_property *property, | ||
543 | uint64_t val) | ||
544 | { | ||
545 | struct drm_mode_config *conf = &encoder->dev->mode_config; | ||
546 | struct drm_crtc *crtc = encoder->crtc; | ||
547 | struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); | ||
548 | struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); | ||
549 | bool modes_changed = false; | ||
550 | |||
551 | if (property == conf->tv_overscan_property) { | ||
552 | tv_enc->overscan = val; | ||
553 | if (encoder->crtc) { | ||
554 | if (tv_norm->kind == CTV_ENC_MODE) | ||
555 | nv17_ctv_update_rescaler(encoder); | ||
556 | else | ||
557 | nv17_tv_update_rescaler(encoder); | ||
558 | } | ||
559 | |||
560 | } else if (property == conf->tv_saturation_property) { | ||
561 | if (tv_norm->kind != TV_ENC_MODE) | ||
562 | return -EINVAL; | ||
563 | |||
564 | tv_enc->saturation = val; | ||
565 | nv17_tv_update_properties(encoder); | ||
566 | |||
567 | } else if (property == conf->tv_hue_property) { | ||
568 | if (tv_norm->kind != TV_ENC_MODE) | ||
569 | return -EINVAL; | ||
570 | |||
571 | tv_enc->hue = val; | ||
572 | nv17_tv_update_properties(encoder); | ||
573 | |||
574 | } else if (property == conf->tv_flicker_reduction_property) { | ||
575 | if (tv_norm->kind != TV_ENC_MODE) | ||
576 | return -EINVAL; | ||
577 | |||
578 | tv_enc->flicker = val; | ||
579 | if (encoder->crtc) | ||
580 | nv17_tv_update_rescaler(encoder); | ||
581 | |||
582 | } else if (property == conf->tv_mode_property) { | ||
583 | if (connector->dpms != DRM_MODE_DPMS_OFF) | ||
584 | return -EINVAL; | ||
585 | |||
586 | tv_enc->tv_norm = val; | ||
587 | |||
588 | modes_changed = true; | ||
589 | |||
590 | } else if (property == conf->tv_select_subconnector_property) { | ||
591 | if (tv_norm->kind != TV_ENC_MODE) | ||
592 | return -EINVAL; | ||
593 | |||
594 | tv_enc->select_subconnector = val; | ||
595 | nv17_tv_update_properties(encoder); | ||
596 | |||
597 | } else { | ||
598 | return -EINVAL; | ||
599 | } | ||
600 | |||
601 | if (modes_changed) { | ||
602 | drm_helper_probe_single_connector_modes(connector, 0, 0); | ||
603 | |||
604 | /* Disable the crtc to ensure a full modeset is | ||
605 | * performed whenever it's turned on again. */ | ||
606 | if (crtc) { | ||
607 | struct drm_mode_set modeset = { | ||
608 | .crtc = crtc, | ||
609 | }; | ||
610 | |||
611 | crtc->funcs->set_config(&modeset); | ||
612 | } | ||
613 | } | ||
614 | |||
615 | return 0; | ||
616 | } | ||
617 | |||
618 | static void nv17_tv_destroy(struct drm_encoder *encoder) | ||
619 | { | ||
620 | struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); | ||
621 | |||
622 | NV_DEBUG(encoder->dev, "\n"); | ||
623 | |||
624 | drm_encoder_cleanup(encoder); | ||
625 | kfree(tv_enc); | ||
626 | } | ||
627 | |||
628 | static struct drm_encoder_helper_funcs nv17_tv_helper_funcs = { | ||
629 | .dpms = nv17_tv_dpms, | ||
630 | .save = nv17_tv_save, | ||
631 | .restore = nv17_tv_restore, | ||
632 | .mode_fixup = nv17_tv_mode_fixup, | ||
633 | .prepare = nv17_tv_prepare, | ||
634 | .commit = nv17_tv_commit, | ||
635 | .mode_set = nv17_tv_mode_set, | ||
636 | .detect = nv17_dac_detect, | ||
637 | }; | ||
638 | |||
639 | static struct drm_encoder_slave_funcs nv17_tv_slave_funcs = { | ||
640 | .get_modes = nv17_tv_get_modes, | ||
641 | .mode_valid = nv17_tv_mode_valid, | ||
642 | .create_resources = nv17_tv_create_resources, | ||
643 | .set_property = nv17_tv_set_property, | ||
644 | }; | ||
645 | |||
646 | static struct drm_encoder_funcs nv17_tv_funcs = { | ||
647 | .destroy = nv17_tv_destroy, | ||
648 | }; | ||
649 | |||
650 | int nv17_tv_create(struct drm_device *dev, struct dcb_entry *entry) | ||
651 | { | ||
652 | struct drm_encoder *encoder; | ||
653 | struct nv17_tv_encoder *tv_enc = NULL; | ||
654 | |||
655 | tv_enc = kzalloc(sizeof(*tv_enc), GFP_KERNEL); | ||
656 | if (!tv_enc) | ||
657 | return -ENOMEM; | ||
658 | |||
659 | tv_enc->overscan = 50; | ||
660 | tv_enc->flicker = 50; | ||
661 | tv_enc->saturation = 50; | ||
662 | tv_enc->hue = 0; | ||
663 | tv_enc->tv_norm = TV_NORM_PAL; | ||
664 | tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Unknown; | ||
665 | tv_enc->select_subconnector = DRM_MODE_SUBCONNECTOR_Automatic; | ||
666 | tv_enc->pin_mask = 0; | ||
667 | |||
668 | encoder = to_drm_encoder(&tv_enc->base); | ||
669 | |||
670 | tv_enc->base.dcb = entry; | ||
671 | tv_enc->base.or = ffs(entry->or) - 1; | ||
672 | |||
673 | drm_encoder_init(dev, encoder, &nv17_tv_funcs, DRM_MODE_ENCODER_TVDAC); | ||
674 | drm_encoder_helper_add(encoder, &nv17_tv_helper_funcs); | ||
675 | to_encoder_slave(encoder)->slave_funcs = &nv17_tv_slave_funcs; | ||
676 | |||
677 | encoder->possible_crtcs = entry->heads; | ||
678 | encoder->possible_clones = 0; | ||
679 | |||
680 | return 0; | ||
681 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nv17_tv.h b/drivers/gpu/drm/nouveau/nv17_tv.h new file mode 100644 index 000000000000..c00977cedabd --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv17_tv.h | |||
@@ -0,0 +1,156 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009 Francisco Jerez. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #ifndef __NV17_TV_H__ | ||
28 | #define __NV17_TV_H__ | ||
29 | |||
30 | struct nv17_tv_state { | ||
31 | uint8_t tv_enc[0x40]; | ||
32 | |||
33 | uint32_t hfilter[4][7]; | ||
34 | uint32_t hfilter2[4][7]; | ||
35 | uint32_t vfilter[4][7]; | ||
36 | |||
37 | uint32_t ptv_200; | ||
38 | uint32_t ptv_204; | ||
39 | uint32_t ptv_208; | ||
40 | uint32_t ptv_20c; | ||
41 | uint32_t ptv_304; | ||
42 | uint32_t ptv_500; | ||
43 | uint32_t ptv_504; | ||
44 | uint32_t ptv_508; | ||
45 | uint32_t ptv_600; | ||
46 | uint32_t ptv_604; | ||
47 | uint32_t ptv_608; | ||
48 | uint32_t ptv_60c; | ||
49 | uint32_t ptv_610; | ||
50 | uint32_t ptv_614; | ||
51 | }; | ||
52 | |||
53 | enum nv17_tv_norm{ | ||
54 | TV_NORM_PAL, | ||
55 | TV_NORM_PAL_M, | ||
56 | TV_NORM_PAL_N, | ||
57 | TV_NORM_PAL_NC, | ||
58 | TV_NORM_NTSC_M, | ||
59 | TV_NORM_NTSC_J, | ||
60 | NUM_LD_TV_NORMS, | ||
61 | TV_NORM_HD480I = NUM_LD_TV_NORMS, | ||
62 | TV_NORM_HD480P, | ||
63 | TV_NORM_HD576I, | ||
64 | TV_NORM_HD576P, | ||
65 | TV_NORM_HD720P, | ||
66 | TV_NORM_HD1080I, | ||
67 | NUM_TV_NORMS | ||
68 | }; | ||
69 | |||
70 | struct nv17_tv_encoder { | ||
71 | struct nouveau_encoder base; | ||
72 | |||
73 | struct nv17_tv_state state; | ||
74 | struct nv17_tv_state saved_state; | ||
75 | |||
76 | int overscan; | ||
77 | int flicker; | ||
78 | int saturation; | ||
79 | int hue; | ||
80 | enum nv17_tv_norm tv_norm; | ||
81 | int subconnector; | ||
82 | int select_subconnector; | ||
83 | uint32_t pin_mask; | ||
84 | }; | ||
85 | #define to_tv_enc(x) container_of(nouveau_encoder(x), \ | ||
86 | struct nv17_tv_encoder, base) | ||
87 | |||
88 | extern char *nv17_tv_norm_names[NUM_TV_NORMS]; | ||
89 | |||
90 | extern struct nv17_tv_norm_params { | ||
91 | enum { | ||
92 | TV_ENC_MODE, | ||
93 | CTV_ENC_MODE, | ||
94 | } kind; | ||
95 | |||
96 | union { | ||
97 | struct { | ||
98 | int hdisplay; | ||
99 | int vdisplay; | ||
100 | int vrefresh; /* mHz */ | ||
101 | |||
102 | uint8_t tv_enc[0x40]; | ||
103 | } tv_enc_mode; | ||
104 | |||
105 | struct { | ||
106 | struct drm_display_mode mode; | ||
107 | |||
108 | uint32_t ctv_regs[38]; | ||
109 | } ctv_enc_mode; | ||
110 | }; | ||
111 | |||
112 | } nv17_tv_norms[NUM_TV_NORMS]; | ||
113 | #define get_tv_norm(enc) (&nv17_tv_norms[to_tv_enc(enc)->tv_norm]) | ||
114 | |||
115 | extern struct drm_display_mode nv17_tv_modes[]; | ||
116 | |||
117 | static inline int interpolate(int y0, int y1, int y2, int x) | ||
118 | { | ||
119 | return y1 + (x < 50 ? y1 - y0 : y2 - y1) * (x - 50) / 50; | ||
120 | } | ||
121 | |||
122 | void nv17_tv_state_save(struct drm_device *dev, struct nv17_tv_state *state); | ||
123 | void nv17_tv_state_load(struct drm_device *dev, struct nv17_tv_state *state); | ||
124 | void nv17_tv_update_properties(struct drm_encoder *encoder); | ||
125 | void nv17_tv_update_rescaler(struct drm_encoder *encoder); | ||
126 | void nv17_ctv_update_rescaler(struct drm_encoder *encoder); | ||
127 | |||
128 | /* TV hardware access functions */ | ||
129 | |||
130 | static inline void nv_write_ptv(struct drm_device *dev, uint32_t reg, uint32_t val) | ||
131 | { | ||
132 | nv_wr32(dev, reg, val); | ||
133 | } | ||
134 | |||
135 | static inline uint32_t nv_read_ptv(struct drm_device *dev, uint32_t reg) | ||
136 | { | ||
137 | return nv_rd32(dev, reg); | ||
138 | } | ||
139 | |||
140 | static inline void nv_write_tv_enc(struct drm_device *dev, uint8_t reg, uint8_t val) | ||
141 | { | ||
142 | nv_write_ptv(dev, NV_PTV_TV_INDEX, reg); | ||
143 | nv_write_ptv(dev, NV_PTV_TV_DATA, val); | ||
144 | } | ||
145 | |||
146 | static inline uint8_t nv_read_tv_enc(struct drm_device *dev, uint8_t reg) | ||
147 | { | ||
148 | nv_write_ptv(dev, NV_PTV_TV_INDEX, reg); | ||
149 | return nv_read_ptv(dev, NV_PTV_TV_DATA); | ||
150 | } | ||
151 | |||
152 | #define nv_load_ptv(dev, state, reg) nv_write_ptv(dev, NV_PTV_OFFSET + 0x##reg, state->ptv_##reg) | ||
153 | #define nv_save_ptv(dev, state, reg) state->ptv_##reg = nv_read_ptv(dev, NV_PTV_OFFSET + 0x##reg) | ||
154 | #define nv_load_tv_enc(dev, state, reg) nv_write_tv_enc(dev, 0x##reg, state->tv_enc[0x##reg]) | ||
155 | |||
156 | #endif | ||
diff --git a/drivers/gpu/drm/nouveau/nv17_tv_modes.c b/drivers/gpu/drm/nouveau/nv17_tv_modes.c new file mode 100644 index 000000000000..d64683d97e0d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv17_tv_modes.c | |||
@@ -0,0 +1,583 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009 Francisco Jerez. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include "drmP.h" | ||
28 | #include "drm_crtc_helper.h" | ||
29 | #include "nouveau_drv.h" | ||
30 | #include "nouveau_encoder.h" | ||
31 | #include "nouveau_crtc.h" | ||
32 | #include "nouveau_hw.h" | ||
33 | #include "nv17_tv.h" | ||
34 | |||
35 | char *nv17_tv_norm_names[NUM_TV_NORMS] = { | ||
36 | [TV_NORM_PAL] = "PAL", | ||
37 | [TV_NORM_PAL_M] = "PAL-M", | ||
38 | [TV_NORM_PAL_N] = "PAL-N", | ||
39 | [TV_NORM_PAL_NC] = "PAL-Nc", | ||
40 | [TV_NORM_NTSC_M] = "NTSC-M", | ||
41 | [TV_NORM_NTSC_J] = "NTSC-J", | ||
42 | [TV_NORM_HD480I] = "hd480i", | ||
43 | [TV_NORM_HD480P] = "hd480p", | ||
44 | [TV_NORM_HD576I] = "hd576i", | ||
45 | [TV_NORM_HD576P] = "hd576p", | ||
46 | [TV_NORM_HD720P] = "hd720p", | ||
47 | [TV_NORM_HD1080I] = "hd1080i" | ||
48 | }; | ||
49 | |||
50 | /* TV standard specific parameters */ | ||
51 | |||
52 | struct nv17_tv_norm_params nv17_tv_norms[NUM_TV_NORMS] = { | ||
53 | [TV_NORM_PAL] = { TV_ENC_MODE, { | ||
54 | .tv_enc_mode = { 720, 576, 50000, { | ||
55 | 0x2a, 0x9, 0x8a, 0xcb, 0x0, 0x0, 0xb, 0x18, | ||
56 | 0x7e, 0x40, 0x8a, 0x35, 0x27, 0x0, 0x34, 0x3, | ||
57 | 0x3e, 0x3, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c, | ||
58 | 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x3, | ||
59 | 0xd3, 0x4, 0xd4, 0x1, 0x2, 0x0, 0xa, 0x5, | ||
60 | 0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0, | ||
61 | 0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b, | ||
62 | 0xbd, 0x15, 0x5, 0x15, 0x3e, 0x3, 0x0, 0x0 | ||
63 | } } } }, | ||
64 | |||
65 | [TV_NORM_PAL_M] = { TV_ENC_MODE, { | ||
66 | .tv_enc_mode = { 720, 480, 59940, { | ||
67 | 0x21, 0xe6, 0xef, 0xe3, 0x0, 0x0, 0xb, 0x18, | ||
68 | 0x7e, 0x44, 0x76, 0x32, 0x25, 0x0, 0x3c, 0x0, | ||
69 | 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83, | ||
70 | 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1, | ||
71 | 0xc5, 0x4, 0xc5, 0x1, 0x2, 0x0, 0xa, 0x5, | ||
72 | 0x0, 0x18, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0, | ||
73 | 0x0, 0xb4, 0x0, 0x15, 0x40, 0x10, 0x0, 0x9c, | ||
74 | 0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0 | ||
75 | } } } }, | ||
76 | |||
77 | [TV_NORM_PAL_N] = { TV_ENC_MODE, { | ||
78 | .tv_enc_mode = { 720, 576, 50000, { | ||
79 | 0x2a, 0x9, 0x8a, 0xcb, 0x0, 0x0, 0xb, 0x18, | ||
80 | 0x7e, 0x40, 0x8a, 0x32, 0x25, 0x0, 0x3c, 0x0, | ||
81 | 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c, | ||
82 | 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1, | ||
83 | 0xc5, 0x4, 0xc5, 0x1, 0x2, 0x0, 0xa, 0x5, | ||
84 | 0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0, | ||
85 | 0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b, | ||
86 | 0xbd, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0 | ||
87 | } } } }, | ||
88 | |||
89 | [TV_NORM_PAL_NC] = { TV_ENC_MODE, { | ||
90 | .tv_enc_mode = { 720, 576, 50000, { | ||
91 | 0x21, 0xf6, 0x94, 0x46, 0x0, 0x0, 0xb, 0x18, | ||
92 | 0x7e, 0x44, 0x8a, 0x35, 0x27, 0x0, 0x34, 0x3, | ||
93 | 0x3e, 0x3, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c, | ||
94 | 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x3, | ||
95 | 0xd3, 0x4, 0xd4, 0x1, 0x2, 0x0, 0xa, 0x5, | ||
96 | 0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0, | ||
97 | 0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b, | ||
98 | 0xbd, 0x15, 0x5, 0x15, 0x3e, 0x3, 0x0, 0x0 | ||
99 | } } } }, | ||
100 | |||
101 | [TV_NORM_NTSC_M] = { TV_ENC_MODE, { | ||
102 | .tv_enc_mode = { 720, 480, 59940, { | ||
103 | 0x21, 0xf0, 0x7c, 0x1f, 0x0, 0x0, 0xb, 0x18, | ||
104 | 0x7e, 0x44, 0x76, 0x48, 0x0, 0x0, 0x3c, 0x0, | ||
105 | 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83, | ||
106 | 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1, | ||
107 | 0xc5, 0x4, 0xc5, 0x1, 0x2, 0x0, 0xa, 0x5, | ||
108 | 0x0, 0x16, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0, | ||
109 | 0x0, 0xb4, 0x0, 0x15, 0x4, 0x10, 0x0, 0x9c, | ||
110 | 0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0 | ||
111 | } } } }, | ||
112 | |||
113 | [TV_NORM_NTSC_J] = { TV_ENC_MODE, { | ||
114 | .tv_enc_mode = { 720, 480, 59940, { | ||
115 | 0x21, 0xf0, 0x7c, 0x1f, 0x0, 0x0, 0xb, 0x18, | ||
116 | 0x7e, 0x44, 0x76, 0x48, 0x0, 0x0, 0x32, 0x0, | ||
117 | 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83, | ||
118 | 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1, | ||
119 | 0xcf, 0x4, 0xcf, 0x1, 0x2, 0x0, 0xa, 0x5, | ||
120 | 0x0, 0x16, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0, | ||
121 | 0x0, 0xb4, 0x0, 0x15, 0x4, 0x10, 0x0, 0xa4, | ||
122 | 0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0 | ||
123 | } } } }, | ||
124 | |||
125 | [TV_NORM_HD480I] = { TV_ENC_MODE, { | ||
126 | .tv_enc_mode = { 720, 480, 59940, { | ||
127 | 0x21, 0xf0, 0x7c, 0x1f, 0x0, 0x0, 0xb, 0x18, | ||
128 | 0x7e, 0x44, 0x76, 0x48, 0x0, 0x0, 0x32, 0x0, | ||
129 | 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83, | ||
130 | 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1, | ||
131 | 0xcf, 0x4, 0xcf, 0x1, 0x2, 0x0, 0xa, 0x5, | ||
132 | 0x0, 0x16, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0, | ||
133 | 0x0, 0xb4, 0x0, 0x15, 0x4, 0x10, 0x0, 0xa4, | ||
134 | 0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0 | ||
135 | } } } }, | ||
136 | |||
137 | [TV_NORM_HD576I] = { TV_ENC_MODE, { | ||
138 | .tv_enc_mode = { 720, 576, 50000, { | ||
139 | 0x2a, 0x9, 0x8a, 0xcb, 0x0, 0x0, 0xb, 0x18, | ||
140 | 0x7e, 0x40, 0x8a, 0x35, 0x27, 0x0, 0x34, 0x3, | ||
141 | 0x3e, 0x3, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c, | ||
142 | 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x3, | ||
143 | 0xd3, 0x4, 0xd4, 0x1, 0x2, 0x0, 0xa, 0x5, | ||
144 | 0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0, | ||
145 | 0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b, | ||
146 | 0xbd, 0x15, 0x5, 0x15, 0x3e, 0x3, 0x0, 0x0 | ||
147 | } } } }, | ||
148 | |||
149 | |||
150 | [TV_NORM_HD480P] = { CTV_ENC_MODE, { | ||
151 | .ctv_enc_mode = { | ||
152 | .mode = { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, | ||
153 | 720, 735, 743, 858, 0, 480, 490, 494, 525, 0, | ||
154 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, | ||
155 | .ctv_regs = { 0x3540000, 0x0, 0x0, 0x314, | ||
156 | 0x354003a, 0x40000, 0x6f0344, 0x18100000, | ||
157 | 0x10160004, 0x10060005, 0x1006000c, 0x10060020, | ||
158 | 0x10060021, 0x140e0022, 0x10060202, 0x1802020a, | ||
159 | 0x1810020b, 0x10000fff, 0x10000fff, 0x10000fff, | ||
160 | 0x10000fff, 0x10000fff, 0x10000fff, 0x70, | ||
161 | 0x3ff0000, 0x57, 0x2e001e, 0x258012c, | ||
162 | 0xa0aa04ec, 0x30, 0x80960019, 0x12c0300, | ||
163 | 0x2019, 0x600, 0x32060019, 0x0, 0x0, 0x400 | ||
164 | } } } }, | ||
165 | |||
166 | [TV_NORM_HD576P] = { CTV_ENC_MODE, { | ||
167 | .ctv_enc_mode = { | ||
168 | .mode = { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, | ||
169 | 720, 730, 738, 864, 0, 576, 581, 585, 625, 0, | ||
170 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, | ||
171 | .ctv_regs = { 0x3540000, 0x0, 0x0, 0x314, | ||
172 | 0x354003a, 0x40000, 0x6f0344, 0x18100000, | ||
173 | 0x10060001, 0x10060009, 0x10060026, 0x10060027, | ||
174 | 0x140e0028, 0x10060268, 0x1810026d, 0x10000fff, | ||
175 | 0x10000fff, 0x10000fff, 0x10000fff, 0x10000fff, | ||
176 | 0x10000fff, 0x10000fff, 0x10000fff, 0x69, | ||
177 | 0x3ff0000, 0x57, 0x2e001e, 0x258012c, | ||
178 | 0xa0aa04ec, 0x30, 0x80960019, 0x12c0300, | ||
179 | 0x2019, 0x600, 0x32060019, 0x0, 0x0, 0x400 | ||
180 | } } } }, | ||
181 | |||
182 | [TV_NORM_HD720P] = { CTV_ENC_MODE, { | ||
183 | .ctv_enc_mode = { | ||
184 | .mode = { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, | ||
185 | 1280, 1349, 1357, 1650, 0, 720, 725, 730, 750, 0, | ||
186 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, | ||
187 | .ctv_regs = { 0x1260394, 0x0, 0x0, 0x622, | ||
188 | 0x66b0021, 0x6004a, 0x1210626, 0x8170000, | ||
189 | 0x70004, 0x70016, 0x70017, 0x40f0018, | ||
190 | 0x702e8, 0x81702ed, 0xfff, 0xfff, | ||
191 | 0xfff, 0xfff, 0xfff, 0xfff, | ||
192 | 0xfff, 0xfff, 0xfff, 0x0, | ||
193 | 0x2e40001, 0x58, 0x2e001e, 0x258012c, | ||
194 | 0xa0aa04ec, 0x30, 0x810c0039, 0x12c0300, | ||
195 | 0xc0002039, 0x600, 0x32060039, 0x0, 0x0, 0x0 | ||
196 | } } } }, | ||
197 | |||
198 | [TV_NORM_HD1080I] = { CTV_ENC_MODE, { | ||
199 | .ctv_enc_mode = { | ||
200 | .mode = { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, | ||
201 | 1920, 1961, 2049, 2200, 0, 1080, 1084, 1088, 1125, 0, | ||
202 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | ||
203 | | DRM_MODE_FLAG_INTERLACE) }, | ||
204 | .ctv_regs = { 0xac0420, 0x44c0478, 0x4a4, 0x4fc0868, | ||
205 | 0x8940028, 0x60054, 0xe80870, 0xbf70000, | ||
206 | 0xbc70004, 0x70005, 0x70012, 0x70013, | ||
207 | 0x40f0014, 0x70230, 0xbf70232, 0xbf70233, | ||
208 | 0x1c70237, 0x70238, 0x70244, 0x70245, | ||
209 | 0x40f0246, 0x70462, 0x1f70464, 0x0, | ||
210 | 0x2e40001, 0x58, 0x2e001e, 0x258012c, | ||
211 | 0xa0aa04ec, 0x30, 0x815f004c, 0x12c0300, | ||
212 | 0xc000204c, 0x600, 0x3206004c, 0x0, 0x0, 0x0 | ||
213 | } } } } | ||
214 | }; | ||
215 | |||
216 | /* | ||
217 | * The following is some guesswork on how the TV encoder flicker | ||
218 | * filter/rescaler works: | ||
219 | * | ||
220 | * It seems to use some sort of resampling filter, it is controlled | ||
221 | * through the registers at NV_PTV_HFILTER and NV_PTV_VFILTER, they | ||
222 | * control the horizontal and vertical stage respectively, there is | ||
223 | * also NV_PTV_HFILTER2 the blob fills identically to NV_PTV_HFILTER, | ||
224 | * but they seem to do nothing. A rough guess might be that they could | ||
225 | * be used to independently control the filtering of each interlaced | ||
226 | * field, but I don't know how they are enabled. The whole filtering | ||
227 | * process seems to be disabled with bits 26:27 of PTV_200, but we | ||
228 | * aren't doing that. | ||
229 | * | ||
230 | * The layout of both register sets is the same: | ||
231 | * | ||
232 | * A: [BASE+0x18]...[BASE+0x0] [BASE+0x58]..[BASE+0x40] | ||
233 | * B: [BASE+0x34]...[BASE+0x1c] [BASE+0x74]..[BASE+0x5c] | ||
234 | * | ||
235 | * Each coefficient is stored in bits [31],[15:9] in two's complement | ||
236 | * format. They seem to be some kind of weights used in a low-pass | ||
237 | * filter. Both A and B coefficients are applied to the 14 nearest | ||
238 | * samples on each side (Listed from nearest to furthermost. They | ||
239 | * roughly cover 2 framebuffer pixels on each side). They are | ||
240 | * probably multiplied with some more hardwired weights before being | ||
241 | * used: B-coefficients are applied the same on both sides, | ||
242 | * A-coefficients are inverted before being applied to the opposite | ||
243 | * side. | ||
244 | * | ||
245 | * After all the hassle, I got the following formula by empirical | ||
246 | * means... | ||
247 | */ | ||
248 | |||
249 | #define calc_overscan(o) interpolate(0x100, 0xe1, 0xc1, o) | ||
250 | |||
251 | #define id1 (1LL << 8) | ||
252 | #define id2 (1LL << 16) | ||
253 | #define id3 (1LL << 24) | ||
254 | #define id4 (1LL << 32) | ||
255 | #define id5 (1LL << 48) | ||
256 | |||
257 | static struct filter_params{ | ||
258 | int64_t k1; | ||
259 | int64_t ki; | ||
260 | int64_t ki2; | ||
261 | int64_t ki3; | ||
262 | int64_t kr; | ||
263 | int64_t kir; | ||
264 | int64_t ki2r; | ||
265 | int64_t ki3r; | ||
266 | int64_t kf; | ||
267 | int64_t kif; | ||
268 | int64_t ki2f; | ||
269 | int64_t ki3f; | ||
270 | int64_t krf; | ||
271 | int64_t kirf; | ||
272 | int64_t ki2rf; | ||
273 | int64_t ki3rf; | ||
274 | } fparams[2][4] = { | ||
275 | /* Horizontal filter parameters */ | ||
276 | { | ||
277 | {64.311690 * id5, -39.516924 * id5, 6.586143 * id5, 0.000002 * id5, | ||
278 | 0.051285 * id4, 26.168746 * id4, -4.361449 * id4, -0.000001 * id4, | ||
279 | 9.308169 * id3, 78.180965 * id3, -13.030158 * id3, -0.000001 * id3, | ||
280 | -8.801540 * id1, -46.572890 * id1, 7.762145 * id1, -0.000000 * id1}, | ||
281 | {-44.565569 * id5, -68.081246 * id5, 39.812074 * id5, -4.009316 * id5, | ||
282 | 29.832207 * id4, 50.047322 * id4, -25.380017 * id4, 2.546422 * id4, | ||
283 | 104.605622 * id3, 141.908641 * id3, -74.322319 * id3, 7.484316 * id3, | ||
284 | -37.081621 * id1, -90.397510 * id1, 42.784229 * id1, -4.289952 * id1}, | ||
285 | {-56.793244 * id5, 31.153584 * id5, -5.192247 * id5, -0.000003 * id5, | ||
286 | 33.541131 * id4, -34.149302 * id4, 5.691537 * id4, 0.000002 * id4, | ||
287 | 87.196610 * id3, -88.995169 * id3, 14.832456 * id3, 0.000012 * id3, | ||
288 | 17.288138 * id1, 71.864786 * id1, -11.977408 * id1, -0.000009 * id1}, | ||
289 | {51.787796 * id5, 21.211771 * id5, -18.993730 * id5, 1.853310 * id5, | ||
290 | -41.470726 * id4, -17.775823 * id4, 13.057821 * id4, -1.15823 * id4, | ||
291 | -154.235673 * id3, -44.878641 * id3, 40.656077 * id3, -3.695595 * id3, | ||
292 | 112.201065 * id1, 39.992155 * id1, -25.155714 * id1, 2.113984 * id1}, | ||
293 | }, | ||
294 | |||
295 | /* Vertical filter parameters */ | ||
296 | { | ||
297 | {67.601979 * id5, 0.428319 * id5, -0.071318 * id5, -0.000012 * id5, | ||
298 | -3.402339 * id4, 0.000209 * id4, -0.000092 * id4, 0.000010 * id4, | ||
299 | -9.180996 * id3, 6.111270 * id3, -1.024457 * id3, 0.001043 * id3, | ||
300 | 6.060315 * id1, -0.017425 * id1, 0.007830 * id1, -0.000869 * id1}, | ||
301 | {6.755647 * id5, 5.841348 * id5, 1.469734 * id5, -0.149656 * id5, | ||
302 | 8.293120 * id4, -1.192888 * id4, -0.947652 * id4, 0.094507 * id4, | ||
303 | 37.526655 * id3, 10.257875 * id3, -10.823275 * id3, 1.081497 * id3, | ||
304 | -2.361928 * id1, -2.059432 * id1, 1.840671 * id1, -0.168100 * id1}, | ||
305 | {-14.780391 * id5, -16.042148 * id5, 2.673692 * id5, -0.000000 * id5, | ||
306 | 39.541978 * id4, 5.680053 * id4, -0.946676 * id4, 0.000000 * id4, | ||
307 | 152.994486 * id3, 12.625439 * id3, -2.119579 * id3, 0.002708 * id3, | ||
308 | -38.125089 * id1, -0.855880 * id1, 0.155359 * id1, -0.002245 * id1}, | ||
309 | {-27.476193 * id5, -1.454976 * id5, 1.286557 * id5, 0.025346 * id5, | ||
310 | 20.687300 * id4, 3.014003 * id4, -0.557786 * id4, -0.01311 * id4, | ||
311 | 60.008737 * id3, -0.738273 * id3, 5.408217 * id3, -0.796798 * id3, | ||
312 | -17.296835 * id1, 4.438577 * id1, -2.809420 * id1, 0.385491 * id1}, | ||
313 | } | ||
314 | }; | ||
315 | |||
316 | static void tv_setup_filter(struct drm_encoder *encoder) | ||
317 | { | ||
318 | struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); | ||
319 | struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); | ||
320 | struct drm_display_mode *mode = &encoder->crtc->mode; | ||
321 | uint32_t (*filters[])[4][7] = {&tv_enc->state.hfilter, | ||
322 | &tv_enc->state.vfilter}; | ||
323 | int i, j, k; | ||
324 | int32_t overscan = calc_overscan(tv_enc->overscan); | ||
325 | int64_t flicker = (tv_enc->flicker - 50) * (id3 / 100); | ||
326 | uint64_t rs[] = {mode->hdisplay * id3, | ||
327 | mode->vdisplay * id3}; | ||
328 | |||
329 | do_div(rs[0], overscan * tv_norm->tv_enc_mode.hdisplay); | ||
330 | do_div(rs[1], overscan * tv_norm->tv_enc_mode.vdisplay); | ||
331 | |||
332 | for (k = 0; k < 2; k++) { | ||
333 | rs[k] = max((int64_t)rs[k], id2); | ||
334 | |||
335 | for (j = 0; j < 4; j++) { | ||
336 | struct filter_params *p = &fparams[k][j]; | ||
337 | |||
338 | for (i = 0; i < 7; i++) { | ||
339 | int64_t c = (p->k1 + p->ki*i + p->ki2*i*i + p->ki3*i*i*i) | ||
340 | + (p->kr + p->kir*i + p->ki2r*i*i + p->ki3r*i*i*i)*rs[k] | ||
341 | + (p->kf + p->kif*i + p->ki2f*i*i + p->ki3f*i*i*i)*flicker | ||
342 | + (p->krf + p->kirf*i + p->ki2rf*i*i + p->ki3rf*i*i*i)*flicker*rs[k]; | ||
343 | |||
344 | (*filters[k])[j][i] = (c + id5/2) >> 39 & (0x1 << 31 | 0x7f << 9); | ||
345 | } | ||
346 | } | ||
347 | } | ||
348 | } | ||
349 | |||
350 | /* Hardware state saving/restoring */ | ||
351 | |||
352 | static void tv_save_filter(struct drm_device *dev, uint32_t base, uint32_t regs[4][7]) | ||
353 | { | ||
354 | int i, j; | ||
355 | uint32_t offsets[] = { base, base + 0x1c, base + 0x40, base + 0x5c }; | ||
356 | |||
357 | for (i = 0; i < 4; i++) { | ||
358 | for (j = 0; j < 7; j++) | ||
359 | regs[i][j] = nv_read_ptv(dev, offsets[i]+4*j); | ||
360 | } | ||
361 | } | ||
362 | |||
363 | static void tv_load_filter(struct drm_device *dev, uint32_t base, uint32_t regs[4][7]) | ||
364 | { | ||
365 | int i, j; | ||
366 | uint32_t offsets[] = { base, base + 0x1c, base + 0x40, base + 0x5c }; | ||
367 | |||
368 | for (i = 0; i < 4; i++) { | ||
369 | for (j = 0; j < 7; j++) | ||
370 | nv_write_ptv(dev, offsets[i]+4*j, regs[i][j]); | ||
371 | } | ||
372 | } | ||
373 | |||
374 | void nv17_tv_state_save(struct drm_device *dev, struct nv17_tv_state *state) | ||
375 | { | ||
376 | int i; | ||
377 | |||
378 | for (i = 0; i < 0x40; i++) | ||
379 | state->tv_enc[i] = nv_read_tv_enc(dev, i); | ||
380 | |||
381 | tv_save_filter(dev, NV_PTV_HFILTER, state->hfilter); | ||
382 | tv_save_filter(dev, NV_PTV_HFILTER2, state->hfilter2); | ||
383 | tv_save_filter(dev, NV_PTV_VFILTER, state->vfilter); | ||
384 | |||
385 | nv_save_ptv(dev, state, 200); | ||
386 | nv_save_ptv(dev, state, 204); | ||
387 | nv_save_ptv(dev, state, 208); | ||
388 | nv_save_ptv(dev, state, 20c); | ||
389 | nv_save_ptv(dev, state, 304); | ||
390 | nv_save_ptv(dev, state, 500); | ||
391 | nv_save_ptv(dev, state, 504); | ||
392 | nv_save_ptv(dev, state, 508); | ||
393 | nv_save_ptv(dev, state, 600); | ||
394 | nv_save_ptv(dev, state, 604); | ||
395 | nv_save_ptv(dev, state, 608); | ||
396 | nv_save_ptv(dev, state, 60c); | ||
397 | nv_save_ptv(dev, state, 610); | ||
398 | nv_save_ptv(dev, state, 614); | ||
399 | } | ||
400 | |||
401 | void nv17_tv_state_load(struct drm_device *dev, struct nv17_tv_state *state) | ||
402 | { | ||
403 | int i; | ||
404 | |||
405 | for (i = 0; i < 0x40; i++) | ||
406 | nv_write_tv_enc(dev, i, state->tv_enc[i]); | ||
407 | |||
408 | tv_load_filter(dev, NV_PTV_HFILTER, state->hfilter); | ||
409 | tv_load_filter(dev, NV_PTV_HFILTER2, state->hfilter2); | ||
410 | tv_load_filter(dev, NV_PTV_VFILTER, state->vfilter); | ||
411 | |||
412 | nv_load_ptv(dev, state, 200); | ||
413 | nv_load_ptv(dev, state, 204); | ||
414 | nv_load_ptv(dev, state, 208); | ||
415 | nv_load_ptv(dev, state, 20c); | ||
416 | nv_load_ptv(dev, state, 304); | ||
417 | nv_load_ptv(dev, state, 500); | ||
418 | nv_load_ptv(dev, state, 504); | ||
419 | nv_load_ptv(dev, state, 508); | ||
420 | nv_load_ptv(dev, state, 600); | ||
421 | nv_load_ptv(dev, state, 604); | ||
422 | nv_load_ptv(dev, state, 608); | ||
423 | nv_load_ptv(dev, state, 60c); | ||
424 | nv_load_ptv(dev, state, 610); | ||
425 | nv_load_ptv(dev, state, 614); | ||
426 | |||
427 | /* This is required for some settings to kick in. */ | ||
428 | nv_write_tv_enc(dev, 0x3e, 1); | ||
429 | nv_write_tv_enc(dev, 0x3e, 0); | ||
430 | } | ||
431 | |||
432 | /* Timings similar to the ones the blob sets */ | ||
433 | |||
434 | struct drm_display_mode nv17_tv_modes[] = { | ||
435 | { DRM_MODE("320x200", DRM_MODE_TYPE_DRIVER, 0, | ||
436 | 320, 344, 392, 560, 0, 200, 200, 202, 220, 0, | ||
437 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | ||
438 | | DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) }, | ||
439 | { DRM_MODE("320x240", DRM_MODE_TYPE_DRIVER, 0, | ||
440 | 320, 344, 392, 560, 0, 240, 240, 246, 263, 0, | ||
441 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | ||
442 | | DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) }, | ||
443 | { DRM_MODE("400x300", DRM_MODE_TYPE_DRIVER, 0, | ||
444 | 400, 432, 496, 640, 0, 300, 300, 303, 314, 0, | ||
445 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | ||
446 | | DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) }, | ||
447 | { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 0, | ||
448 | 640, 672, 768, 880, 0, 480, 480, 492, 525, 0, | ||
449 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, | ||
450 | { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 0, | ||
451 | 720, 752, 872, 960, 0, 480, 480, 493, 525, 0, | ||
452 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, | ||
453 | { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 0, | ||
454 | 720, 776, 856, 960, 0, 576, 576, 588, 597, 0, | ||
455 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, | ||
456 | { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 0, | ||
457 | 800, 840, 920, 1040, 0, 600, 600, 604, 618, 0, | ||
458 | DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, | ||
459 | { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 0, | ||
460 | 1024, 1064, 1200, 1344, 0, 768, 768, 777, 806, 0, | ||
461 | DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, | ||
462 | {} | ||
463 | }; | ||
464 | |||
465 | void nv17_tv_update_properties(struct drm_encoder *encoder) | ||
466 | { | ||
467 | struct drm_device *dev = encoder->dev; | ||
468 | struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); | ||
469 | struct nv17_tv_state *regs = &tv_enc->state; | ||
470 | struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); | ||
471 | int subconnector = tv_enc->select_subconnector ? | ||
472 | tv_enc->select_subconnector : | ||
473 | tv_enc->subconnector; | ||
474 | |||
475 | switch (subconnector) { | ||
476 | case DRM_MODE_SUBCONNECTOR_Composite: | ||
477 | { | ||
478 | regs->ptv_204 = 0x2; | ||
479 | |||
480 | /* The composite connector may be found on either pin. */ | ||
481 | if (tv_enc->pin_mask & 0x4) | ||
482 | regs->ptv_204 |= 0x010000; | ||
483 | else if (tv_enc->pin_mask & 0x2) | ||
484 | regs->ptv_204 |= 0x100000; | ||
485 | else | ||
486 | regs->ptv_204 |= 0x110000; | ||
487 | |||
488 | regs->tv_enc[0x7] = 0x10; | ||
489 | break; | ||
490 | } | ||
491 | case DRM_MODE_SUBCONNECTOR_SVIDEO: | ||
492 | regs->ptv_204 = 0x11012; | ||
493 | regs->tv_enc[0x7] = 0x18; | ||
494 | break; | ||
495 | |||
496 | case DRM_MODE_SUBCONNECTOR_Component: | ||
497 | regs->ptv_204 = 0x111333; | ||
498 | regs->tv_enc[0x7] = 0x14; | ||
499 | break; | ||
500 | |||
501 | case DRM_MODE_SUBCONNECTOR_SCART: | ||
502 | regs->ptv_204 = 0x111012; | ||
503 | regs->tv_enc[0x7] = 0x18; | ||
504 | break; | ||
505 | } | ||
506 | |||
507 | regs->tv_enc[0x20] = interpolate(0, tv_norm->tv_enc_mode.tv_enc[0x20], 255, | ||
508 | tv_enc->saturation); | ||
509 | regs->tv_enc[0x22] = interpolate(0, tv_norm->tv_enc_mode.tv_enc[0x22], 255, | ||
510 | tv_enc->saturation); | ||
511 | regs->tv_enc[0x25] = tv_enc->hue * 255 / 100; | ||
512 | |||
513 | nv_load_ptv(dev, regs, 204); | ||
514 | nv_load_tv_enc(dev, regs, 7); | ||
515 | nv_load_tv_enc(dev, regs, 20); | ||
516 | nv_load_tv_enc(dev, regs, 22); | ||
517 | nv_load_tv_enc(dev, regs, 25); | ||
518 | } | ||
519 | |||
520 | void nv17_tv_update_rescaler(struct drm_encoder *encoder) | ||
521 | { | ||
522 | struct drm_device *dev = encoder->dev; | ||
523 | struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); | ||
524 | struct nv17_tv_state *regs = &tv_enc->state; | ||
525 | |||
526 | regs->ptv_208 = 0x40 | (calc_overscan(tv_enc->overscan) << 8); | ||
527 | |||
528 | tv_setup_filter(encoder); | ||
529 | |||
530 | nv_load_ptv(dev, regs, 208); | ||
531 | tv_load_filter(dev, NV_PTV_HFILTER, regs->hfilter); | ||
532 | tv_load_filter(dev, NV_PTV_HFILTER2, regs->hfilter2); | ||
533 | tv_load_filter(dev, NV_PTV_VFILTER, regs->vfilter); | ||
534 | } | ||
535 | |||
536 | void nv17_ctv_update_rescaler(struct drm_encoder *encoder) | ||
537 | { | ||
538 | struct drm_device *dev = encoder->dev; | ||
539 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
540 | struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); | ||
541 | int head = nouveau_crtc(encoder->crtc)->index; | ||
542 | struct nv04_crtc_reg *regs = &dev_priv->mode_reg.crtc_reg[head]; | ||
543 | struct drm_display_mode *crtc_mode = &encoder->crtc->mode; | ||
544 | struct drm_display_mode *output_mode = &get_tv_norm(encoder)->ctv_enc_mode.mode; | ||
545 | int overscan, hmargin, vmargin, hratio, vratio; | ||
546 | |||
547 | /* The rescaler doesn't do the right thing for interlaced modes. */ | ||
548 | if (output_mode->flags & DRM_MODE_FLAG_INTERLACE) | ||
549 | overscan = 100; | ||
550 | else | ||
551 | overscan = tv_enc->overscan; | ||
552 | |||
553 | hmargin = (output_mode->hdisplay - crtc_mode->hdisplay) / 2; | ||
554 | vmargin = (output_mode->vdisplay - crtc_mode->vdisplay) / 2; | ||
555 | |||
556 | hmargin = interpolate(0, min(hmargin, output_mode->hdisplay/20), hmargin, | ||
557 | overscan); | ||
558 | vmargin = interpolate(0, min(vmargin, output_mode->vdisplay/20), vmargin, | ||
559 | overscan); | ||
560 | |||
561 | hratio = crtc_mode->hdisplay * 0x800 / (output_mode->hdisplay - 2*hmargin); | ||
562 | vratio = crtc_mode->vdisplay * 0x800 / (output_mode->vdisplay - 2*vmargin) & ~3; | ||
563 | |||
564 | regs->fp_horiz_regs[FP_VALID_START] = hmargin; | ||
565 | regs->fp_horiz_regs[FP_VALID_END] = output_mode->hdisplay - hmargin - 1; | ||
566 | regs->fp_vert_regs[FP_VALID_START] = vmargin; | ||
567 | regs->fp_vert_regs[FP_VALID_END] = output_mode->vdisplay - vmargin - 1; | ||
568 | |||
569 | regs->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE | | ||
570 | XLATE(vratio, 0, NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE) | | ||
571 | NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE | | ||
572 | XLATE(hratio, 0, NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE); | ||
573 | |||
574 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HVALID_START, | ||
575 | regs->fp_horiz_regs[FP_VALID_START]); | ||
576 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HVALID_END, | ||
577 | regs->fp_horiz_regs[FP_VALID_END]); | ||
578 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_VVALID_START, | ||
579 | regs->fp_vert_regs[FP_VALID_START]); | ||
580 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_VVALID_END, | ||
581 | regs->fp_vert_regs[FP_VALID_END]); | ||
582 | NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_1, regs->fp_debug_1); | ||
583 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nv20_graph.c b/drivers/gpu/drm/nouveau/nv20_graph.c new file mode 100644 index 000000000000..18ba74f19703 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv20_graph.c | |||
@@ -0,0 +1,780 @@ | |||
1 | #include "drmP.h" | ||
2 | #include "drm.h" | ||
3 | #include "nouveau_drv.h" | ||
4 | #include "nouveau_drm.h" | ||
5 | |||
6 | /* | ||
7 | * NV20 | ||
8 | * ----- | ||
9 | * There are 3 families : | ||
10 | * NV20 is 0x10de:0x020* | ||
11 | * NV25/28 is 0x10de:0x025* / 0x10de:0x028* | ||
12 | * NV2A is 0x10de:0x02A0 | ||
13 | * | ||
14 | * NV30 | ||
15 | * ----- | ||
16 | * There are 3 families : | ||
17 | * NV30/31 is 0x10de:0x030* / 0x10de:0x031* | ||
18 | * NV34 is 0x10de:0x032* | ||
19 | * NV35/36 is 0x10de:0x033* / 0x10de:0x034* | ||
20 | * | ||
21 | * Not seen in the wild, no dumps (probably NV35) : | ||
22 | * NV37 is 0x10de:0x00fc, 0x10de:0x00fd | ||
23 | * NV38 is 0x10de:0x0333, 0x10de:0x00fe | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #define NV20_GRCTX_SIZE (3580*4) | ||
28 | #define NV25_GRCTX_SIZE (3529*4) | ||
29 | #define NV2A_GRCTX_SIZE (3500*4) | ||
30 | |||
31 | #define NV30_31_GRCTX_SIZE (24392) | ||
32 | #define NV34_GRCTX_SIZE (18140) | ||
33 | #define NV35_36_GRCTX_SIZE (22396) | ||
34 | |||
35 | static void | ||
36 | nv20_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx) | ||
37 | { | ||
38 | int i; | ||
39 | |||
40 | nv_wo32(dev, ctx, 0x033c/4, 0xffff0000); | ||
41 | nv_wo32(dev, ctx, 0x03a0/4, 0x0fff0000); | ||
42 | nv_wo32(dev, ctx, 0x03a4/4, 0x0fff0000); | ||
43 | nv_wo32(dev, ctx, 0x047c/4, 0x00000101); | ||
44 | nv_wo32(dev, ctx, 0x0490/4, 0x00000111); | ||
45 | nv_wo32(dev, ctx, 0x04a8/4, 0x44400000); | ||
46 | for (i = 0x04d4; i <= 0x04e0; i += 4) | ||
47 | nv_wo32(dev, ctx, i/4, 0x00030303); | ||
48 | for (i = 0x04f4; i <= 0x0500; i += 4) | ||
49 | nv_wo32(dev, ctx, i/4, 0x00080000); | ||
50 | for (i = 0x050c; i <= 0x0518; i += 4) | ||
51 | nv_wo32(dev, ctx, i/4, 0x01012000); | ||
52 | for (i = 0x051c; i <= 0x0528; i += 4) | ||
53 | nv_wo32(dev, ctx, i/4, 0x000105b8); | ||
54 | for (i = 0x052c; i <= 0x0538; i += 4) | ||
55 | nv_wo32(dev, ctx, i/4, 0x00080008); | ||
56 | for (i = 0x055c; i <= 0x0598; i += 4) | ||
57 | nv_wo32(dev, ctx, i/4, 0x07ff0000); | ||
58 | nv_wo32(dev, ctx, 0x05a4/4, 0x4b7fffff); | ||
59 | nv_wo32(dev, ctx, 0x05fc/4, 0x00000001); | ||
60 | nv_wo32(dev, ctx, 0x0604/4, 0x00004000); | ||
61 | nv_wo32(dev, ctx, 0x0610/4, 0x00000001); | ||
62 | nv_wo32(dev, ctx, 0x0618/4, 0x00040000); | ||
63 | nv_wo32(dev, ctx, 0x061c/4, 0x00010000); | ||
64 | for (i = 0x1c1c; i <= 0x248c; i += 16) { | ||
65 | nv_wo32(dev, ctx, (i + 0)/4, 0x10700ff9); | ||
66 | nv_wo32(dev, ctx, (i + 4)/4, 0x0436086c); | ||
67 | nv_wo32(dev, ctx, (i + 8)/4, 0x000c001b); | ||
68 | } | ||
69 | nv_wo32(dev, ctx, 0x281c/4, 0x3f800000); | ||
70 | nv_wo32(dev, ctx, 0x2830/4, 0x3f800000); | ||
71 | nv_wo32(dev, ctx, 0x285c/4, 0x40000000); | ||
72 | nv_wo32(dev, ctx, 0x2860/4, 0x3f800000); | ||
73 | nv_wo32(dev, ctx, 0x2864/4, 0x3f000000); | ||
74 | nv_wo32(dev, ctx, 0x286c/4, 0x40000000); | ||
75 | nv_wo32(dev, ctx, 0x2870/4, 0x3f800000); | ||
76 | nv_wo32(dev, ctx, 0x2878/4, 0xbf800000); | ||
77 | nv_wo32(dev, ctx, 0x2880/4, 0xbf800000); | ||
78 | nv_wo32(dev, ctx, 0x34a4/4, 0x000fe000); | ||
79 | nv_wo32(dev, ctx, 0x3530/4, 0x000003f8); | ||
80 | nv_wo32(dev, ctx, 0x3540/4, 0x002fe000); | ||
81 | for (i = 0x355c; i <= 0x3578; i += 4) | ||
82 | nv_wo32(dev, ctx, i/4, 0x001c527c); | ||
83 | } | ||
84 | |||
85 | static void | ||
86 | nv25_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx) | ||
87 | { | ||
88 | int i; | ||
89 | |||
90 | nv_wo32(dev, ctx, 0x035c/4, 0xffff0000); | ||
91 | nv_wo32(dev, ctx, 0x03c0/4, 0x0fff0000); | ||
92 | nv_wo32(dev, ctx, 0x03c4/4, 0x0fff0000); | ||
93 | nv_wo32(dev, ctx, 0x049c/4, 0x00000101); | ||
94 | nv_wo32(dev, ctx, 0x04b0/4, 0x00000111); | ||
95 | nv_wo32(dev, ctx, 0x04c8/4, 0x00000080); | ||
96 | nv_wo32(dev, ctx, 0x04cc/4, 0xffff0000); | ||
97 | nv_wo32(dev, ctx, 0x04d0/4, 0x00000001); | ||
98 | nv_wo32(dev, ctx, 0x04e4/4, 0x44400000); | ||
99 | nv_wo32(dev, ctx, 0x04fc/4, 0x4b800000); | ||
100 | for (i = 0x0510; i <= 0x051c; i += 4) | ||
101 | nv_wo32(dev, ctx, i/4, 0x00030303); | ||
102 | for (i = 0x0530; i <= 0x053c; i += 4) | ||
103 | nv_wo32(dev, ctx, i/4, 0x00080000); | ||
104 | for (i = 0x0548; i <= 0x0554; i += 4) | ||
105 | nv_wo32(dev, ctx, i/4, 0x01012000); | ||
106 | for (i = 0x0558; i <= 0x0564; i += 4) | ||
107 | nv_wo32(dev, ctx, i/4, 0x000105b8); | ||
108 | for (i = 0x0568; i <= 0x0574; i += 4) | ||
109 | nv_wo32(dev, ctx, i/4, 0x00080008); | ||
110 | for (i = 0x0598; i <= 0x05d4; i += 4) | ||
111 | nv_wo32(dev, ctx, i/4, 0x07ff0000); | ||
112 | nv_wo32(dev, ctx, 0x05e0/4, 0x4b7fffff); | ||
113 | nv_wo32(dev, ctx, 0x0620/4, 0x00000080); | ||
114 | nv_wo32(dev, ctx, 0x0624/4, 0x30201000); | ||
115 | nv_wo32(dev, ctx, 0x0628/4, 0x70605040); | ||
116 | nv_wo32(dev, ctx, 0x062c/4, 0xb0a09080); | ||
117 | nv_wo32(dev, ctx, 0x0630/4, 0xf0e0d0c0); | ||
118 | nv_wo32(dev, ctx, 0x0664/4, 0x00000001); | ||
119 | nv_wo32(dev, ctx, 0x066c/4, 0x00004000); | ||
120 | nv_wo32(dev, ctx, 0x0678/4, 0x00000001); | ||
121 | nv_wo32(dev, ctx, 0x0680/4, 0x00040000); | ||
122 | nv_wo32(dev, ctx, 0x0684/4, 0x00010000); | ||
123 | for (i = 0x1b04; i <= 0x2374; i += 16) { | ||
124 | nv_wo32(dev, ctx, (i + 0)/4, 0x10700ff9); | ||
125 | nv_wo32(dev, ctx, (i + 4)/4, 0x0436086c); | ||
126 | nv_wo32(dev, ctx, (i + 8)/4, 0x000c001b); | ||
127 | } | ||
128 | nv_wo32(dev, ctx, 0x2704/4, 0x3f800000); | ||
129 | nv_wo32(dev, ctx, 0x2718/4, 0x3f800000); | ||
130 | nv_wo32(dev, ctx, 0x2744/4, 0x40000000); | ||
131 | nv_wo32(dev, ctx, 0x2748/4, 0x3f800000); | ||
132 | nv_wo32(dev, ctx, 0x274c/4, 0x3f000000); | ||
133 | nv_wo32(dev, ctx, 0x2754/4, 0x40000000); | ||
134 | nv_wo32(dev, ctx, 0x2758/4, 0x3f800000); | ||
135 | nv_wo32(dev, ctx, 0x2760/4, 0xbf800000); | ||
136 | nv_wo32(dev, ctx, 0x2768/4, 0xbf800000); | ||
137 | nv_wo32(dev, ctx, 0x308c/4, 0x000fe000); | ||
138 | nv_wo32(dev, ctx, 0x3108/4, 0x000003f8); | ||
139 | nv_wo32(dev, ctx, 0x3468/4, 0x002fe000); | ||
140 | for (i = 0x3484; i <= 0x34a0; i += 4) | ||
141 | nv_wo32(dev, ctx, i/4, 0x001c527c); | ||
142 | } | ||
143 | |||
144 | static void | ||
145 | nv2a_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx) | ||
146 | { | ||
147 | int i; | ||
148 | |||
149 | nv_wo32(dev, ctx, 0x033c/4, 0xffff0000); | ||
150 | nv_wo32(dev, ctx, 0x03a0/4, 0x0fff0000); | ||
151 | nv_wo32(dev, ctx, 0x03a4/4, 0x0fff0000); | ||
152 | nv_wo32(dev, ctx, 0x047c/4, 0x00000101); | ||
153 | nv_wo32(dev, ctx, 0x0490/4, 0x00000111); | ||
154 | nv_wo32(dev, ctx, 0x04a8/4, 0x44400000); | ||
155 | for (i = 0x04d4; i <= 0x04e0; i += 4) | ||
156 | nv_wo32(dev, ctx, i/4, 0x00030303); | ||
157 | for (i = 0x04f4; i <= 0x0500; i += 4) | ||
158 | nv_wo32(dev, ctx, i/4, 0x00080000); | ||
159 | for (i = 0x050c; i <= 0x0518; i += 4) | ||
160 | nv_wo32(dev, ctx, i/4, 0x01012000); | ||
161 | for (i = 0x051c; i <= 0x0528; i += 4) | ||
162 | nv_wo32(dev, ctx, i/4, 0x000105b8); | ||
163 | for (i = 0x052c; i <= 0x0538; i += 4) | ||
164 | nv_wo32(dev, ctx, i/4, 0x00080008); | ||
165 | for (i = 0x055c; i <= 0x0598; i += 4) | ||
166 | nv_wo32(dev, ctx, i/4, 0x07ff0000); | ||
167 | nv_wo32(dev, ctx, 0x05a4/4, 0x4b7fffff); | ||
168 | nv_wo32(dev, ctx, 0x05fc/4, 0x00000001); | ||
169 | nv_wo32(dev, ctx, 0x0604/4, 0x00004000); | ||
170 | nv_wo32(dev, ctx, 0x0610/4, 0x00000001); | ||
171 | nv_wo32(dev, ctx, 0x0618/4, 0x00040000); | ||
172 | nv_wo32(dev, ctx, 0x061c/4, 0x00010000); | ||
173 | for (i = 0x1a9c; i <= 0x22fc; i += 16) { /*XXX: check!! */ | ||
174 | nv_wo32(dev, ctx, (i + 0)/4, 0x10700ff9); | ||
175 | nv_wo32(dev, ctx, (i + 4)/4, 0x0436086c); | ||
176 | nv_wo32(dev, ctx, (i + 8)/4, 0x000c001b); | ||
177 | } | ||
178 | nv_wo32(dev, ctx, 0x269c/4, 0x3f800000); | ||
179 | nv_wo32(dev, ctx, 0x26b0/4, 0x3f800000); | ||
180 | nv_wo32(dev, ctx, 0x26dc/4, 0x40000000); | ||
181 | nv_wo32(dev, ctx, 0x26e0/4, 0x3f800000); | ||
182 | nv_wo32(dev, ctx, 0x26e4/4, 0x3f000000); | ||
183 | nv_wo32(dev, ctx, 0x26ec/4, 0x40000000); | ||
184 | nv_wo32(dev, ctx, 0x26f0/4, 0x3f800000); | ||
185 | nv_wo32(dev, ctx, 0x26f8/4, 0xbf800000); | ||
186 | nv_wo32(dev, ctx, 0x2700/4, 0xbf800000); | ||
187 | nv_wo32(dev, ctx, 0x3024/4, 0x000fe000); | ||
188 | nv_wo32(dev, ctx, 0x30a0/4, 0x000003f8); | ||
189 | nv_wo32(dev, ctx, 0x33fc/4, 0x002fe000); | ||
190 | for (i = 0x341c; i <= 0x3438; i += 4) | ||
191 | nv_wo32(dev, ctx, i/4, 0x001c527c); | ||
192 | } | ||
193 | |||
194 | static void | ||
195 | nv30_31_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx) | ||
196 | { | ||
197 | int i; | ||
198 | |||
199 | nv_wo32(dev, ctx, 0x0410/4, 0x00000101); | ||
200 | nv_wo32(dev, ctx, 0x0424/4, 0x00000111); | ||
201 | nv_wo32(dev, ctx, 0x0428/4, 0x00000060); | ||
202 | nv_wo32(dev, ctx, 0x0444/4, 0x00000080); | ||
203 | nv_wo32(dev, ctx, 0x0448/4, 0xffff0000); | ||
204 | nv_wo32(dev, ctx, 0x044c/4, 0x00000001); | ||
205 | nv_wo32(dev, ctx, 0x0460/4, 0x44400000); | ||
206 | nv_wo32(dev, ctx, 0x048c/4, 0xffff0000); | ||
207 | for (i = 0x04e0; i < 0x04e8; i += 4) | ||
208 | nv_wo32(dev, ctx, i/4, 0x0fff0000); | ||
209 | nv_wo32(dev, ctx, 0x04ec/4, 0x00011100); | ||
210 | for (i = 0x0508; i < 0x0548; i += 4) | ||
211 | nv_wo32(dev, ctx, i/4, 0x07ff0000); | ||
212 | nv_wo32(dev, ctx, 0x0550/4, 0x4b7fffff); | ||
213 | nv_wo32(dev, ctx, 0x058c/4, 0x00000080); | ||
214 | nv_wo32(dev, ctx, 0x0590/4, 0x30201000); | ||
215 | nv_wo32(dev, ctx, 0x0594/4, 0x70605040); | ||
216 | nv_wo32(dev, ctx, 0x0598/4, 0xb8a89888); | ||
217 | nv_wo32(dev, ctx, 0x059c/4, 0xf8e8d8c8); | ||
218 | nv_wo32(dev, ctx, 0x05b0/4, 0xb0000000); | ||
219 | for (i = 0x0600; i < 0x0640; i += 4) | ||
220 | nv_wo32(dev, ctx, i/4, 0x00010588); | ||
221 | for (i = 0x0640; i < 0x0680; i += 4) | ||
222 | nv_wo32(dev, ctx, i/4, 0x00030303); | ||
223 | for (i = 0x06c0; i < 0x0700; i += 4) | ||
224 | nv_wo32(dev, ctx, i/4, 0x0008aae4); | ||
225 | for (i = 0x0700; i < 0x0740; i += 4) | ||
226 | nv_wo32(dev, ctx, i/4, 0x01012000); | ||
227 | for (i = 0x0740; i < 0x0780; i += 4) | ||
228 | nv_wo32(dev, ctx, i/4, 0x00080008); | ||
229 | nv_wo32(dev, ctx, 0x085c/4, 0x00040000); | ||
230 | nv_wo32(dev, ctx, 0x0860/4, 0x00010000); | ||
231 | for (i = 0x0864; i < 0x0874; i += 4) | ||
232 | nv_wo32(dev, ctx, i/4, 0x00040004); | ||
233 | for (i = 0x1f18; i <= 0x3088 ; i += 16) { | ||
234 | nv_wo32(dev, ctx, i/4 + 0, 0x10700ff9); | ||
235 | nv_wo32(dev, ctx, i/4 + 1, 0x0436086c); | ||
236 | nv_wo32(dev, ctx, i/4 + 2, 0x000c001b); | ||
237 | } | ||
238 | for (i = 0x30b8; i < 0x30c8; i += 4) | ||
239 | nv_wo32(dev, ctx, i/4, 0x0000ffff); | ||
240 | nv_wo32(dev, ctx, 0x344c/4, 0x3f800000); | ||
241 | nv_wo32(dev, ctx, 0x3808/4, 0x3f800000); | ||
242 | nv_wo32(dev, ctx, 0x381c/4, 0x3f800000); | ||
243 | nv_wo32(dev, ctx, 0x3848/4, 0x40000000); | ||
244 | nv_wo32(dev, ctx, 0x384c/4, 0x3f800000); | ||
245 | nv_wo32(dev, ctx, 0x3850/4, 0x3f000000); | ||
246 | nv_wo32(dev, ctx, 0x3858/4, 0x40000000); | ||
247 | nv_wo32(dev, ctx, 0x385c/4, 0x3f800000); | ||
248 | nv_wo32(dev, ctx, 0x3864/4, 0xbf800000); | ||
249 | nv_wo32(dev, ctx, 0x386c/4, 0xbf800000); | ||
250 | } | ||
251 | |||
252 | static void | ||
253 | nv34_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx) | ||
254 | { | ||
255 | int i; | ||
256 | |||
257 | nv_wo32(dev, ctx, 0x040c/4, 0x01000101); | ||
258 | nv_wo32(dev, ctx, 0x0420/4, 0x00000111); | ||
259 | nv_wo32(dev, ctx, 0x0424/4, 0x00000060); | ||
260 | nv_wo32(dev, ctx, 0x0440/4, 0x00000080); | ||
261 | nv_wo32(dev, ctx, 0x0444/4, 0xffff0000); | ||
262 | nv_wo32(dev, ctx, 0x0448/4, 0x00000001); | ||
263 | nv_wo32(dev, ctx, 0x045c/4, 0x44400000); | ||
264 | nv_wo32(dev, ctx, 0x0480/4, 0xffff0000); | ||
265 | for (i = 0x04d4; i < 0x04dc; i += 4) | ||
266 | nv_wo32(dev, ctx, i/4, 0x0fff0000); | ||
267 | nv_wo32(dev, ctx, 0x04e0/4, 0x00011100); | ||
268 | for (i = 0x04fc; i < 0x053c; i += 4) | ||
269 | nv_wo32(dev, ctx, i/4, 0x07ff0000); | ||
270 | nv_wo32(dev, ctx, 0x0544/4, 0x4b7fffff); | ||
271 | nv_wo32(dev, ctx, 0x057c/4, 0x00000080); | ||
272 | nv_wo32(dev, ctx, 0x0580/4, 0x30201000); | ||
273 | nv_wo32(dev, ctx, 0x0584/4, 0x70605040); | ||
274 | nv_wo32(dev, ctx, 0x0588/4, 0xb8a89888); | ||
275 | nv_wo32(dev, ctx, 0x058c/4, 0xf8e8d8c8); | ||
276 | nv_wo32(dev, ctx, 0x05a0/4, 0xb0000000); | ||
277 | for (i = 0x05f0; i < 0x0630; i += 4) | ||
278 | nv_wo32(dev, ctx, i/4, 0x00010588); | ||
279 | for (i = 0x0630; i < 0x0670; i += 4) | ||
280 | nv_wo32(dev, ctx, i/4, 0x00030303); | ||
281 | for (i = 0x06b0; i < 0x06f0; i += 4) | ||
282 | nv_wo32(dev, ctx, i/4, 0x0008aae4); | ||
283 | for (i = 0x06f0; i < 0x0730; i += 4) | ||
284 | nv_wo32(dev, ctx, i/4, 0x01012000); | ||
285 | for (i = 0x0730; i < 0x0770; i += 4) | ||
286 | nv_wo32(dev, ctx, i/4, 0x00080008); | ||
287 | nv_wo32(dev, ctx, 0x0850/4, 0x00040000); | ||
288 | nv_wo32(dev, ctx, 0x0854/4, 0x00010000); | ||
289 | for (i = 0x0858; i < 0x0868; i += 4) | ||
290 | nv_wo32(dev, ctx, i/4, 0x00040004); | ||
291 | for (i = 0x15ac; i <= 0x271c ; i += 16) { | ||
292 | nv_wo32(dev, ctx, i/4 + 0, 0x10700ff9); | ||
293 | nv_wo32(dev, ctx, i/4 + 1, 0x0436086c); | ||
294 | nv_wo32(dev, ctx, i/4 + 2, 0x000c001b); | ||
295 | } | ||
296 | for (i = 0x274c; i < 0x275c; i += 4) | ||
297 | nv_wo32(dev, ctx, i/4, 0x0000ffff); | ||
298 | nv_wo32(dev, ctx, 0x2ae0/4, 0x3f800000); | ||
299 | nv_wo32(dev, ctx, 0x2e9c/4, 0x3f800000); | ||
300 | nv_wo32(dev, ctx, 0x2eb0/4, 0x3f800000); | ||
301 | nv_wo32(dev, ctx, 0x2edc/4, 0x40000000); | ||
302 | nv_wo32(dev, ctx, 0x2ee0/4, 0x3f800000); | ||
303 | nv_wo32(dev, ctx, 0x2ee4/4, 0x3f000000); | ||
304 | nv_wo32(dev, ctx, 0x2eec/4, 0x40000000); | ||
305 | nv_wo32(dev, ctx, 0x2ef0/4, 0x3f800000); | ||
306 | nv_wo32(dev, ctx, 0x2ef8/4, 0xbf800000); | ||
307 | nv_wo32(dev, ctx, 0x2f00/4, 0xbf800000); | ||
308 | } | ||
309 | |||
310 | static void | ||
311 | nv35_36_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx) | ||
312 | { | ||
313 | int i; | ||
314 | |||
315 | nv_wo32(dev, ctx, 0x040c/4, 0x00000101); | ||
316 | nv_wo32(dev, ctx, 0x0420/4, 0x00000111); | ||
317 | nv_wo32(dev, ctx, 0x0424/4, 0x00000060); | ||
318 | nv_wo32(dev, ctx, 0x0440/4, 0x00000080); | ||
319 | nv_wo32(dev, ctx, 0x0444/4, 0xffff0000); | ||
320 | nv_wo32(dev, ctx, 0x0448/4, 0x00000001); | ||
321 | nv_wo32(dev, ctx, 0x045c/4, 0x44400000); | ||
322 | nv_wo32(dev, ctx, 0x0488/4, 0xffff0000); | ||
323 | for (i = 0x04dc; i < 0x04e4; i += 4) | ||
324 | nv_wo32(dev, ctx, i/4, 0x0fff0000); | ||
325 | nv_wo32(dev, ctx, 0x04e8/4, 0x00011100); | ||
326 | for (i = 0x0504; i < 0x0544; i += 4) | ||
327 | nv_wo32(dev, ctx, i/4, 0x07ff0000); | ||
328 | nv_wo32(dev, ctx, 0x054c/4, 0x4b7fffff); | ||
329 | nv_wo32(dev, ctx, 0x0588/4, 0x00000080); | ||
330 | nv_wo32(dev, ctx, 0x058c/4, 0x30201000); | ||
331 | nv_wo32(dev, ctx, 0x0590/4, 0x70605040); | ||
332 | nv_wo32(dev, ctx, 0x0594/4, 0xb8a89888); | ||
333 | nv_wo32(dev, ctx, 0x0598/4, 0xf8e8d8c8); | ||
334 | nv_wo32(dev, ctx, 0x05ac/4, 0xb0000000); | ||
335 | for (i = 0x0604; i < 0x0644; i += 4) | ||
336 | nv_wo32(dev, ctx, i/4, 0x00010588); | ||
337 | for (i = 0x0644; i < 0x0684; i += 4) | ||
338 | nv_wo32(dev, ctx, i/4, 0x00030303); | ||
339 | for (i = 0x06c4; i < 0x0704; i += 4) | ||
340 | nv_wo32(dev, ctx, i/4, 0x0008aae4); | ||
341 | for (i = 0x0704; i < 0x0744; i += 4) | ||
342 | nv_wo32(dev, ctx, i/4, 0x01012000); | ||
343 | for (i = 0x0744; i < 0x0784; i += 4) | ||
344 | nv_wo32(dev, ctx, i/4, 0x00080008); | ||
345 | nv_wo32(dev, ctx, 0x0860/4, 0x00040000); | ||
346 | nv_wo32(dev, ctx, 0x0864/4, 0x00010000); | ||
347 | for (i = 0x0868; i < 0x0878; i += 4) | ||
348 | nv_wo32(dev, ctx, i/4, 0x00040004); | ||
349 | for (i = 0x1f1c; i <= 0x308c ; i += 16) { | ||
350 | nv_wo32(dev, ctx, i/4 + 0, 0x10700ff9); | ||
351 | nv_wo32(dev, ctx, i/4 + 1, 0x0436086c); | ||
352 | nv_wo32(dev, ctx, i/4 + 2, 0x000c001b); | ||
353 | } | ||
354 | for (i = 0x30bc; i < 0x30cc; i += 4) | ||
355 | nv_wo32(dev, ctx, i/4, 0x0000ffff); | ||
356 | nv_wo32(dev, ctx, 0x3450/4, 0x3f800000); | ||
357 | nv_wo32(dev, ctx, 0x380c/4, 0x3f800000); | ||
358 | nv_wo32(dev, ctx, 0x3820/4, 0x3f800000); | ||
359 | nv_wo32(dev, ctx, 0x384c/4, 0x40000000); | ||
360 | nv_wo32(dev, ctx, 0x3850/4, 0x3f800000); | ||
361 | nv_wo32(dev, ctx, 0x3854/4, 0x3f000000); | ||
362 | nv_wo32(dev, ctx, 0x385c/4, 0x40000000); | ||
363 | nv_wo32(dev, ctx, 0x3860/4, 0x3f800000); | ||
364 | nv_wo32(dev, ctx, 0x3868/4, 0xbf800000); | ||
365 | nv_wo32(dev, ctx, 0x3870/4, 0xbf800000); | ||
366 | } | ||
367 | |||
368 | int | ||
369 | nv20_graph_create_context(struct nouveau_channel *chan) | ||
370 | { | ||
371 | struct drm_device *dev = chan->dev; | ||
372 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
373 | void (*ctx_init)(struct drm_device *, struct nouveau_gpuobj *); | ||
374 | unsigned int ctx_size; | ||
375 | unsigned int idoffs = 0x28/4; | ||
376 | int ret; | ||
377 | |||
378 | switch (dev_priv->chipset) { | ||
379 | case 0x20: | ||
380 | ctx_size = NV20_GRCTX_SIZE; | ||
381 | ctx_init = nv20_graph_context_init; | ||
382 | idoffs = 0; | ||
383 | break; | ||
384 | case 0x25: | ||
385 | case 0x28: | ||
386 | ctx_size = NV25_GRCTX_SIZE; | ||
387 | ctx_init = nv25_graph_context_init; | ||
388 | break; | ||
389 | case 0x2a: | ||
390 | ctx_size = NV2A_GRCTX_SIZE; | ||
391 | ctx_init = nv2a_graph_context_init; | ||
392 | idoffs = 0; | ||
393 | break; | ||
394 | case 0x30: | ||
395 | case 0x31: | ||
396 | ctx_size = NV30_31_GRCTX_SIZE; | ||
397 | ctx_init = nv30_31_graph_context_init; | ||
398 | break; | ||
399 | case 0x34: | ||
400 | ctx_size = NV34_GRCTX_SIZE; | ||
401 | ctx_init = nv34_graph_context_init; | ||
402 | break; | ||
403 | case 0x35: | ||
404 | case 0x36: | ||
405 | ctx_size = NV35_36_GRCTX_SIZE; | ||
406 | ctx_init = nv35_36_graph_context_init; | ||
407 | break; | ||
408 | default: | ||
409 | ctx_size = 0; | ||
410 | ctx_init = nv35_36_graph_context_init; | ||
411 | NV_ERROR(dev, "Please contact the devs if you want your NV%x" | ||
412 | " card to work\n", dev_priv->chipset); | ||
413 | return -ENOSYS; | ||
414 | break; | ||
415 | } | ||
416 | |||
417 | ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, ctx_size, 16, | ||
418 | NVOBJ_FLAG_ZERO_ALLOC, | ||
419 | &chan->ramin_grctx); | ||
420 | if (ret) | ||
421 | return ret; | ||
422 | |||
423 | /* Initialise default context values */ | ||
424 | dev_priv->engine.instmem.prepare_access(dev, true); | ||
425 | ctx_init(dev, chan->ramin_grctx->gpuobj); | ||
426 | |||
427 | /* nv20: nv_wo32(dev, chan->ramin_grctx->gpuobj, 10, chan->id<<24); */ | ||
428 | nv_wo32(dev, chan->ramin_grctx->gpuobj, idoffs, | ||
429 | (chan->id << 24) | 0x1); /* CTX_USER */ | ||
430 | |||
431 | nv_wo32(dev, dev_priv->ctx_table->gpuobj, chan->id, | ||
432 | chan->ramin_grctx->instance >> 4); | ||
433 | |||
434 | dev_priv->engine.instmem.finish_access(dev); | ||
435 | return 0; | ||
436 | } | ||
437 | |||
438 | void | ||
439 | nv20_graph_destroy_context(struct nouveau_channel *chan) | ||
440 | { | ||
441 | struct drm_device *dev = chan->dev; | ||
442 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
443 | |||
444 | if (chan->ramin_grctx) | ||
445 | nouveau_gpuobj_ref_del(dev, &chan->ramin_grctx); | ||
446 | |||
447 | dev_priv->engine.instmem.prepare_access(dev, true); | ||
448 | nv_wo32(dev, dev_priv->ctx_table->gpuobj, chan->id, 0); | ||
449 | dev_priv->engine.instmem.finish_access(dev); | ||
450 | } | ||
451 | |||
452 | int | ||
453 | nv20_graph_load_context(struct nouveau_channel *chan) | ||
454 | { | ||
455 | struct drm_device *dev = chan->dev; | ||
456 | uint32_t inst; | ||
457 | |||
458 | if (!chan->ramin_grctx) | ||
459 | return -EINVAL; | ||
460 | inst = chan->ramin_grctx->instance >> 4; | ||
461 | |||
462 | nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst); | ||
463 | nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_XFER, | ||
464 | NV20_PGRAPH_CHANNEL_CTX_XFER_LOAD); | ||
465 | nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10010100); | ||
466 | |||
467 | nouveau_wait_for_idle(dev); | ||
468 | return 0; | ||
469 | } | ||
470 | |||
471 | int | ||
472 | nv20_graph_unload_context(struct drm_device *dev) | ||
473 | { | ||
474 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
475 | struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; | ||
476 | struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; | ||
477 | struct nouveau_channel *chan; | ||
478 | uint32_t inst, tmp; | ||
479 | |||
480 | chan = pgraph->channel(dev); | ||
481 | if (!chan) | ||
482 | return 0; | ||
483 | inst = chan->ramin_grctx->instance >> 4; | ||
484 | |||
485 | nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst); | ||
486 | nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_XFER, | ||
487 | NV20_PGRAPH_CHANNEL_CTX_XFER_SAVE); | ||
488 | |||
489 | nouveau_wait_for_idle(dev); | ||
490 | |||
491 | nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000000); | ||
492 | tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER) & 0x00ffffff; | ||
493 | tmp |= (pfifo->channels - 1) << 24; | ||
494 | nv_wr32(dev, NV10_PGRAPH_CTX_USER, tmp); | ||
495 | return 0; | ||
496 | } | ||
497 | |||
498 | static void | ||
499 | nv20_graph_rdi(struct drm_device *dev) | ||
500 | { | ||
501 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
502 | int i, writecount = 32; | ||
503 | uint32_t rdi_index = 0x2c80000; | ||
504 | |||
505 | if (dev_priv->chipset == 0x20) { | ||
506 | rdi_index = 0x3d0000; | ||
507 | writecount = 15; | ||
508 | } | ||
509 | |||
510 | nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, rdi_index); | ||
511 | for (i = 0; i < writecount; i++) | ||
512 | nv_wr32(dev, NV10_PGRAPH_RDI_DATA, 0); | ||
513 | |||
514 | nouveau_wait_for_idle(dev); | ||
515 | } | ||
516 | |||
517 | int | ||
518 | nv20_graph_init(struct drm_device *dev) | ||
519 | { | ||
520 | struct drm_nouveau_private *dev_priv = | ||
521 | (struct drm_nouveau_private *)dev->dev_private; | ||
522 | uint32_t tmp, vramsz; | ||
523 | int ret, i; | ||
524 | |||
525 | nv_wr32(dev, NV03_PMC_ENABLE, | ||
526 | nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PGRAPH); | ||
527 | nv_wr32(dev, NV03_PMC_ENABLE, | ||
528 | nv_rd32(dev, NV03_PMC_ENABLE) | NV_PMC_ENABLE_PGRAPH); | ||
529 | |||
530 | if (!dev_priv->ctx_table) { | ||
531 | /* Create Context Pointer Table */ | ||
532 | dev_priv->ctx_table_size = 32 * 4; | ||
533 | ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, | ||
534 | dev_priv->ctx_table_size, 16, | ||
535 | NVOBJ_FLAG_ZERO_ALLOC, | ||
536 | &dev_priv->ctx_table); | ||
537 | if (ret) | ||
538 | return ret; | ||
539 | } | ||
540 | |||
541 | nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_TABLE, | ||
542 | dev_priv->ctx_table->instance >> 4); | ||
543 | |||
544 | nv20_graph_rdi(dev); | ||
545 | |||
546 | nv_wr32(dev, NV03_PGRAPH_INTR , 0xFFFFFFFF); | ||
547 | nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF); | ||
548 | |||
549 | nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0xFFFFFFFF); | ||
550 | nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x00000000); | ||
551 | nv_wr32(dev, NV04_PGRAPH_DEBUG_1, 0x00118700); | ||
552 | nv_wr32(dev, NV04_PGRAPH_DEBUG_3, 0xF3CE0475); /* 0x4 = auto ctx switch */ | ||
553 | nv_wr32(dev, NV10_PGRAPH_DEBUG_4, 0x00000000); | ||
554 | nv_wr32(dev, 0x40009C , 0x00000040); | ||
555 | |||
556 | if (dev_priv->chipset >= 0x25) { | ||
557 | nv_wr32(dev, 0x400890, 0x00080000); | ||
558 | nv_wr32(dev, 0x400610, 0x304B1FB6); | ||
559 | nv_wr32(dev, 0x400B80, 0x18B82880); | ||
560 | nv_wr32(dev, 0x400B84, 0x44000000); | ||
561 | nv_wr32(dev, 0x400098, 0x40000080); | ||
562 | nv_wr32(dev, 0x400B88, 0x000000ff); | ||
563 | } else { | ||
564 | nv_wr32(dev, 0x400880, 0x00080000); /* 0x0008c7df */ | ||
565 | nv_wr32(dev, 0x400094, 0x00000005); | ||
566 | nv_wr32(dev, 0x400B80, 0x45CAA208); /* 0x45eae20e */ | ||
567 | nv_wr32(dev, 0x400B84, 0x24000000); | ||
568 | nv_wr32(dev, 0x400098, 0x00000040); | ||
569 | nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00E00038); | ||
570 | nv_wr32(dev, NV10_PGRAPH_RDI_DATA , 0x00000030); | ||
571 | nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00E10038); | ||
572 | nv_wr32(dev, NV10_PGRAPH_RDI_DATA , 0x00000030); | ||
573 | } | ||
574 | |||
575 | /* copy tile info from PFB */ | ||
576 | for (i = 0; i < NV10_PFB_TILE__SIZE; i++) { | ||
577 | nv_wr32(dev, 0x00400904 + i * 0x10, | ||
578 | nv_rd32(dev, NV10_PFB_TLIMIT(i))); | ||
579 | /* which is NV40_PGRAPH_TLIMIT0(i) ?? */ | ||
580 | nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0030 + i * 4); | ||
581 | nv_wr32(dev, NV10_PGRAPH_RDI_DATA, | ||
582 | nv_rd32(dev, NV10_PFB_TLIMIT(i))); | ||
583 | nv_wr32(dev, 0x00400908 + i * 0x10, | ||
584 | nv_rd32(dev, NV10_PFB_TSIZE(i))); | ||
585 | /* which is NV40_PGRAPH_TSIZE0(i) ?? */ | ||
586 | nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0050 + i * 4); | ||
587 | nv_wr32(dev, NV10_PGRAPH_RDI_DATA, | ||
588 | nv_rd32(dev, NV10_PFB_TSIZE(i))); | ||
589 | nv_wr32(dev, 0x00400900 + i * 0x10, | ||
590 | nv_rd32(dev, NV10_PFB_TILE(i))); | ||
591 | /* which is NV40_PGRAPH_TILE0(i) ?? */ | ||
592 | nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0010 + i * 4); | ||
593 | nv_wr32(dev, NV10_PGRAPH_RDI_DATA, | ||
594 | nv_rd32(dev, NV10_PFB_TILE(i))); | ||
595 | } | ||
596 | for (i = 0; i < 8; i++) { | ||
597 | nv_wr32(dev, 0x400980 + i * 4, nv_rd32(dev, 0x100300 + i * 4)); | ||
598 | nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0090 + i * 4); | ||
599 | nv_wr32(dev, NV10_PGRAPH_RDI_DATA, | ||
600 | nv_rd32(dev, 0x100300 + i * 4)); | ||
601 | } | ||
602 | nv_wr32(dev, 0x4009a0, nv_rd32(dev, 0x100324)); | ||
603 | nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA000C); | ||
604 | nv_wr32(dev, NV10_PGRAPH_RDI_DATA, nv_rd32(dev, 0x100324)); | ||
605 | |||
606 | nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000100); | ||
607 | nv_wr32(dev, NV10_PGRAPH_STATE , 0xFFFFFFFF); | ||
608 | |||
609 | tmp = nv_rd32(dev, NV10_PGRAPH_SURFACE) & 0x0007ff00; | ||
610 | nv_wr32(dev, NV10_PGRAPH_SURFACE, tmp); | ||
611 | tmp = nv_rd32(dev, NV10_PGRAPH_SURFACE) | 0x00020100; | ||
612 | nv_wr32(dev, NV10_PGRAPH_SURFACE, tmp); | ||
613 | |||
614 | /* begin RAM config */ | ||
615 | vramsz = drm_get_resource_len(dev, 0) - 1; | ||
616 | nv_wr32(dev, 0x4009A4, nv_rd32(dev, NV04_PFB_CFG0)); | ||
617 | nv_wr32(dev, 0x4009A8, nv_rd32(dev, NV04_PFB_CFG1)); | ||
618 | nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0000); | ||
619 | nv_wr32(dev, NV10_PGRAPH_RDI_DATA , nv_rd32(dev, NV04_PFB_CFG0)); | ||
620 | nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0004); | ||
621 | nv_wr32(dev, NV10_PGRAPH_RDI_DATA , nv_rd32(dev, NV04_PFB_CFG1)); | ||
622 | nv_wr32(dev, 0x400820, 0); | ||
623 | nv_wr32(dev, 0x400824, 0); | ||
624 | nv_wr32(dev, 0x400864, vramsz - 1); | ||
625 | nv_wr32(dev, 0x400868, vramsz - 1); | ||
626 | |||
627 | /* interesting.. the below overwrites some of the tile setup above.. */ | ||
628 | nv_wr32(dev, 0x400B20, 0x00000000); | ||
629 | nv_wr32(dev, 0x400B04, 0xFFFFFFFF); | ||
630 | |||
631 | nv_wr32(dev, NV03_PGRAPH_ABS_UCLIP_XMIN, 0); | ||
632 | nv_wr32(dev, NV03_PGRAPH_ABS_UCLIP_YMIN, 0); | ||
633 | nv_wr32(dev, NV03_PGRAPH_ABS_UCLIP_XMAX, 0x7fff); | ||
634 | nv_wr32(dev, NV03_PGRAPH_ABS_UCLIP_YMAX, 0x7fff); | ||
635 | |||
636 | return 0; | ||
637 | } | ||
638 | |||
639 | void | ||
640 | nv20_graph_takedown(struct drm_device *dev) | ||
641 | { | ||
642 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
643 | |||
644 | nouveau_gpuobj_ref_del(dev, &dev_priv->ctx_table); | ||
645 | } | ||
646 | |||
647 | int | ||
648 | nv30_graph_init(struct drm_device *dev) | ||
649 | { | ||
650 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
651 | int ret, i; | ||
652 | |||
653 | nv_wr32(dev, NV03_PMC_ENABLE, | ||
654 | nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PGRAPH); | ||
655 | nv_wr32(dev, NV03_PMC_ENABLE, | ||
656 | nv_rd32(dev, NV03_PMC_ENABLE) | NV_PMC_ENABLE_PGRAPH); | ||
657 | |||
658 | if (!dev_priv->ctx_table) { | ||
659 | /* Create Context Pointer Table */ | ||
660 | dev_priv->ctx_table_size = 32 * 4; | ||
661 | ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, | ||
662 | dev_priv->ctx_table_size, 16, | ||
663 | NVOBJ_FLAG_ZERO_ALLOC, | ||
664 | &dev_priv->ctx_table); | ||
665 | if (ret) | ||
666 | return ret; | ||
667 | } | ||
668 | |||
669 | nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_TABLE, | ||
670 | dev_priv->ctx_table->instance >> 4); | ||
671 | |||
672 | nv_wr32(dev, NV03_PGRAPH_INTR , 0xFFFFFFFF); | ||
673 | nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF); | ||
674 | |||
675 | nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0xFFFFFFFF); | ||
676 | nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x00000000); | ||
677 | nv_wr32(dev, NV04_PGRAPH_DEBUG_1, 0x401287c0); | ||
678 | nv_wr32(dev, 0x400890, 0x01b463ff); | ||
679 | nv_wr32(dev, NV04_PGRAPH_DEBUG_3, 0xf2de0475); | ||
680 | nv_wr32(dev, NV10_PGRAPH_DEBUG_4, 0x00008000); | ||
681 | nv_wr32(dev, NV04_PGRAPH_LIMIT_VIOL_PIX, 0xf04bdff6); | ||
682 | nv_wr32(dev, 0x400B80, 0x1003d888); | ||
683 | nv_wr32(dev, 0x400B84, 0x0c000000); | ||
684 | nv_wr32(dev, 0x400098, 0x00000000); | ||
685 | nv_wr32(dev, 0x40009C, 0x0005ad00); | ||
686 | nv_wr32(dev, 0x400B88, 0x62ff00ff); /* suspiciously like PGRAPH_DEBUG_2 */ | ||
687 | nv_wr32(dev, 0x4000a0, 0x00000000); | ||
688 | nv_wr32(dev, 0x4000a4, 0x00000008); | ||
689 | nv_wr32(dev, 0x4008a8, 0xb784a400); | ||
690 | nv_wr32(dev, 0x400ba0, 0x002f8685); | ||
691 | nv_wr32(dev, 0x400ba4, 0x00231f3f); | ||
692 | nv_wr32(dev, 0x4008a4, 0x40000020); | ||
693 | |||
694 | if (dev_priv->chipset == 0x34) { | ||
695 | nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0004); | ||
696 | nv_wr32(dev, NV10_PGRAPH_RDI_DATA , 0x00200201); | ||
697 | nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0008); | ||
698 | nv_wr32(dev, NV10_PGRAPH_RDI_DATA , 0x00000008); | ||
699 | nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0000); | ||
700 | nv_wr32(dev, NV10_PGRAPH_RDI_DATA , 0x00000032); | ||
701 | nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00E00004); | ||
702 | nv_wr32(dev, NV10_PGRAPH_RDI_DATA , 0x00000002); | ||
703 | } | ||
704 | |||
705 | nv_wr32(dev, 0x4000c0, 0x00000016); | ||
706 | |||
707 | /* copy tile info from PFB */ | ||
708 | for (i = 0; i < NV10_PFB_TILE__SIZE; i++) { | ||
709 | nv_wr32(dev, 0x00400904 + i * 0x10, | ||
710 | nv_rd32(dev, NV10_PFB_TLIMIT(i))); | ||
711 | /* which is NV40_PGRAPH_TLIMIT0(i) ?? */ | ||
712 | nv_wr32(dev, 0x00400908 + i * 0x10, | ||
713 | nv_rd32(dev, NV10_PFB_TSIZE(i))); | ||
714 | /* which is NV40_PGRAPH_TSIZE0(i) ?? */ | ||
715 | nv_wr32(dev, 0x00400900 + i * 0x10, | ||
716 | nv_rd32(dev, NV10_PFB_TILE(i))); | ||
717 | /* which is NV40_PGRAPH_TILE0(i) ?? */ | ||
718 | } | ||
719 | |||
720 | nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000100); | ||
721 | nv_wr32(dev, NV10_PGRAPH_STATE , 0xFFFFFFFF); | ||
722 | nv_wr32(dev, 0x0040075c , 0x00000001); | ||
723 | |||
724 | /* begin RAM config */ | ||
725 | /* vramsz = drm_get_resource_len(dev, 0) - 1; */ | ||
726 | nv_wr32(dev, 0x4009A4, nv_rd32(dev, NV04_PFB_CFG0)); | ||
727 | nv_wr32(dev, 0x4009A8, nv_rd32(dev, NV04_PFB_CFG1)); | ||
728 | if (dev_priv->chipset != 0x34) { | ||
729 | nv_wr32(dev, 0x400750, 0x00EA0000); | ||
730 | nv_wr32(dev, 0x400754, nv_rd32(dev, NV04_PFB_CFG0)); | ||
731 | nv_wr32(dev, 0x400750, 0x00EA0004); | ||
732 | nv_wr32(dev, 0x400754, nv_rd32(dev, NV04_PFB_CFG1)); | ||
733 | } | ||
734 | |||
735 | return 0; | ||
736 | } | ||
737 | |||
738 | struct nouveau_pgraph_object_class nv20_graph_grclass[] = { | ||
739 | { 0x0030, false, NULL }, /* null */ | ||
740 | { 0x0039, false, NULL }, /* m2mf */ | ||
741 | { 0x004a, false, NULL }, /* gdirect */ | ||
742 | { 0x009f, false, NULL }, /* imageblit (nv12) */ | ||
743 | { 0x008a, false, NULL }, /* ifc */ | ||
744 | { 0x0089, false, NULL }, /* sifm */ | ||
745 | { 0x0062, false, NULL }, /* surf2d */ | ||
746 | { 0x0043, false, NULL }, /* rop */ | ||
747 | { 0x0012, false, NULL }, /* beta1 */ | ||
748 | { 0x0072, false, NULL }, /* beta4 */ | ||
749 | { 0x0019, false, NULL }, /* cliprect */ | ||
750 | { 0x0044, false, NULL }, /* pattern */ | ||
751 | { 0x009e, false, NULL }, /* swzsurf */ | ||
752 | { 0x0096, false, NULL }, /* celcius */ | ||
753 | { 0x0097, false, NULL }, /* kelvin (nv20) */ | ||
754 | { 0x0597, false, NULL }, /* kelvin (nv25) */ | ||
755 | {} | ||
756 | }; | ||
757 | |||
758 | struct nouveau_pgraph_object_class nv30_graph_grclass[] = { | ||
759 | { 0x0030, false, NULL }, /* null */ | ||
760 | { 0x0039, false, NULL }, /* m2mf */ | ||
761 | { 0x004a, false, NULL }, /* gdirect */ | ||
762 | { 0x009f, false, NULL }, /* imageblit (nv12) */ | ||
763 | { 0x008a, false, NULL }, /* ifc */ | ||
764 | { 0x038a, false, NULL }, /* ifc (nv30) */ | ||
765 | { 0x0089, false, NULL }, /* sifm */ | ||
766 | { 0x0389, false, NULL }, /* sifm (nv30) */ | ||
767 | { 0x0062, false, NULL }, /* surf2d */ | ||
768 | { 0x0362, false, NULL }, /* surf2d (nv30) */ | ||
769 | { 0x0043, false, NULL }, /* rop */ | ||
770 | { 0x0012, false, NULL }, /* beta1 */ | ||
771 | { 0x0072, false, NULL }, /* beta4 */ | ||
772 | { 0x0019, false, NULL }, /* cliprect */ | ||
773 | { 0x0044, false, NULL }, /* pattern */ | ||
774 | { 0x039e, false, NULL }, /* swzsurf */ | ||
775 | { 0x0397, false, NULL }, /* rankine (nv30) */ | ||
776 | { 0x0497, false, NULL }, /* rankine (nv35) */ | ||
777 | { 0x0697, false, NULL }, /* rankine (nv34) */ | ||
778 | {} | ||
779 | }; | ||
780 | |||
diff --git a/drivers/gpu/drm/nouveau/nv40_fb.c b/drivers/gpu/drm/nouveau/nv40_fb.c new file mode 100644 index 000000000000..ca1d27107a8e --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv40_fb.c | |||
@@ -0,0 +1,62 @@ | |||
1 | #include "drmP.h" | ||
2 | #include "drm.h" | ||
3 | #include "nouveau_drv.h" | ||
4 | #include "nouveau_drm.h" | ||
5 | |||
6 | int | ||
7 | nv40_fb_init(struct drm_device *dev) | ||
8 | { | ||
9 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
10 | uint32_t fb_bar_size, tmp; | ||
11 | int num_tiles; | ||
12 | int i; | ||
13 | |||
14 | /* This is strictly a NV4x register (don't know about NV5x). */ | ||
15 | /* The blob sets these to all kinds of values, and they mess up our setup. */ | ||
16 | /* I got value 0x52802 instead. For some cards the blob even sets it back to 0x1. */ | ||
17 | /* Note: the blob doesn't read this value, so i'm pretty sure this is safe for all cards. */ | ||
18 | /* Any idea what this is? */ | ||
19 | nv_wr32(dev, NV40_PFB_UNK_800, 0x1); | ||
20 | |||
21 | switch (dev_priv->chipset) { | ||
22 | case 0x40: | ||
23 | case 0x45: | ||
24 | tmp = nv_rd32(dev, NV10_PFB_CLOSE_PAGE2); | ||
25 | nv_wr32(dev, NV10_PFB_CLOSE_PAGE2, tmp & ~(1 << 15)); | ||
26 | num_tiles = NV10_PFB_TILE__SIZE; | ||
27 | break; | ||
28 | case 0x46: /* G72 */ | ||
29 | case 0x47: /* G70 */ | ||
30 | case 0x49: /* G71 */ | ||
31 | case 0x4b: /* G73 */ | ||
32 | case 0x4c: /* C51 (G7X version) */ | ||
33 | num_tiles = NV40_PFB_TILE__SIZE_1; | ||
34 | break; | ||
35 | default: | ||
36 | num_tiles = NV40_PFB_TILE__SIZE_0; | ||
37 | break; | ||
38 | } | ||
39 | |||
40 | fb_bar_size = drm_get_resource_len(dev, 0) - 1; | ||
41 | switch (dev_priv->chipset) { | ||
42 | case 0x40: | ||
43 | for (i = 0; i < num_tiles; i++) { | ||
44 | nv_wr32(dev, NV10_PFB_TILE(i), 0); | ||
45 | nv_wr32(dev, NV10_PFB_TLIMIT(i), fb_bar_size); | ||
46 | } | ||
47 | break; | ||
48 | default: | ||
49 | for (i = 0; i < num_tiles; i++) { | ||
50 | nv_wr32(dev, NV40_PFB_TILE(i), 0); | ||
51 | nv_wr32(dev, NV40_PFB_TLIMIT(i), fb_bar_size); | ||
52 | } | ||
53 | break; | ||
54 | } | ||
55 | |||
56 | return 0; | ||
57 | } | ||
58 | |||
59 | void | ||
60 | nv40_fb_takedown(struct drm_device *dev) | ||
61 | { | ||
62 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nv40_fifo.c b/drivers/gpu/drm/nouveau/nv40_fifo.c new file mode 100644 index 000000000000..b4f19ccb8b41 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv40_fifo.c | |||
@@ -0,0 +1,314 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007 Ben Skeggs. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include "drmP.h" | ||
28 | #include "nouveau_drv.h" | ||
29 | #include "nouveau_drm.h" | ||
30 | |||
31 | #define NV40_RAMFC(c) (dev_priv->ramfc_offset + ((c) * NV40_RAMFC__SIZE)) | ||
32 | #define NV40_RAMFC__SIZE 128 | ||
33 | |||
34 | int | ||
35 | nv40_fifo_create_context(struct nouveau_channel *chan) | ||
36 | { | ||
37 | struct drm_device *dev = chan->dev; | ||
38 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
39 | uint32_t fc = NV40_RAMFC(chan->id); | ||
40 | int ret; | ||
41 | |||
42 | ret = nouveau_gpuobj_new_fake(dev, NV40_RAMFC(chan->id), ~0, | ||
43 | NV40_RAMFC__SIZE, NVOBJ_FLAG_ZERO_ALLOC | | ||
44 | NVOBJ_FLAG_ZERO_FREE, NULL, &chan->ramfc); | ||
45 | if (ret) | ||
46 | return ret; | ||
47 | |||
48 | dev_priv->engine.instmem.prepare_access(dev, true); | ||
49 | nv_wi32(dev, fc + 0, chan->pushbuf_base); | ||
50 | nv_wi32(dev, fc + 4, chan->pushbuf_base); | ||
51 | nv_wi32(dev, fc + 12, chan->pushbuf->instance >> 4); | ||
52 | nv_wi32(dev, fc + 24, NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES | | ||
53 | NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES | | ||
54 | NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8 | | ||
55 | #ifdef __BIG_ENDIAN | ||
56 | NV_PFIFO_CACHE1_BIG_ENDIAN | | ||
57 | #endif | ||
58 | 0x30000000 /* no idea.. */); | ||
59 | nv_wi32(dev, fc + 56, chan->ramin_grctx->instance >> 4); | ||
60 | nv_wi32(dev, fc + 60, 0x0001FFFF); | ||
61 | dev_priv->engine.instmem.finish_access(dev); | ||
62 | |||
63 | /* enable the fifo dma operation */ | ||
64 | nv_wr32(dev, NV04_PFIFO_MODE, | ||
65 | nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id)); | ||
66 | return 0; | ||
67 | } | ||
68 | |||
69 | void | ||
70 | nv40_fifo_destroy_context(struct nouveau_channel *chan) | ||
71 | { | ||
72 | struct drm_device *dev = chan->dev; | ||
73 | |||
74 | nv_wr32(dev, NV04_PFIFO_MODE, | ||
75 | nv_rd32(dev, NV04_PFIFO_MODE) & ~(1 << chan->id)); | ||
76 | |||
77 | if (chan->ramfc) | ||
78 | nouveau_gpuobj_ref_del(dev, &chan->ramfc); | ||
79 | } | ||
80 | |||
81 | static void | ||
82 | nv40_fifo_do_load_context(struct drm_device *dev, int chid) | ||
83 | { | ||
84 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
85 | uint32_t fc = NV40_RAMFC(chid), tmp, tmp2; | ||
86 | |||
87 | dev_priv->engine.instmem.prepare_access(dev, false); | ||
88 | |||
89 | nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUT, nv_ri32(dev, fc + 0)); | ||
90 | nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_GET, nv_ri32(dev, fc + 4)); | ||
91 | nv_wr32(dev, NV10_PFIFO_CACHE1_REF_CNT, nv_ri32(dev, fc + 8)); | ||
92 | nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_INSTANCE, nv_ri32(dev, fc + 12)); | ||
93 | nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT, nv_ri32(dev, fc + 16)); | ||
94 | nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_STATE, nv_ri32(dev, fc + 20)); | ||
95 | |||
96 | /* No idea what 0x2058 is.. */ | ||
97 | tmp = nv_ri32(dev, fc + 24); | ||
98 | tmp2 = nv_rd32(dev, 0x2058) & 0xFFF; | ||
99 | tmp2 |= (tmp & 0x30000000); | ||
100 | nv_wr32(dev, 0x2058, tmp2); | ||
101 | tmp &= ~0x30000000; | ||
102 | nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_FETCH, tmp); | ||
103 | |||
104 | nv_wr32(dev, NV04_PFIFO_CACHE1_ENGINE, nv_ri32(dev, fc + 28)); | ||
105 | nv_wr32(dev, NV04_PFIFO_CACHE1_PULL1, nv_ri32(dev, fc + 32)); | ||
106 | nv_wr32(dev, NV10_PFIFO_CACHE1_ACQUIRE_VALUE, nv_ri32(dev, fc + 36)); | ||
107 | tmp = nv_ri32(dev, fc + 40); | ||
108 | nv_wr32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP, tmp); | ||
109 | nv_wr32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT, nv_ri32(dev, fc + 44)); | ||
110 | nv_wr32(dev, NV10_PFIFO_CACHE1_SEMAPHORE, nv_ri32(dev, fc + 48)); | ||
111 | nv_wr32(dev, NV10_PFIFO_CACHE1_DMA_SUBROUTINE, nv_ri32(dev, fc + 52)); | ||
112 | nv_wr32(dev, NV40_PFIFO_GRCTX_INSTANCE, nv_ri32(dev, fc + 56)); | ||
113 | |||
114 | /* Don't clobber the TIMEOUT_ENABLED flag when restoring from RAMFC */ | ||
115 | tmp = nv_rd32(dev, NV04_PFIFO_DMA_TIMESLICE) & ~0x1FFFF; | ||
116 | tmp |= nv_ri32(dev, fc + 60) & 0x1FFFF; | ||
117 | nv_wr32(dev, NV04_PFIFO_DMA_TIMESLICE, tmp); | ||
118 | |||
119 | nv_wr32(dev, 0x32e4, nv_ri32(dev, fc + 64)); | ||
120 | /* NVIDIA does this next line twice... */ | ||
121 | nv_wr32(dev, 0x32e8, nv_ri32(dev, fc + 68)); | ||
122 | nv_wr32(dev, 0x2088, nv_ri32(dev, fc + 76)); | ||
123 | nv_wr32(dev, 0x3300, nv_ri32(dev, fc + 80)); | ||
124 | |||
125 | dev_priv->engine.instmem.finish_access(dev); | ||
126 | |||
127 | nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0); | ||
128 | nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, 0); | ||
129 | } | ||
130 | |||
131 | int | ||
132 | nv40_fifo_load_context(struct nouveau_channel *chan) | ||
133 | { | ||
134 | struct drm_device *dev = chan->dev; | ||
135 | uint32_t tmp; | ||
136 | |||
137 | nv40_fifo_do_load_context(dev, chan->id); | ||
138 | |||
139 | /* Set channel active, and in DMA mode */ | ||
140 | nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, | ||
141 | NV40_PFIFO_CACHE1_PUSH1_DMA | chan->id); | ||
142 | nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 1); | ||
143 | |||
144 | /* Reset DMA_CTL_AT_INFO to INVALID */ | ||
145 | tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_CTL) & ~(1 << 31); | ||
146 | nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_CTL, tmp); | ||
147 | |||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | int | ||
152 | nv40_fifo_unload_context(struct drm_device *dev) | ||
153 | { | ||
154 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
155 | struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; | ||
156 | uint32_t fc, tmp; | ||
157 | int chid; | ||
158 | |||
159 | chid = pfifo->channel_id(dev); | ||
160 | if (chid < 0 || chid >= dev_priv->engine.fifo.channels) | ||
161 | return 0; | ||
162 | fc = NV40_RAMFC(chid); | ||
163 | |||
164 | dev_priv->engine.instmem.prepare_access(dev, true); | ||
165 | nv_wi32(dev, fc + 0, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUT)); | ||
166 | nv_wi32(dev, fc + 4, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET)); | ||
167 | nv_wi32(dev, fc + 8, nv_rd32(dev, NV10_PFIFO_CACHE1_REF_CNT)); | ||
168 | nv_wi32(dev, fc + 12, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_INSTANCE)); | ||
169 | nv_wi32(dev, fc + 16, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT)); | ||
170 | nv_wi32(dev, fc + 20, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_STATE)); | ||
171 | tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_FETCH); | ||
172 | tmp |= nv_rd32(dev, 0x2058) & 0x30000000; | ||
173 | nv_wi32(dev, fc + 24, tmp); | ||
174 | nv_wi32(dev, fc + 28, nv_rd32(dev, NV04_PFIFO_CACHE1_ENGINE)); | ||
175 | nv_wi32(dev, fc + 32, nv_rd32(dev, NV04_PFIFO_CACHE1_PULL1)); | ||
176 | nv_wi32(dev, fc + 36, nv_rd32(dev, NV10_PFIFO_CACHE1_ACQUIRE_VALUE)); | ||
177 | tmp = nv_rd32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP); | ||
178 | nv_wi32(dev, fc + 40, tmp); | ||
179 | nv_wi32(dev, fc + 44, nv_rd32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT)); | ||
180 | nv_wi32(dev, fc + 48, nv_rd32(dev, NV10_PFIFO_CACHE1_SEMAPHORE)); | ||
181 | /* NVIDIA read 0x3228 first, then write DMA_GET here.. maybe something | ||
182 | * more involved depending on the value of 0x3228? | ||
183 | */ | ||
184 | nv_wi32(dev, fc + 52, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET)); | ||
185 | nv_wi32(dev, fc + 56, nv_rd32(dev, NV40_PFIFO_GRCTX_INSTANCE)); | ||
186 | nv_wi32(dev, fc + 60, nv_rd32(dev, NV04_PFIFO_DMA_TIMESLICE) & 0x1ffff); | ||
187 | /* No idea what the below is for exactly, ripped from a mmio-trace */ | ||
188 | nv_wi32(dev, fc + 64, nv_rd32(dev, NV40_PFIFO_UNK32E4)); | ||
189 | /* NVIDIA do this next line twice.. bug? */ | ||
190 | nv_wi32(dev, fc + 68, nv_rd32(dev, 0x32e8)); | ||
191 | nv_wi32(dev, fc + 76, nv_rd32(dev, 0x2088)); | ||
192 | nv_wi32(dev, fc + 80, nv_rd32(dev, 0x3300)); | ||
193 | #if 0 /* no real idea which is PUT/GET in UNK_48.. */ | ||
194 | tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_GET); | ||
195 | tmp |= (nv_rd32(dev, NV04_PFIFO_CACHE1_PUT) << 16); | ||
196 | nv_wi32(dev, fc + 72, tmp); | ||
197 | #endif | ||
198 | dev_priv->engine.instmem.finish_access(dev); | ||
199 | |||
200 | nv40_fifo_do_load_context(dev, pfifo->channels - 1); | ||
201 | nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, | ||
202 | NV40_PFIFO_CACHE1_PUSH1_DMA | (pfifo->channels - 1)); | ||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | static void | ||
207 | nv40_fifo_init_reset(struct drm_device *dev) | ||
208 | { | ||
209 | int i; | ||
210 | |||
211 | nv_wr32(dev, NV03_PMC_ENABLE, | ||
212 | nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PFIFO); | ||
213 | nv_wr32(dev, NV03_PMC_ENABLE, | ||
214 | nv_rd32(dev, NV03_PMC_ENABLE) | NV_PMC_ENABLE_PFIFO); | ||
215 | |||
216 | nv_wr32(dev, 0x003224, 0x000f0078); | ||
217 | nv_wr32(dev, 0x003210, 0x00000000); | ||
218 | nv_wr32(dev, 0x003270, 0x00000000); | ||
219 | nv_wr32(dev, 0x003240, 0x00000000); | ||
220 | nv_wr32(dev, 0x003244, 0x00000000); | ||
221 | nv_wr32(dev, 0x003258, 0x00000000); | ||
222 | nv_wr32(dev, 0x002504, 0x00000000); | ||
223 | for (i = 0; i < 16; i++) | ||
224 | nv_wr32(dev, 0x002510 + (i * 4), 0x00000000); | ||
225 | nv_wr32(dev, 0x00250c, 0x0000ffff); | ||
226 | nv_wr32(dev, 0x002048, 0x00000000); | ||
227 | nv_wr32(dev, 0x003228, 0x00000000); | ||
228 | nv_wr32(dev, 0x0032e8, 0x00000000); | ||
229 | nv_wr32(dev, 0x002410, 0x00000000); | ||
230 | nv_wr32(dev, 0x002420, 0x00000000); | ||
231 | nv_wr32(dev, 0x002058, 0x00000001); | ||
232 | nv_wr32(dev, 0x00221c, 0x00000000); | ||
233 | /* something with 0x2084, read/modify/write, no change */ | ||
234 | nv_wr32(dev, 0x002040, 0x000000ff); | ||
235 | nv_wr32(dev, 0x002500, 0x00000000); | ||
236 | nv_wr32(dev, 0x003200, 0x00000000); | ||
237 | |||
238 | nv_wr32(dev, NV04_PFIFO_DMA_TIMESLICE, 0x2101ffff); | ||
239 | } | ||
240 | |||
241 | static void | ||
242 | nv40_fifo_init_ramxx(struct drm_device *dev) | ||
243 | { | ||
244 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
245 | |||
246 | nv_wr32(dev, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ | | ||
247 | ((dev_priv->ramht_bits - 9) << 16) | | ||
248 | (dev_priv->ramht_offset >> 8)); | ||
249 | nv_wr32(dev, NV03_PFIFO_RAMRO, dev_priv->ramro_offset>>8); | ||
250 | |||
251 | switch (dev_priv->chipset) { | ||
252 | case 0x47: | ||
253 | case 0x49: | ||
254 | case 0x4b: | ||
255 | nv_wr32(dev, 0x2230, 1); | ||
256 | break; | ||
257 | default: | ||
258 | break; | ||
259 | } | ||
260 | |||
261 | switch (dev_priv->chipset) { | ||
262 | case 0x40: | ||
263 | case 0x41: | ||
264 | case 0x42: | ||
265 | case 0x43: | ||
266 | case 0x45: | ||
267 | case 0x47: | ||
268 | case 0x48: | ||
269 | case 0x49: | ||
270 | case 0x4b: | ||
271 | nv_wr32(dev, NV40_PFIFO_RAMFC, 0x30002); | ||
272 | break; | ||
273 | default: | ||
274 | nv_wr32(dev, 0x2230, 0); | ||
275 | nv_wr32(dev, NV40_PFIFO_RAMFC, | ||
276 | ((nouveau_mem_fb_amount(dev) - 512 * 1024 + | ||
277 | dev_priv->ramfc_offset) >> 16) | (3 << 16)); | ||
278 | break; | ||
279 | } | ||
280 | } | ||
281 | |||
282 | static void | ||
283 | nv40_fifo_init_intr(struct drm_device *dev) | ||
284 | { | ||
285 | nv_wr32(dev, 0x002100, 0xffffffff); | ||
286 | nv_wr32(dev, 0x002140, 0xffffffff); | ||
287 | } | ||
288 | |||
289 | int | ||
290 | nv40_fifo_init(struct drm_device *dev) | ||
291 | { | ||
292 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
293 | struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; | ||
294 | int i; | ||
295 | |||
296 | nv40_fifo_init_reset(dev); | ||
297 | nv40_fifo_init_ramxx(dev); | ||
298 | |||
299 | nv40_fifo_do_load_context(dev, pfifo->channels - 1); | ||
300 | nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, pfifo->channels - 1); | ||
301 | |||
302 | nv40_fifo_init_intr(dev); | ||
303 | pfifo->enable(dev); | ||
304 | pfifo->reassign(dev, true); | ||
305 | |||
306 | for (i = 0; i < dev_priv->engine.fifo.channels; i++) { | ||
307 | if (dev_priv->fifos[i]) { | ||
308 | uint32_t mode = nv_rd32(dev, NV04_PFIFO_MODE); | ||
309 | nv_wr32(dev, NV04_PFIFO_MODE, mode | (1 << i)); | ||
310 | } | ||
311 | } | ||
312 | |||
313 | return 0; | ||
314 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nv40_graph.c b/drivers/gpu/drm/nouveau/nv40_graph.c new file mode 100644 index 000000000000..d3e0a2a6acf8 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv40_graph.c | |||
@@ -0,0 +1,560 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007 Ben Skeggs. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include <linux/firmware.h> | ||
28 | |||
29 | #include "drmP.h" | ||
30 | #include "drm.h" | ||
31 | #include "nouveau_drv.h" | ||
32 | |||
33 | MODULE_FIRMWARE("nouveau/nv40.ctxprog"); | ||
34 | MODULE_FIRMWARE("nouveau/nv40.ctxvals"); | ||
35 | MODULE_FIRMWARE("nouveau/nv41.ctxprog"); | ||
36 | MODULE_FIRMWARE("nouveau/nv41.ctxvals"); | ||
37 | MODULE_FIRMWARE("nouveau/nv42.ctxprog"); | ||
38 | MODULE_FIRMWARE("nouveau/nv42.ctxvals"); | ||
39 | MODULE_FIRMWARE("nouveau/nv43.ctxprog"); | ||
40 | MODULE_FIRMWARE("nouveau/nv43.ctxvals"); | ||
41 | MODULE_FIRMWARE("nouveau/nv44.ctxprog"); | ||
42 | MODULE_FIRMWARE("nouveau/nv44.ctxvals"); | ||
43 | MODULE_FIRMWARE("nouveau/nv46.ctxprog"); | ||
44 | MODULE_FIRMWARE("nouveau/nv46.ctxvals"); | ||
45 | MODULE_FIRMWARE("nouveau/nv47.ctxprog"); | ||
46 | MODULE_FIRMWARE("nouveau/nv47.ctxvals"); | ||
47 | MODULE_FIRMWARE("nouveau/nv49.ctxprog"); | ||
48 | MODULE_FIRMWARE("nouveau/nv49.ctxvals"); | ||
49 | MODULE_FIRMWARE("nouveau/nv4a.ctxprog"); | ||
50 | MODULE_FIRMWARE("nouveau/nv4a.ctxvals"); | ||
51 | MODULE_FIRMWARE("nouveau/nv4b.ctxprog"); | ||
52 | MODULE_FIRMWARE("nouveau/nv4b.ctxvals"); | ||
53 | MODULE_FIRMWARE("nouveau/nv4c.ctxprog"); | ||
54 | MODULE_FIRMWARE("nouveau/nv4c.ctxvals"); | ||
55 | MODULE_FIRMWARE("nouveau/nv4e.ctxprog"); | ||
56 | MODULE_FIRMWARE("nouveau/nv4e.ctxvals"); | ||
57 | |||
58 | struct nouveau_channel * | ||
59 | nv40_graph_channel(struct drm_device *dev) | ||
60 | { | ||
61 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
62 | uint32_t inst; | ||
63 | int i; | ||
64 | |||
65 | inst = nv_rd32(dev, NV40_PGRAPH_CTXCTL_CUR); | ||
66 | if (!(inst & NV40_PGRAPH_CTXCTL_CUR_LOADED)) | ||
67 | return NULL; | ||
68 | inst = (inst & NV40_PGRAPH_CTXCTL_CUR_INSTANCE) << 4; | ||
69 | |||
70 | for (i = 0; i < dev_priv->engine.fifo.channels; i++) { | ||
71 | struct nouveau_channel *chan = dev_priv->fifos[i]; | ||
72 | |||
73 | if (chan && chan->ramin_grctx && | ||
74 | chan->ramin_grctx->instance == inst) | ||
75 | return chan; | ||
76 | } | ||
77 | |||
78 | return NULL; | ||
79 | } | ||
80 | |||
81 | int | ||
82 | nv40_graph_create_context(struct nouveau_channel *chan) | ||
83 | { | ||
84 | struct drm_device *dev = chan->dev; | ||
85 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
86 | struct nouveau_gpuobj *ctx; | ||
87 | int ret; | ||
88 | |||
89 | /* Allocate a 175KiB block of PRAMIN to store the context. This | ||
90 | * is massive overkill for a lot of chipsets, but it should be safe | ||
91 | * until we're able to implement this properly (will happen at more | ||
92 | * or less the same time we're able to write our own context programs. | ||
93 | */ | ||
94 | ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, 175*1024, 16, | ||
95 | NVOBJ_FLAG_ZERO_ALLOC, | ||
96 | &chan->ramin_grctx); | ||
97 | if (ret) | ||
98 | return ret; | ||
99 | ctx = chan->ramin_grctx->gpuobj; | ||
100 | |||
101 | /* Initialise default context values */ | ||
102 | dev_priv->engine.instmem.prepare_access(dev, true); | ||
103 | nv40_grctx_vals_load(dev, ctx); | ||
104 | nv_wo32(dev, ctx, 0, ctx->im_pramin->start); | ||
105 | dev_priv->engine.instmem.finish_access(dev); | ||
106 | |||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | void | ||
111 | nv40_graph_destroy_context(struct nouveau_channel *chan) | ||
112 | { | ||
113 | nouveau_gpuobj_ref_del(chan->dev, &chan->ramin_grctx); | ||
114 | } | ||
115 | |||
116 | static int | ||
117 | nv40_graph_transfer_context(struct drm_device *dev, uint32_t inst, int save) | ||
118 | { | ||
119 | uint32_t old_cp, tv = 1000, tmp; | ||
120 | int i; | ||
121 | |||
122 | old_cp = nv_rd32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER); | ||
123 | nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst); | ||
124 | |||
125 | tmp = nv_rd32(dev, NV40_PGRAPH_CTXCTL_0310); | ||
126 | tmp |= save ? NV40_PGRAPH_CTXCTL_0310_XFER_SAVE : | ||
127 | NV40_PGRAPH_CTXCTL_0310_XFER_LOAD; | ||
128 | nv_wr32(dev, NV40_PGRAPH_CTXCTL_0310, tmp); | ||
129 | |||
130 | tmp = nv_rd32(dev, NV40_PGRAPH_CTXCTL_0304); | ||
131 | tmp |= NV40_PGRAPH_CTXCTL_0304_XFER_CTX; | ||
132 | nv_wr32(dev, NV40_PGRAPH_CTXCTL_0304, tmp); | ||
133 | |||
134 | nouveau_wait_for_idle(dev); | ||
135 | |||
136 | for (i = 0; i < tv; i++) { | ||
137 | if (nv_rd32(dev, NV40_PGRAPH_CTXCTL_030C) == 0) | ||
138 | break; | ||
139 | } | ||
140 | |||
141 | nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, old_cp); | ||
142 | |||
143 | if (i == tv) { | ||
144 | uint32_t ucstat = nv_rd32(dev, NV40_PGRAPH_CTXCTL_UCODE_STAT); | ||
145 | NV_ERROR(dev, "Failed: Instance=0x%08x Save=%d\n", inst, save); | ||
146 | NV_ERROR(dev, "IP: 0x%02x, Opcode: 0x%08x\n", | ||
147 | ucstat >> NV40_PGRAPH_CTXCTL_UCODE_STAT_IP_SHIFT, | ||
148 | ucstat & NV40_PGRAPH_CTXCTL_UCODE_STAT_OP_MASK); | ||
149 | NV_ERROR(dev, "0x40030C = 0x%08x\n", | ||
150 | nv_rd32(dev, NV40_PGRAPH_CTXCTL_030C)); | ||
151 | return -EBUSY; | ||
152 | } | ||
153 | |||
154 | return 0; | ||
155 | } | ||
156 | |||
157 | /* Restore the context for a specific channel into PGRAPH */ | ||
158 | int | ||
159 | nv40_graph_load_context(struct nouveau_channel *chan) | ||
160 | { | ||
161 | struct drm_device *dev = chan->dev; | ||
162 | uint32_t inst; | ||
163 | int ret; | ||
164 | |||
165 | if (!chan->ramin_grctx) | ||
166 | return -EINVAL; | ||
167 | inst = chan->ramin_grctx->instance >> 4; | ||
168 | |||
169 | ret = nv40_graph_transfer_context(dev, inst, 0); | ||
170 | if (ret) | ||
171 | return ret; | ||
172 | |||
173 | /* 0x40032C, no idea of it's exact function. Could simply be a | ||
174 | * record of the currently active PGRAPH context. It's currently | ||
175 | * unknown as to what bit 24 does. The nv ddx has it set, so we will | ||
176 | * set it here too. | ||
177 | */ | ||
178 | nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst); | ||
179 | nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, | ||
180 | (inst & NV40_PGRAPH_CTXCTL_CUR_INSTANCE) | | ||
181 | NV40_PGRAPH_CTXCTL_CUR_LOADED); | ||
182 | /* 0x32E0 records the instance address of the active FIFO's PGRAPH | ||
183 | * context. If at any time this doesn't match 0x40032C, you will | ||
184 | * recieve PGRAPH_INTR_CONTEXT_SWITCH | ||
185 | */ | ||
186 | nv_wr32(dev, NV40_PFIFO_GRCTX_INSTANCE, inst); | ||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | int | ||
191 | nv40_graph_unload_context(struct drm_device *dev) | ||
192 | { | ||
193 | uint32_t inst; | ||
194 | int ret; | ||
195 | |||
196 | inst = nv_rd32(dev, NV40_PGRAPH_CTXCTL_CUR); | ||
197 | if (!(inst & NV40_PGRAPH_CTXCTL_CUR_LOADED)) | ||
198 | return 0; | ||
199 | inst &= NV40_PGRAPH_CTXCTL_CUR_INSTANCE; | ||
200 | |||
201 | ret = nv40_graph_transfer_context(dev, inst, 1); | ||
202 | |||
203 | nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, inst); | ||
204 | return ret; | ||
205 | } | ||
206 | |||
207 | struct nouveau_ctxprog { | ||
208 | uint32_t signature; | ||
209 | uint8_t version; | ||
210 | uint16_t length; | ||
211 | uint32_t data[]; | ||
212 | } __attribute__ ((packed)); | ||
213 | |||
214 | struct nouveau_ctxvals { | ||
215 | uint32_t signature; | ||
216 | uint8_t version; | ||
217 | uint32_t length; | ||
218 | struct { | ||
219 | uint32_t offset; | ||
220 | uint32_t value; | ||
221 | } data[]; | ||
222 | } __attribute__ ((packed)); | ||
223 | |||
224 | int | ||
225 | nv40_grctx_init(struct drm_device *dev) | ||
226 | { | ||
227 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
228 | struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; | ||
229 | const int chipset = dev_priv->chipset; | ||
230 | const struct firmware *fw; | ||
231 | const struct nouveau_ctxprog *cp; | ||
232 | const struct nouveau_ctxvals *cv; | ||
233 | char name[32]; | ||
234 | int ret, i; | ||
235 | |||
236 | pgraph->accel_blocked = true; | ||
237 | |||
238 | if (!pgraph->ctxprog) { | ||
239 | sprintf(name, "nouveau/nv%02x.ctxprog", chipset); | ||
240 | ret = request_firmware(&fw, name, &dev->pdev->dev); | ||
241 | if (ret) { | ||
242 | NV_ERROR(dev, "No ctxprog for NV%02x\n", chipset); | ||
243 | return ret; | ||
244 | } | ||
245 | |||
246 | pgraph->ctxprog = kmalloc(fw->size, GFP_KERNEL); | ||
247 | if (!pgraph->ctxprog) { | ||
248 | NV_ERROR(dev, "OOM copying ctxprog\n"); | ||
249 | release_firmware(fw); | ||
250 | return -ENOMEM; | ||
251 | } | ||
252 | memcpy(pgraph->ctxprog, fw->data, fw->size); | ||
253 | |||
254 | cp = pgraph->ctxprog; | ||
255 | if (cp->signature != 0x5043564e || cp->version != 0 || | ||
256 | cp->length != ((fw->size - 7) / 4)) { | ||
257 | NV_ERROR(dev, "ctxprog invalid\n"); | ||
258 | release_firmware(fw); | ||
259 | nv40_grctx_fini(dev); | ||
260 | return -EINVAL; | ||
261 | } | ||
262 | release_firmware(fw); | ||
263 | } | ||
264 | |||
265 | if (!pgraph->ctxvals) { | ||
266 | sprintf(name, "nouveau/nv%02x.ctxvals", chipset); | ||
267 | ret = request_firmware(&fw, name, &dev->pdev->dev); | ||
268 | if (ret) { | ||
269 | NV_ERROR(dev, "No ctxvals for NV%02x\n", chipset); | ||
270 | nv40_grctx_fini(dev); | ||
271 | return ret; | ||
272 | } | ||
273 | |||
274 | pgraph->ctxvals = kmalloc(fw->size, GFP_KERNEL); | ||
275 | if (!pgraph->ctxprog) { | ||
276 | NV_ERROR(dev, "OOM copying ctxprog\n"); | ||
277 | release_firmware(fw); | ||
278 | nv40_grctx_fini(dev); | ||
279 | return -ENOMEM; | ||
280 | } | ||
281 | memcpy(pgraph->ctxvals, fw->data, fw->size); | ||
282 | |||
283 | cv = (void *)pgraph->ctxvals; | ||
284 | if (cv->signature != 0x5643564e || cv->version != 0 || | ||
285 | cv->length != ((fw->size - 9) / 8)) { | ||
286 | NV_ERROR(dev, "ctxvals invalid\n"); | ||
287 | release_firmware(fw); | ||
288 | nv40_grctx_fini(dev); | ||
289 | return -EINVAL; | ||
290 | } | ||
291 | release_firmware(fw); | ||
292 | } | ||
293 | |||
294 | cp = pgraph->ctxprog; | ||
295 | |||
296 | nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_INDEX, 0); | ||
297 | for (i = 0; i < cp->length; i++) | ||
298 | nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_DATA, cp->data[i]); | ||
299 | |||
300 | pgraph->accel_blocked = false; | ||
301 | return 0; | ||
302 | } | ||
303 | |||
304 | void | ||
305 | nv40_grctx_fini(struct drm_device *dev) | ||
306 | { | ||
307 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
308 | struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; | ||
309 | |||
310 | if (pgraph->ctxprog) { | ||
311 | kfree(pgraph->ctxprog); | ||
312 | pgraph->ctxprog = NULL; | ||
313 | } | ||
314 | |||
315 | if (pgraph->ctxvals) { | ||
316 | kfree(pgraph->ctxprog); | ||
317 | pgraph->ctxvals = NULL; | ||
318 | } | ||
319 | } | ||
320 | |||
321 | void | ||
322 | nv40_grctx_vals_load(struct drm_device *dev, struct nouveau_gpuobj *ctx) | ||
323 | { | ||
324 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
325 | struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; | ||
326 | struct nouveau_ctxvals *cv = pgraph->ctxvals; | ||
327 | int i; | ||
328 | |||
329 | if (!cv) | ||
330 | return; | ||
331 | |||
332 | for (i = 0; i < cv->length; i++) | ||
333 | nv_wo32(dev, ctx, cv->data[i].offset, cv->data[i].value); | ||
334 | } | ||
335 | |||
336 | /* | ||
337 | * G70 0x47 | ||
338 | * G71 0x49 | ||
339 | * NV45 0x48 | ||
340 | * G72[M] 0x46 | ||
341 | * G73 0x4b | ||
342 | * C51_G7X 0x4c | ||
343 | * C51 0x4e | ||
344 | */ | ||
345 | int | ||
346 | nv40_graph_init(struct drm_device *dev) | ||
347 | { | ||
348 | struct drm_nouveau_private *dev_priv = | ||
349 | (struct drm_nouveau_private *)dev->dev_private; | ||
350 | uint32_t vramsz, tmp; | ||
351 | int i, j; | ||
352 | |||
353 | nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) & | ||
354 | ~NV_PMC_ENABLE_PGRAPH); | ||
355 | nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) | | ||
356 | NV_PMC_ENABLE_PGRAPH); | ||
357 | |||
358 | nv40_grctx_init(dev); | ||
359 | |||
360 | /* No context present currently */ | ||
361 | nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, 0x00000000); | ||
362 | |||
363 | nv_wr32(dev, NV03_PGRAPH_INTR , 0xFFFFFFFF); | ||
364 | nv_wr32(dev, NV40_PGRAPH_INTR_EN, 0xFFFFFFFF); | ||
365 | |||
366 | nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0xFFFFFFFF); | ||
367 | nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x00000000); | ||
368 | nv_wr32(dev, NV04_PGRAPH_DEBUG_1, 0x401287c0); | ||
369 | nv_wr32(dev, NV04_PGRAPH_DEBUG_3, 0xe0de8055); | ||
370 | nv_wr32(dev, NV10_PGRAPH_DEBUG_4, 0x00008000); | ||
371 | nv_wr32(dev, NV04_PGRAPH_LIMIT_VIOL_PIX, 0x00be3c5f); | ||
372 | |||
373 | nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10010100); | ||
374 | nv_wr32(dev, NV10_PGRAPH_STATE , 0xFFFFFFFF); | ||
375 | |||
376 | j = nv_rd32(dev, 0x1540) & 0xff; | ||
377 | if (j) { | ||
378 | for (i = 0; !(j & 1); j >>= 1, i++) | ||
379 | ; | ||
380 | nv_wr32(dev, 0x405000, i); | ||
381 | } | ||
382 | |||
383 | if (dev_priv->chipset == 0x40) { | ||
384 | nv_wr32(dev, 0x4009b0, 0x83280fff); | ||
385 | nv_wr32(dev, 0x4009b4, 0x000000a0); | ||
386 | } else { | ||
387 | nv_wr32(dev, 0x400820, 0x83280eff); | ||
388 | nv_wr32(dev, 0x400824, 0x000000a0); | ||
389 | } | ||
390 | |||
391 | switch (dev_priv->chipset) { | ||
392 | case 0x40: | ||
393 | case 0x45: | ||
394 | nv_wr32(dev, 0x4009b8, 0x0078e366); | ||
395 | nv_wr32(dev, 0x4009bc, 0x0000014c); | ||
396 | break; | ||
397 | case 0x41: | ||
398 | case 0x42: /* pciid also 0x00Cx */ | ||
399 | /* case 0x0120: XXX (pciid) */ | ||
400 | nv_wr32(dev, 0x400828, 0x007596ff); | ||
401 | nv_wr32(dev, 0x40082c, 0x00000108); | ||
402 | break; | ||
403 | case 0x43: | ||
404 | nv_wr32(dev, 0x400828, 0x0072cb77); | ||
405 | nv_wr32(dev, 0x40082c, 0x00000108); | ||
406 | break; | ||
407 | case 0x44: | ||
408 | case 0x46: /* G72 */ | ||
409 | case 0x4a: | ||
410 | case 0x4c: /* G7x-based C51 */ | ||
411 | case 0x4e: | ||
412 | nv_wr32(dev, 0x400860, 0); | ||
413 | nv_wr32(dev, 0x400864, 0); | ||
414 | break; | ||
415 | case 0x47: /* G70 */ | ||
416 | case 0x49: /* G71 */ | ||
417 | case 0x4b: /* G73 */ | ||
418 | nv_wr32(dev, 0x400828, 0x07830610); | ||
419 | nv_wr32(dev, 0x40082c, 0x0000016A); | ||
420 | break; | ||
421 | default: | ||
422 | break; | ||
423 | } | ||
424 | |||
425 | nv_wr32(dev, 0x400b38, 0x2ffff800); | ||
426 | nv_wr32(dev, 0x400b3c, 0x00006000); | ||
427 | |||
428 | /* copy tile info from PFB */ | ||
429 | switch (dev_priv->chipset) { | ||
430 | case 0x40: /* vanilla NV40 */ | ||
431 | for (i = 0; i < NV10_PFB_TILE__SIZE; i++) { | ||
432 | tmp = nv_rd32(dev, NV10_PFB_TILE(i)); | ||
433 | nv_wr32(dev, NV40_PGRAPH_TILE0(i), tmp); | ||
434 | nv_wr32(dev, NV40_PGRAPH_TILE1(i), tmp); | ||
435 | tmp = nv_rd32(dev, NV10_PFB_TLIMIT(i)); | ||
436 | nv_wr32(dev, NV40_PGRAPH_TLIMIT0(i), tmp); | ||
437 | nv_wr32(dev, NV40_PGRAPH_TLIMIT1(i), tmp); | ||
438 | tmp = nv_rd32(dev, NV10_PFB_TSIZE(i)); | ||
439 | nv_wr32(dev, NV40_PGRAPH_TSIZE0(i), tmp); | ||
440 | nv_wr32(dev, NV40_PGRAPH_TSIZE1(i), tmp); | ||
441 | tmp = nv_rd32(dev, NV10_PFB_TSTATUS(i)); | ||
442 | nv_wr32(dev, NV40_PGRAPH_TSTATUS0(i), tmp); | ||
443 | nv_wr32(dev, NV40_PGRAPH_TSTATUS1(i), tmp); | ||
444 | } | ||
445 | break; | ||
446 | case 0x44: | ||
447 | case 0x4a: | ||
448 | case 0x4e: /* NV44-based cores don't have 0x406900? */ | ||
449 | for (i = 0; i < NV40_PFB_TILE__SIZE_0; i++) { | ||
450 | tmp = nv_rd32(dev, NV40_PFB_TILE(i)); | ||
451 | nv_wr32(dev, NV40_PGRAPH_TILE0(i), tmp); | ||
452 | tmp = nv_rd32(dev, NV40_PFB_TLIMIT(i)); | ||
453 | nv_wr32(dev, NV40_PGRAPH_TLIMIT0(i), tmp); | ||
454 | tmp = nv_rd32(dev, NV40_PFB_TSIZE(i)); | ||
455 | nv_wr32(dev, NV40_PGRAPH_TSIZE0(i), tmp); | ||
456 | tmp = nv_rd32(dev, NV40_PFB_TSTATUS(i)); | ||
457 | nv_wr32(dev, NV40_PGRAPH_TSTATUS0(i), tmp); | ||
458 | } | ||
459 | break; | ||
460 | case 0x46: | ||
461 | case 0x47: | ||
462 | case 0x49: | ||
463 | case 0x4b: /* G7X-based cores */ | ||
464 | for (i = 0; i < NV40_PFB_TILE__SIZE_1; i++) { | ||
465 | tmp = nv_rd32(dev, NV40_PFB_TILE(i)); | ||
466 | nv_wr32(dev, NV47_PGRAPH_TILE0(i), tmp); | ||
467 | nv_wr32(dev, NV40_PGRAPH_TILE1(i), tmp); | ||
468 | tmp = nv_rd32(dev, NV40_PFB_TLIMIT(i)); | ||
469 | nv_wr32(dev, NV47_PGRAPH_TLIMIT0(i), tmp); | ||
470 | nv_wr32(dev, NV40_PGRAPH_TLIMIT1(i), tmp); | ||
471 | tmp = nv_rd32(dev, NV40_PFB_TSIZE(i)); | ||
472 | nv_wr32(dev, NV47_PGRAPH_TSIZE0(i), tmp); | ||
473 | nv_wr32(dev, NV40_PGRAPH_TSIZE1(i), tmp); | ||
474 | tmp = nv_rd32(dev, NV40_PFB_TSTATUS(i)); | ||
475 | nv_wr32(dev, NV47_PGRAPH_TSTATUS0(i), tmp); | ||
476 | nv_wr32(dev, NV40_PGRAPH_TSTATUS1(i), tmp); | ||
477 | } | ||
478 | break; | ||
479 | default: /* everything else */ | ||
480 | for (i = 0; i < NV40_PFB_TILE__SIZE_0; i++) { | ||
481 | tmp = nv_rd32(dev, NV40_PFB_TILE(i)); | ||
482 | nv_wr32(dev, NV40_PGRAPH_TILE0(i), tmp); | ||
483 | nv_wr32(dev, NV40_PGRAPH_TILE1(i), tmp); | ||
484 | tmp = nv_rd32(dev, NV40_PFB_TLIMIT(i)); | ||
485 | nv_wr32(dev, NV40_PGRAPH_TLIMIT0(i), tmp); | ||
486 | nv_wr32(dev, NV40_PGRAPH_TLIMIT1(i), tmp); | ||
487 | tmp = nv_rd32(dev, NV40_PFB_TSIZE(i)); | ||
488 | nv_wr32(dev, NV40_PGRAPH_TSIZE0(i), tmp); | ||
489 | nv_wr32(dev, NV40_PGRAPH_TSIZE1(i), tmp); | ||
490 | tmp = nv_rd32(dev, NV40_PFB_TSTATUS(i)); | ||
491 | nv_wr32(dev, NV40_PGRAPH_TSTATUS0(i), tmp); | ||
492 | nv_wr32(dev, NV40_PGRAPH_TSTATUS1(i), tmp); | ||
493 | } | ||
494 | break; | ||
495 | } | ||
496 | |||
497 | /* begin RAM config */ | ||
498 | vramsz = drm_get_resource_len(dev, 0) - 1; | ||
499 | switch (dev_priv->chipset) { | ||
500 | case 0x40: | ||
501 | nv_wr32(dev, 0x4009A4, nv_rd32(dev, NV04_PFB_CFG0)); | ||
502 | nv_wr32(dev, 0x4009A8, nv_rd32(dev, NV04_PFB_CFG1)); | ||
503 | nv_wr32(dev, 0x4069A4, nv_rd32(dev, NV04_PFB_CFG0)); | ||
504 | nv_wr32(dev, 0x4069A8, nv_rd32(dev, NV04_PFB_CFG1)); | ||
505 | nv_wr32(dev, 0x400820, 0); | ||
506 | nv_wr32(dev, 0x400824, 0); | ||
507 | nv_wr32(dev, 0x400864, vramsz); | ||
508 | nv_wr32(dev, 0x400868, vramsz); | ||
509 | break; | ||
510 | default: | ||
511 | switch (dev_priv->chipset) { | ||
512 | case 0x46: | ||
513 | case 0x47: | ||
514 | case 0x49: | ||
515 | case 0x4b: | ||
516 | nv_wr32(dev, 0x400DF0, nv_rd32(dev, NV04_PFB_CFG0)); | ||
517 | nv_wr32(dev, 0x400DF4, nv_rd32(dev, NV04_PFB_CFG1)); | ||
518 | break; | ||
519 | default: | ||
520 | nv_wr32(dev, 0x4009F0, nv_rd32(dev, NV04_PFB_CFG0)); | ||
521 | nv_wr32(dev, 0x4009F4, nv_rd32(dev, NV04_PFB_CFG1)); | ||
522 | break; | ||
523 | } | ||
524 | nv_wr32(dev, 0x4069F0, nv_rd32(dev, NV04_PFB_CFG0)); | ||
525 | nv_wr32(dev, 0x4069F4, nv_rd32(dev, NV04_PFB_CFG1)); | ||
526 | nv_wr32(dev, 0x400840, 0); | ||
527 | nv_wr32(dev, 0x400844, 0); | ||
528 | nv_wr32(dev, 0x4008A0, vramsz); | ||
529 | nv_wr32(dev, 0x4008A4, vramsz); | ||
530 | break; | ||
531 | } | ||
532 | |||
533 | return 0; | ||
534 | } | ||
535 | |||
536 | void nv40_graph_takedown(struct drm_device *dev) | ||
537 | { | ||
538 | } | ||
539 | |||
540 | struct nouveau_pgraph_object_class nv40_graph_grclass[] = { | ||
541 | { 0x0030, false, NULL }, /* null */ | ||
542 | { 0x0039, false, NULL }, /* m2mf */ | ||
543 | { 0x004a, false, NULL }, /* gdirect */ | ||
544 | { 0x009f, false, NULL }, /* imageblit (nv12) */ | ||
545 | { 0x008a, false, NULL }, /* ifc */ | ||
546 | { 0x0089, false, NULL }, /* sifm */ | ||
547 | { 0x3089, false, NULL }, /* sifm (nv40) */ | ||
548 | { 0x0062, false, NULL }, /* surf2d */ | ||
549 | { 0x3062, false, NULL }, /* surf2d (nv40) */ | ||
550 | { 0x0043, false, NULL }, /* rop */ | ||
551 | { 0x0012, false, NULL }, /* beta1 */ | ||
552 | { 0x0072, false, NULL }, /* beta4 */ | ||
553 | { 0x0019, false, NULL }, /* cliprect */ | ||
554 | { 0x0044, false, NULL }, /* pattern */ | ||
555 | { 0x309e, false, NULL }, /* swzsurf */ | ||
556 | { 0x4097, false, NULL }, /* curie (nv40) */ | ||
557 | { 0x4497, false, NULL }, /* curie (nv44) */ | ||
558 | {} | ||
559 | }; | ||
560 | |||
diff --git a/drivers/gpu/drm/nouveau/nv40_mc.c b/drivers/gpu/drm/nouveau/nv40_mc.c new file mode 100644 index 000000000000..2a3495e848e9 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv40_mc.c | |||
@@ -0,0 +1,38 @@ | |||
1 | #include "drmP.h" | ||
2 | #include "drm.h" | ||
3 | #include "nouveau_drv.h" | ||
4 | #include "nouveau_drm.h" | ||
5 | |||
6 | int | ||
7 | nv40_mc_init(struct drm_device *dev) | ||
8 | { | ||
9 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
10 | uint32_t tmp; | ||
11 | |||
12 | /* Power up everything, resetting each individual unit will | ||
13 | * be done later if needed. | ||
14 | */ | ||
15 | nv_wr32(dev, NV03_PMC_ENABLE, 0xFFFFFFFF); | ||
16 | |||
17 | switch (dev_priv->chipset) { | ||
18 | case 0x44: | ||
19 | case 0x46: /* G72 */ | ||
20 | case 0x4e: | ||
21 | case 0x4c: /* C51_G7X */ | ||
22 | tmp = nv_rd32(dev, NV40_PFB_020C); | ||
23 | nv_wr32(dev, NV40_PMC_1700, tmp); | ||
24 | nv_wr32(dev, NV40_PMC_1704, 0); | ||
25 | nv_wr32(dev, NV40_PMC_1708, 0); | ||
26 | nv_wr32(dev, NV40_PMC_170C, tmp); | ||
27 | break; | ||
28 | default: | ||
29 | break; | ||
30 | } | ||
31 | |||
32 | return 0; | ||
33 | } | ||
34 | |||
35 | void | ||
36 | nv40_mc_takedown(struct drm_device *dev) | ||
37 | { | ||
38 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c new file mode 100644 index 000000000000..f8e28a1e44e7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv50_crtc.c | |||
@@ -0,0 +1,769 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2008 Maarten Maathuis. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include "drmP.h" | ||
28 | #include "drm_mode.h" | ||
29 | #include "drm_crtc_helper.h" | ||
30 | |||
31 | #define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO) | ||
32 | #include "nouveau_reg.h" | ||
33 | #include "nouveau_drv.h" | ||
34 | #include "nouveau_hw.h" | ||
35 | #include "nouveau_encoder.h" | ||
36 | #include "nouveau_crtc.h" | ||
37 | #include "nouveau_fb.h" | ||
38 | #include "nouveau_connector.h" | ||
39 | #include "nv50_display.h" | ||
40 | |||
41 | static void | ||
42 | nv50_crtc_lut_load(struct drm_crtc *crtc) | ||
43 | { | ||
44 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | ||
45 | void __iomem *lut = nvbo_kmap_obj_iovirtual(nv_crtc->lut.nvbo); | ||
46 | int i; | ||
47 | |||
48 | NV_DEBUG(crtc->dev, "\n"); | ||
49 | |||
50 | for (i = 0; i < 256; i++) { | ||
51 | writew(nv_crtc->lut.r[i] >> 2, lut + 8*i + 0); | ||
52 | writew(nv_crtc->lut.g[i] >> 2, lut + 8*i + 2); | ||
53 | writew(nv_crtc->lut.b[i] >> 2, lut + 8*i + 4); | ||
54 | } | ||
55 | |||
56 | if (nv_crtc->lut.depth == 30) { | ||
57 | writew(nv_crtc->lut.r[i - 1] >> 2, lut + 8*i + 0); | ||
58 | writew(nv_crtc->lut.g[i - 1] >> 2, lut + 8*i + 2); | ||
59 | writew(nv_crtc->lut.b[i - 1] >> 2, lut + 8*i + 4); | ||
60 | } | ||
61 | } | ||
62 | |||
63 | int | ||
64 | nv50_crtc_blank(struct nouveau_crtc *nv_crtc, bool blanked) | ||
65 | { | ||
66 | struct drm_device *dev = nv_crtc->base.dev; | ||
67 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
68 | struct nouveau_channel *evo = dev_priv->evo; | ||
69 | int index = nv_crtc->index, ret; | ||
70 | |||
71 | NV_DEBUG(dev, "index %d\n", nv_crtc->index); | ||
72 | NV_DEBUG(dev, "%s\n", blanked ? "blanked" : "unblanked"); | ||
73 | |||
74 | if (blanked) { | ||
75 | nv_crtc->cursor.hide(nv_crtc, false); | ||
76 | |||
77 | ret = RING_SPACE(evo, dev_priv->chipset != 0x50 ? 7 : 5); | ||
78 | if (ret) { | ||
79 | NV_ERROR(dev, "no space while blanking crtc\n"); | ||
80 | return ret; | ||
81 | } | ||
82 | BEGIN_RING(evo, 0, NV50_EVO_CRTC(index, CLUT_MODE), 2); | ||
83 | OUT_RING(evo, NV50_EVO_CRTC_CLUT_MODE_BLANK); | ||
84 | OUT_RING(evo, 0); | ||
85 | if (dev_priv->chipset != 0x50) { | ||
86 | BEGIN_RING(evo, 0, NV84_EVO_CRTC(index, CLUT_DMA), 1); | ||
87 | OUT_RING(evo, NV84_EVO_CRTC_CLUT_DMA_HANDLE_NONE); | ||
88 | } | ||
89 | |||
90 | BEGIN_RING(evo, 0, NV50_EVO_CRTC(index, FB_DMA), 1); | ||
91 | OUT_RING(evo, NV50_EVO_CRTC_FB_DMA_HANDLE_NONE); | ||
92 | } else { | ||
93 | if (nv_crtc->cursor.visible) | ||
94 | nv_crtc->cursor.show(nv_crtc, false); | ||
95 | else | ||
96 | nv_crtc->cursor.hide(nv_crtc, false); | ||
97 | |||
98 | ret = RING_SPACE(evo, dev_priv->chipset != 0x50 ? 10 : 8); | ||
99 | if (ret) { | ||
100 | NV_ERROR(dev, "no space while unblanking crtc\n"); | ||
101 | return ret; | ||
102 | } | ||
103 | BEGIN_RING(evo, 0, NV50_EVO_CRTC(index, CLUT_MODE), 2); | ||
104 | OUT_RING(evo, nv_crtc->lut.depth == 8 ? | ||
105 | NV50_EVO_CRTC_CLUT_MODE_OFF : | ||
106 | NV50_EVO_CRTC_CLUT_MODE_ON); | ||
107 | OUT_RING(evo, (nv_crtc->lut.nvbo->bo.mem.mm_node->start << | ||
108 | PAGE_SHIFT) >> 8); | ||
109 | if (dev_priv->chipset != 0x50) { | ||
110 | BEGIN_RING(evo, 0, NV84_EVO_CRTC(index, CLUT_DMA), 1); | ||
111 | OUT_RING(evo, NvEvoVRAM); | ||
112 | } | ||
113 | |||
114 | BEGIN_RING(evo, 0, NV50_EVO_CRTC(index, FB_OFFSET), 2); | ||
115 | OUT_RING(evo, nv_crtc->fb.offset >> 8); | ||
116 | OUT_RING(evo, 0); | ||
117 | BEGIN_RING(evo, 0, NV50_EVO_CRTC(index, FB_DMA), 1); | ||
118 | if (dev_priv->chipset != 0x50) | ||
119 | if (nv_crtc->fb.tile_flags == 0x7a00) | ||
120 | OUT_RING(evo, NvEvoFB32); | ||
121 | else | ||
122 | if (nv_crtc->fb.tile_flags == 0x7000) | ||
123 | OUT_RING(evo, NvEvoFB16); | ||
124 | else | ||
125 | OUT_RING(evo, NvEvoVRAM); | ||
126 | else | ||
127 | OUT_RING(evo, NvEvoVRAM); | ||
128 | } | ||
129 | |||
130 | nv_crtc->fb.blanked = blanked; | ||
131 | return 0; | ||
132 | } | ||
133 | |||
134 | static int | ||
135 | nv50_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool on, bool update) | ||
136 | { | ||
137 | struct drm_device *dev = nv_crtc->base.dev; | ||
138 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
139 | struct nouveau_channel *evo = dev_priv->evo; | ||
140 | int ret; | ||
141 | |||
142 | NV_DEBUG(dev, "\n"); | ||
143 | |||
144 | ret = RING_SPACE(evo, 2 + (update ? 2 : 0)); | ||
145 | if (ret) { | ||
146 | NV_ERROR(dev, "no space while setting dither\n"); | ||
147 | return ret; | ||
148 | } | ||
149 | |||
150 | BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, DITHER_CTRL), 1); | ||
151 | if (on) | ||
152 | OUT_RING(evo, NV50_EVO_CRTC_DITHER_CTRL_ON); | ||
153 | else | ||
154 | OUT_RING(evo, NV50_EVO_CRTC_DITHER_CTRL_OFF); | ||
155 | |||
156 | if (update) { | ||
157 | BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1); | ||
158 | OUT_RING(evo, 0); | ||
159 | FIRE_RING(evo); | ||
160 | } | ||
161 | |||
162 | return 0; | ||
163 | } | ||
164 | |||
165 | struct nouveau_connector * | ||
166 | nouveau_crtc_connector_get(struct nouveau_crtc *nv_crtc) | ||
167 | { | ||
168 | struct drm_device *dev = nv_crtc->base.dev; | ||
169 | struct drm_connector *connector; | ||
170 | struct drm_crtc *crtc = to_drm_crtc(nv_crtc); | ||
171 | |||
172 | /* The safest approach is to find an encoder with the right crtc, that | ||
173 | * is also linked to a connector. */ | ||
174 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
175 | if (connector->encoder) | ||
176 | if (connector->encoder->crtc == crtc) | ||
177 | return nouveau_connector(connector); | ||
178 | } | ||
179 | |||
180 | return NULL; | ||
181 | } | ||
182 | |||
183 | static int | ||
184 | nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, int scaling_mode, bool update) | ||
185 | { | ||
186 | struct nouveau_connector *nv_connector = | ||
187 | nouveau_crtc_connector_get(nv_crtc); | ||
188 | struct drm_device *dev = nv_crtc->base.dev; | ||
189 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
190 | struct nouveau_channel *evo = dev_priv->evo; | ||
191 | struct drm_display_mode *native_mode = NULL; | ||
192 | struct drm_display_mode *mode = &nv_crtc->base.mode; | ||
193 | uint32_t outX, outY, horiz, vert; | ||
194 | int ret; | ||
195 | |||
196 | NV_DEBUG(dev, "\n"); | ||
197 | |||
198 | switch (scaling_mode) { | ||
199 | case DRM_MODE_SCALE_NONE: | ||
200 | break; | ||
201 | default: | ||
202 | if (!nv_connector || !nv_connector->native_mode) { | ||
203 | NV_ERROR(dev, "No native mode, forcing panel scaling\n"); | ||
204 | scaling_mode = DRM_MODE_SCALE_NONE; | ||
205 | } else { | ||
206 | native_mode = nv_connector->native_mode; | ||
207 | } | ||
208 | break; | ||
209 | } | ||
210 | |||
211 | switch (scaling_mode) { | ||
212 | case DRM_MODE_SCALE_ASPECT: | ||
213 | horiz = (native_mode->hdisplay << 19) / mode->hdisplay; | ||
214 | vert = (native_mode->vdisplay << 19) / mode->vdisplay; | ||
215 | |||
216 | if (vert > horiz) { | ||
217 | outX = (mode->hdisplay * horiz) >> 19; | ||
218 | outY = (mode->vdisplay * horiz) >> 19; | ||
219 | } else { | ||
220 | outX = (mode->hdisplay * vert) >> 19; | ||
221 | outY = (mode->vdisplay * vert) >> 19; | ||
222 | } | ||
223 | break; | ||
224 | case DRM_MODE_SCALE_FULLSCREEN: | ||
225 | outX = native_mode->hdisplay; | ||
226 | outY = native_mode->vdisplay; | ||
227 | break; | ||
228 | case DRM_MODE_SCALE_CENTER: | ||
229 | case DRM_MODE_SCALE_NONE: | ||
230 | default: | ||
231 | outX = mode->hdisplay; | ||
232 | outY = mode->vdisplay; | ||
233 | break; | ||
234 | } | ||
235 | |||
236 | ret = RING_SPACE(evo, update ? 7 : 5); | ||
237 | if (ret) | ||
238 | return ret; | ||
239 | |||
240 | /* Got a better name for SCALER_ACTIVE? */ | ||
241 | /* One day i've got to really figure out why this is needed. */ | ||
242 | BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_CTRL), 1); | ||
243 | if ((mode->flags & DRM_MODE_FLAG_DBLSCAN) || | ||
244 | (mode->flags & DRM_MODE_FLAG_INTERLACE) || | ||
245 | mode->hdisplay != outX || mode->vdisplay != outY) { | ||
246 | OUT_RING(evo, NV50_EVO_CRTC_SCALE_CTRL_ACTIVE); | ||
247 | } else { | ||
248 | OUT_RING(evo, NV50_EVO_CRTC_SCALE_CTRL_INACTIVE); | ||
249 | } | ||
250 | |||
251 | BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_RES1), 2); | ||
252 | OUT_RING(evo, outY << 16 | outX); | ||
253 | OUT_RING(evo, outY << 16 | outX); | ||
254 | |||
255 | if (update) { | ||
256 | BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1); | ||
257 | OUT_RING(evo, 0); | ||
258 | FIRE_RING(evo); | ||
259 | } | ||
260 | |||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | int | ||
265 | nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk) | ||
266 | { | ||
267 | uint32_t pll_reg = NV50_PDISPLAY_CRTC_CLK_CTRL1(head); | ||
268 | struct nouveau_pll_vals pll; | ||
269 | struct pll_lims limits; | ||
270 | uint32_t reg1, reg2; | ||
271 | int ret; | ||
272 | |||
273 | ret = get_pll_limits(dev, pll_reg, &limits); | ||
274 | if (ret) | ||
275 | return ret; | ||
276 | |||
277 | ret = nouveau_calc_pll_mnp(dev, &limits, pclk, &pll); | ||
278 | if (ret <= 0) | ||
279 | return ret; | ||
280 | |||
281 | if (limits.vco2.maxfreq) { | ||
282 | reg1 = nv_rd32(dev, pll_reg + 4) & 0xff00ff00; | ||
283 | reg2 = nv_rd32(dev, pll_reg + 8) & 0x8000ff00; | ||
284 | nv_wr32(dev, pll_reg, 0x10000611); | ||
285 | nv_wr32(dev, pll_reg + 4, reg1 | (pll.M1 << 16) | pll.N1); | ||
286 | nv_wr32(dev, pll_reg + 8, | ||
287 | reg2 | (pll.log2P << 28) | (pll.M2 << 16) | pll.N2); | ||
288 | } else { | ||
289 | reg1 = nv_rd32(dev, pll_reg + 4) & 0xffc00000; | ||
290 | nv_wr32(dev, pll_reg, 0x50000610); | ||
291 | nv_wr32(dev, pll_reg + 4, reg1 | | ||
292 | (pll.log2P << 16) | (pll.M1 << 8) | pll.N1); | ||
293 | } | ||
294 | |||
295 | return 0; | ||
296 | } | ||
297 | |||
298 | static void | ||
299 | nv50_crtc_destroy(struct drm_crtc *crtc) | ||
300 | { | ||
301 | struct drm_device *dev = crtc->dev; | ||
302 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | ||
303 | |||
304 | NV_DEBUG(dev, "\n"); | ||
305 | |||
306 | if (!crtc) | ||
307 | return; | ||
308 | |||
309 | drm_crtc_cleanup(&nv_crtc->base); | ||
310 | |||
311 | nv50_cursor_fini(nv_crtc); | ||
312 | |||
313 | nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo); | ||
314 | nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); | ||
315 | kfree(nv_crtc->mode); | ||
316 | kfree(nv_crtc); | ||
317 | } | ||
318 | |||
319 | int | ||
320 | nv50_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, | ||
321 | uint32_t buffer_handle, uint32_t width, uint32_t height) | ||
322 | { | ||
323 | struct drm_device *dev = crtc->dev; | ||
324 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
325 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | ||
326 | struct nouveau_bo *cursor = NULL; | ||
327 | struct drm_gem_object *gem; | ||
328 | int ret = 0, i; | ||
329 | |||
330 | if (width != 64 || height != 64) | ||
331 | return -EINVAL; | ||
332 | |||
333 | if (!buffer_handle) { | ||
334 | nv_crtc->cursor.hide(nv_crtc, true); | ||
335 | return 0; | ||
336 | } | ||
337 | |||
338 | gem = drm_gem_object_lookup(dev, file_priv, buffer_handle); | ||
339 | if (!gem) | ||
340 | return -EINVAL; | ||
341 | cursor = nouveau_gem_object(gem); | ||
342 | |||
343 | ret = nouveau_bo_map(cursor); | ||
344 | if (ret) | ||
345 | goto out; | ||
346 | |||
347 | /* The simple will do for now. */ | ||
348 | for (i = 0; i < 64 * 64; i++) | ||
349 | nouveau_bo_wr32(nv_crtc->cursor.nvbo, i, nouveau_bo_rd32(cursor, i)); | ||
350 | |||
351 | nouveau_bo_unmap(cursor); | ||
352 | |||
353 | nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.nvbo->bo.offset - | ||
354 | dev_priv->vm_vram_base); | ||
355 | nv_crtc->cursor.show(nv_crtc, true); | ||
356 | |||
357 | out: | ||
358 | mutex_lock(&dev->struct_mutex); | ||
359 | drm_gem_object_unreference(gem); | ||
360 | mutex_unlock(&dev->struct_mutex); | ||
361 | return ret; | ||
362 | } | ||
363 | |||
364 | int | ||
365 | nv50_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) | ||
366 | { | ||
367 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | ||
368 | |||
369 | nv_crtc->cursor.set_pos(nv_crtc, x, y); | ||
370 | return 0; | ||
371 | } | ||
372 | |||
373 | static void | ||
374 | nv50_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, | ||
375 | uint32_t size) | ||
376 | { | ||
377 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | ||
378 | int i; | ||
379 | |||
380 | if (size != 256) | ||
381 | return; | ||
382 | |||
383 | for (i = 0; i < 256; i++) { | ||
384 | nv_crtc->lut.r[i] = r[i]; | ||
385 | nv_crtc->lut.g[i] = g[i]; | ||
386 | nv_crtc->lut.b[i] = b[i]; | ||
387 | } | ||
388 | |||
389 | /* We need to know the depth before we upload, but it's possible to | ||
390 | * get called before a framebuffer is bound. If this is the case, | ||
391 | * mark the lut values as dirty by setting depth==0, and it'll be | ||
392 | * uploaded on the first mode_set_base() | ||
393 | */ | ||
394 | if (!nv_crtc->base.fb) { | ||
395 | nv_crtc->lut.depth = 0; | ||
396 | return; | ||
397 | } | ||
398 | |||
399 | nv50_crtc_lut_load(crtc); | ||
400 | } | ||
401 | |||
402 | static void | ||
403 | nv50_crtc_save(struct drm_crtc *crtc) | ||
404 | { | ||
405 | NV_ERROR(crtc->dev, "!!\n"); | ||
406 | } | ||
407 | |||
408 | static void | ||
409 | nv50_crtc_restore(struct drm_crtc *crtc) | ||
410 | { | ||
411 | NV_ERROR(crtc->dev, "!!\n"); | ||
412 | } | ||
413 | |||
414 | static const struct drm_crtc_funcs nv50_crtc_funcs = { | ||
415 | .save = nv50_crtc_save, | ||
416 | .restore = nv50_crtc_restore, | ||
417 | .cursor_set = nv50_crtc_cursor_set, | ||
418 | .cursor_move = nv50_crtc_cursor_move, | ||
419 | .gamma_set = nv50_crtc_gamma_set, | ||
420 | .set_config = drm_crtc_helper_set_config, | ||
421 | .destroy = nv50_crtc_destroy, | ||
422 | }; | ||
423 | |||
424 | static void | ||
425 | nv50_crtc_dpms(struct drm_crtc *crtc, int mode) | ||
426 | { | ||
427 | } | ||
428 | |||
429 | static void | ||
430 | nv50_crtc_prepare(struct drm_crtc *crtc) | ||
431 | { | ||
432 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | ||
433 | struct drm_device *dev = crtc->dev; | ||
434 | struct drm_encoder *encoder; | ||
435 | |||
436 | NV_DEBUG(dev, "index %d\n", nv_crtc->index); | ||
437 | |||
438 | /* Disconnect all unused encoders. */ | ||
439 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | ||
440 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
441 | |||
442 | if (drm_helper_encoder_in_use(encoder)) | ||
443 | continue; | ||
444 | |||
445 | nv_encoder->disconnect(nv_encoder); | ||
446 | } | ||
447 | |||
448 | nv50_crtc_blank(nv_crtc, true); | ||
449 | } | ||
450 | |||
451 | static void | ||
452 | nv50_crtc_commit(struct drm_crtc *crtc) | ||
453 | { | ||
454 | struct drm_crtc *crtc2; | ||
455 | struct drm_device *dev = crtc->dev; | ||
456 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
457 | struct nouveau_channel *evo = dev_priv->evo; | ||
458 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | ||
459 | int ret; | ||
460 | |||
461 | NV_DEBUG(dev, "index %d\n", nv_crtc->index); | ||
462 | |||
463 | nv50_crtc_blank(nv_crtc, false); | ||
464 | |||
465 | /* Explicitly blank all unused crtc's. */ | ||
466 | list_for_each_entry(crtc2, &dev->mode_config.crtc_list, head) { | ||
467 | if (!drm_helper_crtc_in_use(crtc2)) | ||
468 | nv50_crtc_blank(nouveau_crtc(crtc2), true); | ||
469 | } | ||
470 | |||
471 | ret = RING_SPACE(evo, 2); | ||
472 | if (ret) { | ||
473 | NV_ERROR(dev, "no space while committing crtc\n"); | ||
474 | return; | ||
475 | } | ||
476 | BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1); | ||
477 | OUT_RING(evo, 0); | ||
478 | FIRE_RING(evo); | ||
479 | } | ||
480 | |||
481 | static bool | ||
482 | nv50_crtc_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode *mode, | ||
483 | struct drm_display_mode *adjusted_mode) | ||
484 | { | ||
485 | return true; | ||
486 | } | ||
487 | |||
488 | static int | ||
489 | nv50_crtc_do_mode_set_base(struct drm_crtc *crtc, int x, int y, | ||
490 | struct drm_framebuffer *old_fb, bool update) | ||
491 | { | ||
492 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | ||
493 | struct drm_device *dev = nv_crtc->base.dev; | ||
494 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
495 | struct nouveau_channel *evo = dev_priv->evo; | ||
496 | struct drm_framebuffer *drm_fb = nv_crtc->base.fb; | ||
497 | struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb); | ||
498 | int ret, format; | ||
499 | |||
500 | NV_DEBUG(dev, "index %d\n", nv_crtc->index); | ||
501 | |||
502 | switch (drm_fb->depth) { | ||
503 | case 8: | ||
504 | format = NV50_EVO_CRTC_FB_DEPTH_8; | ||
505 | break; | ||
506 | case 15: | ||
507 | format = NV50_EVO_CRTC_FB_DEPTH_15; | ||
508 | break; | ||
509 | case 16: | ||
510 | format = NV50_EVO_CRTC_FB_DEPTH_16; | ||
511 | break; | ||
512 | case 24: | ||
513 | case 32: | ||
514 | format = NV50_EVO_CRTC_FB_DEPTH_24; | ||
515 | break; | ||
516 | case 30: | ||
517 | format = NV50_EVO_CRTC_FB_DEPTH_30; | ||
518 | break; | ||
519 | default: | ||
520 | NV_ERROR(dev, "unknown depth %d\n", drm_fb->depth); | ||
521 | return -EINVAL; | ||
522 | } | ||
523 | |||
524 | ret = nouveau_bo_pin(fb->nvbo, TTM_PL_FLAG_VRAM); | ||
525 | if (ret) | ||
526 | return ret; | ||
527 | |||
528 | if (old_fb) { | ||
529 | struct nouveau_framebuffer *ofb = nouveau_framebuffer(old_fb); | ||
530 | nouveau_bo_unpin(ofb->nvbo); | ||
531 | } | ||
532 | |||
533 | nv_crtc->fb.offset = fb->nvbo->bo.offset - dev_priv->vm_vram_base; | ||
534 | nv_crtc->fb.tile_flags = fb->nvbo->tile_flags; | ||
535 | nv_crtc->fb.cpp = drm_fb->bits_per_pixel / 8; | ||
536 | if (!nv_crtc->fb.blanked && dev_priv->chipset != 0x50) { | ||
537 | ret = RING_SPACE(evo, 2); | ||
538 | if (ret) | ||
539 | return ret; | ||
540 | |||
541 | BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, FB_DMA), 1); | ||
542 | if (nv_crtc->fb.tile_flags == 0x7a00) | ||
543 | OUT_RING(evo, NvEvoFB32); | ||
544 | else | ||
545 | if (nv_crtc->fb.tile_flags == 0x7000) | ||
546 | OUT_RING(evo, NvEvoFB16); | ||
547 | else | ||
548 | OUT_RING(evo, NvEvoVRAM); | ||
549 | } | ||
550 | |||
551 | ret = RING_SPACE(evo, 12); | ||
552 | if (ret) | ||
553 | return ret; | ||
554 | |||
555 | BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, FB_OFFSET), 5); | ||
556 | OUT_RING(evo, nv_crtc->fb.offset >> 8); | ||
557 | OUT_RING(evo, 0); | ||
558 | OUT_RING(evo, (drm_fb->height << 16) | drm_fb->width); | ||
559 | if (!nv_crtc->fb.tile_flags) { | ||
560 | OUT_RING(evo, drm_fb->pitch | (1 << 20)); | ||
561 | } else { | ||
562 | OUT_RING(evo, ((drm_fb->pitch / 4) << 4) | | ||
563 | fb->nvbo->tile_mode); | ||
564 | } | ||
565 | if (dev_priv->chipset == 0x50) | ||
566 | OUT_RING(evo, (fb->nvbo->tile_flags << 8) | format); | ||
567 | else | ||
568 | OUT_RING(evo, format); | ||
569 | |||
570 | BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, CLUT_MODE), 1); | ||
571 | OUT_RING(evo, fb->base.depth == 8 ? | ||
572 | NV50_EVO_CRTC_CLUT_MODE_OFF : NV50_EVO_CRTC_CLUT_MODE_ON); | ||
573 | |||
574 | BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, COLOR_CTRL), 1); | ||
575 | OUT_RING(evo, NV50_EVO_CRTC_COLOR_CTRL_COLOR); | ||
576 | BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, FB_POS), 1); | ||
577 | OUT_RING(evo, (y << 16) | x); | ||
578 | |||
579 | if (nv_crtc->lut.depth != fb->base.depth) { | ||
580 | nv_crtc->lut.depth = fb->base.depth; | ||
581 | nv50_crtc_lut_load(crtc); | ||
582 | } | ||
583 | |||
584 | if (update) { | ||
585 | ret = RING_SPACE(evo, 2); | ||
586 | if (ret) | ||
587 | return ret; | ||
588 | BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1); | ||
589 | OUT_RING(evo, 0); | ||
590 | FIRE_RING(evo); | ||
591 | } | ||
592 | |||
593 | return 0; | ||
594 | } | ||
595 | |||
596 | static int | ||
597 | nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, | ||
598 | struct drm_display_mode *adjusted_mode, int x, int y, | ||
599 | struct drm_framebuffer *old_fb) | ||
600 | { | ||
601 | struct drm_device *dev = crtc->dev; | ||
602 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
603 | struct nouveau_channel *evo = dev_priv->evo; | ||
604 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); | ||
605 | struct nouveau_connector *nv_connector = NULL; | ||
606 | uint32_t hsync_dur, vsync_dur, hsync_start_to_end, vsync_start_to_end; | ||
607 | uint32_t hunk1, vunk1, vunk2a, vunk2b; | ||
608 | int ret; | ||
609 | |||
610 | /* Find the connector attached to this CRTC */ | ||
611 | nv_connector = nouveau_crtc_connector_get(nv_crtc); | ||
612 | |||
613 | *nv_crtc->mode = *adjusted_mode; | ||
614 | |||
615 | NV_DEBUG(dev, "index %d\n", nv_crtc->index); | ||
616 | |||
617 | hsync_dur = adjusted_mode->hsync_end - adjusted_mode->hsync_start; | ||
618 | vsync_dur = adjusted_mode->vsync_end - adjusted_mode->vsync_start; | ||
619 | hsync_start_to_end = adjusted_mode->htotal - adjusted_mode->hsync_start; | ||
620 | vsync_start_to_end = adjusted_mode->vtotal - adjusted_mode->vsync_start; | ||
621 | /* I can't give this a proper name, anyone else can? */ | ||
622 | hunk1 = adjusted_mode->htotal - | ||
623 | adjusted_mode->hsync_start + adjusted_mode->hdisplay; | ||
624 | vunk1 = adjusted_mode->vtotal - | ||
625 | adjusted_mode->vsync_start + adjusted_mode->vdisplay; | ||
626 | /* Another strange value, this time only for interlaced adjusted_modes. */ | ||
627 | vunk2a = 2 * adjusted_mode->vtotal - | ||
628 | adjusted_mode->vsync_start + adjusted_mode->vdisplay; | ||
629 | vunk2b = adjusted_mode->vtotal - | ||
630 | adjusted_mode->vsync_start + adjusted_mode->vtotal; | ||
631 | |||
632 | if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) { | ||
633 | vsync_dur /= 2; | ||
634 | vsync_start_to_end /= 2; | ||
635 | vunk1 /= 2; | ||
636 | vunk2a /= 2; | ||
637 | vunk2b /= 2; | ||
638 | /* magic */ | ||
639 | if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) { | ||
640 | vsync_start_to_end -= 1; | ||
641 | vunk1 -= 1; | ||
642 | vunk2a -= 1; | ||
643 | vunk2b -= 1; | ||
644 | } | ||
645 | } | ||
646 | |||
647 | ret = RING_SPACE(evo, 17); | ||
648 | if (ret) | ||
649 | return ret; | ||
650 | |||
651 | BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, CLOCK), 2); | ||
652 | OUT_RING(evo, adjusted_mode->clock | 0x800000); | ||
653 | OUT_RING(evo, (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) ? 2 : 0); | ||
654 | |||
655 | BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, DISPLAY_START), 5); | ||
656 | OUT_RING(evo, 0); | ||
657 | OUT_RING(evo, (adjusted_mode->vtotal << 16) | adjusted_mode->htotal); | ||
658 | OUT_RING(evo, (vsync_dur - 1) << 16 | (hsync_dur - 1)); | ||
659 | OUT_RING(evo, (vsync_start_to_end - 1) << 16 | | ||
660 | (hsync_start_to_end - 1)); | ||
661 | OUT_RING(evo, (vunk1 - 1) << 16 | (hunk1 - 1)); | ||
662 | |||
663 | if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) { | ||
664 | BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, UNK0824), 1); | ||
665 | OUT_RING(evo, (vunk2b - 1) << 16 | (vunk2a - 1)); | ||
666 | } else { | ||
667 | OUT_RING(evo, 0); | ||
668 | OUT_RING(evo, 0); | ||
669 | } | ||
670 | |||
671 | BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, UNK082C), 1); | ||
672 | OUT_RING(evo, 0); | ||
673 | |||
674 | /* This is the actual resolution of the mode. */ | ||
675 | BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, REAL_RES), 1); | ||
676 | OUT_RING(evo, (mode->vdisplay << 16) | mode->hdisplay); | ||
677 | BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_CENTER_OFFSET), 1); | ||
678 | OUT_RING(evo, NV50_EVO_CRTC_SCALE_CENTER_OFFSET_VAL(0, 0)); | ||
679 | |||
680 | nv_crtc->set_dither(nv_crtc, nv_connector->use_dithering, false); | ||
681 | nv_crtc->set_scale(nv_crtc, nv_connector->scaling_mode, false); | ||
682 | |||
683 | return nv50_crtc_do_mode_set_base(crtc, x, y, old_fb, false); | ||
684 | } | ||
685 | |||
686 | static int | ||
687 | nv50_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, | ||
688 | struct drm_framebuffer *old_fb) | ||
689 | { | ||
690 | return nv50_crtc_do_mode_set_base(crtc, x, y, old_fb, true); | ||
691 | } | ||
692 | |||
693 | static const struct drm_crtc_helper_funcs nv50_crtc_helper_funcs = { | ||
694 | .dpms = nv50_crtc_dpms, | ||
695 | .prepare = nv50_crtc_prepare, | ||
696 | .commit = nv50_crtc_commit, | ||
697 | .mode_fixup = nv50_crtc_mode_fixup, | ||
698 | .mode_set = nv50_crtc_mode_set, | ||
699 | .mode_set_base = nv50_crtc_mode_set_base, | ||
700 | .load_lut = nv50_crtc_lut_load, | ||
701 | }; | ||
702 | |||
703 | int | ||
704 | nv50_crtc_create(struct drm_device *dev, int index) | ||
705 | { | ||
706 | struct nouveau_crtc *nv_crtc = NULL; | ||
707 | int ret, i; | ||
708 | |||
709 | NV_DEBUG(dev, "\n"); | ||
710 | |||
711 | nv_crtc = kzalloc(sizeof(*nv_crtc), GFP_KERNEL); | ||
712 | if (!nv_crtc) | ||
713 | return -ENOMEM; | ||
714 | |||
715 | nv_crtc->mode = kzalloc(sizeof(*nv_crtc->mode), GFP_KERNEL); | ||
716 | if (!nv_crtc->mode) { | ||
717 | kfree(nv_crtc); | ||
718 | return -ENOMEM; | ||
719 | } | ||
720 | |||
721 | /* Default CLUT parameters, will be activated on the hw upon | ||
722 | * first mode set. | ||
723 | */ | ||
724 | for (i = 0; i < 256; i++) { | ||
725 | nv_crtc->lut.r[i] = i << 8; | ||
726 | nv_crtc->lut.g[i] = i << 8; | ||
727 | nv_crtc->lut.b[i] = i << 8; | ||
728 | } | ||
729 | nv_crtc->lut.depth = 0; | ||
730 | |||
731 | ret = nouveau_bo_new(dev, NULL, 4096, 0x100, TTM_PL_FLAG_VRAM, | ||
732 | 0, 0x0000, false, true, &nv_crtc->lut.nvbo); | ||
733 | if (!ret) { | ||
734 | ret = nouveau_bo_pin(nv_crtc->lut.nvbo, TTM_PL_FLAG_VRAM); | ||
735 | if (!ret) | ||
736 | ret = nouveau_bo_map(nv_crtc->lut.nvbo); | ||
737 | if (ret) | ||
738 | nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo); | ||
739 | } | ||
740 | |||
741 | if (ret) { | ||
742 | kfree(nv_crtc->mode); | ||
743 | kfree(nv_crtc); | ||
744 | return ret; | ||
745 | } | ||
746 | |||
747 | nv_crtc->index = index; | ||
748 | |||
749 | /* set function pointers */ | ||
750 | nv_crtc->set_dither = nv50_crtc_set_dither; | ||
751 | nv_crtc->set_scale = nv50_crtc_set_scale; | ||
752 | |||
753 | drm_crtc_init(dev, &nv_crtc->base, &nv50_crtc_funcs); | ||
754 | drm_crtc_helper_add(&nv_crtc->base, &nv50_crtc_helper_funcs); | ||
755 | drm_mode_crtc_set_gamma_size(&nv_crtc->base, 256); | ||
756 | |||
757 | ret = nouveau_bo_new(dev, NULL, 64*64*4, 0x100, TTM_PL_FLAG_VRAM, | ||
758 | 0, 0x0000, false, true, &nv_crtc->cursor.nvbo); | ||
759 | if (!ret) { | ||
760 | ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM); | ||
761 | if (!ret) | ||
762 | ret = nouveau_bo_map(nv_crtc->cursor.nvbo); | ||
763 | if (ret) | ||
764 | nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); | ||
765 | } | ||
766 | |||
767 | nv50_cursor_init(nv_crtc); | ||
768 | return 0; | ||
769 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nv50_cursor.c b/drivers/gpu/drm/nouveau/nv50_cursor.c new file mode 100644 index 000000000000..e2e79a8f220d --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv50_cursor.c | |||
@@ -0,0 +1,156 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2008 Maarten Maathuis. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include "drmP.h" | ||
28 | #include "drm_mode.h" | ||
29 | |||
30 | #define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO) | ||
31 | #include "nouveau_reg.h" | ||
32 | #include "nouveau_drv.h" | ||
33 | #include "nouveau_crtc.h" | ||
34 | #include "nv50_display.h" | ||
35 | |||
36 | static void | ||
37 | nv50_cursor_show(struct nouveau_crtc *nv_crtc, bool update) | ||
38 | { | ||
39 | struct drm_nouveau_private *dev_priv = nv_crtc->base.dev->dev_private; | ||
40 | struct nouveau_channel *evo = dev_priv->evo; | ||
41 | struct drm_device *dev = nv_crtc->base.dev; | ||
42 | int ret; | ||
43 | |||
44 | NV_DEBUG(dev, "\n"); | ||
45 | |||
46 | if (update && nv_crtc->cursor.visible) | ||
47 | return; | ||
48 | |||
49 | ret = RING_SPACE(evo, (dev_priv->chipset != 0x50 ? 5 : 3) + update * 2); | ||
50 | if (ret) { | ||
51 | NV_ERROR(dev, "no space while unhiding cursor\n"); | ||
52 | return; | ||
53 | } | ||
54 | |||
55 | if (dev_priv->chipset != 0x50) { | ||
56 | BEGIN_RING(evo, 0, NV84_EVO_CRTC(nv_crtc->index, CURSOR_DMA), 1); | ||
57 | OUT_RING(evo, NvEvoVRAM); | ||
58 | } | ||
59 | BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, CURSOR_CTRL), 2); | ||
60 | OUT_RING(evo, NV50_EVO_CRTC_CURSOR_CTRL_SHOW); | ||
61 | OUT_RING(evo, nv_crtc->cursor.offset >> 8); | ||
62 | |||
63 | if (update) { | ||
64 | BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1); | ||
65 | OUT_RING(evo, 0); | ||
66 | FIRE_RING(evo); | ||
67 | nv_crtc->cursor.visible = true; | ||
68 | } | ||
69 | } | ||
70 | |||
71 | static void | ||
72 | nv50_cursor_hide(struct nouveau_crtc *nv_crtc, bool update) | ||
73 | { | ||
74 | struct drm_nouveau_private *dev_priv = nv_crtc->base.dev->dev_private; | ||
75 | struct nouveau_channel *evo = dev_priv->evo; | ||
76 | struct drm_device *dev = nv_crtc->base.dev; | ||
77 | int ret; | ||
78 | |||
79 | NV_DEBUG(dev, "\n"); | ||
80 | |||
81 | if (update && !nv_crtc->cursor.visible) | ||
82 | return; | ||
83 | |||
84 | ret = RING_SPACE(evo, (dev_priv->chipset != 0x50 ? 5 : 3) + update * 2); | ||
85 | if (ret) { | ||
86 | NV_ERROR(dev, "no space while hiding cursor\n"); | ||
87 | return; | ||
88 | } | ||
89 | BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, CURSOR_CTRL), 2); | ||
90 | OUT_RING(evo, NV50_EVO_CRTC_CURSOR_CTRL_HIDE); | ||
91 | OUT_RING(evo, 0); | ||
92 | if (dev_priv->chipset != 0x50) { | ||
93 | BEGIN_RING(evo, 0, NV84_EVO_CRTC(nv_crtc->index, CURSOR_DMA), 1); | ||
94 | OUT_RING(evo, NV84_EVO_CRTC_CURSOR_DMA_HANDLE_NONE); | ||
95 | } | ||
96 | |||
97 | if (update) { | ||
98 | BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1); | ||
99 | OUT_RING(evo, 0); | ||
100 | FIRE_RING(evo); | ||
101 | nv_crtc->cursor.visible = false; | ||
102 | } | ||
103 | } | ||
104 | |||
105 | static void | ||
106 | nv50_cursor_set_pos(struct nouveau_crtc *nv_crtc, int x, int y) | ||
107 | { | ||
108 | struct drm_device *dev = nv_crtc->base.dev; | ||
109 | |||
110 | nv_wr32(dev, NV50_PDISPLAY_CURSOR_USER_POS(nv_crtc->index), | ||
111 | ((y & 0xFFFF) << 16) | (x & 0xFFFF)); | ||
112 | /* Needed to make the cursor move. */ | ||
113 | nv_wr32(dev, NV50_PDISPLAY_CURSOR_USER_POS_CTRL(nv_crtc->index), 0); | ||
114 | } | ||
115 | |||
116 | static void | ||
117 | nv50_cursor_set_offset(struct nouveau_crtc *nv_crtc, uint32_t offset) | ||
118 | { | ||
119 | NV_DEBUG(nv_crtc->base.dev, "\n"); | ||
120 | if (offset == nv_crtc->cursor.offset) | ||
121 | return; | ||
122 | |||
123 | nv_crtc->cursor.offset = offset; | ||
124 | if (nv_crtc->cursor.visible) { | ||
125 | nv_crtc->cursor.visible = false; | ||
126 | nv_crtc->cursor.show(nv_crtc, true); | ||
127 | } | ||
128 | } | ||
129 | |||
130 | int | ||
131 | nv50_cursor_init(struct nouveau_crtc *nv_crtc) | ||
132 | { | ||
133 | nv_crtc->cursor.set_offset = nv50_cursor_set_offset; | ||
134 | nv_crtc->cursor.set_pos = nv50_cursor_set_pos; | ||
135 | nv_crtc->cursor.hide = nv50_cursor_hide; | ||
136 | nv_crtc->cursor.show = nv50_cursor_show; | ||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | void | ||
141 | nv50_cursor_fini(struct nouveau_crtc *nv_crtc) | ||
142 | { | ||
143 | struct drm_device *dev = nv_crtc->base.dev; | ||
144 | int idx = nv_crtc->index; | ||
145 | |||
146 | NV_DEBUG(dev, "\n"); | ||
147 | |||
148 | nv_wr32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(idx), 0); | ||
149 | if (!nv_wait(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(idx), | ||
150 | NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS, 0)) { | ||
151 | NV_ERROR(dev, "timeout: CURSOR_CTRL2_STATUS == 0\n"); | ||
152 | NV_ERROR(dev, "CURSOR_CTRL2 = 0x%08x\n", | ||
153 | nv_rd32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(idx))); | ||
154 | } | ||
155 | } | ||
156 | |||
diff --git a/drivers/gpu/drm/nouveau/nv50_dac.c b/drivers/gpu/drm/nouveau/nv50_dac.c new file mode 100644 index 000000000000..fb5838e3be24 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv50_dac.c | |||
@@ -0,0 +1,304 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2008 Maarten Maathuis. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include "drmP.h" | ||
28 | #include "drm_crtc_helper.h" | ||
29 | |||
30 | #define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO) | ||
31 | #include "nouveau_reg.h" | ||
32 | #include "nouveau_drv.h" | ||
33 | #include "nouveau_dma.h" | ||
34 | #include "nouveau_encoder.h" | ||
35 | #include "nouveau_connector.h" | ||
36 | #include "nouveau_crtc.h" | ||
37 | #include "nv50_display.h" | ||
38 | |||
39 | static void | ||
40 | nv50_dac_disconnect(struct nouveau_encoder *nv_encoder) | ||
41 | { | ||
42 | struct drm_device *dev = to_drm_encoder(nv_encoder)->dev; | ||
43 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
44 | struct nouveau_channel *evo = dev_priv->evo; | ||
45 | int ret; | ||
46 | |||
47 | NV_DEBUG(dev, "Disconnecting DAC %d\n", nv_encoder->or); | ||
48 | |||
49 | ret = RING_SPACE(evo, 2); | ||
50 | if (ret) { | ||
51 | NV_ERROR(dev, "no space while disconnecting DAC\n"); | ||
52 | return; | ||
53 | } | ||
54 | BEGIN_RING(evo, 0, NV50_EVO_DAC(nv_encoder->or, MODE_CTRL), 1); | ||
55 | OUT_RING(evo, 0); | ||
56 | } | ||
57 | |||
58 | static enum drm_connector_status | ||
59 | nv50_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) | ||
60 | { | ||
61 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
62 | struct drm_device *dev = encoder->dev; | ||
63 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
64 | enum drm_connector_status status = connector_status_disconnected; | ||
65 | uint32_t dpms_state, load_pattern, load_state; | ||
66 | int or = nv_encoder->or; | ||
67 | |||
68 | nv_wr32(dev, NV50_PDISPLAY_DAC_CLK_CTRL1(or), 0x00000001); | ||
69 | dpms_state = nv_rd32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or)); | ||
70 | |||
71 | nv_wr32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or), | ||
72 | 0x00150000 | NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING); | ||
73 | if (!nv_wait(NV50_PDISPLAY_DAC_DPMS_CTRL(or), | ||
74 | NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING, 0)) { | ||
75 | NV_ERROR(dev, "timeout: DAC_DPMS_CTRL_PENDING(%d) == 0\n", or); | ||
76 | NV_ERROR(dev, "DAC_DPMS_CTRL(%d) = 0x%08x\n", or, | ||
77 | nv_rd32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or))); | ||
78 | return status; | ||
79 | } | ||
80 | |||
81 | /* Use bios provided value if possible. */ | ||
82 | if (dev_priv->vbios->dactestval) { | ||
83 | load_pattern = dev_priv->vbios->dactestval; | ||
84 | NV_DEBUG(dev, "Using bios provided load_pattern of %d\n", | ||
85 | load_pattern); | ||
86 | } else { | ||
87 | load_pattern = 340; | ||
88 | NV_DEBUG(dev, "Using default load_pattern of %d\n", | ||
89 | load_pattern); | ||
90 | } | ||
91 | |||
92 | nv_wr32(dev, NV50_PDISPLAY_DAC_LOAD_CTRL(or), | ||
93 | NV50_PDISPLAY_DAC_LOAD_CTRL_ACTIVE | load_pattern); | ||
94 | mdelay(45); /* give it some time to process */ | ||
95 | load_state = nv_rd32(dev, NV50_PDISPLAY_DAC_LOAD_CTRL(or)); | ||
96 | |||
97 | nv_wr32(dev, NV50_PDISPLAY_DAC_LOAD_CTRL(or), 0); | ||
98 | nv_wr32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or), dpms_state | | ||
99 | NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING); | ||
100 | |||
101 | if ((load_state & NV50_PDISPLAY_DAC_LOAD_CTRL_PRESENT) == | ||
102 | NV50_PDISPLAY_DAC_LOAD_CTRL_PRESENT) | ||
103 | status = connector_status_connected; | ||
104 | |||
105 | if (status == connector_status_connected) | ||
106 | NV_DEBUG(dev, "Load was detected on output with or %d\n", or); | ||
107 | else | ||
108 | NV_DEBUG(dev, "Load was not detected on output with or %d\n", or); | ||
109 | |||
110 | return status; | ||
111 | } | ||
112 | |||
113 | static void | ||
114 | nv50_dac_dpms(struct drm_encoder *encoder, int mode) | ||
115 | { | ||
116 | struct drm_device *dev = encoder->dev; | ||
117 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
118 | uint32_t val; | ||
119 | int or = nv_encoder->or; | ||
120 | |||
121 | NV_DEBUG(dev, "or %d mode %d\n", or, mode); | ||
122 | |||
123 | /* wait for it to be done */ | ||
124 | if (!nv_wait(NV50_PDISPLAY_DAC_DPMS_CTRL(or), | ||
125 | NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING, 0)) { | ||
126 | NV_ERROR(dev, "timeout: DAC_DPMS_CTRL_PENDING(%d) == 0\n", or); | ||
127 | NV_ERROR(dev, "DAC_DPMS_CTRL(%d) = 0x%08x\n", or, | ||
128 | nv_rd32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or))); | ||
129 | return; | ||
130 | } | ||
131 | |||
132 | val = nv_rd32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or)) & ~0x7F; | ||
133 | |||
134 | if (mode != DRM_MODE_DPMS_ON) | ||
135 | val |= NV50_PDISPLAY_DAC_DPMS_CTRL_BLANKED; | ||
136 | |||
137 | switch (mode) { | ||
138 | case DRM_MODE_DPMS_STANDBY: | ||
139 | val |= NV50_PDISPLAY_DAC_DPMS_CTRL_HSYNC_OFF; | ||
140 | break; | ||
141 | case DRM_MODE_DPMS_SUSPEND: | ||
142 | val |= NV50_PDISPLAY_DAC_DPMS_CTRL_VSYNC_OFF; | ||
143 | break; | ||
144 | case DRM_MODE_DPMS_OFF: | ||
145 | val |= NV50_PDISPLAY_DAC_DPMS_CTRL_OFF; | ||
146 | val |= NV50_PDISPLAY_DAC_DPMS_CTRL_HSYNC_OFF; | ||
147 | val |= NV50_PDISPLAY_DAC_DPMS_CTRL_VSYNC_OFF; | ||
148 | break; | ||
149 | default: | ||
150 | break; | ||
151 | } | ||
152 | |||
153 | nv_wr32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or), val | | ||
154 | NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING); | ||
155 | } | ||
156 | |||
157 | static void | ||
158 | nv50_dac_save(struct drm_encoder *encoder) | ||
159 | { | ||
160 | NV_ERROR(encoder->dev, "!!\n"); | ||
161 | } | ||
162 | |||
163 | static void | ||
164 | nv50_dac_restore(struct drm_encoder *encoder) | ||
165 | { | ||
166 | NV_ERROR(encoder->dev, "!!\n"); | ||
167 | } | ||
168 | |||
169 | static bool | ||
170 | nv50_dac_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, | ||
171 | struct drm_display_mode *adjusted_mode) | ||
172 | { | ||
173 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
174 | struct nouveau_connector *connector; | ||
175 | |||
176 | NV_DEBUG(encoder->dev, "or %d\n", nv_encoder->or); | ||
177 | |||
178 | connector = nouveau_encoder_connector_get(nv_encoder); | ||
179 | if (!connector) { | ||
180 | NV_ERROR(encoder->dev, "Encoder has no connector\n"); | ||
181 | return false; | ||
182 | } | ||
183 | |||
184 | if (connector->scaling_mode != DRM_MODE_SCALE_NONE && | ||
185 | connector->native_mode) { | ||
186 | int id = adjusted_mode->base.id; | ||
187 | *adjusted_mode = *connector->native_mode; | ||
188 | adjusted_mode->base.id = id; | ||
189 | } | ||
190 | |||
191 | return true; | ||
192 | } | ||
193 | |||
194 | static void | ||
195 | nv50_dac_prepare(struct drm_encoder *encoder) | ||
196 | { | ||
197 | } | ||
198 | |||
199 | static void | ||
200 | nv50_dac_commit(struct drm_encoder *encoder) | ||
201 | { | ||
202 | } | ||
203 | |||
204 | static void | ||
205 | nv50_dac_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, | ||
206 | struct drm_display_mode *adjusted_mode) | ||
207 | { | ||
208 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
209 | struct drm_device *dev = encoder->dev; | ||
210 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
211 | struct nouveau_channel *evo = dev_priv->evo; | ||
212 | struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc); | ||
213 | uint32_t mode_ctl = 0, mode_ctl2 = 0; | ||
214 | int ret; | ||
215 | |||
216 | NV_DEBUG(dev, "or %d\n", nv_encoder->or); | ||
217 | |||
218 | nv50_dac_dpms(encoder, DRM_MODE_DPMS_ON); | ||
219 | |||
220 | if (crtc->index == 1) | ||
221 | mode_ctl |= NV50_EVO_DAC_MODE_CTRL_CRTC1; | ||
222 | else | ||
223 | mode_ctl |= NV50_EVO_DAC_MODE_CTRL_CRTC0; | ||
224 | |||
225 | /* Lacking a working tv-out, this is not a 100% sure. */ | ||
226 | if (nv_encoder->dcb->type == OUTPUT_ANALOG) | ||
227 | mode_ctl |= 0x40; | ||
228 | else | ||
229 | if (nv_encoder->dcb->type == OUTPUT_TV) | ||
230 | mode_ctl |= 0x100; | ||
231 | |||
232 | if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) | ||
233 | mode_ctl2 |= NV50_EVO_DAC_MODE_CTRL2_NHSYNC; | ||
234 | |||
235 | if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) | ||
236 | mode_ctl2 |= NV50_EVO_DAC_MODE_CTRL2_NVSYNC; | ||
237 | |||
238 | ret = RING_SPACE(evo, 3); | ||
239 | if (ret) { | ||
240 | NV_ERROR(dev, "no space while connecting DAC\n"); | ||
241 | return; | ||
242 | } | ||
243 | BEGIN_RING(evo, 0, NV50_EVO_DAC(nv_encoder->or, MODE_CTRL), 2); | ||
244 | OUT_RING(evo, mode_ctl); | ||
245 | OUT_RING(evo, mode_ctl2); | ||
246 | } | ||
247 | |||
248 | static const struct drm_encoder_helper_funcs nv50_dac_helper_funcs = { | ||
249 | .dpms = nv50_dac_dpms, | ||
250 | .save = nv50_dac_save, | ||
251 | .restore = nv50_dac_restore, | ||
252 | .mode_fixup = nv50_dac_mode_fixup, | ||
253 | .prepare = nv50_dac_prepare, | ||
254 | .commit = nv50_dac_commit, | ||
255 | .mode_set = nv50_dac_mode_set, | ||
256 | .detect = nv50_dac_detect | ||
257 | }; | ||
258 | |||
259 | static void | ||
260 | nv50_dac_destroy(struct drm_encoder *encoder) | ||
261 | { | ||
262 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
263 | |||
264 | if (!encoder) | ||
265 | return; | ||
266 | |||
267 | NV_DEBUG(encoder->dev, "\n"); | ||
268 | |||
269 | drm_encoder_cleanup(encoder); | ||
270 | kfree(nv_encoder); | ||
271 | } | ||
272 | |||
273 | static const struct drm_encoder_funcs nv50_dac_encoder_funcs = { | ||
274 | .destroy = nv50_dac_destroy, | ||
275 | }; | ||
276 | |||
277 | int | ||
278 | nv50_dac_create(struct drm_device *dev, struct dcb_entry *entry) | ||
279 | { | ||
280 | struct nouveau_encoder *nv_encoder; | ||
281 | struct drm_encoder *encoder; | ||
282 | |||
283 | NV_DEBUG(dev, "\n"); | ||
284 | NV_INFO(dev, "Detected a DAC output\n"); | ||
285 | |||
286 | nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); | ||
287 | if (!nv_encoder) | ||
288 | return -ENOMEM; | ||
289 | encoder = to_drm_encoder(nv_encoder); | ||
290 | |||
291 | nv_encoder->dcb = entry; | ||
292 | nv_encoder->or = ffs(entry->or) - 1; | ||
293 | |||
294 | nv_encoder->disconnect = nv50_dac_disconnect; | ||
295 | |||
296 | drm_encoder_init(dev, encoder, &nv50_dac_encoder_funcs, | ||
297 | DRM_MODE_ENCODER_DAC); | ||
298 | drm_encoder_helper_add(encoder, &nv50_dac_helper_funcs); | ||
299 | |||
300 | encoder->possible_crtcs = entry->heads; | ||
301 | encoder->possible_clones = 0; | ||
302 | return 0; | ||
303 | } | ||
304 | |||
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c new file mode 100644 index 000000000000..12c5ee63495b --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv50_display.c | |||
@@ -0,0 +1,1015 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2008 Maarten Maathuis. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include "nv50_display.h" | ||
28 | #include "nouveau_crtc.h" | ||
29 | #include "nouveau_encoder.h" | ||
30 | #include "nouveau_connector.h" | ||
31 | #include "nouveau_fb.h" | ||
32 | #include "drm_crtc_helper.h" | ||
33 | |||
34 | static void | ||
35 | nv50_evo_channel_del(struct nouveau_channel **pchan) | ||
36 | { | ||
37 | struct nouveau_channel *chan = *pchan; | ||
38 | |||
39 | if (!chan) | ||
40 | return; | ||
41 | *pchan = NULL; | ||
42 | |||
43 | nouveau_gpuobj_channel_takedown(chan); | ||
44 | nouveau_bo_ref(NULL, &chan->pushbuf_bo); | ||
45 | |||
46 | if (chan->user) | ||
47 | iounmap(chan->user); | ||
48 | |||
49 | kfree(chan); | ||
50 | } | ||
51 | |||
52 | static int | ||
53 | nv50_evo_dmaobj_new(struct nouveau_channel *evo, uint32_t class, uint32_t name, | ||
54 | uint32_t tile_flags, uint32_t magic_flags, | ||
55 | uint32_t offset, uint32_t limit) | ||
56 | { | ||
57 | struct drm_nouveau_private *dev_priv = evo->dev->dev_private; | ||
58 | struct drm_device *dev = evo->dev; | ||
59 | struct nouveau_gpuobj *obj = NULL; | ||
60 | int ret; | ||
61 | |||
62 | ret = nouveau_gpuobj_new(dev, evo, 6*4, 32, 0, &obj); | ||
63 | if (ret) | ||
64 | return ret; | ||
65 | obj->engine = NVOBJ_ENGINE_DISPLAY; | ||
66 | |||
67 | ret = nouveau_gpuobj_ref_add(dev, evo, name, obj, NULL); | ||
68 | if (ret) { | ||
69 | nouveau_gpuobj_del(dev, &obj); | ||
70 | return ret; | ||
71 | } | ||
72 | |||
73 | dev_priv->engine.instmem.prepare_access(dev, true); | ||
74 | nv_wo32(dev, obj, 0, (tile_flags << 22) | (magic_flags << 16) | class); | ||
75 | nv_wo32(dev, obj, 1, limit); | ||
76 | nv_wo32(dev, obj, 2, offset); | ||
77 | nv_wo32(dev, obj, 3, 0x00000000); | ||
78 | nv_wo32(dev, obj, 4, 0x00000000); | ||
79 | nv_wo32(dev, obj, 5, 0x00010000); | ||
80 | dev_priv->engine.instmem.finish_access(dev); | ||
81 | |||
82 | return 0; | ||
83 | } | ||
84 | |||
85 | static int | ||
86 | nv50_evo_channel_new(struct drm_device *dev, struct nouveau_channel **pchan) | ||
87 | { | ||
88 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
89 | struct nouveau_channel *chan; | ||
90 | int ret; | ||
91 | |||
92 | chan = kzalloc(sizeof(struct nouveau_channel), GFP_KERNEL); | ||
93 | if (!chan) | ||
94 | return -ENOMEM; | ||
95 | *pchan = chan; | ||
96 | |||
97 | chan->id = -1; | ||
98 | chan->dev = dev; | ||
99 | chan->user_get = 4; | ||
100 | chan->user_put = 0; | ||
101 | |||
102 | INIT_LIST_HEAD(&chan->ramht_refs); | ||
103 | |||
104 | ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, 32768, 0x1000, | ||
105 | NVOBJ_FLAG_ZERO_ALLOC, &chan->ramin); | ||
106 | if (ret) { | ||
107 | NV_ERROR(dev, "Error allocating EVO channel memory: %d\n", ret); | ||
108 | nv50_evo_channel_del(pchan); | ||
109 | return ret; | ||
110 | } | ||
111 | |||
112 | ret = nouveau_mem_init_heap(&chan->ramin_heap, chan->ramin->gpuobj-> | ||
113 | im_pramin->start, 32768); | ||
114 | if (ret) { | ||
115 | NV_ERROR(dev, "Error initialising EVO PRAMIN heap: %d\n", ret); | ||
116 | nv50_evo_channel_del(pchan); | ||
117 | return ret; | ||
118 | } | ||
119 | |||
120 | ret = nouveau_gpuobj_new_ref(dev, chan, chan, 0, 4096, 16, | ||
121 | 0, &chan->ramht); | ||
122 | if (ret) { | ||
123 | NV_ERROR(dev, "Unable to allocate EVO RAMHT: %d\n", ret); | ||
124 | nv50_evo_channel_del(pchan); | ||
125 | return ret; | ||
126 | } | ||
127 | |||
128 | if (dev_priv->chipset != 0x50) { | ||
129 | ret = nv50_evo_dmaobj_new(chan, 0x3d, NvEvoFB16, 0x70, 0x19, | ||
130 | 0, 0xffffffff); | ||
131 | if (ret) { | ||
132 | nv50_evo_channel_del(pchan); | ||
133 | return ret; | ||
134 | } | ||
135 | |||
136 | |||
137 | ret = nv50_evo_dmaobj_new(chan, 0x3d, NvEvoFB32, 0x7a, 0x19, | ||
138 | 0, 0xffffffff); | ||
139 | if (ret) { | ||
140 | nv50_evo_channel_del(pchan); | ||
141 | return ret; | ||
142 | } | ||
143 | } | ||
144 | |||
145 | ret = nv50_evo_dmaobj_new(chan, 0x3d, NvEvoVRAM, 0, 0x19, | ||
146 | 0, nouveau_mem_fb_amount(dev)); | ||
147 | if (ret) { | ||
148 | nv50_evo_channel_del(pchan); | ||
149 | return ret; | ||
150 | } | ||
151 | |||
152 | ret = nouveau_bo_new(dev, NULL, 4096, 0, TTM_PL_FLAG_VRAM, 0, 0, | ||
153 | false, true, &chan->pushbuf_bo); | ||
154 | if (ret == 0) | ||
155 | ret = nouveau_bo_pin(chan->pushbuf_bo, TTM_PL_FLAG_VRAM); | ||
156 | if (ret) { | ||
157 | NV_ERROR(dev, "Error creating EVO DMA push buffer: %d\n", ret); | ||
158 | nv50_evo_channel_del(pchan); | ||
159 | return ret; | ||
160 | } | ||
161 | |||
162 | ret = nouveau_bo_map(chan->pushbuf_bo); | ||
163 | if (ret) { | ||
164 | NV_ERROR(dev, "Error mapping EVO DMA push buffer: %d\n", ret); | ||
165 | nv50_evo_channel_del(pchan); | ||
166 | return ret; | ||
167 | } | ||
168 | |||
169 | chan->user = ioremap(pci_resource_start(dev->pdev, 0) + | ||
170 | NV50_PDISPLAY_USER(0), PAGE_SIZE); | ||
171 | if (!chan->user) { | ||
172 | NV_ERROR(dev, "Error mapping EVO control regs.\n"); | ||
173 | nv50_evo_channel_del(pchan); | ||
174 | return -ENOMEM; | ||
175 | } | ||
176 | |||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | int | ||
181 | nv50_display_init(struct drm_device *dev) | ||
182 | { | ||
183 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
184 | struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer; | ||
185 | struct nouveau_channel *evo = dev_priv->evo; | ||
186 | struct drm_connector *connector; | ||
187 | uint32_t val, ram_amount, hpd_en[2]; | ||
188 | uint64_t start; | ||
189 | int ret, i; | ||
190 | |||
191 | NV_DEBUG(dev, "\n"); | ||
192 | |||
193 | nv_wr32(dev, 0x00610184, nv_rd32(dev, 0x00614004)); | ||
194 | /* | ||
195 | * I think the 0x006101XX range is some kind of main control area | ||
196 | * that enables things. | ||
197 | */ | ||
198 | /* CRTC? */ | ||
199 | for (i = 0; i < 2; i++) { | ||
200 | val = nv_rd32(dev, 0x00616100 + (i * 0x800)); | ||
201 | nv_wr32(dev, 0x00610190 + (i * 0x10), val); | ||
202 | val = nv_rd32(dev, 0x00616104 + (i * 0x800)); | ||
203 | nv_wr32(dev, 0x00610194 + (i * 0x10), val); | ||
204 | val = nv_rd32(dev, 0x00616108 + (i * 0x800)); | ||
205 | nv_wr32(dev, 0x00610198 + (i * 0x10), val); | ||
206 | val = nv_rd32(dev, 0x0061610c + (i * 0x800)); | ||
207 | nv_wr32(dev, 0x0061019c + (i * 0x10), val); | ||
208 | } | ||
209 | /* DAC */ | ||
210 | for (i = 0; i < 3; i++) { | ||
211 | val = nv_rd32(dev, 0x0061a000 + (i * 0x800)); | ||
212 | nv_wr32(dev, 0x006101d0 + (i * 0x04), val); | ||
213 | } | ||
214 | /* SOR */ | ||
215 | for (i = 0; i < 4; i++) { | ||
216 | val = nv_rd32(dev, 0x0061c000 + (i * 0x800)); | ||
217 | nv_wr32(dev, 0x006101e0 + (i * 0x04), val); | ||
218 | } | ||
219 | /* Something not yet in use, tv-out maybe. */ | ||
220 | for (i = 0; i < 3; i++) { | ||
221 | val = nv_rd32(dev, 0x0061e000 + (i * 0x800)); | ||
222 | nv_wr32(dev, 0x006101f0 + (i * 0x04), val); | ||
223 | } | ||
224 | |||
225 | for (i = 0; i < 3; i++) { | ||
226 | nv_wr32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(i), 0x00550000 | | ||
227 | NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING); | ||
228 | nv_wr32(dev, NV50_PDISPLAY_DAC_CLK_CTRL1(i), 0x00000001); | ||
229 | } | ||
230 | |||
231 | /* This used to be in crtc unblank, but seems out of place there. */ | ||
232 | nv_wr32(dev, NV50_PDISPLAY_UNK_380, 0); | ||
233 | /* RAM is clamped to 256 MiB. */ | ||
234 | ram_amount = nouveau_mem_fb_amount(dev); | ||
235 | NV_DEBUG(dev, "ram_amount %d\n", ram_amount); | ||
236 | if (ram_amount > 256*1024*1024) | ||
237 | ram_amount = 256*1024*1024; | ||
238 | nv_wr32(dev, NV50_PDISPLAY_RAM_AMOUNT, ram_amount - 1); | ||
239 | nv_wr32(dev, NV50_PDISPLAY_UNK_388, 0x150000); | ||
240 | nv_wr32(dev, NV50_PDISPLAY_UNK_38C, 0); | ||
241 | |||
242 | /* The precise purpose is unknown, i suspect it has something to do | ||
243 | * with text mode. | ||
244 | */ | ||
245 | if (nv_rd32(dev, NV50_PDISPLAY_INTR_1) & 0x100) { | ||
246 | nv_wr32(dev, NV50_PDISPLAY_INTR_1, 0x100); | ||
247 | nv_wr32(dev, 0x006194e8, nv_rd32(dev, 0x006194e8) & ~1); | ||
248 | if (!nv_wait(0x006194e8, 2, 0)) { | ||
249 | NV_ERROR(dev, "timeout: (0x6194e8 & 2) != 0\n"); | ||
250 | NV_ERROR(dev, "0x6194e8 = 0x%08x\n", | ||
251 | nv_rd32(dev, 0x6194e8)); | ||
252 | return -EBUSY; | ||
253 | } | ||
254 | } | ||
255 | |||
256 | /* taken from nv bug #12637, attempts to un-wedge the hw if it's | ||
257 | * stuck in some unspecified state | ||
258 | */ | ||
259 | start = ptimer->read(dev); | ||
260 | nv_wr32(dev, NV50_PDISPLAY_CHANNEL_STAT(0), 0x2b00); | ||
261 | while ((val = nv_rd32(dev, NV50_PDISPLAY_CHANNEL_STAT(0))) & 0x1e0000) { | ||
262 | if ((val & 0x9f0000) == 0x20000) | ||
263 | nv_wr32(dev, NV50_PDISPLAY_CHANNEL_STAT(0), | ||
264 | val | 0x800000); | ||
265 | |||
266 | if ((val & 0x3f0000) == 0x30000) | ||
267 | nv_wr32(dev, NV50_PDISPLAY_CHANNEL_STAT(0), | ||
268 | val | 0x200000); | ||
269 | |||
270 | if (ptimer->read(dev) - start > 1000000000ULL) { | ||
271 | NV_ERROR(dev, "timeout: (0x610200 & 0x1e0000) != 0\n"); | ||
272 | NV_ERROR(dev, "0x610200 = 0x%08x\n", val); | ||
273 | return -EBUSY; | ||
274 | } | ||
275 | } | ||
276 | |||
277 | nv_wr32(dev, NV50_PDISPLAY_CTRL_STATE, NV50_PDISPLAY_CTRL_STATE_ENABLE); | ||
278 | nv_wr32(dev, NV50_PDISPLAY_CHANNEL_STAT(0), 0x1000b03); | ||
279 | if (!nv_wait(NV50_PDISPLAY_CHANNEL_STAT(0), 0x40000000, 0x40000000)) { | ||
280 | NV_ERROR(dev, "timeout: (0x610200 & 0x40000000) == 0x40000000\n"); | ||
281 | NV_ERROR(dev, "0x610200 = 0x%08x\n", | ||
282 | nv_rd32(dev, NV50_PDISPLAY_CHANNEL_STAT(0))); | ||
283 | return -EBUSY; | ||
284 | } | ||
285 | |||
286 | for (i = 0; i < 2; i++) { | ||
287 | nv_wr32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), 0x2000); | ||
288 | if (!nv_wait(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), | ||
289 | NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS, 0)) { | ||
290 | NV_ERROR(dev, "timeout: CURSOR_CTRL2_STATUS == 0\n"); | ||
291 | NV_ERROR(dev, "CURSOR_CTRL2 = 0x%08x\n", | ||
292 | nv_rd32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i))); | ||
293 | return -EBUSY; | ||
294 | } | ||
295 | |||
296 | nv_wr32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), | ||
297 | NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_ON); | ||
298 | if (!nv_wait(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), | ||
299 | NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS, | ||
300 | NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_ACTIVE)) { | ||
301 | NV_ERROR(dev, "timeout: " | ||
302 | "CURSOR_CTRL2_STATUS_ACTIVE(%d)\n", i); | ||
303 | NV_ERROR(dev, "CURSOR_CTRL2(%d) = 0x%08x\n", i, | ||
304 | nv_rd32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i))); | ||
305 | return -EBUSY; | ||
306 | } | ||
307 | } | ||
308 | |||
309 | nv_wr32(dev, NV50_PDISPLAY_OBJECTS, (evo->ramin->instance >> 8) | 9); | ||
310 | |||
311 | /* initialise fifo */ | ||
312 | nv_wr32(dev, NV50_PDISPLAY_CHANNEL_DMA_CB(0), | ||
313 | ((evo->pushbuf_bo->bo.mem.mm_node->start << PAGE_SHIFT) >> 8) | | ||
314 | NV50_PDISPLAY_CHANNEL_DMA_CB_LOCATION_VRAM | | ||
315 | NV50_PDISPLAY_CHANNEL_DMA_CB_VALID); | ||
316 | nv_wr32(dev, NV50_PDISPLAY_CHANNEL_UNK2(0), 0x00010000); | ||
317 | nv_wr32(dev, NV50_PDISPLAY_CHANNEL_UNK3(0), 0x00000002); | ||
318 | if (!nv_wait(0x610200, 0x80000000, 0x00000000)) { | ||
319 | NV_ERROR(dev, "timeout: (0x610200 & 0x80000000) == 0\n"); | ||
320 | NV_ERROR(dev, "0x610200 = 0x%08x\n", nv_rd32(dev, 0x610200)); | ||
321 | return -EBUSY; | ||
322 | } | ||
323 | nv_wr32(dev, NV50_PDISPLAY_CHANNEL_STAT(0), | ||
324 | (nv_rd32(dev, NV50_PDISPLAY_CHANNEL_STAT(0)) & ~0x00000003) | | ||
325 | NV50_PDISPLAY_CHANNEL_STAT_DMA_ENABLED); | ||
326 | nv_wr32(dev, NV50_PDISPLAY_USER_PUT(0), 0); | ||
327 | nv_wr32(dev, NV50_PDISPLAY_CHANNEL_STAT(0), 0x01000003 | | ||
328 | NV50_PDISPLAY_CHANNEL_STAT_DMA_ENABLED); | ||
329 | nv_wr32(dev, 0x610300, nv_rd32(dev, 0x610300) & ~1); | ||
330 | |||
331 | evo->dma.max = (4096/4) - 2; | ||
332 | evo->dma.put = 0; | ||
333 | evo->dma.cur = evo->dma.put; | ||
334 | evo->dma.free = evo->dma.max - evo->dma.cur; | ||
335 | |||
336 | ret = RING_SPACE(evo, NOUVEAU_DMA_SKIPS); | ||
337 | if (ret) | ||
338 | return ret; | ||
339 | |||
340 | for (i = 0; i < NOUVEAU_DMA_SKIPS; i++) | ||
341 | OUT_RING(evo, 0); | ||
342 | |||
343 | ret = RING_SPACE(evo, 11); | ||
344 | if (ret) | ||
345 | return ret; | ||
346 | BEGIN_RING(evo, 0, NV50_EVO_UNK84, 2); | ||
347 | OUT_RING(evo, NV50_EVO_UNK84_NOTIFY_DISABLED); | ||
348 | OUT_RING(evo, NV50_EVO_DMA_NOTIFY_HANDLE_NONE); | ||
349 | BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, FB_DMA), 1); | ||
350 | OUT_RING(evo, NV50_EVO_CRTC_FB_DMA_HANDLE_NONE); | ||
351 | BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, UNK0800), 1); | ||
352 | OUT_RING(evo, 0); | ||
353 | BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, DISPLAY_START), 1); | ||
354 | OUT_RING(evo, 0); | ||
355 | BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, UNK082C), 1); | ||
356 | OUT_RING(evo, 0); | ||
357 | FIRE_RING(evo); | ||
358 | if (!nv_wait(0x640004, 0xffffffff, evo->dma.put << 2)) | ||
359 | NV_ERROR(dev, "evo pushbuf stalled\n"); | ||
360 | |||
361 | /* enable clock change interrupts. */ | ||
362 | nv_wr32(dev, 0x610028, 0x00010001); | ||
363 | nv_wr32(dev, NV50_PDISPLAY_INTR_EN, (NV50_PDISPLAY_INTR_EN_CLK_UNK10 | | ||
364 | NV50_PDISPLAY_INTR_EN_CLK_UNK20 | | ||
365 | NV50_PDISPLAY_INTR_EN_CLK_UNK40)); | ||
366 | |||
367 | /* enable hotplug interrupts */ | ||
368 | hpd_en[0] = hpd_en[1] = 0; | ||
369 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
370 | struct nouveau_connector *conn = nouveau_connector(connector); | ||
371 | struct dcb_gpio_entry *gpio; | ||
372 | |||
373 | if (connector->connector_type != DRM_MODE_CONNECTOR_DVII && | ||
374 | connector->connector_type != DRM_MODE_CONNECTOR_DVID && | ||
375 | connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort) | ||
376 | continue; | ||
377 | |||
378 | gpio = nouveau_bios_gpio_entry(dev, conn->dcb->gpio_tag); | ||
379 | if (!gpio) | ||
380 | continue; | ||
381 | |||
382 | hpd_en[gpio->line >> 4] |= (0x00010001 << (gpio->line & 0xf)); | ||
383 | } | ||
384 | |||
385 | nv_wr32(dev, 0xe054, 0xffffffff); | ||
386 | nv_wr32(dev, 0xe050, hpd_en[0]); | ||
387 | if (dev_priv->chipset >= 0x90) { | ||
388 | nv_wr32(dev, 0xe074, 0xffffffff); | ||
389 | nv_wr32(dev, 0xe070, hpd_en[1]); | ||
390 | } | ||
391 | |||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | static int nv50_display_disable(struct drm_device *dev) | ||
396 | { | ||
397 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
398 | struct drm_crtc *drm_crtc; | ||
399 | int ret, i; | ||
400 | |||
401 | NV_DEBUG(dev, "\n"); | ||
402 | |||
403 | list_for_each_entry(drm_crtc, &dev->mode_config.crtc_list, head) { | ||
404 | struct nouveau_crtc *crtc = nouveau_crtc(drm_crtc); | ||
405 | |||
406 | nv50_crtc_blank(crtc, true); | ||
407 | } | ||
408 | |||
409 | ret = RING_SPACE(dev_priv->evo, 2); | ||
410 | if (ret == 0) { | ||
411 | BEGIN_RING(dev_priv->evo, 0, NV50_EVO_UPDATE, 1); | ||
412 | OUT_RING(dev_priv->evo, 0); | ||
413 | } | ||
414 | FIRE_RING(dev_priv->evo); | ||
415 | |||
416 | /* Almost like ack'ing a vblank interrupt, maybe in the spirit of | ||
417 | * cleaning up? | ||
418 | */ | ||
419 | list_for_each_entry(drm_crtc, &dev->mode_config.crtc_list, head) { | ||
420 | struct nouveau_crtc *crtc = nouveau_crtc(drm_crtc); | ||
421 | uint32_t mask = NV50_PDISPLAY_INTR_1_VBLANK_CRTC_(crtc->index); | ||
422 | |||
423 | if (!crtc->base.enabled) | ||
424 | continue; | ||
425 | |||
426 | nv_wr32(dev, NV50_PDISPLAY_INTR_1, mask); | ||
427 | if (!nv_wait(NV50_PDISPLAY_INTR_1, mask, mask)) { | ||
428 | NV_ERROR(dev, "timeout: (0x610024 & 0x%08x) == " | ||
429 | "0x%08x\n", mask, mask); | ||
430 | NV_ERROR(dev, "0x610024 = 0x%08x\n", | ||
431 | nv_rd32(dev, NV50_PDISPLAY_INTR_1)); | ||
432 | } | ||
433 | } | ||
434 | |||
435 | nv_wr32(dev, NV50_PDISPLAY_CHANNEL_STAT(0), 0); | ||
436 | nv_wr32(dev, NV50_PDISPLAY_CTRL_STATE, 0); | ||
437 | if (!nv_wait(NV50_PDISPLAY_CHANNEL_STAT(0), 0x1e0000, 0)) { | ||
438 | NV_ERROR(dev, "timeout: (0x610200 & 0x1e0000) == 0\n"); | ||
439 | NV_ERROR(dev, "0x610200 = 0x%08x\n", | ||
440 | nv_rd32(dev, NV50_PDISPLAY_CHANNEL_STAT(0))); | ||
441 | } | ||
442 | |||
443 | for (i = 0; i < 3; i++) { | ||
444 | if (!nv_wait(NV50_PDISPLAY_SOR_DPMS_STATE(i), | ||
445 | NV50_PDISPLAY_SOR_DPMS_STATE_WAIT, 0)) { | ||
446 | NV_ERROR(dev, "timeout: SOR_DPMS_STATE_WAIT(%d) == 0\n", i); | ||
447 | NV_ERROR(dev, "SOR_DPMS_STATE(%d) = 0x%08x\n", i, | ||
448 | nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_STATE(i))); | ||
449 | } | ||
450 | } | ||
451 | |||
452 | /* disable interrupts. */ | ||
453 | nv_wr32(dev, NV50_PDISPLAY_INTR_EN, 0x00000000); | ||
454 | |||
455 | /* disable hotplug interrupts */ | ||
456 | nv_wr32(dev, 0xe054, 0xffffffff); | ||
457 | nv_wr32(dev, 0xe050, 0x00000000); | ||
458 | if (dev_priv->chipset >= 0x90) { | ||
459 | nv_wr32(dev, 0xe074, 0xffffffff); | ||
460 | nv_wr32(dev, 0xe070, 0x00000000); | ||
461 | } | ||
462 | return 0; | ||
463 | } | ||
464 | |||
465 | int nv50_display_create(struct drm_device *dev) | ||
466 | { | ||
467 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
468 | struct parsed_dcb *dcb = dev_priv->vbios->dcb; | ||
469 | uint32_t connector[16] = {}; | ||
470 | int ret, i; | ||
471 | |||
472 | NV_DEBUG(dev, "\n"); | ||
473 | |||
474 | /* init basic kernel modesetting */ | ||
475 | drm_mode_config_init(dev); | ||
476 | |||
477 | /* Initialise some optional connector properties. */ | ||
478 | drm_mode_create_scaling_mode_property(dev); | ||
479 | drm_mode_create_dithering_property(dev); | ||
480 | |||
481 | dev->mode_config.min_width = 0; | ||
482 | dev->mode_config.min_height = 0; | ||
483 | |||
484 | dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs; | ||
485 | |||
486 | dev->mode_config.max_width = 8192; | ||
487 | dev->mode_config.max_height = 8192; | ||
488 | |||
489 | dev->mode_config.fb_base = dev_priv->fb_phys; | ||
490 | |||
491 | /* Create EVO channel */ | ||
492 | ret = nv50_evo_channel_new(dev, &dev_priv->evo); | ||
493 | if (ret) { | ||
494 | NV_ERROR(dev, "Error creating EVO channel: %d\n", ret); | ||
495 | return ret; | ||
496 | } | ||
497 | |||
498 | /* Create CRTC objects */ | ||
499 | for (i = 0; i < 2; i++) | ||
500 | nv50_crtc_create(dev, i); | ||
501 | |||
502 | /* We setup the encoders from the BIOS table */ | ||
503 | for (i = 0 ; i < dcb->entries; i++) { | ||
504 | struct dcb_entry *entry = &dcb->entry[i]; | ||
505 | |||
506 | if (entry->location != DCB_LOC_ON_CHIP) { | ||
507 | NV_WARN(dev, "Off-chip encoder %d/%d unsupported\n", | ||
508 | entry->type, ffs(entry->or) - 1); | ||
509 | continue; | ||
510 | } | ||
511 | |||
512 | switch (entry->type) { | ||
513 | case OUTPUT_TMDS: | ||
514 | case OUTPUT_LVDS: | ||
515 | case OUTPUT_DP: | ||
516 | nv50_sor_create(dev, entry); | ||
517 | break; | ||
518 | case OUTPUT_ANALOG: | ||
519 | nv50_dac_create(dev, entry); | ||
520 | break; | ||
521 | default: | ||
522 | NV_WARN(dev, "DCB encoder %d unknown\n", entry->type); | ||
523 | continue; | ||
524 | } | ||
525 | |||
526 | connector[entry->connector] |= (1 << entry->type); | ||
527 | } | ||
528 | |||
529 | /* It appears that DCB 3.0+ VBIOS has a connector table, however, | ||
530 | * I'm not 100% certain how to decode it correctly yet so just | ||
531 | * look at what encoders are present on each connector index and | ||
532 | * attempt to derive the connector type from that. | ||
533 | */ | ||
534 | for (i = 0 ; i < dcb->entries; i++) { | ||
535 | struct dcb_entry *entry = &dcb->entry[i]; | ||
536 | uint16_t encoders; | ||
537 | int type; | ||
538 | |||
539 | encoders = connector[entry->connector]; | ||
540 | if (!(encoders & (1 << entry->type))) | ||
541 | continue; | ||
542 | connector[entry->connector] = 0; | ||
543 | |||
544 | if (encoders & (1 << OUTPUT_DP)) { | ||
545 | type = DRM_MODE_CONNECTOR_DisplayPort; | ||
546 | } else if (encoders & (1 << OUTPUT_TMDS)) { | ||
547 | if (encoders & (1 << OUTPUT_ANALOG)) | ||
548 | type = DRM_MODE_CONNECTOR_DVII; | ||
549 | else | ||
550 | type = DRM_MODE_CONNECTOR_DVID; | ||
551 | } else if (encoders & (1 << OUTPUT_ANALOG)) { | ||
552 | type = DRM_MODE_CONNECTOR_VGA; | ||
553 | } else if (encoders & (1 << OUTPUT_LVDS)) { | ||
554 | type = DRM_MODE_CONNECTOR_LVDS; | ||
555 | } else { | ||
556 | type = DRM_MODE_CONNECTOR_Unknown; | ||
557 | } | ||
558 | |||
559 | if (type == DRM_MODE_CONNECTOR_Unknown) | ||
560 | continue; | ||
561 | |||
562 | nouveau_connector_create(dev, entry->connector, type); | ||
563 | } | ||
564 | |||
565 | ret = nv50_display_init(dev); | ||
566 | if (ret) | ||
567 | return ret; | ||
568 | |||
569 | return 0; | ||
570 | } | ||
571 | |||
572 | int nv50_display_destroy(struct drm_device *dev) | ||
573 | { | ||
574 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
575 | |||
576 | NV_DEBUG(dev, "\n"); | ||
577 | |||
578 | drm_mode_config_cleanup(dev); | ||
579 | |||
580 | nv50_display_disable(dev); | ||
581 | nv50_evo_channel_del(&dev_priv->evo); | ||
582 | |||
583 | return 0; | ||
584 | } | ||
585 | |||
586 | static inline uint32_t | ||
587 | nv50_display_mode_ctrl(struct drm_device *dev, bool sor, int or) | ||
588 | { | ||
589 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
590 | uint32_t mc; | ||
591 | |||
592 | if (sor) { | ||
593 | if (dev_priv->chipset < 0x90 || | ||
594 | dev_priv->chipset == 0x92 || dev_priv->chipset == 0xa0) | ||
595 | mc = nv_rd32(dev, NV50_PDISPLAY_SOR_MODE_CTRL_P(or)); | ||
596 | else | ||
597 | mc = nv_rd32(dev, NV90_PDISPLAY_SOR_MODE_CTRL_P(or)); | ||
598 | } else { | ||
599 | mc = nv_rd32(dev, NV50_PDISPLAY_DAC_MODE_CTRL_P(or)); | ||
600 | } | ||
601 | |||
602 | return mc; | ||
603 | } | ||
604 | |||
605 | static int | ||
606 | nv50_display_irq_head(struct drm_device *dev, int *phead, | ||
607 | struct dcb_entry **pdcbent) | ||
608 | { | ||
609 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
610 | uint32_t unk30 = nv_rd32(dev, NV50_PDISPLAY_UNK30_CTRL); | ||
611 | uint32_t dac = 0, sor = 0; | ||
612 | int head, i, or = 0, type = OUTPUT_ANY; | ||
613 | |||
614 | /* We're assuming that head 0 *or* head 1 will be active here, | ||
615 | * and not both. I'm not sure if the hw will even signal both | ||
616 | * ever, but it definitely shouldn't for us as we commit each | ||
617 | * CRTC separately, and submission will be blocked by the GPU | ||
618 | * until we handle each in turn. | ||
619 | */ | ||
620 | NV_DEBUG(dev, "0x610030: 0x%08x\n", unk30); | ||
621 | head = ffs((unk30 >> 9) & 3) - 1; | ||
622 | if (head < 0) | ||
623 | return -EINVAL; | ||
624 | |||
625 | /* This assumes CRTCs are never bound to multiple encoders, which | ||
626 | * should be the case. | ||
627 | */ | ||
628 | for (i = 0; i < 3 && type == OUTPUT_ANY; i++) { | ||
629 | uint32_t mc = nv50_display_mode_ctrl(dev, false, i); | ||
630 | if (!(mc & (1 << head))) | ||
631 | continue; | ||
632 | |||
633 | switch ((mc >> 8) & 0xf) { | ||
634 | case 0: type = OUTPUT_ANALOG; break; | ||
635 | case 1: type = OUTPUT_TV; break; | ||
636 | default: | ||
637 | NV_ERROR(dev, "unknown dac mode_ctrl: 0x%08x\n", dac); | ||
638 | return -1; | ||
639 | } | ||
640 | |||
641 | or = i; | ||
642 | } | ||
643 | |||
644 | for (i = 0; i < 4 && type == OUTPUT_ANY; i++) { | ||
645 | uint32_t mc = nv50_display_mode_ctrl(dev, true, i); | ||
646 | if (!(mc & (1 << head))) | ||
647 | continue; | ||
648 | |||
649 | switch ((mc >> 8) & 0xf) { | ||
650 | case 0: type = OUTPUT_LVDS; break; | ||
651 | case 1: type = OUTPUT_TMDS; break; | ||
652 | case 2: type = OUTPUT_TMDS; break; | ||
653 | case 5: type = OUTPUT_TMDS; break; | ||
654 | case 8: type = OUTPUT_DP; break; | ||
655 | case 9: type = OUTPUT_DP; break; | ||
656 | default: | ||
657 | NV_ERROR(dev, "unknown sor mode_ctrl: 0x%08x\n", sor); | ||
658 | return -1; | ||
659 | } | ||
660 | |||
661 | or = i; | ||
662 | } | ||
663 | |||
664 | NV_DEBUG(dev, "type %d, or %d\n", type, or); | ||
665 | if (type == OUTPUT_ANY) { | ||
666 | NV_ERROR(dev, "unknown encoder!!\n"); | ||
667 | return -1; | ||
668 | } | ||
669 | |||
670 | for (i = 0; i < dev_priv->vbios->dcb->entries; i++) { | ||
671 | struct dcb_entry *dcbent = &dev_priv->vbios->dcb->entry[i]; | ||
672 | |||
673 | if (dcbent->type != type) | ||
674 | continue; | ||
675 | |||
676 | if (!(dcbent->or & (1 << or))) | ||
677 | continue; | ||
678 | |||
679 | *phead = head; | ||
680 | *pdcbent = dcbent; | ||
681 | return 0; | ||
682 | } | ||
683 | |||
684 | NV_ERROR(dev, "no DCB entry for %d %d\n", dac != 0, or); | ||
685 | return 0; | ||
686 | } | ||
687 | |||
688 | static uint32_t | ||
689 | nv50_display_script_select(struct drm_device *dev, struct dcb_entry *dcbent, | ||
690 | int pxclk) | ||
691 | { | ||
692 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
693 | struct nvbios *bios = &dev_priv->VBIOS; | ||
694 | uint32_t mc, script = 0, or; | ||
695 | |||
696 | or = ffs(dcbent->or) - 1; | ||
697 | mc = nv50_display_mode_ctrl(dev, dcbent->type != OUTPUT_ANALOG, or); | ||
698 | switch (dcbent->type) { | ||
699 | case OUTPUT_LVDS: | ||
700 | script = (mc >> 8) & 0xf; | ||
701 | if (bios->pub.fp_no_ddc) { | ||
702 | if (bios->fp.dual_link) | ||
703 | script |= 0x0100; | ||
704 | if (bios->fp.if_is_24bit) | ||
705 | script |= 0x0200; | ||
706 | } else { | ||
707 | if (pxclk >= bios->fp.duallink_transition_clk) { | ||
708 | script |= 0x0100; | ||
709 | if (bios->fp.strapless_is_24bit & 2) | ||
710 | script |= 0x0200; | ||
711 | } else | ||
712 | if (bios->fp.strapless_is_24bit & 1) | ||
713 | script |= 0x0200; | ||
714 | } | ||
715 | |||
716 | if (nouveau_uscript_lvds >= 0) { | ||
717 | NV_INFO(dev, "override script 0x%04x with 0x%04x " | ||
718 | "for output LVDS-%d\n", script, | ||
719 | nouveau_uscript_lvds, or); | ||
720 | script = nouveau_uscript_lvds; | ||
721 | } | ||
722 | break; | ||
723 | case OUTPUT_TMDS: | ||
724 | script = (mc >> 8) & 0xf; | ||
725 | if (pxclk >= 165000) | ||
726 | script |= 0x0100; | ||
727 | |||
728 | if (nouveau_uscript_tmds >= 0) { | ||
729 | NV_INFO(dev, "override script 0x%04x with 0x%04x " | ||
730 | "for output TMDS-%d\n", script, | ||
731 | nouveau_uscript_tmds, or); | ||
732 | script = nouveau_uscript_tmds; | ||
733 | } | ||
734 | break; | ||
735 | case OUTPUT_DP: | ||
736 | script = (mc >> 8) & 0xf; | ||
737 | break; | ||
738 | case OUTPUT_ANALOG: | ||
739 | script = 0xff; | ||
740 | break; | ||
741 | default: | ||
742 | NV_ERROR(dev, "modeset on unsupported output type!\n"); | ||
743 | break; | ||
744 | } | ||
745 | |||
746 | return script; | ||
747 | } | ||
748 | |||
749 | static void | ||
750 | nv50_display_vblank_crtc_handler(struct drm_device *dev, int crtc) | ||
751 | { | ||
752 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
753 | struct nouveau_channel *chan; | ||
754 | struct list_head *entry, *tmp; | ||
755 | |||
756 | list_for_each_safe(entry, tmp, &dev_priv->vbl_waiting) { | ||
757 | chan = list_entry(entry, struct nouveau_channel, nvsw.vbl_wait); | ||
758 | |||
759 | nouveau_bo_wr32(chan->notifier_bo, chan->nvsw.vblsem_offset, | ||
760 | chan->nvsw.vblsem_rval); | ||
761 | list_del(&chan->nvsw.vbl_wait); | ||
762 | } | ||
763 | } | ||
764 | |||
765 | static void | ||
766 | nv50_display_vblank_handler(struct drm_device *dev, uint32_t intr) | ||
767 | { | ||
768 | intr &= NV50_PDISPLAY_INTR_1_VBLANK_CRTC; | ||
769 | |||
770 | if (intr & NV50_PDISPLAY_INTR_1_VBLANK_CRTC_0) | ||
771 | nv50_display_vblank_crtc_handler(dev, 0); | ||
772 | |||
773 | if (intr & NV50_PDISPLAY_INTR_1_VBLANK_CRTC_1) | ||
774 | nv50_display_vblank_crtc_handler(dev, 1); | ||
775 | |||
776 | nv_wr32(dev, NV50_PDISPLAY_INTR_EN, nv_rd32(dev, | ||
777 | NV50_PDISPLAY_INTR_EN) & ~intr); | ||
778 | nv_wr32(dev, NV50_PDISPLAY_INTR_1, intr); | ||
779 | } | ||
780 | |||
781 | static void | ||
782 | nv50_display_unk10_handler(struct drm_device *dev) | ||
783 | { | ||
784 | struct dcb_entry *dcbent; | ||
785 | int head, ret; | ||
786 | |||
787 | ret = nv50_display_irq_head(dev, &head, &dcbent); | ||
788 | if (ret) | ||
789 | goto ack; | ||
790 | |||
791 | nv_wr32(dev, 0x619494, nv_rd32(dev, 0x619494) & ~8); | ||
792 | |||
793 | nouveau_bios_run_display_table(dev, dcbent, 0, -1); | ||
794 | |||
795 | ack: | ||
796 | nv_wr32(dev, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK10); | ||
797 | nv_wr32(dev, 0x610030, 0x80000000); | ||
798 | } | ||
799 | |||
800 | static void | ||
801 | nv50_display_unk20_handler(struct drm_device *dev) | ||
802 | { | ||
803 | struct dcb_entry *dcbent; | ||
804 | uint32_t tmp, pclk, script; | ||
805 | int head, or, ret; | ||
806 | |||
807 | ret = nv50_display_irq_head(dev, &head, &dcbent); | ||
808 | if (ret) | ||
809 | goto ack; | ||
810 | or = ffs(dcbent->or) - 1; | ||
811 | pclk = nv_rd32(dev, NV50_PDISPLAY_CRTC_P(head, CLOCK)) & 0x3fffff; | ||
812 | script = nv50_display_script_select(dev, dcbent, pclk); | ||
813 | |||
814 | NV_DEBUG(dev, "head %d pxclk: %dKHz\n", head, pclk); | ||
815 | |||
816 | if (dcbent->type != OUTPUT_DP) | ||
817 | nouveau_bios_run_display_table(dev, dcbent, 0, -2); | ||
818 | |||
819 | nv50_crtc_set_clock(dev, head, pclk); | ||
820 | |||
821 | nouveau_bios_run_display_table(dev, dcbent, script, pclk); | ||
822 | |||
823 | tmp = nv_rd32(dev, NV50_PDISPLAY_CRTC_CLK_CTRL2(head)); | ||
824 | tmp &= ~0x000000f; | ||
825 | nv_wr32(dev, NV50_PDISPLAY_CRTC_CLK_CTRL2(head), tmp); | ||
826 | |||
827 | if (dcbent->type != OUTPUT_ANALOG) { | ||
828 | tmp = nv_rd32(dev, NV50_PDISPLAY_SOR_CLK_CTRL2(or)); | ||
829 | tmp &= ~0x00000f0f; | ||
830 | if (script & 0x0100) | ||
831 | tmp |= 0x00000101; | ||
832 | nv_wr32(dev, NV50_PDISPLAY_SOR_CLK_CTRL2(or), tmp); | ||
833 | } else { | ||
834 | nv_wr32(dev, NV50_PDISPLAY_DAC_CLK_CTRL2(or), 0); | ||
835 | } | ||
836 | |||
837 | ack: | ||
838 | nv_wr32(dev, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK20); | ||
839 | nv_wr32(dev, 0x610030, 0x80000000); | ||
840 | } | ||
841 | |||
842 | static void | ||
843 | nv50_display_unk40_handler(struct drm_device *dev) | ||
844 | { | ||
845 | struct dcb_entry *dcbent; | ||
846 | int head, pclk, script, ret; | ||
847 | |||
848 | ret = nv50_display_irq_head(dev, &head, &dcbent); | ||
849 | if (ret) | ||
850 | goto ack; | ||
851 | pclk = nv_rd32(dev, NV50_PDISPLAY_CRTC_P(head, CLOCK)) & 0x3fffff; | ||
852 | script = nv50_display_script_select(dev, dcbent, pclk); | ||
853 | |||
854 | nouveau_bios_run_display_table(dev, dcbent, script, -pclk); | ||
855 | |||
856 | ack: | ||
857 | nv_wr32(dev, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK40); | ||
858 | nv_wr32(dev, 0x610030, 0x80000000); | ||
859 | nv_wr32(dev, 0x619494, nv_rd32(dev, 0x619494) | 8); | ||
860 | } | ||
861 | |||
862 | void | ||
863 | nv50_display_irq_handler_bh(struct work_struct *work) | ||
864 | { | ||
865 | struct drm_nouveau_private *dev_priv = | ||
866 | container_of(work, struct drm_nouveau_private, irq_work); | ||
867 | struct drm_device *dev = dev_priv->dev; | ||
868 | |||
869 | for (;;) { | ||
870 | uint32_t intr0 = nv_rd32(dev, NV50_PDISPLAY_INTR_0); | ||
871 | uint32_t intr1 = nv_rd32(dev, NV50_PDISPLAY_INTR_1); | ||
872 | |||
873 | NV_DEBUG(dev, "PDISPLAY_INTR_BH 0x%08x 0x%08x\n", intr0, intr1); | ||
874 | |||
875 | if (intr1 & NV50_PDISPLAY_INTR_1_CLK_UNK10) | ||
876 | nv50_display_unk10_handler(dev); | ||
877 | else | ||
878 | if (intr1 & NV50_PDISPLAY_INTR_1_CLK_UNK20) | ||
879 | nv50_display_unk20_handler(dev); | ||
880 | else | ||
881 | if (intr1 & NV50_PDISPLAY_INTR_1_CLK_UNK40) | ||
882 | nv50_display_unk40_handler(dev); | ||
883 | else | ||
884 | break; | ||
885 | } | ||
886 | |||
887 | nv_wr32(dev, NV03_PMC_INTR_EN_0, 1); | ||
888 | } | ||
889 | |||
890 | static void | ||
891 | nv50_display_error_handler(struct drm_device *dev) | ||
892 | { | ||
893 | uint32_t addr, data; | ||
894 | |||
895 | nv_wr32(dev, NV50_PDISPLAY_INTR_0, 0x00010000); | ||
896 | addr = nv_rd32(dev, NV50_PDISPLAY_TRAPPED_ADDR); | ||
897 | data = nv_rd32(dev, NV50_PDISPLAY_TRAPPED_DATA); | ||
898 | |||
899 | NV_ERROR(dev, "EvoCh %d Mthd 0x%04x Data 0x%08x (0x%04x 0x%02x)\n", | ||
900 | 0, addr & 0xffc, data, addr >> 16, (addr >> 12) & 0xf); | ||
901 | |||
902 | nv_wr32(dev, NV50_PDISPLAY_TRAPPED_ADDR, 0x90000000); | ||
903 | } | ||
904 | |||
905 | static void | ||
906 | nv50_display_irq_hotplug(struct drm_device *dev) | ||
907 | { | ||
908 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
909 | struct drm_connector *connector; | ||
910 | const uint32_t gpio_reg[4] = { 0xe104, 0xe108, 0xe280, 0xe284 }; | ||
911 | uint32_t unplug_mask, plug_mask, change_mask; | ||
912 | uint32_t hpd0, hpd1 = 0; | ||
913 | |||
914 | hpd0 = nv_rd32(dev, 0xe054) & nv_rd32(dev, 0xe050); | ||
915 | if (dev_priv->chipset >= 0x90) | ||
916 | hpd1 = nv_rd32(dev, 0xe074) & nv_rd32(dev, 0xe070); | ||
917 | |||
918 | plug_mask = (hpd0 & 0x0000ffff) | (hpd1 << 16); | ||
919 | unplug_mask = (hpd0 >> 16) | (hpd1 & 0xffff0000); | ||
920 | change_mask = plug_mask | unplug_mask; | ||
921 | |||
922 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
923 | struct drm_encoder_helper_funcs *helper; | ||
924 | struct nouveau_connector *nv_connector = | ||
925 | nouveau_connector(connector); | ||
926 | struct nouveau_encoder *nv_encoder; | ||
927 | struct dcb_gpio_entry *gpio; | ||
928 | uint32_t reg; | ||
929 | bool plugged; | ||
930 | |||
931 | if (!nv_connector->dcb) | ||
932 | continue; | ||
933 | |||
934 | gpio = nouveau_bios_gpio_entry(dev, nv_connector->dcb->gpio_tag); | ||
935 | if (!gpio || !(change_mask & (1 << gpio->line))) | ||
936 | continue; | ||
937 | |||
938 | reg = nv_rd32(dev, gpio_reg[gpio->line >> 3]); | ||
939 | plugged = !!(reg & (4 << ((gpio->line & 7) << 2))); | ||
940 | NV_INFO(dev, "%splugged %s\n", plugged ? "" : "un", | ||
941 | drm_get_connector_name(connector)) ; | ||
942 | |||
943 | if (!connector->encoder || !connector->encoder->crtc || | ||
944 | !connector->encoder->crtc->enabled) | ||
945 | continue; | ||
946 | nv_encoder = nouveau_encoder(connector->encoder); | ||
947 | helper = connector->encoder->helper_private; | ||
948 | |||
949 | if (nv_encoder->dcb->type != OUTPUT_DP) | ||
950 | continue; | ||
951 | |||
952 | if (plugged) | ||
953 | helper->dpms(connector->encoder, DRM_MODE_DPMS_ON); | ||
954 | else | ||
955 | helper->dpms(connector->encoder, DRM_MODE_DPMS_OFF); | ||
956 | } | ||
957 | |||
958 | nv_wr32(dev, 0xe054, nv_rd32(dev, 0xe054)); | ||
959 | if (dev_priv->chipset >= 0x90) | ||
960 | nv_wr32(dev, 0xe074, nv_rd32(dev, 0xe074)); | ||
961 | } | ||
962 | |||
963 | void | ||
964 | nv50_display_irq_handler(struct drm_device *dev) | ||
965 | { | ||
966 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
967 | uint32_t delayed = 0; | ||
968 | |||
969 | while (nv_rd32(dev, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_HOTPLUG) | ||
970 | nv50_display_irq_hotplug(dev); | ||
971 | |||
972 | while (nv_rd32(dev, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_DISPLAY) { | ||
973 | uint32_t intr0 = nv_rd32(dev, NV50_PDISPLAY_INTR_0); | ||
974 | uint32_t intr1 = nv_rd32(dev, NV50_PDISPLAY_INTR_1); | ||
975 | uint32_t clock; | ||
976 | |||
977 | NV_DEBUG(dev, "PDISPLAY_INTR 0x%08x 0x%08x\n", intr0, intr1); | ||
978 | |||
979 | if (!intr0 && !(intr1 & ~delayed)) | ||
980 | break; | ||
981 | |||
982 | if (intr0 & 0x00010000) { | ||
983 | nv50_display_error_handler(dev); | ||
984 | intr0 &= ~0x00010000; | ||
985 | } | ||
986 | |||
987 | if (intr1 & NV50_PDISPLAY_INTR_1_VBLANK_CRTC) { | ||
988 | nv50_display_vblank_handler(dev, intr1); | ||
989 | intr1 &= ~NV50_PDISPLAY_INTR_1_VBLANK_CRTC; | ||
990 | } | ||
991 | |||
992 | clock = (intr1 & (NV50_PDISPLAY_INTR_1_CLK_UNK10 | | ||
993 | NV50_PDISPLAY_INTR_1_CLK_UNK20 | | ||
994 | NV50_PDISPLAY_INTR_1_CLK_UNK40)); | ||
995 | if (clock) { | ||
996 | nv_wr32(dev, NV03_PMC_INTR_EN_0, 0); | ||
997 | if (!work_pending(&dev_priv->irq_work)) | ||
998 | queue_work(dev_priv->wq, &dev_priv->irq_work); | ||
999 | delayed |= clock; | ||
1000 | intr1 &= ~clock; | ||
1001 | } | ||
1002 | |||
1003 | if (intr0) { | ||
1004 | NV_ERROR(dev, "unknown PDISPLAY_INTR_0: 0x%08x\n", intr0); | ||
1005 | nv_wr32(dev, NV50_PDISPLAY_INTR_0, intr0); | ||
1006 | } | ||
1007 | |||
1008 | if (intr1) { | ||
1009 | NV_ERROR(dev, | ||
1010 | "unknown PDISPLAY_INTR_1: 0x%08x\n", intr1); | ||
1011 | nv_wr32(dev, NV50_PDISPLAY_INTR_1, intr1); | ||
1012 | } | ||
1013 | } | ||
1014 | } | ||
1015 | |||
diff --git a/drivers/gpu/drm/nouveau/nv50_display.h b/drivers/gpu/drm/nouveau/nv50_display.h new file mode 100644 index 000000000000..3ae8d0725f63 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv50_display.h | |||
@@ -0,0 +1,46 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2008 Maarten Maathuis. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #ifndef __NV50_DISPLAY_H__ | ||
28 | #define __NV50_DISPLAY_H__ | ||
29 | |||
30 | #include "drmP.h" | ||
31 | #include "drm.h" | ||
32 | #include "nouveau_drv.h" | ||
33 | #include "nouveau_dma.h" | ||
34 | #include "nouveau_reg.h" | ||
35 | #include "nouveau_crtc.h" | ||
36 | #include "nv50_evo.h" | ||
37 | |||
38 | void nv50_display_irq_handler(struct drm_device *dev); | ||
39 | void nv50_display_irq_handler_bh(struct work_struct *work); | ||
40 | int nv50_display_init(struct drm_device *dev); | ||
41 | int nv50_display_create(struct drm_device *dev); | ||
42 | int nv50_display_destroy(struct drm_device *dev); | ||
43 | int nv50_crtc_blank(struct nouveau_crtc *, bool blank); | ||
44 | int nv50_crtc_set_clock(struct drm_device *, int head, int pclk); | ||
45 | |||
46 | #endif /* __NV50_DISPLAY_H__ */ | ||
diff --git a/drivers/gpu/drm/nouveau/nv50_evo.h b/drivers/gpu/drm/nouveau/nv50_evo.h new file mode 100644 index 000000000000..aae13343bcec --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv50_evo.h | |||
@@ -0,0 +1,113 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2008 Maarten Maathuis. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #define NV50_EVO_UPDATE 0x00000080 | ||
28 | #define NV50_EVO_UNK84 0x00000084 | ||
29 | #define NV50_EVO_UNK84_NOTIFY 0x40000000 | ||
30 | #define NV50_EVO_UNK84_NOTIFY_DISABLED 0x00000000 | ||
31 | #define NV50_EVO_UNK84_NOTIFY_ENABLED 0x40000000 | ||
32 | #define NV50_EVO_DMA_NOTIFY 0x00000088 | ||
33 | #define NV50_EVO_DMA_NOTIFY_HANDLE 0xffffffff | ||
34 | #define NV50_EVO_DMA_NOTIFY_HANDLE_NONE 0x00000000 | ||
35 | #define NV50_EVO_UNK8C 0x0000008C | ||
36 | |||
37 | #define NV50_EVO_DAC(n, r) ((n) * 0x80 + NV50_EVO_DAC_##r) | ||
38 | #define NV50_EVO_DAC_MODE_CTRL 0x00000400 | ||
39 | #define NV50_EVO_DAC_MODE_CTRL_CRTC0 0x00000001 | ||
40 | #define NV50_EVO_DAC_MODE_CTRL_CRTC1 0x00000002 | ||
41 | #define NV50_EVO_DAC_MODE_CTRL2 0x00000404 | ||
42 | #define NV50_EVO_DAC_MODE_CTRL2_NHSYNC 0x00000001 | ||
43 | #define NV50_EVO_DAC_MODE_CTRL2_NVSYNC 0x00000002 | ||
44 | |||
45 | #define NV50_EVO_SOR(n, r) ((n) * 0x40 + NV50_EVO_SOR_##r) | ||
46 | #define NV50_EVO_SOR_MODE_CTRL 0x00000600 | ||
47 | #define NV50_EVO_SOR_MODE_CTRL_CRTC0 0x00000001 | ||
48 | #define NV50_EVO_SOR_MODE_CTRL_CRTC1 0x00000002 | ||
49 | #define NV50_EVO_SOR_MODE_CTRL_TMDS 0x00000100 | ||
50 | #define NV50_EVO_SOR_MODE_CTRL_TMDS_DUAL_LINK 0x00000400 | ||
51 | #define NV50_EVO_SOR_MODE_CTRL_NHSYNC 0x00001000 | ||
52 | #define NV50_EVO_SOR_MODE_CTRL_NVSYNC 0x00002000 | ||
53 | |||
54 | #define NV50_EVO_CRTC(n, r) ((n) * 0x400 + NV50_EVO_CRTC_##r) | ||
55 | #define NV84_EVO_CRTC(n, r) ((n) * 0x400 + NV84_EVO_CRTC_##r) | ||
56 | #define NV50_EVO_CRTC_UNK0800 0x00000800 | ||
57 | #define NV50_EVO_CRTC_CLOCK 0x00000804 | ||
58 | #define NV50_EVO_CRTC_INTERLACE 0x00000808 | ||
59 | #define NV50_EVO_CRTC_DISPLAY_START 0x00000810 | ||
60 | #define NV50_EVO_CRTC_DISPLAY_TOTAL 0x00000814 | ||
61 | #define NV50_EVO_CRTC_SYNC_DURATION 0x00000818 | ||
62 | #define NV50_EVO_CRTC_SYNC_START_TO_BLANK_END 0x0000081c | ||
63 | #define NV50_EVO_CRTC_UNK0820 0x00000820 | ||
64 | #define NV50_EVO_CRTC_UNK0824 0x00000824 | ||
65 | #define NV50_EVO_CRTC_UNK082C 0x0000082c | ||
66 | #define NV50_EVO_CRTC_CLUT_MODE 0x00000840 | ||
67 | /* You can't have a palette in 8 bit mode (=OFF) */ | ||
68 | #define NV50_EVO_CRTC_CLUT_MODE_BLANK 0x00000000 | ||
69 | #define NV50_EVO_CRTC_CLUT_MODE_OFF 0x80000000 | ||
70 | #define NV50_EVO_CRTC_CLUT_MODE_ON 0xC0000000 | ||
71 | #define NV50_EVO_CRTC_CLUT_OFFSET 0x00000844 | ||
72 | #define NV84_EVO_CRTC_CLUT_DMA 0x0000085C | ||
73 | #define NV84_EVO_CRTC_CLUT_DMA_HANDLE 0xffffffff | ||
74 | #define NV84_EVO_CRTC_CLUT_DMA_HANDLE_NONE 0x00000000 | ||
75 | #define NV50_EVO_CRTC_FB_OFFSET 0x00000860 | ||
76 | #define NV50_EVO_CRTC_FB_SIZE 0x00000868 | ||
77 | #define NV50_EVO_CRTC_FB_CONFIG 0x0000086c | ||
78 | #define NV50_EVO_CRTC_FB_CONFIG_MODE 0x00100000 | ||
79 | #define NV50_EVO_CRTC_FB_CONFIG_MODE_TILE 0x00000000 | ||
80 | #define NV50_EVO_CRTC_FB_CONFIG_MODE_PITCH 0x00100000 | ||
81 | #define NV50_EVO_CRTC_FB_DEPTH 0x00000870 | ||
82 | #define NV50_EVO_CRTC_FB_DEPTH_8 0x00001e00 | ||
83 | #define NV50_EVO_CRTC_FB_DEPTH_15 0x0000e900 | ||
84 | #define NV50_EVO_CRTC_FB_DEPTH_16 0x0000e800 | ||
85 | #define NV50_EVO_CRTC_FB_DEPTH_24 0x0000cf00 | ||
86 | #define NV50_EVO_CRTC_FB_DEPTH_30 0x0000d100 | ||
87 | #define NV50_EVO_CRTC_FB_DMA 0x00000874 | ||
88 | #define NV50_EVO_CRTC_FB_DMA_HANDLE 0xffffffff | ||
89 | #define NV50_EVO_CRTC_FB_DMA_HANDLE_NONE 0x00000000 | ||
90 | #define NV50_EVO_CRTC_CURSOR_CTRL 0x00000880 | ||
91 | #define NV50_EVO_CRTC_CURSOR_CTRL_HIDE 0x05000000 | ||
92 | #define NV50_EVO_CRTC_CURSOR_CTRL_SHOW 0x85000000 | ||
93 | #define NV50_EVO_CRTC_CURSOR_OFFSET 0x00000884 | ||
94 | #define NV84_EVO_CRTC_CURSOR_DMA 0x0000089c | ||
95 | #define NV84_EVO_CRTC_CURSOR_DMA_HANDLE 0xffffffff | ||
96 | #define NV84_EVO_CRTC_CURSOR_DMA_HANDLE_NONE 0x00000000 | ||
97 | #define NV50_EVO_CRTC_DITHER_CTRL 0x000008a0 | ||
98 | #define NV50_EVO_CRTC_DITHER_CTRL_OFF 0x00000000 | ||
99 | #define NV50_EVO_CRTC_DITHER_CTRL_ON 0x00000011 | ||
100 | #define NV50_EVO_CRTC_SCALE_CTRL 0x000008a4 | ||
101 | #define NV50_EVO_CRTC_SCALE_CTRL_INACTIVE 0x00000000 | ||
102 | #define NV50_EVO_CRTC_SCALE_CTRL_ACTIVE 0x00000009 | ||
103 | #define NV50_EVO_CRTC_COLOR_CTRL 0x000008a8 | ||
104 | #define NV50_EVO_CRTC_COLOR_CTRL_COLOR 0x00040000 | ||
105 | #define NV50_EVO_CRTC_FB_POS 0x000008c0 | ||
106 | #define NV50_EVO_CRTC_REAL_RES 0x000008c8 | ||
107 | #define NV50_EVO_CRTC_SCALE_CENTER_OFFSET 0x000008d4 | ||
108 | #define NV50_EVO_CRTC_SCALE_CENTER_OFFSET_VAL(x, y) \ | ||
109 | ((((unsigned)y << 16) & 0xFFFF0000) | (((unsigned)x) & 0x0000FFFF)) | ||
110 | /* Both of these are needed, otherwise nothing happens. */ | ||
111 | #define NV50_EVO_CRTC_SCALE_RES1 0x000008d8 | ||
112 | #define NV50_EVO_CRTC_SCALE_RES2 0x000008dc | ||
113 | |||
diff --git a/drivers/gpu/drm/nouveau/nv50_fbcon.c b/drivers/gpu/drm/nouveau/nv50_fbcon.c new file mode 100644 index 000000000000..6bcc6d39e9b0 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv50_fbcon.c | |||
@@ -0,0 +1,273 @@ | |||
1 | #include "drmP.h" | ||
2 | #include "nouveau_drv.h" | ||
3 | #include "nouveau_dma.h" | ||
4 | #include "nouveau_fbcon.h" | ||
5 | |||
6 | static void | ||
7 | nv50_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect) | ||
8 | { | ||
9 | struct nouveau_fbcon_par *par = info->par; | ||
10 | struct drm_device *dev = par->dev; | ||
11 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
12 | struct nouveau_channel *chan = dev_priv->channel; | ||
13 | |||
14 | if (info->state != FBINFO_STATE_RUNNING) | ||
15 | return; | ||
16 | |||
17 | if (!(info->flags & FBINFO_HWACCEL_DISABLED) && | ||
18 | RING_SPACE(chan, rect->rop == ROP_COPY ? 7 : 11)) { | ||
19 | NV_ERROR(dev, "GPU lockup - switching to software fbcon\n"); | ||
20 | |||
21 | info->flags |= FBINFO_HWACCEL_DISABLED; | ||
22 | } | ||
23 | |||
24 | if (info->flags & FBINFO_HWACCEL_DISABLED) { | ||
25 | cfb_fillrect(info, rect); | ||
26 | return; | ||
27 | } | ||
28 | |||
29 | if (rect->rop != ROP_COPY) { | ||
30 | BEGIN_RING(chan, NvSub2D, 0x02ac, 1); | ||
31 | OUT_RING(chan, 1); | ||
32 | } | ||
33 | BEGIN_RING(chan, NvSub2D, 0x0588, 1); | ||
34 | OUT_RING(chan, rect->color); | ||
35 | BEGIN_RING(chan, NvSub2D, 0x0600, 4); | ||
36 | OUT_RING(chan, rect->dx); | ||
37 | OUT_RING(chan, rect->dy); | ||
38 | OUT_RING(chan, rect->dx + rect->width); | ||
39 | OUT_RING(chan, rect->dy + rect->height); | ||
40 | if (rect->rop != ROP_COPY) { | ||
41 | BEGIN_RING(chan, NvSub2D, 0x02ac, 1); | ||
42 | OUT_RING(chan, 3); | ||
43 | } | ||
44 | FIRE_RING(chan); | ||
45 | } | ||
46 | |||
47 | static void | ||
48 | nv50_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region) | ||
49 | { | ||
50 | struct nouveau_fbcon_par *par = info->par; | ||
51 | struct drm_device *dev = par->dev; | ||
52 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
53 | struct nouveau_channel *chan = dev_priv->channel; | ||
54 | |||
55 | if (info->state != FBINFO_STATE_RUNNING) | ||
56 | return; | ||
57 | |||
58 | if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 12)) { | ||
59 | NV_ERROR(dev, "GPU lockup - switching to software fbcon\n"); | ||
60 | |||
61 | info->flags |= FBINFO_HWACCEL_DISABLED; | ||
62 | } | ||
63 | |||
64 | if (info->flags & FBINFO_HWACCEL_DISABLED) { | ||
65 | cfb_copyarea(info, region); | ||
66 | return; | ||
67 | } | ||
68 | |||
69 | BEGIN_RING(chan, NvSub2D, 0x0110, 1); | ||
70 | OUT_RING(chan, 0); | ||
71 | BEGIN_RING(chan, NvSub2D, 0x08b0, 4); | ||
72 | OUT_RING(chan, region->dx); | ||
73 | OUT_RING(chan, region->dy); | ||
74 | OUT_RING(chan, region->width); | ||
75 | OUT_RING(chan, region->height); | ||
76 | BEGIN_RING(chan, NvSub2D, 0x08d0, 4); | ||
77 | OUT_RING(chan, 0); | ||
78 | OUT_RING(chan, region->sx); | ||
79 | OUT_RING(chan, 0); | ||
80 | OUT_RING(chan, region->sy); | ||
81 | FIRE_RING(chan); | ||
82 | } | ||
83 | |||
84 | static void | ||
85 | nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image) | ||
86 | { | ||
87 | struct nouveau_fbcon_par *par = info->par; | ||
88 | struct drm_device *dev = par->dev; | ||
89 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
90 | struct nouveau_channel *chan = dev_priv->channel; | ||
91 | uint32_t width, dwords, *data = (uint32_t *)image->data; | ||
92 | uint32_t mask = ~(~0 >> (32 - info->var.bits_per_pixel)); | ||
93 | uint32_t *palette = info->pseudo_palette; | ||
94 | |||
95 | if (info->state != FBINFO_STATE_RUNNING) | ||
96 | return; | ||
97 | |||
98 | if (image->depth != 1) { | ||
99 | cfb_imageblit(info, image); | ||
100 | return; | ||
101 | } | ||
102 | |||
103 | if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 11)) { | ||
104 | NV_ERROR(dev, "GPU lockup - switching to software fbcon\n"); | ||
105 | info->flags |= FBINFO_HWACCEL_DISABLED; | ||
106 | } | ||
107 | |||
108 | if (info->flags & FBINFO_HWACCEL_DISABLED) { | ||
109 | cfb_imageblit(info, image); | ||
110 | return; | ||
111 | } | ||
112 | |||
113 | width = (image->width + 31) & ~31; | ||
114 | dwords = (width * image->height) >> 5; | ||
115 | |||
116 | BEGIN_RING(chan, NvSub2D, 0x0814, 2); | ||
117 | if (info->fix.visual == FB_VISUAL_TRUECOLOR || | ||
118 | info->fix.visual == FB_VISUAL_DIRECTCOLOR) { | ||
119 | OUT_RING(chan, palette[image->bg_color] | mask); | ||
120 | OUT_RING(chan, palette[image->fg_color] | mask); | ||
121 | } else { | ||
122 | OUT_RING(chan, image->bg_color); | ||
123 | OUT_RING(chan, image->fg_color); | ||
124 | } | ||
125 | BEGIN_RING(chan, NvSub2D, 0x0838, 2); | ||
126 | OUT_RING(chan, image->width); | ||
127 | OUT_RING(chan, image->height); | ||
128 | BEGIN_RING(chan, NvSub2D, 0x0850, 4); | ||
129 | OUT_RING(chan, 0); | ||
130 | OUT_RING(chan, image->dx); | ||
131 | OUT_RING(chan, 0); | ||
132 | OUT_RING(chan, image->dy); | ||
133 | |||
134 | while (dwords) { | ||
135 | int push = dwords > 2047 ? 2047 : dwords; | ||
136 | |||
137 | if (RING_SPACE(chan, push + 1)) { | ||
138 | NV_ERROR(dev, | ||
139 | "GPU lockup - switching to software fbcon\n"); | ||
140 | info->flags |= FBINFO_HWACCEL_DISABLED; | ||
141 | cfb_imageblit(info, image); | ||
142 | return; | ||
143 | } | ||
144 | |||
145 | dwords -= push; | ||
146 | |||
147 | BEGIN_RING(chan, NvSub2D, 0x40000860, push); | ||
148 | OUT_RINGp(chan, data, push); | ||
149 | data += push; | ||
150 | } | ||
151 | |||
152 | FIRE_RING(chan); | ||
153 | } | ||
154 | |||
155 | int | ||
156 | nv50_fbcon_accel_init(struct fb_info *info) | ||
157 | { | ||
158 | struct nouveau_fbcon_par *par = info->par; | ||
159 | struct drm_device *dev = par->dev; | ||
160 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
161 | struct nouveau_channel *chan = dev_priv->channel; | ||
162 | struct nouveau_gpuobj *eng2d = NULL; | ||
163 | int ret, format; | ||
164 | |||
165 | switch (info->var.bits_per_pixel) { | ||
166 | case 8: | ||
167 | format = 0xf3; | ||
168 | break; | ||
169 | case 15: | ||
170 | format = 0xf8; | ||
171 | break; | ||
172 | case 16: | ||
173 | format = 0xe8; | ||
174 | break; | ||
175 | case 32: | ||
176 | switch (info->var.transp.length) { | ||
177 | case 0: /* depth 24 */ | ||
178 | case 8: /* depth 32, just use 24.. */ | ||
179 | format = 0xe6; | ||
180 | break; | ||
181 | case 2: /* depth 30 */ | ||
182 | format = 0xd1; | ||
183 | break; | ||
184 | default: | ||
185 | return -EINVAL; | ||
186 | } | ||
187 | break; | ||
188 | default: | ||
189 | return -EINVAL; | ||
190 | } | ||
191 | |||
192 | ret = nouveau_gpuobj_gr_new(dev_priv->channel, 0x502d, &eng2d); | ||
193 | if (ret) | ||
194 | return ret; | ||
195 | |||
196 | ret = nouveau_gpuobj_ref_add(dev, dev_priv->channel, Nv2D, eng2d, NULL); | ||
197 | if (ret) | ||
198 | return ret; | ||
199 | |||
200 | ret = RING_SPACE(chan, 59); | ||
201 | if (ret) { | ||
202 | NV_ERROR(dev, "GPU lockup - switching to software fbcon\n"); | ||
203 | return ret; | ||
204 | } | ||
205 | |||
206 | BEGIN_RING(chan, NvSub2D, 0x0000, 1); | ||
207 | OUT_RING(chan, Nv2D); | ||
208 | BEGIN_RING(chan, NvSub2D, 0x0180, 4); | ||
209 | OUT_RING(chan, NvNotify0); | ||
210 | OUT_RING(chan, chan->vram_handle); | ||
211 | OUT_RING(chan, chan->vram_handle); | ||
212 | OUT_RING(chan, chan->vram_handle); | ||
213 | BEGIN_RING(chan, NvSub2D, 0x0290, 1); | ||
214 | OUT_RING(chan, 0); | ||
215 | BEGIN_RING(chan, NvSub2D, 0x0888, 1); | ||
216 | OUT_RING(chan, 1); | ||
217 | BEGIN_RING(chan, NvSub2D, 0x02ac, 1); | ||
218 | OUT_RING(chan, 3); | ||
219 | BEGIN_RING(chan, NvSub2D, 0x02a0, 1); | ||
220 | OUT_RING(chan, 0x55); | ||
221 | BEGIN_RING(chan, NvSub2D, 0x08c0, 4); | ||
222 | OUT_RING(chan, 0); | ||
223 | OUT_RING(chan, 1); | ||
224 | OUT_RING(chan, 0); | ||
225 | OUT_RING(chan, 1); | ||
226 | BEGIN_RING(chan, NvSub2D, 0x0580, 2); | ||
227 | OUT_RING(chan, 4); | ||
228 | OUT_RING(chan, format); | ||
229 | BEGIN_RING(chan, NvSub2D, 0x02e8, 2); | ||
230 | OUT_RING(chan, 2); | ||
231 | OUT_RING(chan, 1); | ||
232 | BEGIN_RING(chan, NvSub2D, 0x0804, 1); | ||
233 | OUT_RING(chan, format); | ||
234 | BEGIN_RING(chan, NvSub2D, 0x0800, 1); | ||
235 | OUT_RING(chan, 1); | ||
236 | BEGIN_RING(chan, NvSub2D, 0x0808, 3); | ||
237 | OUT_RING(chan, 0); | ||
238 | OUT_RING(chan, 0); | ||
239 | OUT_RING(chan, 0); | ||
240 | BEGIN_RING(chan, NvSub2D, 0x081c, 1); | ||
241 | OUT_RING(chan, 1); | ||
242 | BEGIN_RING(chan, NvSub2D, 0x0840, 4); | ||
243 | OUT_RING(chan, 0); | ||
244 | OUT_RING(chan, 1); | ||
245 | OUT_RING(chan, 0); | ||
246 | OUT_RING(chan, 1); | ||
247 | BEGIN_RING(chan, NvSub2D, 0x0200, 2); | ||
248 | OUT_RING(chan, format); | ||
249 | OUT_RING(chan, 1); | ||
250 | BEGIN_RING(chan, NvSub2D, 0x0214, 5); | ||
251 | OUT_RING(chan, info->fix.line_length); | ||
252 | OUT_RING(chan, info->var.xres_virtual); | ||
253 | OUT_RING(chan, info->var.yres_virtual); | ||
254 | OUT_RING(chan, 0); | ||
255 | OUT_RING(chan, info->fix.smem_start - dev_priv->fb_phys + | ||
256 | dev_priv->vm_vram_base); | ||
257 | BEGIN_RING(chan, NvSub2D, 0x0230, 2); | ||
258 | OUT_RING(chan, format); | ||
259 | OUT_RING(chan, 1); | ||
260 | BEGIN_RING(chan, NvSub2D, 0x0244, 5); | ||
261 | OUT_RING(chan, info->fix.line_length); | ||
262 | OUT_RING(chan, info->var.xres_virtual); | ||
263 | OUT_RING(chan, info->var.yres_virtual); | ||
264 | OUT_RING(chan, 0); | ||
265 | OUT_RING(chan, info->fix.smem_start - dev_priv->fb_phys + | ||
266 | dev_priv->vm_vram_base); | ||
267 | |||
268 | info->fbops->fb_fillrect = nv50_fbcon_fillrect; | ||
269 | info->fbops->fb_copyarea = nv50_fbcon_copyarea; | ||
270 | info->fbops->fb_imageblit = nv50_fbcon_imageblit; | ||
271 | return 0; | ||
272 | } | ||
273 | |||
diff --git a/drivers/gpu/drm/nouveau/nv50_fifo.c b/drivers/gpu/drm/nouveau/nv50_fifo.c new file mode 100644 index 000000000000..77ae1aaa0bce --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv50_fifo.c | |||
@@ -0,0 +1,494 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007 Ben Skeggs. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include "drmP.h" | ||
28 | #include "drm.h" | ||
29 | #include "nouveau_drv.h" | ||
30 | |||
31 | struct nv50_fifo_priv { | ||
32 | struct nouveau_gpuobj_ref *thingo[2]; | ||
33 | int cur_thingo; | ||
34 | }; | ||
35 | |||
36 | #define IS_G80 ((dev_priv->chipset & 0xf0) == 0x50) | ||
37 | |||
38 | static void | ||
39 | nv50_fifo_init_thingo(struct drm_device *dev) | ||
40 | { | ||
41 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
42 | struct nv50_fifo_priv *priv = dev_priv->engine.fifo.priv; | ||
43 | struct nouveau_gpuobj_ref *cur; | ||
44 | int i, nr; | ||
45 | |||
46 | NV_DEBUG(dev, "\n"); | ||
47 | |||
48 | cur = priv->thingo[priv->cur_thingo]; | ||
49 | priv->cur_thingo = !priv->cur_thingo; | ||
50 | |||
51 | /* We never schedule channel 0 or 127 */ | ||
52 | dev_priv->engine.instmem.prepare_access(dev, true); | ||
53 | for (i = 1, nr = 0; i < 127; i++) { | ||
54 | if (dev_priv->fifos[i] && dev_priv->fifos[i]->ramfc) | ||
55 | nv_wo32(dev, cur->gpuobj, nr++, i); | ||
56 | } | ||
57 | dev_priv->engine.instmem.finish_access(dev); | ||
58 | |||
59 | nv_wr32(dev, 0x32f4, cur->instance >> 12); | ||
60 | nv_wr32(dev, 0x32ec, nr); | ||
61 | nv_wr32(dev, 0x2500, 0x101); | ||
62 | } | ||
63 | |||
64 | static int | ||
65 | nv50_fifo_channel_enable(struct drm_device *dev, int channel, bool nt) | ||
66 | { | ||
67 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
68 | struct nouveau_channel *chan = dev_priv->fifos[channel]; | ||
69 | uint32_t inst; | ||
70 | |||
71 | NV_DEBUG(dev, "ch%d\n", channel); | ||
72 | |||
73 | if (!chan->ramfc) | ||
74 | return -EINVAL; | ||
75 | |||
76 | if (IS_G80) | ||
77 | inst = chan->ramfc->instance >> 12; | ||
78 | else | ||
79 | inst = chan->ramfc->instance >> 8; | ||
80 | nv_wr32(dev, NV50_PFIFO_CTX_TABLE(channel), | ||
81 | inst | NV50_PFIFO_CTX_TABLE_CHANNEL_ENABLED); | ||
82 | |||
83 | if (!nt) | ||
84 | nv50_fifo_init_thingo(dev); | ||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | static void | ||
89 | nv50_fifo_channel_disable(struct drm_device *dev, int channel, bool nt) | ||
90 | { | ||
91 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
92 | uint32_t inst; | ||
93 | |||
94 | NV_DEBUG(dev, "ch%d, nt=%d\n", channel, nt); | ||
95 | |||
96 | if (IS_G80) | ||
97 | inst = NV50_PFIFO_CTX_TABLE_INSTANCE_MASK_G80; | ||
98 | else | ||
99 | inst = NV50_PFIFO_CTX_TABLE_INSTANCE_MASK_G84; | ||
100 | nv_wr32(dev, NV50_PFIFO_CTX_TABLE(channel), inst); | ||
101 | |||
102 | if (!nt) | ||
103 | nv50_fifo_init_thingo(dev); | ||
104 | } | ||
105 | |||
106 | static void | ||
107 | nv50_fifo_init_reset(struct drm_device *dev) | ||
108 | { | ||
109 | uint32_t pmc_e = NV_PMC_ENABLE_PFIFO; | ||
110 | |||
111 | NV_DEBUG(dev, "\n"); | ||
112 | |||
113 | nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) & ~pmc_e); | ||
114 | nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) | pmc_e); | ||
115 | } | ||
116 | |||
117 | static void | ||
118 | nv50_fifo_init_intr(struct drm_device *dev) | ||
119 | { | ||
120 | NV_DEBUG(dev, "\n"); | ||
121 | |||
122 | nv_wr32(dev, NV03_PFIFO_INTR_0, 0xFFFFFFFF); | ||
123 | nv_wr32(dev, NV03_PFIFO_INTR_EN_0, 0xFFFFFFFF); | ||
124 | } | ||
125 | |||
126 | static void | ||
127 | nv50_fifo_init_context_table(struct drm_device *dev) | ||
128 | { | ||
129 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
130 | int i; | ||
131 | |||
132 | NV_DEBUG(dev, "\n"); | ||
133 | |||
134 | for (i = 0; i < NV50_PFIFO_CTX_TABLE__SIZE; i++) { | ||
135 | if (dev_priv->fifos[i]) | ||
136 | nv50_fifo_channel_enable(dev, i, true); | ||
137 | else | ||
138 | nv50_fifo_channel_disable(dev, i, true); | ||
139 | } | ||
140 | |||
141 | nv50_fifo_init_thingo(dev); | ||
142 | } | ||
143 | |||
144 | static void | ||
145 | nv50_fifo_init_regs__nv(struct drm_device *dev) | ||
146 | { | ||
147 | NV_DEBUG(dev, "\n"); | ||
148 | |||
149 | nv_wr32(dev, 0x250c, 0x6f3cfc34); | ||
150 | } | ||
151 | |||
152 | static void | ||
153 | nv50_fifo_init_regs(struct drm_device *dev) | ||
154 | { | ||
155 | NV_DEBUG(dev, "\n"); | ||
156 | |||
157 | nv_wr32(dev, 0x2500, 0); | ||
158 | nv_wr32(dev, 0x3250, 0); | ||
159 | nv_wr32(dev, 0x3220, 0); | ||
160 | nv_wr32(dev, 0x3204, 0); | ||
161 | nv_wr32(dev, 0x3210, 0); | ||
162 | nv_wr32(dev, 0x3270, 0); | ||
163 | |||
164 | /* Enable dummy channels setup by nv50_instmem.c */ | ||
165 | nv50_fifo_channel_enable(dev, 0, true); | ||
166 | nv50_fifo_channel_enable(dev, 127, true); | ||
167 | } | ||
168 | |||
169 | int | ||
170 | nv50_fifo_init(struct drm_device *dev) | ||
171 | { | ||
172 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
173 | struct nv50_fifo_priv *priv; | ||
174 | int ret; | ||
175 | |||
176 | NV_DEBUG(dev, "\n"); | ||
177 | |||
178 | priv = dev_priv->engine.fifo.priv; | ||
179 | if (priv) { | ||
180 | priv->cur_thingo = !priv->cur_thingo; | ||
181 | goto just_reset; | ||
182 | } | ||
183 | |||
184 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); | ||
185 | if (!priv) | ||
186 | return -ENOMEM; | ||
187 | dev_priv->engine.fifo.priv = priv; | ||
188 | |||
189 | ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, 128*4, 0x1000, | ||
190 | NVOBJ_FLAG_ZERO_ALLOC, &priv->thingo[0]); | ||
191 | if (ret) { | ||
192 | NV_ERROR(dev, "error creating thingo0: %d\n", ret); | ||
193 | return ret; | ||
194 | } | ||
195 | |||
196 | ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, 128*4, 0x1000, | ||
197 | NVOBJ_FLAG_ZERO_ALLOC, &priv->thingo[1]); | ||
198 | if (ret) { | ||
199 | NV_ERROR(dev, "error creating thingo1: %d\n", ret); | ||
200 | return ret; | ||
201 | } | ||
202 | |||
203 | just_reset: | ||
204 | nv50_fifo_init_reset(dev); | ||
205 | nv50_fifo_init_intr(dev); | ||
206 | nv50_fifo_init_context_table(dev); | ||
207 | nv50_fifo_init_regs__nv(dev); | ||
208 | nv50_fifo_init_regs(dev); | ||
209 | dev_priv->engine.fifo.enable(dev); | ||
210 | dev_priv->engine.fifo.reassign(dev, true); | ||
211 | |||
212 | return 0; | ||
213 | } | ||
214 | |||
215 | void | ||
216 | nv50_fifo_takedown(struct drm_device *dev) | ||
217 | { | ||
218 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
219 | struct nv50_fifo_priv *priv = dev_priv->engine.fifo.priv; | ||
220 | |||
221 | NV_DEBUG(dev, "\n"); | ||
222 | |||
223 | if (!priv) | ||
224 | return; | ||
225 | |||
226 | nouveau_gpuobj_ref_del(dev, &priv->thingo[0]); | ||
227 | nouveau_gpuobj_ref_del(dev, &priv->thingo[1]); | ||
228 | |||
229 | dev_priv->engine.fifo.priv = NULL; | ||
230 | kfree(priv); | ||
231 | } | ||
232 | |||
233 | int | ||
234 | nv50_fifo_channel_id(struct drm_device *dev) | ||
235 | { | ||
236 | return nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH1) & | ||
237 | NV50_PFIFO_CACHE1_PUSH1_CHID_MASK; | ||
238 | } | ||
239 | |||
240 | int | ||
241 | nv50_fifo_create_context(struct nouveau_channel *chan) | ||
242 | { | ||
243 | struct drm_device *dev = chan->dev; | ||
244 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
245 | struct nouveau_gpuobj *ramfc = NULL; | ||
246 | int ret; | ||
247 | |||
248 | NV_DEBUG(dev, "ch%d\n", chan->id); | ||
249 | |||
250 | if (IS_G80) { | ||
251 | uint32_t ramin_poffset = chan->ramin->gpuobj->im_pramin->start; | ||
252 | uint32_t ramin_voffset = chan->ramin->gpuobj->im_backing_start; | ||
253 | |||
254 | ret = nouveau_gpuobj_new_fake(dev, ramin_poffset, ramin_voffset, | ||
255 | 0x100, NVOBJ_FLAG_ZERO_ALLOC | | ||
256 | NVOBJ_FLAG_ZERO_FREE, &ramfc, | ||
257 | &chan->ramfc); | ||
258 | if (ret) | ||
259 | return ret; | ||
260 | |||
261 | ret = nouveau_gpuobj_new_fake(dev, ramin_poffset + 0x0400, | ||
262 | ramin_voffset + 0x0400, 4096, | ||
263 | 0, NULL, &chan->cache); | ||
264 | if (ret) | ||
265 | return ret; | ||
266 | } else { | ||
267 | ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, 0x100, 256, | ||
268 | NVOBJ_FLAG_ZERO_ALLOC | | ||
269 | NVOBJ_FLAG_ZERO_FREE, | ||
270 | &chan->ramfc); | ||
271 | if (ret) | ||
272 | return ret; | ||
273 | ramfc = chan->ramfc->gpuobj; | ||
274 | |||
275 | ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, 4096, 256, | ||
276 | 0, &chan->cache); | ||
277 | if (ret) | ||
278 | return ret; | ||
279 | } | ||
280 | |||
281 | dev_priv->engine.instmem.prepare_access(dev, true); | ||
282 | |||
283 | nv_wo32(dev, ramfc, 0x08/4, chan->pushbuf_base); | ||
284 | nv_wo32(dev, ramfc, 0x10/4, chan->pushbuf_base); | ||
285 | nv_wo32(dev, ramfc, 0x48/4, chan->pushbuf->instance >> 4); | ||
286 | nv_wo32(dev, ramfc, 0x80/4, (0xc << 24) | (chan->ramht->instance >> 4)); | ||
287 | nv_wo32(dev, ramfc, 0x3c/4, 0x00086078); | ||
288 | nv_wo32(dev, ramfc, 0x44/4, 0x2101ffff); | ||
289 | nv_wo32(dev, ramfc, 0x60/4, 0x7fffffff); | ||
290 | nv_wo32(dev, ramfc, 0x40/4, 0x00000000); | ||
291 | nv_wo32(dev, ramfc, 0x7c/4, 0x30000001); | ||
292 | nv_wo32(dev, ramfc, 0x78/4, 0x00000000); | ||
293 | nv_wo32(dev, ramfc, 0x4c/4, 0xffffffff); | ||
294 | |||
295 | if (!IS_G80) { | ||
296 | nv_wo32(dev, chan->ramin->gpuobj, 0, chan->id); | ||
297 | nv_wo32(dev, chan->ramin->gpuobj, 1, | ||
298 | chan->ramfc->instance >> 8); | ||
299 | |||
300 | nv_wo32(dev, ramfc, 0x88/4, chan->cache->instance >> 10); | ||
301 | nv_wo32(dev, ramfc, 0x98/4, chan->ramin->instance >> 12); | ||
302 | } | ||
303 | |||
304 | dev_priv->engine.instmem.finish_access(dev); | ||
305 | |||
306 | ret = nv50_fifo_channel_enable(dev, chan->id, false); | ||
307 | if (ret) { | ||
308 | NV_ERROR(dev, "error enabling ch%d: %d\n", chan->id, ret); | ||
309 | nouveau_gpuobj_ref_del(dev, &chan->ramfc); | ||
310 | return ret; | ||
311 | } | ||
312 | |||
313 | return 0; | ||
314 | } | ||
315 | |||
316 | void | ||
317 | nv50_fifo_destroy_context(struct nouveau_channel *chan) | ||
318 | { | ||
319 | struct drm_device *dev = chan->dev; | ||
320 | |||
321 | NV_DEBUG(dev, "ch%d\n", chan->id); | ||
322 | |||
323 | nouveau_gpuobj_ref_del(dev, &chan->ramfc); | ||
324 | nouveau_gpuobj_ref_del(dev, &chan->cache); | ||
325 | |||
326 | nv50_fifo_channel_disable(dev, chan->id, false); | ||
327 | |||
328 | /* Dummy channel, also used on ch 127 */ | ||
329 | if (chan->id == 0) | ||
330 | nv50_fifo_channel_disable(dev, 127, false); | ||
331 | } | ||
332 | |||
333 | int | ||
334 | nv50_fifo_load_context(struct nouveau_channel *chan) | ||
335 | { | ||
336 | struct drm_device *dev = chan->dev; | ||
337 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
338 | struct nouveau_gpuobj *ramfc = chan->ramfc->gpuobj; | ||
339 | struct nouveau_gpuobj *cache = chan->cache->gpuobj; | ||
340 | int ptr, cnt; | ||
341 | |||
342 | NV_DEBUG(dev, "ch%d\n", chan->id); | ||
343 | |||
344 | dev_priv->engine.instmem.prepare_access(dev, false); | ||
345 | |||
346 | nv_wr32(dev, 0x3330, nv_ro32(dev, ramfc, 0x00/4)); | ||
347 | nv_wr32(dev, 0x3334, nv_ro32(dev, ramfc, 0x04/4)); | ||
348 | nv_wr32(dev, 0x3240, nv_ro32(dev, ramfc, 0x08/4)); | ||
349 | nv_wr32(dev, 0x3320, nv_ro32(dev, ramfc, 0x0c/4)); | ||
350 | nv_wr32(dev, 0x3244, nv_ro32(dev, ramfc, 0x10/4)); | ||
351 | nv_wr32(dev, 0x3328, nv_ro32(dev, ramfc, 0x14/4)); | ||
352 | nv_wr32(dev, 0x3368, nv_ro32(dev, ramfc, 0x18/4)); | ||
353 | nv_wr32(dev, 0x336c, nv_ro32(dev, ramfc, 0x1c/4)); | ||
354 | nv_wr32(dev, 0x3370, nv_ro32(dev, ramfc, 0x20/4)); | ||
355 | nv_wr32(dev, 0x3374, nv_ro32(dev, ramfc, 0x24/4)); | ||
356 | nv_wr32(dev, 0x3378, nv_ro32(dev, ramfc, 0x28/4)); | ||
357 | nv_wr32(dev, 0x337c, nv_ro32(dev, ramfc, 0x2c/4)); | ||
358 | nv_wr32(dev, 0x3228, nv_ro32(dev, ramfc, 0x30/4)); | ||
359 | nv_wr32(dev, 0x3364, nv_ro32(dev, ramfc, 0x34/4)); | ||
360 | nv_wr32(dev, 0x32a0, nv_ro32(dev, ramfc, 0x38/4)); | ||
361 | nv_wr32(dev, 0x3224, nv_ro32(dev, ramfc, 0x3c/4)); | ||
362 | nv_wr32(dev, 0x324c, nv_ro32(dev, ramfc, 0x40/4)); | ||
363 | nv_wr32(dev, 0x2044, nv_ro32(dev, ramfc, 0x44/4)); | ||
364 | nv_wr32(dev, 0x322c, nv_ro32(dev, ramfc, 0x48/4)); | ||
365 | nv_wr32(dev, 0x3234, nv_ro32(dev, ramfc, 0x4c/4)); | ||
366 | nv_wr32(dev, 0x3340, nv_ro32(dev, ramfc, 0x50/4)); | ||
367 | nv_wr32(dev, 0x3344, nv_ro32(dev, ramfc, 0x54/4)); | ||
368 | nv_wr32(dev, 0x3280, nv_ro32(dev, ramfc, 0x58/4)); | ||
369 | nv_wr32(dev, 0x3254, nv_ro32(dev, ramfc, 0x5c/4)); | ||
370 | nv_wr32(dev, 0x3260, nv_ro32(dev, ramfc, 0x60/4)); | ||
371 | nv_wr32(dev, 0x3264, nv_ro32(dev, ramfc, 0x64/4)); | ||
372 | nv_wr32(dev, 0x3268, nv_ro32(dev, ramfc, 0x68/4)); | ||
373 | nv_wr32(dev, 0x326c, nv_ro32(dev, ramfc, 0x6c/4)); | ||
374 | nv_wr32(dev, 0x32e4, nv_ro32(dev, ramfc, 0x70/4)); | ||
375 | nv_wr32(dev, 0x3248, nv_ro32(dev, ramfc, 0x74/4)); | ||
376 | nv_wr32(dev, 0x2088, nv_ro32(dev, ramfc, 0x78/4)); | ||
377 | nv_wr32(dev, 0x2058, nv_ro32(dev, ramfc, 0x7c/4)); | ||
378 | nv_wr32(dev, 0x2210, nv_ro32(dev, ramfc, 0x80/4)); | ||
379 | |||
380 | cnt = nv_ro32(dev, ramfc, 0x84/4); | ||
381 | for (ptr = 0; ptr < cnt; ptr++) { | ||
382 | nv_wr32(dev, NV40_PFIFO_CACHE1_METHOD(ptr), | ||
383 | nv_ro32(dev, cache, (ptr * 2) + 0)); | ||
384 | nv_wr32(dev, NV40_PFIFO_CACHE1_DATA(ptr), | ||
385 | nv_ro32(dev, cache, (ptr * 2) + 1)); | ||
386 | } | ||
387 | nv_wr32(dev, 0x3210, cnt << 2); | ||
388 | nv_wr32(dev, 0x3270, 0); | ||
389 | |||
390 | /* guessing that all the 0x34xx regs aren't on NV50 */ | ||
391 | if (!IS_G80) { | ||
392 | nv_wr32(dev, 0x340c, nv_ro32(dev, ramfc, 0x88/4)); | ||
393 | nv_wr32(dev, 0x3400, nv_ro32(dev, ramfc, 0x8c/4)); | ||
394 | nv_wr32(dev, 0x3404, nv_ro32(dev, ramfc, 0x90/4)); | ||
395 | nv_wr32(dev, 0x3408, nv_ro32(dev, ramfc, 0x94/4)); | ||
396 | nv_wr32(dev, 0x3410, nv_ro32(dev, ramfc, 0x98/4)); | ||
397 | } | ||
398 | |||
399 | dev_priv->engine.instmem.finish_access(dev); | ||
400 | |||
401 | nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0); | ||
402 | nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, 0); | ||
403 | nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, chan->id | (1<<16)); | ||
404 | return 0; | ||
405 | } | ||
406 | |||
407 | int | ||
408 | nv50_fifo_unload_context(struct drm_device *dev) | ||
409 | { | ||
410 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
411 | struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; | ||
412 | struct nouveau_gpuobj *ramfc, *cache; | ||
413 | struct nouveau_channel *chan = NULL; | ||
414 | int chid, get, put, ptr; | ||
415 | |||
416 | NV_DEBUG(dev, "\n"); | ||
417 | |||
418 | chid = pfifo->channel_id(dev); | ||
419 | if (chid < 0 || chid >= dev_priv->engine.fifo.channels) | ||
420 | return 0; | ||
421 | |||
422 | chan = dev_priv->fifos[chid]; | ||
423 | if (!chan) { | ||
424 | NV_ERROR(dev, "Inactive channel on PFIFO: %d\n", chid); | ||
425 | return -EINVAL; | ||
426 | } | ||
427 | NV_DEBUG(dev, "ch%d\n", chan->id); | ||
428 | ramfc = chan->ramfc->gpuobj; | ||
429 | cache = chan->cache->gpuobj; | ||
430 | |||
431 | dev_priv->engine.instmem.prepare_access(dev, true); | ||
432 | |||
433 | nv_wo32(dev, ramfc, 0x00/4, nv_rd32(dev, 0x3330)); | ||
434 | nv_wo32(dev, ramfc, 0x04/4, nv_rd32(dev, 0x3334)); | ||
435 | nv_wo32(dev, ramfc, 0x08/4, nv_rd32(dev, 0x3240)); | ||
436 | nv_wo32(dev, ramfc, 0x0c/4, nv_rd32(dev, 0x3320)); | ||
437 | nv_wo32(dev, ramfc, 0x10/4, nv_rd32(dev, 0x3244)); | ||
438 | nv_wo32(dev, ramfc, 0x14/4, nv_rd32(dev, 0x3328)); | ||
439 | nv_wo32(dev, ramfc, 0x18/4, nv_rd32(dev, 0x3368)); | ||
440 | nv_wo32(dev, ramfc, 0x1c/4, nv_rd32(dev, 0x336c)); | ||
441 | nv_wo32(dev, ramfc, 0x20/4, nv_rd32(dev, 0x3370)); | ||
442 | nv_wo32(dev, ramfc, 0x24/4, nv_rd32(dev, 0x3374)); | ||
443 | nv_wo32(dev, ramfc, 0x28/4, nv_rd32(dev, 0x3378)); | ||
444 | nv_wo32(dev, ramfc, 0x2c/4, nv_rd32(dev, 0x337c)); | ||
445 | nv_wo32(dev, ramfc, 0x30/4, nv_rd32(dev, 0x3228)); | ||
446 | nv_wo32(dev, ramfc, 0x34/4, nv_rd32(dev, 0x3364)); | ||
447 | nv_wo32(dev, ramfc, 0x38/4, nv_rd32(dev, 0x32a0)); | ||
448 | nv_wo32(dev, ramfc, 0x3c/4, nv_rd32(dev, 0x3224)); | ||
449 | nv_wo32(dev, ramfc, 0x40/4, nv_rd32(dev, 0x324c)); | ||
450 | nv_wo32(dev, ramfc, 0x44/4, nv_rd32(dev, 0x2044)); | ||
451 | nv_wo32(dev, ramfc, 0x48/4, nv_rd32(dev, 0x322c)); | ||
452 | nv_wo32(dev, ramfc, 0x4c/4, nv_rd32(dev, 0x3234)); | ||
453 | nv_wo32(dev, ramfc, 0x50/4, nv_rd32(dev, 0x3340)); | ||
454 | nv_wo32(dev, ramfc, 0x54/4, nv_rd32(dev, 0x3344)); | ||
455 | nv_wo32(dev, ramfc, 0x58/4, nv_rd32(dev, 0x3280)); | ||
456 | nv_wo32(dev, ramfc, 0x5c/4, nv_rd32(dev, 0x3254)); | ||
457 | nv_wo32(dev, ramfc, 0x60/4, nv_rd32(dev, 0x3260)); | ||
458 | nv_wo32(dev, ramfc, 0x64/4, nv_rd32(dev, 0x3264)); | ||
459 | nv_wo32(dev, ramfc, 0x68/4, nv_rd32(dev, 0x3268)); | ||
460 | nv_wo32(dev, ramfc, 0x6c/4, nv_rd32(dev, 0x326c)); | ||
461 | nv_wo32(dev, ramfc, 0x70/4, nv_rd32(dev, 0x32e4)); | ||
462 | nv_wo32(dev, ramfc, 0x74/4, nv_rd32(dev, 0x3248)); | ||
463 | nv_wo32(dev, ramfc, 0x78/4, nv_rd32(dev, 0x2088)); | ||
464 | nv_wo32(dev, ramfc, 0x7c/4, nv_rd32(dev, 0x2058)); | ||
465 | nv_wo32(dev, ramfc, 0x80/4, nv_rd32(dev, 0x2210)); | ||
466 | |||
467 | put = (nv_rd32(dev, NV03_PFIFO_CACHE1_PUT) & 0x7ff) >> 2; | ||
468 | get = (nv_rd32(dev, NV03_PFIFO_CACHE1_GET) & 0x7ff) >> 2; | ||
469 | ptr = 0; | ||
470 | while (put != get) { | ||
471 | nv_wo32(dev, cache, ptr++, | ||
472 | nv_rd32(dev, NV40_PFIFO_CACHE1_METHOD(get))); | ||
473 | nv_wo32(dev, cache, ptr++, | ||
474 | nv_rd32(dev, NV40_PFIFO_CACHE1_DATA(get))); | ||
475 | get = (get + 1) & 0x1ff; | ||
476 | } | ||
477 | |||
478 | /* guessing that all the 0x34xx regs aren't on NV50 */ | ||
479 | if (!IS_G80) { | ||
480 | nv_wo32(dev, ramfc, 0x84/4, ptr >> 1); | ||
481 | nv_wo32(dev, ramfc, 0x88/4, nv_rd32(dev, 0x340c)); | ||
482 | nv_wo32(dev, ramfc, 0x8c/4, nv_rd32(dev, 0x3400)); | ||
483 | nv_wo32(dev, ramfc, 0x90/4, nv_rd32(dev, 0x3404)); | ||
484 | nv_wo32(dev, ramfc, 0x94/4, nv_rd32(dev, 0x3408)); | ||
485 | nv_wo32(dev, ramfc, 0x98/4, nv_rd32(dev, 0x3410)); | ||
486 | } | ||
487 | |||
488 | dev_priv->engine.instmem.finish_access(dev); | ||
489 | |||
490 | /*XXX: probably reload ch127 (NULL) state back too */ | ||
491 | nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, 127); | ||
492 | return 0; | ||
493 | } | ||
494 | |||
diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c new file mode 100644 index 000000000000..177d8229336f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv50_graph.c | |||
@@ -0,0 +1,385 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007 Ben Skeggs. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include "drmP.h" | ||
28 | #include "drm.h" | ||
29 | #include "nouveau_drv.h" | ||
30 | |||
31 | MODULE_FIRMWARE("nouveau/nv50.ctxprog"); | ||
32 | MODULE_FIRMWARE("nouveau/nv50.ctxvals"); | ||
33 | MODULE_FIRMWARE("nouveau/nv84.ctxprog"); | ||
34 | MODULE_FIRMWARE("nouveau/nv84.ctxvals"); | ||
35 | MODULE_FIRMWARE("nouveau/nv86.ctxprog"); | ||
36 | MODULE_FIRMWARE("nouveau/nv86.ctxvals"); | ||
37 | MODULE_FIRMWARE("nouveau/nv92.ctxprog"); | ||
38 | MODULE_FIRMWARE("nouveau/nv92.ctxvals"); | ||
39 | MODULE_FIRMWARE("nouveau/nv94.ctxprog"); | ||
40 | MODULE_FIRMWARE("nouveau/nv94.ctxvals"); | ||
41 | MODULE_FIRMWARE("nouveau/nv96.ctxprog"); | ||
42 | MODULE_FIRMWARE("nouveau/nv96.ctxvals"); | ||
43 | MODULE_FIRMWARE("nouveau/nv98.ctxprog"); | ||
44 | MODULE_FIRMWARE("nouveau/nv98.ctxvals"); | ||
45 | MODULE_FIRMWARE("nouveau/nva0.ctxprog"); | ||
46 | MODULE_FIRMWARE("nouveau/nva0.ctxvals"); | ||
47 | MODULE_FIRMWARE("nouveau/nva5.ctxprog"); | ||
48 | MODULE_FIRMWARE("nouveau/nva5.ctxvals"); | ||
49 | MODULE_FIRMWARE("nouveau/nva8.ctxprog"); | ||
50 | MODULE_FIRMWARE("nouveau/nva8.ctxvals"); | ||
51 | MODULE_FIRMWARE("nouveau/nvaa.ctxprog"); | ||
52 | MODULE_FIRMWARE("nouveau/nvaa.ctxvals"); | ||
53 | MODULE_FIRMWARE("nouveau/nvac.ctxprog"); | ||
54 | MODULE_FIRMWARE("nouveau/nvac.ctxvals"); | ||
55 | |||
56 | #define IS_G80 ((dev_priv->chipset & 0xf0) == 0x50) | ||
57 | |||
58 | static void | ||
59 | nv50_graph_init_reset(struct drm_device *dev) | ||
60 | { | ||
61 | uint32_t pmc_e = NV_PMC_ENABLE_PGRAPH | (1 << 21); | ||
62 | |||
63 | NV_DEBUG(dev, "\n"); | ||
64 | |||
65 | nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) & ~pmc_e); | ||
66 | nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) | pmc_e); | ||
67 | } | ||
68 | |||
69 | static void | ||
70 | nv50_graph_init_intr(struct drm_device *dev) | ||
71 | { | ||
72 | NV_DEBUG(dev, "\n"); | ||
73 | |||
74 | nv_wr32(dev, NV03_PGRAPH_INTR, 0xffffffff); | ||
75 | nv_wr32(dev, 0x400138, 0xffffffff); | ||
76 | nv_wr32(dev, NV40_PGRAPH_INTR_EN, 0xffffffff); | ||
77 | } | ||
78 | |||
79 | static void | ||
80 | nv50_graph_init_regs__nv(struct drm_device *dev) | ||
81 | { | ||
82 | NV_DEBUG(dev, "\n"); | ||
83 | |||
84 | nv_wr32(dev, 0x400804, 0xc0000000); | ||
85 | nv_wr32(dev, 0x406800, 0xc0000000); | ||
86 | nv_wr32(dev, 0x400c04, 0xc0000000); | ||
87 | nv_wr32(dev, 0x401804, 0xc0000000); | ||
88 | nv_wr32(dev, 0x405018, 0xc0000000); | ||
89 | nv_wr32(dev, 0x402000, 0xc0000000); | ||
90 | |||
91 | nv_wr32(dev, 0x400108, 0xffffffff); | ||
92 | |||
93 | nv_wr32(dev, 0x400824, 0x00004000); | ||
94 | nv_wr32(dev, 0x400500, 0x00010001); | ||
95 | } | ||
96 | |||
97 | static void | ||
98 | nv50_graph_init_regs(struct drm_device *dev) | ||
99 | { | ||
100 | NV_DEBUG(dev, "\n"); | ||
101 | |||
102 | nv_wr32(dev, NV04_PGRAPH_DEBUG_3, | ||
103 | (1 << 2) /* HW_CONTEXT_SWITCH_ENABLED */); | ||
104 | nv_wr32(dev, 0x402ca8, 0x800); | ||
105 | } | ||
106 | |||
107 | static int | ||
108 | nv50_graph_init_ctxctl(struct drm_device *dev) | ||
109 | { | ||
110 | NV_DEBUG(dev, "\n"); | ||
111 | |||
112 | nv40_grctx_init(dev); | ||
113 | |||
114 | nv_wr32(dev, 0x400320, 4); | ||
115 | nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, 0); | ||
116 | nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, 0); | ||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | int | ||
121 | nv50_graph_init(struct drm_device *dev) | ||
122 | { | ||
123 | int ret; | ||
124 | |||
125 | NV_DEBUG(dev, "\n"); | ||
126 | |||
127 | nv50_graph_init_reset(dev); | ||
128 | nv50_graph_init_regs__nv(dev); | ||
129 | nv50_graph_init_regs(dev); | ||
130 | nv50_graph_init_intr(dev); | ||
131 | |||
132 | ret = nv50_graph_init_ctxctl(dev); | ||
133 | if (ret) | ||
134 | return ret; | ||
135 | |||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | void | ||
140 | nv50_graph_takedown(struct drm_device *dev) | ||
141 | { | ||
142 | NV_DEBUG(dev, "\n"); | ||
143 | nv40_grctx_fini(dev); | ||
144 | } | ||
145 | |||
146 | void | ||
147 | nv50_graph_fifo_access(struct drm_device *dev, bool enabled) | ||
148 | { | ||
149 | const uint32_t mask = 0x00010001; | ||
150 | |||
151 | if (enabled) | ||
152 | nv_wr32(dev, 0x400500, nv_rd32(dev, 0x400500) | mask); | ||
153 | else | ||
154 | nv_wr32(dev, 0x400500, nv_rd32(dev, 0x400500) & ~mask); | ||
155 | } | ||
156 | |||
157 | struct nouveau_channel * | ||
158 | nv50_graph_channel(struct drm_device *dev) | ||
159 | { | ||
160 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
161 | uint32_t inst; | ||
162 | int i; | ||
163 | |||
164 | inst = nv_rd32(dev, NV50_PGRAPH_CTXCTL_CUR); | ||
165 | if (!(inst & NV50_PGRAPH_CTXCTL_CUR_LOADED)) | ||
166 | return NULL; | ||
167 | inst = (inst & NV50_PGRAPH_CTXCTL_CUR_INSTANCE) << 12; | ||
168 | |||
169 | for (i = 0; i < dev_priv->engine.fifo.channels; i++) { | ||
170 | struct nouveau_channel *chan = dev_priv->fifos[i]; | ||
171 | |||
172 | if (chan && chan->ramin && chan->ramin->instance == inst) | ||
173 | return chan; | ||
174 | } | ||
175 | |||
176 | return NULL; | ||
177 | } | ||
178 | |||
179 | int | ||
180 | nv50_graph_create_context(struct nouveau_channel *chan) | ||
181 | { | ||
182 | struct drm_device *dev = chan->dev; | ||
183 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
184 | struct nouveau_gpuobj *ramin = chan->ramin->gpuobj; | ||
185 | struct nouveau_gpuobj *ctx; | ||
186 | uint32_t grctx_size = 0x70000; | ||
187 | int hdr, ret; | ||
188 | |||
189 | NV_DEBUG(dev, "ch%d\n", chan->id); | ||
190 | |||
191 | ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, grctx_size, 0x1000, | ||
192 | NVOBJ_FLAG_ZERO_ALLOC | | ||
193 | NVOBJ_FLAG_ZERO_FREE, &chan->ramin_grctx); | ||
194 | if (ret) | ||
195 | return ret; | ||
196 | ctx = chan->ramin_grctx->gpuobj; | ||
197 | |||
198 | hdr = IS_G80 ? 0x200 : 0x20; | ||
199 | dev_priv->engine.instmem.prepare_access(dev, true); | ||
200 | nv_wo32(dev, ramin, (hdr + 0x00)/4, 0x00190002); | ||
201 | nv_wo32(dev, ramin, (hdr + 0x04)/4, chan->ramin_grctx->instance + | ||
202 | grctx_size - 1); | ||
203 | nv_wo32(dev, ramin, (hdr + 0x08)/4, chan->ramin_grctx->instance); | ||
204 | nv_wo32(dev, ramin, (hdr + 0x0c)/4, 0); | ||
205 | nv_wo32(dev, ramin, (hdr + 0x10)/4, 0); | ||
206 | nv_wo32(dev, ramin, (hdr + 0x14)/4, 0x00010000); | ||
207 | dev_priv->engine.instmem.finish_access(dev); | ||
208 | |||
209 | dev_priv->engine.instmem.prepare_access(dev, true); | ||
210 | nv40_grctx_vals_load(dev, ctx); | ||
211 | nv_wo32(dev, ctx, 0x00000/4, chan->ramin->instance >> 12); | ||
212 | if ((dev_priv->chipset & 0xf0) == 0xa0) | ||
213 | nv_wo32(dev, ctx, 0x00004/4, 0x00000000); | ||
214 | else | ||
215 | nv_wo32(dev, ctx, 0x0011c/4, 0x00000000); | ||
216 | dev_priv->engine.instmem.finish_access(dev); | ||
217 | |||
218 | return 0; | ||
219 | } | ||
220 | |||
221 | void | ||
222 | nv50_graph_destroy_context(struct nouveau_channel *chan) | ||
223 | { | ||
224 | struct drm_device *dev = chan->dev; | ||
225 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
226 | int i, hdr = IS_G80 ? 0x200 : 0x20; | ||
227 | |||
228 | NV_DEBUG(dev, "ch%d\n", chan->id); | ||
229 | |||
230 | if (!chan->ramin || !chan->ramin->gpuobj) | ||
231 | return; | ||
232 | |||
233 | dev_priv->engine.instmem.prepare_access(dev, true); | ||
234 | for (i = hdr; i < hdr + 24; i += 4) | ||
235 | nv_wo32(dev, chan->ramin->gpuobj, i/4, 0); | ||
236 | dev_priv->engine.instmem.finish_access(dev); | ||
237 | |||
238 | nouveau_gpuobj_ref_del(dev, &chan->ramin_grctx); | ||
239 | } | ||
240 | |||
241 | static int | ||
242 | nv50_graph_do_load_context(struct drm_device *dev, uint32_t inst) | ||
243 | { | ||
244 | uint32_t fifo = nv_rd32(dev, 0x400500); | ||
245 | |||
246 | nv_wr32(dev, 0x400500, fifo & ~1); | ||
247 | nv_wr32(dev, 0x400784, inst); | ||
248 | nv_wr32(dev, 0x400824, nv_rd32(dev, 0x400824) | 0x40); | ||
249 | nv_wr32(dev, 0x400320, nv_rd32(dev, 0x400320) | 0x11); | ||
250 | nv_wr32(dev, 0x400040, 0xffffffff); | ||
251 | (void)nv_rd32(dev, 0x400040); | ||
252 | nv_wr32(dev, 0x400040, 0x00000000); | ||
253 | nv_wr32(dev, 0x400304, nv_rd32(dev, 0x400304) | 1); | ||
254 | |||
255 | if (nouveau_wait_for_idle(dev)) | ||
256 | nv_wr32(dev, 0x40032c, inst | (1<<31)); | ||
257 | nv_wr32(dev, 0x400500, fifo); | ||
258 | |||
259 | return 0; | ||
260 | } | ||
261 | |||
262 | int | ||
263 | nv50_graph_load_context(struct nouveau_channel *chan) | ||
264 | { | ||
265 | uint32_t inst = chan->ramin->instance >> 12; | ||
266 | |||
267 | NV_DEBUG(chan->dev, "ch%d\n", chan->id); | ||
268 | return nv50_graph_do_load_context(chan->dev, inst); | ||
269 | } | ||
270 | |||
271 | int | ||
272 | nv50_graph_unload_context(struct drm_device *dev) | ||
273 | { | ||
274 | uint32_t inst, fifo = nv_rd32(dev, 0x400500); | ||
275 | |||
276 | inst = nv_rd32(dev, NV50_PGRAPH_CTXCTL_CUR); | ||
277 | if (!(inst & NV50_PGRAPH_CTXCTL_CUR_LOADED)) | ||
278 | return 0; | ||
279 | inst &= NV50_PGRAPH_CTXCTL_CUR_INSTANCE; | ||
280 | |||
281 | nv_wr32(dev, 0x400500, fifo & ~1); | ||
282 | nv_wr32(dev, 0x400784, inst); | ||
283 | nv_wr32(dev, 0x400824, nv_rd32(dev, 0x400824) | 0x20); | ||
284 | nv_wr32(dev, 0x400304, nv_rd32(dev, 0x400304) | 0x01); | ||
285 | nouveau_wait_for_idle(dev); | ||
286 | nv_wr32(dev, 0x400500, fifo); | ||
287 | |||
288 | nv_wr32(dev, NV50_PGRAPH_CTXCTL_CUR, inst); | ||
289 | return 0; | ||
290 | } | ||
291 | |||
292 | void | ||
293 | nv50_graph_context_switch(struct drm_device *dev) | ||
294 | { | ||
295 | uint32_t inst; | ||
296 | |||
297 | nv50_graph_unload_context(dev); | ||
298 | |||
299 | inst = nv_rd32(dev, NV50_PGRAPH_CTXCTL_NEXT); | ||
300 | inst &= NV50_PGRAPH_CTXCTL_NEXT_INSTANCE; | ||
301 | nv50_graph_do_load_context(dev, inst); | ||
302 | |||
303 | nv_wr32(dev, NV40_PGRAPH_INTR_EN, nv_rd32(dev, | ||
304 | NV40_PGRAPH_INTR_EN) | NV_PGRAPH_INTR_CONTEXT_SWITCH); | ||
305 | } | ||
306 | |||
307 | static int | ||
308 | nv50_graph_nvsw_dma_vblsem(struct nouveau_channel *chan, int grclass, | ||
309 | int mthd, uint32_t data) | ||
310 | { | ||
311 | struct nouveau_gpuobj_ref *ref = NULL; | ||
312 | |||
313 | if (nouveau_gpuobj_ref_find(chan, data, &ref)) | ||
314 | return -ENOENT; | ||
315 | |||
316 | if (nouveau_notifier_offset(ref->gpuobj, NULL)) | ||
317 | return -EINVAL; | ||
318 | |||
319 | chan->nvsw.vblsem = ref->gpuobj; | ||
320 | chan->nvsw.vblsem_offset = ~0; | ||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | static int | ||
325 | nv50_graph_nvsw_vblsem_offset(struct nouveau_channel *chan, int grclass, | ||
326 | int mthd, uint32_t data) | ||
327 | { | ||
328 | if (nouveau_notifier_offset(chan->nvsw.vblsem, &data)) | ||
329 | return -ERANGE; | ||
330 | |||
331 | chan->nvsw.vblsem_offset = data >> 2; | ||
332 | return 0; | ||
333 | } | ||
334 | |||
335 | static int | ||
336 | nv50_graph_nvsw_vblsem_release_val(struct nouveau_channel *chan, int grclass, | ||
337 | int mthd, uint32_t data) | ||
338 | { | ||
339 | chan->nvsw.vblsem_rval = data; | ||
340 | return 0; | ||
341 | } | ||
342 | |||
343 | static int | ||
344 | nv50_graph_nvsw_vblsem_release(struct nouveau_channel *chan, int grclass, | ||
345 | int mthd, uint32_t data) | ||
346 | { | ||
347 | struct drm_device *dev = chan->dev; | ||
348 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
349 | |||
350 | if (!chan->nvsw.vblsem || chan->nvsw.vblsem_offset == ~0 || data > 1) | ||
351 | return -EINVAL; | ||
352 | |||
353 | if (!(nv_rd32(dev, NV50_PDISPLAY_INTR_EN) & | ||
354 | NV50_PDISPLAY_INTR_EN_VBLANK_CRTC_(data))) { | ||
355 | nv_wr32(dev, NV50_PDISPLAY_INTR_1, | ||
356 | NV50_PDISPLAY_INTR_1_VBLANK_CRTC_(data)); | ||
357 | nv_wr32(dev, NV50_PDISPLAY_INTR_EN, nv_rd32(dev, | ||
358 | NV50_PDISPLAY_INTR_EN) | | ||
359 | NV50_PDISPLAY_INTR_EN_VBLANK_CRTC_(data)); | ||
360 | } | ||
361 | |||
362 | list_add(&chan->nvsw.vbl_wait, &dev_priv->vbl_waiting); | ||
363 | return 0; | ||
364 | } | ||
365 | |||
366 | static struct nouveau_pgraph_object_method nv50_graph_nvsw_methods[] = { | ||
367 | { 0x018c, nv50_graph_nvsw_dma_vblsem }, | ||
368 | { 0x0400, nv50_graph_nvsw_vblsem_offset }, | ||
369 | { 0x0404, nv50_graph_nvsw_vblsem_release_val }, | ||
370 | { 0x0408, nv50_graph_nvsw_vblsem_release }, | ||
371 | {} | ||
372 | }; | ||
373 | |||
374 | struct nouveau_pgraph_object_class nv50_graph_grclass[] = { | ||
375 | { 0x506e, true, nv50_graph_nvsw_methods }, /* nvsw */ | ||
376 | { 0x0030, false, NULL }, /* null */ | ||
377 | { 0x5039, false, NULL }, /* m2mf */ | ||
378 | { 0x502d, false, NULL }, /* 2d */ | ||
379 | { 0x50c0, false, NULL }, /* compute */ | ||
380 | { 0x5097, false, NULL }, /* tesla (nv50) */ | ||
381 | { 0x8297, false, NULL }, /* tesla (nv80/nv90) */ | ||
382 | { 0x8397, false, NULL }, /* tesla (nva0) */ | ||
383 | { 0x8597, false, NULL }, /* tesla (nva8) */ | ||
384 | {} | ||
385 | }; | ||
diff --git a/drivers/gpu/drm/nouveau/nv50_instmem.c b/drivers/gpu/drm/nouveau/nv50_instmem.c new file mode 100644 index 000000000000..94400f777e7f --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv50_instmem.c | |||
@@ -0,0 +1,509 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007 Ben Skeggs. | ||
3 | * | ||
4 | * All Rights Reserved. | ||
5 | * | ||
6 | * Permission is hereby granted, free of charge, to any person obtaining | ||
7 | * a copy of this software and associated documentation files (the | ||
8 | * "Software"), to deal in the Software without restriction, including | ||
9 | * without limitation the rights to use, copy, modify, merge, publish, | ||
10 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
11 | * permit persons to whom the Software is furnished to do so, subject to | ||
12 | * the following conditions: | ||
13 | * | ||
14 | * The above copyright notice and this permission notice (including the | ||
15 | * next paragraph) shall be included in all copies or substantial | ||
16 | * portions of the Software. | ||
17 | * | ||
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
19 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
20 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
21 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
22 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
23 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
24 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
25 | * | ||
26 | */ | ||
27 | |||
28 | #include "drmP.h" | ||
29 | #include "drm.h" | ||
30 | #include "nouveau_drv.h" | ||
31 | |||
32 | struct nv50_instmem_priv { | ||
33 | uint32_t save1700[5]; /* 0x1700->0x1710 */ | ||
34 | |||
35 | struct nouveau_gpuobj_ref *pramin_pt; | ||
36 | struct nouveau_gpuobj_ref *pramin_bar; | ||
37 | struct nouveau_gpuobj_ref *fb_bar; | ||
38 | |||
39 | bool last_access_wr; | ||
40 | }; | ||
41 | |||
42 | #define NV50_INSTMEM_PAGE_SHIFT 12 | ||
43 | #define NV50_INSTMEM_PAGE_SIZE (1 << NV50_INSTMEM_PAGE_SHIFT) | ||
44 | #define NV50_INSTMEM_PT_SIZE(a) (((a) >> 12) << 3) | ||
45 | |||
46 | /*NOTE: - Assumes 0x1700 already covers the correct MiB of PRAMIN | ||
47 | */ | ||
48 | #define BAR0_WI32(g, o, v) do { \ | ||
49 | uint32_t offset; \ | ||
50 | if ((g)->im_backing) { \ | ||
51 | offset = (g)->im_backing_start; \ | ||
52 | } else { \ | ||
53 | offset = chan->ramin->gpuobj->im_backing_start; \ | ||
54 | offset += (g)->im_pramin->start; \ | ||
55 | } \ | ||
56 | offset += (o); \ | ||
57 | nv_wr32(dev, NV_RAMIN + (offset & 0xfffff), (v)); \ | ||
58 | } while (0) | ||
59 | |||
60 | int | ||
61 | nv50_instmem_init(struct drm_device *dev) | ||
62 | { | ||
63 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
64 | struct nouveau_channel *chan; | ||
65 | uint32_t c_offset, c_size, c_ramfc, c_vmpd, c_base, pt_size; | ||
66 | struct nv50_instmem_priv *priv; | ||
67 | int ret, i; | ||
68 | uint32_t v, save_nv001700; | ||
69 | |||
70 | priv = kzalloc(sizeof(*priv), GFP_KERNEL); | ||
71 | if (!priv) | ||
72 | return -ENOMEM; | ||
73 | dev_priv->engine.instmem.priv = priv; | ||
74 | |||
75 | /* Save state, will restore at takedown. */ | ||
76 | for (i = 0x1700; i <= 0x1710; i += 4) | ||
77 | priv->save1700[(i-0x1700)/4] = nv_rd32(dev, i); | ||
78 | |||
79 | /* Reserve the last MiB of VRAM, we should probably try to avoid | ||
80 | * setting up the below tables over the top of the VBIOS image at | ||
81 | * some point. | ||
82 | */ | ||
83 | dev_priv->ramin_rsvd_vram = 1 << 20; | ||
84 | c_offset = nouveau_mem_fb_amount(dev) - dev_priv->ramin_rsvd_vram; | ||
85 | c_size = 128 << 10; | ||
86 | c_vmpd = ((dev_priv->chipset & 0xf0) == 0x50) ? 0x1400 : 0x200; | ||
87 | c_ramfc = ((dev_priv->chipset & 0xf0) == 0x50) ? 0x0 : 0x20; | ||
88 | c_base = c_vmpd + 0x4000; | ||
89 | pt_size = NV50_INSTMEM_PT_SIZE(dev_priv->ramin_size); | ||
90 | |||
91 | NV_DEBUG(dev, " Rsvd VRAM base: 0x%08x\n", c_offset); | ||
92 | NV_DEBUG(dev, " VBIOS image: 0x%08x\n", | ||
93 | (nv_rd32(dev, 0x619f04) & ~0xff) << 8); | ||
94 | NV_DEBUG(dev, " Aperture size: %d MiB\n", dev_priv->ramin_size >> 20); | ||
95 | NV_DEBUG(dev, " PT size: %d KiB\n", pt_size >> 10); | ||
96 | |||
97 | /* Determine VM layout, we need to do this first to make sure | ||
98 | * we allocate enough memory for all the page tables. | ||
99 | */ | ||
100 | dev_priv->vm_gart_base = roundup(NV50_VM_BLOCK, NV50_VM_BLOCK); | ||
101 | dev_priv->vm_gart_size = NV50_VM_BLOCK; | ||
102 | |||
103 | dev_priv->vm_vram_base = dev_priv->vm_gart_base + dev_priv->vm_gart_size; | ||
104 | dev_priv->vm_vram_size = nouveau_mem_fb_amount(dev); | ||
105 | if (dev_priv->vm_vram_size > NV50_VM_MAX_VRAM) | ||
106 | dev_priv->vm_vram_size = NV50_VM_MAX_VRAM; | ||
107 | dev_priv->vm_vram_size = roundup(dev_priv->vm_vram_size, NV50_VM_BLOCK); | ||
108 | dev_priv->vm_vram_pt_nr = dev_priv->vm_vram_size / NV50_VM_BLOCK; | ||
109 | |||
110 | dev_priv->vm_end = dev_priv->vm_vram_base + dev_priv->vm_vram_size; | ||
111 | |||
112 | NV_DEBUG(dev, "NV50VM: GART 0x%016llx-0x%016llx\n", | ||
113 | dev_priv->vm_gart_base, | ||
114 | dev_priv->vm_gart_base + dev_priv->vm_gart_size - 1); | ||
115 | NV_DEBUG(dev, "NV50VM: VRAM 0x%016llx-0x%016llx\n", | ||
116 | dev_priv->vm_vram_base, | ||
117 | dev_priv->vm_vram_base + dev_priv->vm_vram_size - 1); | ||
118 | |||
119 | c_size += dev_priv->vm_vram_pt_nr * (NV50_VM_BLOCK / 65536 * 8); | ||
120 | |||
121 | /* Map BAR0 PRAMIN aperture over the memory we want to use */ | ||
122 | save_nv001700 = nv_rd32(dev, NV50_PUNK_BAR0_PRAMIN); | ||
123 | nv_wr32(dev, NV50_PUNK_BAR0_PRAMIN, (c_offset >> 16)); | ||
124 | |||
125 | /* Create a fake channel, and use it as our "dummy" channels 0/127. | ||
126 | * The main reason for creating a channel is so we can use the gpuobj | ||
127 | * code. However, it's probably worth noting that NVIDIA also setup | ||
128 | * their channels 0/127 with the same values they configure here. | ||
129 | * So, there may be some other reason for doing this. | ||
130 | * | ||
131 | * Have to create the entire channel manually, as the real channel | ||
132 | * creation code assumes we have PRAMIN access, and we don't until | ||
133 | * we're done here. | ||
134 | */ | ||
135 | chan = kzalloc(sizeof(*chan), GFP_KERNEL); | ||
136 | if (!chan) | ||
137 | return -ENOMEM; | ||
138 | chan->id = 0; | ||
139 | chan->dev = dev; | ||
140 | chan->file_priv = (struct drm_file *)-2; | ||
141 | dev_priv->fifos[0] = dev_priv->fifos[127] = chan; | ||
142 | |||
143 | /* Channel's PRAMIN object + heap */ | ||
144 | ret = nouveau_gpuobj_new_fake(dev, 0, c_offset, c_size, 0, | ||
145 | NULL, &chan->ramin); | ||
146 | if (ret) | ||
147 | return ret; | ||
148 | |||
149 | if (nouveau_mem_init_heap(&chan->ramin_heap, c_base, c_size - c_base)) | ||
150 | return -ENOMEM; | ||
151 | |||
152 | /* RAMFC + zero channel's PRAMIN up to start of VM pagedir */ | ||
153 | ret = nouveau_gpuobj_new_fake(dev, c_ramfc, c_offset + c_ramfc, | ||
154 | 0x4000, 0, NULL, &chan->ramfc); | ||
155 | if (ret) | ||
156 | return ret; | ||
157 | |||
158 | for (i = 0; i < c_vmpd; i += 4) | ||
159 | BAR0_WI32(chan->ramin->gpuobj, i, 0); | ||
160 | |||
161 | /* VM page directory */ | ||
162 | ret = nouveau_gpuobj_new_fake(dev, c_vmpd, c_offset + c_vmpd, | ||
163 | 0x4000, 0, &chan->vm_pd, NULL); | ||
164 | if (ret) | ||
165 | return ret; | ||
166 | for (i = 0; i < 0x4000; i += 8) { | ||
167 | BAR0_WI32(chan->vm_pd, i + 0x00, 0x00000000); | ||
168 | BAR0_WI32(chan->vm_pd, i + 0x04, 0x00000000); | ||
169 | } | ||
170 | |||
171 | /* PRAMIN page table, cheat and map into VM at 0x0000000000. | ||
172 | * We map the entire fake channel into the start of the PRAMIN BAR | ||
173 | */ | ||
174 | ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, pt_size, 0x1000, | ||
175 | 0, &priv->pramin_pt); | ||
176 | if (ret) | ||
177 | return ret; | ||
178 | |||
179 | for (i = 0, v = c_offset; i < pt_size; i += 8, v += 0x1000) { | ||
180 | if (v < (c_offset + c_size)) | ||
181 | BAR0_WI32(priv->pramin_pt->gpuobj, i + 0, v | 1); | ||
182 | else | ||
183 | BAR0_WI32(priv->pramin_pt->gpuobj, i + 0, 0x00000009); | ||
184 | BAR0_WI32(priv->pramin_pt->gpuobj, i + 4, 0x00000000); | ||
185 | } | ||
186 | |||
187 | BAR0_WI32(chan->vm_pd, 0x00, priv->pramin_pt->instance | 0x63); | ||
188 | BAR0_WI32(chan->vm_pd, 0x04, 0x00000000); | ||
189 | |||
190 | /* VRAM page table(s), mapped into VM at +1GiB */ | ||
191 | for (i = 0; i < dev_priv->vm_vram_pt_nr; i++) { | ||
192 | ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, | ||
193 | NV50_VM_BLOCK/65536*8, 0, 0, | ||
194 | &chan->vm_vram_pt[i]); | ||
195 | if (ret) { | ||
196 | NV_ERROR(dev, "Error creating VRAM page tables: %d\n", | ||
197 | ret); | ||
198 | dev_priv->vm_vram_pt_nr = i; | ||
199 | return ret; | ||
200 | } | ||
201 | dev_priv->vm_vram_pt[i] = chan->vm_vram_pt[i]->gpuobj; | ||
202 | |||
203 | for (v = 0; v < dev_priv->vm_vram_pt[i]->im_pramin->size; | ||
204 | v += 4) | ||
205 | BAR0_WI32(dev_priv->vm_vram_pt[i], v, 0); | ||
206 | |||
207 | BAR0_WI32(chan->vm_pd, 0x10 + (i*8), | ||
208 | chan->vm_vram_pt[i]->instance | 0x61); | ||
209 | BAR0_WI32(chan->vm_pd, 0x14 + (i*8), 0); | ||
210 | } | ||
211 | |||
212 | /* DMA object for PRAMIN BAR */ | ||
213 | ret = nouveau_gpuobj_new_ref(dev, chan, chan, 0, 6*4, 16, 0, | ||
214 | &priv->pramin_bar); | ||
215 | if (ret) | ||
216 | return ret; | ||
217 | BAR0_WI32(priv->pramin_bar->gpuobj, 0x00, 0x7fc00000); | ||
218 | BAR0_WI32(priv->pramin_bar->gpuobj, 0x04, dev_priv->ramin_size - 1); | ||
219 | BAR0_WI32(priv->pramin_bar->gpuobj, 0x08, 0x00000000); | ||
220 | BAR0_WI32(priv->pramin_bar->gpuobj, 0x0c, 0x00000000); | ||
221 | BAR0_WI32(priv->pramin_bar->gpuobj, 0x10, 0x00000000); | ||
222 | BAR0_WI32(priv->pramin_bar->gpuobj, 0x14, 0x00000000); | ||
223 | |||
224 | /* DMA object for FB BAR */ | ||
225 | ret = nouveau_gpuobj_new_ref(dev, chan, chan, 0, 6*4, 16, 0, | ||
226 | &priv->fb_bar); | ||
227 | if (ret) | ||
228 | return ret; | ||
229 | BAR0_WI32(priv->fb_bar->gpuobj, 0x00, 0x7fc00000); | ||
230 | BAR0_WI32(priv->fb_bar->gpuobj, 0x04, 0x40000000 + | ||
231 | drm_get_resource_len(dev, 1) - 1); | ||
232 | BAR0_WI32(priv->fb_bar->gpuobj, 0x08, 0x40000000); | ||
233 | BAR0_WI32(priv->fb_bar->gpuobj, 0x0c, 0x00000000); | ||
234 | BAR0_WI32(priv->fb_bar->gpuobj, 0x10, 0x00000000); | ||
235 | BAR0_WI32(priv->fb_bar->gpuobj, 0x14, 0x00000000); | ||
236 | |||
237 | /* Poke the relevant regs, and pray it works :) */ | ||
238 | nv_wr32(dev, NV50_PUNK_BAR_CFG_BASE, (chan->ramin->instance >> 12)); | ||
239 | nv_wr32(dev, NV50_PUNK_UNK1710, 0); | ||
240 | nv_wr32(dev, NV50_PUNK_BAR_CFG_BASE, (chan->ramin->instance >> 12) | | ||
241 | NV50_PUNK_BAR_CFG_BASE_VALID); | ||
242 | nv_wr32(dev, NV50_PUNK_BAR1_CTXDMA, (priv->fb_bar->instance >> 4) | | ||
243 | NV50_PUNK_BAR1_CTXDMA_VALID); | ||
244 | nv_wr32(dev, NV50_PUNK_BAR3_CTXDMA, (priv->pramin_bar->instance >> 4) | | ||
245 | NV50_PUNK_BAR3_CTXDMA_VALID); | ||
246 | |||
247 | for (i = 0; i < 8; i++) | ||
248 | nv_wr32(dev, 0x1900 + (i*4), 0); | ||
249 | |||
250 | /* Assume that praying isn't enough, check that we can re-read the | ||
251 | * entire fake channel back from the PRAMIN BAR */ | ||
252 | dev_priv->engine.instmem.prepare_access(dev, false); | ||
253 | for (i = 0; i < c_size; i += 4) { | ||
254 | if (nv_rd32(dev, NV_RAMIN + i) != nv_ri32(dev, i)) { | ||
255 | NV_ERROR(dev, "Error reading back PRAMIN at 0x%08x\n", | ||
256 | i); | ||
257 | dev_priv->engine.instmem.finish_access(dev); | ||
258 | return -EINVAL; | ||
259 | } | ||
260 | } | ||
261 | dev_priv->engine.instmem.finish_access(dev); | ||
262 | |||
263 | nv_wr32(dev, NV50_PUNK_BAR0_PRAMIN, save_nv001700); | ||
264 | |||
265 | /* Global PRAMIN heap */ | ||
266 | if (nouveau_mem_init_heap(&dev_priv->ramin_heap, | ||
267 | c_size, dev_priv->ramin_size - c_size)) { | ||
268 | dev_priv->ramin_heap = NULL; | ||
269 | NV_ERROR(dev, "Failed to init RAMIN heap\n"); | ||
270 | } | ||
271 | |||
272 | /*XXX: incorrect, but needed to make hash func "work" */ | ||
273 | dev_priv->ramht_offset = 0x10000; | ||
274 | dev_priv->ramht_bits = 9; | ||
275 | dev_priv->ramht_size = (1 << dev_priv->ramht_bits); | ||
276 | return 0; | ||
277 | } | ||
278 | |||
279 | void | ||
280 | nv50_instmem_takedown(struct drm_device *dev) | ||
281 | { | ||
282 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
283 | struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv; | ||
284 | struct nouveau_channel *chan = dev_priv->fifos[0]; | ||
285 | int i; | ||
286 | |||
287 | NV_DEBUG(dev, "\n"); | ||
288 | |||
289 | if (!priv) | ||
290 | return; | ||
291 | |||
292 | /* Restore state from before init */ | ||
293 | for (i = 0x1700; i <= 0x1710; i += 4) | ||
294 | nv_wr32(dev, i, priv->save1700[(i - 0x1700) / 4]); | ||
295 | |||
296 | nouveau_gpuobj_ref_del(dev, &priv->fb_bar); | ||
297 | nouveau_gpuobj_ref_del(dev, &priv->pramin_bar); | ||
298 | nouveau_gpuobj_ref_del(dev, &priv->pramin_pt); | ||
299 | |||
300 | /* Destroy dummy channel */ | ||
301 | if (chan) { | ||
302 | for (i = 0; i < dev_priv->vm_vram_pt_nr; i++) { | ||
303 | nouveau_gpuobj_ref_del(dev, &chan->vm_vram_pt[i]); | ||
304 | dev_priv->vm_vram_pt[i] = NULL; | ||
305 | } | ||
306 | dev_priv->vm_vram_pt_nr = 0; | ||
307 | |||
308 | nouveau_gpuobj_del(dev, &chan->vm_pd); | ||
309 | nouveau_gpuobj_ref_del(dev, &chan->ramfc); | ||
310 | nouveau_gpuobj_ref_del(dev, &chan->ramin); | ||
311 | nouveau_mem_takedown(&chan->ramin_heap); | ||
312 | |||
313 | dev_priv->fifos[0] = dev_priv->fifos[127] = NULL; | ||
314 | kfree(chan); | ||
315 | } | ||
316 | |||
317 | dev_priv->engine.instmem.priv = NULL; | ||
318 | kfree(priv); | ||
319 | } | ||
320 | |||
321 | int | ||
322 | nv50_instmem_suspend(struct drm_device *dev) | ||
323 | { | ||
324 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
325 | struct nouveau_channel *chan = dev_priv->fifos[0]; | ||
326 | struct nouveau_gpuobj *ramin = chan->ramin->gpuobj; | ||
327 | int i; | ||
328 | |||
329 | ramin->im_backing_suspend = vmalloc(ramin->im_pramin->size); | ||
330 | if (!ramin->im_backing_suspend) | ||
331 | return -ENOMEM; | ||
332 | |||
333 | for (i = 0; i < ramin->im_pramin->size; i += 4) | ||
334 | ramin->im_backing_suspend[i/4] = nv_ri32(dev, i); | ||
335 | return 0; | ||
336 | } | ||
337 | |||
338 | void | ||
339 | nv50_instmem_resume(struct drm_device *dev) | ||
340 | { | ||
341 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
342 | struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv; | ||
343 | struct nouveau_channel *chan = dev_priv->fifos[0]; | ||
344 | struct nouveau_gpuobj *ramin = chan->ramin->gpuobj; | ||
345 | int i; | ||
346 | |||
347 | nv_wr32(dev, NV50_PUNK_BAR0_PRAMIN, (ramin->im_backing_start >> 16)); | ||
348 | for (i = 0; i < ramin->im_pramin->size; i += 4) | ||
349 | BAR0_WI32(ramin, i, ramin->im_backing_suspend[i/4]); | ||
350 | vfree(ramin->im_backing_suspend); | ||
351 | ramin->im_backing_suspend = NULL; | ||
352 | |||
353 | /* Poke the relevant regs, and pray it works :) */ | ||
354 | nv_wr32(dev, NV50_PUNK_BAR_CFG_BASE, (chan->ramin->instance >> 12)); | ||
355 | nv_wr32(dev, NV50_PUNK_UNK1710, 0); | ||
356 | nv_wr32(dev, NV50_PUNK_BAR_CFG_BASE, (chan->ramin->instance >> 12) | | ||
357 | NV50_PUNK_BAR_CFG_BASE_VALID); | ||
358 | nv_wr32(dev, NV50_PUNK_BAR1_CTXDMA, (priv->fb_bar->instance >> 4) | | ||
359 | NV50_PUNK_BAR1_CTXDMA_VALID); | ||
360 | nv_wr32(dev, NV50_PUNK_BAR3_CTXDMA, (priv->pramin_bar->instance >> 4) | | ||
361 | NV50_PUNK_BAR3_CTXDMA_VALID); | ||
362 | |||
363 | for (i = 0; i < 8; i++) | ||
364 | nv_wr32(dev, 0x1900 + (i*4), 0); | ||
365 | } | ||
366 | |||
367 | int | ||
368 | nv50_instmem_populate(struct drm_device *dev, struct nouveau_gpuobj *gpuobj, | ||
369 | uint32_t *sz) | ||
370 | { | ||
371 | int ret; | ||
372 | |||
373 | if (gpuobj->im_backing) | ||
374 | return -EINVAL; | ||
375 | |||
376 | *sz = (*sz + (NV50_INSTMEM_PAGE_SIZE-1)) & ~(NV50_INSTMEM_PAGE_SIZE-1); | ||
377 | if (*sz == 0) | ||
378 | return -EINVAL; | ||
379 | |||
380 | ret = nouveau_bo_new(dev, NULL, *sz, 0, TTM_PL_FLAG_VRAM, 0, 0x0000, | ||
381 | true, false, &gpuobj->im_backing); | ||
382 | if (ret) { | ||
383 | NV_ERROR(dev, "error getting PRAMIN backing pages: %d\n", ret); | ||
384 | return ret; | ||
385 | } | ||
386 | |||
387 | ret = nouveau_bo_pin(gpuobj->im_backing, TTM_PL_FLAG_VRAM); | ||
388 | if (ret) { | ||
389 | NV_ERROR(dev, "error pinning PRAMIN backing VRAM: %d\n", ret); | ||
390 | nouveau_bo_ref(NULL, &gpuobj->im_backing); | ||
391 | return ret; | ||
392 | } | ||
393 | |||
394 | gpuobj->im_backing_start = gpuobj->im_backing->bo.mem.mm_node->start; | ||
395 | gpuobj->im_backing_start <<= PAGE_SHIFT; | ||
396 | |||
397 | return 0; | ||
398 | } | ||
399 | |||
400 | void | ||
401 | nv50_instmem_clear(struct drm_device *dev, struct nouveau_gpuobj *gpuobj) | ||
402 | { | ||
403 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
404 | |||
405 | if (gpuobj && gpuobj->im_backing) { | ||
406 | if (gpuobj->im_bound) | ||
407 | dev_priv->engine.instmem.unbind(dev, gpuobj); | ||
408 | nouveau_bo_unpin(gpuobj->im_backing); | ||
409 | nouveau_bo_ref(NULL, &gpuobj->im_backing); | ||
410 | gpuobj->im_backing = NULL; | ||
411 | } | ||
412 | } | ||
413 | |||
414 | int | ||
415 | nv50_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj) | ||
416 | { | ||
417 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
418 | struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv; | ||
419 | uint32_t pte, pte_end, vram; | ||
420 | |||
421 | if (!gpuobj->im_backing || !gpuobj->im_pramin || gpuobj->im_bound) | ||
422 | return -EINVAL; | ||
423 | |||
424 | NV_DEBUG(dev, "st=0x%0llx sz=0x%0llx\n", | ||
425 | gpuobj->im_pramin->start, gpuobj->im_pramin->size); | ||
426 | |||
427 | pte = (gpuobj->im_pramin->start >> 12) << 3; | ||
428 | pte_end = ((gpuobj->im_pramin->size >> 12) << 3) + pte; | ||
429 | vram = gpuobj->im_backing_start; | ||
430 | |||
431 | NV_DEBUG(dev, "pramin=0x%llx, pte=%d, pte_end=%d\n", | ||
432 | gpuobj->im_pramin->start, pte, pte_end); | ||
433 | NV_DEBUG(dev, "first vram page: 0x%08x\n", gpuobj->im_backing_start); | ||
434 | |||
435 | dev_priv->engine.instmem.prepare_access(dev, true); | ||
436 | while (pte < pte_end) { | ||
437 | nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 0)/4, vram | 1); | ||
438 | nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 4)/4, 0x00000000); | ||
439 | |||
440 | pte += 8; | ||
441 | vram += NV50_INSTMEM_PAGE_SIZE; | ||
442 | } | ||
443 | dev_priv->engine.instmem.finish_access(dev); | ||
444 | |||
445 | nv_wr32(dev, 0x100c80, 0x00040001); | ||
446 | if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) { | ||
447 | NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (1)\n"); | ||
448 | NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80)); | ||
449 | return -EBUSY; | ||
450 | } | ||
451 | |||
452 | nv_wr32(dev, 0x100c80, 0x00060001); | ||
453 | if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) { | ||
454 | NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n"); | ||
455 | NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80)); | ||
456 | return -EBUSY; | ||
457 | } | ||
458 | |||
459 | gpuobj->im_bound = 1; | ||
460 | return 0; | ||
461 | } | ||
462 | |||
463 | int | ||
464 | nv50_instmem_unbind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj) | ||
465 | { | ||
466 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
467 | struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv; | ||
468 | uint32_t pte, pte_end; | ||
469 | |||
470 | if (gpuobj->im_bound == 0) | ||
471 | return -EINVAL; | ||
472 | |||
473 | pte = (gpuobj->im_pramin->start >> 12) << 3; | ||
474 | pte_end = ((gpuobj->im_pramin->size >> 12) << 3) + pte; | ||
475 | |||
476 | dev_priv->engine.instmem.prepare_access(dev, true); | ||
477 | while (pte < pte_end) { | ||
478 | nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 0)/4, 0x00000009); | ||
479 | nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 4)/4, 0x00000000); | ||
480 | pte += 8; | ||
481 | } | ||
482 | dev_priv->engine.instmem.finish_access(dev); | ||
483 | |||
484 | gpuobj->im_bound = 0; | ||
485 | return 0; | ||
486 | } | ||
487 | |||
488 | void | ||
489 | nv50_instmem_prepare_access(struct drm_device *dev, bool write) | ||
490 | { | ||
491 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
492 | struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv; | ||
493 | |||
494 | priv->last_access_wr = write; | ||
495 | } | ||
496 | |||
497 | void | ||
498 | nv50_instmem_finish_access(struct drm_device *dev) | ||
499 | { | ||
500 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
501 | struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv; | ||
502 | |||
503 | if (priv->last_access_wr) { | ||
504 | nv_wr32(dev, 0x070000, 0x00000001); | ||
505 | if (!nv_wait(0x070000, 0x00000001, 0x00000000)) | ||
506 | NV_ERROR(dev, "PRAMIN flush timeout\n"); | ||
507 | } | ||
508 | } | ||
509 | |||
diff --git a/drivers/gpu/drm/nouveau/nv50_mc.c b/drivers/gpu/drm/nouveau/nv50_mc.c new file mode 100644 index 000000000000..e0a9c3faa202 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv50_mc.c | |||
@@ -0,0 +1,40 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007 Ben Skeggs. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include "drmP.h" | ||
28 | #include "drm.h" | ||
29 | #include "nouveau_drv.h" | ||
30 | |||
31 | int | ||
32 | nv50_mc_init(struct drm_device *dev) | ||
33 | { | ||
34 | nv_wr32(dev, NV03_PMC_ENABLE, 0xFFFFFFFF); | ||
35 | return 0; | ||
36 | } | ||
37 | |||
38 | void nv50_mc_takedown(struct drm_device *dev) | ||
39 | { | ||
40 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c new file mode 100644 index 000000000000..8c280463a664 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv50_sor.c | |||
@@ -0,0 +1,309 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2008 Maarten Maathuis. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include "drmP.h" | ||
28 | #include "drm_crtc_helper.h" | ||
29 | |||
30 | #define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO) | ||
31 | #include "nouveau_reg.h" | ||
32 | #include "nouveau_drv.h" | ||
33 | #include "nouveau_dma.h" | ||
34 | #include "nouveau_encoder.h" | ||
35 | #include "nouveau_connector.h" | ||
36 | #include "nouveau_crtc.h" | ||
37 | #include "nv50_display.h" | ||
38 | |||
39 | static void | ||
40 | nv50_sor_disconnect(struct nouveau_encoder *nv_encoder) | ||
41 | { | ||
42 | struct drm_device *dev = to_drm_encoder(nv_encoder)->dev; | ||
43 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
44 | struct nouveau_channel *evo = dev_priv->evo; | ||
45 | int ret; | ||
46 | |||
47 | NV_DEBUG(dev, "Disconnecting SOR %d\n", nv_encoder->or); | ||
48 | |||
49 | ret = RING_SPACE(evo, 2); | ||
50 | if (ret) { | ||
51 | NV_ERROR(dev, "no space while disconnecting SOR\n"); | ||
52 | return; | ||
53 | } | ||
54 | BEGIN_RING(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1); | ||
55 | OUT_RING(evo, 0); | ||
56 | } | ||
57 | |||
58 | static void | ||
59 | nv50_sor_dp_link_train(struct drm_encoder *encoder) | ||
60 | { | ||
61 | struct drm_device *dev = encoder->dev; | ||
62 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
63 | struct bit_displayport_encoder_table *dpe; | ||
64 | int dpe_headerlen; | ||
65 | |||
66 | dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen); | ||
67 | if (!dpe) { | ||
68 | NV_ERROR(dev, "SOR-%d: no DP encoder table!\n", nv_encoder->or); | ||
69 | return; | ||
70 | } | ||
71 | |||
72 | if (dpe->script0) { | ||
73 | NV_DEBUG(dev, "SOR-%d: running DP script 0\n", nv_encoder->or); | ||
74 | nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script0), | ||
75 | nv_encoder->dcb); | ||
76 | } | ||
77 | |||
78 | if (!nouveau_dp_link_train(encoder)) | ||
79 | NV_ERROR(dev, "SOR-%d: link training failed\n", nv_encoder->or); | ||
80 | |||
81 | if (dpe->script1) { | ||
82 | NV_DEBUG(dev, "SOR-%d: running DP script 1\n", nv_encoder->or); | ||
83 | nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script1), | ||
84 | nv_encoder->dcb); | ||
85 | } | ||
86 | } | ||
87 | |||
88 | static void | ||
89 | nv50_sor_dpms(struct drm_encoder *encoder, int mode) | ||
90 | { | ||
91 | struct drm_device *dev = encoder->dev; | ||
92 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
93 | uint32_t val; | ||
94 | int or = nv_encoder->or; | ||
95 | |||
96 | NV_DEBUG(dev, "or %d mode %d\n", or, mode); | ||
97 | |||
98 | /* wait for it to be done */ | ||
99 | if (!nv_wait(NV50_PDISPLAY_SOR_DPMS_CTRL(or), | ||
100 | NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING, 0)) { | ||
101 | NV_ERROR(dev, "timeout: SOR_DPMS_CTRL_PENDING(%d) == 0\n", or); | ||
102 | NV_ERROR(dev, "SOR_DPMS_CTRL(%d) = 0x%08x\n", or, | ||
103 | nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_CTRL(or))); | ||
104 | } | ||
105 | |||
106 | val = nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_CTRL(or)); | ||
107 | |||
108 | if (mode == DRM_MODE_DPMS_ON) | ||
109 | val |= NV50_PDISPLAY_SOR_DPMS_CTRL_ON; | ||
110 | else | ||
111 | val &= ~NV50_PDISPLAY_SOR_DPMS_CTRL_ON; | ||
112 | |||
113 | nv_wr32(dev, NV50_PDISPLAY_SOR_DPMS_CTRL(or), val | | ||
114 | NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING); | ||
115 | if (!nv_wait(NV50_PDISPLAY_SOR_DPMS_STATE(or), | ||
116 | NV50_PDISPLAY_SOR_DPMS_STATE_WAIT, 0)) { | ||
117 | NV_ERROR(dev, "timeout: SOR_DPMS_STATE_WAIT(%d) == 0\n", or); | ||
118 | NV_ERROR(dev, "SOR_DPMS_STATE(%d) = 0x%08x\n", or, | ||
119 | nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_STATE(or))); | ||
120 | } | ||
121 | |||
122 | if (nv_encoder->dcb->type == OUTPUT_DP && mode == DRM_MODE_DPMS_ON) | ||
123 | nv50_sor_dp_link_train(encoder); | ||
124 | } | ||
125 | |||
126 | static void | ||
127 | nv50_sor_save(struct drm_encoder *encoder) | ||
128 | { | ||
129 | NV_ERROR(encoder->dev, "!!\n"); | ||
130 | } | ||
131 | |||
132 | static void | ||
133 | nv50_sor_restore(struct drm_encoder *encoder) | ||
134 | { | ||
135 | NV_ERROR(encoder->dev, "!!\n"); | ||
136 | } | ||
137 | |||
138 | static bool | ||
139 | nv50_sor_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, | ||
140 | struct drm_display_mode *adjusted_mode) | ||
141 | { | ||
142 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
143 | struct nouveau_connector *connector; | ||
144 | |||
145 | NV_DEBUG(encoder->dev, "or %d\n", nv_encoder->or); | ||
146 | |||
147 | connector = nouveau_encoder_connector_get(nv_encoder); | ||
148 | if (!connector) { | ||
149 | NV_ERROR(encoder->dev, "Encoder has no connector\n"); | ||
150 | return false; | ||
151 | } | ||
152 | |||
153 | if (connector->scaling_mode != DRM_MODE_SCALE_NONE && | ||
154 | connector->native_mode) { | ||
155 | int id = adjusted_mode->base.id; | ||
156 | *adjusted_mode = *connector->native_mode; | ||
157 | adjusted_mode->base.id = id; | ||
158 | } | ||
159 | |||
160 | return true; | ||
161 | } | ||
162 | |||
163 | static void | ||
164 | nv50_sor_prepare(struct drm_encoder *encoder) | ||
165 | { | ||
166 | } | ||
167 | |||
168 | static void | ||
169 | nv50_sor_commit(struct drm_encoder *encoder) | ||
170 | { | ||
171 | } | ||
172 | |||
173 | static void | ||
174 | nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, | ||
175 | struct drm_display_mode *adjusted_mode) | ||
176 | { | ||
177 | struct drm_nouveau_private *dev_priv = encoder->dev->dev_private; | ||
178 | struct nouveau_channel *evo = dev_priv->evo; | ||
179 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
180 | struct drm_device *dev = encoder->dev; | ||
181 | struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc); | ||
182 | uint32_t mode_ctl = 0; | ||
183 | int ret; | ||
184 | |||
185 | NV_DEBUG(dev, "or %d\n", nv_encoder->or); | ||
186 | |||
187 | nv50_sor_dpms(encoder, DRM_MODE_DPMS_ON); | ||
188 | |||
189 | switch (nv_encoder->dcb->type) { | ||
190 | case OUTPUT_TMDS: | ||
191 | if (nv_encoder->dcb->sorconf.link & 1) { | ||
192 | if (adjusted_mode->clock < 165000) | ||
193 | mode_ctl = 0x0100; | ||
194 | else | ||
195 | mode_ctl = 0x0500; | ||
196 | } else | ||
197 | mode_ctl = 0x0200; | ||
198 | break; | ||
199 | case OUTPUT_DP: | ||
200 | mode_ctl |= 0x00050000; | ||
201 | if (nv_encoder->dcb->sorconf.link & 1) | ||
202 | mode_ctl |= 0x00000800; | ||
203 | else | ||
204 | mode_ctl |= 0x00000900; | ||
205 | break; | ||
206 | default: | ||
207 | break; | ||
208 | } | ||
209 | |||
210 | if (crtc->index == 1) | ||
211 | mode_ctl |= NV50_EVO_SOR_MODE_CTRL_CRTC1; | ||
212 | else | ||
213 | mode_ctl |= NV50_EVO_SOR_MODE_CTRL_CRTC0; | ||
214 | |||
215 | if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) | ||
216 | mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NHSYNC; | ||
217 | |||
218 | if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) | ||
219 | mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NVSYNC; | ||
220 | |||
221 | ret = RING_SPACE(evo, 2); | ||
222 | if (ret) { | ||
223 | NV_ERROR(dev, "no space while connecting SOR\n"); | ||
224 | return; | ||
225 | } | ||
226 | BEGIN_RING(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1); | ||
227 | OUT_RING(evo, mode_ctl); | ||
228 | } | ||
229 | |||
230 | static const struct drm_encoder_helper_funcs nv50_sor_helper_funcs = { | ||
231 | .dpms = nv50_sor_dpms, | ||
232 | .save = nv50_sor_save, | ||
233 | .restore = nv50_sor_restore, | ||
234 | .mode_fixup = nv50_sor_mode_fixup, | ||
235 | .prepare = nv50_sor_prepare, | ||
236 | .commit = nv50_sor_commit, | ||
237 | .mode_set = nv50_sor_mode_set, | ||
238 | .detect = NULL | ||
239 | }; | ||
240 | |||
241 | static void | ||
242 | nv50_sor_destroy(struct drm_encoder *encoder) | ||
243 | { | ||
244 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
245 | |||
246 | if (!encoder) | ||
247 | return; | ||
248 | |||
249 | NV_DEBUG(encoder->dev, "\n"); | ||
250 | |||
251 | drm_encoder_cleanup(encoder); | ||
252 | |||
253 | kfree(nv_encoder); | ||
254 | } | ||
255 | |||
256 | static const struct drm_encoder_funcs nv50_sor_encoder_funcs = { | ||
257 | .destroy = nv50_sor_destroy, | ||
258 | }; | ||
259 | |||
260 | int | ||
261 | nv50_sor_create(struct drm_device *dev, struct dcb_entry *entry) | ||
262 | { | ||
263 | struct nouveau_encoder *nv_encoder = NULL; | ||
264 | struct drm_encoder *encoder; | ||
265 | bool dum; | ||
266 | int type; | ||
267 | |||
268 | NV_DEBUG(dev, "\n"); | ||
269 | |||
270 | switch (entry->type) { | ||
271 | case OUTPUT_TMDS: | ||
272 | NV_INFO(dev, "Detected a TMDS output\n"); | ||
273 | type = DRM_MODE_ENCODER_TMDS; | ||
274 | break; | ||
275 | case OUTPUT_LVDS: | ||
276 | NV_INFO(dev, "Detected a LVDS output\n"); | ||
277 | type = DRM_MODE_ENCODER_LVDS; | ||
278 | |||
279 | if (nouveau_bios_parse_lvds_table(dev, 0, &dum, &dum)) { | ||
280 | NV_ERROR(dev, "Failed parsing LVDS table\n"); | ||
281 | return -EINVAL; | ||
282 | } | ||
283 | break; | ||
284 | case OUTPUT_DP: | ||
285 | NV_INFO(dev, "Detected a DP output\n"); | ||
286 | type = DRM_MODE_ENCODER_TMDS; | ||
287 | break; | ||
288 | default: | ||
289 | return -EINVAL; | ||
290 | } | ||
291 | |||
292 | nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); | ||
293 | if (!nv_encoder) | ||
294 | return -ENOMEM; | ||
295 | encoder = to_drm_encoder(nv_encoder); | ||
296 | |||
297 | nv_encoder->dcb = entry; | ||
298 | nv_encoder->or = ffs(entry->or) - 1; | ||
299 | |||
300 | nv_encoder->disconnect = nv50_sor_disconnect; | ||
301 | |||
302 | drm_encoder_init(dev, encoder, &nv50_sor_encoder_funcs, type); | ||
303 | drm_encoder_helper_add(encoder, &nv50_sor_helper_funcs); | ||
304 | |||
305 | encoder->possible_crtcs = entry->heads; | ||
306 | encoder->possible_clones = 0; | ||
307 | |||
308 | return 0; | ||
309 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nvreg.h b/drivers/gpu/drm/nouveau/nvreg.h new file mode 100644 index 000000000000..5998c35237b0 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvreg.h | |||
@@ -0,0 +1,535 @@ | |||
1 | /* $XConsortium: nvreg.h /main/2 1996/10/28 05:13:41 kaleb $ */ | ||
2 | /* | ||
3 | * Copyright 1996-1997 David J. McKay | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
6 | * copy of this software and associated documentation files (the "Software"), | ||
7 | * to deal in the Software without restriction, including without limitation | ||
8 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||
9 | * and/or sell copies of the Software, and to permit persons to whom the | ||
10 | * Software is furnished to do so, subject to the following conditions: | ||
11 | * | ||
12 | * The above copyright notice and this permission notice shall be included in | ||
13 | * all copies or substantial portions of the Software. | ||
14 | * | ||
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | ||
18 | * DAVID J. MCKAY BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | ||
19 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF | ||
20 | * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
21 | * SOFTWARE. | ||
22 | */ | ||
23 | |||
24 | /* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/nv/nvreg.h,v 1.6 2002/01/25 21:56:06 tsi Exp $ */ | ||
25 | |||
26 | #ifndef __NVREG_H_ | ||
27 | #define __NVREG_H_ | ||
28 | |||
29 | #define NV_PMC_OFFSET 0x00000000 | ||
30 | #define NV_PMC_SIZE 0x00001000 | ||
31 | |||
32 | #define NV_PBUS_OFFSET 0x00001000 | ||
33 | #define NV_PBUS_SIZE 0x00001000 | ||
34 | |||
35 | #define NV_PFIFO_OFFSET 0x00002000 | ||
36 | #define NV_PFIFO_SIZE 0x00002000 | ||
37 | |||
38 | #define NV_HDIAG_OFFSET 0x00005000 | ||
39 | #define NV_HDIAG_SIZE 0x00001000 | ||
40 | |||
41 | #define NV_PRAM_OFFSET 0x00006000 | ||
42 | #define NV_PRAM_SIZE 0x00001000 | ||
43 | |||
44 | #define NV_PVIDEO_OFFSET 0x00008000 | ||
45 | #define NV_PVIDEO_SIZE 0x00001000 | ||
46 | |||
47 | #define NV_PTIMER_OFFSET 0x00009000 | ||
48 | #define NV_PTIMER_SIZE 0x00001000 | ||
49 | |||
50 | #define NV_PPM_OFFSET 0x0000A000 | ||
51 | #define NV_PPM_SIZE 0x00001000 | ||
52 | |||
53 | #define NV_PTV_OFFSET 0x0000D000 | ||
54 | #define NV_PTV_SIZE 0x00001000 | ||
55 | |||
56 | #define NV_PRMVGA_OFFSET 0x000A0000 | ||
57 | #define NV_PRMVGA_SIZE 0x00020000 | ||
58 | |||
59 | #define NV_PRMVIO0_OFFSET 0x000C0000 | ||
60 | #define NV_PRMVIO_SIZE 0x00002000 | ||
61 | #define NV_PRMVIO1_OFFSET 0x000C2000 | ||
62 | |||
63 | #define NV_PFB_OFFSET 0x00100000 | ||
64 | #define NV_PFB_SIZE 0x00001000 | ||
65 | |||
66 | #define NV_PEXTDEV_OFFSET 0x00101000 | ||
67 | #define NV_PEXTDEV_SIZE 0x00001000 | ||
68 | |||
69 | #define NV_PME_OFFSET 0x00200000 | ||
70 | #define NV_PME_SIZE 0x00001000 | ||
71 | |||
72 | #define NV_PROM_OFFSET 0x00300000 | ||
73 | #define NV_PROM_SIZE 0x00010000 | ||
74 | |||
75 | #define NV_PGRAPH_OFFSET 0x00400000 | ||
76 | #define NV_PGRAPH_SIZE 0x00010000 | ||
77 | |||
78 | #define NV_PCRTC0_OFFSET 0x00600000 | ||
79 | #define NV_PCRTC0_SIZE 0x00002000 /* empirical */ | ||
80 | |||
81 | #define NV_PRMCIO0_OFFSET 0x00601000 | ||
82 | #define NV_PRMCIO_SIZE 0x00002000 | ||
83 | #define NV_PRMCIO1_OFFSET 0x00603000 | ||
84 | |||
85 | #define NV50_DISPLAY_OFFSET 0x00610000 | ||
86 | #define NV50_DISPLAY_SIZE 0x0000FFFF | ||
87 | |||
88 | #define NV_PRAMDAC0_OFFSET 0x00680000 | ||
89 | #define NV_PRAMDAC0_SIZE 0x00002000 | ||
90 | |||
91 | #define NV_PRMDIO0_OFFSET 0x00681000 | ||
92 | #define NV_PRMDIO_SIZE 0x00002000 | ||
93 | #define NV_PRMDIO1_OFFSET 0x00683000 | ||
94 | |||
95 | #define NV_PRAMIN_OFFSET 0x00700000 | ||
96 | #define NV_PRAMIN_SIZE 0x00100000 | ||
97 | |||
98 | #define NV_FIFO_OFFSET 0x00800000 | ||
99 | #define NV_FIFO_SIZE 0x00800000 | ||
100 | |||
101 | #define NV_PMC_BOOT_0 0x00000000 | ||
102 | #define NV_PMC_ENABLE 0x00000200 | ||
103 | |||
104 | #define NV_VIO_VSE2 0x000003c3 | ||
105 | #define NV_VIO_SRX 0x000003c4 | ||
106 | |||
107 | #define NV_CIO_CRX__COLOR 0x000003d4 | ||
108 | #define NV_CIO_CR__COLOR 0x000003d5 | ||
109 | |||
110 | #define NV_PBUS_DEBUG_1 0x00001084 | ||
111 | #define NV_PBUS_DEBUG_4 0x00001098 | ||
112 | #define NV_PBUS_DEBUG_DUALHEAD_CTL 0x000010f0 | ||
113 | #define NV_PBUS_POWERCTRL_1 0x00001584 | ||
114 | #define NV_PBUS_POWERCTRL_2 0x00001588 | ||
115 | #define NV_PBUS_POWERCTRL_4 0x00001590 | ||
116 | #define NV_PBUS_PCI_NV_19 0x0000184C | ||
117 | #define NV_PBUS_PCI_NV_20 0x00001850 | ||
118 | # define NV_PBUS_PCI_NV_20_ROM_SHADOW_DISABLED (0 << 0) | ||
119 | # define NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED (1 << 0) | ||
120 | |||
121 | #define NV_PFIFO_RAMHT 0x00002210 | ||
122 | |||
123 | #define NV_PTV_TV_INDEX 0x0000d220 | ||
124 | #define NV_PTV_TV_DATA 0x0000d224 | ||
125 | #define NV_PTV_HFILTER 0x0000d310 | ||
126 | #define NV_PTV_HFILTER2 0x0000d390 | ||
127 | #define NV_PTV_VFILTER 0x0000d510 | ||
128 | |||
129 | #define NV_PRMVIO_MISC__WRITE 0x000c03c2 | ||
130 | #define NV_PRMVIO_SRX 0x000c03c4 | ||
131 | #define NV_PRMVIO_SR 0x000c03c5 | ||
132 | # define NV_VIO_SR_RESET_INDEX 0x00 | ||
133 | # define NV_VIO_SR_CLOCK_INDEX 0x01 | ||
134 | # define NV_VIO_SR_PLANE_MASK_INDEX 0x02 | ||
135 | # define NV_VIO_SR_CHAR_MAP_INDEX 0x03 | ||
136 | # define NV_VIO_SR_MEM_MODE_INDEX 0x04 | ||
137 | #define NV_PRMVIO_MISC__READ 0x000c03cc | ||
138 | #define NV_PRMVIO_GRX 0x000c03ce | ||
139 | #define NV_PRMVIO_GX 0x000c03cf | ||
140 | # define NV_VIO_GX_SR_INDEX 0x00 | ||
141 | # define NV_VIO_GX_SREN_INDEX 0x01 | ||
142 | # define NV_VIO_GX_CCOMP_INDEX 0x02 | ||
143 | # define NV_VIO_GX_ROP_INDEX 0x03 | ||
144 | # define NV_VIO_GX_READ_MAP_INDEX 0x04 | ||
145 | # define NV_VIO_GX_MODE_INDEX 0x05 | ||
146 | # define NV_VIO_GX_MISC_INDEX 0x06 | ||
147 | # define NV_VIO_GX_DONT_CARE_INDEX 0x07 | ||
148 | # define NV_VIO_GX_BIT_MASK_INDEX 0x08 | ||
149 | |||
150 | #define NV_PFB_BOOT_0 0x00100000 | ||
151 | #define NV_PFB_CFG0 0x00100200 | ||
152 | #define NV_PFB_CFG1 0x00100204 | ||
153 | #define NV_PFB_CSTATUS 0x0010020C | ||
154 | #define NV_PFB_REFCTRL 0x00100210 | ||
155 | # define NV_PFB_REFCTRL_VALID_1 (1 << 31) | ||
156 | #define NV_PFB_PAD 0x0010021C | ||
157 | # define NV_PFB_PAD_CKE_NORMAL (1 << 0) | ||
158 | #define NV_PFB_TILE_NV10 0x00100240 | ||
159 | #define NV_PFB_TILE_SIZE_NV10 0x00100244 | ||
160 | #define NV_PFB_REF 0x001002D0 | ||
161 | # define NV_PFB_REF_CMD_REFRESH (1 << 0) | ||
162 | #define NV_PFB_PRE 0x001002D4 | ||
163 | # define NV_PFB_PRE_CMD_PRECHARGE (1 << 0) | ||
164 | #define NV_PFB_CLOSE_PAGE2 0x0010033C | ||
165 | #define NV_PFB_TILE_NV40 0x00100600 | ||
166 | #define NV_PFB_TILE_SIZE_NV40 0x00100604 | ||
167 | |||
168 | #define NV_PEXTDEV_BOOT_0 0x00101000 | ||
169 | # define NV_PEXTDEV_BOOT_0_STRAP_FP_IFACE_12BIT (8 << 12) | ||
170 | #define NV_PEXTDEV_BOOT_3 0x0010100c | ||
171 | |||
172 | #define NV_PCRTC_INTR_0 0x00600100 | ||
173 | # define NV_PCRTC_INTR_0_VBLANK (1 << 0) | ||
174 | #define NV_PCRTC_INTR_EN_0 0x00600140 | ||
175 | #define NV_PCRTC_START 0x00600800 | ||
176 | #define NV_PCRTC_CONFIG 0x00600804 | ||
177 | # define NV_PCRTC_CONFIG_START_ADDRESS_NON_VGA (1 << 0) | ||
178 | # define NV_PCRTC_CONFIG_START_ADDRESS_HSYNC (2 << 0) | ||
179 | #define NV_PCRTC_CURSOR_CONFIG 0x00600810 | ||
180 | # define NV_PCRTC_CURSOR_CONFIG_ENABLE_ENABLE (1 << 0) | ||
181 | # define NV_PCRTC_CURSOR_CONFIG_DOUBLE_SCAN_ENABLE (1 << 4) | ||
182 | # define NV_PCRTC_CURSOR_CONFIG_ADDRESS_SPACE_PNVM (1 << 8) | ||
183 | # define NV_PCRTC_CURSOR_CONFIG_CUR_BPP_32 (1 << 12) | ||
184 | # define NV_PCRTC_CURSOR_CONFIG_CUR_PIXELS_64 (1 << 16) | ||
185 | # define NV_PCRTC_CURSOR_CONFIG_CUR_LINES_32 (2 << 24) | ||
186 | # define NV_PCRTC_CURSOR_CONFIG_CUR_LINES_64 (4 << 24) | ||
187 | # define NV_PCRTC_CURSOR_CONFIG_CUR_BLEND_ALPHA (1 << 28) | ||
188 | |||
189 | /* note: PCRTC_GPIO is not available on nv10, and in fact aliases 0x600810 */ | ||
190 | #define NV_PCRTC_GPIO 0x00600818 | ||
191 | #define NV_PCRTC_GPIO_EXT 0x0060081c | ||
192 | #define NV_PCRTC_830 0x00600830 | ||
193 | #define NV_PCRTC_834 0x00600834 | ||
194 | #define NV_PCRTC_850 0x00600850 | ||
195 | #define NV_PCRTC_ENGINE_CTRL 0x00600860 | ||
196 | # define NV_CRTC_FSEL_I2C (1 << 4) | ||
197 | # define NV_CRTC_FSEL_OVERLAY (1 << 12) | ||
198 | |||
199 | #define NV_PRMCIO_ARX 0x006013c0 | ||
200 | #define NV_PRMCIO_AR__WRITE 0x006013c0 | ||
201 | #define NV_PRMCIO_AR__READ 0x006013c1 | ||
202 | # define NV_CIO_AR_MODE_INDEX 0x10 | ||
203 | # define NV_CIO_AR_OSCAN_INDEX 0x11 | ||
204 | # define NV_CIO_AR_PLANE_INDEX 0x12 | ||
205 | # define NV_CIO_AR_HPP_INDEX 0x13 | ||
206 | # define NV_CIO_AR_CSEL_INDEX 0x14 | ||
207 | #define NV_PRMCIO_INP0 0x006013c2 | ||
208 | #define NV_PRMCIO_CRX__COLOR 0x006013d4 | ||
209 | #define NV_PRMCIO_CR__COLOR 0x006013d5 | ||
210 | /* Standard VGA CRTC registers */ | ||
211 | # define NV_CIO_CR_HDT_INDEX 0x00 /* horizontal display total */ | ||
212 | # define NV_CIO_CR_HDE_INDEX 0x01 /* horizontal display end */ | ||
213 | # define NV_CIO_CR_HBS_INDEX 0x02 /* horizontal blanking start */ | ||
214 | # define NV_CIO_CR_HBE_INDEX 0x03 /* horizontal blanking end */ | ||
215 | # define NV_CIO_CR_HBE_4_0 4:0 | ||
216 | # define NV_CIO_CR_HRS_INDEX 0x04 /* horizontal retrace start */ | ||
217 | # define NV_CIO_CR_HRE_INDEX 0x05 /* horizontal retrace end */ | ||
218 | # define NV_CIO_CR_HRE_4_0 4:0 | ||
219 | # define NV_CIO_CR_HRE_HBE_5 7:7 | ||
220 | # define NV_CIO_CR_VDT_INDEX 0x06 /* vertical display total */ | ||
221 | # define NV_CIO_CR_OVL_INDEX 0x07 /* overflow bits */ | ||
222 | # define NV_CIO_CR_OVL_VDT_8 0:0 | ||
223 | # define NV_CIO_CR_OVL_VDE_8 1:1 | ||
224 | # define NV_CIO_CR_OVL_VRS_8 2:2 | ||
225 | # define NV_CIO_CR_OVL_VBS_8 3:3 | ||
226 | # define NV_CIO_CR_OVL_VDT_9 5:5 | ||
227 | # define NV_CIO_CR_OVL_VDE_9 6:6 | ||
228 | # define NV_CIO_CR_OVL_VRS_9 7:7 | ||
229 | # define NV_CIO_CR_RSAL_INDEX 0x08 /* normally "preset row scan" */ | ||
230 | # define NV_CIO_CR_CELL_HT_INDEX 0x09 /* cell height?! normally "max scan line" */ | ||
231 | # define NV_CIO_CR_CELL_HT_VBS_9 5:5 | ||
232 | # define NV_CIO_CR_CELL_HT_SCANDBL 7:7 | ||
233 | # define NV_CIO_CR_CURS_ST_INDEX 0x0a /* cursor start */ | ||
234 | # define NV_CIO_CR_CURS_END_INDEX 0x0b /* cursor end */ | ||
235 | # define NV_CIO_CR_SA_HI_INDEX 0x0c /* screen start address high */ | ||
236 | # define NV_CIO_CR_SA_LO_INDEX 0x0d /* screen start address low */ | ||
237 | # define NV_CIO_CR_TCOFF_HI_INDEX 0x0e /* cursor offset high */ | ||
238 | # define NV_CIO_CR_TCOFF_LO_INDEX 0x0f /* cursor offset low */ | ||
239 | # define NV_CIO_CR_VRS_INDEX 0x10 /* vertical retrace start */ | ||
240 | # define NV_CIO_CR_VRE_INDEX 0x11 /* vertical retrace end */ | ||
241 | # define NV_CIO_CR_VRE_3_0 3:0 | ||
242 | # define NV_CIO_CR_VDE_INDEX 0x12 /* vertical display end */ | ||
243 | # define NV_CIO_CR_OFFSET_INDEX 0x13 /* sets screen pitch */ | ||
244 | # define NV_CIO_CR_ULINE_INDEX 0x14 /* underline location */ | ||
245 | # define NV_CIO_CR_VBS_INDEX 0x15 /* vertical blank start */ | ||
246 | # define NV_CIO_CR_VBE_INDEX 0x16 /* vertical blank end */ | ||
247 | # define NV_CIO_CR_MODE_INDEX 0x17 /* crtc mode control */ | ||
248 | # define NV_CIO_CR_LCOMP_INDEX 0x18 /* line compare */ | ||
249 | /* Extended VGA CRTC registers */ | ||
250 | # define NV_CIO_CRE_RPC0_INDEX 0x19 /* repaint control 0 */ | ||
251 | # define NV_CIO_CRE_RPC0_OFFSET_10_8 7:5 | ||
252 | # define NV_CIO_CRE_RPC1_INDEX 0x1a /* repaint control 1 */ | ||
253 | # define NV_CIO_CRE_RPC1_LARGE 2:2 | ||
254 | # define NV_CIO_CRE_FF_INDEX 0x1b /* fifo control */ | ||
255 | # define NV_CIO_CRE_ENH_INDEX 0x1c /* enhanced? */ | ||
256 | # define NV_CIO_SR_LOCK_INDEX 0x1f /* crtc lock */ | ||
257 | # define NV_CIO_SR_UNLOCK_RW_VALUE 0x57 | ||
258 | # define NV_CIO_SR_LOCK_VALUE 0x99 | ||
259 | # define NV_CIO_CRE_FFLWM__INDEX 0x20 /* fifo low water mark */ | ||
260 | # define NV_CIO_CRE_21 0x21 /* vga shadow crtc lock */ | ||
261 | # define NV_CIO_CRE_LSR_INDEX 0x25 /* ? */ | ||
262 | # define NV_CIO_CRE_LSR_VDT_10 0:0 | ||
263 | # define NV_CIO_CRE_LSR_VDE_10 1:1 | ||
264 | # define NV_CIO_CRE_LSR_VRS_10 2:2 | ||
265 | # define NV_CIO_CRE_LSR_VBS_10 3:3 | ||
266 | # define NV_CIO_CRE_LSR_HBE_6 4:4 | ||
267 | # define NV_CIO_CR_ARX_INDEX 0x26 /* attribute index -- ro copy of 0x60.3c0 */ | ||
268 | # define NV_CIO_CRE_CHIP_ID_INDEX 0x27 /* chip revision */ | ||
269 | # define NV_CIO_CRE_PIXEL_INDEX 0x28 | ||
270 | # define NV_CIO_CRE_PIXEL_FORMAT 1:0 | ||
271 | # define NV_CIO_CRE_HEB__INDEX 0x2d /* horizontal extra bits? */ | ||
272 | # define NV_CIO_CRE_HEB_HDT_8 0:0 | ||
273 | # define NV_CIO_CRE_HEB_HDE_8 1:1 | ||
274 | # define NV_CIO_CRE_HEB_HBS_8 2:2 | ||
275 | # define NV_CIO_CRE_HEB_HRS_8 3:3 | ||
276 | # define NV_CIO_CRE_HEB_ILC_8 4:4 | ||
277 | # define NV_CIO_CRE_2E 0x2e /* some scratch or dummy reg to force writes to sink in */ | ||
278 | # define NV_CIO_CRE_HCUR_ADDR2_INDEX 0x2f /* cursor */ | ||
279 | # define NV_CIO_CRE_HCUR_ADDR0_INDEX 0x30 /* pixmap */ | ||
280 | # define NV_CIO_CRE_HCUR_ADDR0_ADR 6:0 | ||
281 | # define NV_CIO_CRE_HCUR_ASI 7:7 | ||
282 | # define NV_CIO_CRE_HCUR_ADDR1_INDEX 0x31 /* address */ | ||
283 | # define NV_CIO_CRE_HCUR_ADDR1_ENABLE 0:0 | ||
284 | # define NV_CIO_CRE_HCUR_ADDR1_CUR_DBL 1:1 | ||
285 | # define NV_CIO_CRE_HCUR_ADDR1_ADR 7:2 | ||
286 | # define NV_CIO_CRE_LCD__INDEX 0x33 | ||
287 | # define NV_CIO_CRE_LCD_LCD_SELECT 0:0 | ||
288 | # define NV_CIO_CRE_DDC0_STATUS__INDEX 0x36 | ||
289 | # define NV_CIO_CRE_DDC0_WR__INDEX 0x37 | ||
290 | # define NV_CIO_CRE_ILACE__INDEX 0x39 /* interlace */ | ||
291 | # define NV_CIO_CRE_SCRATCH3__INDEX 0x3b | ||
292 | # define NV_CIO_CRE_SCRATCH4__INDEX 0x3c | ||
293 | # define NV_CIO_CRE_DDC_STATUS__INDEX 0x3e | ||
294 | # define NV_CIO_CRE_DDC_WR__INDEX 0x3f | ||
295 | # define NV_CIO_CRE_EBR_INDEX 0x41 /* extra bits ? (vertical) */ | ||
296 | # define NV_CIO_CRE_EBR_VDT_11 0:0 | ||
297 | # define NV_CIO_CRE_EBR_VDE_11 2:2 | ||
298 | # define NV_CIO_CRE_EBR_VRS_11 4:4 | ||
299 | # define NV_CIO_CRE_EBR_VBS_11 6:6 | ||
300 | # define NV_CIO_CRE_43 0x43 | ||
301 | # define NV_CIO_CRE_44 0x44 /* head control */ | ||
302 | # define NV_CIO_CRE_CSB 0x45 /* colour saturation boost */ | ||
303 | # define NV_CIO_CRE_RCR 0x46 | ||
304 | # define NV_CIO_CRE_RCR_ENDIAN_BIG 7:7 | ||
305 | # define NV_CIO_CRE_47 0x47 /* extended fifo lwm, used on nv30+ */ | ||
306 | # define NV_CIO_CRE_49 0x49 | ||
307 | # define NV_CIO_CRE_4B 0x4b /* given patterns in 0x[2-3][a-c] regs, probably scratch 6 */ | ||
308 | # define NV_CIO_CRE_TVOUT_LATENCY 0x52 | ||
309 | # define NV_CIO_CRE_53 0x53 /* `fp_htiming' according to Haiku */ | ||
310 | # define NV_CIO_CRE_54 0x54 /* `fp_vtiming' according to Haiku */ | ||
311 | # define NV_CIO_CRE_57 0x57 /* index reg for cr58 */ | ||
312 | # define NV_CIO_CRE_58 0x58 /* data reg for cr57 */ | ||
313 | # define NV_CIO_CRE_59 0x59 /* related to on/off-chip-ness of digital outputs */ | ||
314 | # define NV_CIO_CRE_5B 0x5B /* newer colour saturation reg */ | ||
315 | # define NV_CIO_CRE_85 0x85 | ||
316 | # define NV_CIO_CRE_86 0x86 | ||
317 | #define NV_PRMCIO_INP0__COLOR 0x006013da | ||
318 | |||
319 | #define NV_PRAMDAC_CU_START_POS 0x00680300 | ||
320 | # define NV_PRAMDAC_CU_START_POS_X 15:0 | ||
321 | # define NV_PRAMDAC_CU_START_POS_Y 31:16 | ||
322 | #define NV_RAMDAC_NV10_CURSYNC 0x00680404 | ||
323 | |||
324 | #define NV_PRAMDAC_NVPLL_COEFF 0x00680500 | ||
325 | #define NV_PRAMDAC_MPLL_COEFF 0x00680504 | ||
326 | #define NV_PRAMDAC_VPLL_COEFF 0x00680508 | ||
327 | # define NV30_RAMDAC_ENABLE_VCO2 (8 << 4) | ||
328 | |||
329 | #define NV_PRAMDAC_PLL_COEFF_SELECT 0x0068050c | ||
330 | # define NV_PRAMDAC_PLL_COEFF_SELECT_USE_VPLL2_TRUE (4 << 0) | ||
331 | # define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_MPLL (1 << 8) | ||
332 | # define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_VPLL (2 << 8) | ||
333 | # define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_NVPLL (4 << 8) | ||
334 | # define NV_PRAMDAC_PLL_COEFF_SELECT_PLL_SOURCE_VPLL2 (8 << 8) | ||
335 | # define NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 (1 << 16) | ||
336 | # define NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1 (2 << 16) | ||
337 | # define NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2 (4 << 16) | ||
338 | # define NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2 (8 << 16) | ||
339 | # define NV_PRAMDAC_PLL_COEFF_SELECT_TV_CLK_SOURCE_VIP (1 << 20) | ||
340 | # define NV_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO_DB2 (1 << 28) | ||
341 | # define NV_PRAMDAC_PLL_COEFF_SELECT_VCLK2_RATIO_DB2 (2 << 28) | ||
342 | |||
343 | #define NV_PRAMDAC_PLL_SETUP_CONTROL 0x00680510 | ||
344 | #define NV_RAMDAC_VPLL2 0x00680520 | ||
345 | #define NV_PRAMDAC_SEL_CLK 0x00680524 | ||
346 | #define NV_RAMDAC_DITHER_NV11 0x00680528 | ||
347 | #define NV_PRAMDAC_DACCLK 0x0068052c | ||
348 | # define NV_PRAMDAC_DACCLK_SEL_DACCLK (1 << 0) | ||
349 | |||
350 | #define NV_RAMDAC_NVPLL_B 0x00680570 | ||
351 | #define NV_RAMDAC_MPLL_B 0x00680574 | ||
352 | #define NV_RAMDAC_VPLL_B 0x00680578 | ||
353 | #define NV_RAMDAC_VPLL2_B 0x0068057c | ||
354 | # define NV31_RAMDAC_ENABLE_VCO2 (8 << 28) | ||
355 | #define NV_PRAMDAC_580 0x00680580 | ||
356 | # define NV_RAMDAC_580_VPLL1_ACTIVE (1 << 8) | ||
357 | # define NV_RAMDAC_580_VPLL2_ACTIVE (1 << 28) | ||
358 | |||
359 | #define NV_PRAMDAC_GENERAL_CONTROL 0x00680600 | ||
360 | # define NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON (3 << 4) | ||
361 | # define NV_PRAMDAC_GENERAL_CONTROL_VGA_STATE_SEL (1 << 8) | ||
362 | # define NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL (1 << 12) | ||
363 | # define NV_PRAMDAC_GENERAL_CONTROL_TERMINATION_75OHM (2 << 16) | ||
364 | # define NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS (1 << 20) | ||
365 | # define NV_PRAMDAC_GENERAL_CONTROL_PIPE_LONG (2 << 28) | ||
366 | #define NV_PRAMDAC_TEST_CONTROL 0x00680608 | ||
367 | # define NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED (1 << 12) | ||
368 | # define NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF (1 << 16) | ||
369 | # define NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI (1 << 28) | ||
370 | #define NV_PRAMDAC_TESTPOINT_DATA 0x00680610 | ||
371 | # define NV_PRAMDAC_TESTPOINT_DATA_NOTBLANK (8 << 28) | ||
372 | #define NV_PRAMDAC_630 0x00680630 | ||
373 | #define NV_PRAMDAC_634 0x00680634 | ||
374 | |||
375 | #define NV_PRAMDAC_TV_SETUP 0x00680700 | ||
376 | #define NV_PRAMDAC_TV_VTOTAL 0x00680720 | ||
377 | #define NV_PRAMDAC_TV_VSKEW 0x00680724 | ||
378 | #define NV_PRAMDAC_TV_VSYNC_DELAY 0x00680728 | ||
379 | #define NV_PRAMDAC_TV_HTOTAL 0x0068072c | ||
380 | #define NV_PRAMDAC_TV_HSKEW 0x00680730 | ||
381 | #define NV_PRAMDAC_TV_HSYNC_DELAY 0x00680734 | ||
382 | #define NV_PRAMDAC_TV_HSYNC_DELAY2 0x00680738 | ||
383 | |||
384 | #define NV_PRAMDAC_TV_SETUP 0x00680700 | ||
385 | |||
386 | #define NV_PRAMDAC_FP_VDISPLAY_END 0x00680800 | ||
387 | #define NV_PRAMDAC_FP_VTOTAL 0x00680804 | ||
388 | #define NV_PRAMDAC_FP_VCRTC 0x00680808 | ||
389 | #define NV_PRAMDAC_FP_VSYNC_START 0x0068080c | ||
390 | #define NV_PRAMDAC_FP_VSYNC_END 0x00680810 | ||
391 | #define NV_PRAMDAC_FP_VVALID_START 0x00680814 | ||
392 | #define NV_PRAMDAC_FP_VVALID_END 0x00680818 | ||
393 | #define NV_PRAMDAC_FP_HDISPLAY_END 0x00680820 | ||
394 | #define NV_PRAMDAC_FP_HTOTAL 0x00680824 | ||
395 | #define NV_PRAMDAC_FP_HCRTC 0x00680828 | ||
396 | #define NV_PRAMDAC_FP_HSYNC_START 0x0068082c | ||
397 | #define NV_PRAMDAC_FP_HSYNC_END 0x00680830 | ||
398 | #define NV_PRAMDAC_FP_HVALID_START 0x00680834 | ||
399 | #define NV_PRAMDAC_FP_HVALID_END 0x00680838 | ||
400 | |||
401 | #define NV_RAMDAC_FP_DITHER 0x0068083c | ||
402 | #define NV_PRAMDAC_FP_TG_CONTROL 0x00680848 | ||
403 | # define NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS (1 << 0) | ||
404 | # define NV_PRAMDAC_FP_TG_CONTROL_VSYNC_DISABLE (2 << 0) | ||
405 | # define NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS (1 << 4) | ||
406 | # define NV_PRAMDAC_FP_TG_CONTROL_HSYNC_DISABLE (2 << 4) | ||
407 | # define NV_PRAMDAC_FP_TG_CONTROL_MODE_SCALE (0 << 8) | ||
408 | # define NV_PRAMDAC_FP_TG_CONTROL_MODE_CENTER (1 << 8) | ||
409 | # define NV_PRAMDAC_FP_TG_CONTROL_MODE_NATIVE (2 << 8) | ||
410 | # define NV_PRAMDAC_FP_TG_CONTROL_READ_PROG (1 << 20) | ||
411 | # define NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12 (1 << 24) | ||
412 | # define NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS (1 << 28) | ||
413 | # define NV_PRAMDAC_FP_TG_CONTROL_DISPEN_DISABLE (2 << 28) | ||
414 | #define NV_PRAMDAC_FP_MARGIN_COLOR 0x0068084c | ||
415 | #define NV_PRAMDAC_850 0x00680850 | ||
416 | #define NV_PRAMDAC_85C 0x0068085c | ||
417 | #define NV_PRAMDAC_FP_DEBUG_0 0x00680880 | ||
418 | # define NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE (1 << 0) | ||
419 | # define NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE (1 << 4) | ||
420 | /* This doesn't seem to be essential for tmds, but still often set */ | ||
421 | # define NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED (8 << 4) | ||
422 | # define NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR (1 << 8) | ||
423 | # define NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR (1 << 12) | ||
424 | # define NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND (1 << 20) | ||
425 | # define NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND (1 << 24) | ||
426 | # define NV_PRAMDAC_FP_DEBUG_0_PWRDOWN_FPCLK (1 << 28) | ||
427 | #define NV_PRAMDAC_FP_DEBUG_1 0x00680884 | ||
428 | # define NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE 11:0 | ||
429 | # define NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE (1 << 12) | ||
430 | # define NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE 27:16 | ||
431 | # define NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE (1 << 28) | ||
432 | #define NV_PRAMDAC_FP_DEBUG_2 0x00680888 | ||
433 | #define NV_PRAMDAC_FP_DEBUG_3 0x0068088C | ||
434 | |||
435 | /* see NV_PRAMDAC_INDIR_TMDS in rules.xml */ | ||
436 | #define NV_PRAMDAC_FP_TMDS_CONTROL 0x006808b0 | ||
437 | # define NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE (1 << 16) | ||
438 | #define NV_PRAMDAC_FP_TMDS_DATA 0x006808b4 | ||
439 | |||
440 | #define NV_PRAMDAC_8C0 0x006808c0 | ||
441 | |||
442 | /* Some kind of switch */ | ||
443 | #define NV_PRAMDAC_900 0x00680900 | ||
444 | #define NV_PRAMDAC_A20 0x00680A20 | ||
445 | #define NV_PRAMDAC_A24 0x00680A24 | ||
446 | #define NV_PRAMDAC_A34 0x00680A34 | ||
447 | |||
448 | #define NV_PRAMDAC_CTV 0x00680c00 | ||
449 | |||
450 | /* names fabricated from NV_USER_DAC info */ | ||
451 | #define NV_PRMDIO_PIXEL_MASK 0x006813c6 | ||
452 | # define NV_PRMDIO_PIXEL_MASK_MASK 0xff | ||
453 | #define NV_PRMDIO_READ_MODE_ADDRESS 0x006813c7 | ||
454 | #define NV_PRMDIO_WRITE_MODE_ADDRESS 0x006813c8 | ||
455 | #define NV_PRMDIO_PALETTE_DATA 0x006813c9 | ||
456 | |||
457 | #define NV_PGRAPH_DEBUG_0 0x00400080 | ||
458 | #define NV_PGRAPH_DEBUG_1 0x00400084 | ||
459 | #define NV_PGRAPH_DEBUG_2_NV04 0x00400088 | ||
460 | #define NV_PGRAPH_DEBUG_2 0x00400620 | ||
461 | #define NV_PGRAPH_DEBUG_3 0x0040008c | ||
462 | #define NV_PGRAPH_DEBUG_4 0x00400090 | ||
463 | #define NV_PGRAPH_INTR 0x00400100 | ||
464 | #define NV_PGRAPH_INTR_EN 0x00400140 | ||
465 | #define NV_PGRAPH_CTX_CONTROL 0x00400144 | ||
466 | #define NV_PGRAPH_CTX_CONTROL_NV04 0x00400170 | ||
467 | #define NV_PGRAPH_ABS_UCLIP_XMIN 0x0040053C | ||
468 | #define NV_PGRAPH_ABS_UCLIP_YMIN 0x00400540 | ||
469 | #define NV_PGRAPH_ABS_UCLIP_XMAX 0x00400544 | ||
470 | #define NV_PGRAPH_ABS_UCLIP_YMAX 0x00400548 | ||
471 | #define NV_PGRAPH_BETA_AND 0x00400608 | ||
472 | #define NV_PGRAPH_LIMIT_VIOL_PIX 0x00400610 | ||
473 | #define NV_PGRAPH_BOFFSET0 0x00400640 | ||
474 | #define NV_PGRAPH_BOFFSET1 0x00400644 | ||
475 | #define NV_PGRAPH_BOFFSET2 0x00400648 | ||
476 | #define NV_PGRAPH_BLIMIT0 0x00400684 | ||
477 | #define NV_PGRAPH_BLIMIT1 0x00400688 | ||
478 | #define NV_PGRAPH_BLIMIT2 0x0040068c | ||
479 | #define NV_PGRAPH_STATUS 0x00400700 | ||
480 | #define NV_PGRAPH_SURFACE 0x00400710 | ||
481 | #define NV_PGRAPH_STATE 0x00400714 | ||
482 | #define NV_PGRAPH_FIFO 0x00400720 | ||
483 | #define NV_PGRAPH_PATTERN_SHAPE 0x00400810 | ||
484 | #define NV_PGRAPH_TILE 0x00400b00 | ||
485 | |||
486 | #define NV_PVIDEO_INTR_EN 0x00008140 | ||
487 | #define NV_PVIDEO_BUFFER 0x00008700 | ||
488 | #define NV_PVIDEO_STOP 0x00008704 | ||
489 | #define NV_PVIDEO_UVPLANE_BASE(buff) (0x00008800+(buff)*4) | ||
490 | #define NV_PVIDEO_UVPLANE_LIMIT(buff) (0x00008808+(buff)*4) | ||
491 | #define NV_PVIDEO_UVPLANE_OFFSET_BUFF(buff) (0x00008820+(buff)*4) | ||
492 | #define NV_PVIDEO_BASE(buff) (0x00008900+(buff)*4) | ||
493 | #define NV_PVIDEO_LIMIT(buff) (0x00008908+(buff)*4) | ||
494 | #define NV_PVIDEO_LUMINANCE(buff) (0x00008910+(buff)*4) | ||
495 | #define NV_PVIDEO_CHROMINANCE(buff) (0x00008918+(buff)*4) | ||
496 | #define NV_PVIDEO_OFFSET_BUFF(buff) (0x00008920+(buff)*4) | ||
497 | #define NV_PVIDEO_SIZE_IN(buff) (0x00008928+(buff)*4) | ||
498 | #define NV_PVIDEO_POINT_IN(buff) (0x00008930+(buff)*4) | ||
499 | #define NV_PVIDEO_DS_DX(buff) (0x00008938+(buff)*4) | ||
500 | #define NV_PVIDEO_DT_DY(buff) (0x00008940+(buff)*4) | ||
501 | #define NV_PVIDEO_POINT_OUT(buff) (0x00008948+(buff)*4) | ||
502 | #define NV_PVIDEO_SIZE_OUT(buff) (0x00008950+(buff)*4) | ||
503 | #define NV_PVIDEO_FORMAT(buff) (0x00008958+(buff)*4) | ||
504 | # define NV_PVIDEO_FORMAT_PLANAR (1 << 0) | ||
505 | # define NV_PVIDEO_FORMAT_COLOR_LE_CR8YB8CB8YA8 (1 << 16) | ||
506 | # define NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY (1 << 20) | ||
507 | # define NV_PVIDEO_FORMAT_MATRIX_ITURBT709 (1 << 24) | ||
508 | #define NV_PVIDEO_COLOR_KEY 0x00008B00 | ||
509 | |||
510 | /* NV04 overlay defines from VIDIX & Haiku */ | ||
511 | #define NV_PVIDEO_INTR_EN_0 0x00680140 | ||
512 | #define NV_PVIDEO_STEP_SIZE 0x00680200 | ||
513 | #define NV_PVIDEO_CONTROL_Y 0x00680204 | ||
514 | #define NV_PVIDEO_CONTROL_X 0x00680208 | ||
515 | #define NV_PVIDEO_BUFF0_START_ADDRESS 0x0068020c | ||
516 | #define NV_PVIDEO_BUFF0_PITCH_LENGTH 0x00680214 | ||
517 | #define NV_PVIDEO_BUFF0_OFFSET 0x0068021c | ||
518 | #define NV_PVIDEO_BUFF1_START_ADDRESS 0x00680210 | ||
519 | #define NV_PVIDEO_BUFF1_PITCH_LENGTH 0x00680218 | ||
520 | #define NV_PVIDEO_BUFF1_OFFSET 0x00680220 | ||
521 | #define NV_PVIDEO_OE_STATE 0x00680224 | ||
522 | #define NV_PVIDEO_SU_STATE 0x00680228 | ||
523 | #define NV_PVIDEO_RM_STATE 0x0068022c | ||
524 | #define NV_PVIDEO_WINDOW_START 0x00680230 | ||
525 | #define NV_PVIDEO_WINDOW_SIZE 0x00680234 | ||
526 | #define NV_PVIDEO_FIFO_THRES_SIZE 0x00680238 | ||
527 | #define NV_PVIDEO_FIFO_BURST_LENGTH 0x0068023c | ||
528 | #define NV_PVIDEO_KEY 0x00680240 | ||
529 | #define NV_PVIDEO_OVERLAY 0x00680244 | ||
530 | #define NV_PVIDEO_RED_CSC_OFFSET 0x00680280 | ||
531 | #define NV_PVIDEO_GREEN_CSC_OFFSET 0x00680284 | ||
532 | #define NV_PVIDEO_BLUE_CSC_OFFSET 0x00680288 | ||
533 | #define NV_PVIDEO_CSC_ADJUST 0x0068028c | ||
534 | |||
535 | #endif | ||