radeon_fb.c 9.5 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 36 37 38 39
/*
 * 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
 */
    /*
     *  Modularization
     */

#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"

40 41
#include "drm_fb_helper.h"

42 43
#include <linux/vga_switcheroo.h>

44
struct radeon_fb_device {
45
	struct drm_fb_helper helper;
46
	struct radeon_framebuffer	*rfb;
47
	struct radeon_device		*rdev;
48 49 50 51
};

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

/**
64
 * Currently it is assumed that the old framebuffer is reused.
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
 *
 * LOCKING
 * caller should hold the mode config lock.
 *
 */
int radeonfb_resize(struct drm_device *dev, struct drm_crtc *crtc)
{
	struct fb_info *info;
	struct drm_framebuffer *fb;
	struct drm_display_mode *mode = crtc->desired_mode;

	fb = crtc->fb;
	if (fb == NULL) {
		return 1;
	}
	info = fb->fbdev;
	if (info == NULL) {
		return 1;
	}
	if (mode == NULL) {
		return 1;
	}
	info->var.xres = mode->hdisplay;
	info->var.right_margin = mode->hsync_start - mode->hdisplay;
	info->var.hsync_len = mode->hsync_end - mode->hsync_start;
	info->var.left_margin = mode->htotal - mode->hsync_end;
	info->var.yres = mode->vdisplay;
	info->var.lower_margin = mode->vsync_start - mode->vdisplay;
	info->var.vsync_len = mode->vsync_end - mode->vsync_start;
	info->var.upper_margin = mode->vtotal - mode->vsync_end;
	info->var.pixclock = 10000000 / mode->htotal * 1000 / mode->vtotal * 100;
	/* avoid overflow */
	info->var.pixclock = info->var.pixclock * 1000 / mode->vrefresh;

	return 0;
}
EXPORT_SYMBOL(radeonfb_resize);

103
static int radeon_align_pitch(struct radeon_device *rdev, int width, int bpp, bool tiled)
104 105
{
	int aligned = width;
106
	int align_large = (ASIC_IS_AVIVO(rdev)) || tiled;
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
	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;
}

127 128
static struct drm_fb_helper_funcs radeon_fb_helper_funcs = {
	.gamma_set = radeon_crtc_fb_gamma_set,
129
	.gamma_get = radeon_crtc_fb_gamma_get,
130 131 132
};

int radeonfb_create(struct drm_device *dev,
133 134
		    uint32_t fb_width, uint32_t fb_height,
		    uint32_t surface_width, uint32_t surface_height,
135
		    uint32_t surface_depth, uint32_t surface_bpp,
136
		    struct drm_framebuffer **fb_p)
137
{
138
	struct radeon_device *rdev = dev->dev_private;
139 140
	struct fb_info *info;
	struct radeon_fb_device *rfbdev;
141
	struct drm_framebuffer *fb = NULL;
142 143 144
	struct radeon_framebuffer *rfb;
	struct drm_mode_fb_cmd mode_cmd;
	struct drm_gem_object *gobj = NULL;
145
	struct radeon_bo *rbo = NULL;
146 147
	struct device *device = &rdev->pdev->dev;
	int size, aligned_size, ret;
148
	u64 fb_gpuaddr;
149
	void *fbptr = NULL;
150
	unsigned long tmp;
151
	bool fb_tiled = false; /* useful for testing */
152
	u32 tiling_flags = 0;
153 154 155

	mode_cmd.width = surface_width;
	mode_cmd.height = surface_height;
156 157 158 159 160

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

161
	mode_cmd.bpp = surface_bpp;
162
	/* need to align pitch with crtc limits */
163
	mode_cmd.pitch = radeon_align_pitch(rdev, mode_cmd.width, mode_cmd.bpp, fb_tiled) * ((mode_cmd.bpp + 1) / 8);
164
	mode_cmd.depth = surface_depth;
165 166 167 168 169

	size = mode_cmd.pitch * mode_cmd.height;
	aligned_size = ALIGN(size, PAGE_SIZE);

	ret = radeon_gem_object_create(rdev, aligned_size, 0,
170 171
			RADEON_GEM_DOMAIN_VRAM,
			false, ttm_bo_type_kernel,
172
			&gobj);
173
	if (ret) {
174 175
		printk(KERN_ERR "failed to allocate framebuffer (%d %d)\n",
		       surface_width, surface_height);
176 177 178
		ret = -ENOMEM;
		goto out;
	}
179
	rbo = gobj->driver_private;
180

181
	if (fb_tiled)
182 183 184 185 186 187 188 189 190 191 192 193 194 195
		tiling_flags = RADEON_TILING_MACRO;

#ifdef __BIG_ENDIAN
	switch (mode_cmd.bpp) {
	case 32:
		tiling_flags |= RADEON_TILING_SWAP_32BIT;
		break;
	case 16:
		tiling_flags |= RADEON_TILING_SWAP_16BIT;
	default:
		break;
	}
#endif

196 197 198 199 200 201 202
	if (tiling_flags) {
		ret = radeon_bo_set_tiling_flags(rbo,
					tiling_flags | RADEON_TILING_SURFACE,
					mode_cmd.pitch);
		if (ret)
			dev_err(rdev->dev, "FB failed to set tiling flags\n");
	}
203 204 205 206 207 208 209
	mutex_lock(&rdev->ddev->struct_mutex);
	fb = radeon_framebuffer_create(rdev->ddev, &mode_cmd, gobj);
	if (fb == NULL) {
		DRM_ERROR("failed to allocate fb.\n");
		ret = -ENOMEM;
		goto out_unref;
	}
210 211 212 213 214 215 216 217 218 219 220 221
	ret = radeon_bo_reserve(rbo, false);
	if (unlikely(ret != 0))
		goto out_unref;
	ret = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &fb_gpuaddr);
	if (ret) {
		radeon_bo_unreserve(rbo);
		goto out_unref;
	}
	if (fb_tiled)
		radeon_bo_check_tiling(rbo, 0, 0);
	ret = radeon_bo_kmap(rbo, &fbptr);
	radeon_bo_unreserve(rbo);
222 223 224
	if (ret) {
		goto out_unref;
	}
225 226 227

	list_add(&fb->filp_head, &rdev->ddev->mode_config.fb_kernel_list);

228
	*fb_p = fb;
229 230
	rfb = to_radeon_framebuffer(fb);
	rdev->fbdev_rfb = rfb;
231
	rdev->fbdev_rbo = rbo;
232 233 234 235 236 237

	info = framebuffer_alloc(sizeof(struct radeon_fb_device), device);
	if (info == NULL) {
		ret = -ENOMEM;
		goto out_unref;
	}
238

239
	rdev->fbdev_info = info;
240
	rfbdev = info->par;
241 242
	rfbdev->helper.funcs = &radeon_fb_helper_funcs;
	rfbdev->helper.dev = dev;
243
	ret = drm_fb_helper_init_crtc_count(&rfbdev->helper, rdev->num_crtc,
244 245 246
					    RADEONFB_CONN_LIMIT);
	if (ret)
		goto out_unref;
247

248
	memset_io(fbptr, 0x0, aligned_size);
249

250
	strcpy(info->fix.id, "radeondrmfb");
251

252
	drm_fb_helper_fill_fix(info, fb->pitch, fb->depth);
253

254 255
	info->flags = FBINFO_DEFAULT;
	info->fbops = &radeonfb_ops;
256

257
	tmp = fb_gpuaddr - rdev->mc.vram_start;
258
	info->fix.smem_start = rdev->mc.aper_base + tmp;
259 260 261
	info->fix.smem_len = size;
	info->screen_base = fbptr;
	info->screen_size = size;
262 263

	drm_fb_helper_fill_var(info, fb, fb_width, fb_height);
264 265 266 267 268

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

269 270
	info->fix.mmio_start = 0;
	info->fix.mmio_len = 0;
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
	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);
	DRM_INFO("size %lu\n", (unsigned long)size);
	DRM_INFO("fb depth is %d\n", fb->depth);
	DRM_INFO("   pitch is %d\n", fb->pitch);

	fb->fbdev = info;
	rfbdev->rfb = rfb;
	rfbdev->rdev = rdev;

	mutex_unlock(&rdev->ddev->struct_mutex);
291
	vga_switcheroo_client_fb_set(rdev->ddev->pdev, info);
292 293 294
	return 0;

out_unref:
295 296 297 298 299 300
	if (rbo) {
		ret = radeon_bo_reserve(rbo, false);
		if (likely(ret == 0)) {
			radeon_bo_kunmap(rbo);
			radeon_bo_unreserve(rbo);
		}
301
	}
302
	if (fb && ret) {
303 304 305 306 307 308 309 310 311 312 313
		list_del(&fb->filp_head);
		drm_gem_object_unreference(gobj);
		drm_framebuffer_cleanup(fb);
		kfree(fb);
	}
	drm_gem_object_unreference(gobj);
	mutex_unlock(&rdev->ddev->struct_mutex);
out:
	return ret;
}

314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
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;
}

330 331
int radeonfb_probe(struct drm_device *dev)
{
332 333 334 335 336 337 338 339
	struct radeon_device *rdev = dev->dev_private;
	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;

	return drm_fb_helper_single_fb_probe(dev, bpp_sel, &radeonfb_create);
340 341 342 343 344 345
}

int radeonfb_remove(struct drm_device *dev, struct drm_framebuffer *fb)
{
	struct fb_info *info;
	struct radeon_framebuffer *rfb = to_radeon_framebuffer(fb);
346 347
	struct radeon_bo *rbo;
	int r;
348 349 350 351 352 353

	if (!fb) {
		return -EINVAL;
	}
	info = fb->fbdev;
	if (info) {
354
		struct radeon_fb_device *rfbdev = info->par;
355
		rbo = rfb->obj->driver_private;
356
		unregister_framebuffer(info);
357 358 359 360 361 362
		r = radeon_bo_reserve(rbo, false);
		if (likely(r == 0)) {
			radeon_bo_kunmap(rbo);
			radeon_bo_unpin(rbo);
			radeon_bo_unreserve(rbo);
		}
363
		drm_fb_helper_free(&rfbdev->helper);
364 365 366 367
		framebuffer_release(info);
	}

	printk(KERN_INFO "unregistered panic notifier\n");
368

369 370 371 372
	return 0;
}
EXPORT_SYMBOL(radeonfb_remove);
MODULE_LICENSE("GPL");