libdce: fix refcounting in init/deinit
[gstreamer-omap:vstehles-libdce.git] / libdce.c
1 /*
2  * Copyright (c) 2011, Texas Instruments Incorporated
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * *  Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * *  Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * *  Neither the name of Texas Instruments Incorporated nor the names of
17  *    its contributors may be used to endorse or promote products derived
18  *    from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include <stdlib.h>
34 #include <string.h>
35 #include <stdio.h>
36 #include <unistd.h>
37 #include <stdint.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <fcntl.h>
41 #include <pthread.h>
42 #include <xf86drm.h>
43 #include <omap_drm.h>
44 #include <omap_dce.h>
45 #include <omap_drmif.h>
46 #include <dce.h>
47 #include <xdc/std.h>
48 #include <ti/dce/dce_priv.h>
49 #include <ti/sdo/ce/Engine.h>
50 #include <ti/sdo/ce/video3/viddec3.h>
51 #include <ti/sdo/ce/video2/videnc2.h>
52
53 #ifdef HAVE_X11
54 #  include <X11/Xlib.h>
55 #  include <X11/Xmd.h>
56 #  include <X11/extensions/dri2proto.h>
57 #  include <X11/extensions/dri2.h>
58 #endif
59
60 static int init(void);
61 static void deinit(void);
62
63 static int fd = -1;
64 static struct omap_device *dev;
65 static int ioctl_base;
66 #define CMD(name) (ioctl_base + DRM_OMAP_DCE_##name)
67
68 uint32_t dce_debug = 3;
69
70 void dce_set_fd(int dce_fd) { fd = dce_fd; }
71 int dce_get_fd () { return fd; }
72
73 /*
74  * Memory allocation/mapping
75  */
76
77 /**
78  * Allocate a memory block that can be passed as an argument to any of the
79  * CE functions.
80  */
81 void * dce_alloc(int sz)
82 {
83     struct omap_bo *bo = omap_bo_new(dev, sz + sizeof(MemHeader), OMAP_BO_WC);
84     MemHeader *h;
85
86     if (!bo) {
87         ERROR("allocation failed");
88         return NULL;
89     }
90
91     h = omap_bo_map(bo);
92     memset(H2P(h), 0, sz);
93     h->size = sz;
94     h->bo = bo;
95
96     DEBUG("%p:%p, size=%d, bo=%p", h, H2P(h), h->size, h->bo);
97
98     return H2P(h);
99 }
100
101 /**
102  * Free a block allocated by dce_alloc()
103  */
104 void dce_free(void *ptr)
105 {
106     if (ptr) {
107         MemHeader *h = P2H(ptr);
108         DEBUG("%p:%p, size=%d, bo=%p", h, ptr, h->size, h->bo);
109         omap_bo_del(h->bo);
110     }
111 }
112
113 /**
114  * Translate pointer to handle.. block should have been allocated
115  * with dce_alloc().
116  */
117 static uint32_t virt2bo(void *ptr)
118 {
119     if (ptr) {
120         MemHeader *h = P2H(ptr);
121         DEBUG("%p:%p, size=%d, bo=%p", h, ptr, h->size, h->bo);
122         return omap_bo_handle(h->bo);
123     }
124     return 0;
125 }
126
127 /*
128  * Engine_open:
129  */
130 Engine_Handle Engine_open(String name, Engine_Attrs *attrs, Engine_Error *ec)
131 {
132     Engine_Handle engine = NULL;
133     struct drm_omap_dce_engine_open req = {};
134     int ret;
135
136     if (init()) {
137         ERROR("init failed");
138         goto out;
139     }
140
141     DEBUG(">> name=%s, attrs=%p", name, attrs);
142
143     strncpy(req.name, name, sizeof(req.name));
144
145     ret = drmCommandWriteRead(fd, CMD(ENGINE_OPEN), &req, sizeof(req));
146     if (ret) {
147         ERROR("ioctl failed: %d", ret);
148         goto out;
149     }
150
151     engine = (Engine_Handle)req.eng_handle;
152     *ec = (Engine_Error)req.error_code;
153
154 out:
155     DEBUG("<< engine=%p, ec=%d", engine, *ec);
156     return engine;
157 }
158
159 /*
160  * Engine_close:
161  */
162 Void Engine_close(Engine_Handle engine)
163 {
164     struct drm_omap_dce_engine_close req = {
165             .eng_handle = (uint32_t)engine
166     };
167     int ret;
168
169     DEBUG(">> engine=%p", engine);
170
171     ret = drmCommandWrite(fd, CMD(ENGINE_CLOSE), &req, sizeof(req));
172     if (ret) {
173         ERROR("ioctl failed: %d", ret);
174         goto out;
175     }
176
177 out:
178     DEBUG("<<");
179     deinit();
180 }
181
182 /*
183  * Common codec glue.. function signatures are the same between
184  * VIDDEC3/VIDENC2
185  */
186
187 static void * create(Engine_Handle engine, String name, void *params,
188         enum omap_dce_codec codec_id)
189 {
190     struct drm_omap_dce_codec_create req = {
191             .codec_id = codec_id,
192             .eng_handle = (uint32_t)engine,
193             .sparams_bo = virt2bo(params),
194     };
195     void *codec = NULL;
196     int ret;
197
198     strncpy(req.name, name, sizeof(req.name));
199
200     ret = drmCommandWriteRead(fd, CMD(CODEC_CREATE), &req, sizeof(req));
201     if (ret) {
202         ERROR("ioctl failed: %d", ret);
203         goto out;
204     }
205
206     codec = (void *)req.codec_handle;
207
208 out:
209     return codec;
210 }
211
212 XDAS_Int32 control(void *codec, int id, void *dynParams, void *status)
213 {
214     struct drm_omap_dce_codec_control req = {
215             .codec_handle = (uint32_t)codec,
216             .cmd_id = id,
217             .dparams_bo = virt2bo(dynParams),
218             .status_bo = virt2bo(status),
219     };
220     int ret;
221
222     ret = drmCommandWriteRead(fd, CMD(CODEC_CONTROL), &req, sizeof(req));
223     if (ret) {
224         ERROR("ioctl failed: %d", ret);
225         goto out;
226     }
227
228     ret = req.result;
229
230 out:
231     return ret;
232 }
233
234 XDAS_Int32 process(void *codec, void *inBufs, void *outBufs,
235         void *inArgs, void *outArgs)
236 {
237     struct drm_omap_dce_codec_process req = {
238             .codec_handle = (uint32_t)codec,
239             .out_args_bo = virt2bo(outArgs),
240             .in_args = (uint64_t)inArgs,
241             .out_bufs = (uint64_t)outBufs,
242             .in_bufs = (uint64_t)inBufs,
243     };
244     int ret;
245
246     ret = drmCommandWriteRead(fd, CMD(CODEC_PROCESS), &req, sizeof(req));
247     if (ret) {
248         ERROR("ioctl failed: %d", ret);
249         goto out;
250     }
251
252     ret = req.result;
253
254 out:
255     return ret;
256 }
257
258 void delete(void *codec)
259 {
260     struct drm_omap_dce_codec_delete req = {
261             .codec_handle = (uint32_t)codec,
262     };
263     int ret;
264
265     ret = drmCommandWrite(fd, CMD(CODEC_DELETE), &req, sizeof(req));
266     if (ret) {
267         ERROR("ioctl failed: %d", ret);
268     }
269 }
270
271
272 /*
273  * VIDDEC3_create
274  */
275 VIDDEC3_Handle VIDDEC3_create(Engine_Handle engine, String name,
276         VIDDEC3_Params *params)
277 {
278     VIDDEC3_Handle codec;
279     DEBUG(">> engine=%p, name=%s, params=%p", engine, name, params);
280     codec = create(engine, name, params, OMAP_DCE_VIDDEC3);
281     DEBUG("<< codec=%p", codec);
282     return codec;
283 }
284
285 /*
286  * VIDDEC3_control
287  */
288 XDAS_Int32 VIDDEC3_control(VIDDEC3_Handle codec, VIDDEC3_Cmd id,
289         VIDDEC3_DynamicParams *dynParams, VIDDEC3_Status *status)
290 {
291     XDAS_Int32 ret;
292     DEBUG(">> codec=%p, id=%d, dynParams=%p, status=%p",
293             codec, id, dynParams, status);
294     ret = control(codec, id, dynParams, status);
295     DEBUG("<< ret=%d", ret);
296     return ret;
297 }
298
299 /*
300  * VIDDEC3_process
301  */
302 XDAS_Int32 VIDDEC3_process(VIDDEC3_Handle codec,
303         XDM2_BufDesc *inBufs, XDM2_BufDesc *outBufs,
304         VIDDEC3_InArgs *inArgs, VIDDEC3_OutArgs *outArgs)
305 {
306     XDAS_Int32 ret;
307     DEBUG(">> codec=%p, inBufs=%p, outBufs=%p, inArgs=%p, outArgs=%p",
308             codec, inBufs, outBufs, inArgs, outArgs);
309     ret = process(codec, inBufs, outBufs, inArgs, outArgs);
310     DEBUG("<< ret=%d", ret);
311     return ret;
312 }
313
314 /*
315  * VIDDEC3_delete
316  */
317 Void VIDDEC3_delete(VIDDEC3_Handle codec)
318 {
319     DEBUG(">> codec=%p", codec);
320     delete(codec);
321     DEBUG("<<");
322 }
323
324 /*
325  * VIDENC2_create
326  */
327 VIDENC2_Handle VIDENC2_create(Engine_Handle engine, String name,
328         VIDENC2_Params *params)
329 {
330     VIDENC2_Handle codec;
331     DEBUG(">> engine=%p, name=%s, params=%p", engine, name, params);
332     codec = create(engine, name, params, OMAP_DCE_VIDENC2);
333     DEBUG("<< codec=%p", codec);
334     return codec;
335 }
336
337 /*
338  * VIDENC2_control
339  */
340 XDAS_Int32 VIDENC2_control(VIDENC2_Handle codec, VIDENC2_Cmd id,
341         VIDENC2_DynamicParams *dynParams, VIDENC2_Status *status)
342 {
343     XDAS_Int32 ret;
344     DEBUG(">> codec=%p, id=%d, dynParams=%p, status=%p",
345             codec, id, dynParams, status);
346     ret = control(codec, id, dynParams, status);
347     DEBUG("<< ret=%d", ret);
348     return ret;
349 }
350
351 /*
352  * VIDENC2_process
353  */
354 XDAS_Int32 VIDENC2_process(VIDENC2_Handle codec,
355         IVIDEO2_BufDesc *inBufs, XDM2_BufDesc *outBufs,
356         VIDENC2_InArgs *inArgs, VIDENC2_OutArgs *outArgs)
357 {
358     XDAS_Int32 ret;
359     DEBUG(">> codec=%p, inBufs=%p, outBufs=%p, inArgs=%p, outArgs=%p",
360             codec, inBufs, outBufs, inArgs, outArgs);
361     ret = process(codec, inBufs, outBufs, inArgs, outArgs);
362     DEBUG("<< ret=%d", ret);
363     return ret;
364 }
365
366 /*
367  * VIDDEC3_delete
368  */
369 Void VIDENC2_delete(VIDENC2_Handle codec)
370 {
371     DEBUG(">> codec=%p", codec);
372     delete(codec);
373     DEBUG("<<");
374 }
375
376 /*
377  * Startup/Shutdown/Cleanup
378  */
379
380 struct omap_device * dce_init(void)
381 {
382     if (init())
383         return NULL;
384     return dev;
385 }
386
387 void dce_deinit(struct omap_device *dev)
388 {
389     deinit();
390 }
391
392 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
393 static int count = 0;
394
395 static int init(void)
396 {
397 #ifdef HAVE_X11
398     Display *dpy;
399     Window root;
400     drm_magic_t magic;
401     int eventBase, errorBase, major, minor;
402     char *driver, *device;
403 #endif
404
405     pthread_mutex_lock(&mutex);
406
407     if (count++ > 0) {
408         goto out;
409     }
410
411 #ifdef HAVE_X11
412     INFO("attempting to open X11 connection");
413     dpy = XOpenDisplay(NULL);
414     if (!dpy) {
415         ERROR("Could not open display");
416         goto no_x11;
417     }
418
419     if (!DRI2InitDisplay(dpy, NULL)) {
420         ERROR("DRI2InitDisplay failed");
421         goto no_x11;
422     }
423
424     if (!DRI2QueryExtension(dpy, &eventBase, &errorBase)) {
425         ERROR("DRI2QueryExtension failed");
426         goto no_x11;
427     }
428
429     DEBUG("DRI2QueryExtension: eventBase=%d, errorBase=%d", eventBase, errorBase);
430
431     if (!DRI2QueryVersion(dpy, &major, &minor)) {
432         ERROR("DRI2QueryVersion failed");
433         goto no_x11;
434     }
435
436     DEBUG("DRI2QueryVersion: major=%d, minor=%d", major, minor);
437
438     root = RootWindow(dpy, DefaultScreen(dpy));
439
440     if (!DRI2Connect(dpy, root, DRI2DriverDRI, &driver, &device)) {
441         DEBUG("DRI2Connect failed");
442         goto no_x11;
443     }
444
445     DEBUG("DRI2Connect: driver=%s, device=%s", driver, device);
446
447     /* only open the device if we don't already have an fd.. see
448      * dce_set_fd().  Need to sort out a better way to handle this
449      * but GEM buffer handles are only valid within the context of
450      * a given file-open.  Switching to dmabuf or flink handles
451      * would solve this.
452      */
453     if (fd == -1) {
454         fd = open(device, O_RDWR);
455         if (fd < 0) {
456             ERROR("open failed");
457             goto no_x11_free;
458         }
459     }
460
461     if (drmGetMagic(fd, &magic)) {
462         ERROR("drmGetMagic failed");
463         goto no_x11_free;
464     }
465
466     if (!DRI2Authenticate(dpy, root, magic)) {
467         ERROR("DRI2Authenticate failed");
468         goto no_x11_free;
469     }
470
471 no_x11_free:
472     XFree(driver);
473     XFree(device);
474 no_x11:
475 #endif
476
477     if (fd == -1) {
478         INFO("no X11, fallback to opening DRM device directly");
479         fd = drmOpen("omapdrm", "platform:omapdrm:00");
480     }
481
482     if (fd >= 0) {
483         struct drm_omap_get_base req = {};
484         int ret;
485
486         strcpy(req.plugin_name, "dce");
487
488         ret = drmCommandWriteRead(fd, DRM_OMAP_GET_BASE, &req, sizeof(req));
489         if (ret) {
490             ERROR("could not get plugin ioctl base: %d", ret);
491             close(fd);
492             fd = -1;
493             goto out;
494         }
495
496         ioctl_base = req.ioctl_base;
497
498         dev = omap_device_new(fd);
499     }
500
501 out:
502     pthread_mutex_unlock(&mutex);
503     return (fd >= 0) ? 0 : -1;
504 }
505
506 static void deinit(void)
507 {
508     pthread_mutex_lock(&mutex);
509
510     if (--count > 0) {
511         goto out;
512     }
513
514     omap_device_del(dev);
515     dev = NULL;
516     close(fd);
517     fd = -1;
518
519 out:
520     pthread_mutex_unlock(&mutex);
521 }