// SPDX-License-Identifier: GPL-2.0
|
/*
|
* Support for Intel Camera Imaging ISP subsystem.
|
* Copyright (c) 2015, Intel Corporation.
|
*
|
* This program is free software; you can redistribute it and/or modify it
|
* under the terms and conditions of the GNU General Public License,
|
* version 2, as published by the Free Software Foundation.
|
*
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
* more details.
|
*/
|
|
#include "hmm.h"
|
|
#include "ia_css_frame.h"
|
#include <math_support.h>
|
#include "assert_support.h"
|
#include "ia_css_debug.h"
|
#include "isp.h"
|
#include "sh_css_internal.h"
|
#include "atomisp_internal.h"
|
|
#define NV12_TILEY_TILE_WIDTH 128
|
#define NV12_TILEY_TILE_HEIGHT 32
|
|
/**************************************************************************
|
** Static functions declarations
|
**************************************************************************/
|
static void frame_init_plane(struct ia_css_frame_plane *plane,
|
unsigned int width,
|
unsigned int stride,
|
unsigned int height,
|
unsigned int offset);
|
|
static void frame_init_single_plane(struct ia_css_frame *frame,
|
struct ia_css_frame_plane *plane,
|
unsigned int height,
|
unsigned int subpixels_per_line,
|
unsigned int bytes_per_pixel);
|
|
static void frame_init_raw_single_plane(
|
struct ia_css_frame *frame,
|
struct ia_css_frame_plane *plane,
|
unsigned int height,
|
unsigned int subpixels_per_line,
|
unsigned int bits_per_pixel);
|
|
static void frame_init_mipi_plane(struct ia_css_frame *frame,
|
struct ia_css_frame_plane *plane,
|
unsigned int height,
|
unsigned int subpixels_per_line,
|
unsigned int bytes_per_pixel);
|
|
static void frame_init_nv_planes(struct ia_css_frame *frame,
|
unsigned int horizontal_decimation,
|
unsigned int vertical_decimation,
|
unsigned int bytes_per_element);
|
|
static void frame_init_yuv_planes(struct ia_css_frame *frame,
|
unsigned int horizontal_decimation,
|
unsigned int vertical_decimation,
|
bool swap_uv,
|
unsigned int bytes_per_element);
|
|
static void frame_init_rgb_planes(struct ia_css_frame *frame,
|
unsigned int bytes_per_element);
|
|
static void frame_init_qplane6_planes(struct ia_css_frame *frame);
|
|
static int frame_allocate_buffer_data(struct ia_css_frame *frame);
|
|
static int frame_allocate_with_data(struct ia_css_frame **frame,
|
unsigned int width,
|
unsigned int height,
|
enum ia_css_frame_format format,
|
unsigned int padded_width,
|
unsigned int raw_bit_depth,
|
bool contiguous);
|
|
static struct ia_css_frame *frame_create(unsigned int width,
|
unsigned int height,
|
enum ia_css_frame_format format,
|
unsigned int padded_width,
|
unsigned int raw_bit_depth,
|
bool contiguous,
|
bool valid);
|
|
static unsigned
|
ia_css_elems_bytes_from_info(
|
const struct ia_css_frame_info *info);
|
|
/**************************************************************************
|
** CSS API functions, exposed by ia_css.h
|
**************************************************************************/
|
|
void ia_css_frame_zero(struct ia_css_frame *frame)
|
{
|
assert(frame);
|
hmm_set(frame->data, 0, frame->data_bytes);
|
}
|
|
int ia_css_frame_allocate_from_info(struct ia_css_frame **frame,
|
const struct ia_css_frame_info *info)
|
{
|
int err = 0;
|
|
if (!frame || !info)
|
return -EINVAL;
|
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
|
"ia_css_frame_allocate_from_info() enter:\n");
|
err =
|
ia_css_frame_allocate(frame, info->res.width, info->res.height,
|
info->format, info->padded_width,
|
info->raw_bit_depth);
|
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
|
"ia_css_frame_allocate_from_info() leave:\n");
|
return err;
|
}
|
|
int ia_css_frame_allocate(struct ia_css_frame **frame,
|
unsigned int width,
|
unsigned int height,
|
enum ia_css_frame_format format,
|
unsigned int padded_width,
|
unsigned int raw_bit_depth)
|
{
|
int err = 0;
|
|
if (!frame || width == 0 || height == 0)
|
return -EINVAL;
|
|
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
|
"ia_css_frame_allocate() enter: width=%d, height=%d, format=%d, padded_width=%d, raw_bit_depth=%d\n",
|
width, height, format, padded_width, raw_bit_depth);
|
|
err = frame_allocate_with_data(frame, width, height, format,
|
padded_width, raw_bit_depth, false);
|
|
if ((*frame) && err == 0)
|
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
|
"ia_css_frame_allocate() leave: frame=%p, data(DDR address)=0x%x\n", *frame,
|
(*frame)->data);
|
else
|
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
|
"ia_css_frame_allocate() leave: frame=%p, data(DDR address)=0x%x\n",
|
(void *)-1, (unsigned int)-1);
|
|
return err;
|
}
|
|
int ia_css_frame_map(struct ia_css_frame **frame,
|
const struct ia_css_frame_info *info,
|
const void __user *data,
|
u16 attribute,
|
unsigned int pgnr)
|
{
|
int err = 0;
|
struct ia_css_frame *me;
|
|
assert(frame);
|
|
/* Create the frame structure */
|
err = ia_css_frame_create_from_info(&me, info);
|
|
if (err)
|
return err;
|
|
if (!err) {
|
if (pgnr < ((PAGE_ALIGN(me->data_bytes)) >> PAGE_SHIFT)) {
|
dev_err(atomisp_dev,
|
"user space memory size is less than the expected size..\n");
|
err = -ENOMEM;
|
goto error;
|
} else if (pgnr > ((PAGE_ALIGN(me->data_bytes)) >> PAGE_SHIFT)) {
|
dev_err(atomisp_dev,
|
"user space memory size is large than the expected size..\n");
|
err = -ENOMEM;
|
goto error;
|
}
|
|
me->data = hmm_alloc(me->data_bytes, HMM_BO_USER, 0, data,
|
attribute & ATOMISP_MAP_FLAG_CACHED);
|
|
if (me->data == mmgr_NULL)
|
err = -EINVAL;
|
}
|
|
error:
|
if (err) {
|
kvfree(me);
|
me = NULL;
|
}
|
|
*frame = me;
|
|
return err;
|
}
|
|
int ia_css_frame_create_from_info(struct ia_css_frame **frame,
|
const struct ia_css_frame_info *info)
|
{
|
int err = 0;
|
struct ia_css_frame *me;
|
|
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
|
"ia_css_frame_create_from_info() enter:\n");
|
if (!frame || !info) {
|
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
|
"ia_css_frame_create_from_info() leave: invalid arguments\n");
|
return -EINVAL;
|
}
|
|
me = frame_create(info->res.width,
|
info->res.height,
|
info->format,
|
info->padded_width,
|
info->raw_bit_depth,
|
false,
|
false);
|
if (!me) {
|
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
|
"ia_css_frame_create_from_info() leave: frame create failed\n");
|
return -ENOMEM;
|
}
|
|
err = ia_css_frame_init_planes(me);
|
|
if (err) {
|
kvfree(me);
|
me = NULL;
|
}
|
|
*frame = me;
|
|
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
|
"ia_css_frame_create_from_info() leave:\n");
|
|
return err;
|
}
|
|
int ia_css_frame_set_data(struct ia_css_frame *frame,
|
const ia_css_ptr mapped_data,
|
size_t data_bytes)
|
{
|
int err = 0;
|
|
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
|
"ia_css_frame_set_data() enter:\n");
|
if (!frame) {
|
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
|
"ia_css_frame_set_data() leave: NULL frame\n");
|
return -EINVAL;
|
}
|
|
/* If we are setting a valid data.
|
* Make sure that there is enough
|
* room for the expected frame format
|
*/
|
if ((mapped_data != mmgr_NULL) && (frame->data_bytes > data_bytes)) {
|
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
|
"ia_css_frame_set_data() leave: invalid arguments\n");
|
return -EINVAL;
|
}
|
|
frame->data = mapped_data;
|
|
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE, "ia_css_frame_set_data() leave:\n");
|
|
return err;
|
}
|
|
int ia_css_frame_allocate_contiguous(struct ia_css_frame **frame,
|
unsigned int width,
|
unsigned int height,
|
enum ia_css_frame_format format,
|
unsigned int padded_width,
|
unsigned int raw_bit_depth)
|
{
|
int err = 0;
|
|
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
|
"ia_css_frame_allocate_contiguous() enter: width=%d, height=%d, format=%d, padded_width=%d, raw_bit_depth=%d\n",
|
width, height, format, padded_width, raw_bit_depth);
|
|
err = frame_allocate_with_data(frame, width, height, format,
|
padded_width, raw_bit_depth, true);
|
|
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
|
"ia_css_frame_allocate_contiguous() leave: frame=%p\n",
|
frame ? *frame : (void *)-1);
|
|
return err;
|
}
|
|
int ia_css_frame_allocate_contiguous_from_info(
|
struct ia_css_frame **frame,
|
const struct ia_css_frame_info *info)
|
{
|
int err = 0;
|
|
assert(frame);
|
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
|
"ia_css_frame_allocate_contiguous_from_info() enter:\n");
|
err = ia_css_frame_allocate_contiguous(frame,
|
info->res.width,
|
info->res.height,
|
info->format,
|
info->padded_width,
|
info->raw_bit_depth);
|
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
|
"ia_css_frame_allocate_contiguous_from_info() leave:\n");
|
return err;
|
}
|
|
void ia_css_frame_free(struct ia_css_frame *frame)
|
{
|
IA_CSS_ENTER_PRIVATE("frame = %p", frame);
|
|
if (frame) {
|
hmm_free(frame->data);
|
kvfree(frame);
|
}
|
|
IA_CSS_LEAVE_PRIVATE("void");
|
}
|
|
/**************************************************************************
|
** Module public functions
|
**************************************************************************/
|
|
int ia_css_frame_check_info(const struct ia_css_frame_info *info)
|
{
|
assert(info);
|
if (info->res.width == 0 || info->res.height == 0)
|
return -EINVAL;
|
return 0;
|
}
|
|
int ia_css_frame_init_planes(struct ia_css_frame *frame)
|
{
|
assert(frame);
|
|
switch (frame->info.format) {
|
case IA_CSS_FRAME_FORMAT_MIPI:
|
frame_init_mipi_plane(frame, &frame->planes.raw,
|
frame->info.res.height,
|
frame->info.padded_width,
|
frame->info.raw_bit_depth <= 8 ? 1 : 2);
|
break;
|
case IA_CSS_FRAME_FORMAT_RAW_PACKED:
|
frame_init_raw_single_plane(frame, &frame->planes.raw,
|
frame->info.res.height,
|
frame->info.padded_width,
|
frame->info.raw_bit_depth);
|
break;
|
case IA_CSS_FRAME_FORMAT_RAW:
|
frame_init_single_plane(frame, &frame->planes.raw,
|
frame->info.res.height,
|
frame->info.padded_width,
|
frame->info.raw_bit_depth <= 8 ? 1 : 2);
|
break;
|
case IA_CSS_FRAME_FORMAT_RGB565:
|
frame_init_single_plane(frame, &frame->planes.rgb,
|
frame->info.res.height,
|
frame->info.padded_width, 2);
|
break;
|
case IA_CSS_FRAME_FORMAT_RGBA888:
|
frame_init_single_plane(frame, &frame->planes.rgb,
|
frame->info.res.height,
|
frame->info.padded_width * 4, 1);
|
break;
|
case IA_CSS_FRAME_FORMAT_PLANAR_RGB888:
|
frame_init_rgb_planes(frame, 1);
|
break;
|
/* yuyv and uyvu have the same frame layout, only the data
|
* positioning differs.
|
*/
|
case IA_CSS_FRAME_FORMAT_YUYV:
|
case IA_CSS_FRAME_FORMAT_UYVY:
|
case IA_CSS_FRAME_FORMAT_CSI_MIPI_YUV420_8:
|
case IA_CSS_FRAME_FORMAT_CSI_MIPI_LEGACY_YUV420_8:
|
frame_init_single_plane(frame, &frame->planes.yuyv,
|
frame->info.res.height,
|
frame->info.padded_width * 2, 1);
|
break;
|
case IA_CSS_FRAME_FORMAT_YUV_LINE:
|
/* Needs 3 extra lines to allow vf_pp prefetching */
|
frame_init_single_plane(frame, &frame->planes.yuyv,
|
frame->info.res.height * 3 / 2 + 3,
|
frame->info.padded_width, 1);
|
break;
|
case IA_CSS_FRAME_FORMAT_NV11:
|
frame_init_nv_planes(frame, 4, 1, 1);
|
break;
|
/* nv12 and nv21 have the same frame layout, only the data
|
* positioning differs.
|
*/
|
case IA_CSS_FRAME_FORMAT_NV12:
|
case IA_CSS_FRAME_FORMAT_NV21:
|
case IA_CSS_FRAME_FORMAT_NV12_TILEY:
|
frame_init_nv_planes(frame, 2, 2, 1);
|
break;
|
case IA_CSS_FRAME_FORMAT_NV12_16:
|
frame_init_nv_planes(frame, 2, 2, 2);
|
break;
|
/* nv16 and nv61 have the same frame layout, only the data
|
* positioning differs.
|
*/
|
case IA_CSS_FRAME_FORMAT_NV16:
|
case IA_CSS_FRAME_FORMAT_NV61:
|
frame_init_nv_planes(frame, 2, 1, 1);
|
break;
|
case IA_CSS_FRAME_FORMAT_YUV420:
|
frame_init_yuv_planes(frame, 2, 2, false, 1);
|
break;
|
case IA_CSS_FRAME_FORMAT_YUV422:
|
frame_init_yuv_planes(frame, 2, 1, false, 1);
|
break;
|
case IA_CSS_FRAME_FORMAT_YUV444:
|
frame_init_yuv_planes(frame, 1, 1, false, 1);
|
break;
|
case IA_CSS_FRAME_FORMAT_YUV420_16:
|
frame_init_yuv_planes(frame, 2, 2, false, 2);
|
break;
|
case IA_CSS_FRAME_FORMAT_YUV422_16:
|
frame_init_yuv_planes(frame, 2, 1, false, 2);
|
break;
|
case IA_CSS_FRAME_FORMAT_YV12:
|
frame_init_yuv_planes(frame, 2, 2, true, 1);
|
break;
|
case IA_CSS_FRAME_FORMAT_YV16:
|
frame_init_yuv_planes(frame, 2, 1, true, 1);
|
break;
|
case IA_CSS_FRAME_FORMAT_QPLANE6:
|
frame_init_qplane6_planes(frame);
|
break;
|
case IA_CSS_FRAME_FORMAT_BINARY_8:
|
frame_init_single_plane(frame, &frame->planes.binary.data,
|
frame->info.res.height,
|
frame->info.padded_width, 1);
|
frame->planes.binary.size = 0;
|
break;
|
default:
|
return -EINVAL;
|
}
|
return 0;
|
}
|
|
void ia_css_frame_info_set_width(struct ia_css_frame_info *info,
|
unsigned int width,
|
unsigned int min_padded_width)
|
{
|
unsigned int align;
|
|
IA_CSS_ENTER_PRIVATE("info = %p,width = %d, minimum padded width = %d",
|
info, width, min_padded_width);
|
if (!info) {
|
IA_CSS_ERROR("NULL input parameter");
|
IA_CSS_LEAVE_PRIVATE("");
|
return;
|
}
|
if (min_padded_width > width)
|
align = min_padded_width;
|
else
|
align = width;
|
|
info->res.width = width;
|
/* frames with a U and V plane of 8 bits per pixel need to have
|
all planes aligned, this means double the alignment for the
|
Y plane if the horizontal decimation is 2. */
|
if (info->format == IA_CSS_FRAME_FORMAT_YUV420 ||
|
info->format == IA_CSS_FRAME_FORMAT_YV12 ||
|
info->format == IA_CSS_FRAME_FORMAT_NV12 ||
|
info->format == IA_CSS_FRAME_FORMAT_NV21 ||
|
info->format == IA_CSS_FRAME_FORMAT_BINARY_8 ||
|
info->format == IA_CSS_FRAME_FORMAT_YUV_LINE)
|
info->padded_width =
|
CEIL_MUL(align, 2 * HIVE_ISP_DDR_WORD_BYTES);
|
else if (info->format == IA_CSS_FRAME_FORMAT_NV12_TILEY)
|
info->padded_width = CEIL_MUL(align, NV12_TILEY_TILE_WIDTH);
|
else if (info->format == IA_CSS_FRAME_FORMAT_RAW ||
|
info->format == IA_CSS_FRAME_FORMAT_RAW_PACKED)
|
info->padded_width = CEIL_MUL(align, 2 * ISP_VEC_NELEMS);
|
else {
|
info->padded_width = CEIL_MUL(align, HIVE_ISP_DDR_WORD_BYTES);
|
}
|
IA_CSS_LEAVE_PRIVATE("");
|
}
|
|
void ia_css_frame_info_set_format(struct ia_css_frame_info *info,
|
enum ia_css_frame_format format)
|
{
|
assert(info);
|
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
|
"ia_css_frame_info_set_format() enter:\n");
|
info->format = format;
|
}
|
|
void ia_css_frame_info_init(struct ia_css_frame_info *info,
|
unsigned int width,
|
unsigned int height,
|
enum ia_css_frame_format format,
|
unsigned int aligned)
|
{
|
IA_CSS_ENTER_PRIVATE("info = %p, width = %d, height = %d, format = %d, aligned = %d",
|
info, width, height, format, aligned);
|
if (!info) {
|
IA_CSS_ERROR("NULL input parameter");
|
IA_CSS_LEAVE_PRIVATE("");
|
return;
|
}
|
info->res.height = height;
|
info->format = format;
|
ia_css_frame_info_set_width(info, width, aligned);
|
IA_CSS_LEAVE_PRIVATE("");
|
}
|
|
void ia_css_frame_free_multiple(unsigned int num_frames,
|
struct ia_css_frame **frames_array)
|
{
|
unsigned int i;
|
|
for (i = 0; i < num_frames; i++) {
|
if (frames_array[i]) {
|
ia_css_frame_free(frames_array[i]);
|
frames_array[i] = NULL;
|
}
|
}
|
}
|
|
int ia_css_frame_allocate_with_buffer_size(
|
struct ia_css_frame **frame,
|
const unsigned int buffer_size_bytes,
|
const bool contiguous)
|
{
|
/* AM: Body coppied from frame_allocate_with_data(). */
|
int err;
|
struct ia_css_frame *me = frame_create(0, 0,
|
IA_CSS_FRAME_FORMAT_NUM,/* Not valid format yet */
|
0, 0, contiguous, false);
|
|
if (!me)
|
return -ENOMEM;
|
|
/* Get the data size */
|
me->data_bytes = buffer_size_bytes;
|
|
err = frame_allocate_buffer_data(me);
|
|
if (err) {
|
kvfree(me);
|
me = NULL;
|
}
|
|
*frame = me;
|
|
return err;
|
}
|
|
bool ia_css_frame_info_is_same_resolution(
|
const struct ia_css_frame_info *info_a,
|
const struct ia_css_frame_info *info_b)
|
{
|
if (!info_a || !info_b)
|
return false;
|
return (info_a->res.width == info_b->res.width) &&
|
(info_a->res.height == info_b->res.height);
|
}
|
|
bool ia_css_frame_is_same_type(const struct ia_css_frame *frame_a,
|
const struct ia_css_frame *frame_b)
|
{
|
bool is_equal = false;
|
const struct ia_css_frame_info *info_a = &frame_a->info,
|
*info_b = &frame_b->info;
|
|
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
|
"ia_css_frame_is_same_type() enter:\n");
|
|
if (!info_a || !info_b)
|
return false;
|
if (info_a->format != info_b->format)
|
return false;
|
if (info_a->padded_width != info_b->padded_width)
|
return false;
|
is_equal = ia_css_frame_info_is_same_resolution(info_a, info_b);
|
|
ia_css_debug_dtrace(IA_CSS_DEBUG_TRACE,
|
"ia_css_frame_is_same_type() leave:\n");
|
|
return is_equal;
|
}
|
|
void
|
ia_css_dma_configure_from_info(
|
struct dma_port_config *config,
|
const struct ia_css_frame_info *info)
|
{
|
unsigned int is_raw_packed = info->format == IA_CSS_FRAME_FORMAT_RAW_PACKED;
|
unsigned int bits_per_pixel = is_raw_packed ? info->raw_bit_depth :
|
ia_css_elems_bytes_from_info(info) * 8;
|
unsigned int pix_per_ddrword = HIVE_ISP_DDR_WORD_BITS / bits_per_pixel;
|
unsigned int words_per_line = CEIL_DIV(info->padded_width, pix_per_ddrword);
|
unsigned int elems_b = pix_per_ddrword;
|
|
config->stride = HIVE_ISP_DDR_WORD_BYTES * words_per_line;
|
config->elems = (uint8_t)elems_b;
|
config->width = (uint16_t)info->res.width;
|
config->crop = 0;
|
assert(config->width <= info->padded_width);
|
}
|
|
/**************************************************************************
|
** Static functions
|
**************************************************************************/
|
|
static void frame_init_plane(struct ia_css_frame_plane *plane,
|
unsigned int width,
|
unsigned int stride,
|
unsigned int height,
|
unsigned int offset)
|
{
|
plane->height = height;
|
plane->width = width;
|
plane->stride = stride;
|
plane->offset = offset;
|
}
|
|
static void frame_init_single_plane(struct ia_css_frame *frame,
|
struct ia_css_frame_plane *plane,
|
unsigned int height,
|
unsigned int subpixels_per_line,
|
unsigned int bytes_per_pixel)
|
{
|
unsigned int stride;
|
|
stride = subpixels_per_line * bytes_per_pixel;
|
/* Frame height needs to be even number - needed by hw ISYS2401
|
In case of odd number, round up to even.
|
Images won't be impacted by this round up,
|
only needed by jpeg/embedded data.
|
As long as buffer allocation and release are using data_bytes,
|
there won't be memory leak. */
|
frame->data_bytes = stride * CEIL_MUL2(height, 2);
|
frame_init_plane(plane, subpixels_per_line, stride, height, 0);
|
return;
|
}
|
|
static void frame_init_raw_single_plane(
|
struct ia_css_frame *frame,
|
struct ia_css_frame_plane *plane,
|
unsigned int height,
|
unsigned int subpixels_per_line,
|
unsigned int bits_per_pixel)
|
{
|
unsigned int stride;
|
|
assert(frame);
|
|
stride = HIVE_ISP_DDR_WORD_BYTES *
|
CEIL_DIV(subpixels_per_line,
|
HIVE_ISP_DDR_WORD_BITS / bits_per_pixel);
|
frame->data_bytes = stride * height;
|
frame_init_plane(plane, subpixels_per_line, stride, height, 0);
|
return;
|
}
|
|
static void frame_init_mipi_plane(struct ia_css_frame *frame,
|
struct ia_css_frame_plane *plane,
|
unsigned int height,
|
unsigned int subpixels_per_line,
|
unsigned int bytes_per_pixel)
|
{
|
unsigned int stride;
|
|
stride = subpixels_per_line * bytes_per_pixel;
|
frame->data_bytes = 8388608; /* 8*1024*1024 */
|
frame->valid = false;
|
frame->contiguous = true;
|
frame_init_plane(plane, subpixels_per_line, stride, height, 0);
|
return;
|
}
|
|
static void frame_init_nv_planes(struct ia_css_frame *frame,
|
unsigned int horizontal_decimation,
|
unsigned int vertical_decimation,
|
unsigned int bytes_per_element)
|
{
|
unsigned int y_width = frame->info.padded_width;
|
unsigned int y_height = frame->info.res.height;
|
unsigned int uv_width;
|
unsigned int uv_height;
|
unsigned int y_bytes;
|
unsigned int uv_bytes;
|
unsigned int y_stride;
|
unsigned int uv_stride;
|
|
assert(horizontal_decimation != 0 && vertical_decimation != 0);
|
|
uv_width = 2 * (y_width / horizontal_decimation);
|
uv_height = y_height / vertical_decimation;
|
|
if (frame->info.format == IA_CSS_FRAME_FORMAT_NV12_TILEY) {
|
y_width = CEIL_MUL(y_width, NV12_TILEY_TILE_WIDTH);
|
uv_width = CEIL_MUL(uv_width, NV12_TILEY_TILE_WIDTH);
|
y_height = CEIL_MUL(y_height, NV12_TILEY_TILE_HEIGHT);
|
uv_height = CEIL_MUL(uv_height, NV12_TILEY_TILE_HEIGHT);
|
}
|
|
y_stride = y_width * bytes_per_element;
|
uv_stride = uv_width * bytes_per_element;
|
y_bytes = y_stride * y_height;
|
uv_bytes = uv_stride * uv_height;
|
|
frame->data_bytes = y_bytes + uv_bytes;
|
frame_init_plane(&frame->planes.nv.y, y_width, y_stride, y_height, 0);
|
frame_init_plane(&frame->planes.nv.uv, uv_width,
|
uv_stride, uv_height, y_bytes);
|
return;
|
}
|
|
static void frame_init_yuv_planes(struct ia_css_frame *frame,
|
unsigned int horizontal_decimation,
|
unsigned int vertical_decimation,
|
bool swap_uv,
|
unsigned int bytes_per_element)
|
{
|
unsigned int y_width = frame->info.padded_width,
|
y_height = frame->info.res.height,
|
uv_width = y_width / horizontal_decimation,
|
uv_height = y_height / vertical_decimation,
|
y_stride, y_bytes, uv_bytes, uv_stride;
|
|
y_stride = y_width * bytes_per_element;
|
uv_stride = uv_width * bytes_per_element;
|
y_bytes = y_stride * y_height;
|
uv_bytes = uv_stride * uv_height;
|
|
frame->data_bytes = y_bytes + 2 * uv_bytes;
|
frame_init_plane(&frame->planes.yuv.y, y_width, y_stride, y_height, 0);
|
if (swap_uv) {
|
frame_init_plane(&frame->planes.yuv.v, uv_width, uv_stride,
|
uv_height, y_bytes);
|
frame_init_plane(&frame->planes.yuv.u, uv_width, uv_stride,
|
uv_height, y_bytes + uv_bytes);
|
} else {
|
frame_init_plane(&frame->planes.yuv.u, uv_width, uv_stride,
|
uv_height, y_bytes);
|
frame_init_plane(&frame->planes.yuv.v, uv_width, uv_stride,
|
uv_height, y_bytes + uv_bytes);
|
}
|
return;
|
}
|
|
static void frame_init_rgb_planes(struct ia_css_frame *frame,
|
unsigned int bytes_per_element)
|
{
|
unsigned int width = frame->info.res.width,
|
height = frame->info.res.height, stride, bytes;
|
|
stride = width * bytes_per_element;
|
bytes = stride * height;
|
frame->data_bytes = 3 * bytes;
|
frame_init_plane(&frame->planes.planar_rgb.r, width, stride, height, 0);
|
frame_init_plane(&frame->planes.planar_rgb.g,
|
width, stride, height, 1 * bytes);
|
frame_init_plane(&frame->planes.planar_rgb.b,
|
width, stride, height, 2 * bytes);
|
return;
|
}
|
|
static void frame_init_qplane6_planes(struct ia_css_frame *frame)
|
{
|
unsigned int width = frame->info.padded_width / 2,
|
height = frame->info.res.height / 2, bytes, stride;
|
|
stride = width * 2;
|
bytes = stride * height;
|
|
frame->data_bytes = 6 * bytes;
|
frame_init_plane(&frame->planes.plane6.r,
|
width, stride, height, 0 * bytes);
|
frame_init_plane(&frame->planes.plane6.r_at_b,
|
width, stride, height, 1 * bytes);
|
frame_init_plane(&frame->planes.plane6.gr,
|
width, stride, height, 2 * bytes);
|
frame_init_plane(&frame->planes.plane6.gb,
|
width, stride, height, 3 * bytes);
|
frame_init_plane(&frame->planes.plane6.b,
|
width, stride, height, 4 * bytes);
|
frame_init_plane(&frame->planes.plane6.b_at_r,
|
width, stride, height, 5 * bytes);
|
return;
|
}
|
|
static int frame_allocate_buffer_data(struct ia_css_frame *frame)
|
{
|
#ifdef ISP2401
|
IA_CSS_ENTER_LEAVE_PRIVATE("frame->data_bytes=%d\n", frame->data_bytes);
|
#endif
|
frame->data = hmm_alloc(frame->data_bytes,
|
HMM_BO_PRIVATE, 0, NULL,
|
frame->contiguous ?
|
ATOMISP_MAP_FLAG_CONTIGUOUS : 0);
|
|
if (frame->data == mmgr_NULL)
|
return -ENOMEM;
|
return 0;
|
}
|
|
static int frame_allocate_with_data(struct ia_css_frame **frame,
|
unsigned int width,
|
unsigned int height,
|
enum ia_css_frame_format format,
|
unsigned int padded_width,
|
unsigned int raw_bit_depth,
|
bool contiguous)
|
{
|
int err;
|
struct ia_css_frame *me = frame_create(width,
|
height,
|
format,
|
padded_width,
|
raw_bit_depth,
|
contiguous,
|
true);
|
|
if (!me)
|
return -ENOMEM;
|
|
err = ia_css_frame_init_planes(me);
|
|
if (!err)
|
err = frame_allocate_buffer_data(me);
|
|
if (err) {
|
kvfree(me);
|
#ifndef ISP2401
|
return err;
|
#else
|
me = NULL;
|
#endif
|
}
|
|
*frame = me;
|
|
return err;
|
}
|
|
static struct ia_css_frame *frame_create(unsigned int width,
|
unsigned int height,
|
enum ia_css_frame_format format,
|
unsigned int padded_width,
|
unsigned int raw_bit_depth,
|
bool contiguous,
|
bool valid)
|
{
|
struct ia_css_frame *me = kvmalloc(sizeof(*me), GFP_KERNEL);
|
|
if (!me)
|
return NULL;
|
|
memset(me, 0, sizeof(*me));
|
me->info.res.width = width;
|
me->info.res.height = height;
|
me->info.format = format;
|
me->info.padded_width = padded_width;
|
me->info.raw_bit_depth = raw_bit_depth;
|
me->contiguous = contiguous;
|
me->valid = valid;
|
me->data_bytes = 0;
|
me->data = mmgr_NULL;
|
/* To indicate it is not valid frame. */
|
me->dynamic_queue_id = (int)SH_CSS_INVALID_QUEUE_ID;
|
me->buf_type = IA_CSS_BUFFER_TYPE_INVALID;
|
|
return me;
|
}
|
|
static unsigned
|
ia_css_elems_bytes_from_info(const struct ia_css_frame_info *info)
|
{
|
if (info->format == IA_CSS_FRAME_FORMAT_RGB565)
|
return 2; /* bytes per pixel */
|
if (info->format == IA_CSS_FRAME_FORMAT_YUV420_16)
|
return 2; /* bytes per pixel */
|
if (info->format == IA_CSS_FRAME_FORMAT_YUV422_16)
|
return 2; /* bytes per pixel */
|
/* Note: Essentially NV12_16 is a 2 bytes per pixel format, this return value is used
|
* to configure DMA for the output buffer,
|
* At least in SKC this data is overwritten by isp_output_init.sp.c except for elements(elems),
|
* which is configured from this return value,
|
* NV12_16 is implemented by a double buffer of 8 bit elements hence elems should be configured as 8 */
|
if (info->format == IA_CSS_FRAME_FORMAT_NV12_16)
|
return 1; /* bytes per pixel */
|
|
if (info->format == IA_CSS_FRAME_FORMAT_RAW
|
|| (info->format == IA_CSS_FRAME_FORMAT_RAW_PACKED)) {
|
if (info->raw_bit_depth)
|
return CEIL_DIV(info->raw_bit_depth, 8);
|
else
|
return 2; /* bytes per pixel */
|
}
|
if (info->format == IA_CSS_FRAME_FORMAT_PLANAR_RGB888)
|
return 3; /* bytes per pixel */
|
if (info->format == IA_CSS_FRAME_FORMAT_RGBA888)
|
return 4; /* bytes per pixel */
|
if (info->format == IA_CSS_FRAME_FORMAT_QPLANE6)
|
return 2; /* bytes per pixel */
|
return 1; /* Default is 1 byte per pixel */
|
}
|
|
void ia_css_frame_info_to_frame_sp_info(
|
struct ia_css_frame_sp_info *to,
|
const struct ia_css_frame_info *from)
|
{
|
ia_css_resolution_to_sp_resolution(&to->res, &from->res);
|
to->padded_width = (uint16_t)from->padded_width;
|
to->format = (uint8_t)from->format;
|
to->raw_bit_depth = (uint8_t)from->raw_bit_depth;
|
to->raw_bayer_order = from->raw_bayer_order;
|
}
|
|
void ia_css_resolution_to_sp_resolution(
|
struct ia_css_sp_resolution *to,
|
const struct ia_css_resolution *from)
|
{
|
to->width = (uint16_t)from->width;
|
to->height = (uint16_t)from->height;
|
}
|
|
/* ISP2401 */
|
int
|
ia_css_frame_find_crop_resolution(const struct ia_css_resolution *in_res,
|
const struct ia_css_resolution *out_res,
|
struct ia_css_resolution *crop_res) {
|
u32 wd_even_ceil, ht_even_ceil;
|
u32 in_ratio, out_ratio;
|
|
if ((!in_res) || (!out_res) || (!crop_res))
|
return -EINVAL;
|
|
IA_CSS_ENTER_PRIVATE("in(%ux%u) -> out(%ux%u)", in_res->width,
|
in_res->height, out_res->width, out_res->height);
|
|
if ((in_res->width == 0)
|
|| (in_res->height == 0)
|
|| (out_res->width == 0)
|
|| (out_res->height == 0))
|
return -EINVAL;
|
|
if ((out_res->width > in_res->width) ||
|
(out_res->height > in_res->height))
|
return -EINVAL;
|
|
/* If aspect ratio (width/height) of out_res is higher than the aspect
|
* ratio of the in_res, then we crop vertically, otherwise we crop
|
* horizontally.
|
*/
|
in_ratio = in_res->width * out_res->height;
|
out_ratio = out_res->width * in_res->height;
|
|
if (in_ratio == out_ratio)
|
{
|
crop_res->width = in_res->width;
|
crop_res->height = in_res->height;
|
} else if (out_ratio > in_ratio)
|
{
|
crop_res->width = in_res->width;
|
crop_res->height = ROUND_DIV(out_res->height * crop_res->width,
|
out_res->width);
|
} else
|
{
|
crop_res->height = in_res->height;
|
crop_res->width = ROUND_DIV(out_res->width * crop_res->height,
|
out_res->height);
|
}
|
|
/* Round new (cropped) width and height to an even number.
|
* binarydesc_calculate_bds_factor is such that we should consider as
|
* much of the input as possible. This is different only when we end up
|
* with an odd number in the last step. So, we take the next even number
|
* if it falls within the input, otherwise take the previous even no.
|
*/
|
wd_even_ceil = EVEN_CEIL(crop_res->width);
|
ht_even_ceil = EVEN_CEIL(crop_res->height);
|
if ((wd_even_ceil > in_res->width) || (ht_even_ceil > in_res->height))
|
{
|
crop_res->width = EVEN_FLOOR(crop_res->width);
|
crop_res->height = EVEN_FLOOR(crop_res->height);
|
} else
|
{
|
crop_res->width = wd_even_ceil;
|
crop_res->height = ht_even_ceil;
|
}
|
|
IA_CSS_LEAVE_PRIVATE("in(%ux%u) -> out(%ux%u)", crop_res->width,
|
crop_res->height, out_res->width, out_res->height);
|
return 0;
|
}
|