intel_fbdev.c 15.1 KB
Newer Older
J
Jesse Barnes 已提交
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
/*
 * Copyright © 2007 David Airlie
 *
 * 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, sublicense,
 * 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 above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * 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 NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS 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.
 *
 * Authors:
 *     David Airlie
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/tty.h>
#include <linux/sysrq.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/init.h>
37
#include <linux/vga_switcheroo.h>
J
Jesse Barnes 已提交
38

39 40 41
#include <drm/drmP.h>
#include <drm/drm_crtc.h>
#include <drm/drm_fb_helper.h>
J
Jesse Barnes 已提交
42
#include "intel_drv.h"
43
#include <drm/i915_drm.h>
J
Jesse Barnes 已提交
44 45 46 47
#include "i915_drv.h"

static struct fb_ops intelfb_ops = {
	.owner = THIS_MODULE,
48 49
	.fb_check_var = drm_fb_helper_check_var,
	.fb_set_par = drm_fb_helper_set_par,
J
Jesse Barnes 已提交
50 51 52
	.fb_fillrect = cfb_fillrect,
	.fb_copyarea = cfb_copyarea,
	.fb_imageblit = cfb_imageblit,
53 54
	.fb_pan_display = drm_fb_helper_pan_display,
	.fb_blank = drm_fb_helper_blank,
55
	.fb_setcmap = drm_fb_helper_setcmap,
J
Jesse Barnes 已提交
56 57
	.fb_debug_enter = drm_fb_helper_debug_enter,
	.fb_debug_leave = drm_fb_helper_debug_leave,
J
Jesse Barnes 已提交
58 59
};

60 61
static int intelfb_alloc(struct drm_fb_helper *helper,
			 struct drm_fb_helper_surface_size *sizes)
J
Jesse Barnes 已提交
62
{
63 64
	struct intel_fbdev *ifbdev =
		container_of(helper, struct intel_fbdev, helper);
65
	struct drm_framebuffer *fb;
66
	struct drm_device *dev = helper->dev;
67
	struct drm_mode_fb_cmd2 mode_cmd = {};
68
	struct drm_i915_gem_object *obj;
69
	int size, ret;
J
Jesse Barnes 已提交
70

71
	/* we don't do packed 24bpp */
72 73
	if (sizes->surface_bpp == 24)
		sizes->surface_bpp = 32;
74

75 76
	mode_cmd.width = sizes->surface_width;
	mode_cmd.height = sizes->surface_height;
J
Jesse Barnes 已提交
77

78 79
	mode_cmd.pitches[0] = ALIGN(mode_cmd.width *
				    DIV_ROUND_UP(sizes->surface_bpp, 8), 64);
80 81
	mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
							  sizes->surface_depth);
J
Jesse Barnes 已提交
82

83
	size = mode_cmd.pitches[0] * mode_cmd.height;
J
Jesse Barnes 已提交
84
	size = ALIGN(size, PAGE_SIZE);
85 86 87
	obj = i915_gem_object_create_stolen(dev, size);
	if (obj == NULL)
		obj = i915_gem_alloc_object(dev, size);
88
	if (!obj) {
89
		DRM_ERROR("failed to allocate framebuffer\n");
J
Jesse Barnes 已提交
90 91 92 93
		ret = -ENOMEM;
		goto out;
	}

94
	/* Flush everything out, we'll be doing GTT only from now on */
95
	ret = intel_pin_and_fence_fb_obj(dev, obj, NULL);
J
Jesse Barnes 已提交
96
	if (ret) {
97
		DRM_ERROR("failed to pin obj: %d\n", ret);
J
Jesse Barnes 已提交
98 99 100
		goto out_unref;
	}

101 102 103
	fb = __intel_framebuffer_create(dev, &mode_cmd, obj);
	if (IS_ERR(fb)) {
		ret = PTR_ERR(fb);
104
		goto out_unpin;
105 106 107
	}

	ifbdev->fb = to_intel_framebuffer(fb);
108 109 110 111

	return 0;

out_unpin:
B
Ben Widawsky 已提交
112
	i915_gem_object_ggtt_unpin(obj);
113 114 115 116 117 118 119 120 121 122 123
out_unref:
	drm_gem_object_unreference(&obj->base);
out:
	return ret;
}

static int intelfb_create(struct drm_fb_helper *helper,
			  struct drm_fb_helper_surface_size *sizes)
{
	struct intel_fbdev *ifbdev =
		container_of(helper, struct intel_fbdev, helper);
124
	struct intel_framebuffer *intel_fb = ifbdev->fb;
125 126 127 128 129 130 131 132 133
	struct drm_device *dev = helper->dev;
	struct drm_i915_private *dev_priv = dev->dev_private;
	struct fb_info *info;
	struct drm_framebuffer *fb;
	struct drm_i915_gem_object *obj;
	int size, ret;

	mutex_lock(&dev->struct_mutex);

134
	if (!intel_fb || WARN_ON(!intel_fb->obj)) {
135
		DRM_DEBUG_KMS("no BIOS fb, allocating a new one\n");
136 137 138
		ret = intelfb_alloc(helper, sizes);
		if (ret)
			goto out_unlock;
139
		intel_fb = ifbdev->fb;
140
	} else {
141
		DRM_DEBUG_KMS("re-using BIOS fb\n");
142 143 144 145 146 147 148 149
		sizes->fb_width = intel_fb->base.width;
		sizes->fb_height = intel_fb->base.height;
	}

	obj = intel_fb->obj;
	size = obj->base.size;

	info = framebuffer_alloc(0, &dev->pdev->dev);
J
Jesse Barnes 已提交
150 151
	if (!info) {
		ret = -ENOMEM;
152
		goto out_unpin;
J
Jesse Barnes 已提交
153 154
	}

155
	info->par = helper;
J
Jesse Barnes 已提交
156

157
	fb = &ifbdev->fb->base;
J
Jesse Barnes 已提交
158

159 160
	ifbdev->helper.fb = fb;
	ifbdev->helper.fbdev = info;
161

J
Jesse Barnes 已提交
162 163
	strcpy(info->fix.id, "inteldrmfb");

164
	info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
J
Jesse Barnes 已提交
165 166
	info->fbops = &intelfb_ops;

167 168 169 170 171
	ret = fb_alloc_cmap(&info->cmap, 256, 0);
	if (ret) {
		ret = -ENOMEM;
		goto out_unpin;
	}
172
	/* setup aperture base/size for vesafb takeover */
173 174 175 176 177 178
	info->apertures = alloc_apertures(1);
	if (!info->apertures) {
		ret = -ENOMEM;
		goto out_unpin;
	}
	info->apertures->ranges[0].base = dev->mode_config.fb_base;
179
	info->apertures->ranges[0].size = dev_priv->gtt.mappable_end;
180

181
	info->fix.smem_start = dev->mode_config.fb_base + i915_gem_obj_ggtt_offset(obj);
J
Jesse Barnes 已提交
182 183
	info->fix.smem_len = size;

184
	info->screen_base =
185
		ioremap_wc(dev_priv->gtt.mappable_base + i915_gem_obj_ggtt_offset(obj),
186
			   size);
J
Jesse Barnes 已提交
187 188
	if (!info->screen_base) {
		ret = -ENOSPC;
189
		goto out_unpin;
J
Jesse Barnes 已提交
190 191 192
	}
	info->screen_size = size;

193 194 195
	/* This driver doesn't need a VT switch to restore the mode on resume */
	info->skip_vt_switch = true;

196
	drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
197
	drm_fb_helper_fill_var(info, &ifbdev->helper, sizes->fb_width, sizes->fb_height);
J
Jesse Barnes 已提交
198

199 200 201 202
	/* If the object is shmemfs backed, it will have given us zeroed pages.
	 * If the object is stolen however, it will be full of whatever
	 * garbage was left in there.
	 */
203
	if (ifbdev->fb->obj->stolen)
204 205
		memset_io(info->screen_base, 0, info->screen_size);

206
	/* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */
J
Jesse Barnes 已提交
207

208
	DRM_DEBUG_KMS("allocated %dx%d fb: 0x%08lx, bo %p\n",
209
		      fb->width, fb->height,
210
		      i915_gem_obj_ggtt_offset(obj), obj);
J
Jesse Barnes 已提交
211 212

	mutex_unlock(&dev->struct_mutex);
213
	vga_switcheroo_client_fb_set(dev->pdev, info);
J
Jesse Barnes 已提交
214 215
	return 0;

216
out_unpin:
B
Ben Widawsky 已提交
217
	i915_gem_object_ggtt_unpin(obj);
218
	drm_gem_object_unreference(&obj->base);
219
out_unlock:
J
Jesse Barnes 已提交
220 221 222 223
	mutex_unlock(&dev->struct_mutex);
	return ret;
}

224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
/** Sets the color ramps on behalf of RandR */
static void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
				    u16 blue, int regno)
{
	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);

	intel_crtc->lut_r[regno] = red >> 8;
	intel_crtc->lut_g[regno] = green >> 8;
	intel_crtc->lut_b[regno] = blue >> 8;
}

static void intel_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
				    u16 *blue, int regno)
{
	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);

	*red = intel_crtc->lut_r[regno] << 8;
	*green = intel_crtc->lut_g[regno] << 8;
	*blue = intel_crtc->lut_b[regno] << 8;
}

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
static struct drm_fb_helper_crtc *
intel_fb_helper_crtc(struct drm_fb_helper *fb_helper, struct drm_crtc *crtc)
{
	int i;

	for (i = 0; i < fb_helper->crtc_count; i++)
		if (fb_helper->crtc_info[i].mode_set.crtc == crtc)
			return &fb_helper->crtc_info[i];

	return NULL;
}

/*
 * Try to read the BIOS display configuration and use it for the initial
 * fb configuration.
 *
 * The BIOS or boot loader will generally create an initial display
 * configuration for us that includes some set of active pipes and displays.
 * This routine tries to figure out which pipes and connectors are active
 * and stuffs them into the crtcs and modes array given to us by the
 * drm_fb_helper code.
 *
 * The overall sequence is:
 *   intel_fbdev_init - from driver load
 *     intel_fbdev_init_bios - initialize the intel_fbdev using BIOS data
 *     drm_fb_helper_init - build fb helper structs
 *     drm_fb_helper_single_add_all_connectors - more fb helper structs
 *   intel_fbdev_initial_config - apply the config
 *     drm_fb_helper_initial_config - call ->probe then register_framebuffer()
 *         drm_setup_crtcs - build crtc config for fbdev
 *           intel_fb_initial_config - find active connectors etc
 *         drm_fb_helper_single_fb_probe - set up fbdev
 *           intelfb_create - re-use or alloc fb, build out fbdev structs
 *
 * Note that we don't make special consideration whether we could actually
 * switch to the selected modes without a full modeset. E.g. when the display
 * is in VGA mode we need to recalculate watermarks and set a new high-res
 * framebuffer anyway.
 */
static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
				    struct drm_fb_helper_crtc **crtcs,
				    struct drm_display_mode **modes,
				    bool *enabled, int width, int height)
{
289
	struct drm_device *dev = fb_helper->dev;
290
	int i, j;
291
	bool *save_enabled;
292
	bool fallback = true;
293 294
	int num_connectors_enabled = 0;
	int num_connectors_detected = 0;
295

296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
	/*
	 * If the user specified any force options, just bail here
	 * and use that config.
	 */
	for (i = 0; i < fb_helper->connector_count; i++) {
		struct drm_fb_helper_connector *fb_conn;
		struct drm_connector *connector;

		fb_conn = fb_helper->connector_info[i];
		connector = fb_conn->connector;

		if (!enabled[i])
			continue;

		if (connector->force != DRM_FORCE_UNSPECIFIED)
			return false;
	}

314 315 316 317 318 319
	save_enabled = kcalloc(dev->mode_config.num_connector, sizeof(bool),
			       GFP_KERNEL);
	if (!save_enabled)
		return false;

	memcpy(save_enabled, enabled, dev->mode_config.num_connector);
320 321 322 323 324 325 326 327 328

	for (i = 0; i < fb_helper->connector_count; i++) {
		struct drm_fb_helper_connector *fb_conn;
		struct drm_connector *connector;
		struct drm_encoder *encoder;
		struct drm_fb_helper_crtc *new_crtc;

		fb_conn = fb_helper->connector_info[i];
		connector = fb_conn->connector;
329 330 331 332

		if (connector->status == connector_status_connected)
			num_connectors_detected++;

333 334 335 336 337 338 339 340 341 342 343 344 345 346
		if (!enabled[i]) {
			DRM_DEBUG_KMS("connector %d not enabled, skipping\n",
				      connector->base.id);
			continue;
		}

		encoder = connector->encoder;
		if (!encoder || WARN_ON(!encoder->crtc)) {
			DRM_DEBUG_KMS("connector %d has no encoder or crtc, skipping\n",
				      connector->base.id);
			enabled[i] = false;
			continue;
		}

347 348
		num_connectors_enabled++;

349 350 351 352 353 354 355 356
		new_crtc = intel_fb_helper_crtc(fb_helper, encoder->crtc);

		/*
		 * Make sure we're not trying to drive multiple connectors
		 * with a single CRTC, since our cloning support may not
		 * match the BIOS.
		 */
		for (j = 0; j < fb_helper->connector_count; j++) {
357
			if (crtcs[j] == new_crtc) {
358
				DRM_DEBUG_KMS("fallback: cloned configuration\n");
359
				fallback = true;
360 361
				goto out;
			}
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
		}

		DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
			      fb_conn->connector->base.id);

		/* go for command line mode first */
		modes[i] = drm_pick_cmdline_mode(fb_conn, width, height);

		/* try for preferred next */
		if (!modes[i]) {
			DRM_DEBUG_KMS("looking for preferred mode on connector %d\n",
				      fb_conn->connector->base.id);
			modes[i] = drm_has_preferred_mode(fb_conn, width,
							  height);
		}

		/* last resort: use current mode */
		if (!modes[i]) {
			/*
			 * IMPORTANT: We want to use the adjusted mode (i.e.
			 * after the panel fitter upscaling) as the initial
			 * config, not the input mode, which is what crtc->mode
			 * usually contains. But since our current fastboot
			 * code puts a mode derived from the post-pfit timings
			 * into crtc->mode this works out correctly. We don't
			 * use hwmode anywhere right now, so use it for this
			 * since the fb helper layer wants a pointer to
			 * something we own.
			 */
			intel_mode_from_pipe_config(&encoder->crtc->hwmode,
						    &to_intel_crtc(encoder->crtc)->config);
			modes[i] = &encoder->crtc->hwmode;
		}
		crtcs[i] = new_crtc;

		DRM_DEBUG_KMS("connector %s on crtc %d: %s\n",
			      drm_get_connector_name(connector),
			      encoder->crtc->base.id,
			      modes[i]->name);
401

402
		fallback = false;
403 404
	}

405 406 407 408 409 410 411 412 413 414 415 416 417
	/*
	 * If the BIOS didn't enable everything it could, fall back to have the
	 * same user experiencing of lighting up as much as possible like the
	 * fbdev helper library.
	 */
	if (num_connectors_enabled != num_connectors_detected &&
	    num_connectors_enabled < INTEL_INFO(dev)->num_pipes) {
		DRM_DEBUG_KMS("fallback: Not all outputs enabled\n");
		DRM_DEBUG_KMS("Enabled: %i, detected: %i\n", num_connectors_enabled,
			      num_connectors_detected);
		fallback = true;
	}

418
out:
419
	if (fallback) {
420
		DRM_DEBUG_KMS("Not using firmware configuration\n");
421 422 423
		memcpy(enabled, save_enabled, dev->mode_config.num_connector);
		kfree(save_enabled);
		return false;
424 425
	}

426
	kfree(save_enabled);
427 428 429
	return true;
}

430
static struct drm_fb_helper_funcs intel_fb_helper_funcs = {
431
	.initial_config = intel_fb_initial_config,
432 433
	.gamma_set = intel_crtc_fb_gamma_set,
	.gamma_get = intel_crtc_fb_gamma_get,
434
	.fb_probe = intelfb_create,
435
};
J
Jesse Barnes 已提交
436

437 438
static void intel_fbdev_destroy(struct drm_device *dev,
				struct intel_fbdev *ifbdev)
J
Jesse Barnes 已提交
439
{
440
	if (ifbdev->helper.fbdev) {
441 442
		struct fb_info *info = ifbdev->helper.fbdev;

J
Jesse Barnes 已提交
443 444
		unregister_framebuffer(info);
		iounmap(info->screen_base);
445 446
		if (info->cmap.len)
			fb_dealloc_cmap(&info->cmap);
447

J
Jesse Barnes 已提交
448 449 450
		framebuffer_release(info);
	}

451
	drm_fb_helper_fini(&ifbdev->helper);
J
Jesse Barnes 已提交
452

453
	drm_framebuffer_unregister_private(&ifbdev->fb->base);
454
	drm_framebuffer_remove(&ifbdev->fb->base);
J
Jesse Barnes 已提交
455
}
456 457 458

int intel_fbdev_init(struct drm_device *dev)
{
459
	struct intel_fbdev *ifbdev;
460
	struct drm_i915_private *dev_priv = dev->dev_private;
461
	int ret;
462

463
	ifbdev = kzalloc(sizeof(*ifbdev), GFP_KERNEL);
464 465 466 467
	if (!ifbdev)
		return -ENOMEM;

	dev_priv->fbdev = ifbdev;
468 469
	ifbdev->helper.funcs = &intel_fb_helper_funcs;

470
	ret = drm_fb_helper_init(dev, &ifbdev->helper,
471
				 INTEL_INFO(dev)->num_pipes,
D
Daniel Vetter 已提交
472
				 4);
473 474 475 476
	if (ret) {
		kfree(ifbdev);
		return ret;
	}
477

478
	drm_fb_helper_single_add_all_connectors(&ifbdev->helper);
479

J
Jesse Barnes 已提交
480 481
	return 0;
}
482

483 484
void intel_fbdev_initial_config(struct drm_device *dev)
{
485
	struct drm_i915_private *dev_priv = dev->dev_private;
486 487 488 489 490

	/* Due to peculiar init order wrt to hpd handling this is separate. */
	drm_fb_helper_initial_config(&dev_priv->fbdev->helper, 32);
}

491 492
void intel_fbdev_fini(struct drm_device *dev)
{
493
	struct drm_i915_private *dev_priv = dev->dev_private;
494 495 496
	if (!dev_priv->fbdev)
		return;

497
	intel_fbdev_destroy(dev, dev_priv->fbdev);
498
	kfree(dev_priv->fbdev);
499 500
	dev_priv->fbdev = NULL;
}
501 502 503

void intel_fbdev_set_suspend(struct drm_device *dev, int state)
{
504
	struct drm_i915_private *dev_priv = dev->dev_private;
505 506 507 508
	struct intel_fbdev *ifbdev = dev_priv->fbdev;
	struct fb_info *info;

	if (!ifbdev)
509 510
		return;

511 512 513 514 515 516
	info = ifbdev->helper.fbdev;

	/* On resume from hibernation: If the object is shmemfs backed, it has
	 * been restored from swap. If the object is stolen however, it will be
	 * full of whatever garbage was left in there.
	 */
517
	if (state == FBINFO_STATE_RUNNING && ifbdev->fb->obj->stolen)
518 519 520
		memset_io(info->screen_base, 0, info->screen_size);

	fb_set_suspend(info, state);
521 522
}

523
void intel_fbdev_output_poll_changed(struct drm_device *dev)
524
{
525
	struct drm_i915_private *dev_priv = dev->dev_private;
526 527
	drm_fb_helper_hotplug_event(&dev_priv->fbdev->helper);
}
528

529
void intel_fbdev_restore_mode(struct drm_device *dev)
530 531
{
	int ret;
532
	struct drm_i915_private *dev_priv = dev->dev_private;
533

B
Ben Widawsky 已提交
534 535 536
	if (INTEL_INFO(dev)->num_pipes == 0)
		return;

537
	drm_modeset_lock_all(dev);
538

539 540 541
	ret = drm_fb_helper_restore_fbdev_mode(&dev_priv->fbdev->helper);
	if (ret)
		DRM_DEBUG("failed to restore crtc mode\n");
542

543
	drm_modeset_unlock_all(dev);
544
}