1 /* 2 * Copyright (C) 2011 Chia-I Wu <olvaffe (at) gmail.com> 3 * Copyright (C) 2011 LunarG Inc. 4 * 5 * Based on xf86-video-nouveau, which has 6 * 7 * Copyright 2007 Red Hat, Inc. 8 * Copyright 2008 Maarten Maathuis 9 * 10 * Permission is hereby granted, free of charge, to any person obtaining a 11 * copy of this software and associated documentation files (the "Software"), 12 * to deal in the Software without restriction, including without limitation 13 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 14 * and/or sell copies of the Software, and to permit persons to whom the 15 * Software is furnished to do so, subject to the following conditions: 16 * 17 * The above copyright notice and this permission notice shall be included 18 * in all copies or substantial portions of the Software. 19 * 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 23 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 * DEALINGS IN THE SOFTWARE. 27 */ 28 29 #define LOG_TAG "GRALLOC-NOUVEAU" 30 31 #include <cutils/log.h> 32 #include <stdlib.h> 33 #include <errno.h> 34 #include <drm.h> 35 #include <nouveau_drmif.h> 36 #include <nouveau_channel.h> 37 #include <nouveau_bo.h> 38 39 #include "gralloc_drm.h" 40 #include "gralloc_drm_priv.h" 41 42 #define MAX(a, b) (((a) > (b)) ? (a) : (b)) 43 44 #define NVC0_TILE_HEIGHT(m) (8 << ((m) >> 4)) 45 46 enum { 47 NvDmaFB = 0xd8000001, 48 NvDmaTT = 0xd8000002, 49 }; 50 51 struct nouveau_info { 52 struct gralloc_drm_drv_t base; 53 54 int fd; 55 struct nouveau_device *dev; 56 struct nouveau_channel *chan; 57 int arch; 58 int tiled_scanout; 59 }; 60 61 struct nouveau_buffer { 62 struct gralloc_drm_bo_t base; 63 64 struct nouveau_bo *bo; 65 }; 66 67 static struct nouveau_bo *alloc_bo(struct nouveau_info *info, 68 int width, int height, int cpp, int usage, int *pitch) 69 { 70 struct nouveau_bo *bo = NULL; 71 int flags, tile_mode, tile_flags; 72 int tiled, scanout; 73 unsigned int align; 74 75 flags = NOUVEAU_BO_MAP | NOUVEAU_BO_VRAM; 76 tile_mode = 0; 77 tile_flags = 0; 78 79 scanout = !!(usage & GRALLOC_USAGE_HW_FB); 80 81 tiled = !(usage & (GRALLOC_USAGE_SW_READ_OFTEN | 82 GRALLOC_USAGE_SW_WRITE_OFTEN)); 83 if (!info->chan) 84 tiled = 0; 85 else if (scanout && info->tiled_scanout) 86 tiled = 1; 87 88 /* calculate pitch align */ 89 align = 64; 90 if (info->arch >= 0x50) { 91 if (scanout && !info->tiled_scanout) 92 align = 256; 93 else 94 tiled = 1; 95 } 96 97 *pitch = ALIGN(width * cpp, align); 98 99 if (tiled) { 100 if (info->arch >= 0xc0) { 101 if (height > 64) 102 tile_mode = 0x40; 103 else if (height > 32) 104 tile_mode = 0x30; 105 else if (height > 16) 106 tile_mode = 0x20; 107 else if (height > 8) 108 tile_mode = 0x10; 109 else 110 tile_mode = 0x00; 111 112 tile_flags = 0xfe00; 113 114 align = NVC0_TILE_HEIGHT(tile_mode); 115 height = ALIGN(height, align); 116 } 117 else if (info->arch >= 0x50) { 118 if (height > 32) 119 tile_mode = 4; 120 else if (height > 16) 121 tile_mode = 3; 122 else if (height > 8) 123 tile_mode = 2; 124 else if (height > 4) 125 tile_mode = 1; 126 else 127 tile_mode = 0; 128 129 tile_flags = (scanout && cpp != 2) ? 0x7a00 : 0x7000; 130 131 align = 1 << (tile_mode + 2); 132 height = ALIGN(height, align); 133 } 134 else { 135 align = *pitch / 4; 136 137 /* round down to the previous power of two */ 138 align >>= 1; 139 align |= align >> 1; 140 align |= align >> 2; 141 align |= align >> 4; 142 align |= align >> 8; 143 align |= align >> 16; 144 align++; 145 146 align = MAX((info->dev->chipset >= 0x40) ? 1024 : 256, 147 align); 148 149 /* adjust pitch */ 150 *pitch = ALIGN(*pitch, align); 151 152 tile_mode = *pitch; 153 } 154 } 155 156 if (cpp == 4) 157 tile_flags |= NOUVEAU_BO_TILE_32BPP; 158 else if (cpp == 2) 159 tile_flags |= NOUVEAU_BO_TILE_16BPP; 160 161 if (scanout) 162 tile_flags |= NOUVEAU_BO_TILE_SCANOUT; 163 164 if (nouveau_bo_new_tile(info->dev, flags, 0, *pitch * height, 165 tile_mode, tile_flags, &bo)) { 166 ALOGE("failed to allocate bo (flags 0x%x, size %d, tile_mode 0x%x, tile_flags 0x%x)", 167 flags, *pitch * height, tile_mode, tile_flags); 168 bo = NULL; 169 } 170 171 return bo; 172 } 173 174 static struct gralloc_drm_bo_t * 175 nouveau_alloc(struct gralloc_drm_drv_t *drv, struct gralloc_drm_handle_t *handle) 176 { 177 struct nouveau_info *info = (struct nouveau_info *) drv; 178 struct nouveau_buffer *nb; 179 int cpp; 180 181 cpp = gralloc_drm_get_bpp(handle->format); 182 if (!cpp) { 183 ALOGE("unrecognized format 0x%x", handle->format); 184 return NULL; 185 } 186 187 nb = calloc(1, sizeof(*nb)); 188 if (!nb) 189 return NULL; 190 191 if (handle->name) { 192 if (nouveau_bo_handle_ref(info->dev, handle->name, &nb->bo)) { 193 ALOGE("failed to create nouveau bo from name %u", 194 handle->name); 195 free(nb); 196 return NULL; 197 } 198 } 199 else { 200 int width, height, pitch; 201 202 width = handle->width; 203 height = handle->height; 204 gralloc_drm_align_geometry(handle->format, &width, &height); 205 206 nb->bo = alloc_bo(info, width, height, 207 cpp, handle->usage, &pitch); 208 if (!nb->bo) { 209 ALOGE("failed to allocate nouveau bo %dx%dx%d", 210 handle->width, handle->height, cpp); 211 free(nb); 212 return NULL; 213 } 214 215 if (nouveau_bo_handle_get(nb->bo, 216 (uint32_t *) &handle->name)) { 217 ALOGE("failed to flink nouveau bo"); 218 nouveau_bo_ref(NULL, &nb->bo); 219 free(nb); 220 return NULL; 221 } 222 223 handle->stride = pitch; 224 } 225 226 if (handle->usage & GRALLOC_USAGE_HW_FB) 227 nb->base.fb_handle = nb->bo->handle; 228 229 nb->base.handle = handle; 230 231 return &nb->base; 232 } 233 234 static void nouveau_free(struct gralloc_drm_drv_t *drv, 235 struct gralloc_drm_bo_t *bo) 236 { 237 struct nouveau_buffer *nb = (struct nouveau_buffer *) bo; 238 nouveau_bo_ref(NULL, &nb->bo); 239 free(nb); 240 } 241 242 static int nouveau_map(struct gralloc_drm_drv_t *drv, 243 struct gralloc_drm_bo_t *bo, int x, int y, int w, int h, 244 int enable_write, void **addr) 245 { 246 struct nouveau_buffer *nb = (struct nouveau_buffer *) bo; 247 uint32_t flags; 248 int err; 249 250 flags = NOUVEAU_BO_RD; 251 if (enable_write) 252 flags |= NOUVEAU_BO_WR; 253 254 /* TODO if tiled, allocate a linear copy of bo in GART and map it */ 255 err = nouveau_bo_map(nb->bo, flags); 256 if (!err) 257 *addr = nb->bo->map; 258 259 return err; 260 } 261 262 static void nouveau_unmap(struct gralloc_drm_drv_t *drv, 263 struct gralloc_drm_bo_t *bo) 264 { 265 struct nouveau_buffer *nb = (struct nouveau_buffer *) bo; 266 /* TODO if tiled, unmap the linear bo and copy back */ 267 nouveau_bo_unmap(nb->bo); 268 } 269 270 static void nouveau_destroy(struct gralloc_drm_drv_t *drv) 271 { 272 struct nouveau_info *info = (struct nouveau_info *) drv; 273 274 if (info->chan) 275 nouveau_channel_free(&info->chan); 276 nouveau_device_close(&info->dev); 277 free(info); 278 } 279 280 static int nouveau_init(struct nouveau_info *info) 281 { 282 int err = 0; 283 284 switch (info->dev->chipset & 0xf0) { 285 case 0x00: 286 info->arch = 0x04; 287 break; 288 case 0x10: 289 info->arch = 0x10; 290 break; 291 case 0x20: 292 info->arch = 0x20; 293 break; 294 case 0x30: 295 info->arch = 0x30; 296 break; 297 case 0x40: 298 case 0x60: 299 info->arch = 0x40; 300 break; 301 case 0x50: 302 case 0x80: 303 case 0x90: 304 case 0xa0: 305 info->arch = 0x50; 306 break; 307 case 0xc0: 308 info->arch = 0xc0; 309 break; 310 default: 311 ALOGE("unknown nouveau chipset 0x%x", info->dev->chipset); 312 err = -EINVAL; 313 break; 314 } 315 316 info->tiled_scanout = (info->chan != NULL); 317 318 return err; 319 } 320 321 struct gralloc_drm_drv_t *gralloc_drm_drv_create_for_nouveau(int fd) 322 { 323 struct nouveau_info *info; 324 int err; 325 326 info = calloc(1, sizeof(*info)); 327 if (!info) 328 return NULL; 329 330 info->fd = fd; 331 err = nouveau_device_open_existing(&info->dev, 0, info->fd, 0); 332 if (err) { 333 ALOGE("failed to create nouveau device"); 334 free(info); 335 return NULL; 336 } 337 338 err = nouveau_channel_alloc(info->dev, NvDmaFB, NvDmaTT, 339 24 * 1024, &info->chan); 340 if (err) { 341 /* make it non-fatal temporarily as it may require firmwares */ 342 ALOGW("failed to create nouveau channel"); 343 info->chan = NULL; 344 } 345 346 err = nouveau_init(info); 347 if (err) { 348 if (info->chan) 349 nouveau_channel_free(&info->chan); 350 nouveau_device_close(&info->dev); 351 free(info); 352 return NULL; 353 } 354 355 info->base.destroy = nouveau_destroy; 356 info->base.alloc = nouveau_alloc; 357 info->base.free = nouveau_free; 358 info->base.map = nouveau_map; 359 info->base.unmap = nouveau_unmap; 360 361 return &info->base; 362 } 363