1 /*
2  * Copyright (C) 2011 Chia-I Wu <olvaffe@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 
alloc_bo(struct nouveau_info * info,int width,int height,int cpp,int usage,int * pitch)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 *
nouveau_alloc(struct gralloc_drm_drv_t * drv,struct gralloc_drm_handle_t * handle)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 
nouveau_free(struct gralloc_drm_drv_t * drv,struct gralloc_drm_bo_t * bo)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 
nouveau_map(struct gralloc_drm_drv_t * drv,struct gralloc_drm_bo_t * bo,int x,int y,int w,int h,int enable_write,void ** addr)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 
nouveau_unmap(struct gralloc_drm_drv_t * drv,struct gralloc_drm_bo_t * bo)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 
nouveau_destroy(struct gralloc_drm_drv_t * drv)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 
nouveau_init(struct nouveau_info * info)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 
gralloc_drm_drv_create_for_nouveau(int fd)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