radeon_fb.c 10.2 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
/*
 * 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>
27
#include <linux/slab.h>
28 29 30 31 32 33 34 35 36
#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"

37 38
#include "drm_fb_helper.h"

39 40
#include <linux/vga_switcheroo.h>

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

static struct fb_ops radeonfb_ops = {
	.owner = THIS_MODULE,
54
	.fb_check_var = drm_fb_helper_check_var,
55
	.fb_set_par = drm_fb_helper_set_par,
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
	.fb_debug_enter = drm_fb_helper_debug_enter,
	.fb_debug_leave = drm_fb_helper_debug_leave,
64 65 66
};


67
static int radeon_align_pitch(struct radeon_device *rdev, int width, int bpp, bool tiled)
68 69
{
	int aligned = width;
70
	int align_large = (ASIC_IS_AVIVO(rdev)) || tiled;
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
	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;
}

91
static void radeonfb_destroy_pinned_object(struct drm_gem_object *gobj)
92
{
93 94 95 96 97 98
	struct radeon_bo *rbo = gobj->driver_private;
	int ret;

	ret = radeon_bo_reserve(rbo, false);
	if (likely(ret == 0)) {
		radeon_bo_kunmap(rbo);
99
		radeon_bo_unpin(rbo);
100 101 102 103
		radeon_bo_unreserve(rbo);
	}
	drm_gem_object_unreference_unlocked(gobj);
}
104

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

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

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

133
	if (fb_tiled)
134 135 136
		tiling_flags = RADEON_TILING_MACRO;

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

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

156

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

173 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
	*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;
203

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

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

214
	info->par = rfbdev;
215

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

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

	/* setup helper */
	rfbdev->helper.fb = fb;
	rfbdev->helper.fbdev = info;

224
	memset_io(rbo->kptr, 0x0, radeon_bo_size(rbo));
225

226
	strcpy(info->fix.id, "radeondrmfb");
227

228
	drm_fb_helper_fill_fix(info, fb->pitch, fb->depth);
229

230
	info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
231
	info->fbops = &radeonfb_ops;
232

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

239
	drm_fb_helper_fill_var(info, &rfbdev->helper, sizes->fb_width, sizes->fb_height);
240 241

	/* setup aperture base/size for vesafb takeover */
242 243 244 245 246 247 248
	info->apertures = alloc_apertures(1);
	if (!info->apertures) {
		ret = -ENOMEM;
		goto out_unref;
	}
	info->apertures->ranges[0].base = rdev->ddev->mode_config.fb_base;
	info->apertures->ranges[0].size = rdev->mc.real_vram_size;
249

250 251
	info->fix.mmio_start = 0;
	info->fix.mmio_len = 0;
252 253 254 255 256
	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;
257

258 259 260 261
	if (info->screen_base == NULL) {
		ret = -ENOSPC;
		goto out_unref;
	}
262 263 264 265 266 267 268

	ret = fb_alloc_cmap(&info->cmap, 256, 0);
	if (ret) {
		ret = -ENOMEM;
		goto out_unref;
	}

269 270
	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);
271
	DRM_INFO("size %lu\n", (unsigned long)radeon_bo_size(rbo));
272 273 274
	DRM_INFO("fb depth is %d\n", fb->depth);
	DRM_INFO("   pitch is %d\n", fb->pitch);

275
	vga_switcheroo_client_fb_set(rdev->ddev->pdev, info);
276 277 278
	return 0;

out_unref:
279
	if (rbo) {
280

281
	}
282
	if (fb && ret) {
283 284 285 286 287 288 289
		drm_gem_object_unreference(gobj);
		drm_framebuffer_cleanup(fb);
		kfree(fb);
	}
	return ret;
}

290 291
static int radeon_fb_find_or_create_single(struct drm_fb_helper *helper,
					   struct drm_fb_helper_surface_size *sizes)
292
{
293
	struct radeon_fbdev *rfbdev = (struct radeon_fbdev *)helper;
294 295 296
	int new_fb = 0;
	int ret;

297 298
	if (!helper->fb) {
		ret = radeonfb_create(rfbdev, sizes);
299 300 301 302 303 304 305
		if (ret)
			return ret;
		new_fb = 1;
	}
	return new_fb;
}

306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
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;
}

322
void radeon_fb_output_poll_changed(struct radeon_device *rdev)
323
{
324
	drm_fb_helper_hotplug_event(&rdev->mode_info.rfbdev->helper);
325 326
}

327
static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfbdev)
328 329
{
	struct fb_info *info;
330
	struct radeon_framebuffer *rfb = &rfbdev->rfb;
331

332 333
	if (rfbdev->helper.fbdev) {
		info = rfbdev->helper.fbdev;
334

335
		unregister_framebuffer(info);
336 337
		if (info->cmap.len)
			fb_dealloc_cmap(&info->cmap);
338
		framebuffer_release(info);
339 340
	}

341
	if (rfb->obj) {
342 343
		radeonfb_destroy_pinned_object(rfb->obj);
		rfb->obj = NULL;
344
	}
345
	drm_fb_helper_fini(&rfbdev->helper);
346
	drm_framebuffer_cleanup(&rfb->base);
347 348 349

	return 0;
}
350

351 352 353 354 355
static struct drm_fb_helper_funcs radeon_fb_helper_funcs = {
	.gamma_set = radeon_crtc_fb_gamma_set,
	.gamma_get = radeon_crtc_fb_gamma_get,
	.fb_probe = radeon_fb_find_or_create_single,
};
356 357 358

int radeon_fbdev_init(struct radeon_device *rdev)
{
359
	struct radeon_fbdev *rfbdev;
360
	int bpp_sel = 32;
361
	int ret;
362 363 364 365

	/* 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;
366 367 368 369 370 371 372

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

	rfbdev->rdev = rdev;
	rdev->mode_info.rfbdev = rfbdev;
373
	rfbdev->helper.funcs = &radeon_fb_helper_funcs;
374

375 376 377 378 379 380 381 382
	ret = drm_fb_helper_init(rdev->ddev, &rfbdev->helper,
				 rdev->num_crtc,
				 RADEONFB_CONN_LIMIT);
	if (ret) {
		kfree(rfbdev);
		return ret;
	}

383
	drm_fb_helper_single_add_all_connectors(&rfbdev->helper);
384
	drm_fb_helper_initial_config(&rfbdev->helper, bpp_sel);
385
	return 0;
386 387 388 389
}

void radeon_fbdev_fini(struct radeon_device *rdev)
{
390 391 392
	if (!rdev->mode_info.rfbdev)
		return;

393
	radeon_fbdev_destroy(rdev->ddev, rdev->mode_info.rfbdev);
394
	kfree(rdev->mode_info.rfbdev);
395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417
	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;
418
}