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
struct radeon_fb_device {
43
	struct drm_fb_helper helper;
44
	struct radeon_framebuffer	*rfb;
45
	struct radeon_device		*rdev;
46 47 48 49
};

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

/**
62
 * Currently it is assumed that the old framebuffer is reused.
63 64 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
 *
 * 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);

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

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

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

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

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

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

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

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

180
	if (fb_tiled)
181 182 183 184 185 186 187 188 189 190 191 192 193 194
		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

195 196 197 198 199 200 201
	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");
	}
202 203 204 205 206 207 208
	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;
	}
209 210 211 212 213 214 215 216 217 218 219 220
	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);
221 222 223
	if (ret) {
		goto out_unref;
	}
224 225 226

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

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

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

238
	rdev->fbdev_info = info;
239
	rfbdev = info->par;
240 241
	rfbdev->helper.funcs = &radeon_fb_helper_funcs;
	rfbdev->helper.dev = dev;
242 243 244 245 246
	if (rdev->flags & RADEON_SINGLE_CRTC)
		crtc_count = 1;
	else
		crtc_count = 2;
	ret = drm_fb_helper_init_crtc_count(&rfbdev->helper, crtc_count,
247 248 249
					    RADEONFB_CONN_LIMIT);
	if (ret)
		goto out_unref;
250

251
	memset_io(fbptr, 0x0, aligned_size);
252

253
	strcpy(info->fix.id, "radeondrmfb");
254

255
	drm_fb_helper_fill_fix(info, fb->pitch, fb->depth);
256

257 258
	info->flags = FBINFO_DEFAULT;
	info->fbops = &radeonfb_ops;
259

260 261
	tmp = fb_gpuaddr - rdev->mc.vram_location;
	info->fix.smem_start = rdev->mc.aper_base + tmp;
262 263 264
	info->fix.smem_len = size;
	info->screen_base = fbptr;
	info->screen_size = size;
265 266

	drm_fb_helper_fill_var(info, fb, fb_width, fb_height);
267 268 269 270 271

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

272 273
	info->fix.mmio_start = 0;
	info->fix.mmio_len = 0;
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
	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);
	return 0;

out_unref:
297 298 299 300 301 302
	if (rbo) {
		ret = radeon_bo_reserve(rbo, false);
		if (likely(ret == 0)) {
			radeon_bo_kunmap(rbo);
			radeon_bo_unreserve(rbo);
		}
303
	}
304
	if (fb && ret) {
305 306 307 308 309 310 311 312 313 314 315
		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;
}

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

332 333
int radeonfb_probe(struct drm_device *dev)
{
334 335 336 337 338 339 340 341
	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);
342 343 344 345 346 347
}

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

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

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

371 372 373 374
	return 0;
}
EXPORT_SYMBOL(radeonfb_remove);
MODULE_LICENSE("GPL");