intel_fbdev.c 14.4 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 292 293
	bool *save_enabled;
	bool any_enabled = false;

294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
	/*
	 * 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;
	}

312 313 314 315 316 317
	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);
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

	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;
		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;
		}

		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++) {
349 350 351 352
			if (crtcs[j] == new_crtc) {
				any_enabled = false;
				goto out;
			}
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
		}

		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);
392 393 394 395 396 397 398 399 400

		any_enabled = true;
	}

out:
	if (!any_enabled) {
		memcpy(enabled, save_enabled, dev->mode_config.num_connector);
		kfree(save_enabled);
		return false;
401 402
	}

403
	kfree(save_enabled);
404 405 406
	return true;
}

407
static struct drm_fb_helper_funcs intel_fb_helper_funcs = {
408
	.initial_config = intel_fb_initial_config,
409 410
	.gamma_set = intel_crtc_fb_gamma_set,
	.gamma_get = intel_crtc_fb_gamma_get,
411
	.fb_probe = intelfb_create,
412
};
J
Jesse Barnes 已提交
413

414 415
static void intel_fbdev_destroy(struct drm_device *dev,
				struct intel_fbdev *ifbdev)
J
Jesse Barnes 已提交
416
{
417
	if (ifbdev->helper.fbdev) {
418 419
		struct fb_info *info = ifbdev->helper.fbdev;

J
Jesse Barnes 已提交
420 421
		unregister_framebuffer(info);
		iounmap(info->screen_base);
422 423
		if (info->cmap.len)
			fb_dealloc_cmap(&info->cmap);
424

J
Jesse Barnes 已提交
425 426 427
		framebuffer_release(info);
	}

428
	drm_fb_helper_fini(&ifbdev->helper);
J
Jesse Barnes 已提交
429

430
	drm_framebuffer_unregister_private(&ifbdev->fb->base);
431
	drm_framebuffer_remove(&ifbdev->fb->base);
J
Jesse Barnes 已提交
432
}
433 434 435

int intel_fbdev_init(struct drm_device *dev)
{
436
	struct intel_fbdev *ifbdev;
437
	struct drm_i915_private *dev_priv = dev->dev_private;
438
	int ret;
439

440
	ifbdev = kzalloc(sizeof(*ifbdev), GFP_KERNEL);
441 442 443 444
	if (!ifbdev)
		return -ENOMEM;

	dev_priv->fbdev = ifbdev;
445 446
	ifbdev->helper.funcs = &intel_fb_helper_funcs;

447
	ret = drm_fb_helper_init(dev, &ifbdev->helper,
448
				 INTEL_INFO(dev)->num_pipes,
D
Daniel Vetter 已提交
449
				 4);
450 451 452 453
	if (ret) {
		kfree(ifbdev);
		return ret;
	}
454

455
	drm_fb_helper_single_add_all_connectors(&ifbdev->helper);
456

J
Jesse Barnes 已提交
457 458
	return 0;
}
459

460 461
void intel_fbdev_initial_config(struct drm_device *dev)
{
462
	struct drm_i915_private *dev_priv = dev->dev_private;
463 464 465 466 467

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

468 469
void intel_fbdev_fini(struct drm_device *dev)
{
470
	struct drm_i915_private *dev_priv = dev->dev_private;
471 472 473
	if (!dev_priv->fbdev)
		return;

474
	intel_fbdev_destroy(dev, dev_priv->fbdev);
475
	kfree(dev_priv->fbdev);
476 477
	dev_priv->fbdev = NULL;
}
478 479 480

void intel_fbdev_set_suspend(struct drm_device *dev, int state)
{
481
	struct drm_i915_private *dev_priv = dev->dev_private;
482 483 484 485
	struct intel_fbdev *ifbdev = dev_priv->fbdev;
	struct fb_info *info;

	if (!ifbdev)
486 487
		return;

488 489 490 491 492 493
	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.
	 */
494
	if (state == FBINFO_STATE_RUNNING && ifbdev->fb->obj->stolen)
495 496 497
		memset_io(info->screen_base, 0, info->screen_size);

	fb_set_suspend(info, state);
498 499
}

500
void intel_fbdev_output_poll_changed(struct drm_device *dev)
501
{
502
	struct drm_i915_private *dev_priv = dev->dev_private;
503 504
	drm_fb_helper_hotplug_event(&dev_priv->fbdev->helper);
}
505

506
void intel_fbdev_restore_mode(struct drm_device *dev)
507 508
{
	int ret;
509
	struct drm_i915_private *dev_priv = dev->dev_private;
510

B
Ben Widawsky 已提交
511 512 513
	if (INTEL_INFO(dev)->num_pipes == 0)
		return;

514
	drm_modeset_lock_all(dev);
515

516 517 518
	ret = drm_fb_helper_restore_fbdev_mode(&dev_priv->fbdev->helper);
	if (ret)
		DRM_DEBUG("failed to restore crtc mode\n");
519

520
	drm_modeset_unlock_all(dev);
521
}