exynos_drm.c revision 5324fb0d
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		struct drm_gem_close req = {
180			.handle = bo->handle,
181		};
182
183		drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
184	}
185
186	free(bo);
187}
188
189
190/*
191 * Get a exynos buffer object from a gem global object name.
192 *
193 * @dev: a exynos device object.
194 * @name: a gem global object name exported by another process.
195 *
196 * this interface is used to get a exynos buffer object from a gem
197 * global object name sent by another process for buffer sharing.
198 *
199 * if true, return a exynos buffer object else NULL.
200 *
201 */
202drm_public struct exynos_bo *
203exynos_bo_from_name(struct exynos_device *dev, uint32_t name)
204{
205	struct exynos_bo *bo;
206	struct drm_gem_open req = {
207		.name = name,
208	};
209
210	bo = calloc(sizeof(*bo), 1);
211	if (!bo) {
212		fprintf(stderr, "failed to allocate bo[%s].\n",
213				strerror(errno));
214		return NULL;
215	}
216
217	if (drmIoctl(dev->fd, DRM_IOCTL_GEM_OPEN, &req)) {
218		fprintf(stderr, "failed to open gem object[%s].\n",
219				strerror(errno));
220		goto err_free_bo;
221	}
222
223	bo->dev = dev;
224	bo->name = name;
225	bo->handle = req.handle;
226
227	return bo;
228
229err_free_bo:
230	free(bo);
231	return NULL;
232}
233
234/*
235 * Get a gem global object name from a gem object handle.
236 *
237 * @bo: a exynos buffer object including gem handle.
238 * @name: a gem global object name to be got by kernel driver.
239 *
240 * this interface is used to get a gem global object name from a gem object
241 * handle to a buffer that wants to share it with another process.
242 *
243 * if true, return 0 else negative.
244 */
245drm_public int exynos_bo_get_name(struct exynos_bo *bo, uint32_t *name)
246{
247	if (!bo->name) {
248		struct drm_gem_flink req = {
249			.handle = bo->handle,
250		};
251		int ret;
252
253		ret = drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_FLINK, &req);
254		if (ret) {
255			fprintf(stderr, "failed to get gem global name[%s].\n",
256					strerror(errno));
257			return ret;
258		}
259
260		bo->name = req.name;
261	}
262
263	*name = bo->name;
264
265	return 0;
266}
267
268drm_public uint32_t exynos_bo_handle(struct exynos_bo *bo)
269{
270	return bo->handle;
271}
272
273/*
274 * Mmap a buffer to user space.
275 *
276 * @bo: a exynos buffer object including a gem object handle to be mmapped
277 *	to user space.
278 *
279 * if true, user pointer mmapped else NULL.
280 */
281drm_public void *exynos_bo_map(struct exynos_bo *bo)
282{
283	if (!bo->vaddr) {
284		struct exynos_device *dev = bo->dev;
285		struct drm_mode_map_dumb arg;
286		void *map = NULL;
287		int ret;
288
289		memset(&arg, 0, sizeof(arg));
290		arg.handle = bo->handle;
291
292		ret = drmIoctl(dev->fd, DRM_IOCTL_MODE_MAP_DUMB, &arg);
293		if (ret) {
294			fprintf(stderr, "failed to map dumb buffer[%s].\n",
295				strerror(errno));
296			return NULL;
297		}
298
299		map = drm_mmap(0, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED,
300				dev->fd, arg.offset);
301
302		if (map != MAP_FAILED)
303			bo->vaddr = map;
304	}
305
306	return bo->vaddr;
307}
308
309/*
310 * Export gem object to dmabuf as file descriptor.
311 *
312 * @dev: exynos device object
313 * @handle: gem handle to export as file descriptor of dmabuf
314 * @fd: file descriptor returned from kernel
315 *
316 * @return: 0 on success, -1 on error, and errno will be set
317 */
318drm_public int
319exynos_prime_handle_to_fd(struct exynos_device *dev, uint32_t handle, int *fd)
320{
321	return drmPrimeHandleToFD(dev->fd, handle, 0, fd);
322}
323
324/*
325 * Import file descriptor into gem handle.
326 *
327 * @dev: exynos device object
328 * @fd: file descriptor of dmabuf to import
329 * @handle: gem handle returned from kernel
330 *
331 * @return: 0 on success, -1 on error, and errno will be set
332 */
333drm_public int
334exynos_prime_fd_to_handle(struct exynos_device *dev, int fd, uint32_t *handle)
335{
336	return drmPrimeFDToHandle(dev->fd, fd, handle);
337}
338
339
340
341/*
342 * Request Wireless Display connection or disconnection.
343 *
344 * @dev: a exynos device object.
345 * @connect: indicate whether connectoin or disconnection request.
346 * @ext: indicate whether edid data includes extensions data or not.
347 * @edid: a pointer to edid data from Wireless Display device.
348 *
349 * this interface is used to request Virtual Display driver connection or
350 * disconnection. for this, user should get a edid data from the Wireless
351 * Display device and then send that data to kernel driver with connection
352 * request
353 *
354 * if true, return 0 else negative.
355 */
356drm_public int
357exynos_vidi_connection(struct exynos_device *dev, uint32_t connect,
358		       uint32_t ext, void *edid)
359{
360	struct drm_exynos_vidi_connection req = {
361		.connection	= connect,
362		.extensions	= ext,
363		.edid		= (uint64_t)(uintptr_t)edid,
364	};
365	int ret;
366
367	ret = drmIoctl(dev->fd, DRM_IOCTL_EXYNOS_VIDI_CONNECTION, &req);
368	if (ret) {
369		fprintf(stderr, "failed to request vidi connection[%s].\n",
370				strerror(errno));
371		return ret;
372	}
373
374	return 0;
375}
376
377static void
378exynos_handle_vendor(int fd, struct drm_event *e, void *ctx)
379{
380	struct drm_exynos_g2d_event *g2d;
381	struct exynos_event_context *ectx = ctx;
382
383	switch (e->type) {
384		case DRM_EXYNOS_G2D_EVENT:
385			if (ectx->version < 1 || ectx->g2d_event_handler == NULL)
386				break;
387			g2d = (struct drm_exynos_g2d_event *)e;
388			ectx->g2d_event_handler(fd, g2d->cmdlist_no, g2d->tv_sec,
389						g2d->tv_usec, U642VOID(g2d->user_data));
390			break;
391
392		default:
393			break;
394	}
395}
396
397drm_public int
398exynos_handle_event(struct exynos_device *dev, struct exynos_event_context *ctx)
399{
400	char buffer[1024];
401	int len, i;
402	struct drm_event *e;
403	struct drm_event_vblank *vblank;
404	drmEventContextPtr evctx = &ctx->base;
405
406	/* The DRM read semantics guarantees that we always get only
407	 * complete events. */
408	len = read(dev->fd, buffer, sizeof buffer);
409	if (len == 0)
410		return 0;
411	if (len < (int)sizeof *e)
412		return -1;
413
414	i = 0;
415	while (i < len) {
416		e = (struct drm_event *)(buffer + i);
417		switch (e->type) {
418		case DRM_EVENT_VBLANK:
419			if (evctx->version < 1 ||
420			    evctx->vblank_handler == NULL)
421				break;
422			vblank = (struct drm_event_vblank *) e;
423			evctx->vblank_handler(dev->fd,
424					      vblank->sequence,
425					      vblank->tv_sec,
426					      vblank->tv_usec,
427					      U642VOID (vblank->user_data));
428			break;
429		case DRM_EVENT_FLIP_COMPLETE:
430			if (evctx->version < 2 ||
431			    evctx->page_flip_handler == NULL)
432				break;
433			vblank = (struct drm_event_vblank *) e;
434			evctx->page_flip_handler(dev->fd,
435						 vblank->sequence,
436						 vblank->tv_sec,
437						 vblank->tv_usec,
438						 U642VOID (vblank->user_data));
439			break;
440		default:
441			exynos_handle_vendor(dev->fd, e, evctx);
442			break;
443		}
444		i += e->length;
445	}
446
447	return 0;
448}
449