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
46enum {
47	NvDmaFB = 0xd8000001,
48	NvDmaTT = 0xd8000002,
49};
50
51struct 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
61struct nouveau_buffer {
62	struct gralloc_drm_bo_t base;
63
64	struct nouveau_bo *bo;
65};
66
67static 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
174static struct gralloc_drm_bo_t *
175nouveau_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
234static 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
242static 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
262static 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
270static 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
280static 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
321struct 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