/*
* Copyright (c) 2011 Intel Corporation. All Rights Reserved.
* Copyright (c) Imagination Technologies Limited, UK
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sub license, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Authors:
* Binglin Chen <binglin.chen@intel.com>
* Zhaohan Ren <zhaohan.ren@intel.com>
* Shengquan Yuan <shengquan.yuan@intel.com>
*/
#include <stdio.h>
#include <unistd.h>
#include <math.h>
#ifdef ANDROID
#include <drm/ttm/ttm_placement.h>
#include <linux/psb_drm.h>
#else
#include <psb_drm.h>
#endif
#include <va/va_backend.h>
#include <va/va_drmcommon.h>
#include "psb_drv_debug.h"
#include <wsbm/wsbm_manager.h>
#ifndef ANDROID
#include <X11/Xlib.h>
#include "x11/psb_xrandr.h"
#include "x11/psb_x11.h"
#endif
#include "mrst/pvr2d.h"
#include "psb_drv_video.h"
#include "psb_output.h"
#include "psb_surface_ext.h"
#include "psb_texture.h"
#define INIT_DRIVER_DATA psb_driver_data_p driver_data = (psb_driver_data_p) ctx->pDriverData;
#define INIT_OUTPUT_PRIV psb_x11_output_p output = (psb_x11_output_p)(((psb_driver_data_p)ctx->pDriverData)->ws_priv)
#define SURFACE(id) ((object_surface_p) object_heap_lookup( &driver_data->surface_heap, id ))
#define SUBPIC(id) ((object_subpic_p) object_heap_lookup( &driver_data->subpic_heap, id ))
#define Degree (2*PI / 360.0)
#define PI 3.1415927
#define OV_HUE_DEFAULT_VALUE 0
#define OV_HUE_MIN -30
#define OV_HUE_MAX 30
#define OV_BRIGHTNESS_DEFAULT_VALUE 0
#define OV_BRIGHTNESS_MIN -50
#define OV_BRIGHTNESS_MAX 50
#define OV_CONTRAST_DEFAULT_VALUE 0
#define OV_CONTRAST_MIN -100
#define OV_CONTRAST_MAX 100
#define OV_SATURATION_DEFAULT_VALUE 100
#define OV_SATURATION_MIN 0
#define OV_SATURATION_MAX 200
typedef struct _psb_transform_coeffs_ {
double rY, rCb, rCr;
double gY, gCb, gCr;
double bY, bCb, bCr;
} psb_transform_coeffs;
typedef enum _psb_videotransfermatrix {
PSB_VideoTransferMatrixMask = 0x07,
PSB_VideoTransferMatrix_Unknown = 0,
PSB_VideoTransferMatrix_BT709 = 1,
PSB_VideoTransferMatrix_BT601 = 2,
PSB_VideoTransferMatrix_SMPTE240M = 3
} psb_videotransfermatrix;
typedef enum _psb_nominalrange {
PSB_NominalRangeMask = 0x07,
PSB_NominalRange_Unknown = 0,
PSB_NominalRange_Normal = 1,
PSB_NominalRange_Wide = 2,
/* explicit range forms */
PSB_NominalRange_0_255 = 1,
PSB_NominalRange_16_235 = 2,
PSB_NominalRange_48_208 = 3
} psb_nominalrange;
/*
* ITU-R BT.601, BT.709 and SMPTE 240M transfer matrices from VA 2.0
* Video Color Field definitions Design Spec(Version 0.03).
* [R', G', B'] values are in the range [0, 1], Y' is in the range [0,1]
* and [Pb, Pr] components are in the range [-0.5, 0.5].
*/
static psb_transform_coeffs s601 = {
1, -0.000001, 1.402,
1, -0.344136, -0.714136,
1, 1.772, 0
};
static psb_transform_coeffs s709 = {
1, 0, 1.5748,
1, -0.187324, -0.468124,
1, 1.8556, 0
};
static psb_transform_coeffs s240M = {
1, -0.000657, 1.575848,
1, -0.226418, -0.476529,
1, 1.825958, 0.000378
};
static void psb_setup_coeffs(struct psb_texture_s * pPriv);
static void psb_scale_transfermatrix(psb_transform_coeffs * transfer_matrix,
double YColumScale, double CbColumScale,
double CrColumnScale);
static void psb_select_transfermatrix(struct psb_texture_s * pPriv,
psb_transform_coeffs * transfer_matrix,
double *Y_offset, double *CbCr_offset,
double *RGB_offset);
static void psb_create_coeffs(double yOff, double uOff, double vOff, double rgbOff,
double yScale, double uScale, double vScale,
double brightness, double contrast,
double *pYCoeff, double *pUCoeff, double *pVCoeff,
double *pConstant);
static void psb_convert_coeffs(double Ycoeff, double Ucoeff, double Vcoeff,
double ConstantTerm, signed char *pY, signed char *pU,
signed char *pV, signed short *constant,
unsigned char *pShift);
static int psb_check_coeffs(double Ycoeff, double Ucoeff, double Vcoeff,
double ConstantTerm, signed char byShift);
static void
psb_transform_sathuecoeffs(psb_transform_coeffs * dest,
const psb_transform_coeffs * const source,
double fHue, double fSat);
static unsigned long PVRCalculateStride(unsigned long widthInPixels, unsigned int bitsPerPixel, unsigned int stride_alignment)
{
int ulActiveLinelenInPixels = (widthInPixels + (stride_alignment - 1)) & ~(stride_alignment - 1);
return ((ulActiveLinelenInPixels * bitsPerPixel) + 7) >> 3;
}
static int pvr_context_create(unsigned char **pvr_ctx)
{
#ifdef _FOR_FPGA_
return PVR2D_OK;
#endif
int ret = 0;
int pvr_devices = PVR2DEnumerateDevices(0);
PVR2DDEVICEINFO *pvr_devs = NULL;
if ((pvr_devices < PVR2D_OK) || (pvr_devices == 0)) {
drv_debug_msg(VIDEO_DEBUG_ERROR, "%s(): PowerVR device not found", __func__);
goto out;
}
pvr_devs = calloc(1, pvr_devices * sizeof(*pvr_devs));
if (!pvr_devs) {
drv_debug_msg(VIDEO_DEBUG_ERROR, "%s(): not enough memory", __func__);
goto out;
}
ret = PVR2DEnumerateDevices(pvr_devs);
if (ret != PVR2D_OK) {
drv_debug_msg(VIDEO_DEBUG_ERROR, "%s(): PVR2DEnumerateDevices() failed(%d)", __func__,
ret);
goto out;
}
/* Choose the first display device */
ret = PVR2DCreateDeviceContext(pvr_devs[0].ulDevID, (PVR2DCONTEXTHANDLE *)pvr_ctx, 0);
if (ret != PVR2D_OK) {
drv_debug_msg(VIDEO_DEBUG_ERROR, "%s(): PVR2DCreateDeviceContext() failed(%d)", __func__,
ret);
goto out;
}
out:
if (pvr_devs)
free(pvr_devs);
return ret;
}
void psb_fix_drmfd_closesequence(psb_driver_data_p driver_data)
{
driver_data->dup_drm_fd = dup(driver_data->drm_fd);
}
int psb_ctexture_init(VADriverContextP ctx)
{
INIT_DRIVER_DATA;
struct psb_texture_s *texture_priv = &driver_data->ctexture_priv;
int i, ret;
ret = pvr_context_create(&driver_data->hPVR2DContext);
if (ret != PVR2D_OK) {
drv_debug_msg(VIDEO_DEBUG_ERROR, "%s(): null PVR context!!", __func__);
return ret;
}
texture_priv->video_transfermatrix = PSB_VideoTransferMatrix_BT709;
texture_priv->src_nominalrange = PSB_NominalRange_0_255;
texture_priv->dst_nominalrange = PSB_NominalRange_0_255;
texture_priv->brightness.Value = OV_BRIGHTNESS_DEFAULT_VALUE;
texture_priv->brightness.Fraction = 0;
texture_priv->contrast.Value = OV_CONTRAST_DEFAULT_VALUE;
texture_priv->contrast.Fraction = 0;
texture_priv->hue.Value = OV_HUE_DEFAULT_VALUE;
texture_priv->hue.Fraction = 0;
texture_priv->saturation.Value = OV_SATURATION_DEFAULT_VALUE;
texture_priv->saturation.Fraction = 0;
texture_priv->gamma5 = 0xc0c0c0;
texture_priv->gamma4 = 0x808080;
texture_priv->gamma3 = 0x404040;
texture_priv->gamma2 = 0x202020;
texture_priv->gamma1 = 0x101010;
texture_priv->gamma0 = 0x080808;
texture_priv->dri_init_flag = 0;
texture_priv->drawable_update_flag = 0;
texture_priv->extend_dri_init_flag = 0;
texture_priv->current_blt_buffer = 0;
texture_priv->extend_current_blt_buffer = 0;
texture_priv->adjust_window_flag = 0;
texture_priv->destw_save = 0;
texture_priv->desth_save = 0;
texture_priv->local_rotation_save = -1;
texture_priv->extend_rotation_save = -1;
texture_priv->dri_drawable = NULL;
texture_priv->extend_dri_drawable = NULL;
for (i = 0; i < DRI2_BLIT_BUFFERS_NUM; i++) {
texture_priv->blt_meminfo[i] = NULL;
texture_priv->extend_blt_meminfo[i] = NULL;
}
for (i = 0; i < DRI2_FLIP_BUFFERS_NUM; i++)
texture_priv->flip_meminfo[i] = NULL;
texture_priv->blt_meminfo_pixmap = NULL;
for (i = 0; i < 6; i++)
texture_priv->pal_meminfo[i] = NULL;
psb_setup_coeffs(texture_priv);
psb_fix_drmfd_closesequence(driver_data);
return 0;
}
void psb_ctexture_deinit(VADriverContextP ctx)
{
INIT_DRIVER_DATA;
PVR2DERROR ePVR2DStatus;
int i;
struct psb_texture_s *texture_priv = &driver_data->ctexture_priv;
if (texture_priv->blt_meminfo_pixmap) {
ePVR2DStatus = PVR2DMemFree(driver_data->hPVR2DContext, texture_priv->blt_meminfo_pixmap);
if (ePVR2DStatus != PVR2D_OK)
drv_debug_msg(VIDEO_DEBUG_ERROR, "%s: PVR2DMemFree error %d\n", __FUNCTION__, ePVR2DStatus);
}
for (i = 0; i < DRI2_BLIT_BUFFERS_NUM; i++) {
if (texture_priv->blt_meminfo[i]) {
ePVR2DStatus = PVR2DMemFree(driver_data->hPVR2DContext, texture_priv->blt_meminfo[i]);
if (ePVR2DStatus != PVR2D_OK)
drv_debug_msg(VIDEO_DEBUG_ERROR, "%s: PVR2DMemFree error %d\n", __FUNCTION__, ePVR2DStatus);
texture_priv->blt_meminfo[i] = NULL;
}
}
for (i = 0; i < DRI2_FLIP_BUFFERS_NUM; i++) {
if (texture_priv->flip_meminfo[i]) {
ePVR2DStatus = PVR2DMemFree(driver_data->hPVR2DContext, texture_priv->flip_meminfo[i]);
if (ePVR2DStatus != PVR2D_OK)
drv_debug_msg(VIDEO_DEBUG_ERROR, "%s: PVR2DMemFree error %d\n", __FUNCTION__, ePVR2DStatus);
texture_priv->flip_meminfo[i] = NULL;
}
}
for (i = 0; i < DRI2_BLIT_BUFFERS_NUM; i++) {
if (texture_priv->extend_blt_meminfo[i]) {
ePVR2DStatus = PVR2DMemFree(driver_data->hPVR2DContext, texture_priv->extend_blt_meminfo[i]);
if (ePVR2DStatus != PVR2D_OK)
drv_debug_msg(VIDEO_DEBUG_ERROR, "%s: PVR2DMemFree error %d\n", __FUNCTION__, ePVR2DStatus);
texture_priv->extend_blt_meminfo[i] = NULL;
}
}
for (i = 0; i < 6; i++) {
if (texture_priv->pal_meminfo[i]) {
ePVR2DStatus = PVR2DMemFree(driver_data->hPVR2DContext, texture_priv->pal_meminfo[i]);
if (ePVR2DStatus != PVR2D_OK)
drv_debug_msg(VIDEO_DEBUG_ERROR, "%s: PVR2DMemFree error %d\n", __FUNCTION__, ePVR2DStatus);
texture_priv->pal_meminfo[i] = NULL;
}
}
if (driver_data->hPVR2DContext) {
ePVR2DStatus = PVR2DDestroyDeviceContext(driver_data->hPVR2DContext);
if (ePVR2DStatus != PVR2D_OK)
drv_debug_msg(VIDEO_DEBUG_ERROR, "%s: PVR2DMemFree error %d\n", __FUNCTION__, ePVR2DStatus);
driver_data->hPVR2DContext = NULL;
}
if (driver_data->dup_drm_fd)
close(driver_data->dup_drm_fd);
}
/* calculate subpicture size according to the downscale situation of both main and subpicture bitstream */
static void psb_calculate_subpic_size(int surf_width, int surf_height, int dst_w, int dst_h, PsbVASurfaceRec *surface_subpic)
{
float src_h_ratio, src_v_ratio;
float subpic_h_ratio, subpic_v_ratio;
float subpic_h_dest_ratio, subpic_v_dest_ratio;
src_h_ratio = (float)surf_width / dst_w;
src_v_ratio = (float)surf_height / dst_h;
subpic_h_ratio = (float)surface_subpic->subpic_srcw / surface_subpic->subpic_dstw;
subpic_v_ratio = (float)surface_subpic->subpic_srch / surface_subpic->subpic_dsth;
subpic_h_dest_ratio = (float)dst_w / surface_subpic->subpic_dstw;
subpic_v_dest_ratio = (float)dst_h / surface_subpic->subpic_dsth;
if (!(surface_subpic->subpic_flags & VA_SUBPICTURE_DESTINATION_IS_SCREEN_COORD)) {
/* If coordinates are video relative then scale subpicture with video */
surface_subpic->subpic_dstx /= src_h_ratio;
surface_subpic->subpic_dsty /= src_v_ratio;
surface_subpic->subpic_dstx /= subpic_h_ratio;
surface_subpic->subpic_dsty /= subpic_v_ratio;
surface_subpic->subpic_dstw /= src_h_ratio;
surface_subpic->subpic_dsth /= src_v_ratio;
surface_subpic->subpic_dstw /= subpic_h_ratio;
surface_subpic->subpic_dsth /= subpic_v_ratio;
}
}
static PPVR2DMEMINFO psb_check_subpic_buffer(psb_driver_data_p driver_data, PsbVASurfaceRec* surface_subpic)
{
unsigned int i, j;
unsigned char* tmp_buffer;
unsigned char tmp;
PVR2DERROR ePVR2DStatus;
/* Find and return the wrapped buffer index */
for (i = 0; i < VIDEO_BUFFER_NUM; i++) {
if (driver_data->wrapped_subpic_id[i] == surface_subpic->subpic_id && driver_data->subpicBuf[i]) {
return driver_data->subpicBuf[i];
}
}
/* Wrap a un-wrapped buffer and return */
for (i = 0; i < VIDEO_BUFFER_NUM; i++) {
if (driver_data->wrapped_subpic_id[i] == VA_INVALID_ID) {
tmp_buffer = NULL;
tmp_buffer = wsbmBOMap(surface_subpic->bo, WSBM_ACCESS_READ | WSBM_ACCESS_WRITE);
if (NULL == tmp_buffer) {
drv_debug_msg(VIDEO_DEBUG_ERROR, "%s L%d: wsbmBOMap failed!",
__FUNCTION__, __LINE__);
return NULL;
}
for (j = 0; j < surface_subpic->size; j = j + 4096) {
tmp = *(tmp_buffer + j);
if (tmp == 0)
*(tmp_buffer + j) = 0;
}
ePVR2DStatus = PVR2DMemWrap(driver_data->hPVR2DContext,
tmp_buffer,
0,
surface_subpic->size,
NULL,
&driver_data->subpicBuf[i]);
if (ePVR2DStatus != PVR2D_OK) {
drv_debug_msg(VIDEO_DEBUG_ERROR, "%s: PVR2DMemWrap error %d\n", __FUNCTION__, ePVR2DStatus);
return NULL;
}
driver_data->wrapped_subpic_id[i] = surface_subpic->subpic_id;
return driver_data->subpicBuf[i];
}
}
if (i == VIDEO_BUFFER_NUM - 1) {
drv_debug_msg(VIDEO_DEBUG_ERROR, "%s: Out of warpped subpic buffer memory\n", __FUNCTION__);
return NULL;
}
return NULL;
}
void psb_init_surface_pvr2dbuf(psb_driver_data_p driver_data)
{
int i;
for (i = 0; i < VIDEO_BUFFER_NUM; i++) {
driver_data->videoBuf[i] = NULL;
driver_data->subpicBuf[i] = NULL;
driver_data->wrapped_surface_id[i] = VA_INVALID_ID;
driver_data->wrapped_subpic_id[i] = VA_INVALID_ID;
}
}
void psb_free_surface_pvr2dbuf(psb_driver_data_p driver_data)
{
int i;
PVR2DERROR ePVR2DStatus;
for (i = 0; i < VIDEO_BUFFER_NUM; i++) {
if ((driver_data->wrapped_surface_id[i] != VA_INVALID_ID) && driver_data->videoBuf[i]) {
ePVR2DStatus = PVR2DMemFree(driver_data->hPVR2DContext, driver_data->videoBuf[i]);
if (ePVR2DStatus != PVR2D_OK)
drv_debug_msg(VIDEO_DEBUG_ERROR, "%s: PVR2DMemFree error %d\n", __FUNCTION__, ePVR2DStatus);
}
if ((driver_data->wrapped_subpic_id[i] != VA_INVALID_ID) && driver_data->subpicBuf[i]) {
ePVR2DStatus = PVR2DMemFree(driver_data->hPVR2DContext, driver_data->subpicBuf[i]);
if (ePVR2DStatus != PVR2D_OK)
drv_debug_msg(VIDEO_DEBUG_ERROR, "%s: PVR2DMemFree error %d\n", __FUNCTION__, ePVR2DStatus);
}
driver_data->wrapped_surface_id[i] = VA_INVALID_ID;
driver_data->wrapped_subpic_id[i] = -1;
driver_data->videoBuf[i] = NULL;
driver_data->subpicBuf[i] = NULL;
}
}
static PPVR2DMEMINFO psb_wrap_surface_pvr2dbuf(psb_driver_data_p driver_data, VASurfaceID surface)
{
int i, j;
unsigned char* tmp_buffer;
unsigned char tmp;
object_surface_p obj_surface = SURFACE(surface);
psb_surface_p psb_surface;
VAStatus vaStatus = VA_STATUS_SUCCESS;
PVR2DERROR ePVR2DStatus;
CHECK_SURFACE(obj_surface);
psb_surface = obj_surface->psb_surface;
/* Find and return the wrapped buffer index */
for (i = 0; i < VIDEO_BUFFER_NUM; i++) {
if (driver_data->wrapped_surface_id[i] == surface && driver_data->videoBuf[i]) {
return driver_data->videoBuf[i];
}
}
/* Wrap a un-wrapped buffer and return */
for (i = 0; i < VIDEO_BUFFER_NUM; i++) {
if (driver_data->wrapped_surface_id[i] == VA_INVALID_ID) {
tmp_buffer = NULL;
tmp_buffer = wsbmBOMap(psb_surface->buf.drm_buf, WSBM_ACCESS_READ | WSBM_ACCESS_WRITE);
if (NULL == tmp_buffer) {
drv_debug_msg(VIDEO_DEBUG_ERROR, "%s L%d: wsbmBOMap failed!",
__FUNCTION__, __LINE__);
return NULL;
}
for (j = 0; j < psb_surface->size; j = j + 4096) {
tmp = *(tmp_buffer + j);
if (tmp == 0)
*(tmp_buffer + j) = 0;
}
ePVR2DStatus = PVR2DMemWrap(driver_data->hPVR2DContext,
tmp_buffer,
0,
psb_surface->size,
NULL,
&driver_data->videoBuf[i]);
if (ePVR2DStatus != PVR2D_OK) {
drv_debug_msg(VIDEO_DEBUG_ERROR, "%s: PVR2DMemWrap error %d\n", __FUNCTION__, ePVR2DStatus);
}
driver_data->wrapped_surface_id[i] = surface;
return driver_data->videoBuf[i];
}
}
if (i == VIDEO_BUFFER_NUM - 1) {
drv_debug_msg(VIDEO_DEBUG_ERROR, "%s: Out of warpped buffer memory\n", __FUNCTION__);
return NULL;
}
return NULL;
}
#if 0
void psb_putsurface_textureblit(
VADriverContextP ctx, unsigned char *dst, VASurfaceID surface, int src_x, int src_y, int src_w,
int src_h, int dst_x, int dst_y, int dst_w, int dst_h, unsigned int subtitle,
int width, int height,
int src_pitch, struct _WsbmBufferObject * src_buf,
unsigned int placement, int wrap_dst)
{
INIT_DRIVER_DATA;
unsigned int i;
unsigned char *tmp_palette;
struct psb_texture_s *texture_priv = &driver_data->ctexture_priv;
object_surface_p obj_surface;
PsbVASurfaceRec *surface_subpic = NULL;
VAStatus vaStatus = VA_STATUS_SUCCESS;
obj_surface = SURFACE(surface);
PVR2D_VPBLT sBltVP;
PVR2DERROR ePVR2DStatus;
PPVR2DMEMINFO pVaVideoSubpicMemInfo;
PPVR2DMEMINFO pVaVideoMemInfo;
PPVR2DMEMINFO pDstMeminfo;
src_pitch = (src_pitch + 0x3) & ~0x3;
if (NULL == obj_surface) {
drv_debug_msg(VIDEO_DEBUG_ERROR, "%s: Invalid surface ID 0x%08x!\n", __func__, surface);
return;
}
surface_subpic = (PsbVASurfaceRec *)obj_surface->subpictures;
/* check whether we need to update coeffs */
if ((height > 576) &&
(texture_priv->video_transfermatrix != PSB_VideoTransferMatrix_BT709)) {
texture_priv->video_transfermatrix = PSB_VideoTransferMatrix_BT709;
texture_priv->update_coeffs = 1;
} else if ((height <= 576) &&
(texture_priv->video_transfermatrix != PSB_VideoTransferMatrix_BT601)) {
texture_priv->video_transfermatrix = PSB_VideoTransferMatrix_BT601;
texture_priv->update_coeffs = 1;
}
/* prepare coeffs if needed */
memset(&sBltVP, 0, sizeof(PVR2D_VPBLT));
if (texture_priv->update_coeffs == 1) {
psb_setup_coeffs(texture_priv);
sBltVP.psYUVCoeffs = (PPVR2D_YUVCOEFFS) & texture_priv->coeffs;
/* FIXME: is it right? */
sBltVP.bCoeffsGiven = 1;
}
pVaVideoMemInfo = psb_wrap_surface_pvr2dbuf(driver_data, surface);
if (!pVaVideoMemInfo) {
drv_debug_msg(VIDEO_DEBUG_ERROR, "%s: Failed to get source PVR2DMEMINFO!\n", __func__);
return;
}
/* wrap the dest source */
/* FIXME: this is wrap for rgb565 */
if (wrap_dst == 0) {
/* comment out for rebasing to staging
pDstMeminfo = (PPVR2DMEMINFO)dst;
if (IS_MFLD(driver_data))
sBltVP.sDst.Stride = PVRCalculateStride(((struct dri_drawable*)texture_priv->dri_drawable)->width, 32, 8);
sBltVP.sDst.Format = PVR2D_ARGB8888;
*/
drv_debug_msg(VIDEO_DEBUG_ERROR, "%s: Not support ARGB8888!\n", __func__);
return;
} else {
ePVR2DStatus = PVR2DMemWrap(driver_data->hPVR2DContext,
dst,
0,
(dst_w * dst_h * 2),
NULL,
&pDstMeminfo);
if (ePVR2DStatus != PVR2D_OK) {
drv_debug_msg(VIDEO_DEBUG_ERROR, "%s: PVR2DMemWrap error %d\n", __FUNCTION__, ePVR2DStatus);
return;
}
/* FIXME: this wrong, how to get system pitch */
sBltVP.sDst.Stride = dst_w * 2;//align_to(dst_w, 64);
sBltVP.sDst.Format = PVR2D_RGB565;
}
sBltVP.sDst.pSurfMemInfo = pDstMeminfo;
sBltVP.sDst.SurfOffset = 0;
sBltVP.sDst.SurfWidth = dst_w;
sBltVP.sDst.SurfHeight = dst_h;
/* Y plane UV plane */
sBltVP.uiNumLayers = 1;
sBltVP.sSrc->Stride = src_pitch;
sBltVP.sSrc->Format = VA_FOURCC_NV12;
sBltVP.sSrc->SurfWidth = width;
sBltVP.sSrc->SurfHeight = height;
sBltVP.sSrc[0].pSurfMemInfo = pVaVideoMemInfo;
/* FIXME: check for top-bottom */
sBltVP.sSrc->SurfOffset = 0;
/* FIXME: check rotation setting */
/* FIXME: use PVR define */
sBltVP.RotationValue = 1;
/* clip box */
sBltVP.rcDest.left = dst_x;
sBltVP.rcDest.right = dst_x + dst_w;
sBltVP.rcDest.top = dst_y;
sBltVP.rcDest.bottom = dst_y + dst_h;
sBltVP.rcSource->left = src_x;
sBltVP.rcSource->right = src_x + src_w;
sBltVP.rcSource->top = src_y;
sBltVP.rcSource->bottom = src_y + src_h;
if (subtitle == 1 && obj_surface->subpic_count) {
for (i = 0; i < obj_surface->subpic_count; i++) {
sBltVP.uiNumLayers += 1;
psb_calculate_subpic_size(obj_surface->width, obj_surface->height, dst_w, dst_h, surface_subpic);
pVaVideoSubpicMemInfo = psb_check_subpic_buffer(driver_data, surface_subpic);
if (!pVaVideoSubpicMemInfo) {
drv_debug_msg(VIDEO_DEBUG_ERROR, "%s: Failed to get subpic PVR2DMEMINFO!\n", __func__);
return;
}
object_subpic_p obj_subpic = SUBPIC(surface_subpic->subpic_id);
CHECK_SURFACE(obj_subpic);
// sBltVP.AlphaBlendingFunc = PVR2D_ALPHA_OP_GLOBAL;
sBltVP.AlphaBlendingFunc = 3;
sBltVP.subpicGlobalAlpha[i] = obj_subpic->global_alpha;
sBltVP.sSrcSubpic[i].pSurfMemInfo = pVaVideoSubpicMemInfo;
sBltVP.sSrcSubpic[i].SurfOffset = 0;
sBltVP.sSrcSubpic[i].Stride = surface_subpic->stride;
if (surface_subpic->fourcc == VA_FOURCC_AI44)
sBltVP.sSrcSubpic[i].Format = MAKEFOURCC('A', 'I' , '4', '4');
else
sBltVP.sSrcSubpic[i].Format = surface_subpic->fourcc;
sBltVP.sSrcSubpic[i].SurfWidth = surface_subpic->subpic_srcw;
sBltVP.sSrcSubpic[i].SurfHeight = surface_subpic->subpic_srch;
sBltVP.rcSubpicSource[i].left = surface_subpic->subpic_srcx;
sBltVP.rcSubpicSource[i].right = surface_subpic->subpic_srcx + surface_subpic->subpic_srcw;
sBltVP.rcSubpicSource[i].top = surface_subpic->subpic_srcy;
sBltVP.rcSubpicSource[i].bottom = surface_subpic->subpic_srcy + surface_subpic->subpic_srch;
sBltVP.rcSubpicDest[i].left = surface_subpic->subpic_dstx;
sBltVP.rcSubpicDest[i].right = surface_subpic->subpic_dstx + surface_subpic->subpic_dstw;
sBltVP.rcSubpicDest[i].top = surface_subpic->subpic_dsty;
sBltVP.rcSubpicDest[i].bottom = surface_subpic->subpic_dsty + surface_subpic->subpic_dsth;
//only allocate memory once for palette
if (surface_subpic->fourcc == VA_FOURCC_AI44) {
if (!texture_priv->pal_meminfo[i]) {
ePVR2DStatus = PVR2DMemAlloc(driver_data->hPVR2DContext, 16 * sizeof(unsigned int), 0, 0, &texture_priv->pal_meminfo[i]);
if (ePVR2DStatus != PVR2D_OK) {
drv_debug_msg(VIDEO_DEBUG_ERROR, "%s: PVR2DMemAlloc error %d\n", __FUNCTION__, ePVR2DStatus);
return;
}
}
sBltVP.pPalMemInfo[i] = texture_priv->pal_meminfo[i];
tmp_palette = sBltVP.pPalMemInfo[i]->pBase;
memcpy(tmp_palette, surface_subpic->palette_ptr, 16 * sizeof(unsigned int));
sBltVP.PalOffset[i] = 0;
}
surface_subpic = surface_subpic->next;
}
}
//#ifndef ANDROID /* MRST Android not enable this API, uncomment for MRST */
ePVR2DStatus = PVR2DBltVideo(driver_data->hPVR2DContext, &sBltVP);
//#endif
if (ePVR2DStatus != PVR2D_OK)
drv_debug_msg(VIDEO_DEBUG_ERROR, "%s: failed to do PVR2DBltVideo with error code %d\n",
__FUNCTION__, ePVR2DStatus);
if (wrap_dst) {
ePVR2DStatus = PVR2DMemFree(driver_data->hPVR2DContext, pDstMeminfo);
if (ePVR2DStatus != PVR2D_OK)
drv_debug_msg(VIDEO_DEBUG_ERROR, "%s: PVR2DMemFree error %d\n", __FUNCTION__, ePVR2DStatus);
}
driver_data->cur_displaying_surface = VA_INVALID_SURFACE;
driver_data->last_displaying_surface = VA_INVALID_SURFACE;
obj_surface->display_timestamp = 0;
}
#endif
static void
psb_setup_coeffs(struct psb_texture_s * pPriv)
{
double yCoeff, uCoeff, vCoeff, Constant;
double fContrast;
double Y_offset, CbCr_offset, RGB_offset;
int bright_off = 0;
psb_transform_coeffs coeffs, transfer_matrix;
memset(&coeffs, 0, sizeof(psb_transform_coeffs));
memset(&transfer_matrix, 0, sizeof(psb_transform_coeffs));
/* Offsets in the input and output ranges are
* included in the constant of the transform equation
*/
psb_select_transfermatrix(pPriv, &transfer_matrix,
&Y_offset, &CbCr_offset, &RGB_offset);
/*
* It is at this point we should adjust the parameters for the procamp:
* - Brightness is handled as an offset of the Y parameter.
* - Contrast is an adjustment of the Y scale.
* - Saturation is a scaling of the U anc V parameters.
* - Hue is a rotation of the U and V parameters.
*/
bright_off = pPriv->brightness.Value;
fContrast = (pPriv->contrast.Value + 100) / 100.0;
/* Apply hue and saturation correction to transfer matrix */
psb_transform_sathuecoeffs(&coeffs,
&transfer_matrix,
pPriv->hue.Value * Degree,
pPriv->saturation.Value / 100.0);
/* Create coefficients to get component R
* (including brightness and contrast correction)
*/
psb_create_coeffs(-1 * Y_offset, -1 * CbCr_offset, -1 * CbCr_offset,
RGB_offset, coeffs.rY, coeffs.rCb, coeffs.rCr,
bright_off, fContrast, &yCoeff, &uCoeff, &vCoeff,
&Constant);
/* Convert transform operation from floating point to fixed point */
psb_convert_coeffs(yCoeff, uCoeff, vCoeff, Constant, /* input coefficients */
&pPriv->coeffs.rY, &pPriv->coeffs.rU,
&pPriv->coeffs.rV, &pPriv->coeffs.rConst,
&pPriv->coeffs.rShift);
/* Create coefficients to get component G
* (including brightness and contrast correction)
*/
psb_create_coeffs(-1 * Y_offset, -1 * CbCr_offset, -1 * CbCr_offset,
RGB_offset, coeffs.gY, coeffs.gCb, coeffs.gCr,
bright_off, fContrast, &yCoeff, &uCoeff, &vCoeff,
&Constant);
/* Convert transform operation from floating point to fixed point */
psb_convert_coeffs(yCoeff, uCoeff, vCoeff, Constant,
/* tranfer matrix coefficients for G */
&pPriv->coeffs.gY, &pPriv->coeffs.gU,
&pPriv->coeffs.gV, &pPriv->coeffs.gConst,
&pPriv->coeffs.gShift);
/* Create coefficients to get component B
* (including brightness and contrast correction)
*/
psb_create_coeffs(-1 * Y_offset, -1 * CbCr_offset, -1 * CbCr_offset,
RGB_offset, coeffs.bY, coeffs.bCb, coeffs.bCr,
bright_off, fContrast, &yCoeff, &uCoeff, &vCoeff,
&Constant);
/* Convert transform operation from floating point to fixed point */
psb_convert_coeffs(yCoeff, uCoeff, vCoeff, Constant,
/* tranfer matrix coefficients for B */
&pPriv->coeffs.bY, &pPriv->coeffs.bU,
&pPriv->coeffs.bV, &pPriv->coeffs.bConst,
&pPriv->coeffs.bShift);
}
/*
These are the corresponding matrices when using NominalRange_16_235
for the input surface and NominalRange_0_255 for the outpur surface:
static const psb_transform_coeffs s601 = {
1.164, 0, 1.596,
1.164, -0.391, -0.813,
1.164, 2.018, 0
};
static const psb_transform_coeffs s709 = {
1.164, 0, 1.793,
1.164, -0.213, -0.534,
1.164, 2.115, 0
};
static const psb_transform_coeffs s240M = {
1.164, -0.0007, 1.793,
1.164, -0.257, -0.542,
1.164, 2.078, 0.0004
};
*/
/**
* Select which transfer matrix to use in the YUV->RGB conversion.
*/
static void
psb_select_transfermatrix(struct psb_texture_s * pPriv,
psb_transform_coeffs * transfer_matrix,
double *Y_offset, double *CbCr_offset,
double *RGB_offset)
{
double RGB_scale, Y_scale, Cb_scale, Cr_scale;
/*
* Depending on the nominal ranges of the input YUV surface and the output RGB
* surface, it might be needed to perform some scaling on the transfer matrix.
* The excursion in the YUV values implies that the first column of the matrix
* must be divided by the Y excursion, and the second and third columns be
* divided by the U and V excursions respectively. The offset does not affect
* the values of the matrix.
* The excursion in the RGB values implies that all the values in the transfer
* matrix must be multiplied by the value of the excursion.
*
* Example: Conversion of the SMPTE 240M transfer matrix.
*
* Conversion from [Y', Pb, Pr] to [R', G', B'] in the range of [0, 1]. Y' is in
* the range of [0, 1] and Pb and Pr in the range of [-0.5, 0.5].
*
* R' 1 -0.000657 1.575848 Y'
* G' = 1 -0.226418 -0.476529 * Pb
* B' 1 1.825958 0.000378 Pr
*
* Conversion from [Y', Cb, Cr] to {R', G', B'] in the range of [0, 1]. Y' has an
* excursion of 219 and an offset of +16, and CB and CR have excursions of +/-112
* and offset of +128, for a range of 16 through 240 inclusive.
*
* R' 1/219 -0.000657/224 1.575848/224 Y' 16
* G' = 1/219 -0.226418/224 -0.476529/224 * Cb - 128
* B' 1/219 1.825958/224 0.000378/224 Cr 128
*
* Conversion from [Y', Cb, Cr] to R'G'B' in the range [0, 255].
*
* R' 1/219 -0.000657/224 1.575848/224 Y' 16
* G' = 255 * 1/219 -0.226418/224 -0.476529/224 * Cb - 128
* B' 1/219 1.825958/224 0.000378/224 Cr 128
*/
switch (pPriv->src_nominalrange) {
case PSB_NominalRange_0_255:
/* Y has a range of [0, 255], U and V have a range of [0, 255] */
{
double tmp = 0.0;
(void)tmp;
} /* workaroud for float point bug? */
Y_scale = 255.0;
*Y_offset = 0;
Cb_scale = Cr_scale = 255;
*CbCr_offset = 128;
break;
case PSB_NominalRange_16_235:
case PSB_NominalRange_Unknown:
/* Y has a range of [16, 235] and Cb, Cr have a range of [16, 240] */
Y_scale = 219;
*Y_offset = 16;
Cb_scale = Cr_scale = 224;
*CbCr_offset = 128;
break;
case PSB_NominalRange_48_208:
/* Y has a range of [48, 208] and Cb, Cr have a range of [48, 208] */
Y_scale = 160;
*Y_offset = 48;
Cb_scale = Cr_scale = 160;
*CbCr_offset = 128;
break;
default:
/* Y has a range of [0, 1], U and V have a range of [-0.5, 0.5] */
Y_scale = 1;
*Y_offset = 0;
Cb_scale = Cr_scale = 1;
*CbCr_offset = 0;
break;
}
/*
* 8-bit computer RGB, also known as sRGB or "full-scale" RGB, and studio
* video RGB, or "RGB with head-room and toe-room." These are defined as follows:
*
* - Computer RGB uses 8 bits for each sample of red, green, and blue. Black
* is represented by R = G = B = 0, and white is represented by R = G = B = 255.
* - Studio video RGB uses some number of bits N for each sample of red, green,
* and blue, where N is 8 or more. Studio video RGB uses a different scaling
* factor than computer RGB, and it has an offset. Black is represented by
* R = G = B = 16*2^(N-8), and white is represented by R = G = B = 235*2^(N-8).
* However, actual values may fall outside this range.
*/
switch (pPriv->dst_nominalrange) {
case PSB_NominalRange_0_255: // for sRGB
case PSB_NominalRange_Unknown:
/* R, G and B have a range of [0, 255] */
RGB_scale = 255;
*RGB_offset = 0;
break;
case PSB_NominalRange_16_235: // for stRGB
/* R, G and B have a range of [16, 235] */
RGB_scale = 219;
*RGB_offset = 16;
break;
case PSB_NominalRange_48_208: // for Bt.1361 RGB
/* R, G and B have a range of [48, 208] */
RGB_scale = 160;
*RGB_offset = 48;
break;
default:
/* R, G and B have a range of [0, 1] */
RGB_scale = 1;
*RGB_offset = 0;
break;
}
switch (pPriv->video_transfermatrix) {
case PSB_VideoTransferMatrix_BT709:
memcpy(transfer_matrix, &s709, sizeof(psb_transform_coeffs));
break;
case PSB_VideoTransferMatrix_BT601:
memcpy(transfer_matrix, &s601, sizeof(psb_transform_coeffs));
break;
case PSB_VideoTransferMatrix_SMPTE240M:
memcpy(transfer_matrix, &s240M, sizeof(psb_transform_coeffs));
break;
case PSB_VideoTransferMatrix_Unknown:
/*
* Specifies that the video transfer matrix is not specified.
* The default value is BT601 for standard definition (SD) video and BT709
* for high definition (HD) video.
*/
if (1 /*pPriv->sVideoDesc.SampleWidth < 720 */) { /* TODO, width selection */
memcpy(transfer_matrix, &s601, sizeof(psb_transform_coeffs));
} else {
memcpy(transfer_matrix, &s709, sizeof(psb_transform_coeffs));
}
break;
default:
break;
}
if (Y_scale != 1 || Cb_scale != 1 || Cr_scale != 1) {
/* Each column of the transfer matrix has to
* be scaled by the excursion of each component
*/
psb_scale_transfermatrix(transfer_matrix, 1 / Y_scale, 1 / Cb_scale,
1 / Cr_scale);
}
if (RGB_scale != 1) {
/* All the values in the transfer matrix have to be multiplied
* by the excursion of the RGB components
*/
psb_scale_transfermatrix(transfer_matrix, RGB_scale, RGB_scale,
RGB_scale);
}
}
static void
psb_scale_transfermatrix(psb_transform_coeffs * transfer_matrix,
double YColumScale, double CbColumScale,
double CrColumnScale)
{
/* First column of the transfer matrix */
transfer_matrix->rY *= YColumScale;
transfer_matrix->gY *= YColumScale;
transfer_matrix->bY *= YColumScale;
/* Second column of the transfer matrix */
transfer_matrix->rCb *= CbColumScale;
transfer_matrix->gCb *= CbColumScale;
transfer_matrix->bCb *= CbColumScale;
/* Third column of the transfer matrix */
transfer_matrix->rCr *= CrColumnScale;
transfer_matrix->gCr *= CrColumnScale;
transfer_matrix->bCr *= CrColumnScale;
}
/*
* Calculates the coefficintes of a YUV->RGB conversion based on
* the provided basis coefficients (already had HUe and Satu applied).
* Performs brightness and contrast adjustment as well as the required
* offsets to put into correct range for hardware conversion.
*/
static void
psb_create_coeffs(double yOff, double uOff, double vOff, double rgbOff,
double yScale, double uScale, double vScale,
double brightness, double contrast,
double *pYCoeff, double *pUCoeff, double *pVCoeff,
double *pConstant)
{
*pYCoeff = yScale * contrast;
*pUCoeff = uScale * contrast;
*pVCoeff = vScale * contrast;
*pConstant = (((yOff + brightness) * yScale)
+ (uOff * uScale) + (vOff * vScale)) * contrast + rgbOff;
}
/*
* Converts a floating point function in the form
* a*yCoeff + b*uCoeff + c * vCoeff + d
* Into a fixed point function of the forrm
* (a*pY + b * pU + c * pV + constant)>>pShift
*/
static void
psb_convert_coeffs(double Ycoeff, double Ucoeff, double Vcoeff,
double ConstantTerm, signed char *pY, signed char *pU,
signed char *pV, signed short *constant,
unsigned char *pShift)
{
*pShift = 0;
Ycoeff *= 256;
Ucoeff *= 256;
Vcoeff *= 256;
ConstantTerm *= 256;
*pShift = 8;
/*
* What we want to do is scale up the coefficients so that they just fit into their
* allowed bits, so we are using signed maths giving us coefficients can be between +-128.
* The constant can be between =- 32767.
* The divide can be between 0 and 256 (on powers of two only).
* A mathematical approach would be nice, but for simplicity do an iterative compare
* and divide. Until something fits.
*/
while (psb_check_coeffs(Ycoeff, Ucoeff, Vcoeff, ConstantTerm, *pShift)) {
Ycoeff /= 2;
Ucoeff /= 2;
Vcoeff /= 2;
ConstantTerm /= 2;
(*pShift)--;
}
*pY = (signed char)(Ycoeff + 0.5);
*pU = (signed char)(Ucoeff + 0.5);
*pV = (signed char)(Vcoeff + 0.5);
*constant = (signed short)(ConstantTerm + 0.5);
}
/**
* Checks if the specified coefficients are within the ranges required
* and returns true if they are else false.
*/
static int
psb_check_coeffs(double Ycoeff, double Ucoeff, double Vcoeff,
double ConstantTerm, signed char byShift)
{
if ((Ycoeff > 127) || (Ycoeff < -128)) {
return 1;
}
if ((Ucoeff > 127) || (Ucoeff < -128)) {
return 1;
}
if ((Vcoeff > 127) || (Vcoeff < -128)) {
return 1;
}
if ((ConstantTerm > 32766) || (ConstantTerm < -32767)) {
return 1;
}
return 0;
}
static void
psb_transform_sathuecoeffs(psb_transform_coeffs * dest,
const psb_transform_coeffs * const source,
double fHue, double fSat)
{
double fHueSatSin, fHueSatCos;
fHueSatSin = sin(fHue) * fSat;
fHueSatCos = cos(fHue) * fSat;
dest->rY = source->rY;
dest->rCb = source->rCb * fHueSatCos - source->rCr * fHueSatSin;
dest->rCr = source->rCr * fHueSatCos + source->rCb * fHueSatSin;
dest->gY = source->gY;
dest->gCb = source->gCb * fHueSatCos - source->gCr * fHueSatSin;
dest->gCr = source->gCr * fHueSatCos + source->gCb * fHueSatSin;
dest->bY = source->bY;
dest->bCb = source->bCb * fHueSatCos - source->bCr * fHueSatSin;
dest->bCr = source->bCr * fHueSatCos + source->bCb * fHueSatSin;
}