1177 lines
34 KiB
C
1177 lines
34 KiB
C
|
/*
|
||
|
* linux-4.9/drivers/media/platform/sunxi-vfe/sunxi_isp.c
|
||
|
*
|
||
|
* Copyright (c) 2007-2017 Allwinnertech Co., Ltd.
|
||
|
*
|
||
|
* This software is licensed under the terms of the GNU General Public
|
||
|
* License version 2, as published by the Free Software Foundation, and
|
||
|
* may be copied, distributed, and modified under those terms.
|
||
|
*
|
||
|
* This program 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 General Public License for more details.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
***************************************************************************************
|
||
|
*
|
||
|
* sunxi_isp.c
|
||
|
*
|
||
|
* Hawkview ISP - sunxi_isp.c module
|
||
|
*
|
||
|
* Copyright (c) 2014 by Allwinnertech Co., Ltd. http://www.allwinnertech.com
|
||
|
*
|
||
|
* Version Author Date Description
|
||
|
*
|
||
|
* 3.0 Yang Feng 2014/12/11 ISP Tuning Tools Support
|
||
|
*
|
||
|
****************************************************************************************
|
||
|
*/
|
||
|
|
||
|
#include <linux/platform_device.h>
|
||
|
#include <linux/kernel.h>
|
||
|
#include <linux/module.h>
|
||
|
#include <media/v4l2-device.h>
|
||
|
#include <media/v4l2-mediabus.h>
|
||
|
#include <media/v4l2-subdev.h>
|
||
|
#include "vfe_os.h"
|
||
|
#include "platform_cfg.h"
|
||
|
#include "lib/bsp_isp.h"
|
||
|
#include "sunxi_isp.h"
|
||
|
#include "vfe.h"
|
||
|
/*
|
||
|
*#define ISP_MODULE_NAME "sunxi_isp"
|
||
|
*#if defined CONFIG_ARCH_SUN50I
|
||
|
*#define ISP_HEIGHT_16B_ALIGN 0
|
||
|
*#else
|
||
|
*#define ISP_HEIGHT_16B_ALIGN 1
|
||
|
*#endif
|
||
|
*/
|
||
|
#define ISP_MODULE_NAME "sunxi_isp"
|
||
|
#if defined CONFIG_ARCH_SUN8IW8P1
|
||
|
#define ISP_HEIGHT_16B_ALIGN 1
|
||
|
#else
|
||
|
#define ISP_HEIGHT_16B_ALIGN 0
|
||
|
#endif
|
||
|
|
||
|
static int isp_dbg_en;
|
||
|
static int isp_dbg_lv = 1;
|
||
|
|
||
|
static LIST_HEAD(isp_drv_list);
|
||
|
|
||
|
static const struct isp_pix_fmt sunxi_isp_formats[] = {
|
||
|
{
|
||
|
.name = "RAW8 (GRBG)",
|
||
|
.fourcc = V4L2_PIX_FMT_SGRBG8,
|
||
|
.depth = { 8 },
|
||
|
.color = 0,
|
||
|
.memplanes = 1,
|
||
|
.mbus_code = MEDIA_BUS_FMT_SGRBG8_1X8,
|
||
|
}, {
|
||
|
.name = "RAW10 (GRBG)",
|
||
|
.fourcc = V4L2_PIX_FMT_SGRBG10,
|
||
|
.depth = { 10 },
|
||
|
.color = 0,
|
||
|
.memplanes = 1,
|
||
|
.mbus_code = MEDIA_BUS_FMT_SGRBG10_1X10,
|
||
|
}, {
|
||
|
.name = "RAW12 (GRBG)",
|
||
|
.fourcc = V4L2_PIX_FMT_SGRBG12,
|
||
|
.depth = { 12 },
|
||
|
.color = 0,
|
||
|
.memplanes = 1,
|
||
|
.mbus_code = MEDIA_BUS_FMT_SGRBG12_1X12,
|
||
|
},
|
||
|
};
|
||
|
|
||
|
static int __isp_set_input_fmt_internal(enum bus_pixeltype type)
|
||
|
{
|
||
|
enum isp_input_fmt fmt;
|
||
|
enum isp_input_seq seq_t;
|
||
|
|
||
|
switch (type) {
|
||
|
/* yuv420 */
|
||
|
case BUS_FMT_YY_YUYV:
|
||
|
fmt = ISP_YUV420;
|
||
|
seq_t = ISP_YUYV;
|
||
|
break;
|
||
|
case BUS_FMT_YY_YVYU:
|
||
|
fmt = ISP_YUV420;
|
||
|
seq_t = ISP_YVYU;
|
||
|
break;
|
||
|
case BUS_FMT_YY_UYVY:
|
||
|
fmt = ISP_YUV420;
|
||
|
seq_t = ISP_UYVY;
|
||
|
break;
|
||
|
case BUS_FMT_YY_VYUY:
|
||
|
fmt = ISP_YUV420;
|
||
|
seq_t = ISP_VYUY;
|
||
|
break;
|
||
|
/* yuv422 */
|
||
|
case BUS_FMT_YUYV:
|
||
|
fmt = ISP_YUV422;
|
||
|
seq_t = ISP_YUYV;
|
||
|
break;
|
||
|
case BUS_FMT_YVYU:
|
||
|
fmt = ISP_YUV422;
|
||
|
seq_t = ISP_YVYU;
|
||
|
break;
|
||
|
case BUS_FMT_UYVY:
|
||
|
fmt = ISP_YUV422;
|
||
|
seq_t = ISP_UYVY;
|
||
|
break;
|
||
|
case BUS_FMT_VYUY:
|
||
|
fmt = ISP_YUV422;
|
||
|
seq_t = ISP_VYUY;
|
||
|
break;
|
||
|
/* raw */
|
||
|
case BUS_FMT_SBGGR:
|
||
|
fmt = ISP_RAW;
|
||
|
seq_t = ISP_BGGR;
|
||
|
break;
|
||
|
case BUS_FMT_SGBRG:
|
||
|
fmt = ISP_RAW;
|
||
|
seq_t = ISP_GBRG;
|
||
|
break;
|
||
|
case BUS_FMT_SGRBG:
|
||
|
fmt = ISP_RAW;
|
||
|
seq_t = ISP_GRBG;
|
||
|
break;
|
||
|
case BUS_FMT_SRGGB:
|
||
|
fmt = ISP_RAW;
|
||
|
seq_t = ISP_RGGB;
|
||
|
break;
|
||
|
default:
|
||
|
return -1;
|
||
|
}
|
||
|
bsp_isp_set_input_fmt(fmt, seq_t);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int __isp_set_output_fmt_internal(enum pixel_fmt fmt, enum isp_channel ch)
|
||
|
{
|
||
|
enum isp_output_fmt isp_fmt;
|
||
|
enum isp_output_seq seq_t;
|
||
|
|
||
|
switch (fmt) {
|
||
|
/* yuv_p */
|
||
|
case PIX_FMT_YUV422P_8:
|
||
|
isp_fmt = ISP_YUV422_P;
|
||
|
seq_t = ISP_UV;
|
||
|
break;
|
||
|
case PIX_FMT_YVU422P_8:
|
||
|
isp_fmt = ISP_YUV422_P;
|
||
|
seq_t = ISP_VU;
|
||
|
break;
|
||
|
case PIX_FMT_YUV420P_8:
|
||
|
isp_fmt = ISP_YUV420_P;
|
||
|
seq_t = ISP_UV;
|
||
|
break;
|
||
|
case PIX_FMT_YVU420P_8:
|
||
|
isp_fmt = ISP_YUV420_P;
|
||
|
seq_t = ISP_VU;
|
||
|
break;
|
||
|
/* yuv_sp */
|
||
|
case PIX_FMT_YUV420SP_8:
|
||
|
isp_fmt = ISP_YUV420_SP;
|
||
|
seq_t = ISP_UV;
|
||
|
break;
|
||
|
case PIX_FMT_YVU420SP_8:
|
||
|
isp_fmt = ISP_YUV420_SP;
|
||
|
seq_t = ISP_VU;
|
||
|
break;
|
||
|
case PIX_FMT_YUV422SP_8:
|
||
|
isp_fmt = ISP_YUV422_SP;
|
||
|
seq_t = ISP_UV;
|
||
|
break;
|
||
|
case PIX_FMT_YVU422SP_8:
|
||
|
isp_fmt = ISP_YUV422_SP;
|
||
|
seq_t = ISP_VU;
|
||
|
break;
|
||
|
default:
|
||
|
return -1;
|
||
|
}
|
||
|
bsp_isp_set_output_fmt(isp_fmt, seq_t, ch);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int __isp_cal_ch_size(enum pixel_fmt fmt, struct isp_size *size,
|
||
|
struct isp_yuv_size_addr_info *isp_size_info)
|
||
|
{
|
||
|
switch (fmt) {
|
||
|
case PIX_FMT_YUV420P_8:
|
||
|
case PIX_FMT_YVU420P_8:
|
||
|
isp_size_info->line_stride_y = ALIGN_16B(size->width);
|
||
|
isp_size_info->line_stride_c = ALIGN_16B(isp_size_info->line_stride_y>>1);
|
||
|
if (ISP_HEIGHT_16B_ALIGN)
|
||
|
isp_size_info->buf_height_y = ALIGN_16B(size->height);
|
||
|
else
|
||
|
isp_size_info->buf_height_y = size->height;
|
||
|
|
||
|
isp_size_info->buf_height_cb = isp_size_info->buf_height_y >> 1;
|
||
|
isp_size_info->buf_height_cr = isp_size_info->buf_height_y >> 1;
|
||
|
isp_size_info->valid_height_y = size->height;
|
||
|
isp_size_info->valid_height_cb = isp_size_info->valid_height_y >> 1;
|
||
|
isp_size_info->valid_height_cr = isp_size_info->valid_height_y >> 1;
|
||
|
break;
|
||
|
case PIX_FMT_YUV422P_8:
|
||
|
case PIX_FMT_YVU422P_8:
|
||
|
isp_size_info->line_stride_y = ALIGN_16B(size->width);
|
||
|
isp_size_info->line_stride_c = ALIGN_16B(isp_size_info->line_stride_y>>1);
|
||
|
if (ISP_HEIGHT_16B_ALIGN)
|
||
|
isp_size_info->buf_height_y = ALIGN_16B(size->height);
|
||
|
else
|
||
|
isp_size_info->buf_height_y = size->height;
|
||
|
|
||
|
isp_size_info->buf_height_cb = isp_size_info->buf_height_y;
|
||
|
isp_size_info->buf_height_cr = isp_size_info->buf_height_y;
|
||
|
|
||
|
isp_size_info->valid_height_y = size->height;
|
||
|
isp_size_info->valid_height_cb = isp_size_info->valid_height_y;
|
||
|
isp_size_info->valid_height_cr = isp_size_info->valid_height_y;
|
||
|
break;
|
||
|
case PIX_FMT_YUV420SP_8:
|
||
|
case PIX_FMT_YVU420SP_8:
|
||
|
isp_size_info->line_stride_y = ALIGN_16B(size->width);
|
||
|
isp_size_info->line_stride_c = isp_size_info->line_stride_y;
|
||
|
if (ISP_HEIGHT_16B_ALIGN)
|
||
|
isp_size_info->buf_height_y = ALIGN_16B(size->height);
|
||
|
else
|
||
|
isp_size_info->buf_height_y = size->height;
|
||
|
|
||
|
isp_size_info->buf_height_cb = isp_size_info->buf_height_y>>1;
|
||
|
isp_size_info->buf_height_cr = 0;
|
||
|
|
||
|
isp_size_info->valid_height_y = size->height;
|
||
|
isp_size_info->valid_height_cb = isp_size_info->valid_height_y>>1;
|
||
|
isp_size_info->valid_height_cr = 0;
|
||
|
break;
|
||
|
case PIX_FMT_YUV422SP_8:
|
||
|
case PIX_FMT_YVU422SP_8:
|
||
|
isp_size_info->line_stride_y = ALIGN_16B(size->width);
|
||
|
isp_size_info->line_stride_c = isp_size_info->line_stride_y;
|
||
|
if (ISP_HEIGHT_16B_ALIGN)
|
||
|
isp_size_info->buf_height_y = ALIGN_16B(size->height);
|
||
|
else
|
||
|
isp_size_info->buf_height_y = size->height;
|
||
|
|
||
|
isp_size_info->buf_height_cb = isp_size_info->buf_height_y;
|
||
|
isp_size_info->buf_height_cr = 0;
|
||
|
|
||
|
isp_size_info->valid_height_y = size->height;
|
||
|
isp_size_info->valid_height_cb = isp_size_info->valid_height_y;
|
||
|
isp_size_info->valid_height_cr = 0;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
isp_size_info->isp_byte_size = isp_size_info->line_stride_y * isp_size_info->buf_height_y +
|
||
|
isp_size_info->line_stride_c * isp_size_info->buf_height_cb +
|
||
|
isp_size_info->line_stride_c * isp_size_info->buf_height_cr;
|
||
|
|
||
|
return isp_size_info->isp_byte_size;
|
||
|
}
|
||
|
|
||
|
static int __isp_cal_ch_addr(enum enable_flag flip, unsigned int buf_base_addr,
|
||
|
struct isp_yuv_size_addr_info *isp_size_info)
|
||
|
{
|
||
|
isp_size_info->yuv_addr.y_addr = buf_base_addr;
|
||
|
isp_size_info->yuv_addr.u_addr = isp_size_info->yuv_addr.y_addr + isp_size_info->line_stride_y * isp_size_info->buf_height_y;
|
||
|
isp_size_info->yuv_addr.v_addr = isp_size_info->yuv_addr.u_addr + isp_size_info->line_stride_c * isp_size_info->buf_height_cb;
|
||
|
if (flip == ENABLE) {
|
||
|
isp_size_info->yuv_addr.y_addr = isp_size_info->yuv_addr.y_addr +
|
||
|
isp_size_info->line_stride_y * isp_size_info->valid_height_y - isp_size_info->line_stride_y;
|
||
|
isp_size_info->yuv_addr.u_addr = isp_size_info->yuv_addr.u_addr +
|
||
|
isp_size_info->line_stride_c * isp_size_info->valid_height_cb - isp_size_info->line_stride_c;
|
||
|
isp_size_info->yuv_addr.v_addr = isp_size_info->yuv_addr.v_addr +
|
||
|
isp_size_info->line_stride_c * isp_size_info->valid_height_cr - isp_size_info->line_stride_c;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
static unsigned int __isp_new_set_size_internal(struct isp_dev *isp,
|
||
|
enum pixel_fmt *fmt,
|
||
|
struct isp_size_settings *size_settings)
|
||
|
{
|
||
|
int x_ratio, y_ratio, weight_shift;
|
||
|
struct coor *ob_start = &size_settings->ob_start;
|
||
|
struct isp_size *ob_black_size, *ob_valid_size, *full_size, *scale_size, *ob_rot_size;
|
||
|
struct isp_yuv_size_addr_info *isp_yuv_size_addr = &isp->isp_yuv_size_addr[0];
|
||
|
|
||
|
ob_black_size = &size_settings->ob_black_size;
|
||
|
ob_valid_size = &size_settings->ob_valid_size;
|
||
|
full_size = &size_settings->full_size;
|
||
|
scale_size = &size_settings->scale_size;
|
||
|
ob_rot_size = &size_settings->ob_rot_size;
|
||
|
|
||
|
bsp_isp_set_ob_zone(ob_black_size, ob_valid_size, ob_start, ISP_SRC0);
|
||
|
if (scale_size && scale_size->width != 0 && scale_size->height != 0) {
|
||
|
full_size->width = full_size->width&0x1ffc;
|
||
|
full_size->height = full_size->height&(~1);
|
||
|
pr_info("[ISP] full_size width = %d, height = %d.\n", full_size->width, full_size->height);
|
||
|
x_ratio = ob_valid_size->width * 256 / full_size->width;
|
||
|
y_ratio = ob_valid_size->height * 256 / full_size->height;
|
||
|
weight_shift = min_scale_w_shift(x_ratio, y_ratio);
|
||
|
bsp_isp_channel_enable(MAIN_CH);
|
||
|
bsp_isp_scale_enable(MAIN_CH);
|
||
|
bsp_isp_set_output_size(MAIN_CH, full_size);
|
||
|
bsp_isp_scale_cfg(MAIN_CH, x_ratio, y_ratio, weight_shift);
|
||
|
|
||
|
__isp_cal_ch_size(fmt[MAIN_CH], full_size, &isp_yuv_size_addr[MAIN_CH]);
|
||
|
bsp_isp_set_stride_y(isp_yuv_size_addr[MAIN_CH].line_stride_y, MAIN_CH);
|
||
|
bsp_isp_set_stride_uv(isp_yuv_size_addr[MAIN_CH].line_stride_c, MAIN_CH);
|
||
|
/* scale channel */
|
||
|
/* scale config */
|
||
|
scale_size->width = scale_size->width&0x1ffc;/* 4 byte */
|
||
|
scale_size->height = scale_size->height&(~1);
|
||
|
pr_info("[ISP] scale width = %d, height = %d\n", scale_size->width, scale_size->height);
|
||
|
x_ratio = ob_valid_size->width * 256 / scale_size->width;
|
||
|
y_ratio = ob_valid_size->height * 256 / scale_size->height;
|
||
|
weight_shift = min_scale_w_shift(x_ratio, y_ratio);
|
||
|
|
||
|
bsp_isp_channel_enable(SUB_CH);
|
||
|
bsp_isp_scale_enable(SUB_CH);
|
||
|
bsp_isp_set_output_size(SUB_CH, scale_size);
|
||
|
bsp_isp_scale_cfg(SUB_CH, x_ratio, y_ratio, weight_shift);
|
||
|
|
||
|
__isp_cal_ch_size(fmt[SUB_CH], scale_size, &isp_yuv_size_addr[SUB_CH]);
|
||
|
|
||
|
bsp_isp_set_stride_y(isp_yuv_size_addr[SUB_CH].line_stride_y, SUB_CH);
|
||
|
bsp_isp_set_stride_uv(isp_yuv_size_addr[SUB_CH].line_stride_c, SUB_CH);
|
||
|
} else {
|
||
|
/* main channel, hardware use sub_ch */
|
||
|
/* scale config */
|
||
|
full_size->width = full_size->width&0x1ffc;
|
||
|
full_size->height = full_size->height&(~1);
|
||
|
pr_info("[ISP] full_size width = %d, height = %d, ob_valid = %d, %d\n",
|
||
|
full_size->width, full_size->height,
|
||
|
ob_valid_size->width, ob_valid_size->height);
|
||
|
x_ratio = ob_valid_size->width * 256 / full_size->width;
|
||
|
y_ratio = ob_valid_size->height * 256 / full_size->height;
|
||
|
weight_shift = min_scale_w_shift(x_ratio, y_ratio);
|
||
|
|
||
|
bsp_isp_channel_enable(SUB_CH);
|
||
|
bsp_isp_scale_enable(SUB_CH);
|
||
|
bsp_isp_set_output_size(SUB_CH, full_size);
|
||
|
bsp_isp_scale_cfg(SUB_CH, x_ratio, y_ratio, weight_shift);
|
||
|
|
||
|
/* for exchange sub and man ch. NOTE: size and fmt. */
|
||
|
__isp_cal_ch_size(fmt[MAIN_CH], full_size, &isp_yuv_size_addr[SUB_CH]);
|
||
|
|
||
|
bsp_isp_set_stride_y(isp_yuv_size_addr[SUB_CH].line_stride_y, SUB_CH);
|
||
|
bsp_isp_set_stride_uv(isp_yuv_size_addr[SUB_CH].line_stride_c, SUB_CH);
|
||
|
|
||
|
bsp_isp_channel_disable(MAIN_CH);
|
||
|
isp_yuv_size_addr[MAIN_CH].isp_byte_size = 0;
|
||
|
}
|
||
|
|
||
|
if (ob_rot_size && 0 != ob_rot_size->height && 0 != ob_rot_size->width) {
|
||
|
__isp_cal_ch_size(fmt[ROT_CH], ob_rot_size, &isp_yuv_size_addr[ROT_CH]);
|
||
|
bsp_isp_set_stride_y(isp_yuv_size_addr[ROT_CH].line_stride_y, ROT_CH);
|
||
|
bsp_isp_set_stride_uv(isp_yuv_size_addr[ROT_CH].line_stride_c, ROT_CH);
|
||
|
} else
|
||
|
isp_yuv_size_addr[ROT_CH].isp_byte_size = 0;
|
||
|
|
||
|
isp_yuv_size_addr[MAIN_CH].isp_byte_size = ALIGN_4K(isp_yuv_size_addr[MAIN_CH].isp_byte_size);
|
||
|
isp_yuv_size_addr[SUB_CH].isp_byte_size = ALIGN_4K(isp_yuv_size_addr[SUB_CH].isp_byte_size);
|
||
|
isp_yuv_size_addr[ROT_CH].isp_byte_size = ALIGN_4K(isp_yuv_size_addr[ROT_CH].isp_byte_size);
|
||
|
|
||
|
return (isp_yuv_size_addr[MAIN_CH].isp_byte_size + isp_yuv_size_addr[SUB_CH].isp_byte_size +
|
||
|
isp_yuv_size_addr[ROT_CH].isp_byte_size);
|
||
|
}
|
||
|
|
||
|
unsigned int sunxi_isp_set_size(struct isp_dev *isp, enum pixel_fmt *fmt,
|
||
|
struct isp_size_settings *size_settings)
|
||
|
{
|
||
|
return __isp_new_set_size_internal(isp, fmt, size_settings);
|
||
|
}
|
||
|
|
||
|
void sunxi_isp_set_fmt(struct isp_dev *isp, enum bus_pixeltype type, enum pixel_fmt *fmt)
|
||
|
{
|
||
|
/* pr_info("type = %d\n", type); */
|
||
|
__isp_set_input_fmt_internal(type);
|
||
|
|
||
|
if (fmt[SUB_CH] != PIX_FMT_NONE) {
|
||
|
__isp_set_output_fmt_internal(fmt[MAIN_CH], MAIN_CH);
|
||
|
|
||
|
if (fmt[MAIN_CH] == PIX_FMT_YVU420P_8 || fmt[MAIN_CH] == PIX_FMT_YVU422P_8)
|
||
|
isp->plannar_uv_exchange_flag[MAIN_CH] = 1;
|
||
|
else
|
||
|
isp->plannar_uv_exchange_flag[MAIN_CH] = 0;
|
||
|
|
||
|
|
||
|
bsp_isp_module_enable(TG_EN);
|
||
|
__isp_set_output_fmt_internal(fmt[SUB_CH], SUB_CH);
|
||
|
|
||
|
if (fmt[SUB_CH] == PIX_FMT_YVU420P_8 || fmt[SUB_CH] == PIX_FMT_YVU422P_8)
|
||
|
isp->plannar_uv_exchange_flag[SUB_CH] = 1;
|
||
|
else
|
||
|
isp->plannar_uv_exchange_flag[SUB_CH] = 0;
|
||
|
} else {
|
||
|
/* for exchange sub and man ch. */
|
||
|
__isp_set_output_fmt_internal(fmt[MAIN_CH], SUB_CH);
|
||
|
|
||
|
if (fmt[MAIN_CH] == PIX_FMT_YVU420P_8 || fmt[MAIN_CH] == PIX_FMT_YVU422P_8)
|
||
|
isp->plannar_uv_exchange_flag[SUB_CH] = 1;
|
||
|
else
|
||
|
isp->plannar_uv_exchange_flag[SUB_CH] = 0;
|
||
|
|
||
|
bsp_isp_module_disable(TG_EN);
|
||
|
}
|
||
|
|
||
|
if (fmt[ROT_CH] != PIX_FMT_NONE) {
|
||
|
isp->rotation_en = 1;
|
||
|
bsp_isp_module_enable(ROT_EN);
|
||
|
__isp_set_output_fmt_internal(fmt[ROT_CH], ROT_CH);
|
||
|
|
||
|
if (fmt[SUB_CH] == PIX_FMT_YVU420P_8 || fmt[SUB_CH] == PIX_FMT_YVU422P_8)
|
||
|
isp->plannar_uv_exchange_flag[ROT_CH] = 1;
|
||
|
else
|
||
|
isp->plannar_uv_exchange_flag[ROT_CH] = 0;
|
||
|
} else
|
||
|
bsp_isp_module_disable(ROT_EN);
|
||
|
}
|
||
|
|
||
|
void sunxi_isp_set_flip(struct isp_dev *isp, enum isp_channel ch, enum enable_flag on_off)
|
||
|
{
|
||
|
enum enable_flag *flip_en_glb = &isp->flip_en_glb[0];
|
||
|
|
||
|
bsp_isp_set_flip(ch, on_off);
|
||
|
flip_en_glb[ch] = on_off;
|
||
|
}
|
||
|
|
||
|
void sunxi_isp_set_mirror(enum isp_channel ch, enum enable_flag on_off)
|
||
|
{
|
||
|
bsp_isp_set_mirror(ch, on_off);
|
||
|
}
|
||
|
|
||
|
void sunxi_isp_set_output_addr(struct v4l2_subdev *sd, unsigned long buf_base_addr)
|
||
|
{
|
||
|
struct isp_dev *isp = v4l2_get_subdevdata(sd);
|
||
|
int tmp_addr;
|
||
|
struct isp_yuv_size_addr_info *isp_yuv_size_addr = &isp->isp_yuv_size_addr[0];
|
||
|
|
||
|
__isp_cal_ch_addr(isp->flip_en_glb[MAIN_CH], buf_base_addr, &isp_yuv_size_addr[MAIN_CH]);
|
||
|
if (isp->plannar_uv_exchange_flag[MAIN_CH] == 1) {
|
||
|
tmp_addr = isp_yuv_size_addr[MAIN_CH].yuv_addr.u_addr;
|
||
|
isp_yuv_size_addr[MAIN_CH].yuv_addr.u_addr = isp_yuv_size_addr[MAIN_CH].yuv_addr.v_addr;
|
||
|
isp_yuv_size_addr[MAIN_CH].yuv_addr.v_addr = tmp_addr;
|
||
|
}
|
||
|
|
||
|
__isp_cal_ch_addr(isp->flip_en_glb[SUB_CH], ALIGN_4K(buf_base_addr + isp_yuv_size_addr[MAIN_CH].isp_byte_size), &isp_yuv_size_addr[SUB_CH]);
|
||
|
if (isp->plannar_uv_exchange_flag[SUB_CH] == 1) {
|
||
|
tmp_addr = isp_yuv_size_addr[SUB_CH].yuv_addr.u_addr;
|
||
|
isp_yuv_size_addr[SUB_CH].yuv_addr.u_addr = isp_yuv_size_addr[SUB_CH].yuv_addr.v_addr;
|
||
|
isp_yuv_size_addr[SUB_CH].yuv_addr.v_addr = tmp_addr;
|
||
|
}
|
||
|
|
||
|
bsp_isp_set_yuv_addr(&isp_yuv_size_addr[MAIN_CH].yuv_addr, MAIN_CH, ISP_SRC0);
|
||
|
bsp_isp_set_yuv_addr(&isp_yuv_size_addr[SUB_CH].yuv_addr, SUB_CH, ISP_SRC0);
|
||
|
|
||
|
if (isp->rotation_en == 1) {
|
||
|
__isp_cal_ch_addr(DISABLE, ALIGN_4K(buf_base_addr + isp_yuv_size_addr[MAIN_CH].isp_byte_size +
|
||
|
isp_yuv_size_addr[SUB_CH].isp_byte_size), &isp_yuv_size_addr[ROT_CH]);
|
||
|
|
||
|
if (isp->plannar_uv_exchange_flag[ROT_CH] == 1) {
|
||
|
tmp_addr = isp_yuv_size_addr[ROT_CH].yuv_addr.u_addr;
|
||
|
isp_yuv_size_addr[ROT_CH].yuv_addr.u_addr = isp_yuv_size_addr[ROT_CH].yuv_addr.v_addr;
|
||
|
isp_yuv_size_addr[ROT_CH].yuv_addr.v_addr = tmp_addr;
|
||
|
}
|
||
|
bsp_isp_set_yuv_addr(&isp_yuv_size_addr[ROT_CH].yuv_addr, ROT_CH, ISP_SRC0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
static int sunxi_isp_subdev_s_power(struct v4l2_subdev *sd, int enable)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int sunxi_isp_subdev_s_stream(struct v4l2_subdev *sd, int enable)
|
||
|
{
|
||
|
struct isp_dev *isp = v4l2_get_subdevdata(sd);
|
||
|
|
||
|
if (!enable)
|
||
|
return 0;
|
||
|
|
||
|
if (isp->vflip == 0) {
|
||
|
sunxi_isp_set_flip(isp, MAIN_CH, DISABLE);
|
||
|
sunxi_isp_set_flip(isp, SUB_CH, DISABLE);
|
||
|
} else {
|
||
|
sunxi_isp_set_flip(isp, MAIN_CH, ENABLE);
|
||
|
sunxi_isp_set_flip(isp, SUB_CH, ENABLE);
|
||
|
}
|
||
|
if (isp->hflip == 0) {
|
||
|
sunxi_isp_set_mirror(MAIN_CH, DISABLE);
|
||
|
sunxi_isp_set_mirror(SUB_CH, DISABLE);
|
||
|
} else {
|
||
|
sunxi_isp_set_mirror(MAIN_CH, ENABLE);
|
||
|
sunxi_isp_set_mirror(SUB_CH, ENABLE);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static const struct isp_pix_fmt *__sunxi_isp_find_format(const u32 *pixelformat,
|
||
|
const u32 *mbus_code, int index)
|
||
|
{
|
||
|
const struct isp_pix_fmt *fmt, *def_fmt = NULL;
|
||
|
unsigned int i;
|
||
|
int id = 0;
|
||
|
|
||
|
if (index >= (int)ARRAY_SIZE(sunxi_isp_formats))
|
||
|
return NULL;
|
||
|
|
||
|
for (i = 0; i < ARRAY_SIZE(sunxi_isp_formats); ++i) {
|
||
|
fmt = &sunxi_isp_formats[i];
|
||
|
if (pixelformat && fmt->fourcc == *pixelformat)
|
||
|
return fmt;
|
||
|
if (mbus_code && fmt->mbus_code == *mbus_code)
|
||
|
return fmt;
|
||
|
if (index == id)
|
||
|
def_fmt = fmt;
|
||
|
id++;
|
||
|
}
|
||
|
return def_fmt;
|
||
|
}
|
||
|
|
||
|
static struct v4l2_mbus_framefmt *__sunxi_isp_get_format(
|
||
|
struct isp_dev *isp, struct v4l2_subdev_pad_config *cfg,
|
||
|
u32 pad, enum v4l2_subdev_format_whence which)
|
||
|
{
|
||
|
/*if (which == V4L2_SUBDEV_FORMAT_TRY)
|
||
|
* return fh ? v4l2_subdev_get_try_format(fh, pad) : NULL;
|
||
|
*/
|
||
|
return &isp->format;
|
||
|
}
|
||
|
|
||
|
static int sunxi_isp_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
|
||
|
struct v4l2_subdev_mbus_code_enum *code)
|
||
|
{
|
||
|
const struct isp_pix_fmt *fmt;
|
||
|
|
||
|
fmt = __sunxi_isp_find_format(NULL, NULL, code->index);
|
||
|
if (!fmt)
|
||
|
return -EINVAL;
|
||
|
code->code = fmt->mbus_code;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int sunxi_isp_subdev_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
|
||
|
struct v4l2_subdev_format *fmt)
|
||
|
{
|
||
|
struct isp_dev *isp = v4l2_get_subdevdata(sd);
|
||
|
struct v4l2_mbus_framefmt *mf;
|
||
|
|
||
|
mf = __sunxi_isp_get_format(isp, cfg, fmt->pad, fmt->which);
|
||
|
if (!mf)
|
||
|
return -EINVAL;
|
||
|
|
||
|
mutex_lock(&isp->subdev_lock);
|
||
|
fmt->format = *mf;
|
||
|
mutex_unlock(&isp->subdev_lock);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static const struct isp_pix_fmt *__sunxi_isp_try_format(struct isp_dev *isp,
|
||
|
u32 *width, u32 *height,
|
||
|
u32 *code, u32 *fourcc, int pad)
|
||
|
{
|
||
|
const struct isp_pix_fmt *fmt;
|
||
|
|
||
|
fmt = __sunxi_isp_find_format(fourcc, code, ARRAY_SIZE(sunxi_isp_formats)-1);
|
||
|
|
||
|
if (WARN_ON(!fmt))
|
||
|
return NULL;
|
||
|
v4l_bound_align_image(width, 16 + 8, 4096, 0,
|
||
|
height, 12 + 8, 4096, 0, 0);
|
||
|
return fmt;
|
||
|
}
|
||
|
static int sunxi_isp_subdev_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg,
|
||
|
struct v4l2_subdev_format *fmt)
|
||
|
{
|
||
|
struct isp_dev *isp = v4l2_get_subdevdata(sd);
|
||
|
struct v4l2_mbus_framefmt *mf;
|
||
|
const struct isp_pix_fmt *isp_fmt;
|
||
|
|
||
|
v4l2_info(sd, "%s: w: %d, h: %d\n", __func__,
|
||
|
fmt->format.width, fmt->format.height);
|
||
|
mf = __sunxi_isp_get_format(isp, cfg, fmt->pad, fmt->which);
|
||
|
isp_fmt = __sunxi_isp_try_format(isp, &fmt->format.width,
|
||
|
&fmt->format.height, &fmt->format.code, NULL, 0);
|
||
|
if (mf) {
|
||
|
mutex_lock(&isp->subdev_lock);
|
||
|
*mf = fmt->format;
|
||
|
mutex_unlock(&isp->subdev_lock);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int sunxi_isp_addr_init(struct v4l2_subdev *sd, u32 val)
|
||
|
{
|
||
|
struct isp_dev *isp = v4l2_get_subdevdata(sd);
|
||
|
|
||
|
bsp_isp_set_dma_load_addr((unsigned long)isp->isp_load_reg_mm.dma_addr);
|
||
|
bsp_isp_set_dma_saved_addr((unsigned long)isp->isp_save_reg_mm.dma_addr);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int sunxi_isp_set_mainchannel(struct isp_dev *isp, struct main_channel_cfg *main_cfg)
|
||
|
{
|
||
|
struct isp_size_settings size_settings;
|
||
|
struct isp_fmt_cfg *isp_fmt_cfg = &isp->isp_fmt;
|
||
|
struct sensor_win_size *win_cfg = &main_cfg->win_cfg;
|
||
|
|
||
|
memset(isp_fmt_cfg, 0, sizeof(struct isp_fmt_cfg));
|
||
|
|
||
|
isp_fmt_cfg->isp_fmt[MAIN_CH] = pix_fmt_v4l2_to_common(main_cfg->pix.pixelformat);
|
||
|
isp_fmt_cfg->isp_size[MAIN_CH].width = main_cfg->pix.width;
|
||
|
isp_fmt_cfg->isp_size[MAIN_CH].height = main_cfg->pix.height;
|
||
|
isp_fmt_cfg->isp_fmt[SUB_CH] = PIX_FMT_NONE;
|
||
|
isp_fmt_cfg->isp_fmt[ROT_CH] = PIX_FMT_NONE;
|
||
|
|
||
|
isp_dbg(0, "bus_code = %d, isp_fmt = %p\n", isp_fmt_cfg->bus_code, isp_fmt_cfg->isp_fmt);
|
||
|
isp_fmt_cfg->bus_code = main_cfg->bus_code;
|
||
|
sunxi_isp_set_fmt(isp, isp_fmt_cfg->bus_code, &isp_fmt_cfg->isp_fmt[0]);
|
||
|
|
||
|
if (0 == win_cfg->width || 0 == win_cfg->height) {
|
||
|
win_cfg->width = isp_fmt_cfg->isp_size[MAIN_CH].width;
|
||
|
win_cfg->height = isp_fmt_cfg->isp_size[MAIN_CH].height;
|
||
|
}
|
||
|
if (0 == win_cfg->width_input || 0 == win_cfg->height_input) {
|
||
|
win_cfg->width_input = win_cfg->width;
|
||
|
win_cfg->height_input = win_cfg->height;
|
||
|
}
|
||
|
|
||
|
isp_print("width_input = %d, height_input = %d, width = %d, height = %d\n",
|
||
|
win_cfg->width_input, win_cfg->height_input,
|
||
|
win_cfg->width, win_cfg->height);
|
||
|
isp_fmt_cfg->ob_black_size.width = win_cfg->width_input + 2*win_cfg->hoffset;
|
||
|
isp_fmt_cfg->ob_black_size.height = win_cfg->height_input + 2*win_cfg->voffset;
|
||
|
isp_fmt_cfg->ob_valid_size.width = win_cfg->width_input;
|
||
|
isp_fmt_cfg->ob_valid_size.height = win_cfg->height_input;
|
||
|
isp_fmt_cfg->ob_start.hor = win_cfg->hoffset;
|
||
|
isp_fmt_cfg->ob_start.ver = win_cfg->voffset;
|
||
|
|
||
|
/*dev->buf_byte_size = bsp_isp_set_size(isp_fmt, &ob_black_size, &ob_valid_size,
|
||
|
* &isp_size[MAIN_CH], &isp_size[ROT_CH], &ob_start, &isp_size[SUB_CH]);
|
||
|
*/
|
||
|
size_settings.full_size = isp_fmt_cfg->isp_size[MAIN_CH];
|
||
|
size_settings.scale_size = isp_fmt_cfg->isp_size[SUB_CH];
|
||
|
size_settings.ob_black_size = isp_fmt_cfg->ob_black_size;
|
||
|
size_settings.ob_start = isp_fmt_cfg->ob_start;
|
||
|
size_settings.ob_valid_size = isp_fmt_cfg->ob_valid_size;
|
||
|
size_settings.ob_rot_size = isp_fmt_cfg->isp_size[ROT_CH];
|
||
|
main_cfg->pix.sizeimage = sunxi_isp_set_size(isp, &isp_fmt_cfg->isp_fmt[0], &size_settings);
|
||
|
isp_print("main_cfg->pix.sizeimage = %d,main_cfg->pix.width = %d ,main_cfg->pix.height = %d\n",
|
||
|
main_cfg->pix.sizeimage, main_cfg->pix.width, main_cfg->pix.height);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int sunxi_isp_set_subchannel(struct isp_dev *isp, struct v4l2_pix_format *sub)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
struct isp_size_settings size_settings;
|
||
|
struct isp_fmt_cfg *isp_fmt_cfg = &isp->isp_fmt;
|
||
|
|
||
|
isp_fmt_cfg->isp_fmt[SUB_CH] = pix_fmt_v4l2_to_common(sub->pixelformat);
|
||
|
isp_fmt_cfg->isp_size[SUB_CH].width = sub->width;
|
||
|
isp_fmt_cfg->isp_size[SUB_CH].height = sub->height;
|
||
|
if (isp_fmt_cfg->isp_size[SUB_CH].height > isp_fmt_cfg->isp_size[MAIN_CH].height ||
|
||
|
isp_fmt_cfg->isp_size[SUB_CH].width > isp_fmt_cfg->isp_size[MAIN_CH].width) {
|
||
|
vfe_err("subchannel size > main channel size!!! MAIN_CH = %d %d,SUB_CH = %d %d\n",
|
||
|
isp_fmt_cfg->isp_size[MAIN_CH].width,
|
||
|
isp_fmt_cfg->isp_size[MAIN_CH].height,
|
||
|
isp_fmt_cfg->isp_size[SUB_CH].width,
|
||
|
isp_fmt_cfg->isp_size[SUB_CH].height);
|
||
|
return -1;
|
||
|
}
|
||
|
size_settings.full_size = isp_fmt_cfg->isp_size[MAIN_CH];
|
||
|
size_settings.scale_size = isp_fmt_cfg->isp_size[SUB_CH];
|
||
|
size_settings.ob_black_size = isp_fmt_cfg->ob_black_size;
|
||
|
size_settings.ob_start = isp_fmt_cfg->ob_start;
|
||
|
size_settings.ob_valid_size = isp_fmt_cfg->ob_valid_size;
|
||
|
size_settings.ob_rot_size = isp_fmt_cfg->isp_size[ROT_CH];
|
||
|
sunxi_isp_set_fmt(isp, isp_fmt_cfg->bus_code, &isp_fmt_cfg->isp_fmt[0]);
|
||
|
sub->sizeimage = sunxi_isp_set_size(isp, &isp_fmt_cfg->isp_fmt[0], &size_settings);
|
||
|
isp_print("sub->sizeimage = %d, sub->width = %d, sub->height = %d\n",
|
||
|
sub->sizeimage, sub->width, sub->height);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int sunxi_isp_set_rotchannel(struct isp_dev *isp, struct rot_channel_cfg *rot)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
struct isp_size_settings size_settings;
|
||
|
struct isp_fmt_cfg *isp_fmt_cfg = &isp->isp_fmt;
|
||
|
|
||
|
isp_fmt_cfg->isp_fmt[ROT_CH] = isp_fmt_cfg->isp_fmt[rot->sel_ch];
|
||
|
isp_fmt_cfg->rot_angle = rot->rotation;
|
||
|
isp_fmt_cfg->rot_ch = rot->sel_ch;
|
||
|
if (isp_fmt_cfg->rot_angle == 90 || isp_fmt_cfg->rot_angle == 270) {
|
||
|
isp_fmt_cfg->isp_size[ROT_CH].width = isp_fmt_cfg->isp_size[rot->sel_ch].height;
|
||
|
isp_fmt_cfg->isp_size[ROT_CH].height = isp_fmt_cfg->isp_size[rot->sel_ch].width;
|
||
|
} else {
|
||
|
isp_fmt_cfg->isp_size[ROT_CH].width = isp_fmt_cfg->isp_size[rot->sel_ch].width;
|
||
|
isp_fmt_cfg->isp_size[ROT_CH].height = isp_fmt_cfg->isp_size[rot->sel_ch].height;
|
||
|
}
|
||
|
if (isp_fmt_cfg->rot_ch == MAIN_CH) {
|
||
|
if (isp_fmt_cfg->rot_angle == 0)
|
||
|
bsp_isp_set_rot(MAIN_CH, ANGLE_0);
|
||
|
else if (isp_fmt_cfg->rot_angle == 90)
|
||
|
bsp_isp_set_rot(MAIN_CH, ANGLE_90);
|
||
|
else if (isp_fmt_cfg->rot_angle == 180)
|
||
|
bsp_isp_set_rot(MAIN_CH, ANGLE_180);
|
||
|
else if (isp_fmt_cfg->rot_angle == 270)
|
||
|
bsp_isp_set_rot(MAIN_CH, ANGLE_270);
|
||
|
else
|
||
|
bsp_isp_set_rot(MAIN_CH, ANGLE_0);
|
||
|
|
||
|
} else if (isp_fmt_cfg->rot_ch == SUB_CH) {
|
||
|
if (isp_fmt_cfg->rot_angle == 0)
|
||
|
bsp_isp_set_rot(SUB_CH, ANGLE_0);
|
||
|
else if (isp_fmt_cfg->rot_angle == 90)
|
||
|
bsp_isp_set_rot(SUB_CH, ANGLE_90);
|
||
|
else if (isp_fmt_cfg->rot_angle == 180)
|
||
|
bsp_isp_set_rot(SUB_CH, ANGLE_180);
|
||
|
else if (isp_fmt_cfg->rot_angle == 270)
|
||
|
bsp_isp_set_rot(SUB_CH, ANGLE_270);
|
||
|
else
|
||
|
bsp_isp_set_rot(SUB_CH, ANGLE_0);
|
||
|
} else
|
||
|
vfe_err("vidioc_set_rotchannel rot_ch = %d is error!!!", isp_fmt_cfg->rot_ch);
|
||
|
|
||
|
size_settings.full_size = isp_fmt_cfg->isp_size[MAIN_CH];
|
||
|
size_settings.scale_size = isp_fmt_cfg->isp_size[SUB_CH];
|
||
|
size_settings.ob_black_size = isp_fmt_cfg->ob_black_size;
|
||
|
size_settings.ob_start = isp_fmt_cfg->ob_start;
|
||
|
size_settings.ob_valid_size = isp_fmt_cfg->ob_valid_size;
|
||
|
size_settings.ob_rot_size = isp_fmt_cfg->isp_size[ROT_CH];
|
||
|
sunxi_isp_set_fmt(isp, isp_fmt_cfg->bus_code, &isp_fmt_cfg->isp_fmt[0]);
|
||
|
rot->pix.sizeimage = sunxi_isp_set_size(isp, &isp_fmt_cfg->isp_fmt[0], &size_settings);
|
||
|
isp_print("rot->pix.sizeimage = %d, rot->pix.width = %d, rot->pix.height = %d\n",
|
||
|
rot->pix.sizeimage, rot->pix.width, rot->pix.height);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static long sunxi_isp_subdev_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
|
||
|
{
|
||
|
struct isp_dev *isp = v4l2_get_subdevdata(sd);
|
||
|
int ret;
|
||
|
|
||
|
switch (cmd) {
|
||
|
case VIDIOC_SUNXI_ISP_MAIN_CH_CFG:
|
||
|
mutex_lock(&isp->subdev_lock);
|
||
|
ret = sunxi_isp_set_mainchannel(isp, arg);
|
||
|
mutex_unlock(&isp->subdev_lock);
|
||
|
break;
|
||
|
case VIDIOC_SUNXI_ISP_SUB_CH_CFG:
|
||
|
mutex_lock(&isp->subdev_lock);
|
||
|
ret = sunxi_isp_set_subchannel(isp, arg);
|
||
|
mutex_unlock(&isp->subdev_lock);
|
||
|
break;
|
||
|
case VIDIOC_SUNXI_ISP_ROT_CH_CFG:
|
||
|
mutex_lock(&isp->subdev_lock);
|
||
|
ret = sunxi_isp_set_rotchannel(isp, arg);
|
||
|
mutex_unlock(&isp->subdev_lock);
|
||
|
break;
|
||
|
default:
|
||
|
return -ENOIOCTLCMD;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static const struct v4l2_subdev_core_ops sunxi_isp_core_ops = {
|
||
|
.s_power = sunxi_isp_subdev_s_power,
|
||
|
.init = sunxi_isp_addr_init,
|
||
|
.ioctl = sunxi_isp_subdev_ioctl,
|
||
|
};
|
||
|
|
||
|
static const struct v4l2_subdev_video_ops sunxi_isp_subdev_video_ops = {
|
||
|
.s_stream = sunxi_isp_subdev_s_stream,
|
||
|
};
|
||
|
|
||
|
static const struct v4l2_subdev_pad_ops sunxi_isp_subdev_pad_ops = {
|
||
|
.enum_mbus_code = sunxi_isp_enum_mbus_code,
|
||
|
.get_fmt = sunxi_isp_subdev_get_fmt,
|
||
|
.set_fmt = sunxi_isp_subdev_set_fmt,
|
||
|
};
|
||
|
|
||
|
|
||
|
static struct v4l2_subdev_ops sunxi_isp_subdev_ops = {
|
||
|
.core = &sunxi_isp_core_ops,
|
||
|
.video = &sunxi_isp_subdev_video_ops,
|
||
|
.pad = &sunxi_isp_subdev_pad_ops,
|
||
|
};
|
||
|
/*
|
||
|
*static int sunxi_isp_subdev_registered(struct v4l2_subdev *sd)
|
||
|
*{
|
||
|
* struct vfe_dev *vfe = v4l2_get_subdevdata(sd);
|
||
|
* int ret = 0;
|
||
|
* return ret;
|
||
|
*}
|
||
|
*/
|
||
|
/*
|
||
|
*static void sunxi_isp_subdev_unregistered(struct v4l2_subdev *sd)
|
||
|
*{
|
||
|
* struct vfe_dev *vfe = v4l2_get_subdevdata(sd);
|
||
|
* return;
|
||
|
*}
|
||
|
*/
|
||
|
/*
|
||
|
*static const struct v4l2_subdev_internal_ops sunxi_isp_sd_internal_ops = {
|
||
|
* .registered = sunxi_isp_subdev_registered,
|
||
|
* .unregistered = sunxi_isp_subdev_unregistered,
|
||
|
*};
|
||
|
*/
|
||
|
|
||
|
static int __sunxi_isp_ctrl(struct isp_dev *isp, struct v4l2_ctrl *ctrl)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE)
|
||
|
return 0;
|
||
|
|
||
|
switch (ctrl->id) {
|
||
|
case V4L2_CID_HFLIP:
|
||
|
if (ctrl->val == 0)
|
||
|
sunxi_isp_set_mirror(MAIN_CH, DISABLE);
|
||
|
else
|
||
|
sunxi_isp_set_mirror(MAIN_CH, ENABLE);
|
||
|
isp->hflip = ctrl->val;
|
||
|
break;
|
||
|
case V4L2_CID_VFLIP:
|
||
|
if (ctrl->val == 0)
|
||
|
sunxi_isp_set_flip(isp, MAIN_CH, DISABLE);
|
||
|
else
|
||
|
sunxi_isp_set_flip(isp, MAIN_CH, ENABLE);
|
||
|
isp->vflip = ctrl->val;
|
||
|
break;
|
||
|
case V4L2_CID_HFLIP_THUMB:
|
||
|
if (ctrl->val == 0)
|
||
|
sunxi_isp_set_mirror(SUB_CH, DISABLE);
|
||
|
else
|
||
|
sunxi_isp_set_mirror(SUB_CH, ENABLE);
|
||
|
break;
|
||
|
case V4L2_CID_VFLIP_THUMB:
|
||
|
if (ctrl->val == 0)
|
||
|
sunxi_isp_set_flip(isp, SUB_CH, DISABLE);
|
||
|
else
|
||
|
sunxi_isp_set_flip(isp, SUB_CH, ENABLE);
|
||
|
break;
|
||
|
case V4L2_CID_ROTATE:
|
||
|
isp->rotate = ctrl->val;
|
||
|
break;
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
#define ctrl_to_sunxi_isp(ctrl) \
|
||
|
container_of(ctrl->handler, struct isp_dev, ctrls.handler)
|
||
|
|
||
|
static int sunxi_isp_s_ctrl(struct v4l2_ctrl *ctrl)
|
||
|
{
|
||
|
struct isp_dev *isp = ctrl_to_sunxi_isp(ctrl);
|
||
|
unsigned long flags;
|
||
|
int ret;
|
||
|
|
||
|
spin_lock_irqsave(&isp->slock, flags);
|
||
|
ret = __sunxi_isp_ctrl(isp, ctrl);
|
||
|
spin_unlock_irqrestore(&isp->slock, flags);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static const struct v4l2_ctrl_ops sunxi_isp_ctrl_ops = {
|
||
|
.s_ctrl = sunxi_isp_s_ctrl,
|
||
|
};
|
||
|
|
||
|
static const struct v4l2_ctrl_config isp_ctrls[] = {
|
||
|
{
|
||
|
.ops = &sunxi_isp_ctrl_ops,
|
||
|
.id = V4L2_CID_HFLIP_THUMB,
|
||
|
.name = "Horizontal Flip For Thumb",
|
||
|
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||
|
.min = 0,
|
||
|
.max = 1,
|
||
|
.step = 1,
|
||
|
.def = 0,
|
||
|
},
|
||
|
{
|
||
|
.ops = &sunxi_isp_ctrl_ops,
|
||
|
.id = V4L2_CID_VFLIP_THUMB,
|
||
|
.name = "Vertical Flip For Thumb",
|
||
|
.type = V4L2_CTRL_TYPE_BOOLEAN,
|
||
|
.min = 0,
|
||
|
.max = 1,
|
||
|
.step = 1,
|
||
|
.def = 0,
|
||
|
},
|
||
|
};
|
||
|
|
||
|
int sunxi_isp_init_subdev(struct isp_dev *isp)
|
||
|
{
|
||
|
const struct v4l2_ctrl_ops *ops = &sunxi_isp_ctrl_ops;
|
||
|
struct v4l2_ctrl_handler *handler = &isp->ctrls.handler;
|
||
|
struct v4l2_subdev *sd = &isp->subdev;
|
||
|
struct sunxi_isp_ctrls *ctrls = &isp->ctrls;
|
||
|
enum enable_flag *flip_en_glb = &isp->flip_en_glb[0];
|
||
|
int i;
|
||
|
|
||
|
mutex_init(&isp->subdev_lock);
|
||
|
|
||
|
v4l2_subdev_init(sd, &sunxi_isp_subdev_ops);
|
||
|
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
||
|
snprintf(sd->name, sizeof(sd->name), "sunxi_isp.%u", isp->id);
|
||
|
|
||
|
v4l2_ctrl_handler_init(handler, 3 + ARRAY_SIZE(isp_ctrls));
|
||
|
|
||
|
ctrls->rotate = v4l2_ctrl_new_std(handler, ops, V4L2_CID_ROTATE, 0, 270, 90, 0);
|
||
|
ctrls->hflip = v4l2_ctrl_new_std(handler, ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
|
||
|
ctrls->vflip = v4l2_ctrl_new_std(handler, ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
|
||
|
|
||
|
for (i = 0; i < ARRAY_SIZE(isp_ctrls); i++)
|
||
|
v4l2_ctrl_new_custom(handler, &isp_ctrls[i], NULL);
|
||
|
|
||
|
if (handler->error)
|
||
|
return handler->error;
|
||
|
|
||
|
sd->ctrl_handler = handler;
|
||
|
/* sd->internal_ops = &sunxi_isp_sd_internal_ops; */
|
||
|
v4l2_set_subdevdata(sd, isp);
|
||
|
|
||
|
flip_en_glb[MAIN_CH] = 0;
|
||
|
flip_en_glb[SUB_CH] = 0;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int isp_resource_alloc(struct device *dev, struct isp_dev *isp)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
isp->isp_load_reg_mm.size = ISP_LOAD_REG_SIZE;
|
||
|
isp->isp_save_reg_mm.size = ISP_SAVED_REG_SIZE;
|
||
|
|
||
|
os_mem_alloc(dev, &isp->isp_load_reg_mm);
|
||
|
os_mem_alloc(dev, &isp->isp_save_reg_mm);
|
||
|
|
||
|
if (isp->isp_load_reg_mm.phy_addr != NULL) {
|
||
|
if (!isp->isp_load_reg_mm.vir_addr) {
|
||
|
vfe_err("isp load regs va requset failed!\n");
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
} else {
|
||
|
vfe_err("isp load regs pa requset failed!\n");
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
|
||
|
if (isp->isp_save_reg_mm.phy_addr != NULL) {
|
||
|
if (!isp->isp_save_reg_mm.vir_addr) {
|
||
|
vfe_err("isp save regs va requset failed!\n");
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
} else {
|
||
|
vfe_err("isp save regs pa requset failed!\n");
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
static void isp_resource_release(struct device *dev, struct isp_dev *isp)
|
||
|
{
|
||
|
os_mem_free(dev, &isp->isp_load_reg_mm);
|
||
|
os_mem_free(dev, &isp->isp_save_reg_mm);
|
||
|
}
|
||
|
|
||
|
static int isp_probe(struct platform_device *pdev)
|
||
|
{
|
||
|
struct device_node *np = pdev->dev.of_node;
|
||
|
struct isp_dev *isp = NULL;
|
||
|
struct device *dev = &pdev->dev;
|
||
|
int ret = 0;
|
||
|
|
||
|
if (np == NULL) {
|
||
|
vfe_err("ISP failed to get of node\n");
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
|
||
|
isp = kzalloc(sizeof(struct isp_dev), GFP_KERNEL);
|
||
|
if (!isp) {
|
||
|
ret = -ENOMEM;
|
||
|
goto ekzalloc;
|
||
|
}
|
||
|
|
||
|
pdev->id = of_alias_get_id(np, "isp");
|
||
|
if (pdev->id < 0) {
|
||
|
vfe_err("ISP failed to get alias id\n");
|
||
|
ret = -EINVAL;
|
||
|
goto freedev;
|
||
|
}
|
||
|
|
||
|
isp->base = of_iomap(np, 0);
|
||
|
if (!isp->base) {
|
||
|
ret = -EIO;
|
||
|
goto freedev;
|
||
|
}
|
||
|
isp->id = pdev->id;
|
||
|
isp->pdev = pdev;
|
||
|
|
||
|
list_add_tail(&isp->isp_list, &isp_drv_list);
|
||
|
isp->rotation_en = 0;
|
||
|
sunxi_isp_init_subdev(isp);
|
||
|
|
||
|
spin_lock_init(&isp->slock);
|
||
|
init_waitqueue_head(&isp->wait);
|
||
|
|
||
|
if (isp_resource_alloc(dev, isp) < 0) {
|
||
|
ret = -EIO;
|
||
|
goto ehwinit;
|
||
|
}
|
||
|
|
||
|
bsp_isp_init_platform(SUNXI_PLATFORM_ID);
|
||
|
bsp_isp_set_base_addr((unsigned long)isp->base);
|
||
|
bsp_isp_set_map_load_addr((unsigned long)isp->isp_load_reg_mm.vir_addr);
|
||
|
bsp_isp_set_map_saved_addr((unsigned long)isp->isp_save_reg_mm.vir_addr);
|
||
|
memset((unsigned int *)isp->isp_load_reg_mm.vir_addr, 0, ISP_LOAD_REG_SIZE);
|
||
|
memset((unsigned int *)isp->isp_save_reg_mm.vir_addr, 0, ISP_SAVED_REG_SIZE);
|
||
|
platform_set_drvdata(pdev, isp);
|
||
|
vfe_print("isp%d probe end!\n", isp->id);
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
ehwinit:
|
||
|
iounmap(isp->base);
|
||
|
freedev:
|
||
|
kfree(isp);
|
||
|
ekzalloc:
|
||
|
vfe_print("isp probe err!\n");
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int isp_remove(struct platform_device *pdev)
|
||
|
{
|
||
|
struct isp_dev *isp = platform_get_drvdata(pdev);
|
||
|
struct device *dev = &pdev->dev;
|
||
|
|
||
|
platform_set_drvdata(pdev, NULL);
|
||
|
isp_resource_release(dev, isp);
|
||
|
if (isp->ioarea) {
|
||
|
release_resource(isp->ioarea);
|
||
|
kfree(isp->ioarea);
|
||
|
}
|
||
|
if (isp->base)
|
||
|
iounmap(isp->base);
|
||
|
kfree(isp);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static const struct of_device_id sunxi_isp_match[] = {
|
||
|
{.compatible = "allwinner,sunxi-isp",},
|
||
|
{},
|
||
|
};
|
||
|
|
||
|
static struct platform_driver isp_platform_driver = {
|
||
|
.probe = isp_probe,
|
||
|
.remove = isp_remove,
|
||
|
.driver = {
|
||
|
.name = ISP_MODULE_NAME,
|
||
|
.owner = THIS_MODULE,
|
||
|
.of_match_table = sunxi_isp_match,
|
||
|
}
|
||
|
};
|
||
|
|
||
|
void sunxi_isp_dump_regs(struct v4l2_subdev *sd)
|
||
|
{
|
||
|
struct isp_dev *isp = v4l2_get_subdevdata(sd);
|
||
|
int i = 0;
|
||
|
|
||
|
pr_info("Vfe dump ISP regs :\n");
|
||
|
for (i = 0; i < 0x40; i = i + 4) {
|
||
|
if (i % 0x10 == 0)
|
||
|
pr_info("0x%08x: ", i);
|
||
|
pr_info("0x%08x, ", readl(isp->base + i));
|
||
|
if (i % 0x10 == 0xc)
|
||
|
pr_info("\n");
|
||
|
}
|
||
|
for (i = 0x40; i < 0x240; i = i + 4) {
|
||
|
if (i % 0x10 == 0)
|
||
|
pr_info("0x%08x: ", i);
|
||
|
pr_info("0x%08x, ", readl(isp->isp_load_reg_mm.vir_addr + i));
|
||
|
if (i % 0x10 == 0xc)
|
||
|
pr_info("\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int sunxi_isp_register_subdev(struct v4l2_device *v4l2_dev, struct v4l2_subdev *sd)
|
||
|
{
|
||
|
if (sd == NULL)
|
||
|
return -ENODEV;
|
||
|
|
||
|
return v4l2_device_register_subdev(v4l2_dev, sd);
|
||
|
}
|
||
|
|
||
|
void sunxi_isp_unregister_subdev(struct v4l2_subdev *sd)
|
||
|
{
|
||
|
if (sd == NULL)
|
||
|
return;
|
||
|
v4l2_device_unregister_subdev(sd);
|
||
|
v4l2_ctrl_handler_free(sd->ctrl_handler);
|
||
|
v4l2_set_subdevdata(sd, NULL);
|
||
|
}
|
||
|
|
||
|
int sunxi_isp_get_subdev(struct v4l2_subdev **sd, int sel)
|
||
|
{
|
||
|
struct isp_dev *isp;
|
||
|
|
||
|
list_for_each_entry(isp, &isp_drv_list, isp_list) {
|
||
|
if (isp->id == sel) {
|
||
|
*sd = &isp->subdev;
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
int sunxi_isp_put_subdev(struct v4l2_subdev **sd, int sel)
|
||
|
{
|
||
|
*sd = NULL;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int sunxi_isp_platform_register(void)
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
ret = platform_driver_register(&isp_platform_driver);
|
||
|
if (ret) {
|
||
|
vfe_err("platform driver register failed\n");
|
||
|
return ret;
|
||
|
}
|
||
|
vfe_print("sunxi_isp_platform_register end\n");
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void sunxi_isp_platform_unregister(void)
|
||
|
{
|
||
|
vfe_print("sunxi_isp_platform_unregister start\n");
|
||
|
platform_driver_unregister(&isp_platform_driver);
|
||
|
vfe_print("sunxi_isp_platform_unregister end\n");
|
||
|
}
|
||
|
|
||
|
|