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 252 253 254
	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;
255

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

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

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

273
	vga_switcheroo_client_fb_set(rdev->ddev->pdev, info);
274 275 276
	return 0;

out_unref:
277
	if (rbo) {
278

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

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

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

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

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

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

330 331
	if (rfbdev->helper.fbdev) {
		info = rfbdev->helper.fbdev;
332

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

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

	return 0;
}
348

349 350 351 352 353
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,
};
354 355 356

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

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

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

	rfbdev->rdev = rdev;
	rdev->mode_info.rfbdev = rfbdev;
371
	rfbdev->helper.funcs = &radeon_fb_helper_funcs;
372

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

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

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

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