1/*
2 * Copyright (C) 2010-2011 Chia-I Wu <olvaffe@gmail.com>
3 * Copyright (C) 2010-2011 LunarG Inc.
4 *
5 * drm_gem_intel_copy is based on xorg-driver-intel, which has
6 *
7 * Copyright 1998-1999 Precision Insight, Inc., Cedar Park, Texas.
8 * All Rights Reserved.
9 * Copyright (c) 2005 Jesse Barnes <jbarnes@virtuousgeek.org>
10 *
11 * Permission is hereby granted, free of charge, to any person obtaining a
12 * copy of this software and associated documentation files (the "Software"),
13 * to deal in the Software without restriction, including without limitation
14 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 * and/or sell copies of the Software, and to permit persons to whom the
16 * Software is furnished to do so, subject to the following conditions:
17 *
18 * The above copyright notice and this permission notice shall be included
19 * in all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
24 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 * DEALINGS IN THE SOFTWARE.
28 */
29
30#define LOG_TAG "GRALLOC-I915"
31
32#include <cutils/log.h>
33#include <stdlib.h>
34#include <errno.h>
35#include <assert.h>
36#include <drm.h>
37#include <intel_bufmgr.h>
38#include <i915_drm.h>
39
40#include "gralloc_drm.h"
41#include "gralloc_drm_priv.h"
42
43#define MI_NOOP                     (0)
44#define MI_BATCH_BUFFER_END         (0x0a << 23)
45#define MI_FLUSH                    (0x04 << 23)
46#define MI_FLUSH_DW                 (0x26 << 23)
47#define MI_WRITE_DIRTY_STATE        (1 << 4)
48#define MI_INVALIDATE_MAP_CACHE     (1 << 0)
49#define XY_SRC_COPY_BLT_CMD         ((2 << 29) | (0x53 << 22) | 6)
50#define XY_SRC_COPY_BLT_WRITE_ALPHA (1 << 21)
51#define XY_SRC_COPY_BLT_WRITE_RGB   (1 << 20)
52#define XY_SRC_COPY_BLT_SRC_TILED   (1 << 15)
53#define XY_SRC_COPY_BLT_DST_TILED   (1 << 11)
54
55struct intel_info {
56	struct gralloc_drm_drv_t base;
57
58	int fd;
59	drm_intel_bufmgr *bufmgr;
60	int gen;
61
62	drm_intel_bo *batch_ibo;
63	uint32_t *batch, *cur;
64	int capacity, size;
65	int exec_blt;
66};
67
68struct intel_buffer {
69	struct gralloc_drm_bo_t base;
70	drm_intel_bo *ibo;
71	uint32_t tiling;
72};
73
74static int
75batch_next(struct intel_info *info)
76{
77	info->cur = info->batch;
78
79	if (info->batch_ibo)
80		drm_intel_bo_unreference(info->batch_ibo);
81
82	info->batch_ibo = drm_intel_bo_alloc(info->bufmgr,
83			"gralloc-batchbuffer", info->size, 4096);
84
85	return (info->batch_ibo) ? 0 : -ENOMEM;
86}
87
88static int
89batch_count(struct intel_info *info)
90{
91	return info->cur - info->batch;
92}
93
94static void
95batch_dword(struct intel_info *info, uint32_t dword)
96{
97	*info->cur++ = dword;
98}
99
100static int
101batch_reloc(struct intel_info *info, struct gralloc_drm_bo_t *bo,
102		uint32_t read_domains, uint32_t write_domain)
103{
104	struct intel_buffer *target = (struct intel_buffer *) bo;
105	uint32_t offset = (info->cur - info->batch) * sizeof(info->batch[0]);
106	int ret;
107
108	ret = drm_intel_bo_emit_reloc(info->batch_ibo, offset,
109			target->ibo, 0, read_domains, write_domain);
110	if (!ret)
111		batch_dword(info, target->ibo->offset);
112
113	return ret;
114}
115
116static int
117batch_flush(struct intel_info *info)
118{
119	int size, ret;
120
121	batch_dword(info, MI_BATCH_BUFFER_END);
122	size = batch_count(info);
123	if (size & 1) {
124		batch_dword(info, MI_NOOP);
125		size = batch_count(info);
126	}
127
128	size *= sizeof(info->batch[0]);
129	ret = drm_intel_bo_subdata(info->batch_ibo, 0, size, info->batch);
130	if (ret) {
131		ALOGE("failed to subdata batch");
132		goto fail;
133	}
134	ret = drm_intel_bo_mrb_exec(info->batch_ibo, size,
135		NULL, 0, 0, info->exec_blt);
136	if (ret) {
137		ALOGE("failed to exec batch");
138		goto fail;
139	}
140
141	return batch_next(info);
142
143fail:
144	info->cur = info->batch;
145
146	return ret;
147}
148
149static int
150batch_reserve(struct intel_info *info, int count)
151{
152	int ret = 0;
153
154	if (batch_count(info) + count > info->capacity)
155		ret = batch_flush(info);
156
157	return ret;
158}
159
160static void
161batch_destroy(struct intel_info *info)
162{
163	if (info->batch_ibo) {
164		drm_intel_bo_unreference(info->batch_ibo);
165		info->batch_ibo = NULL;
166	}
167
168	if (info->batch) {
169		free(info->batch);
170		info->batch = NULL;
171	}
172}
173
174static int
175batch_init(struct intel_info *info)
176{
177	int ret;
178
179	info->capacity = 512;
180	info->size = (info->capacity + 16) * sizeof(info->batch[0]);
181
182	info->batch = malloc(info->size);
183	if (!info->batch)
184		return -ENOMEM;
185
186	ret = batch_next(info);
187	if (ret) {
188		free(info->batch);
189		info->batch = NULL;
190	}
191
192	return ret;
193}
194
195static void intel_resolve_format(struct gralloc_drm_drv_t *drv,
196		struct gralloc_drm_bo_t *bo,
197		uint32_t *pitches, uint32_t *offsets, uint32_t *handles)
198{
199	/*
200	 * TODO - should take account hw specific padding, alignment
201	 * for camera, video decoder etc.
202	 */
203
204	struct intel_buffer *ib = (struct intel_buffer *) bo;
205
206	memset(pitches, 0, 4 * sizeof(uint32_t));
207	memset(offsets, 0, 4 * sizeof(uint32_t));
208	memset(handles, 0, 4 * sizeof(uint32_t));
209
210	pitches[0] = ib->base.handle->stride;
211	handles[0] = ib->base.fb_handle;
212
213	switch(ib->base.handle->format) {
214		case HAL_PIXEL_FORMAT_YV12:
215
216			// U and V stride are half of Y plane
217			pitches[2] = pitches[0]/2;
218			pitches[1] = pitches[0]/2;
219
220			// like I420 but U and V are in reverse order
221			offsets[2] = offsets[0] +
222				pitches[0] * ib->base.handle->height;
223			offsets[1] = offsets[2] +
224				pitches[2] * ib->base.handle->height/2;
225
226			handles[1] = handles[2] = handles[0];
227			break;
228
229		case HAL_PIXEL_FORMAT_DRM_NV12:
230
231			// U and V are interleaved in 2nd plane
232			pitches[1] = pitches[0];
233			offsets[1] = offsets[0] +
234				pitches[0] * ib->base.handle->height;
235
236			handles[1] = handles[0];
237			break;
238	}
239}
240
241static drm_intel_bo *alloc_ibo(struct intel_info *info,
242		const struct gralloc_drm_handle_t *handle,
243		uint32_t *tiling, unsigned long *stride)
244{
245	drm_intel_bo *ibo;
246	const char *name;
247	int aligned_width, aligned_height, bpp;
248	unsigned long flags;
249
250	flags = 0;
251	bpp = gralloc_drm_get_bpp(handle->format);
252	if (!bpp) {
253		ALOGE("unrecognized format 0x%x", handle->format);
254		return NULL;
255	}
256
257	aligned_width = handle->width;
258	aligned_height = handle->height;
259	gralloc_drm_align_geometry(handle->format,
260			&aligned_width, &aligned_height);
261
262	if (handle->usage & GRALLOC_USAGE_HW_FB) {
263		unsigned long max_stride;
264
265		max_stride = 32 * 1024;
266		if (info->gen < 50)
267			max_stride /= 2;
268		if (info->gen < 40)
269			max_stride /= 2;
270
271		name = "gralloc-fb";
272		aligned_width = ALIGN(aligned_width, 64);
273		flags = BO_ALLOC_FOR_RENDER;
274
275		*tiling = I915_TILING_X;
276		*stride = aligned_width * bpp;
277		if (*stride > max_stride) {
278			*tiling = I915_TILING_NONE;
279			max_stride = 32 * 1024;
280			if (*stride > max_stride)
281				return NULL;
282		}
283
284		while (1) {
285			ibo = drm_intel_bo_alloc_tiled(info->bufmgr, name,
286					aligned_width, aligned_height,
287					bpp, tiling, stride, flags);
288			if (!ibo || *stride > max_stride) {
289				if (ibo) {
290					drm_intel_bo_unreference(ibo);
291					ibo = NULL;
292				}
293
294				if (*tiling != I915_TILING_NONE) {
295					/* retry */
296					*tiling = I915_TILING_NONE;
297					max_stride = 32 * 1024;
298					continue;
299				}
300			}
301			if (ibo)
302				drm_intel_bo_disable_reuse(ibo);
303			break;
304		}
305	}
306	else {
307		if (handle->usage & (GRALLOC_USAGE_SW_READ_OFTEN |
308				     GRALLOC_USAGE_SW_WRITE_OFTEN))
309			*tiling = I915_TILING_NONE;
310		else if ((handle->usage & GRALLOC_USAGE_HW_RENDER) ||
311			 ((handle->usage & GRALLOC_USAGE_HW_TEXTURE) &&
312			  handle->width >= 64))
313			*tiling = I915_TILING_X;
314		else
315			*tiling = I915_TILING_NONE;
316
317		if (handle->usage & GRALLOC_USAGE_HW_TEXTURE) {
318			name = "gralloc-texture";
319			/* see 2D texture layout of DRI drivers */
320			aligned_width = ALIGN(aligned_width, 4);
321			aligned_height = ALIGN(aligned_height, 2);
322		}
323		else {
324			name = "gralloc-buffer";
325		}
326
327		if (handle->usage & GRALLOC_USAGE_HW_RENDER)
328			flags = BO_ALLOC_FOR_RENDER;
329
330		ibo = drm_intel_bo_alloc_tiled(info->bufmgr, name,
331				aligned_width, aligned_height,
332				bpp, tiling, stride, flags);
333	}
334
335	return ibo;
336}
337
338static struct gralloc_drm_bo_t *intel_alloc(struct gralloc_drm_drv_t *drv,
339		struct gralloc_drm_handle_t *handle)
340{
341	struct intel_info *info = (struct intel_info *) drv;
342	struct intel_buffer *ib;
343
344	ib = calloc(1, sizeof(*ib));
345	if (!ib)
346		return NULL;
347
348	if (handle->name) {
349		uint32_t dummy;
350
351		ib->ibo = drm_intel_bo_gem_create_from_name(info->bufmgr,
352				"gralloc-r", handle->name);
353		if (!ib->ibo) {
354			ALOGE("failed to create ibo from name %u",
355					handle->name);
356			free(ib);
357			return NULL;
358		}
359
360		if (drm_intel_bo_get_tiling(ib->ibo, &ib->tiling, &dummy)) {
361			ALOGE("failed to get ibo tiling");
362			drm_intel_bo_unreference(ib->ibo);
363			free(ib);
364			return NULL;
365		}
366	}
367	else {
368		unsigned long stride;
369
370		ib->ibo = alloc_ibo(info, handle, &ib->tiling, &stride);
371		if (!ib->ibo) {
372			ALOGE("failed to allocate ibo %dx%d (format %d)",
373					handle->width,
374					handle->height,
375					handle->format);
376			free(ib);
377			return NULL;
378		}
379
380		handle->stride = stride;
381
382		if (drm_intel_bo_flink(ib->ibo, (uint32_t *) &handle->name)) {
383			ALOGE("failed to flink ibo");
384			drm_intel_bo_unreference(ib->ibo);
385			free(ib);
386			return NULL;
387		}
388	}
389
390	ib->base.fb_handle = ib->ibo->handle;
391
392	ib->base.handle = handle;
393
394	return &ib->base;
395}
396
397static void intel_free(struct gralloc_drm_drv_t *drv,
398		struct gralloc_drm_bo_t *bo)
399{
400	struct intel_buffer *ib = (struct intel_buffer *) bo;
401
402	drm_intel_bo_unreference(ib->ibo);
403	free(ib);
404}
405
406static int intel_map(struct gralloc_drm_drv_t *drv,
407		struct gralloc_drm_bo_t *bo,
408		int x, int y, int w, int h,
409		int enable_write, void **addr)
410{
411	struct intel_buffer *ib = (struct intel_buffer *) bo;
412	int err;
413
414	if (ib->tiling != I915_TILING_NONE ||
415	    (ib->base.handle->usage & GRALLOC_USAGE_HW_FB))
416		err = drm_intel_gem_bo_map_gtt(ib->ibo);
417	else
418		err = drm_intel_bo_map(ib->ibo, enable_write);
419	if (!err)
420		*addr = ib->ibo->virtual;
421
422	return err;
423}
424
425static void intel_unmap(struct gralloc_drm_drv_t *drv,
426		struct gralloc_drm_bo_t *bo)
427{
428	struct intel_buffer *ib = (struct intel_buffer *) bo;
429
430	if (ib->tiling != I915_TILING_NONE ||
431	    (ib->base.handle->usage & GRALLOC_USAGE_HW_FB))
432		drm_intel_gem_bo_unmap_gtt(ib->ibo);
433	else
434		drm_intel_bo_unmap(ib->ibo);
435}
436
437#include "intel_chipset.h" /* for platform detection macros */
438static void gen_init(struct intel_info *info)
439{
440	struct drm_i915_getparam gp;
441	int pageflipping, id, has_blt;
442
443	memset(&gp, 0, sizeof(gp));
444	gp.param = I915_PARAM_CHIPSET_ID;
445	gp.value = &id;
446	if (drmCommandWriteRead(info->fd, DRM_I915_GETPARAM, &gp, sizeof(gp)))
447		id = 0;
448
449	memset(&gp, 0, sizeof(gp));
450	gp.param = I915_PARAM_HAS_BLT;
451	gp.value = &has_blt;
452	if (drmCommandWriteRead(info->fd, DRM_I915_GETPARAM, &gp, sizeof(gp)))
453		has_blt = 0;
454	info->exec_blt = has_blt ? I915_EXEC_BLT : 0;
455
456	/* GEN4, G4X, GEN5, GEN6, GEN7 */
457	if ((IS_9XX(id) || IS_G4X(id)) && !IS_GEN3(id)) {
458		if (IS_GEN7(id))
459			info->gen = 70;
460		else if (IS_GEN6(id))
461			info->gen = 60;
462		else if (IS_GEN5(id))
463			info->gen = 50;
464		else
465			info->gen = 40;
466	}
467	else {
468		info->gen = 30;
469	}
470}
471
472static void intel_destroy(struct gralloc_drm_drv_t *drv)
473{
474	struct intel_info *info = (struct intel_info *) drv;
475
476	batch_destroy(info);
477	drm_intel_bufmgr_destroy(info->bufmgr);
478	free(info);
479}
480
481struct gralloc_drm_drv_t *gralloc_drm_drv_create_for_intel(int fd)
482{
483	struct intel_info *info;
484
485	info = calloc(1, sizeof(*info));
486	if (!info) {
487		ALOGE("failed to allocate driver info");
488		return NULL;
489	}
490
491	info->fd = fd;
492	info->bufmgr = drm_intel_bufmgr_gem_init(info->fd, 16 * 1024);
493	if (!info->bufmgr) {
494		ALOGE("failed to create buffer manager");
495		free(info);
496		return NULL;
497	}
498
499	batch_init(info);
500	gen_init(info);
501
502	info->base.destroy = intel_destroy;
503	info->base.alloc = intel_alloc;
504	info->base.free = intel_free;
505	info->base.map = intel_map;
506	info->base.unmap = intel_unmap;
507	info->base.resolve_format = intel_resolve_format;
508
509	return &info->base;
510}
511