347 lines
10 KiB
C++
347 lines
10 KiB
C++
/*
|
|
* Copyright (C) 2014 The Android Open Source Project
|
|
* Copyright@ Samsung Electronics Co. LTD
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
/*!
|
|
* \file libscaler-m2m1shot.cpp
|
|
* \brief source file for Scaler HAL
|
|
* \author Cho KyongHo <pullip.cho@samsung.com>
|
|
* \date 2014/05/08
|
|
*
|
|
* <b>Revision History: </b>
|
|
* - 2014.05.08 : Cho KyongHo (pullip.cho@samsung.com) \n
|
|
* Create
|
|
*/
|
|
#include <cstring>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/mman.h>
|
|
|
|
#include <exynos_scaler.h>
|
|
|
|
#include "libscaler-common.h"
|
|
#include "libscaler-m2m1shot.h"
|
|
#include "libscaler-swscaler.h"
|
|
|
|
using namespace std;
|
|
|
|
const char *dev_base_name[] = {
|
|
"/dev/m2m1shot_scaler0",
|
|
"/dev/m2m1shot_scaler1",
|
|
"/dev/m2m1shot_scaler2",
|
|
"/dev/m2m1shot_scaler3",
|
|
};
|
|
|
|
struct PixFormat {
|
|
unsigned int pixfmt;
|
|
char planes;
|
|
char bit_pp[3];
|
|
};
|
|
|
|
const static PixFormat g_pixfmt_table[] = {
|
|
{V4L2_PIX_FMT_RGB32, 1, {32, 0, 0}, },
|
|
{V4L2_PIX_FMT_BGR32, 1, {32, 0, 0}, },
|
|
{V4L2_PIX_FMT_RGB565, 1, {16, 0, 0}, },
|
|
{V4L2_PIX_FMT_RGB555X, 1, {16, 0, 0}, },
|
|
{V4L2_PIX_FMT_RGB444, 1, {16, 0, 0}, },
|
|
{V4L2_PIX_FMT_YUYV, 1, {16, 0, 0}, },
|
|
{V4L2_PIX_FMT_YVYU, 1, {16, 0, 0}, },
|
|
{V4L2_PIX_FMT_UYVY, 1, {16, 0, 0}, },
|
|
{V4L2_PIX_FMT_NV16, 1, {16, 0, 0}, },
|
|
{V4L2_PIX_FMT_NV61, 1, {16, 0, 0}, },
|
|
{V4L2_PIX_FMT_YUV420, 1, {12, 0, 0}, },
|
|
{V4L2_PIX_FMT_YVU420, 1, {12, 0, 0}, },
|
|
{V4L2_PIX_FMT_NV12M, 2, {8, 4, 0}, },
|
|
{V4L2_PIX_FMT_NV21M, 2, {8, 4, 0}, },
|
|
{v4l2_fourcc('V', 'M', '1', '2'), 2, {8, 4, 0}, },
|
|
{V4L2_PIX_FMT_NV12, 1, {12, 0, 0}, },
|
|
{V4L2_PIX_FMT_NV21, 1, {12, 0, 0}, },
|
|
{v4l2_fourcc('N', 'M', '2', '1'), 2, {8, 4, 0}, },
|
|
{V4L2_PIX_FMT_YUV420M, 3, {8, 2, 2}, },
|
|
{V4L2_PIX_FMT_YVU420M, 3, {8, 2, 2}, },
|
|
{V4L2_PIX_FMT_NV12M_P010, 2, {16, 8, 0}, },
|
|
{V4L2_PIX_FMT_NV24, 1, {24, 0, 0}, },
|
|
{V4L2_PIX_FMT_NV42, 1, {24, 0, 0}, },
|
|
};
|
|
|
|
|
|
CScalerM2M1SHOT::CScalerM2M1SHOT(int devid, int __UNUSED__ drm) : m_iFD(-1)
|
|
{
|
|
memset(&m_task, 0, sizeof(m_task));
|
|
|
|
if ((devid < 0) || (devid > 3)) { // instance number must be between 0 ~ 3
|
|
SC_LOGE("Invalid device instance ID %d", devid);
|
|
return;
|
|
}
|
|
|
|
m_iFD = open(dev_base_name[devid], O_RDWR);
|
|
if (m_iFD < 0) {
|
|
SC_LOGERR("Failed to open '%s'", dev_base_name[devid]);
|
|
} else {
|
|
// default 3 planes not to miss any buffer address
|
|
m_task.buf_out.num_planes = 3;
|
|
m_task.buf_cap.num_planes = 3;
|
|
}
|
|
}
|
|
|
|
CScalerM2M1SHOT::~CScalerM2M1SHOT()
|
|
{
|
|
if (m_iFD >= 0)
|
|
close(m_iFD);
|
|
}
|
|
|
|
bool CScalerM2M1SHOT::Run()
|
|
{
|
|
int ret;
|
|
|
|
if (LibScaler::UnderOne16thScaling(
|
|
m_task.fmt_out.crop.width, m_task.fmt_out.crop.height,
|
|
m_task.fmt_cap.crop.width, m_task.fmt_cap.crop.height,
|
|
m_task.op.rotate))
|
|
return RunSWScaling();
|
|
|
|
ret = ioctl(m_iFD, M2M1SHOT_IOC_PROCESS, &m_task);
|
|
if (ret < 0) {
|
|
SC_LOGERR("Failed to process the given M2M1SHOT task");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#define SCALER_EXT_SIZE 512
|
|
bool CScalerM2M1SHOT::SetFormat(m2m1shot_pix_format &fmt, m2m1shot_buffer &buf,
|
|
unsigned int width, unsigned int height, unsigned int v4l2_fmt) {
|
|
const PixFormat *pixfmt = NULL;
|
|
|
|
fmt.width = width;
|
|
fmt.height = height;
|
|
fmt.fmt = v4l2_fmt;
|
|
|
|
for (size_t i = 0; i < ARRSIZE(g_pixfmt_table); i++) {
|
|
if (g_pixfmt_table[i].pixfmt == v4l2_fmt) {
|
|
pixfmt = &g_pixfmt_table[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!pixfmt) {
|
|
SC_LOGE("Format %#x is not supported", v4l2_fmt);
|
|
return false;
|
|
}
|
|
|
|
for (int i = 0; i < pixfmt->planes; i++) {
|
|
if (((pixfmt->bit_pp[i] * width) % 8) != 0) {
|
|
SC_LOGE("Plane %d of format %#x must have even width", i, v4l2_fmt);
|
|
return false;
|
|
}
|
|
buf.plane[i].len = (pixfmt->bit_pp[i] * width * height) / 8;
|
|
}
|
|
|
|
if (pixfmt->pixfmt == V4L2_PIX_FMT_YVU420) {
|
|
unsigned int y_size = width * height;
|
|
unsigned int c_span = ALIGN(width / 2, 16);
|
|
buf.plane[0].len = y_size + (c_span * height / 2) * 2;
|
|
}
|
|
|
|
#ifdef SCALER_ALIGN_RESTRICTION
|
|
for (int i = 0; i < pixfmt->planes; i++)
|
|
buf.plane[i].len += (i == 0) ? SCALER_EXT_SIZE : SCALER_EXT_SIZE / 2;
|
|
#endif
|
|
|
|
buf.num_planes = pixfmt->planes;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CScalerM2M1SHOT::SetCrop(m2m1shot_pix_format &fmt,
|
|
unsigned int l, unsigned int t, unsigned int w, unsigned int h) {
|
|
if (fmt.width <= l) {
|
|
SC_LOGE("crop left %d is larger than image width %d", l, fmt.width);
|
|
return false;
|
|
}
|
|
if (fmt.height <= t) {
|
|
SC_LOGE("crop top %d is larger than image height %d", t, fmt.height);
|
|
return false;
|
|
}
|
|
if (fmt.width < (l + w)) {
|
|
SC_LOGE("crop width %d@%d exceeds image width %d", w, l, fmt.width);
|
|
return false;
|
|
}
|
|
if (fmt.height < (t + h)) {
|
|
SC_LOGE("crop height %d@%d exceeds image height %d", h, t, fmt.height);
|
|
return false;
|
|
}
|
|
|
|
fmt.crop.left = l;
|
|
fmt.crop.top = t;
|
|
fmt.crop.width = w;
|
|
fmt.crop.height = h;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CScalerM2M1SHOT::SetAddr(
|
|
m2m1shot_buffer &buf, void *addr[SC_NUM_OF_PLANES], int mem_type) {
|
|
if (mem_type == V4L2_MEMORY_DMABUF) {
|
|
buf.type = M2M1SHOT_BUFFER_DMABUF;
|
|
for (int i = 0; i < buf.num_planes; i++)
|
|
buf.plane[i].fd = static_cast<__s32>(reinterpret_cast<long>(addr[i]));
|
|
} else if (mem_type == V4L2_MEMORY_USERPTR) {
|
|
buf.type = M2M1SHOT_BUFFER_USERPTR;
|
|
for (int i = 0; i < buf.num_planes; i++)
|
|
buf.plane[i].userptr = reinterpret_cast<unsigned long>(addr[i]);
|
|
} else {
|
|
SC_LOGE("Unknown buffer type %d", mem_type);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CScalerM2M1SHOT::SetRotate(int rot, int hflip, int vflip) {
|
|
if ((rot % 90) != 0) {
|
|
SC_LOGE("Rotation degree %d must be multiple of 90", rot);
|
|
return false;
|
|
}
|
|
|
|
rot = rot % 360;
|
|
if (rot < 0)
|
|
rot = 360 + rot;
|
|
|
|
m_task.op.rotate = rot;
|
|
m_task.op.op &= ~(M2M1SHOT_OP_FLIP_HORI | M2M1SHOT_OP_FLIP_VIRT);
|
|
if (hflip)
|
|
m_task.op.op |= M2M1SHOT_OP_FLIP_HORI;
|
|
if (vflip)
|
|
m_task.op.op |= M2M1SHOT_OP_FLIP_VIRT;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool GetBuffer(m2m1shot_buffer &buf, char *addr[])
|
|
{
|
|
for (int i = 0; i < buf.num_planes; i++) {
|
|
if (buf.type == M2M1SHOT_BUFFER_DMABUF) {
|
|
addr[i] = reinterpret_cast<char *>(mmap(NULL, buf.plane[i].len,
|
|
PROT_READ | PROT_WRITE, MAP_SHARED,
|
|
buf.plane[i].fd, 0));
|
|
if (addr[i] == MAP_FAILED) {
|
|
SC_LOGE("Failed to map FD %d", buf.plane[i].fd);
|
|
while (i-- > 0)
|
|
munmap(addr[i], buf.plane[i].len);
|
|
return false;
|
|
}
|
|
} else {
|
|
addr[i] = reinterpret_cast<char *>(buf.plane[i].userptr);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void PutBuffer(m2m1shot_buffer &buf, char *addr[])
|
|
{
|
|
for (int i = 0; i < buf.num_planes; i++) {
|
|
if (buf.type == M2M1SHOT_BUFFER_DMABUF)
|
|
munmap(addr[i], buf.plane[i].len);
|
|
}
|
|
}
|
|
|
|
bool CScalerM2M1SHOT::RunSWScaling()
|
|
{
|
|
if (m_task.fmt_cap.fmt != m_task.fmt_out.fmt) {
|
|
SC_LOGE("Source and target image format must be the same");
|
|
return false;
|
|
}
|
|
|
|
if (m_task.op.rotate != 0) {
|
|
SC_LOGE("Rotation is not allowed for S/W Scaling");
|
|
return false;
|
|
}
|
|
|
|
SC_LOGI("Running S/W Scaler: %dx%d -> %dx%d",
|
|
m_task.fmt_out.crop.width, m_task.fmt_out.crop.height,
|
|
m_task.fmt_cap.crop.width, m_task.fmt_cap.crop.height);
|
|
|
|
CScalerSW *swsc;
|
|
char *src[3], *dst[3];
|
|
|
|
switch (m_task.fmt_cap.fmt) {
|
|
case V4L2_PIX_FMT_YUYV:
|
|
case V4L2_PIX_FMT_YVYU:
|
|
if (!GetBuffer(m_task.buf_out, src))
|
|
return false;
|
|
|
|
if (!GetBuffer(m_task.buf_cap, dst)) {
|
|
PutBuffer(m_task.buf_out, src);
|
|
return false;
|
|
}
|
|
|
|
swsc = new CScalerSW_YUYV(src[0], dst[0]);
|
|
break;
|
|
case V4L2_PIX_FMT_NV12M:
|
|
case V4L2_PIX_FMT_NV21M:
|
|
case V4L2_PIX_FMT_NV12:
|
|
case V4L2_PIX_FMT_NV21:
|
|
if (!GetBuffer(m_task.buf_out, src))
|
|
return false;
|
|
|
|
if (!GetBuffer(m_task.buf_cap, dst)) {
|
|
PutBuffer(m_task.buf_out, src);
|
|
return false;
|
|
}
|
|
|
|
if (m_task.buf_out.num_planes == 1)
|
|
src[1] = src[0] + m_task.fmt_out.width * m_task.fmt_out.height;
|
|
|
|
if (m_task.buf_cap.num_planes == 1)
|
|
dst[1] = dst[0] + m_task.fmt_cap.width * m_task.fmt_cap.height;
|
|
|
|
swsc = new CScalerSW_NV12(src[0], src[1], dst[0], dst[1]);
|
|
break;
|
|
case V4L2_PIX_FMT_UYVY: // TODO: UYVY is not implemented yet.
|
|
default:
|
|
SC_LOGE("Format %x is not supported", m_task.fmt_out.fmt);
|
|
return false;
|
|
}
|
|
|
|
if (swsc == NULL) {
|
|
SC_LOGE("Failed to allocate SW Scaler");
|
|
PutBuffer(m_task.buf_out, src);
|
|
PutBuffer(m_task.buf_cap, dst);
|
|
return false;
|
|
}
|
|
|
|
swsc->SetSrcRect(m_task.fmt_out.crop.left, m_task.fmt_out.crop.top,
|
|
m_task.fmt_out.crop.width, m_task.fmt_out.crop.height,
|
|
m_task.fmt_out.width);
|
|
|
|
swsc->SetDstRect(m_task.fmt_cap.crop.left, m_task.fmt_cap.crop.top,
|
|
m_task.fmt_cap.crop.width, m_task.fmt_cap.crop.height,
|
|
m_task.fmt_cap.width);
|
|
|
|
bool ret = swsc->Scale();
|
|
|
|
delete swsc;
|
|
|
|
PutBuffer(m_task.buf_out, src);
|
|
PutBuffer(m_task.buf_cap, dst);
|
|
|
|
return ret;
|
|
}
|