Home | History | Annotate | Line # | Download | only in radeon
radeon_fb.c revision 1.5.20.2
      1 /*	$NetBSD: radeon_fb.c,v 1.5.20.2 2020/04/08 14:08:26 martin Exp $	*/
      2 
      3 /*
      4  * Copyright  2007 David Airlie
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the "Software"),
      8  * to deal in the Software without restriction, including without limitation
      9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     10  * and/or sell copies of the Software, and to permit persons to whom the
     11  * Software is furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice (including the next
     14  * paragraph) shall be included in all copies or substantial portions of the
     15  * Software.
     16  *
     17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     22  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
     23  * DEALINGS IN THE SOFTWARE.
     24  *
     25  * Authors:
     26  *     David Airlie
     27  */
     28 #include <sys/cdefs.h>
     29 __KERNEL_RCSID(0, "$NetBSD: radeon_fb.c,v 1.5.20.2 2020/04/08 14:08:26 martin Exp $");
     30 
     31 #include <linux/module.h>
     32 #include <linux/slab.h>
     33 #include <linux/fb.h>
     34 
     35 #include <drm/drmP.h>
     36 #include <drm/drm_crtc.h>
     37 #include <drm/drm_crtc_helper.h>
     38 #include <drm/radeon_drm.h>
     39 #include "radeon.h"
     40 
     41 #include <drm/drm_fb_helper.h>
     42 
     43 #include <linux/vga_switcheroo.h>
     44 
     45 #ifdef __NetBSD__
     46 #include "radeondrmkmsfb.h"
     47 
     48 #include <linux/nbsd-namespace.h>
     49 #endif
     50 
     51 /* object hierarchy -
     52    this contains a helper + a radeon fb
     53    the helper contains a pointer to radeon framebuffer baseclass.
     54 */
     55 struct radeon_fbdev {
     56 	struct drm_fb_helper helper;
     57 	struct radeon_framebuffer rfb;
     58 	struct list_head fbdev_list;
     59 	struct radeon_device *rdev;
     60 };
     61 
     62 #ifndef __NetBSD__
     63 static struct fb_ops radeonfb_ops = {
     64 	.owner = THIS_MODULE,
     65 	.fb_check_var = drm_fb_helper_check_var,
     66 	.fb_set_par = drm_fb_helper_set_par,
     67 	.fb_fillrect = drm_fb_helper_cfb_fillrect,
     68 	.fb_copyarea = drm_fb_helper_cfb_copyarea,
     69 	.fb_imageblit = drm_fb_helper_cfb_imageblit,
     70 	.fb_pan_display = drm_fb_helper_pan_display,
     71 	.fb_blank = drm_fb_helper_blank,
     72 	.fb_setcmap = drm_fb_helper_setcmap,
     73 	.fb_debug_enter = drm_fb_helper_debug_enter,
     74 	.fb_debug_leave = drm_fb_helper_debug_leave,
     75 };
     76 #endif
     77 
     78 
     79 int radeon_align_pitch(struct radeon_device *rdev, int width, int bpp, bool tiled)
     80 {
     81 	int aligned = width;
     82 	int align_large = (ASIC_IS_AVIVO(rdev)) || tiled;
     83 	int pitch_mask = 0;
     84 
     85 	switch (bpp / 8) {
     86 	case 1:
     87 		pitch_mask = align_large ? 255 : 127;
     88 		break;
     89 	case 2:
     90 		pitch_mask = align_large ? 127 : 31;
     91 		break;
     92 	case 3:
     93 	case 4:
     94 		pitch_mask = align_large ? 63 : 15;
     95 		break;
     96 	}
     97 
     98 	aligned += pitch_mask;
     99 	aligned &= ~pitch_mask;
    100 	return aligned;
    101 }
    102 
    103 static void radeonfb_destroy_pinned_object(struct drm_gem_object *gobj)
    104 {
    105 	struct radeon_bo *rbo = gem_to_radeon_bo(gobj);
    106 	int ret;
    107 
    108 	ret = radeon_bo_reserve(rbo, false);
    109 	if (likely(ret == 0)) {
    110 		radeon_bo_kunmap(rbo);
    111 		radeon_bo_unpin(rbo);
    112 		radeon_bo_unreserve(rbo);
    113 	}
    114 	drm_gem_object_unreference_unlocked(gobj);
    115 }
    116 
    117 static int radeonfb_create_pinned_object(struct radeon_fbdev *rfbdev,
    118 					 struct drm_mode_fb_cmd2 *mode_cmd,
    119 					 struct drm_gem_object **gobj_p)
    120 {
    121 	struct radeon_device *rdev = rfbdev->rdev;
    122 	struct drm_gem_object *gobj = NULL;
    123 	struct radeon_bo *rbo = NULL;
    124 	bool fb_tiled = false; /* useful for testing */
    125 	u32 tiling_flags = 0;
    126 	int ret;
    127 	int aligned_size, size;
    128 	int height = mode_cmd->height;
    129 	u32 bpp, depth;
    130 
    131 	drm_fb_get_bpp_depth(mode_cmd->pixel_format, &depth, &bpp);
    132 
    133 	/* need to align pitch with crtc limits */
    134 	mode_cmd->pitches[0] = radeon_align_pitch(rdev, mode_cmd->width, bpp,
    135 						  fb_tiled) * ((bpp + 1) / 8);
    136 
    137 	if (rdev->family >= CHIP_R600)
    138 		height = ALIGN(mode_cmd->height, 8);
    139 	size = mode_cmd->pitches[0] * height;
    140 	aligned_size = ALIGN(size, PAGE_SIZE);
    141 	ret = radeon_gem_object_create(rdev, aligned_size, 0,
    142 				       RADEON_GEM_DOMAIN_VRAM,
    143 				       0, true, &gobj);
    144 	if (ret) {
    145 		printk(KERN_ERR "failed to allocate framebuffer (%d)\n",
    146 		       aligned_size);
    147 		return -ENOMEM;
    148 	}
    149 	rbo = gem_to_radeon_bo(gobj);
    150 
    151 	if (fb_tiled)
    152 		tiling_flags = RADEON_TILING_MACRO;
    153 
    154 #ifdef __BIG_ENDIAN
    155 	switch (bpp) {
    156 	case 32:
    157 		tiling_flags |= RADEON_TILING_SWAP_32BIT;
    158 		break;
    159 	case 16:
    160 		tiling_flags |= RADEON_TILING_SWAP_16BIT;
    161 	default:
    162 		break;
    163 	}
    164 #endif
    165 
    166 	if (tiling_flags) {
    167 		ret = radeon_bo_set_tiling_flags(rbo,
    168 						 tiling_flags | RADEON_TILING_SURFACE,
    169 						 mode_cmd->pitches[0]);
    170 		if (ret)
    171 			dev_err(rdev->dev, "FB failed to set tiling flags\n");
    172 	}
    173 
    174 
    175 	ret = radeon_bo_reserve(rbo, false);
    176 	if (unlikely(ret != 0))
    177 		goto out_unref;
    178 	/* Only 27 bit offset for legacy CRTC */
    179 	ret = radeon_bo_pin_restricted(rbo, RADEON_GEM_DOMAIN_VRAM,
    180 				       ASIC_IS_AVIVO(rdev) ? 0 : 1 << 27,
    181 				       NULL);
    182 	if (ret) {
    183 		radeon_bo_unreserve(rbo);
    184 		goto out_unref;
    185 	}
    186 	if (fb_tiled)
    187 		radeon_bo_check_tiling(rbo, 0, 0);
    188 	ret = radeon_bo_kmap(rbo, NULL);
    189 	radeon_bo_unreserve(rbo);
    190 	if (ret) {
    191 		goto out_unref;
    192 	}
    193 
    194 	*gobj_p = gobj;
    195 	return 0;
    196 out_unref:
    197 	radeonfb_destroy_pinned_object(gobj);
    198 	*gobj_p = NULL;
    199 	return ret;
    200 }
    201 
    202 static int radeonfb_create(struct drm_fb_helper *helper,
    203 			   struct drm_fb_helper_surface_size *sizes)
    204 {
    205 	struct radeon_fbdev *rfbdev =
    206 		container_of(helper, struct radeon_fbdev, helper);
    207 	struct radeon_device *rdev = rfbdev->rdev;
    208 #ifndef __NetBSD__
    209 	struct fb_info *info;
    210 #endif
    211 	struct drm_framebuffer *fb = NULL;
    212 	struct drm_mode_fb_cmd2 mode_cmd;
    213 	struct drm_gem_object *gobj = NULL;
    214 	struct radeon_bo *rbo = NULL;
    215 	int ret;
    216 #ifndef __NetBSD__
    217 	unsigned long tmp;
    218 #endif
    219 
    220 	mode_cmd.width = sizes->surface_width;
    221 	mode_cmd.height = sizes->surface_height;
    222 
    223 	/* avivo can't scanout real 24bpp */
    224 	if ((sizes->surface_bpp == 24) && ASIC_IS_AVIVO(rdev))
    225 		sizes->surface_bpp = 32;
    226 
    227 	mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
    228 							  sizes->surface_depth);
    229 
    230 	ret = radeonfb_create_pinned_object(rfbdev, &mode_cmd, &gobj);
    231 	if (ret) {
    232 		DRM_ERROR("failed to create fbcon object %d\n", ret);
    233 		return ret;
    234 	}
    235 
    236 	rbo = gem_to_radeon_bo(gobj);
    237 
    238 #ifdef __NetBSD__
    239 	ret = radeon_framebuffer_init(rdev->ddev, &rfbdev->rfb, &mode_cmd, gobj);
    240 	if (ret) {
    241 		DRM_ERROR("failed to initialize framebuffer %d\n", ret);
    242 		goto out_unref;
    243 	}
    244 
    245 	(void)memset(rbo->kptr, 0, radeon_bo_size(rbo));
    246 
    247     {
    248 	static const struct radeonfb_attach_args zero_rfa;
    249 	struct radeonfb_attach_args rfa = zero_rfa;
    250 
    251 	rfa.rfa_fb_helper = helper;
    252 	rfa.rfa_fb_sizes = *sizes;
    253 	rfa.rfa_fb_ptr = rbo->kptr;
    254 	rfa.rfa_fb_linebytes = mode_cmd.pitches[0];
    255 
    256 	helper->fbdev = config_found_ia(rdev->ddev->dev, "radeonfbbus", &rfa,
    257 	    NULL);
    258 	if (helper->fbdev == NULL) {
    259 		DRM_ERROR("failed to attach genfb\n");
    260 		goto out_unref;
    261 	}
    262     }
    263 	fb = &rfbdev->rfb.base;
    264 	rfbdev->helper.fb = fb;
    265 #else
    266 	/* okay we have an object now allocate the framebuffer */
    267 	info = drm_fb_helper_alloc_fbi(helper);
    268 	if (IS_ERR(info)) {
    269 		ret = PTR_ERR(info);
    270 		goto out_unref;
    271 	}
    272 
    273 	info->par = rfbdev;
    274 
    275 	ret = radeon_framebuffer_init(rdev->ddev, &rfbdev->rfb, &mode_cmd, gobj);
    276 	if (ret) {
    277 		DRM_ERROR("failed to initialize framebuffer %d\n", ret);
    278 		goto out_destroy_fbi;
    279 	}
    280 
    281 	fb = &rfbdev->rfb.base;
    282 
    283 	/* setup helper */
    284 	rfbdev->helper.fb = fb;
    285 
    286 	memset_io(rbo->kptr, 0x0, radeon_bo_size(rbo));
    287 
    288 	strcpy(info->fix.id, "radeondrmfb");
    289 
    290 	drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
    291 
    292 	info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
    293 	info->fbops = &radeonfb_ops;
    294 
    295 	tmp = radeon_bo_gpu_offset(rbo) - rdev->mc.vram_start;
    296 	info->fix.smem_start = rdev->mc.aper_base + tmp;
    297 	info->fix.smem_len = radeon_bo_size(rbo);
    298 	info->screen_base = rbo->kptr;
    299 	info->screen_size = radeon_bo_size(rbo);
    300 
    301 	drm_fb_helper_fill_var(info, &rfbdev->helper, sizes->fb_width, sizes->fb_height);
    302 
    303 	/* setup aperture base/size for vesafb takeover */
    304 	info->apertures->ranges[0].base = rdev->ddev->mode_config.fb_base;
    305 	info->apertures->ranges[0].size = rdev->mc.aper_size;
    306 
    307 	/* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */
    308 
    309 	if (info->screen_base == NULL) {
    310 		ret = -ENOSPC;
    311 		goto out_destroy_fbi;
    312 	}
    313 
    314 	DRM_INFO("fb mappable at 0x%lX\n",  info->fix.smem_start);
    315 	DRM_INFO("vram apper at 0x%lX\n",  (unsigned long)rdev->mc.aper_base);
    316 	DRM_INFO("size %lu\n", (unsigned long)radeon_bo_size(rbo));
    317 	DRM_INFO("fb depth is %d\n", fb->depth);
    318 	DRM_INFO("   pitch is %d\n", fb->pitches[0]);
    319 
    320 	vga_switcheroo_client_fb_set(rdev->ddev->pdev, info);
    321 #endif
    322 	return 0;
    323 
    324 #ifndef __NetBSD__
    325 out_destroy_fbi:
    326 	drm_fb_helper_release_fbi(helper);
    327 #endif
    328 out_unref:
    329 	if (rbo) {
    330 
    331 	}
    332 	if (fb && ret) {
    333 		drm_gem_object_unreference(gobj);
    334 		drm_framebuffer_unregister_private(fb);
    335 		drm_framebuffer_cleanup(fb);
    336 		kfree(fb);
    337 	}
    338 	return ret;
    339 }
    340 
    341 void radeon_fb_output_poll_changed(struct radeon_device *rdev)
    342 {
    343 	drm_fb_helper_hotplug_event(&rdev->mode_info.rfbdev->helper);
    344 }
    345 
    346 static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfbdev)
    347 {
    348 	struct radeon_framebuffer *rfb = &rfbdev->rfb;
    349 #ifdef __NetBSD__
    350 	int ret;
    351 #endif
    352 
    353 #ifdef __NetBSD__
    354 	/* XXX errno NetBSD->Linux */
    355 	ret = -config_detach(rfbdev->helper.fbdev, DETACH_FORCE);
    356 	if (ret)
    357 		DRM_ERROR("failed to detach radeonfb: %d\n", ret);
    358 	rfbdev->helper.fbdev = NULL;
    359 #else
    360 	drm_fb_helper_unregister_fbi(&rfbdev->helper);
    361 	drm_fb_helper_release_fbi(&rfbdev->helper);
    362 #endif
    363 
    364 	if (rfb->obj) {
    365 		radeonfb_destroy_pinned_object(rfb->obj);
    366 		rfb->obj = NULL;
    367 	}
    368 	drm_fb_helper_fini(&rfbdev->helper);
    369 	drm_framebuffer_unregister_private(&rfb->base);
    370 	drm_framebuffer_cleanup(&rfb->base);
    371 
    372 	return 0;
    373 }
    374 
    375 static const struct drm_fb_helper_funcs radeon_fb_helper_funcs = {
    376 	.gamma_set = radeon_crtc_fb_gamma_set,
    377 	.gamma_get = radeon_crtc_fb_gamma_get,
    378 	.fb_probe = radeonfb_create,
    379 };
    380 
    381 int radeon_fbdev_init(struct radeon_device *rdev)
    382 {
    383 	struct radeon_fbdev *rfbdev;
    384 	int bpp_sel = 32;
    385 	int ret;
    386 
    387 	/* select 8 bpp console on RN50 or 16MB cards */
    388 	if (ASIC_IS_RN50(rdev) || rdev->mc.real_vram_size <= (32*1024*1024))
    389 		bpp_sel = 8;
    390 
    391 	rfbdev = kzalloc(sizeof(struct radeon_fbdev), GFP_KERNEL);
    392 	if (!rfbdev)
    393 		return -ENOMEM;
    394 
    395 	rfbdev->rdev = rdev;
    396 	rdev->mode_info.rfbdev = rfbdev;
    397 
    398 	drm_fb_helper_prepare(rdev->ddev, &rfbdev->helper,
    399 			      &radeon_fb_helper_funcs);
    400 
    401 	ret = drm_fb_helper_init(rdev->ddev, &rfbdev->helper,
    402 				 rdev->num_crtc,
    403 				 RADEONFB_CONN_LIMIT);
    404 	if (ret)
    405 		goto free;
    406 
    407 	ret = drm_fb_helper_single_add_all_connectors(&rfbdev->helper);
    408 	if (ret)
    409 		goto fini;
    410 
    411 	/* disable all the possible outputs/crtcs before entering KMS mode */
    412 	drm_helper_disable_unused_functions(rdev->ddev);
    413 
    414 	ret = drm_fb_helper_initial_config(&rfbdev->helper, bpp_sel);
    415 	if (ret)
    416 		goto fini;
    417 
    418 	return 0;
    419 
    420 fini:
    421 	drm_fb_helper_fini(&rfbdev->helper);
    422 free:
    423 	kfree(rfbdev);
    424 	return ret;
    425 }
    426 
    427 void radeon_fbdev_fini(struct radeon_device *rdev)
    428 {
    429 	if (!rdev->mode_info.rfbdev)
    430 		return;
    431 
    432 	radeon_fbdev_destroy(rdev->ddev, rdev->mode_info.rfbdev);
    433 	kfree(rdev->mode_info.rfbdev);
    434 	rdev->mode_info.rfbdev = NULL;
    435 }
    436 
    437 void radeon_fbdev_set_suspend(struct radeon_device *rdev, int state)
    438 {
    439 #ifndef __NetBSD__		/* XXX radeon fb suspend */
    440 	fb_set_suspend(rdev->mode_info.rfbdev->helper.fbdev, state);
    441 #endif
    442 }
    443 
    444 bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj)
    445 {
    446 	if (robj == gem_to_radeon_bo(rdev->mode_info.rfbdev->rfb.obj))
    447 		return true;
    448 	return false;
    449 }
    450 
    451 void radeon_fb_add_connector(struct radeon_device *rdev, struct drm_connector *connector)
    452 {
    453 	drm_fb_helper_add_one_connector(&rdev->mode_info.rfbdev->helper, connector);
    454 }
    455 
    456 void radeon_fb_remove_connector(struct radeon_device *rdev, struct drm_connector *connector)
    457 {
    458 	drm_fb_helper_remove_one_connector(&rdev->mode_info.rfbdev->helper, connector);
    459 }
    460 
    461 void radeon_fbdev_restore_mode(struct radeon_device *rdev)
    462 {
    463 	struct radeon_fbdev *rfbdev = rdev->mode_info.rfbdev;
    464 	struct drm_fb_helper *fb_helper;
    465 	int ret;
    466 
    467 	if (!rfbdev)
    468 		return;
    469 
    470 	fb_helper = &rfbdev->helper;
    471 
    472 	ret = drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper);
    473 	if (ret)
    474 		DRM_DEBUG("failed to restore crtc mode\n");
    475 }
    476