1254885Sdumbbell/* 2254885Sdumbbell * Copyright �� 2007 David Airlie 3254885Sdumbbell * 4254885Sdumbbell * Permission is hereby granted, free of charge, to any person obtaining a 5254885Sdumbbell * copy of this software and associated documentation files (the "Software"), 6254885Sdumbbell * to deal in the Software without restriction, including without limitation 7254885Sdumbbell * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8254885Sdumbbell * and/or sell copies of the Software, and to permit persons to whom the 9254885Sdumbbell * Software is furnished to do so, subject to the following conditions: 10254885Sdumbbell * 11254885Sdumbbell * The above copyright notice and this permission notice (including the next 12254885Sdumbbell * paragraph) shall be included in all copies or substantial portions of the 13254885Sdumbbell * Software. 14254885Sdumbbell * 15254885Sdumbbell * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16254885Sdumbbell * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17254885Sdumbbell * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18254885Sdumbbell * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19254885Sdumbbell * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20254885Sdumbbell * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21254885Sdumbbell * DEALINGS IN THE SOFTWARE. 22254885Sdumbbell * 23254885Sdumbbell * Authors: 24254885Sdumbbell * David Airlie 25254885Sdumbbell */ 26254885Sdumbbell 27254885Sdumbbell#include <sys/cdefs.h> 28254885Sdumbbell__FBSDID("$FreeBSD$"); 29254885Sdumbbell 30262861Sjhb#include <machine/_inttypes.h> 31262861Sjhb 32254885Sdumbbell#include <dev/drm2/drmP.h> 33254885Sdumbbell#include <dev/drm2/drm_crtc.h> 34254885Sdumbbell#include <dev/drm2/drm_crtc_helper.h> 35254885Sdumbbell#include <dev/drm2/radeon/radeon_drm.h> 36254885Sdumbbell#include "radeon.h" 37254885Sdumbbell 38254885Sdumbbell#include <dev/drm2/drm_fb_helper.h> 39254885Sdumbbell 40254885Sdumbbell/* object hierarchy - 41254885Sdumbbell this contains a helper + a radeon fb 42254885Sdumbbell the helper contains a pointer to radeon framebuffer baseclass. 43254885Sdumbbell*/ 44254885Sdumbbellstruct radeon_fbdev { 45254885Sdumbbell struct drm_fb_helper helper; 46254885Sdumbbell struct radeon_framebuffer rfb; 47254885Sdumbbell struct list_head fbdev_list; 48254885Sdumbbell struct radeon_device *rdev; 49254885Sdumbbell}; 50254885Sdumbbell 51262861Sjhb#if defined(__linux__) 52254885Sdumbbellstatic struct fb_ops radeonfb_ops = { 53254885Sdumbbell .owner = THIS_MODULE, 54254885Sdumbbell .fb_check_var = drm_fb_helper_check_var, 55254885Sdumbbell .fb_set_par = drm_fb_helper_set_par, 56254885Sdumbbell .fb_fillrect = cfb_fillrect, 57254885Sdumbbell .fb_copyarea = cfb_copyarea, 58254885Sdumbbell .fb_imageblit = cfb_imageblit, 59254885Sdumbbell .fb_pan_display = drm_fb_helper_pan_display, 60254885Sdumbbell .fb_blank = drm_fb_helper_blank, 61254885Sdumbbell .fb_setcmap = drm_fb_helper_setcmap, 62254885Sdumbbell .fb_debug_enter = drm_fb_helper_debug_enter, 63254885Sdumbbell .fb_debug_leave = drm_fb_helper_debug_leave, 64254885Sdumbbell}; 65262861Sjhb#endif 66254885Sdumbbell 67254885Sdumbbell 68254885Sdumbbellint radeon_align_pitch(struct radeon_device *rdev, int width, int bpp, bool tiled) 69254885Sdumbbell{ 70254885Sdumbbell int aligned = width; 71254885Sdumbbell int align_large = (ASIC_IS_AVIVO(rdev)) || tiled; 72254885Sdumbbell int pitch_mask = 0; 73254885Sdumbbell 74254885Sdumbbell switch (bpp / 8) { 75254885Sdumbbell case 1: 76254885Sdumbbell pitch_mask = align_large ? 255 : 127; 77254885Sdumbbell break; 78254885Sdumbbell case 2: 79254885Sdumbbell pitch_mask = align_large ? 127 : 31; 80254885Sdumbbell break; 81254885Sdumbbell case 3: 82254885Sdumbbell case 4: 83254885Sdumbbell pitch_mask = align_large ? 63 : 15; 84254885Sdumbbell break; 85254885Sdumbbell } 86254885Sdumbbell 87254885Sdumbbell aligned += pitch_mask; 88254885Sdumbbell aligned &= ~pitch_mask; 89254885Sdumbbell return aligned; 90254885Sdumbbell} 91254885Sdumbbell 92254885Sdumbbellstatic void radeonfb_destroy_pinned_object(struct drm_gem_object *gobj) 93254885Sdumbbell{ 94254885Sdumbbell struct radeon_bo *rbo = gem_to_radeon_bo(gobj); 95254885Sdumbbell int ret; 96254885Sdumbbell 97254885Sdumbbell ret = radeon_bo_reserve(rbo, false); 98254885Sdumbbell if (likely(ret == 0)) { 99254885Sdumbbell radeon_bo_kunmap(rbo); 100254885Sdumbbell radeon_bo_unpin(rbo); 101254885Sdumbbell radeon_bo_unreserve(rbo); 102254885Sdumbbell } 103254885Sdumbbell drm_gem_object_unreference_unlocked(gobj); 104254885Sdumbbell} 105254885Sdumbbell 106254885Sdumbbellstatic int radeonfb_create_pinned_object(struct radeon_fbdev *rfbdev, 107254885Sdumbbell struct drm_mode_fb_cmd2 *mode_cmd, 108254885Sdumbbell struct drm_gem_object **gobj_p) 109254885Sdumbbell{ 110254885Sdumbbell struct radeon_device *rdev = rfbdev->rdev; 111254885Sdumbbell struct drm_gem_object *gobj = NULL; 112254885Sdumbbell struct radeon_bo *rbo = NULL; 113254885Sdumbbell bool fb_tiled = false; /* useful for testing */ 114254885Sdumbbell u32 tiling_flags = 0; 115254885Sdumbbell int ret; 116254885Sdumbbell int aligned_size, size; 117254885Sdumbbell int height = mode_cmd->height; 118254885Sdumbbell u32 bpp, depth; 119254885Sdumbbell 120254885Sdumbbell drm_fb_get_bpp_depth(mode_cmd->pixel_format, &depth, &bpp); 121254885Sdumbbell 122254885Sdumbbell /* need to align pitch with crtc limits */ 123254885Sdumbbell mode_cmd->pitches[0] = radeon_align_pitch(rdev, mode_cmd->width, bpp, 124254885Sdumbbell fb_tiled) * ((bpp + 1) / 8); 125254885Sdumbbell 126254885Sdumbbell if (rdev->family >= CHIP_R600) 127254885Sdumbbell height = roundup2(mode_cmd->height, 8); 128254885Sdumbbell size = mode_cmd->pitches[0] * height; 129254885Sdumbbell aligned_size = roundup2(size, PAGE_SIZE); 130254885Sdumbbell ret = radeon_gem_object_create(rdev, aligned_size, 0, 131254885Sdumbbell RADEON_GEM_DOMAIN_VRAM, 132254885Sdumbbell false, true, 133254885Sdumbbell &gobj); 134254885Sdumbbell if (ret) { 135254885Sdumbbell DRM_ERROR("failed to allocate framebuffer (%d)\n", 136254885Sdumbbell aligned_size); 137254885Sdumbbell return -ENOMEM; 138254885Sdumbbell } 139254885Sdumbbell rbo = gem_to_radeon_bo(gobj); 140254885Sdumbbell 141254885Sdumbbell if (fb_tiled) 142254885Sdumbbell tiling_flags = RADEON_TILING_MACRO; 143254885Sdumbbell 144254885Sdumbbell#ifdef __BIG_ENDIAN 145254885Sdumbbell switch (bpp) { 146254885Sdumbbell case 32: 147254885Sdumbbell tiling_flags |= RADEON_TILING_SWAP_32BIT; 148254885Sdumbbell break; 149254885Sdumbbell case 16: 150254885Sdumbbell tiling_flags |= RADEON_TILING_SWAP_16BIT; 151254885Sdumbbell default: 152254885Sdumbbell break; 153254885Sdumbbell } 154254885Sdumbbell#endif 155254885Sdumbbell 156254885Sdumbbell if (tiling_flags) { 157254885Sdumbbell ret = radeon_bo_set_tiling_flags(rbo, 158254885Sdumbbell tiling_flags | RADEON_TILING_SURFACE, 159254885Sdumbbell mode_cmd->pitches[0]); 160254885Sdumbbell if (ret) 161254885Sdumbbell dev_err(rdev->dev, "FB failed to set tiling flags\n"); 162254885Sdumbbell } 163254885Sdumbbell 164254885Sdumbbell 165254885Sdumbbell ret = radeon_bo_reserve(rbo, false); 166254885Sdumbbell if (unlikely(ret != 0)) 167254885Sdumbbell goto out_unref; 168254885Sdumbbell /* Only 27 bit offset for legacy CRTC */ 169254885Sdumbbell ret = radeon_bo_pin_restricted(rbo, RADEON_GEM_DOMAIN_VRAM, 170254885Sdumbbell ASIC_IS_AVIVO(rdev) ? 0 : 1 << 27, 171254885Sdumbbell NULL); 172254885Sdumbbell if (ret) { 173254885Sdumbbell radeon_bo_unreserve(rbo); 174254885Sdumbbell goto out_unref; 175254885Sdumbbell } 176254885Sdumbbell if (fb_tiled) 177254885Sdumbbell radeon_bo_check_tiling(rbo, 0, 0); 178254885Sdumbbell ret = radeon_bo_kmap(rbo, NULL); 179254885Sdumbbell radeon_bo_unreserve(rbo); 180254885Sdumbbell if (ret) { 181254885Sdumbbell goto out_unref; 182254885Sdumbbell } 183254885Sdumbbell 184254885Sdumbbell *gobj_p = gobj; 185254885Sdumbbell return 0; 186254885Sdumbbellout_unref: 187254885Sdumbbell radeonfb_destroy_pinned_object(gobj); 188254885Sdumbbell *gobj_p = NULL; 189254885Sdumbbell return ret; 190254885Sdumbbell} 191254885Sdumbbell 192254885Sdumbbellstatic int radeonfb_create(struct radeon_fbdev *rfbdev, 193254885Sdumbbell struct drm_fb_helper_surface_size *sizes) 194254885Sdumbbell{ 195254885Sdumbbell struct radeon_device *rdev = rfbdev->rdev; 196254885Sdumbbell struct fb_info *info; 197254885Sdumbbell struct drm_framebuffer *fb = NULL; 198254885Sdumbbell struct drm_mode_fb_cmd2 mode_cmd; 199254885Sdumbbell struct drm_gem_object *gobj = NULL; 200254885Sdumbbell struct radeon_bo *rbo = NULL; 201254885Sdumbbell int ret; 202254885Sdumbbell unsigned long tmp; 203254885Sdumbbell 204254885Sdumbbell mode_cmd.width = sizes->surface_width; 205254885Sdumbbell mode_cmd.height = sizes->surface_height; 206254885Sdumbbell 207254885Sdumbbell /* avivo can't scanout real 24bpp */ 208254885Sdumbbell if ((sizes->surface_bpp == 24) && ASIC_IS_AVIVO(rdev)) 209254885Sdumbbell sizes->surface_bpp = 32; 210254885Sdumbbell 211254885Sdumbbell mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, 212254885Sdumbbell sizes->surface_depth); 213254885Sdumbbell 214254885Sdumbbell ret = radeonfb_create_pinned_object(rfbdev, &mode_cmd, &gobj); 215254885Sdumbbell if (ret) { 216254885Sdumbbell DRM_ERROR("failed to create fbcon object %d\n", ret); 217254885Sdumbbell return ret; 218254885Sdumbbell } 219254885Sdumbbell 220254885Sdumbbell rbo = gem_to_radeon_bo(gobj); 221254885Sdumbbell 222262861Sjhb info = malloc(sizeof(*info), DRM_MEM_KMS, M_WAITOK | M_ZERO); 223254885Sdumbbell 224254885Sdumbbell ret = radeon_framebuffer_init(rdev->ddev, &rfbdev->rfb, &mode_cmd, gobj); 225254885Sdumbbell if (ret) { 226254885Sdumbbell DRM_ERROR("failed to initalise framebuffer %d\n", ret); 227254885Sdumbbell goto out_unref; 228254885Sdumbbell } 229254885Sdumbbell 230254885Sdumbbell fb = &rfbdev->rfb.base; 231254885Sdumbbell 232254885Sdumbbell /* setup helper */ 233254885Sdumbbell rfbdev->helper.fb = fb; 234254885Sdumbbell rfbdev->helper.fbdev = info; 235254885Sdumbbell 236262861Sjhb memset(rbo->kptr, 0x0, radeon_bo_size(rbo)); 237254885Sdumbbell 238254885Sdumbbell tmp = radeon_bo_gpu_offset(rbo) - rdev->mc.vram_start; 239262861Sjhb info->fb_size = radeon_bo_size(rbo); 240262861Sjhb info->fb_bpp = sizes->surface_bpp; 241262861Sjhb info->fb_width = sizes->surface_width; 242262861Sjhb info->fb_height = sizes->surface_height; 243262861Sjhb info->fb_pbase = rdev->mc.aper_base + tmp; 244262861Sjhb info->fb_vbase = (vm_offset_t)rbo->kptr; 245254885Sdumbbell 246262861Sjhb DRM_INFO("fb mappable at 0x%" PRIXPTR "\n", info->fb_pbase); 247254885Sdumbbell DRM_INFO("vram apper at 0x%lX\n", (unsigned long)rdev->mc.aper_base); 248254885Sdumbbell DRM_INFO("size %lu\n", (unsigned long)radeon_bo_size(rbo)); 249254885Sdumbbell DRM_INFO("fb depth is %d\n", fb->depth); 250254885Sdumbbell DRM_INFO(" pitch is %d\n", fb->pitches[0]); 251254885Sdumbbell 252254885Sdumbbell return 0; 253254885Sdumbbell 254254885Sdumbbellout_unref: 255254885Sdumbbell if (rbo) { 256262861Sjhb /* TODO? dumbbell@ */ 257254885Sdumbbell } 258254885Sdumbbell if (fb && ret) { 259254885Sdumbbell drm_gem_object_unreference(gobj); 260254885Sdumbbell drm_framebuffer_cleanup(fb); 261254885Sdumbbell free(fb, DRM_MEM_DRIVER); /* XXX malloc'd in radeon_user_framebuffer_create? */ 262254885Sdumbbell } 263254885Sdumbbell return ret; 264254885Sdumbbell} 265254885Sdumbbell 266254885Sdumbbellstatic int radeon_fb_find_or_create_single(struct drm_fb_helper *helper, 267254885Sdumbbell struct drm_fb_helper_surface_size *sizes) 268254885Sdumbbell{ 269254885Sdumbbell struct radeon_fbdev *rfbdev = (struct radeon_fbdev *)helper; 270254885Sdumbbell int new_fb = 0; 271254885Sdumbbell int ret; 272254885Sdumbbell 273254885Sdumbbell if (!helper->fb) { 274254885Sdumbbell ret = radeonfb_create(rfbdev, sizes); 275254885Sdumbbell if (ret) 276254885Sdumbbell return ret; 277254885Sdumbbell new_fb = 1; 278254885Sdumbbell } 279254885Sdumbbell return new_fb; 280254885Sdumbbell} 281254885Sdumbbell 282254885Sdumbbellvoid radeon_fb_output_poll_changed(struct radeon_device *rdev) 283254885Sdumbbell{ 284254885Sdumbbell drm_fb_helper_hotplug_event(&rdev->mode_info.rfbdev->helper); 285254885Sdumbbell} 286254885Sdumbbell 287254885Sdumbbellstatic int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfbdev) 288254885Sdumbbell{ 289254885Sdumbbell struct fb_info *info; 290254885Sdumbbell struct radeon_framebuffer *rfb = &rfbdev->rfb; 291254885Sdumbbell 292254885Sdumbbell if (rfbdev->helper.fbdev) { 293254885Sdumbbell info = rfbdev->helper.fbdev; 294271090Sdumbbell free(info->fb_priv, DRM_MEM_KMS); 295262861Sjhb free(info, DRM_MEM_KMS); 296254885Sdumbbell } 297254885Sdumbbell 298254885Sdumbbell if (rfb->obj) { 299254885Sdumbbell DRM_UNLOCK(dev); /* Work around lock recursion. dumbbell@ */ 300254885Sdumbbell radeonfb_destroy_pinned_object(rfb->obj); 301254885Sdumbbell DRM_LOCK(dev); 302254885Sdumbbell rfb->obj = NULL; 303254885Sdumbbell } 304254885Sdumbbell drm_fb_helper_fini(&rfbdev->helper); 305254885Sdumbbell drm_framebuffer_cleanup(&rfb->base); 306254885Sdumbbell 307254885Sdumbbell return 0; 308254885Sdumbbell} 309254885Sdumbbell 310254885Sdumbbellstatic struct drm_fb_helper_funcs radeon_fb_helper_funcs = { 311254885Sdumbbell .gamma_set = radeon_crtc_fb_gamma_set, 312254885Sdumbbell .gamma_get = radeon_crtc_fb_gamma_get, 313254885Sdumbbell .fb_probe = radeon_fb_find_or_create_single, 314254885Sdumbbell}; 315254885Sdumbbell 316254885Sdumbbellint radeon_fbdev_init(struct radeon_device *rdev) 317254885Sdumbbell{ 318254885Sdumbbell struct radeon_fbdev *rfbdev; 319254885Sdumbbell int bpp_sel = 32; 320254885Sdumbbell int ret; 321254885Sdumbbell 322254885Sdumbbell /* select 8 bpp console on RN50 or 16MB cards */ 323254885Sdumbbell if (ASIC_IS_RN50(rdev) || rdev->mc.real_vram_size <= (32*1024*1024)) 324254885Sdumbbell bpp_sel = 8; 325254885Sdumbbell 326254885Sdumbbell rfbdev = malloc(sizeof(struct radeon_fbdev), 327254885Sdumbbell DRM_MEM_DRIVER, M_WAITOK | M_ZERO); 328254885Sdumbbell if (!rfbdev) 329254885Sdumbbell return -ENOMEM; 330254885Sdumbbell 331254885Sdumbbell rfbdev->rdev = rdev; 332254885Sdumbbell rdev->mode_info.rfbdev = rfbdev; 333254885Sdumbbell rfbdev->helper.funcs = &radeon_fb_helper_funcs; 334254885Sdumbbell 335254885Sdumbbell ret = drm_fb_helper_init(rdev->ddev, &rfbdev->helper, 336254885Sdumbbell rdev->num_crtc, 337254885Sdumbbell RADEONFB_CONN_LIMIT); 338254885Sdumbbell if (ret) { 339254885Sdumbbell free(rfbdev, DRM_MEM_DRIVER); 340254885Sdumbbell return ret; 341254885Sdumbbell } 342254885Sdumbbell 343254885Sdumbbell drm_fb_helper_single_add_all_connectors(&rfbdev->helper); 344254885Sdumbbell drm_fb_helper_initial_config(&rfbdev->helper, bpp_sel); 345254885Sdumbbell return 0; 346254885Sdumbbell} 347254885Sdumbbell 348254885Sdumbbellvoid radeon_fbdev_fini(struct radeon_device *rdev) 349254885Sdumbbell{ 350254885Sdumbbell if (!rdev->mode_info.rfbdev) 351254885Sdumbbell return; 352254885Sdumbbell 353254885Sdumbbell radeon_fbdev_destroy(rdev->ddev, rdev->mode_info.rfbdev); 354254885Sdumbbell free(rdev->mode_info.rfbdev, DRM_MEM_DRIVER); 355254885Sdumbbell rdev->mode_info.rfbdev = NULL; 356254885Sdumbbell} 357254885Sdumbbell 358254885Sdumbbellvoid radeon_fbdev_set_suspend(struct radeon_device *rdev, int state) 359254885Sdumbbell{ 360254885Sdumbbell#ifdef DUMBBELL_WIP 361254885Sdumbbell fb_set_suspend(rdev->mode_info.rfbdev->helper.fbdev, state); 362254885Sdumbbell#endif /* DUMBBELL_WIP */ 363254885Sdumbbell} 364254885Sdumbbell 365254885Sdumbbellint radeon_fbdev_total_size(struct radeon_device *rdev) 366254885Sdumbbell{ 367254885Sdumbbell struct radeon_bo *robj; 368254885Sdumbbell int size = 0; 369254885Sdumbbell 370254885Sdumbbell robj = gem_to_radeon_bo(rdev->mode_info.rfbdev->rfb.obj); 371254885Sdumbbell size += radeon_bo_size(robj); 372254885Sdumbbell return size; 373254885Sdumbbell} 374254885Sdumbbell 375254885Sdumbbellbool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj) 376254885Sdumbbell{ 377254885Sdumbbell if (robj == gem_to_radeon_bo(rdev->mode_info.rfbdev->rfb.obj)) 378254885Sdumbbell return true; 379254885Sdumbbell return false; 380254885Sdumbbell} 381262861Sjhb 382262861Sjhbstruct fb_info * 383262861Sjhbradeon_fb_helper_getinfo(device_t kdev) 384262861Sjhb{ 385262861Sjhb struct drm_device *dev; 386262861Sjhb struct radeon_device *rdev; 387262861Sjhb struct radeon_fbdev *rfbdev; 388262861Sjhb struct fb_info *info; 389262861Sjhb 390262861Sjhb dev = device_get_softc(kdev); 391262861Sjhb rdev = dev->dev_private; 392262861Sjhb rfbdev = rdev->mode_info.rfbdev; 393262861Sjhb if (rfbdev == NULL) 394262861Sjhb return (NULL); 395262861Sjhb 396262861Sjhb info = rfbdev->helper.fbdev; 397262861Sjhb 398262861Sjhb return (info); 399262861Sjhb} 400