exynos_drm.c revision 49ef06a4
1/*
2 * Copyright (C) 2012 Samsung Electronics Co., Ltd.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 *
23 * Authors:
24 *    Inki Dae <inki.dae@samsung.com>
25 */
26
27#include <stdlib.h>
28#include <stdio.h>
29#include <string.h>
30#include <errno.h>
31#include <unistd.h>
32
33#include <sys/mman.h>
34#include <linux/stddef.h>
35
36#include <xf86drm.h>
37
38#include "libdrm_macros.h"
39#include "exynos_drm.h"
40#include "exynos_drmif.h"
41
42#define U642VOID(x) ((void *)(unsigned long)(x))
43
44/*
45 * Create exynos drm device object.
46 *
47 * @fd: file descriptor to exynos drm driver opened.
48 *
49 * if true, return the device object else NULL.
50 */
51drm_public struct exynos_device * exynos_device_create(int fd)
52{
53	struct exynos_device *dev;
54
55	dev = calloc(sizeof(*dev), 1);
56	if (!dev) {
57		fprintf(stderr, "failed to create device[%s].\n",
58				strerror(errno));
59		return NULL;
60	}
61
62	dev->fd = fd;
63
64	return dev;
65}
66
67/*
68 * Destroy exynos drm device object
69 *
70 * @dev: exynos drm device object.
71 */
72drm_public void exynos_device_destroy(struct exynos_device *dev)
73{
74	free(dev);
75}
76
77/*
78 * Create a exynos buffer object to exynos drm device.
79 *
80 * @dev: exynos drm device object.
81 * @size: user-desired size.
82 * flags: user-desired memory type.
83 *	user can set one or more types among several types to memory
84 *	allocation and cache attribute types. and as default,
85 *	EXYNOS_BO_NONCONTIG and EXYNOS-BO_NONCACHABLE types would
86 *	be used.
87 *
88 * if true, return a exynos buffer object else NULL.
89 */
90drm_public struct exynos_bo * exynos_bo_create(struct exynos_device *dev,
91                                               size_t size, uint32_t flags)
92{
93	struct exynos_bo *bo;
94	struct drm_exynos_gem_create req = {
95		.size = size,
96		.flags = flags,
97	};
98
99	if (size == 0) {
100		fprintf(stderr, "invalid size.\n");
101		goto fail;
102	}
103
104	bo = calloc(sizeof(*bo), 1);
105	if (!bo) {
106		fprintf(stderr, "failed to create bo[%s].\n",
107				strerror(errno));
108		goto err_free_bo;
109	}
110
111	bo->dev = dev;
112
113	if (drmIoctl(dev->fd, DRM_IOCTL_EXYNOS_GEM_CREATE, &req)){
114		fprintf(stderr, "failed to create gem object[%s].\n",
115				strerror(errno));
116		goto err_free_bo;
117	}
118
119	bo->handle = req.handle;
120	bo->size = size;
121	bo->flags = flags;
122
123	return bo;
124
125err_free_bo:
126	free(bo);
127fail:
128	return NULL;
129}
130
131/*
132 * Get information to gem region allocated.
133 *
134 * @dev: exynos drm device object.
135 * @handle: gem handle to request gem info.
136 * @size: size to gem object and returned by kernel side.
137 * @flags: gem flags to gem object and returned by kernel side.
138 *
139 * with this function call, you can get flags and size to gem handle
140 * through bo object.
141 *
142 * if true, return 0 else negative.
143 */
144drm_public int exynos_bo_get_info(struct exynos_device *dev, uint32_t handle,
145                                  size_t *size, uint32_t *flags)
146{
147	int ret;
148	struct drm_exynos_gem_info req = {
149		.handle = handle,
150	};
151
152	ret = drmIoctl(dev->fd, DRM_IOCTL_EXYNOS_GEM_GET, &req);
153	if (ret < 0) {
154		fprintf(stderr, "failed to get gem object information[%s].\n",
155				strerror(errno));
156		return ret;
157	}
158
159	*size = req.size;
160	*flags = req.flags;
161
162	return 0;
163}
164
165/*
166 * Destroy a exynos buffer object.
167 *
168 * @bo: a exynos buffer object to be destroyed.
169 */
170drm_public void exynos_bo_destroy(struct exynos_bo *bo)
171{
172	if (!bo)
173		return;
174
175	if (bo->vaddr)
176		munmap(bo->vaddr, bo->size);
177
178	if (bo->handle) {
179		drmCloseBufferHandle(bo->dev->fd, bo->handle);
180	}
181
182	free(bo);
183}
184
185
186/*
187 * Get a exynos buffer object from a gem global object name.
188 *
189 * @dev: a exynos device object.
190 * @name: a gem global object name exported by another process.
191 *
192 * this interface is used to get a exynos buffer object from a gem
193 * global object name sent by another process for buffer sharing.
194 *
195 * if true, return a exynos buffer object else NULL.
196 *
197 */
198drm_public struct exynos_bo *
199exynos_bo_from_name(struct exynos_device *dev, uint32_t name)
200{
201	struct exynos_bo *bo;
202	struct drm_gem_open req = {
203		.name = name,
204	};
205
206	bo = calloc(sizeof(*bo), 1);
207	if (!bo) {
208		fprintf(stderr, "failed to allocate bo[%s].\n",
209				strerror(errno));
210		return NULL;
211	}
212
213	if (drmIoctl(dev->fd, DRM_IOCTL_GEM_OPEN, &req)) {
214		fprintf(stderr, "failed to open gem object[%s].\n",
215				strerror(errno));
216		goto err_free_bo;
217	}
218
219	bo->dev = dev;
220	bo->name = name;
221	bo->handle = req.handle;
222
223	return bo;
224
225err_free_bo:
226	free(bo);
227	return NULL;
228}
229
230/*
231 * Get a gem global object name from a gem object handle.
232 *
233 * @bo: a exynos buffer object including gem handle.
234 * @name: a gem global object name to be got by kernel driver.
235 *
236 * this interface is used to get a gem global object name from a gem object
237 * handle to a buffer that wants to share it with another process.
238 *
239 * if true, return 0 else negative.
240 */
241drm_public int exynos_bo_get_name(struct exynos_bo *bo, uint32_t *name)
242{
243	if (!bo->name) {
244		struct drm_gem_flink req = {
245			.handle = bo->handle,
246		};
247		int ret;
248
249		ret = drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_FLINK, &req);
250		if (ret) {
251			fprintf(stderr, "failed to get gem global name[%s].\n",
252					strerror(errno));
253			return ret;
254		}
255
256		bo->name = req.name;
257	}
258
259	*name = bo->name;
260
261	return 0;
262}
263
264drm_public uint32_t exynos_bo_handle(struct exynos_bo *bo)
265{
266	return bo->handle;
267}
268
269/*
270 * Mmap a buffer to user space.
271 *
272 * @bo: a exynos buffer object including a gem object handle to be mmapped
273 *	to user space.
274 *
275 * if true, user pointer mmapped else NULL.
276 */
277drm_public void *exynos_bo_map(struct exynos_bo *bo)
278{
279	if (!bo->vaddr) {
280		struct exynos_device *dev = bo->dev;
281		struct drm_mode_map_dumb arg;
282		void *map = NULL;
283		int ret;
284
285		memset(&arg, 0, sizeof(arg));
286		arg.handle = bo->handle;
287
288		ret = drmIoctl(dev->fd, DRM_IOCTL_MODE_MAP_DUMB, &arg);
289		if (ret) {
290			fprintf(stderr, "failed to map dumb buffer[%s].\n",
291				strerror(errno));
292			return NULL;
293		}
294
295		map = drm_mmap(0, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED,
296				dev->fd, arg.offset);
297
298		if (map != MAP_FAILED)
299			bo->vaddr = map;
300	}
301
302	return bo->vaddr;
303}
304
305/*
306 * Export gem object to dmabuf as file descriptor.
307 *
308 * @dev: exynos device object
309 * @handle: gem handle to export as file descriptor of dmabuf
310 * @fd: file descriptor returned from kernel
311 *
312 * @return: 0 on success, -1 on error, and errno will be set
313 */
314drm_public int
315exynos_prime_handle_to_fd(struct exynos_device *dev, uint32_t handle, int *fd)
316{
317	return drmPrimeHandleToFD(dev->fd, handle, 0, fd);
318}
319
320/*
321 * Import file descriptor into gem handle.
322 *
323 * @dev: exynos device object
324 * @fd: file descriptor of dmabuf to import
325 * @handle: gem handle returned from kernel
326 *
327 * @return: 0 on success, -1 on error, and errno will be set
328 */
329drm_public int
330exynos_prime_fd_to_handle(struct exynos_device *dev, int fd, uint32_t *handle)
331{
332	return drmPrimeFDToHandle(dev->fd, fd, handle);
333}
334
335
336
337/*
338 * Request Wireless Display connection or disconnection.
339 *
340 * @dev: a exynos device object.
341 * @connect: indicate whether connectoin or disconnection request.
342 * @ext: indicate whether edid data includes extensions data or not.
343 * @edid: a pointer to edid data from Wireless Display device.
344 *
345 * this interface is used to request Virtual Display driver connection or
346 * disconnection. for this, user should get a edid data from the Wireless
347 * Display device and then send that data to kernel driver with connection
348 * request
349 *
350 * if true, return 0 else negative.
351 */
352drm_public int
353exynos_vidi_connection(struct exynos_device *dev, uint32_t connect,
354		       uint32_t ext, void *edid)
355{
356	struct drm_exynos_vidi_connection req = {
357		.connection	= connect,
358		.extensions	= ext,
359		.edid		= (uint64_t)(uintptr_t)edid,
360	};
361	int ret;
362
363	ret = drmIoctl(dev->fd, DRM_IOCTL_EXYNOS_VIDI_CONNECTION, &req);
364	if (ret) {
365		fprintf(stderr, "failed to request vidi connection[%s].\n",
366				strerror(errno));
367		return ret;
368	}
369
370	return 0;
371}
372
373static void
374exynos_handle_vendor(int fd, struct drm_event *e, void *ctx)
375{
376	struct drm_exynos_g2d_event *g2d;
377	struct exynos_event_context *ectx = ctx;
378
379	switch (e->type) {
380		case DRM_EXYNOS_G2D_EVENT:
381			if (ectx->version < 1 || ectx->g2d_event_handler == NULL)
382				break;
383			g2d = (struct drm_exynos_g2d_event *)e;
384			ectx->g2d_event_handler(fd, g2d->cmdlist_no, g2d->tv_sec,
385						g2d->tv_usec, U642VOID(g2d->user_data));
386			break;
387
388		default:
389			break;
390	}
391}
392
393drm_public int
394exynos_handle_event(struct exynos_device *dev, struct exynos_event_context *ctx)
395{
396	char buffer[1024];
397	int len, i;
398	struct drm_event *e;
399	struct drm_event_vblank *vblank;
400	drmEventContextPtr evctx = &ctx->base;
401
402	/* The DRM read semantics guarantees that we always get only
403	 * complete events. */
404	len = read(dev->fd, buffer, sizeof buffer);
405	if (len == 0)
406		return 0;
407	if (len < (int)sizeof *e)
408		return -1;
409
410	i = 0;
411	while (i < len) {
412		e = (struct drm_event *)(buffer + i);
413		switch (e->type) {
414		case DRM_EVENT_VBLANK:
415			if (evctx->version < 1 ||
416			    evctx->vblank_handler == NULL)
417				break;
418			vblank = (struct drm_event_vblank *) e;
419			evctx->vblank_handler(dev->fd,
420					      vblank->sequence,
421					      vblank->tv_sec,
422					      vblank->tv_usec,
423					      U642VOID (vblank->user_data));
424			break;
425		case DRM_EVENT_FLIP_COMPLETE:
426			if (evctx->version < 2 ||
427			    evctx->page_flip_handler == NULL)
428				break;
429			vblank = (struct drm_event_vblank *) e;
430			evctx->page_flip_handler(dev->fd,
431						 vblank->sequence,
432						 vblank->tv_sec,
433						 vblank->tv_usec,
434						 U642VOID (vblank->user_data));
435			break;
436		default:
437			exynos_handle_vendor(dev->fd, e, evctx);
438			break;
439		}
440		i += e->length;
441	}
442
443	return 0;
444}
445