blob: 99378a99d2d92c3ac4c85007f7588f0d3f90c764 [file] [log] [blame]
/* GStreamer IMX PXP Device
* Copyright (c) 2015, Freescale Semiconductor, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <fcntl.h>
#include <sys/ioctl.h>
#include "pxp_lib.h"
#include "imx_2d_device.h"
GST_DEBUG_CATEGORY_EXTERN (imx2ddevice_debug);
#define GST_CAT_DEFAULT imx2ddevice_debug
/* current PXP hardware has some problem on some video format when performing
* CSC. undefine following macro to disable those formats for now.
* if next PXP module solved those problem, we should enable them again
*/
//#define ENABLE_ALL_PXP_FORMATS
#define PXP_WAIT_COMPLETE_TIME 3
#define ENABLE_PXP_ALPHA_OVERLAY
#define PXP_OVERLAY_TMP_BUF_SIZE_INIT (1280*720*2)
#define PXP_OVERLAY_RGB_TMP_BUF_SIZE_INIT (1280*720*2)
typedef struct _Imx2DDevicePxp {
gint capabilities;
struct pxp_config_data config;
pxp_chan_handle_t pxp_chan;
gboolean first_frame_done;
#ifdef ENABLE_PXP_ALPHA_OVERLAY
PhyMemBlock ov_temp;
PhyMemBlock dummy;
PhyMemBlock rgb_temp;
guint background;
#endif
} Imx2DDevicePxp;
typedef struct {
GstVideoFormat gst_video_format;
guint pxp_format;
guint bpp;
} PxpFmtMap;
static PxpFmtMap pxp_in_fmts_map[] = {
{GST_VIDEO_FORMAT_BGRx, PXP_PIX_FMT_RGB32, 32},
{GST_VIDEO_FORMAT_RGB16, PXP_PIX_FMT_RGB565, 16},
{GST_VIDEO_FORMAT_RGB15, PXP_PIX_FMT_RGB555, 16},
{GST_VIDEO_FORMAT_I420, PXP_PIX_FMT_YUV420P, 12},
{GST_VIDEO_FORMAT_YV12, PXP_PIX_FMT_YVU420P, 12},
{GST_VIDEO_FORMAT_Y42B, PXP_PIX_FMT_YUV422P, 16},
{GST_VIDEO_FORMAT_UYVY, PXP_PIX_FMT_UYVY, 16},
{GST_VIDEO_FORMAT_YUY2, PXP_PIX_FMT_YUYV, 16},
{GST_VIDEO_FORMAT_YVYU, PXP_PIX_FMT_YVYU, 16},
{GST_VIDEO_FORMAT_NV12, PXP_PIX_FMT_NV12, 12},
{GST_VIDEO_FORMAT_NV21, PXP_PIX_FMT_NV21, 12},
{GST_VIDEO_FORMAT_NV16, PXP_PIX_FMT_NV16, 16},
#ifdef ENABLE_ALL_PXP_FORMATS
{GST_VIDEO_FORMAT_BGRA, PXP_PIX_FMT_BGRA32, 32},
{GST_VIDEO_FORMAT_AYUV, PXP_PIX_FMT_VUY444, 32},
{GST_VIDEO_FORMAT_GRAY8, PXP_PIX_FMT_GREY, 8},
#endif
/* There is no corresponding GST Video format for those PXP input formats
PXP_PIX_FMT_GY04
PXP_PIX_FMT_YVU422P
PXP_PIX_FMT_VYUY
PXP_PIX_FMT_NV61
*/
{GST_VIDEO_FORMAT_UNKNOWN, -1, 0}
};
static PxpFmtMap pxp_out_fmts_map[] = {
{GST_VIDEO_FORMAT_BGRx, PXP_PIX_FMT_RGB32, 32},
{GST_VIDEO_FORMAT_BGRA, PXP_PIX_FMT_BGRA32, 32},
{GST_VIDEO_FORMAT_BGR, PXP_PIX_FMT_RGB24, 24},
{GST_VIDEO_FORMAT_RGB16, PXP_PIX_FMT_RGB565, 16},
{GST_VIDEO_FORMAT_GRAY8, PXP_PIX_FMT_GREY, 8},
#ifdef ENABLE_ALL_PXP_FORMATS
{GST_VIDEO_FORMAT_RGB15, PXP_PIX_FMT_RGB555, 16},
{GST_VIDEO_FORMAT_UYVY, PXP_PIX_FMT_UYVY, 16},
{GST_VIDEO_FORMAT_NV12, PXP_PIX_FMT_NV12, 12},
{GST_VIDEO_FORMAT_NV21, PXP_PIX_FMT_NV21, 12},
{GST_VIDEO_FORMAT_NV16, PXP_PIX_FMT_NV16, 16},
#endif
/* There is no corresponding GST Video format for those PXP output formats
PXP_PIX_FMT_GY04
PXP_PIX_FMT_VYUY
PXP_PIX_FMT_NV61
*/
{GST_VIDEO_FORMAT_UNKNOWN, -1, 0}
};
static const PxpFmtMap * imx_pxp_get_format(GstVideoFormat format,
PxpFmtMap *map)
{
while(map->gst_video_format != GST_VIDEO_FORMAT_UNKNOWN) {
if (map->gst_video_format == format)
return map;
map++;
};
GST_ERROR ("pxp : format (%s) is not supported.",
gst_video_format_to_string(format));
return NULL;
}
static gint imx_pxp_open(Imx2DDevice *device)
{
if (!device)
return -1;
gint ret = pxp_init();
if (ret < 0) {
GST_ERROR("pxp init failed (%d)", ret);
return -1;
}
pxp_chan_handle_t pxp_chan;
ret = pxp_request_channel(&pxp_chan);
if (ret < 0) {
pxp_uninit();
GST_ERROR("pxp request channel failed (%d)", ret);
return -1;
}
Imx2DDevicePxp *pxp = g_slice_alloc(sizeof(Imx2DDevicePxp));
if (!pxp) {
pxp_release_channel(&pxp_chan);
pxp_uninit();
GST_ERROR("allocate pxp structure failed");
return -1;
}
memset(pxp, 0, sizeof (Imx2DDevicePxp));
memcpy(&pxp->pxp_chan, &pxp_chan, sizeof(pxp_chan_handle_t));
device->priv = (gpointer)pxp;
GST_DEBUG("requested pxp chan handle %d", pxp->pxp_chan.handle);
return 0;
}
static gint
imx_pxp_alloc_mem(Imx2DDevice *device, PhyMemBlock *memblk)
{
if (!device || !device->priv || !memblk)
return -1;
struct pxp_mem_desc *mem = g_slice_alloc(sizeof(struct pxp_mem_desc));
if (!mem)
return -1;
memset(mem, 0, sizeof (struct pxp_mem_desc));
mem->size = memblk->size;
gint ret = pxp_get_mem (mem);
if (ret < 0) {
GST_ERROR("PXP allocate %u bytes memory failed: %s",
memblk->size, strerror(errno));
return -1;
}
memblk->vaddr = (guchar*) mem->virt_uaddr;
memblk->paddr = (guchar*) mem->phys_addr;
memblk->user_data = (gpointer) mem;
GST_DEBUG("PXP allocated memory (%p)", memblk->paddr);
return 0;
}
static gint imx_pxp_free_mem(Imx2DDevice *device, PhyMemBlock *memblk)
{
if (!device || !device->priv || !memblk)
return -1;
if (memblk->vaddr == NULL)
return 0;
GST_DEBUG("PXP free memory (%p)", memblk->paddr);
gint ret = pxp_put_mem ((struct pxp_mem_desc*)(memblk->user_data));
memblk->user_data = NULL;
memblk->vaddr = NULL;
memblk->paddr = NULL;
memblk->size = 0;
return ret;
}
static gint imx_pxp_close(Imx2DDevice *device)
{
if (!device)
return -1;
if (device) {
Imx2DDevicePxp *pxp = (Imx2DDevicePxp *) (device->priv);
if (pxp) {
imx_pxp_free_mem(device, &pxp->ov_temp);
imx_pxp_free_mem(device, &pxp->dummy);
imx_pxp_free_mem(device, &pxp->rgb_temp);
pxp_release_channel(&pxp->pxp_chan);
pxp_uninit();
g_slice_free1(sizeof(Imx2DDevicePxp), pxp);
}
device->priv = NULL;
}
return 0;
}
static gint imx_pxp_copy_mem(Imx2DDevice* device, PhyMemBlock *dst_mem,
PhyMemBlock *src_mem, guint offset, guint size)
{
if (!device || !device->priv || !src_mem || !dst_mem)
return -1;
if (size > src_mem->size - offset)
size = src_mem->size - offset;
struct pxp_mem_desc *mem = g_slice_alloc(sizeof(struct pxp_mem_desc));
if (!mem)
return -1;
memset(mem, 0, sizeof (struct pxp_mem_desc));
mem->size = size;
gint ret = pxp_get_mem (mem);
if (ret < 0) {
GST_ERROR("PXP allocate %u bytes memory failed: %s", size, strerror(errno));
return -1;
}
dst_mem->vaddr = (guchar*) mem->virt_uaddr;
dst_mem->paddr = (guchar*) mem->phys_addr;
dst_mem->user_data = (gpointer) mem;
memcpy(dst_mem->vaddr, src_mem->vaddr+offset, size);
GST_DEBUG ("PXP copy from vaddr (%p), paddr (%p), size (%d) to "
"vaddr (%p), paddr (%p), size (%d)",
src_mem->vaddr, src_mem->paddr, src_mem->size,
dst_mem->vaddr, dst_mem->paddr, dst_mem->size);
return 0;
}
static gint imx_pxp_frame_copy(Imx2DDevice *device,
PhyMemBlock *from, PhyMemBlock *to)
{
if (!device || !device->priv || !from || !to)
return -1;
memcpy(to->vaddr, from->vaddr, (from->size > to->size) ? to->size:from->size);
GST_LOG("PXP frame memory (%p)->(%p)", from->paddr, to->paddr);
return 0;
}
static gint imx_pxp_config_input(Imx2DDevice *device, Imx2DVideoInfo* in_info)
{
if (!device || !device->priv)
return -1;
Imx2DDevicePxp *pxp = (Imx2DDevicePxp *) (device->priv);
const PxpFmtMap *in_map = imx_pxp_get_format(in_info->fmt, pxp_in_fmts_map);
if (!in_map)
return -1;
pxp->config.proc_data.srect.left = 0;
pxp->config.proc_data.srect.top = 0;
pxp->config.proc_data.srect.width = in_info->w;
pxp->config.proc_data.srect.height = in_info->h;
/* set S0 parameters */
pxp->config.s0_param.pixel_fmt = in_map->pxp_format;
pxp->config.s0_param.width = in_info->w;
pxp->config.s0_param.height = in_info->h;
pxp->config.s0_param.stride = in_info->w;
GST_TRACE("input format = %s", gst_video_format_to_string(in_info->fmt));
return 0;
}
static gint imx_pxp_config_output(Imx2DDevice *device, Imx2DVideoInfo* out_info)
{
if (!device || !device->priv)
return -1;
Imx2DDevicePxp *pxp = (Imx2DDevicePxp *) (device->priv);
const PxpFmtMap *out_map = imx_pxp_get_format(out_info->fmt,pxp_out_fmts_map);
if (!out_map)
return -1;
pxp->config.proc_data.drect.left = 0;
pxp->config.proc_data.drect.top = 0;
pxp->config.proc_data.drect.width = out_info->w;
pxp->config.proc_data.drect.height = out_info->h;
/* set Output channel parameters */
pxp->config.out_param.pixel_fmt = out_map->pxp_format;
pxp->config.out_param.width = out_info->w;
pxp->config.out_param.height = out_info->h;
pxp->config.out_param.stride = out_info->w;
GST_TRACE("output format = %s", gst_video_format_to_string(out_info->fmt));
return 0;
}
static gint imx_pxp_do_channel(Imx2DDevicePxp *pxp)
{
gint ret = 0;
ret = pxp_config_channel(&pxp->pxp_chan, &pxp->config);
if (ret < 0) {
GST_ERROR("pxp config channel fail (%d)", ret);
return -1;
}
ret = pxp_start_channel(&pxp->pxp_chan);
if (ret < 0) {
GST_ERROR("pxp start channel fail (%d)", ret);
return -1;
}
ret = pxp_wait_for_completion(&pxp->pxp_chan, 3);
if (ret < 0) {
GST_ERROR("pxp wait for completion fail (%d)", ret);
return -1;
}
return ret;
}
static gint imx_pxp_convert(Imx2DDevice *device,
Imx2DFrame *dst, Imx2DFrame *src)
{
gint ret = 0;
if (!device || !device->priv || !dst || !src || !dst->mem || !src->mem)
return -1;
Imx2DDevicePxp *pxp = (Imx2DDevicePxp *) (device->priv);
memset(&pxp->config.ol_param[0], 0, sizeof(struct pxp_layer_param));
// Set input crop
pxp->config.proc_data.srect.left = src->crop.x;
pxp->config.proc_data.srect.top = src->crop.y;
pxp->config.proc_data.srect.width =
MIN(src->crop.w, pxp->config.proc_data.srect.width);
pxp->config.proc_data.srect.height =
MIN(src->crop.h, pxp->config.proc_data.srect.height);
pxp->config.s0_param.paddr = (dma_addr_t)src->mem->paddr;
GST_TRACE ("pxp src : %dx%d,%d(%d,%d-%d,%d), format=%x",
pxp->config.s0_param.width, pxp->config.s0_param.height,
pxp->config.s0_param.stride,
pxp->config.proc_data.srect.left, pxp->config.proc_data.srect.top,
pxp->config.proc_data.srect.width, pxp->config.proc_data.srect.height,
pxp->config.s0_param.pixel_fmt);
// Set output crop
pxp->config.proc_data.drect.left = dst->crop.x;
pxp->config.proc_data.drect.top = dst->crop.y;
pxp->config.proc_data.drect.width =
MIN(dst->crop.w, pxp->config.proc_data.drect.width);
pxp->config.proc_data.drect.height =
MIN(dst->crop.h, pxp->config.proc_data.drect.height);
pxp->config.out_param.paddr = (dma_addr_t)dst->mem->paddr;
GST_TRACE ("pxp dest : %dx%d,%d(%d,%d-%d,%d), format=%x",
pxp->config.out_param.width, pxp->config.out_param.height,
pxp->config.out_param.stride,
pxp->config.proc_data.drect.left, pxp->config.proc_data.drect.top,
pxp->config.proc_data.drect.width, pxp->config.proc_data.drect.height,
pxp->config.out_param.pixel_fmt);
// Final conversion
return imx_pxp_do_channel(pxp);
}
static gint imx_pxp_blend_without_alpha(Imx2DDevice *device,
Imx2DFrame *dst, Imx2DFrame *src)
{
gint ret = 0;
if (!device || !device->priv || !dst || !src || !dst->mem || !src->mem)
return -1;
Imx2DDevicePxp *pxp = (Imx2DDevicePxp *) (device->priv);
memset(&pxp->config.ol_param[0], 0, sizeof(struct pxp_layer_param));
if (pxp->first_frame_done == FALSE) {
pxp->config.proc_data.drect.left = dst->crop.x;
pxp->config.proc_data.drect.top = dst->crop.y;
pxp->config.proc_data.drect.width =
MIN(dst->crop.w, pxp->config.proc_data.drect.width);
pxp->config.proc_data.drect.height =
MIN(dst->crop.h, pxp->config.proc_data.drect.height);
pxp->config.out_param.paddr = (dma_addr_t)dst->mem->paddr;
pxp->first_frame_done = TRUE;
} else {
pxp->config.proc_data.drect.left = 0;
pxp->config.proc_data.drect.top = 0;
pxp->config.proc_data.drect.width = dst->crop.w;
pxp->config.proc_data.drect.height = dst->crop.h;
pxp->config.out_param.paddr = (dma_addr_t)dst->mem->paddr +
(dst->crop.y * dst->info.stride +
dst->crop.x*(dst->info.stride/dst->info.w));
pxp->config.out_param.width = pxp->config.proc_data.drect.width;
pxp->config.out_param.height = pxp->config.proc_data.drect.height;
}
pxp->config.proc_data.srect.left = src->crop.x;
pxp->config.proc_data.srect.top = src->crop.y;
pxp->config.proc_data.srect.width = MIN(src->crop.w, src->info.w);
pxp->config.proc_data.srect.height = MIN(src->crop.h, src->info.h);
pxp->config.s0_param.paddr = (dma_addr_t)src->mem->paddr;
GST_TRACE ("pxp dest : %dx%d,%d(%d,%d-%d,%d), format=%x",
pxp->config.out_param.width, pxp->config.out_param.height,
pxp->config.out_param.stride,
pxp->config.proc_data.drect.left, pxp->config.proc_data.drect.top,
pxp->config.proc_data.drect.width, pxp->config.proc_data.drect.height,
pxp->config.out_param.pixel_fmt);
GST_TRACE ("pxp s0 : %dx%d,%d(%d,%d-%d,%d), format=%x",
pxp->config.s0_param.width, pxp->config.s0_param.height,
pxp->config.s0_param.stride,
pxp->config.proc_data.srect.left, pxp->config.proc_data.srect.top,
pxp->config.proc_data.srect.width, pxp->config.proc_data.srect.height,
pxp->config.s0_param.pixel_fmt);
return imx_pxp_do_channel(pxp);
}
static gint imx_pxp_overlay(Imx2DDevice *device,
Imx2DFrame *dst, Imx2DFrame *src)
{
guint orig_dst_w;
guint orig_dst_h;
guint orig_dst_s;
guint orig_dst_fmt;
guint orig_src_fmt;
guint BPP = 4;
const PxpFmtMap *fmt_map = NULL;
if (!device || !device->priv || !dst || !src || !dst->mem || !src->mem)
return -1;
Imx2DDevicePxp *pxp = (Imx2DDevicePxp *) (device->priv);
memset(&pxp->config.ol_param[0], 0, sizeof(struct pxp_layer_param));
orig_src_fmt = pxp->config.s0_param.pixel_fmt;
orig_dst_fmt = pxp->config.out_param.pixel_fmt;
orig_dst_w = dst->info.w;
orig_dst_h = dst->info.h;
orig_dst_s = dst->info.w;
if (pxp->ov_temp.vaddr == NULL) {
pxp->ov_temp.size = PXP_OVERLAY_TMP_BUF_SIZE_INIT;
if (imx_pxp_alloc_mem(device, &pxp->ov_temp) < 0)
return -1;
}
fmt_map = imx_pxp_get_format(dst->info.fmt, pxp_out_fmts_map);
if (fmt_map)
BPP = fmt_map->bpp/8 + (fmt_map->bpp%8 ? 1 : 0);
if (pxp->ov_temp.vaddr
&& pxp->ov_temp.size < (dst->crop.w * dst->crop.h * BPP)) {
imx_pxp_free_mem(device, &pxp->ov_temp);
pxp->ov_temp.size = dst->crop.w * dst->crop.h * BPP;
GST_LOG ("reallocte memory %d, BPP=%d", pxp->ov_temp.size, BPP);
if (imx_pxp_alloc_mem(device, &pxp->ov_temp) < 0)
return -1;
}
if (pxp->ov_temp.vaddr == NULL)
return -1;
if (pxp->first_frame_done == FALSE) {
//pxp background was filled along with output, if the first frame isn't done
//we need fill the background before we can apply alpha blending on the
//background.
//output a small dummy area with the color of background to let pxp fill all
//output frame with background color.
if (pxp->dummy.vaddr == NULL) {
pxp->dummy.size = 16 * 16 * 4;
if (imx_pxp_alloc_mem(device, &pxp->dummy) < 0)
return -1;
}
gchar R,G,B,A;
R = pxp->background & 0x000000FF;
G = (pxp->background & 0x0000FF00) >> 8;
B = (pxp->background & 0x00FF0000) >> 16;
A = (pxp->background & 0xFF000000) >> 24;
gchar *p = pxp->dummy.vaddr;
gint i;
for (i = 0; i < 16*16; i++) {
p[4 * i + 0] = B;
p[4 * i + 1] = G;
p[4 * i + 2] = R;
p[4 * i + 3] = A;
}
pxp->config.proc_data.srect.left = 0;
pxp->config.proc_data.srect.top = 0;
pxp->config.proc_data.srect.width = 16;
pxp->config.proc_data.srect.height = 16;
pxp->config.s0_param.width = 16;
pxp->config.s0_param.height = 16;
pxp->config.s0_param.stride = 16;
pxp->config.s0_param.pixel_fmt = PXP_PIX_FMT_RGB32;
pxp->config.s0_param.paddr = (dma_addr_t)pxp->dummy.paddr;
pxp->config.proc_data.drect.left = 0;
pxp->config.proc_data.drect.top = 0;
pxp->config.proc_data.drect.width = 16;
pxp->config.proc_data.drect.height = 16;
pxp->config.out_param.paddr = (dma_addr_t)dst->mem->paddr;
imx_pxp_do_channel(pxp);
pxp->first_frame_done = TRUE;
}
// get the original overlapped destination area to tmep buffer
pxp->config.s0_param.paddr = (dma_addr_t)dst->mem->paddr;
pxp->config.s0_param.pixel_fmt = orig_dst_fmt;
pxp->config.s0_param.width = orig_dst_w;
pxp->config.s0_param.height = orig_dst_h;
pxp->config.s0_param.stride = orig_dst_s;
pxp->config.proc_data.srect.left = dst->crop.x;
pxp->config.proc_data.srect.top = dst->crop.y;
pxp->config.proc_data.srect.width = MIN(dst->crop.w, orig_dst_w-dst->crop.x);
pxp->config.proc_data.srect.height = MIN(dst->crop.h, orig_dst_h-dst->crop.y);
GST_TRACE ("pxp temp src : %dx%d,%d(%d,%d-%d,%d), format=%x",
pxp->config.s0_param.width, pxp->config.s0_param.height,
pxp->config.s0_param.stride,
pxp->config.proc_data.srect.left, pxp->config.proc_data.srect.top,
pxp->config.proc_data.srect.width, pxp->config.proc_data.srect.height,
pxp->config.s0_param.pixel_fmt);
pxp->config.out_param.paddr = (dma_addr_t)pxp->ov_temp.paddr;
pxp->config.proc_data.drect.left = 0;
pxp->config.proc_data.drect.top = 0;
pxp->config.proc_data.drect.width = pxp->config.proc_data.srect.width;
pxp->config.proc_data.drect.height = pxp->config.proc_data.srect.height;
pxp->config.out_param.pixel_fmt = orig_dst_fmt;
pxp->config.out_param.width = dst->crop.w;
pxp->config.out_param.height = dst->crop.h;
pxp->config.out_param.stride = dst->crop.w;
GST_TRACE ("pxp temp dest : %dx%d,%d(%d,%d-%d,%d), format=%x",
pxp->config.out_param.width, pxp->config.out_param.height,
pxp->config.out_param.stride,
pxp->config.proc_data.drect.left, pxp->config.proc_data.drect.top,
pxp->config.proc_data.drect.width, pxp->config.proc_data.drect.height,
pxp->config.out_param.pixel_fmt);
if (imx_pxp_do_channel(pxp) < 0) {
GST_ERROR("pxp overlay copy temp dst buffer failed");
return -1;
}
if (orig_src_fmt == PXP_PIX_FMT_RGB32 || orig_src_fmt == PXP_PIX_FMT_BGRA32 ||
orig_src_fmt == PXP_PIX_FMT_RGB565 || orig_src_fmt == PXP_PIX_FMT_RGB555){
//overlay don't support resize, resize to s0 size before blending
if (dst->crop.w != src->crop.w || dst->crop.h != src->crop.h) {
if (pxp->rgb_temp.vaddr == NULL) {
pxp->rgb_temp.size = PXP_OVERLAY_RGB_TMP_BUF_SIZE_INIT;
if (imx_pxp_alloc_mem(device, &pxp->rgb_temp) < 0)
return -1;
}
guint BPP = 2;
if (orig_src_fmt == PXP_PIX_FMT_RGB32
|| orig_src_fmt == PXP_PIX_FMT_BGRA32)
BPP = 4;
if (pxp->rgb_temp.vaddr
&& pxp->rgb_temp.size < (orig_dst_w * orig_dst_h * BPP)) {
imx_pxp_free_mem(device, &pxp->rgb_temp);
pxp->rgb_temp.size = orig_dst_w * orig_dst_h * BPP;
GST_LOG ("reallocte memory %d", pxp->rgb_temp.size);
if (imx_pxp_alloc_mem(device, &pxp->rgb_temp) < 0)
return -1;
}
pxp->config.s0_param.paddr = (dma_addr_t)src->mem->paddr;
pxp->config.s0_param.pixel_fmt = orig_src_fmt;
pxp->config.s0_param.width = src->info.w;
pxp->config.s0_param.height = src->info.h;
pxp->config.s0_param.stride = src->info.w;
pxp->config.proc_data.srect.left = src->crop.x;
pxp->config.proc_data.srect.top = src->crop.y;
pxp->config.proc_data.srect.width = MIN(src->crop.w, src->info.w);
pxp->config.proc_data.srect.height = MIN(src->crop.h, src->info.h);
GST_TRACE ("pxp rgb resize src : %dx%d,%d(%d,%d-%d,%d), format=%x",
pxp->config.s0_param.width, pxp->config.s0_param.height,
pxp->config.s0_param.stride,
pxp->config.proc_data.srect.left, pxp->config.proc_data.srect.top,
pxp->config.proc_data.srect.width, pxp->config.proc_data.srect.height,
pxp->config.s0_param.pixel_fmt);
pxp->config.out_param.paddr = (dma_addr_t)pxp->rgb_temp.paddr;
pxp->config.proc_data.drect.left = 0;
pxp->config.proc_data.drect.top = 0;
pxp->config.proc_data.drect.width = MIN(dst->crop.w, orig_dst_w-dst->crop.x);
pxp->config.proc_data.drect.height = MIN(dst->crop.h, orig_dst_h-dst->crop.y);
pxp->config.out_param.pixel_fmt = orig_src_fmt;
pxp->config.out_param.width = dst->crop.w;
pxp->config.out_param.height = dst->crop.h;
pxp->config.out_param.stride = dst->crop.w;
GST_TRACE ("pxp rgb resize dest : %dx%d,%d(%d,%d-%d,%d), format=%x",
pxp->config.out_param.width, pxp->config.out_param.height,
pxp->config.out_param.stride,
pxp->config.proc_data.drect.left, pxp->config.proc_data.drect.top,
pxp->config.proc_data.drect.width, pxp->config.proc_data.drect.height,
pxp->config.out_param.pixel_fmt);
if (imx_pxp_do_channel(pxp) < 0) {
GST_ERROR("pxp overlay copy temp dst buffer failed");
return -1;
}
pxp->config.ol_param[0].left = 0;
pxp->config.ol_param[0].top = 0;
pxp->config.ol_param[0].stride = dst->crop.w;
pxp->config.ol_param[0].paddr = (dma_addr_t)pxp->rgb_temp.paddr;
} else {
pxp->config.ol_param[0].left = src->crop.x;
pxp->config.ol_param[0].top = src->crop.y;
pxp->config.ol_param[0].stride = src->info.w;
pxp->config.ol_param[0].paddr = (dma_addr_t)src->mem->paddr;
}
pxp->config.ol_param[0].pixel_fmt = orig_src_fmt;
} else {
//overlay don't support YUV color space. need convert src to RGB space first
if (pxp->rgb_temp.vaddr == NULL) {
pxp->rgb_temp.size = PXP_OVERLAY_RGB_TMP_BUF_SIZE_INIT;
if (imx_pxp_alloc_mem(device, &pxp->rgb_temp) < 0)
return -1;
}
if (pxp->rgb_temp.vaddr
&& pxp->rgb_temp.size < (dst->crop.w * dst->crop.h * 2)) {
imx_pxp_free_mem(device, &pxp->rgb_temp);
pxp->rgb_temp.size = dst->crop.w * dst->crop.h * 2;
GST_LOG ("reallocte memory %d", pxp->rgb_temp.size);
if (imx_pxp_alloc_mem(device, &pxp->rgb_temp) < 0)
return -1;
}
pxp->config.s0_param.paddr = (dma_addr_t)src->mem->paddr;
pxp->config.s0_param.pixel_fmt = orig_src_fmt;
pxp->config.s0_param.width = src->info.w;
pxp->config.s0_param.height = src->info.h;
pxp->config.s0_param.stride = src->info.w;
pxp->config.proc_data.srect.left = src->crop.x;
pxp->config.proc_data.srect.top = src->crop.y;
pxp->config.proc_data.srect.width = MIN(src->crop.w, src->info.w);
pxp->config.proc_data.srect.height = MIN(src->crop.h, src->info.h);
GST_TRACE ("pxp rgb temp src : %dx%d,%d(%d,%d-%d,%d), format=%x",
pxp->config.s0_param.width, pxp->config.s0_param.height,
pxp->config.s0_param.stride,
pxp->config.proc_data.srect.left, pxp->config.proc_data.srect.top,
pxp->config.proc_data.srect.width, pxp->config.proc_data.srect.height,
pxp->config.s0_param.pixel_fmt);
pxp->config.out_param.paddr = (dma_addr_t)pxp->rgb_temp.paddr;
pxp->config.proc_data.drect.left = 0;
pxp->config.proc_data.drect.top = 0;
pxp->config.proc_data.drect.width = MIN(dst->crop.w, orig_dst_w-dst->crop.x);
pxp->config.proc_data.drect.height = MIN(dst->crop.h, orig_dst_h-dst->crop.y);
pxp->config.out_param.pixel_fmt = PXP_PIX_FMT_RGB565;
pxp->config.out_param.width = dst->crop.w;
pxp->config.out_param.height = dst->crop.h;
pxp->config.out_param.stride = dst->crop.w;
GST_TRACE ("pxp rgb temp dest : %dx%d,%d(%d,%d-%d,%d), format=%x",
pxp->config.out_param.width, pxp->config.out_param.height,
pxp->config.out_param.stride,
pxp->config.proc_data.drect.left, pxp->config.proc_data.drect.top,
pxp->config.proc_data.drect.width, pxp->config.proc_data.drect.height,
pxp->config.out_param.pixel_fmt);
if (imx_pxp_do_channel(pxp) < 0) {
GST_ERROR("pxp overlay copy temp dst buffer failed");
return -1;
}
pxp->config.ol_param[0].left = 0;
pxp->config.ol_param[0].top = 0;
pxp->config.ol_param[0].stride = pxp->config.proc_data.drect.width;
pxp->config.ol_param[0].pixel_fmt = PXP_PIX_FMT_RGB565;
pxp->config.ol_param[0].paddr = (dma_addr_t)pxp->rgb_temp.paddr;
}
pxp->config.ol_param[0].global_alpha_enable = TRUE;
pxp->config.ol_param[0].global_alpha = src->alpha;
pxp->config.ol_param[0].local_alpha_enable = TRUE;
pxp->config.ol_param[0].global_override = FALSE;
pxp->config.ol_param[0].width = dst->crop.w;
pxp->config.ol_param[0].height = dst->crop.h;
pxp->config.ol_param[0].combine_enable = TRUE;
GST_TRACE ("pxp overlay : %dx%d,%d(%d,%d-%d,%d), format=%x",
pxp->config.ol_param[0].width, pxp->config.ol_param[0].height,
pxp->config.ol_param[0].stride, pxp->config.ol_param[0].left,
pxp->config.ol_param[0].top, pxp->config.ol_param[0].width,
pxp->config.ol_param[0].height, pxp->config.ol_param[0].pixel_fmt);
pxp->config.s0_param.width = dst->crop.w;
pxp->config.s0_param.height = dst->crop.h;
pxp->config.s0_param.stride = pxp->config.s0_param.width;
pxp->config.s0_param.pixel_fmt = orig_dst_fmt;
pxp->config.proc_data.srect.left = 0;
pxp->config.proc_data.srect.top = 0;
pxp->config.proc_data.srect.width = dst->crop.w;
pxp->config.proc_data.srect.height = dst->crop.h;
pxp->config.s0_param.paddr = (dma_addr_t)pxp->ov_temp.paddr;
GST_TRACE ("pxp src : %dx%d,%d(%d,%d-%d,%d), format=%x",
pxp->config.s0_param.width, pxp->config.s0_param.height,
pxp->config.s0_param.stride,
pxp->config.proc_data.srect.left, pxp->config.proc_data.srect.top,
pxp->config.proc_data.srect.width, pxp->config.proc_data.srect.height,
pxp->config.s0_param.pixel_fmt);
pxp->config.out_param.pixel_fmt = orig_dst_fmt;
pxp->config.out_param.stride = orig_dst_s;
pxp->config.proc_data.drect.left = 0;
pxp->config.proc_data.drect.top = 0;
pxp->config.proc_data.drect.width = dst->crop.w;
pxp->config.proc_data.drect.height = dst->crop.h;
pxp->config.out_param.paddr = (dma_addr_t)dst->mem->paddr +
(dst->crop.y * dst->info.stride + dst->crop.x * BPP);
pxp->config.out_param.width = pxp->config.proc_data.drect.width;
pxp->config.out_param.height = pxp->config.proc_data.drect.height;
GST_TRACE ("pxp dest : %dx%d,%d(%d,%d-%d,%d), format=%x",
pxp->config.out_param.width, pxp->config.out_param.height,
pxp->config.out_param.stride,
pxp->config.proc_data.drect.left, pxp->config.proc_data.drect.top,
pxp->config.proc_data.drect.width, pxp->config.proc_data.drect.height,
pxp->config.out_param.pixel_fmt);
return imx_pxp_do_channel(pxp);
}
static gboolean is_format_has_alpha(guint pxp_format) {
return (pxp_format == PXP_PIX_FMT_BGRA32 || pxp_format == PXP_PIX_FMT_VUY444);
}
static gint imx_pxp_blend(Imx2DDevice *device, Imx2DFrame *dst, Imx2DFrame *src)
{
if (!device || !device->priv || !dst || !src || !dst->mem || !src->mem)
return -1;
Imx2DDevicePxp *pxp = (Imx2DDevicePxp *) (device->priv);
if (src->alpha < 0xFF
|| is_format_has_alpha(pxp->config.s0_param.pixel_fmt)) {
return imx_pxp_overlay(device, dst, src);
} else {
return imx_pxp_blend_without_alpha(device, dst, src);
}
}
static gint imx_pxp_blend_finish(Imx2DDevice *device)
{
Imx2DDevicePxp *pxp = (Imx2DDevicePxp *) (device->priv);
pxp->first_frame_done = FALSE;
return 0;
}
static gint imx_pxp_fill_color(Imx2DDevice *device, Imx2DFrame *dst,
guint RGBA8888)
{
if (!device || !device->priv)
return -1;
guint bgcolor;
gchar R,G,B,A,Y,U,V;
gdouble y,u,v;
R = RGBA8888 & 0x000000FF;
G = (RGBA8888 & 0x0000FF00) >> 8;
B = (RGBA8888 & 0x00FF0000) >> 16;
A = (RGBA8888 & 0xFF000000) >> 24;
Imx2DDevicePxp *pxp = (Imx2DDevicePxp *) (device->priv);
if (dst->info.fmt == GST_VIDEO_FORMAT_BGRx
|| dst->info.fmt == GST_VIDEO_FORMAT_RGB16
|| dst->info.fmt == GST_VIDEO_FORMAT_BGRA
|| dst->info.fmt == GST_VIDEO_FORMAT_BGR) {
bgcolor = (A << 24)| (R << 16) | (G << 8) | B;
} else {
//BT.709
y = (0.213*R + 0.715*G + 0.072*B);
u = -0.117*R - 0.394*G + 0.511*B + 128;
v = 0.511*R - 0.464*G - 0.047*B + 128;
if (y > 255.0)
Y = 255;
else
Y = (gchar)y;
if (u < 0.0)
U = 0;
else
U = (gchar)u;
if (u > 255.0)
U = 255;
else
U = (gchar)u;
if (v < 0.0)
V = 0;
else
V = (gchar)v;
if (v > 255.0)
V = 255;
else
V = (gchar)v;
bgcolor = (A << 24) | (Y << 16) | (U << 8) | V;
}
pxp->config.proc_data.bgcolor = bgcolor;
pxp->background = RGBA8888;
return 0;
}
static gint imx_pxp_set_rotate(Imx2DDevice *device, Imx2DRotationMode rot)
{
if (!device || !device->priv)
return -1;
Imx2DDevicePxp *pxp = (Imx2DDevicePxp *) (device->priv);
switch (rot) {
case IMX_2D_ROTATION_0: pxp->config.proc_data.rotate = 0; break;
case IMX_2D_ROTATION_90: pxp->config.proc_data.rotate = 90; break;
case IMX_2D_ROTATION_180: pxp->config.proc_data.rotate = 180; break;
case IMX_2D_ROTATION_270: pxp->config.proc_data.rotate = 270; break;
case IMX_2D_ROTATION_HFLIP: pxp->config.proc_data.hflip = 1; break;
case IMX_2D_ROTATION_VFLIP: pxp->config.proc_data.vflip = 1; break;
default: pxp->config.proc_data.rotate = 0; break;
}
return 0;
}
static gint imx_pxp_set_deinterlace(Imx2DDevice *device,
Imx2DDeinterlaceMode mode)
{
return 0;
}
static Imx2DRotationMode imx_pxp_get_rotate (Imx2DDevice* device)
{
if (!device || !device->priv)
return 0;
Imx2DDevicePxp *pxp = (Imx2DDevicePxp *) (device->priv);
Imx2DRotationMode rot = IMX_2D_ROTATION_0;
switch (pxp->config.proc_data.rotate) {
case 0: rot = IMX_2D_ROTATION_0; break;
case 90: rot = IMX_2D_ROTATION_90; break;
case 180: rot = IMX_2D_ROTATION_180; break;
case 270: rot = IMX_2D_ROTATION_270; break;
default: rot = IMX_2D_ROTATION_0; break;
}
if (pxp->config.proc_data.hflip)
rot = IMX_2D_ROTATION_HFLIP;
else if (pxp->config.proc_data.vflip)
rot = IMX_2D_ROTATION_VFLIP;
return rot;
}
static Imx2DDeinterlaceMode imx_pxp_get_deinterlace (Imx2DDevice* device)
{
return IMX_2D_DEINTERLACE_NONE;
}
static gint imx_pxp_get_capabilities (Imx2DDevice* device)
{
void *pxp_handle = NULL;
gint capabilities = 0;
capabilities = IMX_2D_DEVICE_CAP_SCALE | IMX_2D_DEVICE_CAP_CSC \
| IMX_2D_DEVICE_CAP_ROTATE | IMX_2D_DEVICE_CAP_OVERLAY;
#ifdef ENABLE_PXP_ALPHA_OVERLAY
capabilities |= IMX_2D_DEVICE_CAP_ALPHA;
#endif
return capabilities;
}
static GList* imx_pxp_get_supported_in_fmts(Imx2DDevice* device)
{
GList* list = NULL;
const PxpFmtMap *map = pxp_in_fmts_map;
while (map->gst_video_format != GST_VIDEO_FORMAT_UNKNOWN) {
list = g_list_append(list, (gpointer)(map->gst_video_format));
map++;
}
return list;
}
static GList* imx_pxp_get_supported_out_fmts(Imx2DDevice* device)
{
GList* list = NULL;
const PxpFmtMap *map = pxp_out_fmts_map;
while (map->gst_video_format != GST_VIDEO_FORMAT_UNKNOWN) {
list = g_list_append(list, (gpointer)(map->gst_video_format));
map++;
}
return list;
}
Imx2DDevice * imx_pxp_create(Imx2DDeviceType device_type)
{
Imx2DDevice * device = g_slice_alloc(sizeof(Imx2DDevice));
if (!device) {
GST_ERROR("allocate device structure failed\n");
return NULL;
}
device->device_type = device_type;
device->priv = NULL;
device->open = imx_pxp_open;
device->close = imx_pxp_close;
device->alloc_mem = imx_pxp_alloc_mem;
device->free_mem = imx_pxp_free_mem;
device->copy_mem = imx_pxp_copy_mem;
device->frame_copy = imx_pxp_frame_copy;
device->config_input = imx_pxp_config_input;
device->config_output = imx_pxp_config_output;
device->convert = imx_pxp_convert;
device->blend = imx_pxp_blend;
device->blend_finish = imx_pxp_blend_finish;
device->fill = imx_pxp_fill_color;
device->set_rotate = imx_pxp_set_rotate;
device->set_deinterlace = imx_pxp_set_deinterlace;
device->get_rotate = imx_pxp_get_rotate;
device->get_deinterlace = imx_pxp_get_deinterlace;
device->get_capabilities = imx_pxp_get_capabilities;
device->get_supported_in_fmts = imx_pxp_get_supported_in_fmts;
device->get_supported_out_fmts = imx_pxp_get_supported_out_fmts;
return device;
}
gint imx_pxp_destroy(Imx2DDevice *device)
{
if (!device)
return -1;
g_slice_free1(sizeof(Imx2DDevice), device);
return 0;
}
gboolean imx_pxp_is_exist (void)
{
return HAS_PXP();
}