intel_fbdev.c 9.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 65
	struct intel_fbdev *ifbdev =
		container_of(helper, struct intel_fbdev, helper);
	struct drm_device *dev = helper->dev;
66
	struct drm_mode_fb_cmd2 mode_cmd = {};
67
	struct drm_i915_gem_object *obj;
68
	int size, ret;
J
Jesse Barnes 已提交
69

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

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

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

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

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

100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
	ret = intel_framebuffer_init(dev, &ifbdev->ifb, &mode_cmd, obj);
	if (ret)
		goto out_unpin;

	return 0;

out_unpin:
	i915_gem_object_unpin(obj);
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);
	struct intel_framebuffer *intel_fb = &ifbdev->ifb;
	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);

	if (!intel_fb->obj) {
		DRM_ERROR("no BIOS fb, allocating a new one\n");
		ret = intelfb_alloc(helper, sizes);
		if (ret)
			goto out_unlock;
	} else {
		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 已提交
143 144
	if (!info) {
		ret = -ENOMEM;
145
		goto out_unpin;
J
Jesse Barnes 已提交
146 147
	}

148
	info->par = helper;
J
Jesse Barnes 已提交
149

150
	fb = &ifbdev->ifb.base;
J
Jesse Barnes 已提交
151

152 153
	ifbdev->helper.fb = fb;
	ifbdev->helper.fbdev = info;
154

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

157
	info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
J
Jesse Barnes 已提交
158 159
	info->fbops = &intelfb_ops;

160 161 162 163 164
	ret = fb_alloc_cmap(&info->cmap, 256, 0);
	if (ret) {
		ret = -ENOMEM;
		goto out_unpin;
	}
165
	/* setup aperture base/size for vesafb takeover */
166 167 168 169 170 171
	info->apertures = alloc_apertures(1);
	if (!info->apertures) {
		ret = -ENOMEM;
		goto out_unpin;
	}
	info->apertures->ranges[0].base = dev->mode_config.fb_base;
172
	info->apertures->ranges[0].size = dev_priv->gtt.mappable_end;
173

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

177
	info->screen_base =
178
		ioremap_wc(dev_priv->gtt.mappable_base + i915_gem_obj_ggtt_offset(obj),
179
			   size);
J
Jesse Barnes 已提交
180 181
	if (!info->screen_base) {
		ret = -ENOSPC;
182
		goto out_unpin;
J
Jesse Barnes 已提交
183 184 185
	}
	info->screen_size = size;

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

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

192 193 194 195 196 197 198
	/* 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.
	 */
	if (ifbdev->ifb.obj->stolen)
		memset_io(info->screen_base, 0, info->screen_size);

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

201
	DRM_DEBUG_KMS("allocated %dx%d fb: 0x%08lx, bo %p\n",
202
		      fb->width, fb->height,
203
		      i915_gem_obj_ggtt_offset(obj), obj);
J
Jesse Barnes 已提交
204 205

	mutex_unlock(&dev->struct_mutex);
206
	vga_switcheroo_client_fb_set(dev->pdev, info);
J
Jesse Barnes 已提交
207 208
	return 0;

209
out_unpin:
210 211
	i915_gem_object_unpin(obj);
	drm_gem_object_unreference(&obj->base);
212
out_unlock:
J
Jesse Barnes 已提交
213 214 215 216
	mutex_unlock(&dev->struct_mutex);
	return ret;
}

217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
/** 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;
}

238 239 240
static struct drm_fb_helper_funcs intel_fb_helper_funcs = {
	.gamma_set = intel_crtc_fb_gamma_set,
	.gamma_get = intel_crtc_fb_gamma_get,
241
	.fb_probe = intelfb_create,
242
};
J
Jesse Barnes 已提交
243

244 245
static void intel_fbdev_destroy(struct drm_device *dev,
				struct intel_fbdev *ifbdev)
J
Jesse Barnes 已提交
246
{
247
	if (ifbdev->helper.fbdev) {
248 249
		struct fb_info *info = ifbdev->helper.fbdev;

J
Jesse Barnes 已提交
250 251
		unregister_framebuffer(info);
		iounmap(info->screen_base);
252 253
		if (info->cmap.len)
			fb_dealloc_cmap(&info->cmap);
254

J
Jesse Barnes 已提交
255 256 257
		framebuffer_release(info);
	}

258
	drm_fb_helper_fini(&ifbdev->helper);
J
Jesse Barnes 已提交
259

260 261
	drm_framebuffer_unregister_private(&ifbdev->ifb.base);
	intel_framebuffer_fini(&ifbdev->ifb);
J
Jesse Barnes 已提交
262
}
263 264 265

int intel_fbdev_init(struct drm_device *dev)
{
266
	struct intel_fbdev *ifbdev;
267
	struct drm_i915_private *dev_priv = dev->dev_private;
268
	int ret;
269

270
	ifbdev = kzalloc(sizeof(*ifbdev), GFP_KERNEL);
271 272 273 274
	if (!ifbdev)
		return -ENOMEM;

	dev_priv->fbdev = ifbdev;
275 276
	ifbdev->helper.funcs = &intel_fb_helper_funcs;

277
	ret = drm_fb_helper_init(dev, &ifbdev->helper,
278
				 INTEL_INFO(dev)->num_pipes,
D
Daniel Vetter 已提交
279
				 4);
280 281 282 283
	if (ret) {
		kfree(ifbdev);
		return ret;
	}
284

285
	drm_fb_helper_single_add_all_connectors(&ifbdev->helper);
286

J
Jesse Barnes 已提交
287 288
	return 0;
}
289

290 291
void intel_fbdev_initial_config(struct drm_device *dev)
{
292
	struct drm_i915_private *dev_priv = dev->dev_private;
293 294 295 296 297

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

298 299
void intel_fbdev_fini(struct drm_device *dev)
{
300
	struct drm_i915_private *dev_priv = dev->dev_private;
301 302 303
	if (!dev_priv->fbdev)
		return;

304
	intel_fbdev_destroy(dev, dev_priv->fbdev);
305
	kfree(dev_priv->fbdev);
306 307
	dev_priv->fbdev = NULL;
}
308 309 310

void intel_fbdev_set_suspend(struct drm_device *dev, int state)
{
311
	struct drm_i915_private *dev_priv = dev->dev_private;
312 313 314 315
	struct intel_fbdev *ifbdev = dev_priv->fbdev;
	struct fb_info *info;

	if (!ifbdev)
316 317
		return;

318 319 320 321 322 323
	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.
	 */
324
	if (state == FBINFO_STATE_RUNNING && ifbdev->ifb.obj->stolen)
325 326 327
		memset_io(info->screen_base, 0, info->screen_size);

	fb_set_suspend(info, state);
328 329
}

J
Jesse Barnes 已提交
330
MODULE_LICENSE("GPL and additional rights");
331

332
void intel_fbdev_output_poll_changed(struct drm_device *dev)
333
{
334
	struct drm_i915_private *dev_priv = dev->dev_private;
335 336
	drm_fb_helper_hotplug_event(&dev_priv->fbdev->helper);
}
337

338
void intel_fbdev_restore_mode(struct drm_device *dev)
339 340
{
	int ret;
341
	struct drm_i915_private *dev_priv = dev->dev_private;
342

B
Ben Widawsky 已提交
343 344 345
	if (INTEL_INFO(dev)->num_pipes == 0)
		return;

346
	drm_modeset_lock_all(dev);
347

348 349 350
	ret = drm_fb_helper_restore_fbdev_mode(&dev_priv->fbdev->helper);
	if (ret)
		DRM_DEBUG("failed to restore crtc mode\n");
351

352
	drm_modeset_unlock_all(dev);
353
}