Home | History | Annotate | Download | only in drm_gralloc
      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