radeon_fb.c 10.7 KB
Newer Older
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
/*
 * 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/fb.h>

#include "drmP.h"
#include "drm.h"
#include "drm_crtc.h"
#include "drm_crtc_helper.h"
#include "radeon_drm.h"
#include "radeon.h"

36 37
#include "drm_fb_helper.h"

38 39
#include <linux/vga_switcheroo.h>

40 41 42 43
/* object hierarchy -
   this contains a helper + a radeon fb
   the helper contains a pointer to radeon framebuffer baseclass.
*/
44
struct radeon_fbdev {
45
	struct drm_fb_helper helper;
46 47 48
	struct radeon_framebuffer rfb;
	struct list_head fbdev_list;
	struct radeon_device *rdev;
49 50 51 52
};

static struct fb_ops radeonfb_ops = {
	.owner = THIS_MODULE,
53
	.fb_check_var = drm_fb_helper_check_var,
54 55
	.fb_set_par = drm_fb_helper_set_par,
	.fb_setcolreg = drm_fb_helper_setcolreg,
56 57 58
	.fb_fillrect = cfb_fillrect,
	.fb_copyarea = cfb_copyarea,
	.fb_imageblit = cfb_imageblit,
59 60
	.fb_pan_display = drm_fb_helper_pan_display,
	.fb_blank = drm_fb_helper_blank,
61
	.fb_setcmap = drm_fb_helper_setcmap,
62 63 64
};


65
static int radeon_align_pitch(struct radeon_device *rdev, int width, int bpp, bool tiled)
66 67
{
	int aligned = width;
68
	int align_large = (ASIC_IS_AVIVO(rdev)) || tiled;
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
	int pitch_mask = 0;

	switch (bpp / 8) {
	case 1:
		pitch_mask = align_large ? 255 : 127;
		break;
	case 2:
		pitch_mask = align_large ? 127 : 31;
		break;
	case 3:
	case 4:
		pitch_mask = align_large ? 63 : 15;
		break;
	}

	aligned += pitch_mask;
	aligned &= ~pitch_mask;
	return aligned;
}

89 90
static struct drm_fb_helper_funcs radeon_fb_helper_funcs = {
	.gamma_set = radeon_crtc_fb_gamma_set,
91
	.gamma_get = radeon_crtc_fb_gamma_get,
92 93
};

94
static void radeonfb_destroy_pinned_object(struct drm_gem_object *gobj)
95
{
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
	struct radeon_bo *rbo = gobj->driver_private;
	int ret;

	ret = radeon_bo_reserve(rbo, false);
	if (likely(ret == 0)) {
		radeon_bo_kunmap(rbo);
		radeon_bo_unreserve(rbo);
	}
	drm_gem_object_unreference_unlocked(gobj);
}

static int radeonfb_create_pinned_object(struct radeon_fbdev *rfbdev,
					 struct drm_mode_fb_cmd *mode_cmd,
					 struct drm_gem_object **gobj_p)
{
	struct radeon_device *rdev = rfbdev->rdev;
112
	struct drm_gem_object *gobj = NULL;
113
	struct radeon_bo *rbo = NULL;
114
	bool fb_tiled = false; /* useful for testing */
115
	u32 tiling_flags = 0;
116 117
	int ret;
	int aligned_size, size;
118 119

	/* need to align pitch with crtc limits */
120
	mode_cmd->pitch = radeon_align_pitch(rdev, mode_cmd->width, mode_cmd->bpp, fb_tiled) * ((mode_cmd->bpp + 1) / 8);
121

122
	size = mode_cmd->pitch * mode_cmd->height;
123 124
	aligned_size = ALIGN(size, PAGE_SIZE);
	ret = radeon_gem_object_create(rdev, aligned_size, 0,
125 126 127
				       RADEON_GEM_DOMAIN_VRAM,
				       false, ttm_bo_type_kernel,
				       &gobj);
128
	if (ret) {
129 130 131
		printk(KERN_ERR "failed to allocate framebuffer (%d)\n",
		       aligned_size);
		return -ENOMEM;
132
	}
133
	rbo = gobj->driver_private;
134

135
	if (fb_tiled)
136 137 138
		tiling_flags = RADEON_TILING_MACRO;

#ifdef __BIG_ENDIAN
139
	switch (mode_cmd->bpp) {
140 141 142 143 144 145 146 147 148 149
	case 32:
		tiling_flags |= RADEON_TILING_SWAP_32BIT;
		break;
	case 16:
		tiling_flags |= RADEON_TILING_SWAP_16BIT;
	default:
		break;
	}
#endif

150 151
	if (tiling_flags) {
		ret = radeon_bo_set_tiling_flags(rbo,
152 153
						 tiling_flags | RADEON_TILING_SURFACE,
						 mode_cmd->pitch);
154 155 156
		if (ret)
			dev_err(rdev->dev, "FB failed to set tiling flags\n");
	}
157

158

159 160 161
	ret = radeon_bo_reserve(rbo, false);
	if (unlikely(ret != 0))
		goto out_unref;
162
	ret = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, NULL);
163 164 165 166 167 168
	if (ret) {
		radeon_bo_unreserve(rbo);
		goto out_unref;
	}
	if (fb_tiled)
		radeon_bo_check_tiling(rbo, 0, 0);
169
	ret = radeon_bo_kmap(rbo, NULL);
170
	radeon_bo_unreserve(rbo);
171 172 173
	if (ret) {
		goto out_unref;
	}
174

175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
	*gobj_p = gobj;
	return 0;
out_unref:
	radeonfb_destroy_pinned_object(gobj);
	*gobj_p = NULL;
	return ret;
}

static int radeonfb_create(struct radeon_fbdev *rfbdev,
			   struct drm_fb_helper_surface_size *sizes)
{
	struct radeon_device *rdev = rfbdev->rdev;
	struct fb_info *info;
	struct drm_framebuffer *fb = NULL;
	struct drm_mode_fb_cmd mode_cmd;
	struct drm_gem_object *gobj = NULL;
	struct radeon_bo *rbo = NULL;
	struct device *device = &rdev->pdev->dev;
	int ret;
	unsigned long tmp;

	mode_cmd.width = sizes->surface_width;
	mode_cmd.height = sizes->surface_height;

	/* avivo can't scanout real 24bpp */
	if ((sizes->surface_bpp == 24) && ASIC_IS_AVIVO(rdev))
		sizes->surface_bpp = 32;

	mode_cmd.bpp = sizes->surface_bpp;
	mode_cmd.depth = sizes->surface_depth;

	ret = radeonfb_create_pinned_object(rfbdev, &mode_cmd, &gobj);
	rbo = gobj->driver_private;

	/* okay we have an object now allocate the framebuffer */
	info = framebuffer_alloc(0, device);
211 212 213 214
	if (info == NULL) {
		ret = -ENOMEM;
		goto out_unref;
	}
215

216 217 218 219
	info->par = rfbdev;

	radeon_framebuffer_init(rdev->ddev, &rfbdev->rfb, &mode_cmd, gobj);

220 221 222 223 224
	fb = &rfbdev->rfb.base;

	/* setup helper */
	rfbdev->helper.fb = fb;
	rfbdev->helper.fbdev = info;
225
	rfbdev->helper.funcs = &radeon_fb_helper_funcs;
226

227
	memset_io(rbo->kptr, 0x0, radeon_bo_size(rbo));
228

229
	strcpy(info->fix.id, "radeondrmfb");
230

231
	drm_fb_helper_fill_fix(info, fb->pitch, fb->depth);
232

233 234
	info->flags = FBINFO_DEFAULT;
	info->fbops = &radeonfb_ops;
235

236
	tmp = radeon_bo_gpu_offset(rbo) - rdev->mc.vram_start;
237
	info->fix.smem_start = rdev->mc.aper_base + tmp;
238 239 240
	info->fix.smem_len = radeon_bo_size(rbo);
	info->screen_base = rbo->kptr;
	info->screen_size = radeon_bo_size(rbo);
241

242
	drm_fb_helper_fill_var(info, &rfbdev->helper, sizes->fb_width, sizes->fb_height);
243 244 245 246 247

	/* setup aperture base/size for vesafb takeover */
	info->aperture_base = rdev->ddev->mode_config.fb_base;
	info->aperture_size = rdev->mc.real_vram_size;

248 249
	info->fix.mmio_start = 0;
	info->fix.mmio_len = 0;
250 251 252 253 254 255 256 257 258 259 260
	info->pixmap.size = 64*1024;
	info->pixmap.buf_align = 8;
	info->pixmap.access_align = 32;
	info->pixmap.flags = FB_PIXMAP_SYSTEM;
	info->pixmap.scan_align = 1;
	if (info->screen_base == NULL) {
		ret = -ENOSPC;
		goto out_unref;
	}
	DRM_INFO("fb mappable at 0x%lX\n",  info->fix.smem_start);
	DRM_INFO("vram apper at 0x%lX\n",  (unsigned long)rdev->mc.aper_base);
261
	DRM_INFO("size %lu\n", (unsigned long)radeon_bo_size(rbo));
262 263 264
	DRM_INFO("fb depth is %d\n", fb->depth);
	DRM_INFO("   pitch is %d\n", fb->pitch);

265
	vga_switcheroo_client_fb_set(rdev->ddev->pdev, info);
266 267 268
	return 0;

out_unref:
269
	if (rbo) {
270

271
	}
272
	if (fb && ret) {
273 274 275 276 277 278 279
		drm_gem_object_unreference(gobj);
		drm_framebuffer_cleanup(fb);
		kfree(fb);
	}
	return ret;
}

280 281
static int radeon_fb_find_or_create_single(struct drm_fb_helper *helper,
					   struct drm_fb_helper_surface_size *sizes)
282
{
283
	struct radeon_fbdev *rfbdev = (struct radeon_fbdev *)helper;
284 285 286
	int new_fb = 0;
	int ret;

287 288
	if (!helper->fb) {
		ret = radeonfb_create(rfbdev, sizes);
289 290 291 292 293 294 295
		if (ret)
			return ret;
		new_fb = 1;
	}
	return new_fb;
}

296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
static char *mode_option;
int radeon_parse_options(char *options)
{
	char *this_opt;

	if (!options || !*options)
		return 0;

	while ((this_opt = strsep(&options, ",")) != NULL) {
		if (!*this_opt)
			continue;
		mode_option = this_opt;
	}
	return 0;
}

312
static int radeonfb_probe(struct radeon_fbdev *rfbdev)
313
{
314
	struct radeon_device *rdev = rfbdev->rdev;
315 316 317 318 319 320
	int bpp_sel = 32;

	/* select 8 bpp console on RN50 or 16MB cards */
	if (ASIC_IS_RN50(rdev) || rdev->mc.real_vram_size <= (32*1024*1024))
		bpp_sel = 8;

321
	return drm_fb_helper_single_fb_probe(&rfbdev->helper, bpp_sel);
322 323
}

324
void radeonfb_hotplug(struct drm_device *dev, bool polled)
325
{
326 327 328 329 330
	struct radeon_device *rdev = dev->dev_private;
	int max_width, max_height;

	max_width = rdev->mode_info.rfbdev->rfb.base.width;
	max_height = rdev->mode_info.rfbdev->rfb.base.height;
331
	drm_helper_fb_hotplug_event(&rdev->mode_info.rfbdev->helper, max_width, max_height, polled);
332

333
	radeonfb_probe(rdev->mode_info.rfbdev);
334 335
}

336 337 338 339 340
static void radeon_fb_poll_changed(struct drm_fb_helper *fb_helper)
{
	radeonfb_hotplug(fb_helper->dev, true);
}

341
static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfbdev)
342 343
{
	struct fb_info *info;
344
	struct radeon_framebuffer *rfb = &rfbdev->rfb;
345 346
	struct radeon_bo *rbo;
	int r;
347

348 349 350 351
	if (rfbdev->helper.fbdev) {
		info = rfbdev->helper.fbdev;
		unregister_framebuffer(info);
		framebuffer_release(info);
352 353
	}

354 355 356 357 358 359 360 361 362 363
	if (rfb->obj) {
		rbo = rfb->obj->driver_private;
		r = radeon_bo_reserve(rbo, false);
		if (likely(r == 0)) {
			radeon_bo_kunmap(rbo);
			radeon_bo_unpin(rbo);
			radeon_bo_unreserve(rbo);
		}
		drm_gem_object_unreference_unlocked(rfb->obj);
	}
364 365
	drm_fb_helper_free(&rfbdev->helper);
	drm_framebuffer_cleanup(&rfb->base);
366

367 368 369
	return 0;
}
MODULE_LICENSE("GPL");
370 371 372

int radeon_fbdev_init(struct radeon_device *rdev)
{
373 374 375 376 377 378 379 380 381 382 383 384 385
	struct radeon_fbdev *rfbdev;

	rfbdev = kzalloc(sizeof(struct radeon_fbdev), GFP_KERNEL);
	if (!rfbdev)
		return -ENOMEM;

	rfbdev->rdev = rdev;
	rdev->mode_info.rfbdev = rfbdev;

	drm_fb_helper_init_crtc_count(rdev->ddev, &rfbdev->helper,
				      rdev->num_crtc,
				      RADEONFB_CONN_LIMIT);
	rfbdev->helper.fb_probe = radeon_fb_find_or_create_single;
386 387 388

	drm_fb_helper_single_add_all_connectors(&rfbdev->helper);

389 390 391
	rfbdev->helper.fb_poll_changed = radeon_fb_poll_changed;
	drm_fb_helper_poll_init(&rfbdev->helper);

392 393
	drm_fb_helper_initial_config(&rfbdev->helper);
	radeonfb_probe(rfbdev);
394

395
	return 0;
396

397 398 399 400
}

void radeon_fbdev_fini(struct radeon_device *rdev)
{
401 402 403
	if (!rdev->mode_info.rfbdev)
		return;

404
	drm_fb_helper_poll_fini(&rdev->mode_info.rfbdev->helper);
405
	radeon_fbdev_destroy(rdev->ddev, rdev->mode_info.rfbdev);
406
	kfree(rdev->mode_info.rfbdev);
407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
	rdev->mode_info.rfbdev = NULL;
}

void radeon_fbdev_set_suspend(struct radeon_device *rdev, int state)
{
	fb_set_suspend(rdev->mode_info.rfbdev->helper.fbdev, state);
}

int radeon_fbdev_total_size(struct radeon_device *rdev)
{
	struct radeon_bo *robj;
	int size = 0;

	robj = rdev->mode_info.rfbdev->rfb.obj->driver_private;
	size += radeon_bo_size(robj);
	return size;
}

bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj)
{
	if (robj == rdev->mode_info.rfbdev->rfb.obj->driver_private)
		return true;
	return false;
}