/* * 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; }