summaryrefslogtreecommitdiff
path: root/shared-core/radeon_ms_bus.c
blob: 333fd55860cadd23e87437c8ed3e797f9c343874 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
/*
 * Copyright 2007 Jérôme Glisse
 * Copyright 2007 Alex Deucher
 * Copyright 2007 Dave Airlie
 * All Rights Reserved.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sub license, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 
 * USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial portions
 * of the Software.
 */
/*
 * Authors:
 *    Dave Airlie <airlied@linux.ie>
 *    Jerome Glisse <glisse@freedesktop.org>
 */
#include "drmP.h"
#include "drm.h"
#include "radeon_ms.h"

struct radeon_pcie {
	uint32_t                    gart_table_size;
	struct drm_buffer_object    *gart_table_object;
	volatile uint32_t           *gart_table;
	struct drm_device           *dev;
	unsigned long               page_last;
};

struct radeon_pcie_gart {
	struct drm_ttm_backend  backend;
	struct radeon_pcie      *pcie;
	unsigned long           page_first;
	struct page             **pages;
	struct page             *dummy_read_page;
	unsigned long           num_pages;
	int                     populated;
	int                     bound;
};

static int pcie_ttm_bind(struct drm_ttm_backend *backend,
			 struct drm_bo_mem_reg *bo_mem);
static void pcie_ttm_clear(struct drm_ttm_backend *backend);
static void pcie_ttm_destroy(struct drm_ttm_backend *backend);
static int pcie_ttm_needs_ub_cache_adjust(struct drm_ttm_backend *backend);
static int pcie_ttm_populate(struct drm_ttm_backend *backend,
			     unsigned long num_pages, struct page **pages,
			     struct page *dummy_read_page);
static int pcie_ttm_unbind(struct drm_ttm_backend *backend);

static struct drm_ttm_backend_func radeon_pcie_gart_ttm_backend = 
{
	.needs_ub_cache_adjust = pcie_ttm_needs_ub_cache_adjust,
	.populate = pcie_ttm_populate,
	.clear = pcie_ttm_clear,
	.bind = pcie_ttm_bind,
	.unbind = pcie_ttm_unbind,
	.destroy =  pcie_ttm_destroy,
};

static void pcie_gart_flush(struct radeon_pcie *pcie)
{
	struct drm_device *dev;
	struct drm_radeon_private *dev_priv;
	uint32_t flush;

	if (pcie == NULL) {
		return;
	}
	dev = pcie->dev;
	dev_priv = dev->dev_private;
	flush = dev_priv->driver_state.pcie_tx_gart_cntl;
	flush |= PCIE_TX_GART_CNTL__GART_INVALIDATE_TLB;
	PCIE_W(PCIE_TX_GART_CNTL, flush);
	PCIE_W(PCIE_TX_GART_CNTL, dev_priv->driver_state.pcie_tx_gart_cntl);
}

static __inline__ uint32_t pcie_gart_get_page_base(struct radeon_pcie *pcie,
						   unsigned long page)
{
	if (pcie == NULL || pcie->gart_table == NULL) {
		return 0;
	}
	return ((pcie->gart_table[page] & (~0xC)) << 8);
}

static __inline__ void pcie_gart_set_page_base(struct radeon_pcie *pcie,
					       unsigned long page,
					       uint32_t page_base)
{
	if (pcie == NULL || pcie->gart_table == NULL) {
		return;
	}
	pcie->gart_table[page] = cpu_to_le32((page_base >> 8) | 0xC);
}

static int pcie_ttm_bind(struct drm_ttm_backend *backend,
			 struct drm_bo_mem_reg *bo_mem)
{
	struct radeon_pcie_gart *pcie_gart;
	unsigned long page_first;
	unsigned long page_last;
	unsigned long page, i;
	uint32_t page_base;

	pcie_gart = container_of(backend, struct radeon_pcie_gart, backend);
	page = page_first = bo_mem->mm_node->start;
	page_last = page_first + pcie_gart->num_pages;
	if (page_first >= pcie_gart->pcie->page_last ||
	    page_last >= pcie_gart->pcie->page_last)
	    return -EINVAL;
        while (page < page_last) {
		if (pcie_gart_get_page_base(pcie_gart->pcie, page)) {
			return -EBUSY;
		}
                page++;
        }

        for (i = 0, page = page_first; i < pcie_gart->num_pages; i++, page++) {
		struct page *cur_page = pcie_gart->pages[i];

		if (!page) {
			cur_page = pcie_gart->dummy_read_page;
		}
                /* write value */
		page_base = page_to_phys(cur_page);
		pcie_gart_set_page_base(pcie_gart->pcie, page, page_base);
        }
	DRM_MEMORYBARRIER();
	pcie_gart_flush(pcie_gart->pcie);
	pcie_gart->bound = 1;
	pcie_gart->page_first = page_first;
	return 0;
}

static void pcie_ttm_clear(struct drm_ttm_backend *backend)
{
	struct radeon_pcie_gart *pcie_gart;

	pcie_gart = container_of(backend, struct radeon_pcie_gart, backend);
	if (pcie_gart->pages) {
		backend->func->unbind(backend);
		pcie_gart->pages = NULL;
	}
	pcie_gart->num_pages = 0;
}

static void pcie_ttm_destroy(struct drm_ttm_backend *backend)
{
	struct radeon_pcie_gart *pcie_gart;

	if (backend == NULL) {
		return;
	}
	pcie_gart = container_of(backend, struct radeon_pcie_gart, backend);
	if (pcie_gart->pages) {
		backend->func->clear(backend);
	}
	drm_ctl_free(pcie_gart, sizeof(*pcie_gart), DRM_MEM_TTM);
}

static int pcie_ttm_needs_ub_cache_adjust(struct drm_ttm_backend *backend)
{
	return ((backend->flags & DRM_BE_FLAG_BOUND_CACHED) ? 0 : 1);
}

static int pcie_ttm_populate(struct drm_ttm_backend *backend,
			     unsigned long num_pages, struct page **pages,
			     struct page *dummy_read_page)
{
	struct radeon_pcie_gart *pcie_gart;

	pcie_gart = container_of(backend, struct radeon_pcie_gart, backend);
	pcie_gart->pages = pages;
	pcie_gart->num_pages = num_pages;
	pcie_gart->populated = 1;
	return 0;
}

static int pcie_ttm_unbind(struct drm_ttm_backend *backend)
{
	struct radeon_pcie_gart *pcie_gart;
	unsigned long page, i;

	pcie_gart = container_of(backend, struct radeon_pcie_gart, backend);
	if (pcie_gart->bound != 1 || pcie_gart->pcie->gart_table == NULL) {
		return -EINVAL;
	}
	for (i = 0, page = pcie_gart->page_first; i < pcie_gart->num_pages;
	     i++, page++) {
	     	pcie_gart->pcie->gart_table[page] = 0;
	}
	pcie_gart_flush(pcie_gart->pcie);
	pcie_gart->bound = 0;
	pcie_gart->page_first = 0;
	return 0;
}

int radeon_ms_agp_finish(struct drm_device *dev)
{
	struct drm_radeon_private *dev_priv = dev->dev_private;

	if (!dev_priv->bus_ready) {
		return 0;
	}
	dev_priv->bus_ready = 0;
	DRM_INFO("[radeon_ms] release agp\n");
	drm_agp_release(dev);
	return 0;
}

int radeon_ms_agp_init(struct drm_device *dev)
{
	struct drm_radeon_private *dev_priv = dev->dev_private;
	struct radeon_state *state = &dev_priv->driver_state;
	struct drm_agp_mode mode;
	uint32_t agp_status;
	int ret;

	dev_priv->bus_ready = -1;
	if (dev->agp == NULL) {
		DRM_ERROR("[radeon_ms] can't initialize AGP\n");
		return -EINVAL;
	}
	ret = drm_agp_acquire(dev);
	if (ret) {
		DRM_ERROR("[radeon_ms] error failed to acquire agp %d\n", ret);
		return ret;
	}
	agp_status = MMIO_R(AGP_STATUS);
	if ((AGP_STATUS__MODE_AGP30 & agp_status)) {
		mode.mode = AGP_STATUS__RATE4X;
	} else {
		mode.mode = AGP_STATUS__RATE2X_8X;
	}
	ret = drm_agp_enable(dev, mode);
	if (ret) {
		DRM_ERROR("[radeon_ms] error failed to enable agp\n");
		return ret;
	}
	state->agp_command = MMIO_R(AGP_COMMAND) | AGP_COMMAND__AGP_EN;
	state->agp_command &= ~AGP_COMMAND__FW_EN;
	state->agp_command &= ~AGP_COMMAND__MODE_4G_EN;
	state->aic_ctrl = 0;
	state->agp_base = REG_S(AGP_BASE, AGP_BASE_ADDR, dev->agp->base);
	state->agp_base_2 = 0;
	state->bus_cntl = MMIO_R(BUS_CNTL);
	state->bus_cntl &= ~BUS_CNTL__BUS_MASTER_DIS;
	state->mc_agp_location =
		REG_S(MC_AGP_LOCATION, MC_AGP_START,
				dev_priv->gpu_gart_start >> 16) |
		REG_S(MC_AGP_LOCATION, MC_AGP_TOP,
				dev_priv->gpu_gart_end >> 16);
	DRM_INFO("[radeon_ms] gpu agp base 0x%08X\n", MMIO_R(AGP_BASE));
	DRM_INFO("[radeon_ms] gpu agp location 0x%08X\n",
		 MMIO_R(MC_AGP_LOCATION));
	DRM_INFO("[radeon_ms] gpu agp location 0x%08X\n",
		 state->mc_agp_location);
	DRM_INFO("[radeon_ms] bus ready\n");
	dev_priv->bus_ready = 1;
	return 0;
}

void radeon_ms_agp_restore(struct drm_device *dev, struct radeon_state *state)
{
	struct drm_radeon_private *dev_priv = dev->dev_private;

	MMIO_W(MC_AGP_LOCATION, state->mc_agp_location);
	MMIO_W(AGP_BASE, state->agp_base);
	MMIO_W(AGP_BASE_2, state->agp_base_2);
	MMIO_W(AGP_COMMAND, state->agp_command);
}

void radeon_ms_agp_save(struct drm_device *dev, struct radeon_state *state)
{
	struct drm_radeon_private *dev_priv = dev->dev_private;

	state->agp_command = MMIO_R(AGP_COMMAND);
	state->agp_base = MMIO_R(AGP_BASE);
	state->agp_base_2 = MMIO_R(AGP_BASE_2);
	state->mc_agp_location = MMIO_R(MC_AGP_LOCATION);
}

struct drm_ttm_backend *radeon_ms_pcie_create_ttm(struct drm_device *dev)
{
	struct drm_radeon_private *dev_priv = dev->dev_private;
	struct radeon_pcie_gart *pcie_gart;

	pcie_gart = drm_ctl_calloc(1, sizeof (*pcie_gart), DRM_MEM_TTM);
	if (pcie_gart == NULL) {
		return NULL;
	}
	memset(pcie_gart, 0, sizeof(struct radeon_pcie_gart));
	pcie_gart->populated = 0;
	pcie_gart->pcie = dev_priv->bus;
	pcie_gart->backend.func = &radeon_pcie_gart_ttm_backend;

	return &pcie_gart->backend;
}

int radeon_ms_pcie_finish(struct drm_device *dev)
{
	struct drm_radeon_private *dev_priv = dev->dev_private;
	struct radeon_pcie *pcie = dev_priv->bus;

	if (!dev_priv->bus_ready || pcie == NULL) {
		dev_priv->bus_ready = 0;
		return 0;
	}
	dev_priv->bus_ready = 0;
	if (pcie->gart_table) {
		drm_mem_reg_iounmap(dev, &pcie->gart_table_object->mem,
				    (void *)pcie->gart_table);
	}
	pcie->gart_table = NULL;
	if (pcie->gart_table_object) {
		mutex_lock(&dev->struct_mutex);
		drm_bo_usage_deref_locked(&pcie->gart_table_object);
		mutex_unlock(&dev->struct_mutex);
	}
	dev_priv->bus = NULL;
	drm_free(pcie, sizeof(*pcie), DRM_MEM_DRIVER);
	return 0;
}

int radeon_ms_pcie_init(struct drm_device *dev)
{
	struct drm_radeon_private *dev_priv = dev->dev_private;
	struct radeon_state *state = &dev_priv->driver_state;
	struct radeon_pcie *pcie;
	int ret = 0;

	dev_priv->bus_ready = -1;
	/* allocate and clear device private structure */
	pcie = drm_alloc(sizeof(struct radeon_pcie), DRM_MEM_DRIVER);
	if (pcie == NULL) {
		return -ENOMEM;
	}
	memset(pcie, 0, sizeof(struct radeon_pcie));
	pcie->dev = dev;
	dev_priv->bus = (void *)pcie;
	pcie->gart_table_size = (dev_priv->gpu_gart_size / RADEON_PAGE_SIZE) *
				4;
	/* gart table start must be aligned on 16bytes, align it on one page */
	ret = drm_buffer_object_create(dev,
				       pcie->gart_table_size,
				       drm_bo_type_kernel,
				       DRM_BO_FLAG_READ |
				       DRM_BO_FLAG_WRITE |
				       DRM_BO_FLAG_MEM_VRAM |
				       DRM_BO_FLAG_NO_EVICT,
				       DRM_BO_HINT_DONT_FENCE,
				       1,
				       0,
				       &pcie->gart_table_object);
	if (ret) {
		return ret;
	}
	ret = drm_mem_reg_ioremap(dev, &pcie->gart_table_object->mem,
				  (void **) &pcie->gart_table);
	if (ret) {
		DRM_ERROR("[radeon_ms] error mapping gart table: %d\n", ret);
		return ret;
	}
	DRM_INFO("[radeon_ms] gart table in vram at 0x%08lX\n",
		 pcie->gart_table_object->offset);
	memset((void *)pcie->gart_table, 0, pcie->gart_table_size);
	pcie->page_last = pcie->gart_table_size >> 2;
	state->pcie_tx_gart_discard_rd_addr_lo =
		REG_S(PCIE_TX_GART_DISCARD_RD_ADDR_LO,
				GART_DISCARD_RD_ADDR_LO,
				dev_priv->gpu_gart_start);
	state->pcie_tx_gart_discard_rd_addr_hi =
		REG_S(PCIE_TX_GART_DISCARD_RD_ADDR_HI,
				GART_DISCARD_RD_ADDR_HI, 0);
	state->pcie_tx_gart_base =
		REG_S(PCIE_TX_GART_BASE, GART_BASE,
				pcie->gart_table_object->offset);
	state->pcie_tx_gart_start_lo =
		REG_S(PCIE_TX_GART_START_LO, GART_START_LO,
				dev_priv->gpu_gart_start);
	state->pcie_tx_gart_start_hi =
		REG_S(PCIE_TX_GART_START_HI, GART_START_HI, 0);
	state->pcie_tx_gart_end_lo =
		REG_S(PCIE_TX_GART_END_LO, GART_END_LO, dev_priv->gpu_gart_end);
	state->pcie_tx_gart_end_hi =
		REG_S(PCIE_TX_GART_END_HI, GART_END_HI, 0);
	/* FIXME: why this ? */
	state->aic_ctrl = 0;
	state->agp_base = 0; 
	state->agp_base_2 = 0; 
	state->bus_cntl = MMIO_R(BUS_CNTL);
	state->mc_agp_location = REG_S(MC_AGP_LOCATION, MC_AGP_START, 0xffc0) |
				 REG_S(MC_AGP_LOCATION, MC_AGP_TOP, 0xffff);
	state->pcie_tx_gart_cntl =
		PCIE_TX_GART_CNTL__GART_EN |
		REG_S(PCIE_TX_GART_CNTL, GART_UNMAPPED_ACCESS,
				GART_UNMAPPED_ACCESS__DISCARD) |
		REG_S(PCIE_TX_GART_CNTL, GART_MODE, GART_MODE__CACHE_32x128) |
		REG_S(PCIE_TX_GART_CNTL, GART_RDREQPATH_SEL,
				GART_RDREQPATH_SEL__HDP);
	DRM_INFO("[radeon_ms] gpu gart start 0x%08X\n",
		 PCIE_R(PCIE_TX_GART_START_LO));
	DRM_INFO("[radeon_ms] gpu gart end   0x%08X\n",
		 PCIE_R(PCIE_TX_GART_END_LO));
	DRM_INFO("[radeon_ms] bus ready\n");
	dev_priv->bus_ready = 1;
	return 0;
}

void radeon_ms_pcie_restore(struct drm_device *dev, struct radeon_state *state)
{
	struct drm_radeon_private *dev_priv = dev->dev_private;

	/* disable gart before programing other registers */
	radeon_ms_agp_restore(dev, state);
	PCIE_W(PCIE_TX_GART_CNTL, 0);
	PCIE_W(PCIE_TX_GART_BASE, state->pcie_tx_gart_base);
	PCIE_W(PCIE_TX_GART_BASE, state->pcie_tx_gart_base);
	PCIE_W(PCIE_TX_GART_DISCARD_RD_ADDR_HI,
	       state->pcie_tx_gart_discard_rd_addr_hi);
	PCIE_W(PCIE_TX_GART_DISCARD_RD_ADDR_LO,
	       state->pcie_tx_gart_discard_rd_addr_lo);
	PCIE_W(PCIE_TX_GART_START_HI, state->pcie_tx_gart_start_hi);
	PCIE_W(PCIE_TX_GART_START_LO, state->pcie_tx_gart_start_lo);
	PCIE_W(PCIE_TX_GART_END_HI, state->pcie_tx_gart_end_hi);
	PCIE_W(PCIE_TX_GART_END_LO, state->pcie_tx_gart_end_lo);
	PCIE_W(PCIE_TX_GART_CNTL, state->pcie_tx_gart_cntl);
}

void radeon_ms_pcie_save(struct drm_device *dev, struct radeon_state *state)
{
	struct drm_radeon_private *dev_priv = dev->dev_private;

	radeon_ms_agp_save(dev, state);
	state->pcie_tx_gart_base = PCIE_R(PCIE_TX_GART_BASE);
	state->pcie_tx_gart_base = PCIE_R(PCIE_TX_GART_BASE);
	state->pcie_tx_gart_discard_rd_addr_hi =
		PCIE_R(PCIE_TX_GART_DISCARD_RD_ADDR_HI);
	state->pcie_tx_gart_discard_rd_addr_lo =
		PCIE_R(PCIE_TX_GART_DISCARD_RD_ADDR_LO);
	state->pcie_tx_gart_start_hi = PCIE_R(PCIE_TX_GART_START_HI);
	state->pcie_tx_gart_start_lo = PCIE_R(PCIE_TX_GART_START_LO);
	state->pcie_tx_gart_end_hi = PCIE_R(PCIE_TX_GART_END_HI);
	state->pcie_tx_gart_end_lo = PCIE_R(PCIE_TX_GART_END_LO);
	state->pcie_tx_gart_cntl = PCIE_R(PCIE_TX_GART_CNTL);
}
(struct drm_device *dev) { return 0; /* not needed until nouveaufb? */ } static const struct drm_mode_config_funcs nv50_kms_mode_funcs = { .resize_fb = NULL, .fb_create = nv50_kms_framebuffer_create, .fb_changed = nv50_kms_fb_changed, }; /* * CRTC functions. */ static int nv50_kms_crtc_cursor_set(struct drm_crtc *drm_crtc, struct drm_file *file_priv, uint32_t buffer_handle, uint32_t width, uint32_t height) { struct nv50_crtc *crtc = to_nv50_crtc(drm_crtc); struct nv50_display *display = nv50_get_display(crtc->dev); int rval = 0; if (width != 64 || height != 64) return -EINVAL; /* set bo before doing show cursor */ if (buffer_handle) { rval = crtc->cursor->set_bo(crtc, (drm_handle_t) buffer_handle); if (rval != 0) goto out; } crtc->cursor->visible = buffer_handle ? true : false; if (buffer_handle) { rval = crtc->cursor->show(crtc); if (rval != 0) goto out; } else { /* no handle implies hiding the cursor */ rval = crtc->cursor->hide(crtc); goto out; } if (rval != 0) return rval; out: /* in case this triggers any other cursor changes */ display->update(display); return rval; } static int nv50_kms_crtc_cursor_move(struct drm_crtc *drm_crtc, int x, int y) { struct nv50_crtc *crtc = to_nv50_crtc(drm_crtc); return crtc->cursor->set_pos(crtc, x, y); } void nv50_kms_crtc_gamma_set(struct drm_crtc *drm_crtc, u16 *r, u16 *g, u16 *b, uint32_t size) { struct nv50_crtc *crtc = to_nv50_crtc(drm_crtc); if (size != 256) return; crtc->lut->set(crtc, (uint16_t *)r, (uint16_t *)g, (uint16_t *)b); } int nv50_kms_crtc_set_config(struct drm_mode_set *set) { int rval = 0, i; uint32_t crtc_mask = 0; struct drm_device *dev = NULL; struct drm_nouveau_private *dev_priv = NULL; struct nv50_display *display = NULL; struct drm_connector *drm_connector = NULL; struct drm_encoder *drm_encoder = NULL; struct drm_crtc *drm_crtc = NULL; struct nv50_crtc *crtc = NULL; struct nv50_output *output = NULL; struct nv50_connector *connector = NULL; struct nouveau_hw_mode *hw_mode = NULL; struct nv50_fb_info fb_info; bool blank = false; bool switch_fb = false; bool modeset = false; NV50_DEBUG("\n"); /* * Supported operations: * - Switch mode. * - Switch framebuffer. * - Blank screen. */ /* Sanity checking */ if (!set) { DRM_ERROR("Sanity check failed\n"); goto out; } if (!set->crtc) { DRM_ERROR("Sanity check failed\n"); goto out; } if (set->mode) { if (set->fb) { if (!drm_mode_equal(set->mode, &set->crtc->mode)) modeset = true; if (set->fb != set->crtc->fb) switch_fb = true; if (set->x != set->crtc->x || set->y != set->crtc->y) switch_fb = true; } } else { blank = true; } if (!set->connectors && !blank) { DRM_ERROR("Sanity check failed\n"); goto out; } /* Basic variable setting */ dev = set->crtc->dev; dev_priv = dev->dev_private; display = nv50_get_display(dev); crtc = to_nv50_crtc(set->crtc); /** * Wiring up the encoders and connectors. */ /* for switch_fb we verify if any important changes happened */ if (!blank) { /* Mode validation */ hw_mode = nv50_kms_to_hw_mode(set->mode); rval = crtc->validate_mode(crtc, hw_mode); if (rval != MODE_OK) { DRM_ERROR("Mode not ok\n"); goto out; } for (i = 0; i < set->num_connectors; i++) { drm_connector = set->connectors[i]; if (!drm_connector) { DRM_ERROR("No connector\n"); goto out; } connector = to_nv50_connector(drm_connector); /* This is to ensure it knows the connector subtype. */ drm_connector->funcs->fill_modes(drm_connector, 0, 0); output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector)); if (!output) { DRM_ERROR("No output\n"); goto out; } rval = output->validate_mode(output, hw_mode); if (rval != MODE_OK) { DRM_ERROR("Mode not ok\n"); goto out; } /* verify if any "sneaky" changes happened */ if (output != connector->output) modeset = true; if (output->crtc != crtc) modeset = true; } } /* Now we verified if anything changed, fail if nothing has. */ if (!modeset && !switch_fb && !blank) DRM_INFO("A seemingly empty modeset encountered, this could be a bug.\n"); /* Validation done, move on to cleaning of existing structures. */ if (modeset) { /* find encoders that use this crtc. */ list_for_each_entry(drm_encoder, &dev->mode_config.encoder_list, head) { if (drm_encoder->crtc == set->crtc) { /* find the connector that goes with it */ list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) { if (drm_connector->encoder == drm_encoder) { drm_connector->encoder = NULL; break; } } drm_encoder->crtc = NULL; } } /* now find if our desired encoders or connectors are in use already. */ for (i = 0; i < set->num_connectors; i++) { drm_connector = set->connectors[i]; if (!drm_connector) { DRM_ERROR("No connector\n"); goto out; } if (!drm_connector->encoder) continue; drm_encoder = drm_connector->encoder; drm_connector->encoder = NULL; if (!drm_encoder->crtc) continue; drm_crtc = drm_encoder->crtc; drm_encoder->crtc = NULL; drm_crtc->enabled = false; } /* Time to wire up the public encoder, the private one will be handled later. */ for (i = 0; i < set->num_connectors; i++) { drm_connector = set->connectors[i]; if (!drm_connector) { DRM_ERROR("No connector\n"); goto out; } output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector)); if (!output) { DRM_ERROR("No output\n"); goto out; } /* find the encoder public structure that matches out output structure. */ drm_encoder = to_nv50_kms_encoder(output); if (!drm_encoder) { DRM_ERROR("No encoder\n"); goto out; } drm_encoder->crtc = set->crtc; set->crtc->enabled = true; drm_connector->encoder = drm_encoder; } } /** * Disable crtc. */ if (blank) { crtc = to_nv50_crtc(set->crtc); set->crtc->enabled = false; /* disconnect encoders and connectors */ for (i = 0; i < set->num_connectors; i++) { drm_connector = set->connectors[i]; if (!drm_connector->encoder) continue; drm_connector->encoder->crtc = NULL; drm_connector->encoder = NULL; } } /** * All state should now be updated, now onto the real work. */ /* mirror everything to the private structs */ nv50_kms_mirror_routing(dev); /** * Bind framebuffer. */ if (switch_fb) { crtc = to_nv50_crtc(set->crtc); /* set framebuffer */ set->crtc->fb = set->fb; /* set private framebuffer */ crtc = to_nv50_crtc(set->crtc); fb_info.block = find_block_by_handle(dev_priv->fb_heap, set->fb->mm_handle); fb_info.width = set->fb->width; fb_info.height = set->fb->height; fb_info.depth = set->fb->depth; fb_info.bpp = set->fb->bits_per_pixel; fb_info.pitch = set->fb->pitch; fb_info.x = set->x; fb_info.y = set->y; rval = crtc->fb->bind(crtc, &fb_info); if (rval != 0) { DRM_ERROR("fb_bind failed\n"); goto out; } } /* this is !cursor_show */ if (!crtc->cursor->enabled) { rval = crtc->cursor->enable(crtc); if (rval != 0) { DRM_ERROR("cursor_enable failed\n"); goto out; } } /** * Blanking. */ if (blank) { crtc = to_nv50_crtc(set->crtc); rval = crtc->blank(crtc, true); if (rval != 0) { DRM_ERROR("blanking failed\n"); goto out; } /* detach any outputs that are currently unused */ list_for_each_entry(drm_encoder, &dev->mode_config.encoder_list, head) { if (!drm_encoder->crtc) { output = to_nv50_output(drm_encoder); rval = output->execute_mode(output, true); if (rval != 0) { DRM_ERROR("detaching output failed\n"); goto out; } } } } /** * Change framebuffer, without changing mode. */ if (switch_fb && !modeset && !blank) { crtc = to_nv50_crtc(set->crtc); rval = crtc->set_fb(crtc); if (rval != 0) { DRM_ERROR("set_fb failed\n"); goto out; } /* this also sets the fb offset */ rval = crtc->blank(crtc, false); if (rval != 0) { DRM_ERROR("unblanking failed\n"); goto out; } } /** * Normal modesetting. */ if (modeset) { crtc = to_nv50_crtc(set->crtc); /* disconnect unused outputs */ list_for_each_entry(output, &display->outputs, item) { if (output->crtc) { crtc_mask |= 1 << output->crtc->index; } else { rval = output->execute_mode(output, true); if (rval != 0) { DRM_ERROR("detaching output failed\n"); goto out; } } } /* blank any unused crtcs */ list_for_each_entry(crtc, &display->crtcs, item) { if (!(crtc_mask & (1 << crtc->index))) crtc->blank(crtc, true); } crtc = to_nv50_crtc(set->crtc); rval = crtc->set_mode(crtc, hw_mode); if (rval != 0) { DRM_ERROR("crtc mode set failed\n"); goto out; } /* find native mode. */ list_for_each_entry(output, &display->outputs, item) { if (output->crtc != crtc) continue; *crtc->native_mode = *output->native_mode; list_for_each_entry(connector, &display->connectors, item) { if (connector->output != output) continue; crtc->requested_scaling_mode = connector->requested_scaling_mode; crtc->use_dithering = connector->use_dithering; break; } if (crtc->requested_scaling_mode == SCALE_NON_GPU) crtc->use_native_mode = false; else crtc->use_native_mode = true; break; /* no use in finding more than one mode */ } rval = crtc->execute_mode(crtc); if (rval != 0) { DRM_ERROR("crtc execute mode failed\n"); goto out; } list_for_each_entry(output, &display->outputs, item) { if (output->crtc != crtc) continue; rval = output->execute_mode(output, false); if (rval != 0) { DRM_ERROR("output execute mode failed\n"); goto out; } } rval = crtc->set_scale(crtc); if (rval != 0) { DRM_ERROR("crtc set scale failed\n"); goto out; } /* next line changes crtc, so putting it here is important */ display->last_crtc = crtc->index; } /* always reset dpms, regardless if any other modesetting is done. */ if (!blank) { /* this is executed immediately */ list_for_each_entry(output, &display->outputs, item) { if (output->crtc != crtc) continue; rval = output->set_power_mode(output, DRM_MODE_DPMS_ON); if (rval != 0) { DRM_ERROR("output set power mode failed\n"); goto out; } } /* update dpms state to DPMSModeOn */ for (i = 0; i < set->num_connectors; i++) { drm_connector = set->connectors[i]; if (!drm_connector) { DRM_ERROR("No connector\n"); goto out; } rval = drm_connector_property_set_value(drm_connector, dev->mode_config.dpms_property, DRM_MODE_DPMS_ON); if (rval != 0) { DRM_ERROR("failed to update dpms state\n"); goto out; } } } display->update(display); /* Update the current mode, now that all has gone well. */ if (modeset) { set->crtc->mode = *(set->mode); set->crtc->x = set->x; set->crtc->y = set->y; } kfree(hw_mode); return 0; out: kfree(hw_mode); if (rval != 0) return rval; else return -EINVAL; } static void nv50_kms_crtc_destroy(struct drm_crtc *drm_crtc) { struct nv50_crtc *crtc = to_nv50_crtc(drm_crtc); drm_crtc_cleanup(drm_crtc); /* this will even destroy the public structure. */ crtc->destroy(crtc); } static const struct drm_crtc_funcs nv50_kms_crtc_funcs = { .save = NULL, .restore = NULL, .cursor_set = nv50_kms_crtc_cursor_set, .cursor_move = nv50_kms_crtc_cursor_move, .gamma_set = nv50_kms_crtc_gamma_set, .set_config = nv50_kms_crtc_set_config, .destroy = nv50_kms_crtc_destroy, }; static int nv50_kms_crtcs_init(struct drm_device *dev) { struct nv50_display *display = nv50_get_display(dev); struct nv50_crtc *crtc = NULL; /* * This may look a bit confusing, but: * The internal structure is already allocated and so is the public one. * Just a matter of getting to the memory and register it. */ list_for_each_entry(crtc, &display->crtcs, item) { struct drm_crtc *drm_crtc = to_nv50_kms_crtc(crtc); drm_crtc_init(dev, drm_crtc, &nv50_kms_crtc_funcs); /* init lut storage */ drm_mode_crtc_set_gamma_size(drm_crtc, 256); } return 0; } /* * Encoder functions */ static void nv50_kms_encoder_destroy(struct drm_encoder *drm_encoder) { struct nv50_output *output = to_nv50_output(drm_encoder); drm_encoder_cleanup(drm_encoder); /* this will even destroy the public structure. */ output->destroy(output); } static const struct drm_encoder_funcs nv50_kms_encoder_funcs = { .destroy = nv50_kms_encoder_destroy, }; static int nv50_kms_encoders_init(struct drm_device *dev) { struct nv50_display *display = nv50_get_display(dev); struct nv50_output *output = NULL; list_for_each_entry(output, &display->outputs, item) { struct drm_encoder *drm_encoder = to_nv50_kms_encoder(output); uint32_t type = DRM_MODE_ENCODER_NONE; switch (output->type) { case OUTPUT_DAC: type = DRM_MODE_ENCODER_DAC; break; case OUTPUT_TMDS: type = DRM_MODE_ENCODER_TMDS; break; case OUTPUT_LVDS: type = DRM_MODE_ENCODER_LVDS; break; case OUTPUT_TV: type = DRM_MODE_ENCODER_TVDAC; break; default: type = DRM_MODE_ENCODER_NONE; break; } if (type == DRM_MODE_ENCODER_NONE) { DRM_ERROR("DRM_MODE_ENCODER_NONE encountered\n"); continue; } drm_encoder_init(dev, drm_encoder, &nv50_kms_encoder_funcs, type); /* I've never seen possible crtc's restricted. */ drm_encoder->possible_crtcs = 3; drm_encoder->possible_clones = 0; } return 0; } /* * Connector functions */ /* These 2 functions wrap the connector properties that deal with multiple encoders per connector. */ bool nv50_kms_connector_get_digital(struct drm_connector *drm_connector) { struct drm_device *dev = drm_connector->dev; switch (drm_connector->connector_type) { case DRM_MODE_CONNECTOR_VGA: case DRM_MODE_CONNECTOR_SVIDEO: return false; case DRM_MODE_CONNECTOR_DVID: case DRM_MODE_CONNECTOR_LVDS: return true; default: break; } if (drm_connector->connector_type == DRM_MODE_CONNECTOR_DVII) { int rval; uint64_t prop_val; rval = drm_connector_property_get_value(drm_connector, dev->mode_config.dvi_i_select_subconnector_property, &prop_val); if (rval) { DRM_ERROR("Unable to find select subconnector property, defaulting to DVI-D\n"); return true; } /* Is a subconnector explicitly selected? */ switch (prop_val) { case DRM_MODE_SUBCONNECTOR_DVID: return true; case DRM_MODE_SUBCONNECTOR_DVIA: return false; default: break; } rval = drm_connector_property_get_value(drm_connector, dev->mode_config.dvi_i_subconnector_property, &prop_val); if (rval) { DRM_ERROR("Unable to find subconnector property, defaulting to DVI-D\n"); return true; } /* Do we know what subconnector we currently have connected? */ switch (prop_val) { case DRM_MODE_SUBCONNECTOR_DVID: return true; case DRM_MODE_SUBCONNECTOR_DVIA: return false; default: DRM_ERROR("Unknown subconnector value, defaulting to DVI-D\n"); return true; } } DRM_ERROR("Unknown connector type, defaulting to analog\n"); return false; } static void nv50_kms_connector_set_digital(struct drm_connector *drm_connector, int digital, bool force) { struct drm_device *dev = drm_connector->dev; if (drm_connector->connector_type == DRM_MODE_CONNECTOR_DVII) { uint64_t cur_value, new_value; int rval = drm_connector_property_get_value(drm_connector, dev->mode_config.dvi_i_subconnector_property, &cur_value); if (rval) { DRM_ERROR("Unable to find subconnector property\n"); return; } /* Only set when unknown or when forced to do so. */ if (cur_value != DRM_MODE_SUBCONNECTOR_Unknown && !force) return; if (digital == 1) new_value = DRM_MODE_SUBCONNECTOR_DVID; else if (digital == 0) new_value = DRM_MODE_SUBCONNECTOR_DVIA; else new_value = DRM_MODE_SUBCONNECTOR_Unknown; drm_connector_property_set_value(drm_connector, dev->mode_config.dvi_i_subconnector_property, new_value); } } void nv50_kms_connector_detect_all(struct drm_device *dev) { struct drm_connector *drm_connector = NULL; list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) { drm_connector->funcs->detect(drm_connector); } } static enum drm_connector_status nv50_kms_connector_detect(struct drm_connector *drm_connector) { struct drm_device *dev = drm_connector->dev; struct nv50_connector *connector = to_nv50_connector(drm_connector); struct nv50_output *output = NULL; int hpd_detect = 0, load_detect = 0, i2c_detect = 0; int old_status = drm_connector->status; /* hotplug detect */ hpd_detect = connector->hpd_detect(connector); /* load detect */ output = connector->to_output(connector, false); /* analog */ if (output && output->detect) load_detect = output->detect(output); if (hpd_detect < 0 || load_detect < 0) /* did an error occur? */ i2c_detect = connector->i2c_detect(connector); if (load_detect == 1) { nv50_kms_connector_set_digital(drm_connector, 0, true); /* analog, forced */ } else if (hpd_detect == 1 && load_detect == 0) { nv50_kms_connector_set_digital(drm_connector, 1, true); /* digital, forced */ } else { nv50_kms_connector_set_digital(drm_connector, -1, true); /* unknown, forced */ } if (hpd_detect == 1 || load_detect == 1 || i2c_detect == 1) drm_connector->status = connector_status_connected; else drm_connector->status = connector_status_disconnected; /* update our modes whenever there is reason to */ if (old_status != drm_connector->status) { drm_connector->funcs->fill_modes(drm_connector, 0, 0); /* notify fb of changes */ dev->mode_config.funcs->fb_changed(dev); /* sent a hotplug event when appropriate. */ drm_sysfs_hotplug_event(dev); } return drm_connector->status; } static void nv50_kms_connector_destroy(struct drm_connector *drm_connector) { struct nv50_connector *connector = to_nv50_connector(drm_connector); drm_sysfs_connector_remove(drm_connector); drm_connector_cleanup(drm_connector); /* this will even destroy the public structure. */ connector->destroy(connector); } /* * Detailed mode info for a standard 640x480@60Hz monitor */ static struct drm_display_mode std_mode[] = { /*{ DRM_MODE("640x480", DRM_MODE_TYPE_DEFAULT, 25200, 640, 656, 752, 800, 0, 480, 490, 492, 525, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },*/ /* 640x480@60Hz */ { DRM_MODE("1280x1024", DRM_MODE_TYPE_DEFAULT, 135000, 1280, 1296, 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1280x1024@75Hz */ }; static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, uint32_t maxX, uint32_t maxY) { struct nv50_connector *connector = to_nv50_connector(drm_connector); struct drm_device *dev = drm_connector->dev; int rval = 0; bool connected = false; struct drm_display_mode *mode, *t; struct edid *edid = NULL; NV50_DEBUG("%s\n", drm_get_connector_name(drm_connector)); /* set all modes to the unverified state */ list_for_each_entry_safe(mode, t, &drm_connector->modes, head) mode->status = MODE_UNVERIFIED; if (nv50_kms_connector_detect(drm_connector) == connector_status_connected) connected = true; if (connected) NV50_DEBUG("%s is connected\n", drm_get_connector_name(drm_connector)); else NV50_DEBUG("%s is disconnected\n", drm_get_connector_name(drm_connector)); /* Not all connnectors have an i2c channel. */ if (connected && connector->i2c_chan) edid = (struct edid *) drm_do_probe_ddc_edid(&connector->i2c_chan->adapter); /* This will remove edid if needed. */ drm_mode_connector_update_edid_property(drm_connector, edid); if (edid) { rval = drm_add_edid_modes(drm_connector, edid); /* Only update when relevant and when detect couldn't determine type. */ nv50_kms_connector_set_digital(drm_connector, edid->digital ? 1 : 0, false); kfree(edid); } if (rval) /* number of modes > 1 */ drm_mode_connector_list_update(drm_connector); if (maxX && maxY) drm_mode_validate_size(dev, &drm_connector->modes, maxX, maxY, 0); list_for_each_entry_safe(mode, t, &drm_connector->modes, head) { if (mode->status == MODE_OK) { struct nouveau_hw_mode *hw_mode = nv50_kms_to_hw_mode(mode); struct nv50_output *output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector)); mode->status = output->validate_mode(output, hw_mode); /* find native mode, TODO: also check if we actually found one */ if (mode->status == MODE_OK) { if (mode->type & DRM_MODE_TYPE_PREFERRED) *output->native_mode = *hw_mode; } kfree(hw_mode); } } /* revalidate now that we have native mode */ list_for_each_entry_safe(mode, t, &drm_connector->modes, head) { if (mode->status == MODE_OK) { struct nouveau_hw_mode *hw_mode = nv50_kms_to_hw_mode(mode); struct nv50_output *output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector)); mode->status = output->validate_mode(output, hw_mode); kfree(hw_mode); } } drm_mode_prune_invalid(dev, &drm_connector->modes, true); /* pruning is done, so bail out. */ if (!connected) return; if (list_empty(&drm_connector->modes)) { struct drm_display_mode *stdmode; struct nouveau_hw_mode *hw_mode; struct nv50_output *output; NV50_DEBUG("No valid modes on %s\n", drm_get_connector_name(drm_connector)); /* Making up native modes for LVDS is a bad idea. */ if (drm_connector->connector_type == DRM_MODE_CONNECTOR_LVDS) return; /* Should we do this here ??? * When no valid EDID modes are available we end up * here and bailed in the past, now we add a standard * 640x480@60Hz mode and carry on. */ stdmode = drm_mode_duplicate(dev, &std_mode[0]); drm_mode_probed_add(drm_connector, stdmode); drm_mode_list_concat(&drm_connector->probed_modes, &drm_connector->modes); /* also add it as native mode */ hw_mode = nv50_kms_to_hw_mode(mode); output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector)); if (hw_mode) *output->native_mode = *hw_mode; DRM_DEBUG("Adding standard 640x480 @ 60Hz to %s\n", drm_get_connector_name(drm_connector)); } drm_mode_sort(&drm_connector->modes); NV50_DEBUG("Probed modes for %s\n", drm_get_connector_name(drm_connector)); list_for_each_entry_safe(mode, t, &drm_connector->modes, head) { mode->vrefresh = drm_mode_vrefresh(mode); /* is this needed, as it's unused by the driver? */ drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); drm_mode_debug_printmodeline(mode); } } static int nv50_kms_connector_set_property(struct drm_connector *drm_connector, struct drm_property *property, uint64_t value) { struct drm_device *dev = drm_connector->dev; struct nv50_connector *connector = to_nv50_connector(drm_connector); int rval = 0; bool delay_change = false; /* DPMS */ if (property == dev->mode_config.dpms_property && drm_connector->encoder) { struct nv50_output *output = to_nv50_output(drm_connector->encoder); rval = output->set_power_mode(output, (int) value); return rval; } /* Scaling mode */ if (property == dev->mode_config.scaling_mode_property) { struct nv50_crtc *crtc = NULL; struct nv50_display *display = nv50_get_display(dev); int internal_value = 0; switch (value) { case DRM_MODE_SCALE_NON_GPU: internal_value = SCALE_NON_GPU; break; case DRM_MODE_SCALE_FULLSCREEN: internal_value = SCALE_FULLSCREEN; break; case DRM_MODE_SCALE_NO_SCALE: internal_value = SCALE_NOSCALE; break; case DRM_MODE_SCALE_ASPECT: internal_value = SCALE_ASPECT; break; default: break; } /* LVDS always needs gpu scaling */ if (connector->type == CONNECTOR_LVDS && internal_value == SCALE_NON_GPU) return -EINVAL; connector->requested_scaling_mode = internal_value; if (drm_connector->encoder && drm_connector->encoder->crtc) crtc = to_nv50_crtc(drm_connector->encoder->crtc); if (!crtc) return 0; crtc->requested_scaling_mode = connector->requested_scaling_mode; /* going from and to a gpu scaled regime requires a modesetting, so wait until next modeset */ if (crtc->scaling_mode == SCALE_NON_GPU || internal_value == SCALE_NON_GPU) { DRM_INFO("Moving from or to a non-gpu scaled mode, this will be processed upon next modeset."); delay_change = true; } if (delay_change) return 0; rval = crtc->set_scale(crtc); if (rval) return rval; /* process command buffer */ display->update(display); return 0; } /* Dithering */ if (property == dev->mode_config.dithering_mode_property) { struct nv50_crtc *crtc = NULL; struct nv50_display *display = nv50_get_display(dev); if (value == DRM_MODE_DITHERING_ON) connector->use_dithering = true; else connector->use_dithering = false; if (drm_connector->encoder && drm_connector->encoder->crtc) crtc = to_nv50_crtc(drm_connector->encoder->crtc); if (!crtc) return 0; /* update hw state */ crtc->use_dithering = connector->use_dithering; rval = crtc->set_dither(crtc); if (rval) return rval; /* process command buffer */ display->update(display); return 0; } return -EINVAL; } static const struct drm_connector_funcs nv50_kms_connector_funcs = { .save = NULL, .restore = NULL, .detect = nv50_kms_connector_detect, .destroy = nv50_kms_connector_destroy, .fill_modes = nv50_kms_connector_fill_modes, .set_property = nv50_kms_connector_set_property }; static int nv50_kms_get_scaling_mode(struct drm_connector *drm_connector) { struct nv50_connector *connector = NULL; int drm_mode = 0; if (!drm_connector) { DRM_ERROR("drm_connector is NULL\n"); return 0; } connector = to_nv50_connector(drm_connector); switch (connector->requested_scaling_mode) { case SCALE_NON_GPU: drm_mode = DRM_MODE_SCALE_NON_GPU; break; case SCALE_FULLSCREEN: drm_mode = DRM_MODE_SCALE_FULLSCREEN; break; case SCALE_NOSCALE: drm_mode = DRM_MODE_SCALE_NO_SCALE; break; case SCALE_ASPECT: drm_mode = DRM_MODE_SCALE_ASPECT; break; default: break; } return drm_mode; } static int nv50_kms_connectors_init(struct drm_device *dev) { struct nv50_display *display = nv50_get_display(dev); struct nv50_connector *connector = NULL; int i; /* Initialise some optional connector properties. */ drm_mode_create_scaling_mode_property(dev); drm_mode_create_dithering_property(dev); list_for_each_entry(connector, &display->connectors, item) { struct drm_connector *drm_connector = to_nv50_kms_connector(connector); uint32_t type = DRM_MODE_CONNECTOR_Unknown; switch (connector->type) { case CONNECTOR_VGA: type = DRM_MODE_CONNECTOR_VGA; break; case CONNECTOR_DVI_D: type = DRM_MODE_CONNECTOR_DVID; break; case CONNECTOR_DVI_I: type = DRM_MODE_CONNECTOR_DVII; break; case CONNECTOR_LVDS: type = DRM_MODE_CONNECTOR_LVDS; break; case CONNECTOR_TV: type = DRM_MODE_CONNECTOR_SVIDEO; break; default: type = DRM_MODE_CONNECTOR_Unknown; break; } if (type == DRM_MODE_CONNECTOR_Unknown) { DRM_ERROR("DRM_MODE_CONNECTOR_Unknown encountered\n"); continue; } /* It should be allowed sometimes, but let's be safe for the moment. */ drm_connector->interlace_allowed = false; drm_connector->doublescan_allowed = false; drm_connector_init(dev, drm_connector, &nv50_kms_connector_funcs, type); /* Init DVI-I specific properties */ if (type == DRM_MODE_CONNECTOR_DVII) { drm_mode_create_dvi_i_properties(dev); drm_connector_attach_property(drm_connector, dev->mode_config.dvi_i_subconnector_property, 0); drm_connector_attach_property(drm_connector, dev->mode_config.dvi_i_select_subconnector_property, 0); } /* If supported in the future, it will have to use the scalers internally and not expose them. */ if (type != DRM_MODE_CONNECTOR_SVIDEO) { drm_connector_attach_property(drm_connector, dev->mode_config.scaling_mode_property, nv50_kms_get_scaling_mode(drm_connector)); } drm_connector_attach_property(drm_connector, dev->mode_config.dithering_mode_property, connector->use_dithering ? DRM_MODE_DITHERING_ON : DRM_MODE_DITHERING_OFF); /* attach encoders, possibilities are analog + digital */ for (i = 0; i < 2; i++) { struct drm_encoder *drm_encoder = NULL; struct nv50_output *output = connector->to_output(connector, i); if (!output) continue; drm_encoder = to_nv50_kms_encoder(output); if (!drm_encoder) { DRM_ERROR("No struct drm_connector to match struct nv50_output\n"); continue; } drm_mode_connector_attach_encoder(drm_connector, drm_encoder); } drm_sysfs_connector_add(drm_connector); } return 0; } /* * Main functions */ int nv50_kms_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nv50_kms_priv *kms_priv = kzalloc(sizeof(struct nv50_kms_priv), GFP_KERNEL); struct nv50_display *display = NULL; int rval = 0; if (!kms_priv) return -ENOMEM; dev_priv->kms_priv = kms_priv; /* function pointers */ /* an allocation interface that deals with the outside world, without polluting the core. */ dev_priv->alloc_crtc = nv50_kms_alloc_crtc; dev_priv->alloc_output = nv50_kms_alloc_output; dev_priv->alloc_connector = nv50_kms_alloc_connector; dev_priv->free_crtc = nv50_kms_free_crtc; dev_priv->free_output = nv50_kms_free_output; dev_priv->free_connector = nv50_kms_free_connector; /* bios is needed for tables. */ rval = nouveau_parse_bios(dev); if (rval != 0) goto out; /* init basic kernel modesetting */ drm_mode_config_init(dev); dev->mode_config.min_width = 0; dev->mode_config.min_height = 0; dev->mode_config.funcs = (void *)&nv50_kms_mode_funcs; dev->mode_config.max_width = 8192; dev->mode_config.max_height = 8192; dev->mode_config.fb_base = dev_priv->fb_phys; /* init kms lists */ INIT_LIST_HEAD(&kms_priv->crtcs); INIT_LIST_HEAD(&kms_priv->encoders); INIT_LIST_HEAD(&kms_priv->connectors); /* init the internal core, must be done first. */ rval = nv50_display_create(dev); if (rval != 0) goto out; display = nv50_get_display(dev); if (!display) { rval = -EINVAL; goto out; } /* pre-init now */ rval = display->pre_init(display); if (rval != 0) goto out; /* init external layer */ rval = nv50_kms_crtcs_init(dev); if (rval != 0) goto out; rval = nv50_kms_encoders_init(dev); if (rval != 0) goto out; rval = nv50_kms_connectors_init(dev); if (rval != 0) goto out; /* init now, this'll kill the textmode */ rval = display->init(display); if (rval != 0) goto out; /* process cmdbuffer */ display->update(display); return 0; out: kfree(kms_priv); dev_priv->kms_priv = NULL; return rval; } int nv50_kms_destroy(struct drm_device *dev) { drm_mode_config_cleanup(dev); return 0; }