android_mt6572_jiabo/hardware/ti/omap4/bltsville/gcbv/gcmain.c
2025-09-05 16:56:03 +08:00

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();
}