531 lines
12 KiB
C
531 lines
12 KiB
C
/*
|
|
* Copyright (c) 2012,
|
|
* Texas Instruments, Inc. and Vivante Corporation
|
|
*
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* * Neither the name of Texas Instruments, Inc. nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL TEXAS INSTRUMENTS, INC. BE LIABLE FOR ANY
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "gcmain.h"
|
|
#include "gcbv.h"
|
|
#include <semaphore.h>
|
|
|
|
#if ANDROID
|
|
#include <cutils/log.h>
|
|
#include <cutils/process_name.h>
|
|
#endif
|
|
|
|
#define GCZONE_NONE 0
|
|
#define GCZONE_ALL (~0U)
|
|
#define GCZONE_INIT (1 << 0)
|
|
#define GCZONE_CALLBACK (1 << 1)
|
|
|
|
GCDBG_FILTERDEF(gcmain, GCZONE_NONE,
|
|
"init",
|
|
"callback")
|
|
|
|
|
|
static int g_handle;
|
|
|
|
|
|
/*******************************************************************************
|
|
* Callback manager.
|
|
*/
|
|
enum gccallbackinfo_status {
|
|
UNINIT,
|
|
SUPPORTED,
|
|
UNSUPPORTED
|
|
};
|
|
|
|
static const char * const g_statusNames[] = {
|
|
"UNINIT",
|
|
"SUPPORTED",
|
|
"UNSUPPORTED"
|
|
};
|
|
|
|
struct gccallbackinfo {
|
|
/* Callback status */
|
|
enum gccallbackinfo_status status;
|
|
|
|
/* Callback handle. */
|
|
unsigned long handle;
|
|
|
|
/* Termination semaphore. */
|
|
sem_t stop;
|
|
|
|
/* Callback thread handle. */
|
|
pthread_t thread;
|
|
|
|
/* Start/stop mutex */
|
|
pthread_mutex_t mutex;
|
|
};
|
|
|
|
struct gccallbackinfo g_callbackinfo = {
|
|
.status = UNINIT
|
|
};
|
|
|
|
static void *callbackthread(void *_gccallbackinfo)
|
|
{
|
|
struct gccallbackinfo *gccallbackinfo;
|
|
struct gcicallbackwait gccmdcallbackwait;
|
|
int result;
|
|
|
|
/* Get callback info. */
|
|
gccallbackinfo = (struct gccallbackinfo *) _gccallbackinfo;
|
|
|
|
/* Initialize the command. */
|
|
gccmdcallbackwait.handle = gccallbackinfo->handle;
|
|
gccmdcallbackwait.timeoutms = 2000;
|
|
|
|
/* Enter wait loop. */
|
|
while (1) {
|
|
/* Call the kernel to wait for callback event. */
|
|
result = ioctl(g_handle, GCIOCTL_CALLBACK_WAIT,
|
|
&gccmdcallbackwait);
|
|
if (result == 0) {
|
|
if (gccmdcallbackwait.gcerror == GCERR_NONE) {
|
|
/* Work completed. */
|
|
GCDBG(GCZONE_CALLBACK,
|
|
"callback 0x%08X(0x%08X).\n",
|
|
(unsigned int)
|
|
gccmdcallbackwait.callback,
|
|
(unsigned int)
|
|
gccmdcallbackwait.callbackparam);
|
|
|
|
/* Invoke the callback. */
|
|
gccmdcallbackwait.callback(
|
|
gccmdcallbackwait.callbackparam);
|
|
} else if (gccmdcallbackwait.gcerror == GCERR_TIMEOUT) {
|
|
/* Timeout. */
|
|
GCDBG(GCZONE_CALLBACK,
|
|
"callback wait timeout.\n");
|
|
} else {
|
|
/* Error occurred. */
|
|
GCERR("callback wait failed (0x%08X).\n",
|
|
gccmdcallbackwait.gcerror);
|
|
break;
|
|
}
|
|
} else if (result != -EINTR) {
|
|
GCERR("callback wait ioctl failed (%d).\n", result);
|
|
break;
|
|
}
|
|
|
|
/* Stop requested? */
|
|
if (sem_trywait(&gccallbackinfo->stop) == 0) {
|
|
GCDBG(GCZONE_CALLBACK, "terminating.\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int callback_start(struct gccallbackinfo *gccallbackinfo)
|
|
{
|
|
int result = 0;
|
|
struct gcicallback gccmdcallback;
|
|
|
|
GCENTER(GCZONE_CALLBACK);
|
|
|
|
pthread_mutex_lock(&gccallbackinfo->mutex);
|
|
|
|
if (gccallbackinfo->status != UNINIT) {
|
|
pthread_mutex_unlock(&gccallbackinfo->mutex);
|
|
return 0;
|
|
}
|
|
|
|
gccmdcallback.handle = 0;
|
|
|
|
gccallbackinfo->status =
|
|
#if ANDROID
|
|
/* The Android zygote process refuses to fork if there is
|
|
* more than one thread present. */
|
|
(strcmp(get_process_name(), "zygote") == 0) ? UNSUPPORTED :
|
|
#endif
|
|
SUPPORTED;
|
|
|
|
GCDBG(GCZONE_CALLBACK, "callback status: %s\n",
|
|
g_statusNames[gccallbackinfo->status]);
|
|
|
|
if (gccallbackinfo->status == SUPPORTED) {
|
|
/* Initialize callback. */
|
|
result = ioctl(g_handle,
|
|
GCIOCTL_CALLBACK_ALLOC,
|
|
&gccmdcallback);
|
|
if (result != 0) {
|
|
GCERR("callback ioctl failed (%d).\n", result);
|
|
goto fail;
|
|
}
|
|
|
|
if (gccmdcallback.gcerror != GCERR_NONE) {
|
|
GCERR("failed to initialize callback "
|
|
"mechanism (0x%08X).\n",
|
|
gccmdcallback.gcerror);
|
|
goto fail;
|
|
}
|
|
|
|
gccallbackinfo->handle = gccmdcallback.handle;
|
|
|
|
/* Initialize the termination semaphore. */
|
|
result = sem_init(&gccallbackinfo->stop, 0, 0);
|
|
if (result != 0) {
|
|
GCERR("callback semaphore init failed (%d).\n", result);
|
|
goto fail;
|
|
}
|
|
|
|
/* Start the thread. */
|
|
result = pthread_create(&gccallbackinfo->thread, NULL,
|
|
callbackthread, gccallbackinfo);
|
|
if (result != 0) {
|
|
GCERR("failed to start callback thread.\n");
|
|
goto fail;
|
|
}
|
|
|
|
gccmdcallback.handle = 0;
|
|
}
|
|
|
|
fail:
|
|
if (gccmdcallback.handle != 0) {
|
|
ioctl(g_handle, GCIOCTL_CALLBACK_FREE, &gccmdcallback);
|
|
gccallbackinfo->handle = 0;
|
|
}
|
|
|
|
pthread_mutex_unlock(&gccallbackinfo->mutex);
|
|
|
|
GCEXITARG(GCZONE_CALLBACK, "result=%d", result);
|
|
return result;
|
|
}
|
|
|
|
static void callback_stop(struct gccallbackinfo *gccallbackinfo)
|
|
{
|
|
struct gcicallback gccmdcallback;
|
|
|
|
GCENTER(GCZONE_CALLBACK);
|
|
|
|
pthread_mutex_lock(&gccallbackinfo->mutex);
|
|
|
|
if (gccallbackinfo->status == SUPPORTED) {
|
|
if (gccallbackinfo->thread) {
|
|
sem_post(&gccallbackinfo->stop);
|
|
pthread_kill(gccallbackinfo->thread, SIGINT);
|
|
|
|
GCDBG(GCZONE_CALLBACK,
|
|
"waiting to join callback thread...\n");
|
|
|
|
pthread_join(gccallbackinfo->thread, NULL);
|
|
gccallbackinfo->thread = 0;
|
|
}
|
|
|
|
/* Free kernel resources. */
|
|
gccmdcallback.handle = gccallbackinfo->handle;
|
|
ioctl(g_handle, GCIOCTL_CALLBACK_FREE, &gccmdcallback);
|
|
gccallbackinfo->handle = 0;
|
|
}
|
|
|
|
gccallbackinfo->status == UNINIT;
|
|
|
|
pthread_mutex_unlock(&gccallbackinfo->mutex);
|
|
|
|
GCEXIT(GCZONE_CALLBACK);
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* IOCTL wrappers.
|
|
*/
|
|
|
|
#if GCDEBUG_ENABLE && 0
|
|
#define GCPRINTDELAY() sleep(1)
|
|
#else
|
|
#define GCPRINTDELAY()
|
|
#endif
|
|
|
|
void gc_getcaps_wrapper(struct gcicaps *gcicaps)
|
|
{
|
|
int result;
|
|
|
|
GCPRINTDELAY();
|
|
|
|
result = ioctl(g_handle, GCIOCTL_GETCAPS, gcicaps);
|
|
if (result != 0) {
|
|
GCERR("ioctl failed (%d).\n", result);
|
|
gcicaps->gcerror = GCERR_IOCTL;
|
|
}
|
|
}
|
|
|
|
void gc_map_wrapper(struct gcimap *gcmap)
|
|
{
|
|
int result;
|
|
|
|
GCPRINTDELAY();
|
|
result = ioctl(g_handle, GCIOCTL_MAP, gcmap);
|
|
|
|
if (result != 0) {
|
|
GCERR("ioctl failed (%d).\n", result);
|
|
gcmap->gcerror = GCERR_IOCTL;
|
|
}
|
|
}
|
|
|
|
void gc_unmap_wrapper(struct gcimap *gcmap)
|
|
{
|
|
int result;
|
|
|
|
GCPRINTDELAY();
|
|
result = ioctl(g_handle, GCIOCTL_UNMAP, gcmap);
|
|
|
|
if (result != 0) {
|
|
GCERR("ioctl failed (%d).\n", result);
|
|
gcmap->gcerror = GCERR_IOCTL;
|
|
}
|
|
}
|
|
|
|
void gc_commit_wrapper(struct gcicommit *gccommit)
|
|
{
|
|
int result;
|
|
|
|
GCPRINTDELAY();
|
|
|
|
/* Callback start is delayed until needed to handle a case
|
|
* where it's unsupported on Android. */
|
|
if (gccommit->callback)
|
|
callback_start(&g_callbackinfo);
|
|
|
|
gccommit->handle = g_callbackinfo.handle;
|
|
result = ioctl(g_handle, GCIOCTL_COMMIT, gccommit);
|
|
|
|
if (result != 0) {
|
|
GCERR("ioctl failed (%d).\n", result);
|
|
gccommit->gcerror = GCERR_IOCTL;
|
|
}
|
|
}
|
|
|
|
void gc_callback_wrapper(struct gcicallbackarm *gcicallbackarm)
|
|
{
|
|
int result;
|
|
|
|
GCPRINTDELAY();
|
|
|
|
callback_start(&g_callbackinfo);
|
|
|
|
gcicallbackarm->handle = g_callbackinfo.handle;
|
|
result = ioctl(g_handle, GCIOCTL_CALLBACK_ARM, gcicallbackarm);
|
|
if (result != 0) {
|
|
GCERR("ioctl failed (%d).\n", result);
|
|
gcicallbackarm->gcerror = GCERR_IOCTL;
|
|
}
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* Convert floating point in 0..1 range to an 8-bit value in range 0..255.
|
|
*/
|
|
|
|
union gcfp {
|
|
struct {
|
|
unsigned int mantissa:23;
|
|
unsigned int exponent:8;
|
|
unsigned int sign:1;
|
|
} comp;
|
|
|
|
float value;
|
|
};
|
|
|
|
unsigned char gcfp2norm8(float value)
|
|
{
|
|
union gcfp gcfp;
|
|
int exponent;
|
|
unsigned int mantissa;
|
|
int shift;
|
|
|
|
/* Get access to components. */
|
|
gcfp.value = value;
|
|
|
|
/* Clamp negatives. */
|
|
if (gcfp.comp.sign)
|
|
return 0;
|
|
|
|
/* Get unbiased exponent. */
|
|
exponent = (int) gcfp.comp.exponent - 127;
|
|
|
|
/* Clamp if too large. */
|
|
if (exponent >= 0)
|
|
return 255;
|
|
|
|
/* Clamp if too small. */
|
|
if (exponent < -8)
|
|
return 0;
|
|
|
|
/* Determine the shift value. */
|
|
shift = (23 - 8) - exponent;
|
|
|
|
/* Compute the mantissa. */
|
|
mantissa = (gcfp.comp.mantissa | 0x00800000) >> shift;
|
|
|
|
/* Normalize. */
|
|
mantissa = (mantissa * 255) >> 8;
|
|
|
|
return (unsigned char) mantissa;
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* Surface allocation.
|
|
*/
|
|
|
|
enum bverror allocate_surface(struct bvbuffdesc **bvbuffdesc,
|
|
void **buffer,
|
|
unsigned int size)
|
|
{
|
|
enum bverror bverror = BVERR_NONE;
|
|
struct bvbuffdesc *tempbuffdesc = NULL;
|
|
void *tempbuff = NULL;
|
|
unsigned long base;
|
|
|
|
/* Allocate surface buffer descriptor. */
|
|
tempbuffdesc = gcalloc(struct bvbuffdesc, sizeof(struct bvbuffdesc));
|
|
if (tempbuffdesc == NULL) {
|
|
BVSETERROR(BVERR_OOM, "failed to allocate surface");
|
|
goto exit;
|
|
}
|
|
|
|
/* Initialize buffer descriptor. */
|
|
tempbuffdesc->structsize = sizeof(struct bvbuffdesc);
|
|
tempbuffdesc->virtaddr = NULL;
|
|
tempbuffdesc->length = size;
|
|
tempbuffdesc->map = NULL;
|
|
tempbuffdesc->auxtype = BVAT_NONE;
|
|
tempbuffdesc->auxptr = NULL;
|
|
|
|
/* Allocate the surface. */
|
|
tempbuff = gcalloc(void, size + GC_MAX_BASE_ALIGN);
|
|
if (tempbuff == NULL) {
|
|
BVSETERROR(BVERR_OOM, "failed to allocate surface");
|
|
goto exit;
|
|
}
|
|
|
|
/* Align the base address. */
|
|
tempbuffdesc->virtaddr
|
|
= (void *) (((unsigned long) tempbuff + GC_MAX_BASE_ALIGN - 1)
|
|
& ~(GC_MAX_BASE_ALIGN - 1));
|
|
|
|
/* Set return pointers. */
|
|
*bvbuffdesc = tempbuffdesc;
|
|
*buffer = tempbuff;
|
|
return BVERR_NONE;
|
|
|
|
exit:
|
|
free_surface(tempbuffdesc, tempbuff);
|
|
return bverror;
|
|
}
|
|
|
|
void free_surface(struct bvbuffdesc *bvbuffdesc,
|
|
void *buffer)
|
|
{
|
|
gcfree(buffer);
|
|
gcfree(bvbuffdesc);
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* Cache operation wrapper.
|
|
*/
|
|
|
|
enum bverror gcbvcacheop(int count, struct c2dmrgn rgn[],
|
|
enum bvcacheop cacheop)
|
|
{
|
|
int result;
|
|
struct gcicache xfer;
|
|
|
|
if ((count < 0) || (count > 3))
|
|
return BVERR_CACHEOP;
|
|
|
|
xfer.count = count;
|
|
xfer.dir = cacheop;
|
|
memcpy(xfer.rgn, rgn, count * sizeof(struct c2dmrgn));
|
|
|
|
GCPRINTDELAY();
|
|
result = ioctl(g_handle, GCIOCTL_CACHE, &xfer);
|
|
|
|
if (result != 0)
|
|
GCERR("ioctl failed (%d).\n", result);
|
|
|
|
return BVERR_NONE;
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
* Device init/cleanup.
|
|
*/
|
|
|
|
void __attribute__((constructor)) dev_init(void)
|
|
{
|
|
char *env;
|
|
|
|
env = getenv("GCBV_DEBUG");
|
|
if (env && (atol(env) != 0))
|
|
GCDBG_ENABLEDUMP();
|
|
|
|
GCDBG_INIT();
|
|
GCDBG_REGISTER(gcmain);
|
|
|
|
GCENTER(GCZONE_INIT);
|
|
|
|
g_handle = open("/dev/gcioctl", O_RDWR);
|
|
if (g_handle == -1) {
|
|
GCERR("failed to open device (%d).\n", errno);
|
|
goto fail;
|
|
}
|
|
|
|
bv_init();
|
|
|
|
pthread_mutex_init(&g_callbackinfo.mutex, 0);
|
|
|
|
GCEXIT(GCZONE_INIT);
|
|
return;
|
|
|
|
fail:
|
|
if (g_handle > 0) {
|
|
close(g_handle);
|
|
g_handle = 0;
|
|
}
|
|
|
|
GCEXIT(GCZONE_INIT);
|
|
}
|
|
|
|
void __attribute__((destructor)) dev_exit(void)
|
|
{
|
|
GCENTER(GCZONE_INIT);
|
|
|
|
bv_exit();
|
|
callback_stop(&g_callbackinfo);
|
|
|
|
if (g_handle != 0) {
|
|
close(g_handle);
|
|
g_handle = 0;
|
|
}
|
|
|
|
GCEXIT(GCZONE_INIT);
|
|
GCDBG_EXIT();
|
|
}
|
|
|