blob: 619d5d3cd0db7fad44aaaf65bd8eb0ae0a745bee [file] [log] [blame]
/*
* Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/
* Copyright(c) 2008 Imagination Technologies Ltd. All rights reserved.
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#define LINUX /* Needed by IMG headers */
#include "pvrmodule.h"
#include "img_defs.h"
#include "servicesext.h"
#include "kernelbuffer.h"
#include "gfx_bc.h"
#include "v4gfx.h"
#define DEVICE_COUNT 1
#define BCLOGNM "v4l2-gfx bc: "
#define BCERR(fmt, arg...) printk(KERN_ERR BCLOGNM fmt, ## arg)
#define BCLOG(fmt, arg...) \
do { \
if (debug >= 1) \
printk(KERN_INFO BCLOGNM fmt, ## arg); \
} while (0)
struct bc_buffer {
u32 size;
unsigned long *paddrp; /* physical addr. array */
PVRSRV_SYNC_DATA *pvr_sync_data;
};
struct gfx_bc_devinfo {
struct bc_buffer bc_buf[VIDEO_MAX_FRAME];
int ref;
int num_bufs;
int ref_cnt;
/* PVR data types */
IMG_UINT32 pvr_id;
BUFFER_INFO pvr_bcinfo;
PVRSRV_BC_SRV2BUFFER_KMJTABLE pvr_s2b_jt;
};
static struct gfx_bc_devinfo *g_devices[DEVICE_COUNT] = { NULL };
static PVRSRV_BC_BUFFER2SRV_KMJTABLE pvr_b2s_jt; /* Jump table from driver to SGX */
/*
* Service to Buffer Device API - this section covers the entry points from
* the SGX kernel services to this driver
*/
static PVRSRV_ERROR s2b_open_bc_device(IMG_UINT32 ui32DeviceID,
IMG_HANDLE *hdevicep)
{
struct gfx_bc_devinfo *devinfo;
BCLOG("+%s %d\n", __func__, (int)ui32DeviceID);
#ifdef MULTIPLEBUFFERCLASSDEVICESUPPORTED
if (ui32DeviceID >= DEVICE_COUNT) {
BCERR("Attempting to open device %d, max device id is %d\n",
ui32DeviceID, DEVICE_COUNT-1);
return -EINVAL;
}
devinfo = g_devices[ui32DeviceID];
#else
devinfo = g_devices[0];
#endif
*hdevicep = (IMG_HANDLE)devinfo;
return PVRSRV_OK;
}
static PVRSRV_ERROR s2b_close_bc_device(IMG_UINT32 ui32DeviceID,
IMG_HANDLE hdevice)
{
PVR_UNREFERENCED_PARAMETER(hdevice);
return PVRSRV_OK;
}
static PVRSRV_ERROR s2b_get_bc_buffer(IMG_HANDLE hdevice,
IMG_UINT32 bufno,
PVRSRV_SYNC_DATA *pvr_sync_data,
IMG_HANDLE *hbufferp)
{
struct gfx_bc_devinfo *devinfo;
BCLOG("+%s\n", __func__);
if (!hdevice || !hbufferp)
return PVRSRV_ERROR_INVALID_PARAMS;
devinfo = (struct gfx_bc_devinfo *) hdevice;
if (bufno < devinfo->pvr_bcinfo.ui32BufferCount) {
devinfo->bc_buf[bufno].pvr_sync_data = pvr_sync_data;
*hbufferp = (IMG_HANDLE) &devinfo->bc_buf[bufno];
} else {
return PVRSRV_ERROR_INVALID_PARAMS;
}
return PVRSRV_OK;
}
static PVRSRV_ERROR s2b_get_bc_info(IMG_HANDLE hdevice, BUFFER_INFO *bcinfop)
{
struct gfx_bc_devinfo *devinfo = NULL;
int rv = 0;
if (!hdevice || !bcinfop) {
rv = PVRSRV_ERROR_INVALID_PARAMS;
} else {
devinfo = (struct gfx_bc_devinfo *) hdevice;
*bcinfop = devinfo->pvr_bcinfo;
BCLOG("ui32BufferCount =%d",
(int)devinfo->pvr_bcinfo.ui32BufferCount);
BCLOG("pixelformat =%d",
(int)devinfo->pvr_bcinfo.pixelformat);
BCLOG("ui32Width =%d",
(int)devinfo->pvr_bcinfo.ui32Width);
BCLOG("ui32Height =%d",
(int)devinfo->pvr_bcinfo.ui32Height);
BCLOG("ui32ByteStride =%d",
(int)devinfo->pvr_bcinfo.ui32ByteStride);
BCLOG("ui32BufferDeviceID =%d",
(int)devinfo->pvr_bcinfo.ui32BufferDeviceID);
BCLOG("ui32Flags = %d",
(int)devinfo->pvr_bcinfo.ui32Flags);
}
BCLOG("-%s %d (0x%x)\n", __func__, rv, (int)devinfo);
return PVRSRV_OK;
}
static PVRSRV_ERROR s2b_get_buffer_addr(IMG_HANDLE hdevice,
IMG_HANDLE hbuffer,
IMG_SYS_PHYADDR **sysaddrpp,
IMG_UINT32 *sizebytesp,
IMG_VOID **cpuvaddrpp,
IMG_HANDLE *osmapinfop,
IMG_BOOL *iscontiguousp,
IMG_UINT32 *pui32TilingStride)
{
struct bc_buffer *bc_buf;
PVRSRV_ERROR rv = PVRSRV_OK;
BCLOG("+%s\n", __func__);
if (!hdevice || !hbuffer || !sysaddrpp || !sizebytesp)
return PVRSRV_ERROR_INVALID_PARAMS;
bc_buf = (struct bc_buffer *)hbuffer;
*cpuvaddrpp = NULL;
*sizebytesp = bc_buf->size;
if (bc_buf->paddrp) {
*iscontiguousp = IMG_FALSE;
*sysaddrpp = (IMG_SYS_PHYADDR *)bc_buf->paddrp;
*osmapinfop = IMG_NULL;
*pui32TilingStride = 0;
BCLOG("+%s paddrp[0] 0x%x, vaddr = 0x%x, sizebytes = %d",
__func__, (int)bc_buf->paddrp[0],
(int)*cpuvaddrpp, (int)*sizebytesp);
} else {
rv = PVRSRV_ERROR_NOT_SUPPORTED;
}
return rv;
}
/*
* Rest of the functions
*/
static PVRSRV_PIXEL_FORMAT v4l2_to_pvr_pixfmt(u32 v4l2pixelfmt)
{
PVRSRV_PIXEL_FORMAT pvr_fmt;
switch (v4l2pixelfmt) {
case V4L2_PIX_FMT_RGB565:
pvr_fmt = PVRSRV_PIXEL_FORMAT_RGB565;
break;
case V4L2_PIX_FMT_RGB32:
pvr_fmt = PVRSRV_PIXEL_FORMAT_RGB888;
break;
case V4L2_PIX_FMT_YUYV:
pvr_fmt = PVRSRV_PIXEL_FORMAT_FOURCC_ORG_YUYV;
break;
case V4L2_PIX_FMT_UYVY:
pvr_fmt = PVRSRV_PIXEL_FORMAT_FOURCC_ORG_UYVY;
break;
case V4L2_PIX_FMT_NV12:
pvr_fmt = PVRSRV_PIXEL_FORMAT_NV12;
break;
default:
pvr_fmt = PVRSRV_PIXEL_FORMAT_UNKNOWN;
}
return pvr_fmt;
}
static int gfx_bc_release_device_resources(int id)
{
struct gfx_bc_devinfo *devinfo;
devinfo = g_devices[id];
if (devinfo == NULL)
return -ENOENT;
if (!devinfo->num_bufs)
return 0;
devinfo->num_bufs = 0;
devinfo->pvr_bcinfo.pixelformat = PVRSRV_PIXEL_FORMAT_UNKNOWN;
devinfo->pvr_bcinfo.ui32Width = 0;
devinfo->pvr_bcinfo.ui32Height = 0;
devinfo->pvr_bcinfo.ui32ByteStride = 0;
devinfo->pvr_bcinfo.ui32BufferDeviceID = id;
devinfo->pvr_bcinfo.ui32Flags = 0;
devinfo->pvr_bcinfo.ui32BufferCount = 0;
return 0;
}
static int gfx_bc_register(int id)
{
struct gfx_bc_devinfo *devinfo;
int rv = 0;
BCLOG("+%s\n", __func__);
devinfo = g_devices[id];
if (devinfo) {
devinfo->ref_cnt++;
BCLOG("%s device already registered\n", __func__);
rv = 0;
goto end;
}
devinfo = (struct gfx_bc_devinfo *)
kzalloc(sizeof(*devinfo), GFP_KERNEL);
if (!devinfo) {
rv = -ENOMEM;
goto end;
}
BCLOG("%s devinfo id=%d addr=0x%x\n", __func__, id, (int)devinfo);
devinfo->pvr_bcinfo.pixelformat = PVRSRV_PIXEL_FORMAT_UNKNOWN;
devinfo->pvr_bcinfo.ui32Width = 0;
devinfo->pvr_bcinfo.ui32Height = 0;
devinfo->pvr_bcinfo.ui32ByteStride = 0;
devinfo->pvr_bcinfo.ui32BufferDeviceID = id;
devinfo->pvr_bcinfo.ui32Flags = 0;
devinfo->pvr_bcinfo.ui32BufferCount = devinfo->num_bufs;
devinfo->pvr_s2b_jt.ui32TableSize =
sizeof(PVRSRV_BC_SRV2BUFFER_KMJTABLE);
devinfo->pvr_s2b_jt.pfnOpenBCDevice = s2b_open_bc_device;
devinfo->pvr_s2b_jt.pfnCloseBCDevice = s2b_close_bc_device;
devinfo->pvr_s2b_jt.pfnGetBCBuffer = s2b_get_bc_buffer;
devinfo->pvr_s2b_jt.pfnGetBCInfo = s2b_get_bc_info;
devinfo->pvr_s2b_jt.pfnGetBufferAddr = s2b_get_buffer_addr;
if (pvr_b2s_jt.pfnPVRSRVRegisterBCDevice(&devinfo->pvr_s2b_jt,
&devinfo->pvr_id) != PVRSRV_OK) {
BCLOG("RegisterBCDevice failed\n");
rv = -EIO;
goto end;
}
BCLOG("my device id: %d\n", (int)devinfo->pvr_id);
devinfo->ref_cnt++;
g_devices[id] = devinfo;
end:
BCLOG("-%s [%d]\n", __func__, rv);
return rv;
}
static int gfx_bc_unregister(int id)
{
int rv = 0;
struct gfx_bc_devinfo *devinfo;
devinfo = g_devices[id];
if (devinfo == NULL) {
rv = -ENODEV;
goto end;
}
devinfo->ref_cnt--;
if (devinfo->ref_cnt) {
rv = -EAGAIN;
goto end;
}
if (pvr_b2s_jt.pfnPVRSRVRemoveBCDevice(devinfo->pvr_id) != PVRSRV_OK) {
rv = -EIO;
goto end;
}
kfree(devinfo);
g_devices[id] = NULL;
end:
return rv;
}
#define FIELDCOPY(dst, src, field) { (dst)->field = (src)->field; }
#define BC_BUF_PARAMS_COPY(dst, src) { \
FIELDCOPY(dst, src, count); \
FIELDCOPY(dst, src, width); \
FIELDCOPY(dst, src, height); \
FIELDCOPY(dst, src, pixel_fmt); \
FIELDCOPY(dst, src, stride); \
FIELDCOPY(dst, src, size); \
}
static void gfx_bc_params2_to_common(struct bc_buf_params2 *p,
struct bc_buf_params_common *pc)
{
BC_BUF_PARAMS_COPY(pc, p);
}
/*
* Validate the bc_buf_params and get the PVR pixel format
*
* We shouldn't need to do any further validation of the V4L2 pixelformat
* properties as this should have been taken care of in the appropriate V4L2
* ioctl handlers.
*/
static int gfx_bc_validateparams(
int id,
struct bc_buf_params_common *p,
struct gfx_bc_devinfo **devinfop,
PVRSRV_PIXEL_FORMAT *pvr_pix_fmtp)
{
struct gfx_bc_devinfo *devinfo;
int rv = 0;
devinfo = g_devices[id];
if (devinfo == NULL) {
BCLOG("%s: no such device %d", __func__, id);
rv = -ENODEV;
}
/* validate a series of params */
if (p->count <= 0) {
BCLOG("%s: invalid count", __func__);
rv = -EINVAL;
}
*pvr_pix_fmtp = v4l2_to_pvr_pixfmt(p->pixel_fmt);
if (*pvr_pix_fmtp == PVRSRV_PIXEL_FORMAT_UNKNOWN) {
BCLOG("%s: invalid pixel format", __func__);
rv = -EINVAL;
}
*devinfop = rv != 0 ? NULL : devinfo;
return rv;
}
/*
* API for the V4L2 component
*/
int bc_init(void)
{
int id, rv;
BCLOG("+%s\n", __func__);
if (!PVRGetBufferClassJTable(&pvr_b2s_jt)) {
BCERR("no jump table to SGX APIs\n");
rv = -EIO;
goto end;
}
for (id = 0; id < DEVICE_COUNT; id++) {
rv = gfx_bc_register(id);
if (rv != 0) {
BCERR("can't register BC service\n");
goto end;
}
}
end:
BCLOG("-%s [%d]\n", __func__, rv);
return rv;
}
void bc_cleanup(void)
{
int id;
for (id = 0; id < DEVICE_COUNT; id++) {
if (gfx_bc_release_device_resources(id) != 0)
BCERR("can't b/c device resources: %d\n", id);
if (gfx_bc_unregister(id) != 0)
BCERR("can't un-register BC service\n");
}
}
int bc_setup_complete(int id, struct bc_buf_params2 *p)
{
/* Fn called after successful bc_setup() so id should be valid */
struct gfx_bc_devinfo *devinfo = g_devices[id];
if (p->count != devinfo->num_bufs) {
BCLOG("+%s: Count doesn't match\n", __func__);
return -ENODEV;
}
return 0;
}
int bc_setup_buffer(int id, struct bc_buf_params2 *p, unsigned long *paddrp)
{
int idx;
/* Fn called after successful bc_setup() so id should be valid */
struct gfx_bc_devinfo *devinfo = g_devices[id];
idx = devinfo->num_bufs;
if (unlikely(idx >= VIDEO_MAX_FRAME))
return -ENOENT;
devinfo->num_bufs++;
devinfo->pvr_bcinfo.ui32BufferCount = devinfo->num_bufs;
memset(&devinfo->bc_buf[idx], 0, sizeof(devinfo->bc_buf[idx]));
devinfo->bc_buf[idx].paddrp = paddrp;
devinfo->bc_buf[idx].size = p->size;
devinfo->bc_buf[idx].pvr_sync_data = IMG_NULL;
return 0;
}
int bc_setup(int id, struct bc_buf_params2 *p)
{
struct gfx_bc_devinfo *devinfo;
int rv = 0;
PVRSRV_PIXEL_FORMAT pvr_pix_fmt;
struct bc_buf_params_common pc;
BCLOG("+%s\n", __func__);
gfx_bc_params2_to_common(p, &pc);
rv = gfx_bc_validateparams(id, &pc, &devinfo, &pvr_pix_fmt);
if (rv != 0)
goto end;
p->stride = 4096; /* Tiler stride */
p->size = p->height * p->stride;
if (p->pixel_fmt == V4L2_PIX_FMT_NV12)
p->size += (p->height / 2) * p->stride; /* UV size */
devinfo->num_bufs = 0; /* See bc_setup_buffer */
devinfo->pvr_bcinfo.pixelformat = pvr_pix_fmt;
devinfo->pvr_bcinfo.ui32Width = p->width;
devinfo->pvr_bcinfo.ui32Height = p->height;
devinfo->pvr_bcinfo.ui32ByteStride = p->stride;
devinfo->pvr_bcinfo.ui32BufferDeviceID = id;
/* I'm not 100% sure these flags are right but here goes */
devinfo->pvr_bcinfo.ui32Flags =
PVRSRV_BC_FLAGS_YUVCSC_FULL_RANGE |
PVRSRV_BC_FLAGS_YUVCSC_BT601;
BCLOG("buffers: count=%d, w=%d, h=%d, stride=%d, sz=%d fmt=%d\n",
p->count, p->width, p->height, p->stride, p->size, pvr_pix_fmt);
end:
BCLOG("-%s [%d]\n", __func__, rv);
return rv;
}
/*
* The caller of this API will ensure that the arguments are valid
*/
int bc_sync_status(int id, int bufidx)
{
struct gfx_bc_devinfo *devinfo = g_devices[id];
int ui32ReadOpsPending, ui32ReadOpsComplete;
ui32ReadOpsPending =
devinfo->bc_buf[bufidx].pvr_sync_data->ui32ReadOpsPending;
ui32ReadOpsComplete =
devinfo->bc_buf[bufidx].pvr_sync_data->ui32ReadOpsComplete;
return ui32ReadOpsComplete == ui32ReadOpsPending ? 1 : 0;
}