Subversion Repositories Kolibri OS

Rev

Rev 6296 | Rev 6937 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1179 serge 1
/*
2
 * Copyright (c) 2006-2009 Red Hat Inc.
3
 * Copyright (c) 2006-2008 Intel Corporation
4
 * Copyright (c) 2007 Dave Airlie 
5
 *
6
 * DRM framebuffer helper functions
7
 *
8
 * Permission to use, copy, modify, distribute, and sell this software and its
9
 * documentation for any purpose is hereby granted without fee, provided that
10
 * the above copyright notice appear in all copies and that both that copyright
11
 * notice and this permission notice appear in supporting documentation, and
12
 * that the name of the copyright holders not be used in advertising or
13
 * publicity pertaining to distribution of the software without specific,
14
 * written prior permission.  The copyright holders make no representations
15
 * about the suitability of this software for any purpose.  It is provided "as
16
 * is" without express or implied warranty.
17
 *
18
 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
19
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
20
 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
21
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
22
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
23
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
24
 * OF THIS SOFTWARE.
25
 *
26
 * Authors:
27
 *      Dave Airlie 
28
 *      Jesse Barnes 
29
 */
3192 Serge 30
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
31
 
1430 serge 32
#include 
1963 serge 33
#include 
34
#include 
1179 serge 35
#include 
3031 serge 36
#include 
37
#include 
38
#include 
39
#include 
40
#include 
6084 serge 41
#include 
42
#include 
1179 serge 43
 
6084 serge 44
static bool drm_fbdev_emulation = true;
45
module_param_named(fbdev_emulation, drm_fbdev_emulation, bool, 0600);
46
MODULE_PARM_DESC(fbdev_emulation,
47
		 "Enable legacy fbdev emulation [default=true]");
48
 
1179 serge 49
static LIST_HEAD(kernel_fb_helper_list);
50
 
3192 Serge 51
/**
52
 * DOC: fbdev helpers
53
 *
54
 * The fb helper functions are useful to provide an fbdev on top of a drm kernel
5060 serge 55
 * mode setting driver. They can be used mostly independently from the crtc
3192 Serge 56
 * helper functions used by many drivers to implement the kernel mode setting
57
 * interfaces.
3480 Serge 58
 *
5060 serge 59
 * Initialization is done as a four-step process with drm_fb_helper_prepare(),
60
 * drm_fb_helper_init(), drm_fb_helper_single_add_all_connectors() and
61
 * drm_fb_helper_initial_config(). Drivers with fancier requirements than the
62
 * default behaviour can override the third step with their own code.
63
 * Teardown is done with drm_fb_helper_fini().
3480 Serge 64
 *
65
 * At runtime drivers should restore the fbdev console by calling
6084 serge 66
 * drm_fb_helper_restore_fbdev_mode_unlocked() from their ->lastclose callback.
67
 * They should also notify the fb helper code from updates to the output
3480 Serge 68
 * configuration by calling drm_fb_helper_hotplug_event(). For easier
69
 * integration with the output polling code in drm_crtc_helper.c the modeset
5060 serge 70
 * code provides a ->output_poll_changed callback.
3480 Serge 71
 *
72
 * All other functions exported by the fb helper library can be used to
73
 * implement the fbdev driver interface by the driver.
5060 serge 74
 *
75
 * It is possible, though perhaps somewhat tricky, to implement race-free
76
 * hotplug detection using the fbdev helpers. The drm_fb_helper_prepare()
77
 * helper must be called first to initialize the minimum required to make
78
 * hotplug detection work. Drivers also need to make sure to properly set up
79
 * the dev->mode_config.funcs member. After calling drm_kms_helper_poll_init()
80
 * it is safe to enable interrupts and start processing hotplug events. At the
81
 * same time, drivers should initialize all modeset objects such as CRTCs,
82
 * encoders and connectors. To finish up the fbdev helper initialization, the
83
 * drm_fb_helper_init() function is called. To probe for all attached displays
84
 * and set up an initial configuration using the detected hardware, drivers
85
 * should call drm_fb_helper_single_add_all_connectors() followed by
86
 * drm_fb_helper_initial_config().
3192 Serge 87
 */
88
 
3480 Serge 89
/**
90
 * drm_fb_helper_single_add_all_connectors() - add all connectors to fbdev
91
 * 					       emulation helper
92
 * @fb_helper: fbdev initialized with drm_fb_helper_init
93
 *
94
 * This functions adds all the available connectors for use with the given
95
 * fb_helper. This is a separate step to allow drivers to freely assign
96
 * connectors to the fbdev, e.g. if some are reserved for special purposes or
97
 * not adequate to be used for the fbcon.
98
 *
6084 serge 99
 * This function is protected against concurrent connector hotadds/removals
100
 * using drm_fb_helper_add_one_connector() and
101
 * drm_fb_helper_remove_one_connector().
3480 Serge 102
 */
1963 serge 103
int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
1221 serge 104
{
1963 serge 105
	struct drm_device *dev = fb_helper->dev;
106
	struct drm_connector *connector;
107
	int i;
1221 serge 108
 
6084 serge 109
	if (!drm_fbdev_emulation)
110
		return 0;
111
 
112
	mutex_lock(&dev->mode_config.mutex);
113
	drm_for_each_connector(connector, dev) {
1963 serge 114
		struct drm_fb_helper_connector *fb_helper_connector;
115
 
116
		fb_helper_connector = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
117
		if (!fb_helper_connector)
118
			goto fail;
119
 
120
		fb_helper_connector->connector = connector;
121
		fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector;
122
	}
6084 serge 123
	mutex_unlock(&dev->mode_config.mutex);
1221 serge 124
	return 0;
1963 serge 125
fail:
126
	for (i = 0; i < fb_helper->connector_count; i++) {
127
		kfree(fb_helper->connector_info[i]);
128
		fb_helper->connector_info[i] = NULL;
129
	}
130
	fb_helper->connector_count = 0;
6084 serge 131
	mutex_unlock(&dev->mode_config.mutex);
132
 
133
	return -ENOMEM;
1221 serge 134
}
1963 serge 135
EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors);
1221 serge 136
 
5060 serge 137
int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_connector *connector)
138
{
139
	struct drm_fb_helper_connector **temp;
140
	struct drm_fb_helper_connector *fb_helper_connector;
141
 
6084 serge 142
	if (!drm_fbdev_emulation)
143
		return 0;
144
 
5060 serge 145
	WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex));
146
	if (fb_helper->connector_count + 1 > fb_helper->connector_info_alloc_count) {
5271 serge 147
		temp = krealloc(fb_helper->connector_info, sizeof(struct drm_fb_helper_connector *) * (fb_helper->connector_count + 1), GFP_KERNEL);
5060 serge 148
		if (!temp)
149
			return -ENOMEM;
150
 
151
		fb_helper->connector_info_alloc_count = fb_helper->connector_count + 1;
152
		fb_helper->connector_info = temp;
153
	}
154
 
155
 
156
	fb_helper_connector = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
157
	if (!fb_helper_connector)
158
		return -ENOMEM;
159
 
160
	fb_helper_connector->connector = connector;
161
	fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector;
162
	return 0;
163
}
164
EXPORT_SYMBOL(drm_fb_helper_add_one_connector);
165
 
6084 serge 166
static void remove_from_modeset(struct drm_mode_set *set,
167
		struct drm_connector *connector)
168
{
169
	int i, j;
170
 
171
	for (i = 0; i < set->num_connectors; i++) {
172
		if (set->connectors[i] == connector)
173
			break;
174
	}
175
 
176
	if (i == set->num_connectors)
177
		return;
178
 
179
	for (j = i + 1; j < set->num_connectors; j++) {
180
		set->connectors[j - 1] = set->connectors[j];
181
	}
182
	set->num_connectors--;
183
 
184
	/*
185
	 * TODO maybe need to makes sure we set it back to !=NULL somewhere?
186
	 */
187
	if (set->num_connectors == 0) {
188
		set->fb = NULL;
189
		drm_mode_destroy(connector->dev, set->mode);
190
		set->mode = NULL;
191
	}
192
}
193
 
5060 serge 194
int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper,
195
				       struct drm_connector *connector)
196
{
197
	struct drm_fb_helper_connector *fb_helper_connector;
198
	int i, j;
199
 
6084 serge 200
	if (!drm_fbdev_emulation)
201
		return 0;
202
 
5060 serge 203
	WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex));
204
 
205
	for (i = 0; i < fb_helper->connector_count; i++) {
206
		if (fb_helper->connector_info[i]->connector == connector)
207
			break;
208
	}
209
 
210
	if (i == fb_helper->connector_count)
211
		return -EINVAL;
212
	fb_helper_connector = fb_helper->connector_info[i];
213
 
214
	for (j = i + 1; j < fb_helper->connector_count; j++) {
215
		fb_helper->connector_info[j - 1] = fb_helper->connector_info[j];
216
	}
217
	fb_helper->connector_count--;
218
	kfree(fb_helper_connector);
6084 serge 219
 
220
	/* also cleanup dangling references to the connector: */
221
	for (i = 0; i < fb_helper->crtc_count; i++)
222
		remove_from_modeset(&fb_helper->crtc_info[i].mode_set, connector);
223
 
5060 serge 224
	return 0;
225
}
226
EXPORT_SYMBOL(drm_fb_helper_remove_one_connector);
5271 serge 227
 
1963 serge 228
static void drm_fb_helper_save_lut_atomic(struct drm_crtc *crtc, struct drm_fb_helper *helper)
1179 serge 229
{
1963 serge 230
	uint16_t *r_base, *g_base, *b_base;
231
	int i;
1179 serge 232
 
4104 Serge 233
	if (helper->funcs->gamma_get == NULL)
234
		return;
235
 
1963 serge 236
	r_base = crtc->gamma_store;
237
	g_base = r_base + crtc->gamma_size;
238
	b_base = g_base + crtc->gamma_size;
1179 serge 239
 
1963 serge 240
	for (i = 0; i < crtc->gamma_size; i++)
241
		helper->funcs->gamma_get(crtc, &r_base[i], &g_base[i], &b_base[i], i);
1179 serge 242
}
243
 
1963 serge 244
static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc)
1179 serge 245
{
1963 serge 246
	uint16_t *r_base, *g_base, *b_base;
247
 
3031 serge 248
	if (crtc->funcs->gamma_set == NULL)
249
		return;
250
 
1963 serge 251
	r_base = crtc->gamma_store;
252
	g_base = r_base + crtc->gamma_size;
253
	b_base = g_base + crtc->gamma_size;
254
 
255
	crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size);
1179 serge 256
}
257
 
6084 serge 258
/* Find the real fb for a given fb helper CRTC */
259
static struct drm_framebuffer *drm_mode_config_fb(struct drm_crtc *crtc)
260
{
261
	struct drm_device *dev = crtc->dev;
262
	struct drm_crtc *c;
1179 serge 263
 
6084 serge 264
	drm_for_each_crtc(c, dev) {
265
		if (crtc->base.id == c->base.id)
266
			return c->primary->fb;
267
	}
268
 
269
	return NULL;
270
}
271
static int restore_fbdev_mode_atomic(struct drm_fb_helper *fb_helper)
5060 serge 272
{
273
	struct drm_device *dev = fb_helper->dev;
274
	struct drm_plane *plane;
6084 serge 275
	struct drm_atomic_state *state;
276
	int i, ret;
277
	unsigned plane_mask;
278
 
279
	state = drm_atomic_state_alloc(dev);
280
	if (!state)
281
		return -ENOMEM;
282
 
283
	state->acquire_ctx = dev->mode_config.acquire_ctx;
284
retry:
285
	plane_mask = 0;
286
	drm_for_each_plane(plane, dev) {
287
		struct drm_plane_state *plane_state;
288
 
289
		plane_state = drm_atomic_get_plane_state(state, plane);
290
		if (IS_ERR(plane_state)) {
291
			ret = PTR_ERR(plane_state);
292
			goto fail;
293
		}
294
 
295
		plane_state->rotation = BIT(DRM_ROTATE_0);
296
 
297
		plane->old_fb = plane->fb;
298
		plane_mask |= 1 << drm_plane_index(plane);
299
 
300
		/* disable non-primary: */
301
		if (plane->type == DRM_PLANE_TYPE_PRIMARY)
302
			continue;
303
 
304
		ret = __drm_atomic_helper_disable_plane(plane, plane_state);
305
		if (ret != 0)
306
			goto fail;
307
	}
308
 
309
	for(i = 0; i < fb_helper->crtc_count; i++) {
310
		struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
311
 
312
		ret = __drm_atomic_helper_set_config(mode_set, state);
313
		if (ret != 0)
314
			goto fail;
315
	}
316
 
317
	ret = drm_atomic_commit(state);
318
 
319
fail:
320
	drm_atomic_clean_old_fb(dev, plane_mask, ret);
321
 
322
	if (ret == -EDEADLK)
323
		goto backoff;
324
 
325
	if (ret != 0)
326
		drm_atomic_state_free(state);
327
 
328
	return ret;
329
 
330
backoff:
331
	drm_atomic_state_clear(state);
332
	drm_atomic_legacy_backoff(state);
333
 
334
	goto retry;
335
}
336
 
337
static int restore_fbdev_mode(struct drm_fb_helper *fb_helper)
338
{
339
	struct drm_device *dev = fb_helper->dev;
340
	struct drm_plane *plane;
5060 serge 341
	int i;
342
 
343
	drm_warn_on_modeset_not_all_locked(dev);
344
 
6084 serge 345
	if (fb_helper->atomic)
346
		return restore_fbdev_mode_atomic(fb_helper);
347
 
348
	drm_for_each_plane(plane, dev) {
5060 serge 349
		if (plane->type != DRM_PLANE_TYPE_PRIMARY)
350
			drm_plane_force_disable(plane);
351
 
5271 serge 352
		if (dev->mode_config.rotation_property) {
353
			drm_mode_plane_set_obj_prop(plane,
354
						    dev->mode_config.rotation_property,
355
						    BIT(DRM_ROTATE_0));
356
		}
357
	}
358
 
5060 serge 359
	for (i = 0; i < fb_helper->crtc_count; i++) {
360
		struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
361
		struct drm_crtc *crtc = mode_set->crtc;
362
		int ret;
363
 
6084 serge 364
		if (crtc->funcs->cursor_set2) {
365
			ret = crtc->funcs->cursor_set2(crtc, NULL, 0, 0, 0, 0, 0);
366
			if (ret)
367
				return ret;
368
		} else if (crtc->funcs->cursor_set) {
5060 serge 369
			ret = crtc->funcs->cursor_set(crtc, NULL, 0, 0, 0);
370
			if (ret)
6084 serge 371
				return ret;
5060 serge 372
		}
373
 
374
		ret = drm_mode_set_config_internal(mode_set);
375
		if (ret)
6084 serge 376
			return ret;
5060 serge 377
	}
6084 serge 378
 
379
	return 0;
5060 serge 380
}
6084 serge 381
 
5060 serge 382
/**
383
 * drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration
384
 * @fb_helper: fbcon to restore
385
 *
386
 * This should be called from driver's drm ->lastclose callback
387
 * when implementing an fbcon on top of kms using this helper. This ensures that
388
 * the user isn't greeted with a black screen when e.g. X dies.
6084 serge 389
 *
390
 * RETURNS:
391
 * Zero if everything went ok, negative error code otherwise.
5060 serge 392
 */
6084 serge 393
int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
5060 serge 394
{
395
	struct drm_device *dev = fb_helper->dev;
6084 serge 396
	bool do_delayed;
397
	int ret;
5271 serge 398
 
6084 serge 399
	if (!drm_fbdev_emulation)
400
		return -ENODEV;
401
 
5060 serge 402
	drm_modeset_lock_all(dev);
403
	ret = restore_fbdev_mode(fb_helper);
6084 serge 404
 
405
	do_delayed = fb_helper->delayed_hotplug;
406
	if (do_delayed)
407
		fb_helper->delayed_hotplug = false;
5060 serge 408
	drm_modeset_unlock_all(dev);
6084 serge 409
 
410
	if (do_delayed)
411
		drm_fb_helper_hotplug_event(fb_helper);
5060 serge 412
	return ret;
413
}
414
EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked);
415
 
3480 Serge 416
static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper)
417
{
418
	struct drm_device *dev = fb_helper->dev;
419
	struct drm_crtc *crtc;
420
	int bound = 0, crtcs_bound = 0;
1179 serge 421
 
6084 serge 422
	/* Sometimes user space wants everything disabled, so don't steal the
423
	 * display if there's a master. */
6296 serge 424
//   if (dev->primary->master)
425
//       return false;
6084 serge 426
 
427
	drm_for_each_crtc(crtc, dev) {
5060 serge 428
		if (crtc->primary->fb)
3480 Serge 429
			crtcs_bound++;
5060 serge 430
		if (crtc->primary->fb == fb_helper->fb)
3480 Serge 431
			bound++;
432
	}
433
 
434
	if (bound < crtcs_bound)
435
		return false;
4560 Serge 436
 
3480 Serge 437
	return true;
438
}
439
 
4560 Serge 440
#ifdef CONFIG_MAGIC_SYSRQ
441
static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
442
{
443
	bool ret;
444
	ret = drm_fb_helper_force_kernel_mode();
445
	if (ret == true)
446
		DRM_ERROR("Failed to restore crtc configuration\n");
447
}
448
static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);
449
 
450
static void drm_fb_helper_sysrq(int dummy1)
451
{
452
	schedule_work(&drm_fb_helper_restore_work);
453
}
454
 
455
static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
456
	.handler = drm_fb_helper_sysrq,
457
	.help_msg = "force-fb(V)",
458
	.action_msg = "Restore framebuffer console",
459
};
460
#else
461
 
462
#endif
463
 
3031 serge 464
static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode)
1179 serge 465
{
466
	struct drm_fb_helper *fb_helper = info->par;
467
	struct drm_device *dev = fb_helper->dev;
468
	struct drm_crtc *crtc;
1963 serge 469
	struct drm_connector *connector;
470
	int i, j;
1179 serge 471
 
472
	/*
3031 serge 473
	 * For each CRTC in this fb, turn the connectors on/off.
1179 serge 474
	 */
3480 Serge 475
	drm_modeset_lock_all(dev);
476
	if (!drm_fb_helper_is_bound(fb_helper)) {
477
		drm_modeset_unlock_all(dev);
478
		return;
479
	}
480
 
1179 serge 481
	for (i = 0; i < fb_helper->crtc_count; i++) {
1963 serge 482
		crtc = fb_helper->crtc_info[i].mode_set.crtc;
1179 serge 483
 
1963 serge 484
		if (!crtc->enabled)
6084 serge 485
			continue;
1179 serge 486
 
3031 serge 487
		/* Walk the connectors & encoders on this fb turning them on/off */
1963 serge 488
		for (j = 0; j < fb_helper->connector_count; j++) {
489
			connector = fb_helper->connector_info[j]->connector;
3031 serge 490
			connector->funcs->dpms(connector, dpms_mode);
3192 Serge 491
			drm_object_property_set_value(&connector->base,
3031 serge 492
				dev->mode_config.dpms_property, dpms_mode);
1963 serge 493
		}
494
	}
3480 Serge 495
	drm_modeset_unlock_all(dev);
1179 serge 496
}
497
 
3480 Serge 498
/**
499
 * drm_fb_helper_blank - implementation for ->fb_blank
500
 * @blank: desired blanking state
501
 * @info: fbdev registered by the helper
502
 */
1179 serge 503
int drm_fb_helper_blank(int blank, struct fb_info *info)
504
{
6084 serge 505
	if (oops_in_progress)
506
		return -EBUSY;
507
 
1179 serge 508
	switch (blank) {
1321 serge 509
	/* Display: On; HSync: On, VSync: On */
1179 serge 510
	case FB_BLANK_UNBLANK:
3031 serge 511
		drm_fb_helper_dpms(info, DRM_MODE_DPMS_ON);
1179 serge 512
		break;
1321 serge 513
	/* Display: Off; HSync: On, VSync: On */
1179 serge 514
	case FB_BLANK_NORMAL:
3031 serge 515
		drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY);
1179 serge 516
		break;
1321 serge 517
	/* Display: Off; HSync: Off, VSync: On */
1179 serge 518
	case FB_BLANK_HSYNC_SUSPEND:
3031 serge 519
		drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY);
1179 serge 520
		break;
1321 serge 521
	/* Display: Off; HSync: On, VSync: Off */
1179 serge 522
	case FB_BLANK_VSYNC_SUSPEND:
3031 serge 523
		drm_fb_helper_dpms(info, DRM_MODE_DPMS_SUSPEND);
1179 serge 524
		break;
1321 serge 525
	/* Display: Off; HSync: Off, VSync: Off */
1179 serge 526
	case FB_BLANK_POWERDOWN:
3031 serge 527
		drm_fb_helper_dpms(info, DRM_MODE_DPMS_OFF);
1179 serge 528
		break;
529
	}
530
	return 0;
531
}
532
EXPORT_SYMBOL(drm_fb_helper_blank);
533
 
534
static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
535
{
536
	int i;
537
 
1963 serge 538
	for (i = 0; i < helper->connector_count; i++)
539
		kfree(helper->connector_info[i]);
540
	kfree(helper->connector_info);
3031 serge 541
	for (i = 0; i < helper->crtc_count; i++) {
1179 serge 542
		kfree(helper->crtc_info[i].mode_set.connectors);
3031 serge 543
		if (helper->crtc_info[i].mode_set.mode)
544
			drm_mode_destroy(helper->dev, helper->crtc_info[i].mode_set.mode);
545
	}
1179 serge 546
	kfree(helper->crtc_info);
547
}
548
 
3480 Serge 549
/**
5060 serge 550
 * drm_fb_helper_prepare - setup a drm_fb_helper structure
551
 * @dev: DRM device
552
 * @helper: driver-allocated fbdev helper structure to set up
553
 * @funcs: pointer to structure of functions associate with this helper
554
 *
555
 * Sets up the bare minimum to make the framebuffer helper usable. This is
556
 * useful to implement race-free initialization of the polling helpers.
557
 */
558
void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
559
			   const struct drm_fb_helper_funcs *funcs)
560
{
561
	INIT_LIST_HEAD(&helper->kernel_fb_list);
562
	helper->funcs = funcs;
563
	helper->dev = dev;
564
}
565
EXPORT_SYMBOL(drm_fb_helper_prepare);
566
 
567
/**
3480 Serge 568
 * drm_fb_helper_init - initialize a drm_fb_helper structure
569
 * @dev: drm device
570
 * @fb_helper: driver-allocated fbdev helper structure to initialize
571
 * @crtc_count: maximum number of crtcs to support in this fbdev emulation
572
 * @max_conn_count: max connector count
573
 *
574
 * This allocates the structures for the fbdev helper with the given limits.
575
 * Note that this won't yet touch the hardware (through the driver interfaces)
576
 * nor register the fbdev. This is only done in drm_fb_helper_initial_config()
577
 * to allow driver writes more control over the exact init sequence.
578
 *
5060 serge 579
 * Drivers must call drm_fb_helper_prepare() before calling this function.
3480 Serge 580
 *
581
 * RETURNS:
582
 * Zero if everything went ok, nonzero otherwise.
583
 */
1963 serge 584
int drm_fb_helper_init(struct drm_device *dev,
585
		       struct drm_fb_helper *fb_helper,
586
		       int crtc_count, int max_conn_count)
1179 serge 587
{
588
	struct drm_crtc *crtc;
589
	int i;
590
 
6084 serge 591
	if (!drm_fbdev_emulation)
592
		return 0;
593
 
5060 serge 594
	if (!max_conn_count)
595
		return -EINVAL;
1963 serge 596
 
597
	fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
598
	if (!fb_helper->crtc_info)
1179 serge 599
		return -ENOMEM;
600
 
1963 serge 601
	fb_helper->crtc_count = crtc_count;
602
	fb_helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL);
603
	if (!fb_helper->connector_info) {
604
		kfree(fb_helper->crtc_info);
605
		return -ENOMEM;
606
	}
5060 serge 607
	fb_helper->connector_info_alloc_count = dev->mode_config.num_connector;
1963 serge 608
	fb_helper->connector_count = 0;
1179 serge 609
 
610
	for (i = 0; i < crtc_count; i++) {
1963 serge 611
		fb_helper->crtc_info[i].mode_set.connectors =
1179 serge 612
			kcalloc(max_conn_count,
613
				sizeof(struct drm_connector *),
614
				GFP_KERNEL);
615
 
3031 serge 616
		if (!fb_helper->crtc_info[i].mode_set.connectors)
1179 serge 617
			goto out_free;
1963 serge 618
		fb_helper->crtc_info[i].mode_set.num_connectors = 0;
1179 serge 619
	}
620
 
621
	i = 0;
6084 serge 622
	drm_for_each_crtc(crtc, dev) {
1963 serge 623
		fb_helper->crtc_info[i].mode_set.crtc = crtc;
1179 serge 624
		i++;
625
	}
3031 serge 626
 
6084 serge 627
	fb_helper->atomic = !!drm_core_check_feature(dev, DRIVER_ATOMIC);
628
 
1179 serge 629
	return 0;
630
out_free:
1963 serge 631
	drm_fb_helper_crtc_free(fb_helper);
1179 serge 632
	return -ENOMEM;
633
}
1963 serge 634
EXPORT_SYMBOL(drm_fb_helper_init);
1179 serge 635
 
6084 serge 636
/**
637
 * drm_fb_helper_alloc_fbi - allocate fb_info and some of its members
638
 * @fb_helper: driver-allocated fbdev helper
639
 *
640
 * A helper to alloc fb_info and the members cmap and apertures. Called
641
 * by the driver within the fb_probe fb_helper callback function.
642
 *
643
 * RETURNS:
644
 * fb_info pointer if things went okay, pointer containing error code
645
 * otherwise
646
 */
647
struct fb_info *drm_fb_helper_alloc_fbi(struct drm_fb_helper *fb_helper)
648
{
649
	struct device *dev = fb_helper->dev->dev;
650
	struct fb_info *info;
651
	int ret;
652
 
653
	info = framebuffer_alloc(0, dev);
654
	if (!info)
655
		return ERR_PTR(-ENOMEM);
656
 
657
 
658
	fb_helper->fbdev = info;
659
 
660
	return info;
661
 
662
}
663
EXPORT_SYMBOL(drm_fb_helper_alloc_fbi);
1246 serge 664
static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
1221 serge 665
		     u16 blue, u16 regno, struct fb_info *info)
666
{
667
	struct drm_fb_helper *fb_helper = info->par;
668
	struct drm_framebuffer *fb = fb_helper->fb;
669
	int pindex;
670
 
1246 serge 671
	if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
672
		u32 *palette;
673
		u32 value;
674
		/* place color in psuedopalette */
675
		if (regno > 16)
676
			return -EINVAL;
677
		palette = (u32 *)info->pseudo_palette;
678
		red >>= (16 - info->var.red.length);
679
		green >>= (16 - info->var.green.length);
680
		blue >>= (16 - info->var.blue.length);
681
		value = (red << info->var.red.offset) |
682
			(green << info->var.green.offset) |
683
			(blue << info->var.blue.offset);
1963 serge 684
		if (info->var.transp.length > 0) {
685
			u32 mask = (1 << info->var.transp.length) - 1;
686
			mask <<= info->var.transp.offset;
687
			value |= mask;
688
		}
1246 serge 689
		palette[regno] = value;
690
		return 0;
691
	}
692
 
4104 Serge 693
	/*
694
	 * The driver really shouldn't advertise pseudo/directcolor
695
	 * visuals if it can't deal with the palette.
696
	 */
697
	if (WARN_ON(!fb_helper->funcs->gamma_set ||
698
		    !fb_helper->funcs->gamma_get))
699
		return -EINVAL;
700
 
1221 serge 701
	pindex = regno;
702
 
703
	if (fb->bits_per_pixel == 16) {
704
		pindex = regno << 3;
705
 
706
		if (fb->depth == 16 && regno > 63)
1246 serge 707
			return -EINVAL;
1221 serge 708
		if (fb->depth == 15 && regno > 31)
1246 serge 709
			return -EINVAL;
1221 serge 710
 
711
		if (fb->depth == 16) {
712
			u16 r, g, b;
713
			int i;
714
			if (regno < 32) {
715
				for (i = 0; i < 8; i++)
716
					fb_helper->funcs->gamma_set(crtc, red,
717
						green, blue, pindex + i);
718
			}
719
 
720
			fb_helper->funcs->gamma_get(crtc, &r,
721
						    &g, &b,
722
						    pindex >> 1);
723
 
724
			for (i = 0; i < 4; i++)
725
				fb_helper->funcs->gamma_set(crtc, r,
726
							    green, b,
727
							    (pindex >> 1) + i);
728
		}
729
	}
730
 
731
	if (fb->depth != 16)
732
		fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex);
1246 serge 733
	return 0;
1221 serge 734
}
735
 
3480 Serge 736
/**
737
 * drm_fb_helper_setcmap - implementation for ->fb_setcmap
738
 * @cmap: cmap to set
739
 * @info: fbdev registered by the helper
740
 */
1221 serge 741
int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
742
{
743
	struct drm_fb_helper *fb_helper = info->par;
4104 Serge 744
	struct drm_device *dev = fb_helper->dev;
6084 serge 745
	const struct drm_crtc_helper_funcs *crtc_funcs;
1221 serge 746
	u16 *red, *green, *blue, *transp;
747
	struct drm_crtc *crtc;
1963 serge 748
	int i, j, rc = 0;
1221 serge 749
	int start;
750
 
6084 serge 751
	if (oops_in_progress)
752
		return -EBUSY;
753
 
4104 Serge 754
	drm_modeset_lock_all(dev);
755
	if (!drm_fb_helper_is_bound(fb_helper)) {
756
		drm_modeset_unlock_all(dev);
757
		return -EBUSY;
758
	}
759
 
6084 serge 760
	for (i = 0; i < fb_helper->crtc_count; i++) {
1963 serge 761
		crtc = fb_helper->crtc_info[i].mode_set.crtc;
762
		crtc_funcs = crtc->helper_private;
1221 serge 763
 
764
		red = cmap->red;
765
		green = cmap->green;
766
		blue = cmap->blue;
767
		transp = cmap->transp;
768
		start = cmap->start;
769
 
1963 serge 770
		for (j = 0; j < cmap->len; j++) {
1221 serge 771
			u16 hred, hgreen, hblue, htransp = 0xffff;
772
 
773
			hred = *red++;
774
			hgreen = *green++;
775
			hblue = *blue++;
776
 
777
			if (transp)
778
				htransp = *transp++;
779
 
1246 serge 780
			rc = setcolreg(crtc, hred, hgreen, hblue, start++, info);
781
			if (rc)
4104 Serge 782
				goto out;
1221 serge 783
		}
4104 Serge 784
		if (crtc_funcs->load_lut)
6084 serge 785
			crtc_funcs->load_lut(crtc);
1221 serge 786
	}
4104 Serge 787
 out:
788
	drm_modeset_unlock_all(dev);
1221 serge 789
	return rc;
790
}
791
EXPORT_SYMBOL(drm_fb_helper_setcmap);
792
 
3480 Serge 793
/**
794
 * drm_fb_helper_check_var - implementation for ->fb_check_var
795
 * @var: screeninfo to check
796
 * @info: fbdev registered by the helper
797
 */
1179 serge 798
int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
799
			    struct fb_info *info)
800
{
801
	struct drm_fb_helper *fb_helper = info->par;
802
	struct drm_framebuffer *fb = fb_helper->fb;
803
	int depth;
804
 
1963 serge 805
	if (var->pixclock != 0 || in_dbg_master())
1179 serge 806
		return -EINVAL;
807
 
808
	/* Need to resize the fb object !!! */
3031 serge 809
	if (var->bits_per_pixel > fb->bits_per_pixel ||
810
	    var->xres > fb->width || var->yres > fb->height ||
811
	    var->xres_virtual > fb->width || var->yres_virtual > fb->height) {
1404 serge 812
		DRM_DEBUG("fb userspace requested width/height/bpp is greater than current fb "
3031 serge 813
			  "request %dx%d-%d (virtual %dx%d) > %dx%d-%d\n",
814
			  var->xres, var->yres, var->bits_per_pixel,
815
			  var->xres_virtual, var->yres_virtual,
1404 serge 816
			  fb->width, fb->height, fb->bits_per_pixel);
1179 serge 817
		return -EINVAL;
818
	}
819
 
820
	switch (var->bits_per_pixel) {
821
	case 16:
822
		depth = (var->green.length == 6) ? 16 : 15;
823
		break;
824
	case 32:
825
		depth = (var->transp.length > 0) ? 32 : 24;
826
		break;
827
	default:
828
		depth = var->bits_per_pixel;
829
		break;
830
	}
831
 
832
	switch (depth) {
833
	case 8:
834
		var->red.offset = 0;
835
		var->green.offset = 0;
836
		var->blue.offset = 0;
837
		var->red.length = 8;
838
		var->green.length = 8;
839
		var->blue.length = 8;
840
		var->transp.length = 0;
841
		var->transp.offset = 0;
842
		break;
843
	case 15:
844
		var->red.offset = 10;
845
		var->green.offset = 5;
846
		var->blue.offset = 0;
847
		var->red.length = 5;
848
		var->green.length = 5;
849
		var->blue.length = 5;
850
		var->transp.length = 1;
851
		var->transp.offset = 15;
852
		break;
853
	case 16:
854
		var->red.offset = 11;
855
		var->green.offset = 5;
856
		var->blue.offset = 0;
857
		var->red.length = 5;
858
		var->green.length = 6;
859
		var->blue.length = 5;
860
		var->transp.length = 0;
861
		var->transp.offset = 0;
862
		break;
863
	case 24:
864
		var->red.offset = 16;
865
		var->green.offset = 8;
866
		var->blue.offset = 0;
867
		var->red.length = 8;
868
		var->green.length = 8;
869
		var->blue.length = 8;
870
		var->transp.length = 0;
871
		var->transp.offset = 0;
872
		break;
873
	case 32:
874
		var->red.offset = 16;
875
		var->green.offset = 8;
876
		var->blue.offset = 0;
877
		var->red.length = 8;
878
		var->green.length = 8;
879
		var->blue.length = 8;
880
		var->transp.length = 8;
881
		var->transp.offset = 24;
882
		break;
883
	default:
884
		return -EINVAL;
885
	}
886
	return 0;
887
}
888
EXPORT_SYMBOL(drm_fb_helper_check_var);
889
 
3480 Serge 890
/**
891
 * drm_fb_helper_set_par - implementation for ->fb_set_par
892
 * @info: fbdev registered by the helper
893
 *
894
 * This will let fbcon do the mode init and is called at initialization time by
895
 * the fbdev core when registering the driver, and later on through the hotplug
896
 * callback.
897
 */
1179 serge 898
int drm_fb_helper_set_par(struct fb_info *info)
899
{
900
	struct drm_fb_helper *fb_helper = info->par;
901
	struct fb_var_screeninfo *var = &info->var;
902
 
6084 serge 903
	if (oops_in_progress)
904
		return -EBUSY;
905
 
1313 serge 906
	if (var->pixclock != 0) {
1430 serge 907
		DRM_ERROR("PIXEL CLOCK SET\n");
1179 serge 908
		return -EINVAL;
909
	}
910
 
5060 serge 911
	drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper);
1963 serge 912
 
1179 serge 913
	return 0;
914
}
915
EXPORT_SYMBOL(drm_fb_helper_set_par);
916
 
6084 serge 917
static int pan_display_atomic(struct fb_var_screeninfo *var,
918
			      struct fb_info *info)
919
{
920
	struct drm_fb_helper *fb_helper = info->par;
921
	struct drm_device *dev = fb_helper->dev;
922
	struct drm_atomic_state *state;
923
	struct drm_plane *plane;
924
	int i, ret;
925
	unsigned plane_mask;
926
 
927
	state = drm_atomic_state_alloc(dev);
928
	if (!state)
929
		return -ENOMEM;
930
 
931
	state->acquire_ctx = dev->mode_config.acquire_ctx;
932
retry:
933
	plane_mask = 0;
934
	for(i = 0; i < fb_helper->crtc_count; i++) {
935
		struct drm_mode_set *mode_set;
936
 
937
		mode_set = &fb_helper->crtc_info[i].mode_set;
938
 
939
		mode_set->x = var->xoffset;
940
		mode_set->y = var->yoffset;
941
 
942
		ret = __drm_atomic_helper_set_config(mode_set, state);
943
		if (ret != 0)
944
			goto fail;
945
 
946
		plane = mode_set->crtc->primary;
947
		plane_mask |= drm_plane_index(plane);
948
		plane->old_fb = plane->fb;
949
	}
950
 
951
	ret = drm_atomic_commit(state);
952
	if (ret != 0)
953
		goto fail;
954
 
955
	info->var.xoffset = var->xoffset;
956
	info->var.yoffset = var->yoffset;
957
 
958
 
959
fail:
960
	drm_atomic_clean_old_fb(dev, plane_mask, ret);
961
 
962
	if (ret == -EDEADLK)
963
		goto backoff;
964
 
965
	if (ret != 0)
966
		drm_atomic_state_free(state);
967
 
968
	return ret;
969
 
970
backoff:
971
	drm_atomic_state_clear(state);
972
	drm_atomic_legacy_backoff(state);
973
 
974
	goto retry;
975
}
976
 
3480 Serge 977
/**
978
 * drm_fb_helper_pan_display - implementation for ->fb_pan_display
979
 * @var: updated screen information
980
 * @info: fbdev registered by the helper
981
 */
1179 serge 982
int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
983
			      struct fb_info *info)
984
{
985
	struct drm_fb_helper *fb_helper = info->par;
986
	struct drm_device *dev = fb_helper->dev;
987
	struct drm_mode_set *modeset;
988
	int ret = 0;
989
	int i;
990
 
6084 serge 991
	if (oops_in_progress)
992
		return -EBUSY;
993
 
3480 Serge 994
	drm_modeset_lock_all(dev);
995
	if (!drm_fb_helper_is_bound(fb_helper)) {
996
		drm_modeset_unlock_all(dev);
997
		return -EBUSY;
998
	}
999
 
6084 serge 1000
	if (fb_helper->atomic) {
1001
		ret = pan_display_atomic(var, info);
1002
		goto unlock;
1003
	}
1004
 
1005
	for (i = 0; i < fb_helper->crtc_count; i++) {
1179 serge 1006
		modeset = &fb_helper->crtc_info[i].mode_set;
1007
 
1008
		modeset->x = var->xoffset;
1009
		modeset->y = var->yoffset;
1010
 
1011
		if (modeset->num_connectors) {
3480 Serge 1012
			ret = drm_mode_set_config_internal(modeset);
1179 serge 1013
			if (!ret) {
1014
				info->var.xoffset = var->xoffset;
1015
				info->var.yoffset = var->yoffset;
1016
			}
1017
		}
1018
	}
6084 serge 1019
unlock:
3480 Serge 1020
	drm_modeset_unlock_all(dev);
1179 serge 1021
	return ret;
1022
}
1023
EXPORT_SYMBOL(drm_fb_helper_pan_display);
1024
 
3480 Serge 1025
/*
1026
 * Allocates the backing storage and sets up the fbdev info structure through
1027
 * the ->fb_probe callback and then registers the fbdev and sets up the panic
1028
 * notifier.
1029
 */
1030
static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
6084 serge 1031
					 int preferred_bpp)
1179 serge 1032
{
3480 Serge 1033
	int ret = 0;
1179 serge 1034
	int crtc_count = 0;
1963 serge 1035
	int i;
1179 serge 1036
	struct fb_info *info;
1963 serge 1037
	struct drm_fb_helper_surface_size sizes;
1038
	int gamma_size = 0;
1179 serge 1039
 
1963 serge 1040
	memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size));
1041
	sizes.surface_depth = 24;
1042
	sizes.surface_bpp = 32;
1043
	sizes.fb_width = (unsigned)-1;
1044
	sizes.fb_height = (unsigned)-1;
1221 serge 1045
 
3031 serge 1046
	/* if driver picks 8 or 16 by default use that
1047
	   for both depth/bpp */
3192 Serge 1048
	if (preferred_bpp != sizes.surface_bpp)
3031 serge 1049
		sizes.surface_depth = sizes.surface_bpp = preferred_bpp;
3192 Serge 1050
 
3031 serge 1051
	/* first up get a count of crtcs now in use and new min/maxes width/heights */
1052
	for (i = 0; i < fb_helper->connector_count; i++) {
1053
		struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i];
1054
		struct drm_cmdline_mode *cmdline_mode;
1055
 
5271 serge 1056
		cmdline_mode = &fb_helper_conn->connector->cmdline_mode;
3031 serge 1057
 
1058
		if (cmdline_mode->bpp_specified) {
1059
			switch (cmdline_mode->bpp) {
1060
			case 8:
1061
				sizes.surface_depth = sizes.surface_bpp = 8;
1062
				break;
1063
			case 15:
1064
				sizes.surface_depth = 15;
1065
				sizes.surface_bpp = 16;
1066
				break;
1067
			case 16:
1068
				sizes.surface_depth = sizes.surface_bpp = 16;
1069
				break;
1070
			case 24:
1071
				sizes.surface_depth = sizes.surface_bpp = 24;
1072
				break;
1073
			case 32:
6084 serge 1074
				sizes.surface_depth = 24;
1075
				sizes.surface_bpp = 32;
3031 serge 1076
				break;
1077
			}
1078
			break;
1079
		}
1080
	}
1221 serge 1081
 
1963 serge 1082
	crtc_count = 0;
1083
	for (i = 0; i < fb_helper->crtc_count; i++) {
1084
		struct drm_display_mode *desired_mode;
6084 serge 1085
		struct drm_mode_set *mode_set;
1086
		int x, y, j;
1087
		/* in case of tile group, are we the last tile vert or horiz?
1088
		 * If no tile group you are always the last one both vertically
1089
		 * and horizontally
1090
		 */
1091
		bool lastv = true, lasth = true;
1092
 
1963 serge 1093
		desired_mode = fb_helper->crtc_info[i].desired_mode;
6084 serge 1094
		mode_set = &fb_helper->crtc_info[i].mode_set;
1095
 
1096
		if (!desired_mode)
1097
			continue;
1098
 
1099
		crtc_count++;
1100
 
5271 serge 1101
		x = fb_helper->crtc_info[i].x;
1102
		y = fb_helper->crtc_info[i].y;
6084 serge 1103
 
1104
		if (gamma_size == 0)
1105
			gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size;
1106
 
1107
		sizes.surface_width  = max_t(u32, desired_mode->hdisplay + x, sizes.surface_width);
1108
		sizes.surface_height = max_t(u32, desired_mode->vdisplay + y, sizes.surface_height);
1109
 
1110
		for (j = 0; j < mode_set->num_connectors; j++) {
1111
			struct drm_connector *connector = mode_set->connectors[j];
1112
			if (connector->has_tile) {
1113
				lasth = (connector->tile_h_loc == (connector->num_h_tile - 1));
1114
				lastv = (connector->tile_v_loc == (connector->num_v_tile - 1));
1115
				/* cloning to multiple tiles is just crazy-talk, so: */
1116
				break;
1117
			}
1118
		}
1119
 
1120
		if (lasth)
1121
			sizes.fb_width  = min_t(u32, desired_mode->hdisplay + x, sizes.fb_width);
1122
		if (lastv)
1123
			sizes.fb_height = min_t(u32, desired_mode->vdisplay + y, sizes.fb_height);
1179 serge 1124
	}
1125
 
1963 serge 1126
	if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) {
1179 serge 1127
		/* hmm everyone went away - assume VGA cable just fell out
1128
		   and will come back later. */
1963 serge 1129
		DRM_INFO("Cannot find any crtc or sizes - going 1024x768\n");
1130
		sizes.fb_width = sizes.surface_width = 1024;
1131
		sizes.fb_height = sizes.surface_height = 768;
1179 serge 1132
	}
1133
 
1963 serge 1134
	/* push down into drivers */
3480 Serge 1135
	ret = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes);
1136
	if (ret < 0)
1137
		return ret;
1179 serge 1138
 
1963 serge 1139
	info = fb_helper->fbdev;
1179 serge 1140
 
3480 Serge 1141
	/*
1142
	 * Set the fb pointer - usually drm_setup_crtcs does this for hotplug
1143
	 * events, but at init time drm_setup_crtcs needs to be called before
1144
	 * the fb is allocated (since we need to figure out the desired size of
1145
	 * the fb before we can allocate it ...). Hence we need to fix things up
1146
	 * here again.
1147
	 */
3192 Serge 1148
	for (i = 0; i < fb_helper->crtc_count; i++)
3480 Serge 1149
		if (fb_helper->crtc_info[i].mode_set.num_connectors)
6084 serge 1150
			fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb;
1179 serge 1151
 
3480 Serge 1152
 
6084 serge 1153
	info->var.pixclock = 0;
1154
 
5271 serge 1155
	dev_info(fb_helper->dev->dev, "fb%d: %s frame buffer device\n",
1156
			info->node, info->fix.id);
1963 serge 1157
 
1158
 
6084 serge 1159
	list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
1160
 
1179 serge 1161
	return 0;
1162
}
1163
 
3480 Serge 1164
/**
1165
 * drm_fb_helper_fill_fix - initializes fixed fbdev information
1166
 * @info: fbdev registered by the helper
1167
 * @pitch: desired pitch
1168
 * @depth: desired depth
1169
 *
1170
 * Helper to fill in the fixed fbdev information useful for a non-accelerated
1171
 * fbdev emulations. Drivers which support acceleration methods which impose
1172
 * additional constraints need to set up their own limits.
1173
 *
1174
 * Drivers should call this (or their equivalent setup code) from their
1175
 * ->fb_probe callback.
1176
 */
1221 serge 1177
void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
1178
			    uint32_t depth)
1179 serge 1179
{
1180
	info->fix.type = FB_TYPE_PACKED_PIXELS;
1221 serge 1181
	info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR :
1246 serge 1182
		FB_VISUAL_TRUECOLOR;
1963 serge 1183
	info->fix.mmio_start = 0;
1184
	info->fix.mmio_len = 0;
1179 serge 1185
	info->fix.type_aux = 0;
1186
	info->fix.xpanstep = 1; /* doing it in hw */
1187
	info->fix.ypanstep = 1; /* doing it in hw */
1188
	info->fix.ywrapstep = 0;
1189
	info->fix.accel = FB_ACCEL_NONE;
1190
 
1191
	info->fix.line_length = pitch;
1192
	return;
1193
}
1194
EXPORT_SYMBOL(drm_fb_helper_fill_fix);
1195
 
3480 Serge 1196
/**
1197
 * drm_fb_helper_fill_var - initalizes variable fbdev information
1198
 * @info: fbdev instance to set up
1199
 * @fb_helper: fb helper instance to use as template
1200
 * @fb_width: desired fb width
1201
 * @fb_height: desired fb height
1202
 *
1203
 * Sets up the variable fbdev metainformation from the given fb helper instance
1204
 * and the drm framebuffer allocated in fb_helper->fb.
1205
 *
1206
 * Drivers should call this (or their equivalent setup code) from their
1207
 * ->fb_probe callback after having allocated the fbdev backing
1208
 * storage framebuffer.
1209
 */
1963 serge 1210
void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper,
1179 serge 1211
			    uint32_t fb_width, uint32_t fb_height)
1212
{
1963 serge 1213
	struct drm_framebuffer *fb = fb_helper->fb;
1214
	info->pseudo_palette = fb_helper->pseudo_palette;
1179 serge 1215
	info->var.xres_virtual = fb->width;
1216
	info->var.yres_virtual = fb->height;
1217
	info->var.bits_per_pixel = fb->bits_per_pixel;
1963 serge 1218
	info->var.accel_flags = FB_ACCELF_TEXT;
1179 serge 1219
	info->var.xoffset = 0;
1220
	info->var.yoffset = 0;
1221
	info->var.activate = FB_ACTIVATE_NOW;
1222
	info->var.height = -1;
1223
	info->var.width = -1;
1224
 
1225
	switch (fb->depth) {
1226
	case 8:
1227
		info->var.red.offset = 0;
1228
		info->var.green.offset = 0;
1229
		info->var.blue.offset = 0;
1230
		info->var.red.length = 8; /* 8bit DAC */
1231
		info->var.green.length = 8;
1232
		info->var.blue.length = 8;
1233
		info->var.transp.offset = 0;
1234
		info->var.transp.length = 0;
1235
		break;
1236
	case 15:
1237
		info->var.red.offset = 10;
1238
		info->var.green.offset = 5;
1239
		info->var.blue.offset = 0;
1240
		info->var.red.length = 5;
1241
		info->var.green.length = 5;
1242
		info->var.blue.length = 5;
1243
		info->var.transp.offset = 15;
1244
		info->var.transp.length = 1;
1245
		break;
1246
	case 16:
1247
		info->var.red.offset = 11;
1248
		info->var.green.offset = 5;
1249
		info->var.blue.offset = 0;
1250
		info->var.red.length = 5;
1251
		info->var.green.length = 6;
1252
		info->var.blue.length = 5;
1253
		info->var.transp.offset = 0;
1254
		break;
1255
	case 24:
1256
		info->var.red.offset = 16;
1257
		info->var.green.offset = 8;
1258
		info->var.blue.offset = 0;
1259
		info->var.red.length = 8;
1260
		info->var.green.length = 8;
1261
		info->var.blue.length = 8;
1262
		info->var.transp.offset = 0;
1263
		info->var.transp.length = 0;
1264
		break;
1265
	case 32:
1266
		info->var.red.offset = 16;
1267
		info->var.green.offset = 8;
1268
		info->var.blue.offset = 0;
1269
		info->var.red.length = 8;
1270
		info->var.green.length = 8;
1271
		info->var.blue.length = 8;
1272
		info->var.transp.offset = 24;
1273
		info->var.transp.length = 8;
1274
		break;
1275
	default:
1276
		break;
1277
	}
1278
 
1279
	info->var.xres = fb_width;
1280
	info->var.yres = fb_height;
1281
}
1282
EXPORT_SYMBOL(drm_fb_helper_fill_var);
1963 serge 1283
 
1284
static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper,
1285
					       uint32_t maxX,
1286
					       uint32_t maxY)
1287
{
1288
	struct drm_connector *connector;
1289
	int count = 0;
1290
	int i;
1291
 
1292
	for (i = 0; i < fb_helper->connector_count; i++) {
1293
		connector = fb_helper->connector_info[i]->connector;
1294
		count += connector->funcs->fill_modes(connector, maxX, maxY);
1295
	}
1296
 
1297
	return count;
1298
}
1299
 
5060 serge 1300
struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height)
1963 serge 1301
{
1302
	struct drm_display_mode *mode;
1303
 
1304
	list_for_each_entry(mode, &fb_connector->connector->modes, head) {
5060 serge 1305
		if (mode->hdisplay > width ||
1306
		    mode->vdisplay > height)
1963 serge 1307
			continue;
1308
		if (mode->type & DRM_MODE_TYPE_PREFERRED)
1309
			return mode;
1310
	}
1311
	return NULL;
1312
}
5060 serge 1313
EXPORT_SYMBOL(drm_has_preferred_mode);
1963 serge 1314
 
1315
static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector)
1316
{
5271 serge 1317
	return fb_connector->connector->cmdline_mode.specified;
1963 serge 1318
}
1319
 
5060 serge 1320
struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn,
3746 Serge 1321
						      int width, int height)
1322
{
1323
	struct drm_cmdline_mode *cmdline_mode;
6084 serge 1324
	struct drm_display_mode *mode;
5060 serge 1325
	bool prefer_non_interlace;
1963 serge 1326
 
6084 serge 1327
	cmdline_mode = &fb_helper_conn->connector->cmdline_mode;
3746 Serge 1328
	if (cmdline_mode->specified == false)
6088 serge 1329
		return NULL;
3746 Serge 1330
 
1331
	/* attempt to find a matching mode in the list of modes
1332
	 *  we have gotten so far, if not add a CVT mode that conforms
1333
	 */
1334
	if (cmdline_mode->rb || cmdline_mode->margins)
1335
		goto create_mode;
1336
 
5060 serge 1337
	prefer_non_interlace = !cmdline_mode->interlace;
6084 serge 1338
again:
3746 Serge 1339
	list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
1340
		/* check width/height */
1341
		if (mode->hdisplay != cmdline_mode->xres ||
1342
		    mode->vdisplay != cmdline_mode->yres)
1343
			continue;
1344
 
1345
		if (cmdline_mode->refresh_specified) {
1346
			if (mode->vrefresh != cmdline_mode->refresh)
1347
				continue;
1348
		}
1349
 
1350
		if (cmdline_mode->interlace) {
1351
			if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
1352
				continue;
5060 serge 1353
		} else if (prefer_non_interlace) {
1354
			if (mode->flags & DRM_MODE_FLAG_INTERLACE)
1355
				continue;
3746 Serge 1356
		}
1357
		return mode;
1358
	}
1359
 
5060 serge 1360
	if (prefer_non_interlace) {
1361
		prefer_non_interlace = false;
1362
		goto again;
1363
	}
1364
 
3746 Serge 1365
create_mode:
1366
	mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev,
1367
						 cmdline_mode);
1368
	list_add(&mode->head, &fb_helper_conn->connector->modes);
1369
	return mode;
1370
}
5060 serge 1371
EXPORT_SYMBOL(drm_pick_cmdline_mode);
3746 Serge 1372
 
1963 serge 1373
static bool drm_connector_enabled(struct drm_connector *connector, bool strict)
1374
{
1375
	bool enable;
1376
 
3192 Serge 1377
	if (strict)
1963 serge 1378
		enable = connector->status == connector_status_connected;
3192 Serge 1379
	else
1963 serge 1380
		enable = connector->status != connector_status_disconnected;
3192 Serge 1381
 
1963 serge 1382
	return enable;
1383
}
1384
 
1385
static void drm_enable_connectors(struct drm_fb_helper *fb_helper,
1386
				  bool *enabled)
1387
{
1388
	bool any_enabled = false;
1389
	struct drm_connector *connector;
1390
	int i = 0;
1391
 
1392
	for (i = 0; i < fb_helper->connector_count; i++) {
1393
		connector = fb_helper->connector_info[i]->connector;
1394
		enabled[i] = drm_connector_enabled(connector, true);
1395
		DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id,
1396
			  enabled[i] ? "yes" : "no");
1397
		any_enabled |= enabled[i];
1398
	}
1399
 
1400
	if (any_enabled)
1401
		return;
1402
 
1403
	for (i = 0; i < fb_helper->connector_count; i++) {
1404
		connector = fb_helper->connector_info[i]->connector;
1405
		enabled[i] = drm_connector_enabled(connector, false);
1406
	}
1407
}
1408
 
3746 Serge 1409
static bool drm_target_cloned(struct drm_fb_helper *fb_helper,
1410
			      struct drm_display_mode **modes,
5271 serge 1411
			      struct drm_fb_offset *offsets,
3746 Serge 1412
			      bool *enabled, int width, int height)
1413
{
1414
	int count, i, j;
1415
	bool can_clone = false;
1416
	struct drm_fb_helper_connector *fb_helper_conn;
1417
	struct drm_display_mode *dmt_mode, *mode;
1963 serge 1418
 
3746 Serge 1419
	/* only contemplate cloning in the single crtc case */
1420
	if (fb_helper->crtc_count > 1)
1421
		return false;
1422
 
1423
	count = 0;
1424
	for (i = 0; i < fb_helper->connector_count; i++) {
1425
		if (enabled[i])
1426
			count++;
1427
	}
1428
 
1429
	/* only contemplate cloning if more than one connector is enabled */
1430
	if (count <= 1)
1431
		return false;
1432
 
1433
	/* check the command line or if nothing common pick 1024x768 */
1434
	can_clone = true;
1435
	for (i = 0; i < fb_helper->connector_count; i++) {
1436
		if (!enabled[i])
1437
			continue;
1438
		fb_helper_conn = fb_helper->connector_info[i];
1439
		modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height);
1440
		if (!modes[i]) {
1441
			can_clone = false;
1442
			break;
1443
		}
1444
		for (j = 0; j < i; j++) {
1445
			if (!enabled[j])
1446
				continue;
1447
			if (!drm_mode_equal(modes[j], modes[i]))
1448
				can_clone = false;
1449
		}
1450
	}
1451
 
1452
	if (can_clone) {
1453
		DRM_DEBUG_KMS("can clone using command line\n");
1454
		return true;
1455
	}
1456
 
1457
	/* try and find a 1024x768 mode on each connector */
1458
	can_clone = true;
1459
	dmt_mode = drm_mode_find_dmt(fb_helper->dev, 1024, 768, 60, false);
1460
 
1461
	for (i = 0; i < fb_helper->connector_count; i++) {
1462
 
1463
		if (!enabled[i])
1464
			continue;
1465
 
1466
		fb_helper_conn = fb_helper->connector_info[i];
1467
		list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
1468
			if (drm_mode_equal(mode, dmt_mode))
1469
				modes[i] = mode;
1470
		}
1471
		if (!modes[i])
1472
			can_clone = false;
1473
	}
1474
 
1475
	if (can_clone) {
1476
		DRM_DEBUG_KMS("can clone using 1024x768\n");
1477
		return true;
1478
	}
1479
	DRM_INFO("kms: can't enable cloning when we probably wanted to.\n");
1480
	return false;
1481
}
1482
 
5271 serge 1483
static int drm_get_tile_offsets(struct drm_fb_helper *fb_helper,
1484
				struct drm_display_mode **modes,
1485
				struct drm_fb_offset *offsets,
1486
				int idx,
1487
				int h_idx, int v_idx)
1488
{
1489
	struct drm_fb_helper_connector *fb_helper_conn;
1490
	int i;
1491
	int hoffset = 0, voffset = 0;
1492
 
1493
	for (i = 0; i < fb_helper->connector_count; i++) {
1494
		fb_helper_conn = fb_helper->connector_info[i];
1495
		if (!fb_helper_conn->connector->has_tile)
1496
			continue;
1497
 
1498
		if (!modes[i] && (h_idx || v_idx)) {
1499
			DRM_DEBUG_KMS("no modes for connector tiled %d %d\n", i,
1500
				      fb_helper_conn->connector->base.id);
1501
			continue;
1502
		}
1503
		if (fb_helper_conn->connector->tile_h_loc < h_idx)
1504
			hoffset += modes[i]->hdisplay;
1505
 
1506
		if (fb_helper_conn->connector->tile_v_loc < v_idx)
1507
			voffset += modes[i]->vdisplay;
1508
	}
1509
	offsets[idx].x = hoffset;
1510
	offsets[idx].y = voffset;
1511
	DRM_DEBUG_KMS("returned %d %d for %d %d\n", hoffset, voffset, h_idx, v_idx);
1512
	return 0;
1513
}
1514
 
1963 serge 1515
static bool drm_target_preferred(struct drm_fb_helper *fb_helper,
1516
				 struct drm_display_mode **modes,
5271 serge 1517
				 struct drm_fb_offset *offsets,
1963 serge 1518
				 bool *enabled, int width, int height)
1519
{
1520
	struct drm_fb_helper_connector *fb_helper_conn;
1521
	int i;
5271 serge 1522
	uint64_t conn_configured = 0, mask;
1523
	int tile_pass = 0;
1524
	mask = (1 << fb_helper->connector_count) - 1;
1525
retry:
1963 serge 1526
	for (i = 0; i < fb_helper->connector_count; i++) {
1527
		fb_helper_conn = fb_helper->connector_info[i];
1528
 
5271 serge 1529
		if (conn_configured & (1 << i))
1963 serge 1530
			continue;
1531
 
5271 serge 1532
		if (enabled[i] == false) {
1533
			conn_configured |= (1 << i);
1534
			continue;
1535
		}
1536
 
1537
		/* first pass over all the untiled connectors */
1538
		if (tile_pass == 0 && fb_helper_conn->connector->has_tile)
1539
			continue;
1540
 
1541
		if (tile_pass == 1) {
1542
			if (fb_helper_conn->connector->tile_h_loc != 0 ||
1543
			    fb_helper_conn->connector->tile_v_loc != 0)
6084 serge 1544
				continue;
5271 serge 1545
 
1546
		} else {
1547
			if (fb_helper_conn->connector->tile_h_loc != tile_pass -1 &&
1548
			    fb_helper_conn->connector->tile_v_loc != tile_pass - 1)
1549
			/* if this tile_pass doesn't cover any of the tiles - keep going */
1550
				continue;
1551
 
1552
			/* find the tile offsets for this pass - need
1553
			   to find all tiles left and above */
1554
			drm_get_tile_offsets(fb_helper, modes, offsets,
1555
					     i, fb_helper_conn->connector->tile_h_loc, fb_helper_conn->connector->tile_v_loc);
1556
		}
1963 serge 1557
		DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
1558
			      fb_helper_conn->connector->base.id);
1559
 
1560
		/* got for command line mode first */
3746 Serge 1561
		modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height);
1963 serge 1562
		if (!modes[i]) {
5271 serge 1563
			DRM_DEBUG_KMS("looking for preferred mode on connector %d %d\n",
1564
				      fb_helper_conn->connector->base.id, fb_helper_conn->connector->tile_group ? fb_helper_conn->connector->tile_group->id : 0);
1963 serge 1565
			modes[i] = drm_has_preferred_mode(fb_helper_conn, width, height);
1566
		}
1567
		/* No preferred modes, pick one off the list */
1568
		if (!modes[i] && !list_empty(&fb_helper_conn->connector->modes)) {
1569
			list_for_each_entry(modes[i], &fb_helper_conn->connector->modes, head)
1570
				break;
1571
		}
1572
		DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name :
1573
			  "none");
5271 serge 1574
		conn_configured |= (1 << i);
1963 serge 1575
	}
5271 serge 1576
 
1577
	if ((conn_configured & mask) != mask) {
1578
		tile_pass++;
1579
		goto retry;
1580
	}
1963 serge 1581
	return true;
1582
}
1583
 
1584
static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
1585
			  struct drm_fb_helper_crtc **best_crtcs,
1586
			  struct drm_display_mode **modes,
1587
			  int n, int width, int height)
1588
{
1589
	int c, o;
1590
	struct drm_connector *connector;
6084 serge 1591
	const struct drm_connector_helper_funcs *connector_funcs;
1963 serge 1592
	struct drm_encoder *encoder;
1593
	int my_score, best_score, score;
1594
	struct drm_fb_helper_crtc **crtcs, *crtc;
1595
	struct drm_fb_helper_connector *fb_helper_conn;
1596
 
1597
	if (n == fb_helper->connector_count)
1598
		return 0;
1599
 
1600
	fb_helper_conn = fb_helper->connector_info[n];
1601
	connector = fb_helper_conn->connector;
1602
 
1603
	best_crtcs[n] = NULL;
1604
	best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height);
1605
	if (modes[n] == NULL)
1606
		return best_score;
1607
 
6660 serge 1608
	crtcs = kzalloc(fb_helper->connector_count *
1963 serge 1609
			sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
1610
	if (!crtcs)
1611
		return best_score;
1612
 
1613
	my_score = 1;
1614
	if (connector->status == connector_status_connected)
1615
		my_score++;
1616
	if (drm_has_cmdline_mode(fb_helper_conn))
1617
		my_score++;
1618
	if (drm_has_preferred_mode(fb_helper_conn, width, height))
1619
		my_score++;
1620
 
1621
	connector_funcs = connector->helper_private;
1622
	encoder = connector_funcs->best_encoder(connector);
1623
	if (!encoder)
1624
		goto out;
1625
 
1626
	/* select a crtc for this connector and then attempt to configure
1627
	   remaining connectors */
1628
	for (c = 0; c < fb_helper->crtc_count; c++) {
1629
		crtc = &fb_helper->crtc_info[c];
1630
 
3192 Serge 1631
		if ((encoder->possible_crtcs & (1 << c)) == 0)
1963 serge 1632
			continue;
1633
 
1634
		for (o = 0; o < n; o++)
1635
			if (best_crtcs[o] == crtc)
1636
				break;
1637
 
1638
		if (o < n) {
1639
			/* ignore cloning unless only a single crtc */
1640
			if (fb_helper->crtc_count > 1)
1641
				continue;
1642
 
1643
			if (!drm_mode_equal(modes[o], modes[n]))
1644
				continue;
1645
		}
1646
 
1647
		crtcs[n] = crtc;
1648
		memcpy(crtcs, best_crtcs, n * sizeof(struct drm_fb_helper_crtc *));
1649
		score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1,
1650
						  width, height);
1651
		if (score > best_score) {
1652
			best_score = score;
1653
			memcpy(best_crtcs, crtcs,
6660 serge 1654
			       fb_helper->connector_count *
1963 serge 1655
			       sizeof(struct drm_fb_helper_crtc *));
1656
		}
1657
	}
1658
out:
1659
	kfree(crtcs);
1660
	return best_score;
1661
}
1662
 
1663
static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
1664
{
1665
	struct drm_device *dev = fb_helper->dev;
1666
	struct drm_fb_helper_crtc **crtcs;
1667
	struct drm_display_mode **modes;
5271 serge 1668
	struct drm_fb_offset *offsets;
1963 serge 1669
	struct drm_mode_set *modeset;
1670
	bool *enabled;
1671
	int width, height;
3746 Serge 1672
	int i;
1963 serge 1673
 
1674
	DRM_DEBUG_KMS("\n");
1675
 
1676
	width = dev->mode_config.max_width;
1677
	height = dev->mode_config.max_height;
1678
 
1679
	crtcs = kcalloc(dev->mode_config.num_connector,
1680
			sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
1681
	modes = kcalloc(dev->mode_config.num_connector,
1682
			sizeof(struct drm_display_mode *), GFP_KERNEL);
5271 serge 1683
	offsets = kcalloc(dev->mode_config.num_connector,
1684
			  sizeof(struct drm_fb_offset), GFP_KERNEL);
1963 serge 1685
	enabled = kcalloc(dev->mode_config.num_connector,
1686
			  sizeof(bool), GFP_KERNEL);
5271 serge 1687
	if (!crtcs || !modes || !enabled || !offsets) {
3192 Serge 1688
		DRM_ERROR("Memory allocation failed\n");
1689
		goto out;
1690
	}
1963 serge 1691
 
3192 Serge 1692
 
1963 serge 1693
	drm_enable_connectors(fb_helper, enabled);
1694
 
3746 Serge 1695
	if (!(fb_helper->funcs->initial_config &&
1696
	      fb_helper->funcs->initial_config(fb_helper, crtcs, modes,
5271 serge 1697
					       offsets,
3746 Serge 1698
					       enabled, width, height))) {
1699
		memset(modes, 0, dev->mode_config.num_connector*sizeof(modes[0]));
1700
		memset(crtcs, 0, dev->mode_config.num_connector*sizeof(crtcs[0]));
5271 serge 1701
		memset(offsets, 0, dev->mode_config.num_connector*sizeof(offsets[0]));
1963 serge 1702
 
5271 serge 1703
		if (!drm_target_cloned(fb_helper, modes, offsets,
1704
				       enabled, width, height) &&
1705
		    !drm_target_preferred(fb_helper, modes, offsets,
1706
					  enabled, width, height))
1963 serge 1707
			DRM_ERROR("Unable to find initial modes\n");
1708
 
3746 Serge 1709
		DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n",
1710
			      width, height);
1963 serge 1711
 
6084 serge 1712
		drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height);
3746 Serge 1713
	}
1963 serge 1714
 
1715
	/* need to set the modesets up here for use later */
1716
	/* fill out the connector<->crtc mappings into the modesets */
1717
	for (i = 0; i < fb_helper->crtc_count; i++) {
1718
		modeset = &fb_helper->crtc_info[i].mode_set;
1719
		modeset->num_connectors = 0;
3480 Serge 1720
		modeset->fb = NULL;
1963 serge 1721
	}
1722
 
1723
	for (i = 0; i < fb_helper->connector_count; i++) {
1724
		struct drm_display_mode *mode = modes[i];
1725
		struct drm_fb_helper_crtc *fb_crtc = crtcs[i];
5271 serge 1726
		struct drm_fb_offset *offset = &offsets[i];
1963 serge 1727
		modeset = &fb_crtc->mode_set;
1728
 
1729
		if (mode && fb_crtc) {
5271 serge 1730
			DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n",
1731
				      mode->name, fb_crtc->mode_set.crtc->base.id, offset->x, offset->y);
1963 serge 1732
			fb_crtc->desired_mode = mode;
5271 serge 1733
			fb_crtc->x = offset->x;
1734
			fb_crtc->y = offset->y;
1963 serge 1735
			if (modeset->mode)
1736
				drm_mode_destroy(dev, modeset->mode);
1737
			modeset->mode = drm_mode_duplicate(dev,
1738
							   fb_crtc->desired_mode);
1739
			modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector;
3480 Serge 1740
			modeset->fb = fb_helper->fb;
5271 serge 1741
			modeset->x = offset->x;
1742
			modeset->y = offset->y;
1963 serge 1743
		}
1744
	}
1745
 
3480 Serge 1746
	/* Clear out any old modes if there are no more connected outputs. */
1747
	for (i = 0; i < fb_helper->crtc_count; i++) {
1748
		modeset = &fb_helper->crtc_info[i].mode_set;
1749
		if (modeset->num_connectors == 0) {
1750
			BUG_ON(modeset->fb);
1751
			if (modeset->mode)
1752
				drm_mode_destroy(dev, modeset->mode);
1753
			modeset->mode = NULL;
1754
		}
1755
	}
3192 Serge 1756
out:
1963 serge 1757
	kfree(crtcs);
1758
	kfree(modes);
5271 serge 1759
	kfree(offsets);
1963 serge 1760
	kfree(enabled);
1761
}
1762
 
1763
/**
3480 Serge 1764
 * drm_fb_helper_initial_config - setup a sane initial connector configuration
3192 Serge 1765
 * @fb_helper: fb_helper device struct
1766
 * @bpp_sel: bpp value to use for the framebuffer configuration
1963 serge 1767
 *
3192 Serge 1768
 * Scans the CRTCs and connectors and tries to put together an initial setup.
1963 serge 1769
 * At the moment, this is a cloned configuration across all heads with
1770
 * a new framebuffer object as the backing store.
1771
 *
3480 Serge 1772
 * Note that this also registers the fbdev and so allows userspace to call into
1773
 * the driver through the fbdev interfaces.
1774
 *
1775
 * This function will call down into the ->fb_probe callback to let
1776
 * the driver allocate and initialize the fbdev info structure and the drm
1777
 * framebuffer used to back the fbdev. drm_fb_helper_fill_var() and
1778
 * drm_fb_helper_fill_fix() are provided as helpers to setup simple default
1779
 * values for the fbdev info structure.
1780
 *
1963 serge 1781
 * RETURNS:
1782
 * Zero if everything went ok, nonzero otherwise.
1783
 */
6084 serge 1784
int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
1963 serge 1785
{
1786
	struct drm_device *dev = fb_helper->dev;
1787
	int count = 0;
1788
 
6084 serge 1789
	if (!drm_fbdev_emulation)
1790
		return 0;
1791
 
5060 serge 1792
	mutex_lock(&dev->mode_config.mutex);
1963 serge 1793
	count = drm_fb_helper_probe_connector_modes(fb_helper,
1794
						    dev->mode_config.max_width,
1795
						    dev->mode_config.max_height);
5060 serge 1796
	mutex_unlock(&dev->mode_config.mutex);
1963 serge 1797
	/*
1798
	 * we shouldn't end up with no modes here.
1799
	 */
3192 Serge 1800
	if (count == 0)
1801
		dev_info(fb_helper->dev->dev, "No connectors reported connected with modes\n");
1802
 
1803
	drm_setup_crtcs(fb_helper);
1804
 
3243 Serge 1805
	return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
3192 Serge 1806
}
1807
EXPORT_SYMBOL(drm_fb_helper_initial_config);
1808
 
1809
/**
1810
 * drm_fb_helper_hotplug_event - respond to a hotplug notification by
1811
 *                               probing all the outputs attached to the fb
1812
 * @fb_helper: the drm_fb_helper
1813
 *
1814
 * Scan the connectors attached to the fb_helper and try to put together a
1815
 * setup after *notification of a change in output configuration.
1816
 *
3480 Serge 1817
 * Called at runtime, takes the mode config locks to be able to check/change the
1818
 * modeset configuration. Must be run from process context (which usually means
1819
 * either the output polling work or a work item launched from the driver's
1820
 * hotplug interrupt).
1821
 *
5060 serge 1822
 * Note that drivers may call this even before calling
1823
 * drm_fb_helper_initial_config but only aftert drm_fb_helper_init. This allows
1824
 * for a race-free fbcon setup and will make sure that the fbdev emulation will
1825
 * not miss any hotplug events.
3480 Serge 1826
 *
3192 Serge 1827
 * RETURNS:
1828
 * 0 on success and a non-zero error code otherwise.
1829
 */
1830
int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
1831
{
1832
	struct drm_device *dev = fb_helper->dev;
4560 Serge 1833
	u32 max_width, max_height;
3192 Serge 1834
 
6084 serge 1835
	if (!drm_fbdev_emulation)
1836
		return 0;
1837
 
3480 Serge 1838
	mutex_lock(&fb_helper->dev->mode_config.mutex);
5060 serge 1839
	if (!fb_helper->fb || !drm_fb_helper_is_bound(fb_helper)) {
3192 Serge 1840
		fb_helper->delayed_hotplug = true;
3480 Serge 1841
		mutex_unlock(&fb_helper->dev->mode_config.mutex);
3192 Serge 1842
		return 0;
1843
	}
1844
	DRM_DEBUG_KMS("\n");
1845
 
6296 serge 1846
    max_width = 8192; //fb_helper->fb->width;
1847
    max_height = 8192; //fb_helper->fb->height;
3192 Serge 1848
 
4560 Serge 1849
	drm_fb_helper_probe_connector_modes(fb_helper, max_width, max_height);
3480 Serge 1850
	mutex_unlock(&fb_helper->dev->mode_config.mutex);
1851
 
6296 serge 1852
//   drm_modeset_lock_all(dev);
1853
//   drm_setup_crtcs(fb_helper);
1854
//   drm_modeset_unlock_all(dev);
1855
//   drm_fb_helper_set_par(fb_helper->fbdev);
3480 Serge 1856
	return 0;
1963 serge 1857
}
3192 Serge 1858
EXPORT_SYMBOL(drm_fb_helper_hotplug_event);
1859
 
4560 Serge 1860
/* The Kconfig DRM_KMS_HELPER selects FRAMEBUFFER_CONSOLE (if !EXPERT)
1861
 * but the module doesn't depend on any fb console symbols.  At least
1862
 * attempt to load fbcon to avoid leaving the system without a usable console.
1863
 */
1864
#if defined(CONFIG_FRAMEBUFFER_CONSOLE_MODULE) && !defined(CONFIG_EXPERT)
1865
static int __init drm_fb_helper_modinit(void)
1866
{
1867
	const char *name = "fbcon";
1868
	struct module *fbcon;
3192 Serge 1869
 
4560 Serge 1870
	mutex_lock(&module_mutex);
1871
	fbcon = find_module(name);
1872
	mutex_unlock(&module_mutex);
3192 Serge 1873
 
4560 Serge 1874
	if (!fbcon)
1875
		request_module_nowait(name);
1876
	return 0;
1877
}
1878
 
1879
module_init(drm_fb_helper_modinit);
1880
#endif