6296 lines
182 KiB
C

/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Copyright(c) 2020 - 2023 Allwinner Technology Co.,Ltd. All rights reserved. */
/*
* vin_video.c for video api
*
* Copyright (c) 2017 by Allwinnertech Co., Ltd. http://www.allwinnertech.com
*
* Authors: Zhao Wei <zhaowei@allwinnertech.com>
* Yang Feng <yangfeng@allwinnertech.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include "../utility/vin_log.h"
#include <linux/version.h>
#include <linux/videodev2.h>
#include <linux/string.h>
#include <linux/freezer.h>
#include <linux/sort.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/mman.h>
#include <linux/fs.h>
#include <linux/moduleparam.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-common.h>
#include <media/v4l2-mediabus.h>
#include <media/v4l2-subdev.h>
#include <media/videobuf2-dma-contig.h>
#include <linux/dma-buf.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
#include <linux/dma-heap.h>
#endif
#include <linux/dma-mapping.h>
#include <linux/regulator/consumer.h>
#if IS_ENABLED(CONFIG_DEVFREQ_DRAM_FREQ_WITH_SOFT_NOTIFY)
#include <linux/sunxi_dramfreq.h>
#endif
#include "../utility/config.h"
#include "../modules/sensor/sensor_helper.h"
#include "../utility/vin_io.h"
#include "../vin-csi/sunxi_csi.h"
#include "../vin-isp/sunxi_isp.h"
#include "../vin-vipp/sunxi_scaler.h"
#include "../vin-mipi/sunxi_mipi.h"
#include "../vin-tdm/vin_tdm.h"
#include "../vin.h"
#if IS_ENABLED(CONFIG_ISP_SERVER_MELIS)
#include "../vin-isp/isp_tuning_priv.h"
#endif
#define VIN_MAJOR_VERSION 1
#define VIN_MINOR_VERSION 1
#define VIN_RELEASE 0
#define MIN_IN_WIDTH 192
#define MIN_IN_HEIGHT 128
#define VIN_VERSION \
KERNEL_VERSION(VIN_MAJOR_VERSION, VIN_MINOR_VERSION, VIN_RELEASE)
extern struct vin_core *vin_core_gbl[VIN_MAX_DEV];
extern struct scaler_dev *glb_vipp[VIN_MAX_SCALER];
#define GET_BIT(x, bit) ((x & (1 << bit)) >> bit)
#if IS_ENABLED(CONFIG_VIN_INIT_MELIS)
void *vin_map_kernel(unsigned long phys_addr, unsigned long size)
{
int npages = PAGE_ALIGN(size) / PAGE_SIZE;
struct page **pages = vmalloc(sizeof(struct page *) * npages);
struct page **tmp = pages;
struct page *cur_page = phys_to_page(phys_addr);
pgprot_t pgprot;
void *vaddr = NULL;
int i;
if (!pages)
return NULL;
for (i = 0; i < npages; i++)
*(tmp++) = cur_page++;
pgprot = PAGE_KERNEL;
vaddr = vmap(pages, npages, VM_MAP, pgprot);
vfree(pages);
return vaddr;
}
EXPORT_SYMBOL(vin_map_kernel);
void vin_unmap_kernel(void *vaddr)
{
vunmap(vaddr);
}
EXPORT_SYMBOL(vin_unmap_kernel);
#endif
void __vin_s_stream_handle(struct work_struct *work)
{
int ret = 0;
struct vin_vid_cap *cap =
container_of(work, struct vin_vid_cap, s_stream_task);
vin_timer_init(cap->vinc);
ret = vin_pipeline_call(cap->vinc, set_stream, &cap->pipe, cap->vinc->stream_idx);
if (ret < 0) {
vin_err("%s error!\n", __func__);
return;
}
/* set saved exp and gain for reopen */
if (cap->vinc->exp_gain.exp_val && cap->vinc->exp_gain.gain_val) {
v4l2_subdev_call(cap->pipe.sd[VIN_IND_SENSOR], core, ioctl,
VIDIOC_VIN_SENSOR_EXP_GAIN, &cap->vinc->exp_gain);
}
vin_log(VIN_LOG_VIDEO, "%s done, id = %d!\n", __func__, cap->vinc->id);
}
/* make sure addr was update to register */
#if defined CSIC_DMA_VER_140_000
static int __check_bk_bufaddr(struct vin_core *vinc, struct vin_addr *paddr)
{
unsigned int y, cb, cr;
if (vinc->vid_cap.frame.fmt.fourcc == V4L2_PIX_FMT_YVU420) {
csic_dma_get_buffer_address(vinc->vipp_sel, CSI_BUF_0_A, &y);
csic_dma_get_buffer_address(vinc->vipp_sel, CSI_BUF_1_A, &cr);
csic_dma_get_buffer_address(vinc->vipp_sel, CSI_BUF_2_A, &cb);
} else {
csic_dma_get_buffer_address(vinc->vipp_sel, CSI_BUF_0_A, &y);
csic_dma_get_buffer_address(vinc->vipp_sel, CSI_BUF_1_A, &cb);
csic_dma_get_buffer_address(vinc->vipp_sel, CSI_BUF_2_A, &cr);
}
if (paddr->y != y || paddr->cb != cb || paddr->cr != cr) {
vin_err("vinc%d cannot write and read the right addr to register,y 0x%x, cb 0x%x,cr 0x%x\n", vinc->id, y, cb, cr);
return -EINVAL;
}
return 0;
}
#else
static int __check_bk_bufaddr(struct vin_core *vinc, struct vin_addr *paddr)
{
unsigned int y, cb, cr;
/* unsigned int cnt = 0; */
if (vinc->vid_cap.frame.fmt.fourcc == V4L2_PIX_FMT_YVU420) {
y = readl(vinc->base + 0x20) << 2;
cr = readl(vinc->base + 0x28) << 2;
cb = readl(vinc->base + 0x30) << 2;
} else {
y = readl(vinc->base + 0x20) << 2;
cb = readl(vinc->base + 0x28) << 2;
cr = readl(vinc->base + 0x30) << 2;
}
/*
while ((paddr->y != y || paddr->cb != cb || paddr->cr != cr) && (cnt < 2)) {
if(vinc->vid_cap.frame.fmt.fourcc == V4L2_PIX_FMT_YVU420) {
csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_0_A, paddr->y);
csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_2_A, paddr->cb);
csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_1_A, paddr->cr);
y = readl(vinc->base + 0x20) << 2;
cr = readl(vinc->base + 0x28) << 2;
cb = readl(vinc->base + 0x30) << 2;
} else {
csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_0_A, paddr->y);
csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_1_A, paddr->cb);
csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_2_A, paddr->cr);
y = readl(vinc->base + 0x20) << 2;
cb = readl(vinc->base + 0x28) << 2;
cr = readl(vinc->base + 0x30) << 2;
}
cnt++;
}
*/
if (paddr->y != y || paddr->cb != cb || paddr->cr != cr) {
vin_err("vinc%d cannot write and read the right addr to register!!\n", vinc->id);
return -EINVAL;
}
return 0;
}
#endif
/* The color format (colplanes, memplanes) must be already configured. */
int vin_set_addr(struct vin_core *vinc, struct vb2_buffer *vb,
struct vin_frame *frame, struct vin_addr *paddr)
{
u32 pix_size, depth, y_stride, u_stride, v_stride;
struct vb2_v4l2_buffer *vb2_v4l2;
struct vin_buffer *buf;
__maybe_unused int offset_width;
__maybe_unused struct vin_core *vinc_bind = NULL;
vb2_v4l2 = container_of(vb, struct vb2_v4l2_buffer, vb2_buf);
buf = container_of(vb2_v4l2, struct vin_buffer, vb);
if (vinc->vid_cap.special_active == 1) {
if (buf == NULL || buf->paddr == NULL)
return -EINVAL;
} else {
if (vb == NULL || frame == NULL)
return -EINVAL;
}
#if !defined CONFIG_ARCH_SUN8IW21
pix_size = ALIGN(frame->o_width, VIN_ALIGN_WIDTH) * frame->o_height;
#else
pix_size = ALIGN(frame->o_width, VIN_ALIGN_WIDTH) * ALIGN(frame->o_height, VIN_ALIGN_HEIGHT);
#endif
depth = frame->fmt.depth[0] + frame->fmt.depth[1] + frame->fmt.depth[2];
if (vinc->vid_cap.special_active == 1) {
paddr->y = (dma_addr_t)buf->paddr;
frame->fmt.memplanes = 1;
} else
paddr->y = vb2_dma_contig_plane_dma_addr(vb, 0);
if (frame->fmt.memplanes == 1) {
switch (frame->fmt.colplanes) {
case 1:
paddr->cb = 0;
paddr->cr = 0;
break;
case 2:
/* decompose Y into Y/Cb */
if (frame->fmt.fourcc == V4L2_PIX_FMT_FBC) {
#if IS_ENABLED(CONFIG_ARCH_SUN8IW12P1)
paddr->cb = (u32)(paddr->y + CEIL_EXP(frame->o_width, 7) * CEIL_EXP(frame->o_height, 5) * 96);
#else
paddr->cb = paddr->y + ALIGN(DIV_ROUND_UP(frame->o_width, 128) * DIV_ROUND_UP(frame->o_height, 32) * 96, 64);
#endif
paddr->cr = 0;
} else {
paddr->cb = (u32)(paddr->y + pix_size);
paddr->cr = 0;
}
break;
case 3:
paddr->cb = (u32)(paddr->y + pix_size);
/* 420 */
if (frame->fmt.depth[0] == 12)
paddr->cr = (u32)(paddr->cb + (pix_size >> 2));
else /* 422 */
paddr->cr = (u32)(paddr->cb + (pix_size >> 1));
break;
default:
return -EINVAL;
}
} else if (!frame->fmt.mdataplanes) {
if (frame->fmt.memplanes >= 2)
paddr->cb = vb2_dma_contig_plane_dma_addr(vb, 1);
if (frame->fmt.memplanes == 3)
paddr->cr = vb2_dma_contig_plane_dma_addr(vb, 2);
}
#ifdef NO_SUPPROT_HARDWARE_CALCULATE
if ((vinc->vflip == 1) && (frame->fmt.fourcc == V4L2_PIX_FMT_FBC)) {
paddr->y += CEIL_EXP(frame->o_width, 7) * (CEIL_EXP(frame->o_height, 5) - 1) * 96;
paddr->cb += CEIL_EXP(frame->o_width, 4) * (CEIL_EXP(frame->o_height, 2) - 1) * 96;
paddr->cr = 0;
} else if (vinc->vflip == 1) {
switch (frame->fmt.colplanes) {
case 1:
paddr->y += (pix_size - frame->o_width) * frame->fmt.depth[0] / 8;
paddr->cb = 0;
paddr->cr = 0;
break;
case 2:
paddr->y += pix_size - frame->o_width;
/* 420 */
if (depth == 12)
paddr->cb += pix_size / 2 - frame->o_width;
else /* 422 */
paddr->cb += pix_size - frame->o_width;
paddr->cr = 0;
break;
case 3:
paddr->y += pix_size - frame->o_width;
if (depth == 12) {
paddr->cb += pix_size / 4 - frame->o_width / 2;
paddr->cr += pix_size / 4 - frame->o_width / 2;
} else {
paddr->cb += pix_size / 2 - frame->o_width / 2;
paddr->cr += pix_size / 2 - frame->o_width / 2;
}
break;
default:
return -EINVAL;
}
}
#endif
if ((vinc->large_image == 2) && (vinc->vin_status.frame_cnt % 2)) {
if (frame->fmt.colplanes == 3) {
if (depth == 12) {
/* 420 */
y_stride = frame->o_width / 2;
u_stride = frame->o_width / 2 / 2;
v_stride = frame->o_width / 2 / 2;
} else {
/* 422 */
y_stride = frame->o_width / 2;
u_stride = frame->o_width / 2;
v_stride = frame->o_width / 2;
}
} else {
y_stride = frame->o_width / 2;
u_stride = frame->o_width / 2;
v_stride = frame->o_width / 2;
}
csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_0_A, paddr->y + y_stride);
csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_1_A, paddr->cb + u_stride);
csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_2_A, paddr->cr + v_stride);
} else {
if (vinc->vid_cap.frame.fmt.fourcc == V4L2_PIX_FMT_YVU420) {
csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_0_A, paddr->y);
csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_2_A, paddr->cb);
csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_1_A, paddr->cr);
} else {
csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_0_A, paddr->y);
csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_1_A, paddr->cb);
csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_2_A, paddr->cr);
}
if (__check_bk_bufaddr(vinc, paddr))
return -EINVAL;
}
if (vinc->dma_merge_mode == 1) {
if (vinc->id % 4 == 1 && vin_core_gbl[vinc->id - 1]) {
offset_width = frame->o_width/2;
vinc_bind = vin_core_gbl[vinc->id - 1];
csic_dma_buffer_address(vinc_bind->vipp_sel, CSI_BUF_0_A, paddr->y);
csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_0_A, paddr->y + offset_width);
if (paddr->cr && paddr->cb) {
csic_dma_buffer_address(vinc_bind->vipp_sel, CSI_BUF_1_A, paddr->cb);
csic_dma_buffer_address(vinc_bind->vipp_sel, CSI_BUF_2_A, paddr->cr);
csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_1_A, paddr->cb + (offset_width >> 1));
csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_2_A, paddr->cr + (offset_width >> 1));
} else if (paddr->cb) {
csic_dma_buffer_address(vinc_bind->vipp_sel, CSI_BUF_1_A, paddr->cb);
csic_dma_buffer_address(vinc_bind->vipp_sel, CSI_BUF_2_A, paddr->cr);
csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_1_A, paddr->cb + (offset_width));
csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_2_A, paddr->cr);
} else {
csic_dma_buffer_address(vinc_bind->vipp_sel, CSI_BUF_1_A, paddr->cb);
csic_dma_buffer_address(vinc_bind->vipp_sel, CSI_BUF_2_A, paddr->cr);
csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_1_A, paddr->cb);
csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_2_A, paddr->cr);
}
}
}
return 0;
}
void vin_set_next_buf_addr(struct vin_core *vinc)
{
struct vin_vid_cap *cap = &vinc->vid_cap;
struct vin_buffer *buf;
__maybe_unused struct list_head *buf_next;
__maybe_unused int i;
if (vinc->large_image == 1)
return;
vinc->vid_cap.first_flag = 0;
vinc->vin_status.frame_cnt = 0;
vinc->vin_status.err_cnt = 0;
vinc->vin_status.lost_cnt = 0;
#ifndef BUF_AUTO_UPDATE
buf = list_entry(cap->vidq_active.next, struct vin_buffer, list);
vin_set_addr(vinc, &buf->vb.vb2_buf, &vinc->vid_cap.frame, &vinc->vid_cap.frame.paddr);
#else
vin_get_rest_buf_cnt(vinc);
cap->threshold.bufa_fifo_threshold = 1;
cap->threshold.stored_frm_threshold = 2;
cap->threshold.bufa_fifo_total = vinc->vin_status.buf_rest;
csic_buf_addr_fifo_en(vinc->vipp_sel, 1);
csic_set_threshold_for_bufa_mode(vinc->vipp_sel, &cap->threshold);
buf_next = cap->vidq_active.next;
for (i = 0; i < cap->threshold.bufa_fifo_total; i++) {
buf = list_entry(buf_next, struct vin_buffer, list);
vin_set_addr(vinc, &buf->vb.vb2_buf, &vinc->vid_cap.frame, &vinc->vid_cap.frame.paddr);
buf_next = buf_next->next;
}
#endif
}
static int lbc_mode_select(struct dma_lbc_cmp *lbc_cmp, unsigned int fourcc)
{
switch (fourcc) {
case V4L2_PIX_FMT_LBC_2_0X: /* 2x */
lbc_cmp->is_lossy = 1;
lbc_cmp->bit_depth = 8;
lbc_cmp->glb_enable = 1;
lbc_cmp->dts_enable = 1;
lbc_cmp->ots_enable = 1;
lbc_cmp->msq_enable = 1;
lbc_cmp->cmp_ratio_even = 600;
lbc_cmp->cmp_ratio_odd = 450;
lbc_cmp->mb_mi_bits[0] = 55;
lbc_cmp->mb_mi_bits[1] = 110;
lbc_cmp->rc_adv[0] = 60;
lbc_cmp->rc_adv[1] = 30;
lbc_cmp->rc_adv[2] = 15;
lbc_cmp->rc_adv[3] = 8;
lbc_cmp->lmtqp_en = 1;
lbc_cmp->lmtqp_min = 1;
lbc_cmp->updata_adv_en = 1;
lbc_cmp->updata_adv_ratio = 2;
break;
case V4L2_PIX_FMT_LBC_2_5X: /* 2.5x */
lbc_cmp->is_lossy = 1;
lbc_cmp->bit_depth = 8;
lbc_cmp->glb_enable = 1;
lbc_cmp->dts_enable = 1;
lbc_cmp->ots_enable = 1;
lbc_cmp->msq_enable = 1;
lbc_cmp->cmp_ratio_even = 440;
lbc_cmp->cmp_ratio_odd = 380;
lbc_cmp->mb_mi_bits[0] = 55;
lbc_cmp->mb_mi_bits[1] = 94;
lbc_cmp->rc_adv[0] = 60;
lbc_cmp->rc_adv[1] = 30;
lbc_cmp->rc_adv[2] = 15;
lbc_cmp->rc_adv[3] = 8;
lbc_cmp->lmtqp_en = 1;
lbc_cmp->lmtqp_min = 1;
lbc_cmp->updata_adv_en = 1;
lbc_cmp->updata_adv_ratio = 2;
break;
case V4L2_PIX_FMT_LBC_1_0X: /* lossless */
lbc_cmp->is_lossy = 0;
lbc_cmp->bit_depth = 8;
lbc_cmp->glb_enable = 1;
lbc_cmp->dts_enable = 1;
lbc_cmp->ots_enable = 1;
lbc_cmp->msq_enable = 1;
lbc_cmp->cmp_ratio_even = 1000;
lbc_cmp->cmp_ratio_odd = 1000;
lbc_cmp->mb_mi_bits[0] = 55;
lbc_cmp->mb_mi_bits[1] = 94;
lbc_cmp->rc_adv[0] = 60;
lbc_cmp->rc_adv[1] = 30;
lbc_cmp->rc_adv[2] = 15;
lbc_cmp->rc_adv[3] = 8;
lbc_cmp->lmtqp_en = 1;
lbc_cmp->lmtqp_min = 1;
lbc_cmp->updata_adv_en = 1;
lbc_cmp->updata_adv_ratio = 2;
break;
case V4L2_PIX_FMT_LBC_1_5X: /* 1.5x */
lbc_cmp->is_lossy = 1;
lbc_cmp->bit_depth = 8;
lbc_cmp->glb_enable = 1;
lbc_cmp->dts_enable = 1;
lbc_cmp->ots_enable = 1;
lbc_cmp->msq_enable = 1;
lbc_cmp->cmp_ratio_even = 670;
lbc_cmp->cmp_ratio_odd = 658;
lbc_cmp->mb_mi_bits[0] = 87;
lbc_cmp->mb_mi_bits[1] = 167;
lbc_cmp->rc_adv[0] = 60;
lbc_cmp->rc_adv[1] = 30;
lbc_cmp->rc_adv[2] = 15;
lbc_cmp->rc_adv[3] = 8;
lbc_cmp->lmtqp_en = 1;
lbc_cmp->lmtqp_min = 1;
lbc_cmp->updata_adv_en = 1;
lbc_cmp->updata_adv_ratio = 2;
break;
default:
return -1;
}
return 0;
}
/*
* Videobuf operations
*/
static int queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], struct device *alloc_devs[])
{
struct vin_vid_cap *cap = vb2_get_drv_priv(vq);
unsigned int size;
int buf_max_flag = 0;
int wth;
int i;
cap->frame.bytesperline[0] = cap->frame.o_width * cap->frame.fmt.depth[0] / 8;
cap->frame.bytesperline[1] = cap->frame.o_width * cap->frame.fmt.depth[1] / 8;
cap->frame.bytesperline[2] = cap->frame.o_width * cap->frame.fmt.depth[2] / 8;
#if VIN_FALSE
size = cap->frame.o_width * cap->frame.o_height;
#else
size = roundup(cap->frame.o_width, VIN_ALIGN_WIDTH) * roundup(cap->frame.o_height, VIN_ALIGN_HEIGHT);
#endif
switch (cap->frame.fmt.fourcc) {
case V4L2_PIX_FMT_FBC:
#if IS_ENABLED(CONFIG_ARCH_SUN8IW12P1)
cap->frame.payload[0] = (CEIL_EXP(cap->frame.o_width, 7) * CEIL_EXP(cap->frame.o_height, 5) +
CEIL_EXP(cap->frame.o_width, 4) * CEIL_EXP(cap->frame.o_height, 2)) * 96;
#else
cap->frame.payload[0] = (ALIGN(cap->frame.o_width / 16 * 96, 64) * (cap->frame.o_height / 4)) +
ALIGN(DIV_ROUND_UP(cap->frame.o_width, 128) * DIV_ROUND_UP(cap->frame.o_height, 32) * 96, 64);
#endif
break;
case V4L2_PIX_FMT_LBC_2_0X:
case V4L2_PIX_FMT_LBC_2_5X:
case V4L2_PIX_FMT_LBC_1_0X:
case V4L2_PIX_FMT_LBC_1_5X:
lbc_mode_select(&cap->lbc_cmp, cap->frame.fmt.fourcc);
wth = roundup(cap->frame.o_width, 32);
if (cap->lbc_cmp.is_lossy) {
cap->lbc_cmp.line_tar_bits[0] = roundup(cap->lbc_cmp.cmp_ratio_even * wth * cap->lbc_cmp.bit_depth/1000, 512);
cap->lbc_cmp.line_tar_bits[1] = roundup(cap->lbc_cmp.cmp_ratio_odd * wth * cap->lbc_cmp.bit_depth/500, 512);
} else {
cap->lbc_cmp.line_tar_bits[0] = roundup(wth * cap->lbc_cmp.bit_depth * 1 + (wth * 1 / 16 * 2), 512);
cap->lbc_cmp.line_tar_bits[1] = roundup(wth * cap->lbc_cmp.bit_depth * 2 + (wth * 2 / 16 * 2), 512);
}
/* add 1KB buffer to fix ve-lbc error */
cap->frame.payload[0] = (cap->lbc_cmp.line_tar_bits[0] + cap->lbc_cmp.line_tar_bits[1]) * cap->frame.o_height/2/8 + 1024;
break;
default:
cap->frame.payload[0] = size * cap->frame.fmt.depth[0] / 8;
break;
}
cap->frame.payload[1] = size * cap->frame.fmt.depth[1] / 8;
cap->frame.payload[2] = size * cap->frame.fmt.depth[2] / 8;
cap->buf_byte_size =
PAGE_ALIGN(cap->frame.payload[0]) +
PAGE_ALIGN(cap->frame.payload[1]) +
PAGE_ALIGN(cap->frame.payload[2]);
size = cap->buf_byte_size;
if (size == 0)
return -EINVAL;
if (*nbuffers == 0)
*nbuffers = 8;
while (size * *nbuffers > MAX_FRAME_MEM) {
(*nbuffers)--;
buf_max_flag = 1;
if (*nbuffers == 0)
vin_err("Buffer size > max frame memory! count = %d\n",
*nbuffers);
}
if (buf_max_flag == 0) {
if (cap->capture_mode == V4L2_MODE_IMAGE) {
if (*nbuffers != 1) {
*nbuffers = 1;
vin_err("buffer count != 1 in capture mode\n");
}
} else {
if (cap->vinc->ve_online_cfg.dma_buf_num == BK_TWO_BUFFER && cap->vinc->id == CSI_VE_ONLINE_VIDEO && *nbuffers != 2) {
*nbuffers = 2;
vin_print("video%d:buffer count is invalid, set to 2\n", cap->vinc->id);
} else if (cap->vinc->ve_online_cfg.dma_buf_num == BK_ONE_BUFFER && cap->vinc->id == CSI_VE_ONLINE_VIDEO && *nbuffers != 1) {
*nbuffers = 1;
vin_print("video%d:buffer count is invalid, set to 1\n", cap->vinc->id);
} else {
if (*nbuffers < 3) {
#if IS_ENABLED(CONFIG_DISPPLAY_SYNC)
if (cap->vinc->id != disp_sync_video)
*nbuffers = 3;
vin_warn("buffer count is %d\n", *nbuffers);
#else
*nbuffers = 3;
vin_err("buffer count is invalid, set to 3\n");
#endif
}
}
}
}
*nplanes = cap->frame.fmt.memplanes;
for (i = 0; i < *nplanes; i++) {
sizes[i] = cap->frame.payload[i];
alloc_devs[i] = cap->dev;
}
vin_log(VIN_LOG_VIDEO, "%s, buf count = %d, nplanes = %d, size = %d\n",
__func__, *nbuffers, *nplanes, size);
cap->vinc->vin_status.buf_cnt = *nbuffers;
cap->vinc->vin_status.buf_size = size;
return 0;
}
static int buffer_prepare(struct vb2_buffer *vb)
{
struct vin_vid_cap *cap = vb2_get_drv_priv(vb->vb2_queue);
struct vb2_v4l2_buffer *vvb = container_of(vb, struct vb2_v4l2_buffer, vb2_buf);
struct vin_buffer *buf = container_of(vvb, struct vin_buffer, vb);
int i;
if (cap->frame.o_width < MIN_WIDTH || cap->frame.o_width > MAX_WIDTH ||
cap->frame.o_height < MIN_HEIGHT || cap->frame.o_height > MAX_HEIGHT) {
return -EINVAL;
}
/* size = dev->buf_byte_size; */
for (i = 0; i < cap->frame.fmt.memplanes; i++) {
if (vb2_plane_size(vb, i) < cap->frame.payload[i]) {
vin_err("%s data will not fit into plane (%lu < %lu)\n",
__func__, vb2_plane_size(vb, i),
cap->frame.payload[i]);
return -EINVAL;
}
vb2_set_plane_payload(&buf->vb.vb2_buf, i, cap->frame.payload[i]);
vb->planes[i].m.offset = vb2_dma_contig_plane_dma_addr(vb, i);
}
return 0;
}
static void buffer_queue(struct vb2_buffer *vb)
{
struct vin_vid_cap *cap = vb2_get_drv_priv(vb->vb2_queue);
struct vb2_v4l2_buffer *vvb = container_of(vb, struct vb2_v4l2_buffer, vb2_buf);
struct vin_buffer *buf = container_of(vvb, struct vin_buffer, vb);
unsigned long flags = 0;
spin_lock_irqsave(&cap->slock, flags);
if (cap->vinc->id == CSI_VE_ONLINE_VIDEO && cap->vinc->ve_online_cfg.dma_buf_num == BK_TWO_BUFFER) {
if (buf->first_qbuf == 0) {
list_add_tail(&buf->list, &cap->vidq_active);
buf->first_qbuf = 1;
}
buf->qbufed = 1;
} else {
list_add_tail(&buf->list, &cap->vidq_active);
}
#ifdef BUF_AUTO_UPDATE
vin_set_addr(cap->vinc, &buf->vb.vb2_buf, &cap->frame, &cap->frame.paddr);
#endif
spin_unlock_irqrestore(&cap->slock, flags);
}
static int start_streaming(struct vb2_queue *vq, unsigned int count)
{
return 0;
}
/* abort streaming and wait for last buffer */
static void stop_streaming(struct vb2_queue *vq)
{
struct vin_vid_cap *cap = vb2_get_drv_priv(vq);
unsigned long flags = 0;
spin_lock_irqsave(&cap->slock, flags);
/* Release all active buffers */
while (!list_empty(&cap->vidq_active)) {
struct vin_buffer *buf;
buf = list_entry(cap->vidq_active.next, struct vin_buffer, list);
list_del(&buf->list);
if (cap->vinc->id == CSI_VE_ONLINE_VIDEO && cap->vinc->ve_online_cfg.dma_buf_num == BK_TWO_BUFFER) {
if (!buf->qbufed)
continue;
buf->first_qbuf = 0;
}
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
vin_log(VIN_LOG_VIDEO, "buf %d stop\n", buf->vb.vb2_buf.index);
}
spin_unlock_irqrestore(&cap->slock, flags);
}
static const struct vb2_ops vin_video_qops = {
.queue_setup = queue_setup,
.buf_prepare = buffer_prepare,
.buf_queue = buffer_queue,
.start_streaming = start_streaming,
.stop_streaming = stop_streaming,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
};
/*
* IOCTL vidioc handling
*/
static int vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
strcpy(cap->driver, "sunxi-vin");
strcpy(cap->card, "sunxi-vin");
cap->version = VIN_VERSION;
cap->capabilities = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_STREAMING |
V4L2_CAP_READWRITE | V4L2_CAP_DEVICE_CAPS;
return 0;
}
static int vidioc_enum_fmt_vid_cap_mplane(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
struct vin_fmt *fmt;
fmt = vin_find_format(NULL, NULL, VIN_FMT_ALL, f->index, false);
if (!fmt)
return -EINVAL;
strlcpy(f->description, fmt->name, sizeof(f->description));
f->pixelformat = fmt->fourcc;
return 0;
}
static int vidioc_enum_framesizes(struct file *file, void *fh,
struct v4l2_frmsizeenum *fsize)
{
struct vin_core *vinc = video_drvdata(file);
struct v4l2_subdev_frame_size_enum fse;
int ret;
if (vinc == NULL)
return -EINVAL;
memset(&fse, 0, sizeof(fse));
fse.index = fsize->index;
fse.which = V4L2_SUBDEV_FORMAT_ACTIVE;
ret = v4l2_subdev_call(vinc->vid_cap.pipe.sd[VIN_IND_SENSOR], pad,
enum_frame_size, NULL, &fse);
if (ret < 0)
return -1;
fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
fsize->stepwise.min_width = MIN_IN_WIDTH;
fsize->stepwise.min_height = MIN_IN_HEIGHT;
fsize->stepwise.step_width = 2;
fsize->stepwise.max_width = fse.max_width;
fsize->stepwise.max_height = fse.max_height;
fsize->stepwise.step_height = 2;
return 0;
}
static int vidioc_enum_frameintervals(struct file *file, void *fh,
struct v4l2_frmivalenum *fival)
{
struct vin_core *vinc = video_drvdata(file);
struct v4l2_subdev_frame_interval_enum fie;
int ret;
if (vinc == NULL)
return -EINVAL;
memset(&fie, 0, sizeof(fie));
fie.index = fival->index;
fie.width = fival->width;
fie.height = fival->height;
fie.which = V4L2_SUBDEV_FORMAT_ACTIVE;
ret = v4l2_subdev_call(vinc->vid_cap.pipe.sd[VIN_IND_SENSOR], pad,
enum_frame_interval, NULL, &fie);
if (ret < 0)
return -1;
fival->index = fie.index; /* update index */
fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
fival->discrete = fie.interval;
return 0;
}
static int vidioc_g_fmt_vid_cap_mplane(struct file *file, void *priv,
struct v4l2_format *f)
{
struct vin_core *vinc = video_drvdata(file);
struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
int i;
pixm->width = vinc->vid_cap.frame.o_width;
pixm->height = vinc->vid_cap.frame.o_height;
pixm->field = V4L2_FIELD_NONE;
pixm->pixelformat = vinc->vid_cap.frame.fmt.fourcc;
pixm->colorspace = vinc->vid_cap.frame.fmt.color;/* V4L2_COLORSPACE_JPEG; */
pixm->num_planes = vinc->vid_cap.frame.fmt.memplanes;
for (i = 0; i < pixm->num_planes; ++i) {
pixm->plane_fmt[i].bytesperline = vinc->vid_cap.frame.bytesperline[i];
pixm->plane_fmt[i].sizeimage = vinc->vid_cap.frame.payload[i];
}
return 0;
}
static int vin_pipeline_try_format(struct vin_core *vinc,
struct v4l2_mbus_framefmt *tfmt,
struct vin_fmt **fmt_id,
bool set)
{
struct v4l2_subdev *sd = vinc->vid_cap.pipe.sd[VIN_IND_SENSOR];
struct v4l2_subdev_format sfmt;
struct media_entity *me;
struct vin_fmt *ffmt;
unsigned int mask;
struct media_graph graph;
int ret, i = 0, sd_ind;
int ch_id;
if (WARN_ON(!sd || !tfmt || !fmt_id))
return -EINVAL;
memset(&sfmt, 0, sizeof(sfmt));
sfmt.format = *tfmt;
sfmt.which = set ? V4L2_SUBDEV_FORMAT_ACTIVE : V4L2_SUBDEV_FORMAT_TRY;
mask = (*fmt_id)->flags;
if ((mask & VIN_FMT_YUV) && (vinc->support_raw == 0))
mask = VIN_FMT_YUV;
/* when diffrent video output have same sensor,
* this pipeline try fmt will lead to false result.
* so it should be updated at later.
*/
while (1) {
ffmt = vin_find_format(NULL, sfmt.format.code != 0 ? &sfmt.format.code : NULL,
mask, i++, true);
if (ffmt == NULL) {
/*
* Notify user-space if common pixel code for
* host and sensor does not exist.
*/
vin_err("vin is not support this pixelformat\n");
return -EINVAL;
}
sfmt.format.code = tfmt->code = ffmt->mbus_code;
me = &vinc->vid_cap.subdev.entity;
mutex_lock(&vinc->vid_cap.vdev.entity.graph_obj.mdev->graph_mutex);
if (media_graph_walk_init(&graph, me->graph_obj.mdev) != 0) {
mutex_unlock(&vinc->vid_cap.vdev.entity.graph_obj.mdev->graph_mutex);
return -EINVAL;
}
media_graph_walk_start(&graph, me);
while ((me = media_graph_walk_next(&graph)) &&
me != &vinc->vid_cap.subdev.entity) {
sd = media_entity_to_v4l2_subdev(me);
switch (sd->grp_id) {
case VIN_GRP_ID_SENSOR:
sd_ind = VIN_IND_SENSOR;
break;
case VIN_GRP_ID_MIPI:
sd_ind = VIN_IND_MIPI;
break;
case VIN_GRP_ID_CSI:
sd_ind = VIN_IND_CSI;
break;
case VIN_GRP_ID_TDM_RX:
sd_ind = VIN_IND_TDM_RX;
break;
case VIN_GRP_ID_ISP:
sd_ind = VIN_IND_ISP;
break;
case VIN_GRP_ID_SCALER:
sd_ind = VIN_IND_SCALER;
break;
case VIN_GRP_ID_CAPTURE:
sd_ind = VIN_IND_CAPTURE;
break;
default:
sd_ind = VIN_IND_SENSOR;
break;
}
if (sd != vinc->vid_cap.pipe.sd[sd_ind])
continue;
vin_log(VIN_LOG_FMT, "found %s in this pipeline\n", me->name);
if (me->num_pads == 1 &&
(me->pads[0].flags & MEDIA_PAD_FL_SINK)) {
vin_log(VIN_LOG_FMT, "skip %s.\n", me->name);
continue;
}
/* find ch id to set for tvin*/
if (vinc->tvin.flag) {
#if IS_ENABLED(CONFIG_ARCH_SUN55IW3) || IS_ENABLED(CONFIG_ARCH_SUN60IW2)
if (vinc->id > TVIN_VIDEO_MAX)
ch_id = vinc->id / TVIN_VIDEO_STRIP + vinc->id % TVIN_VIDEO_STRIP;
else
ch_id = vinc->id / TVIN_VIDEO_STRIP;
#else
ch_id = vinc->id;
#endif
if (ch_id >= TVIN_SEPARATE)
sfmt.reserved[0] = ch_id - TVIN_SEPARATE;
else
sfmt.reserved[0] = ch_id;
}
sfmt.pad = 0;
ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &sfmt);
if (ret) {
mutex_unlock(&vinc->vid_cap.vdev.entity.graph_obj.mdev->graph_mutex);
media_graph_walk_cleanup(&graph);
return ret;
}
/* set isp input win size for isp server call sensor_req_cfg */
if (sd->grp_id == VIN_GRP_ID_ISP)
sensor_isp_input(vinc->vid_cap.pipe.sd[VIN_IND_SENSOR], &sfmt.format);
/* change output resolution of scaler */
if (sd->grp_id == VIN_GRP_ID_SCALER) {
sfmt.format.width = tfmt->width;
sfmt.format.height = tfmt->height;
}
if (me->pads[0].flags & MEDIA_PAD_FL_SINK) {
sfmt.pad = me->num_pads - 1;
ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &sfmt);
if (ret) {
mutex_unlock(&vinc->vid_cap.vdev.entity.graph_obj.mdev->graph_mutex);
media_graph_walk_cleanup(&graph);
return ret;
}
}
}
mutex_unlock(&vinc->vid_cap.vdev.entity.graph_obj.mdev->graph_mutex);
media_graph_walk_cleanup(&graph);
if (sfmt.format.code != tfmt->code)
continue;
if (ffmt->mbus_code)
sfmt.format.code = ffmt->mbus_code;
break;
}
if (ffmt)
*fmt_id = ffmt;
*tfmt = sfmt.format;
return 0;
}
static int vin_pipeline_set_mbus_config(struct vin_core *vinc)
{
struct vin_pipeline *pipe = &vinc->vid_cap.pipe;
struct v4l2_subdev *sd = pipe->sd[VIN_IND_SENSOR];
struct v4l2_mbus_config mcfg;
struct media_entity *me;
struct media_graph graph;
struct csi_dev *csi = NULL;
int ret;
ret = v4l2_subdev_call(sd, pad, get_mbus_config, 0, &mcfg);
if (ret < 0) {
vin_err("%s get_mbus_config error!\n", sd->name);
goto out;
}
/* s_mbus_config on all mipi and csi */
me = &vinc->vid_cap.subdev.entity;
mutex_lock(&vinc->vid_cap.vdev.entity.graph_obj.mdev->graph_mutex);
if (media_graph_walk_init(&graph, me->graph_obj.mdev) != 0) {
mutex_unlock(&vinc->vid_cap.vdev.entity.graph_obj.mdev->graph_mutex);
return -EINVAL;
}
media_graph_walk_start(&graph, me);
while ((me = media_graph_walk_next(&graph)) &&
me != &vinc->vid_cap.subdev.entity) {
sd = media_entity_to_v4l2_subdev(me);
if ((sd == pipe->sd[VIN_IND_MIPI]) ||
(sd == pipe->sd[VIN_IND_CSI])) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
ret = sd->ops->pad->get_mbus_config(sd, 0, &mcfg);
#else
ret = sd->ops->pad->set_mbus_config(sd, 0, &mcfg);
#endif
/* ret = v4l2_subdev_call(sd, pad, set_mbus_config,0, &mcfg); */
if (ret < 0) {
vin_err("%s set_mbus_config error!\n", me->name);
media_graph_walk_cleanup(&graph);
goto out;
}
}
}
mutex_unlock(&vinc->vid_cap.vdev.entity.graph_obj.mdev->graph_mutex);
media_graph_walk_cleanup(&graph);
csi = v4l2_get_subdevdata(pipe->sd[VIN_IND_CSI]);
vinc->total_rx_ch = csi->bus_info.ch_total_num;
vinc->vid_cap.frame.fmt.mbus_type = mcfg.type;
#ifdef SUPPORT_PTN
if (vinc->ptn_cfg.ptn_en) {
csi->bus_info.bus_if = V4L2_MBUS_PARALLEL;
switch (vinc->ptn_cfg.ptn_dw) {
case 0:
csi->csi_fmt->data_width = 8;
break;
case 1:
csi->csi_fmt->data_width = 10;
break;
case 2:
csi->csi_fmt->data_width = 12;
break;
default:
csi->csi_fmt->data_width = 12;
break;
}
}
#endif
return 0;
out:
return ret;
}
static int vidioc_try_fmt_vid_cap_mplane(struct file *file, void *priv,
struct v4l2_format *f)
{
struct vin_core *vinc = video_drvdata(file);
struct v4l2_mbus_framefmt mf;
struct vin_fmt *ffmt = NULL;
ffmt = vin_find_format(&f->fmt.pix_mp.pixelformat, NULL,
VIN_FMT_ALL, -1, false);
if (ffmt == NULL) {
vin_err("vin is not support this pixelformat\n");
return -EINVAL;
}
mf.width = f->fmt.pix_mp.width;
mf.height = f->fmt.pix_mp.height;
mf.code = ffmt->mbus_code;
vin_pipeline_try_format(vinc, &mf, &ffmt, true);
f->fmt.pix_mp.width = mf.width;
f->fmt.pix_mp.height = mf.height;
f->fmt.pix_mp.colorspace = mf.colorspace;
f->fmt.pix_mp.field = V4L2_FIELD_NONE;
return 0;
}
static int __vin_set_fmt(struct vin_core *vinc, struct v4l2_format *f)
{
struct vin_vid_cap *cap = &vinc->vid_cap;
struct sensor_win_size win_cfg;
struct v4l2_mbus_framefmt mf;
struct vin_fmt *ffmt = NULL;
struct mbus_framefmt_res *res = (void *)mf.reserved;
__maybe_unused struct vin_md *vind = dev_get_drvdata(vinc->v4l2_dev->dev);
int ret = 0, i;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)
struct v4l2_subdev_state state;
#endif
struct v4l2_subdev_pad_config cfg;
struct v4l2_subdev_selection sel;
struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
if (vin_streaming(cap)) {
vin_err("%s device busy\n", __func__);
return -EBUSY;
}
ffmt = vin_find_format(&f->fmt.pix_mp.pixelformat, NULL,
VIN_FMT_ALL, -1, false);
if (ffmt == NULL) {
vin_err("vin does not support this pixelformat 0x%x\n",
f->fmt.pix_mp.pixelformat);
return -EINVAL;
}
cap->frame.fmt = *ffmt;
mf.width = f->fmt.pix_mp.width;
mf.height = f->fmt.pix_mp.height;
mf.field = f->fmt.pix_mp.field;
mf.colorspace = f->fmt.pix_mp.colorspace;
mf.code = ffmt->mbus_code;
res->res_pix_fmt = f->fmt.pix_mp.pixelformat;
ret = vin_pipeline_try_format(vinc, &mf, &ffmt, true);
if (ret < 0) {
vin_err("vin_pipeline_try_format failed\n");
return -EINVAL;
}
cap->frame.fmt.mbus_code = mf.code;
cap->frame.fmt.field = mf.field;
cap->frame.fmt.color = mf.colorspace;
f->fmt.pix_mp.colorspace = mf.colorspace;
vin_log(VIN_LOG_FMT, "pipeline try fmt %d*%d code %x field %d colorspace %d\n",
mf.width, mf.height, mf.code, mf.field, mf.colorspace);
vin_pipeline_set_mbus_config(vinc);
/* get current win configs */
memset(&win_cfg, 0, sizeof(win_cfg));
ret = v4l2_subdev_call(cap->pipe.sd[VIN_IND_SENSOR], core, ioctl,
GET_CURRENT_WIN_CFG, &win_cfg);
#ifdef CSIC_SDRAM_DFS
if (win_cfg.wdr_mode != ISP_NORMAL_MODE) {
vinc->vin_dfs.vinc_sdram_status = SDRAM_NOT_USED;
vin_warn("the sensor is wdr sensor, csic sdeam function is prohibited\n");
}
#endif
if (ret == 0) {
sunxi_isp_sensor_fps(cap->pipe.sd[VIN_IND_ISP], win_cfg.fps_fixed);
#ifdef SUPPORT_ISP_TDM
if (cap->pipe.sd[VIN_IND_TDM_RX])
sunxi_tdm_fps_clk(cap->pipe.sd[VIN_IND_TDM_RX], win_cfg.fps_fixed,
clk_get_rate(vind->clk[VIN_TOP_CLK].clock), win_cfg.vts);
#endif
vinc->vin_status.width = win_cfg.width;
vinc->vin_status.height = win_cfg.height;
vinc->vin_status.h_off = win_cfg.hoffset;
vinc->vin_status.v_off = win_cfg.voffset;
/* parser crop */
memset(&cfg, 0, sizeof(cfg));
memset(&sel, 0, sizeof(sel));
cfg.try_crop.width = win_cfg.width;
cfg.try_crop.height = win_cfg.height;
cfg.try_crop.left = win_cfg.hoffset;
cfg.try_crop.top = win_cfg.voffset;
sel.which = V4L2_SUBDEV_FORMAT_TRY;
sel.pad = cap->pipe.sd[VIN_IND_CSI]->entity.num_pads - 1;
if (cap->pipe.sd[VIN_IND_CSI]) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)
memset(&state, 0, sizeof(state));
state.pads = &cfg;
ret = v4l2_subdev_call(cap->pipe.sd[VIN_IND_CSI], pad,
set_selection, &state, &sel);
#else
ret = v4l2_subdev_call(cap->pipe.sd[VIN_IND_CSI], pad,
set_selection, &cfg, &sel);
#endif
if (ret < 0) {
vin_err("csi parser set_selection error! code = %d, %d\n", ret, -ENODEV);
goto out;
}
} else {
vin_err("csi sd is null\n");
}
/* vipp crop */
if ((win_cfg.vipp_hoff != 0) || (win_cfg.vipp_voff != 0)) {
if ((win_cfg.vipp_w + win_cfg.vipp_hoff > win_cfg.width_input) || (win_cfg.vipp_w == 0))
win_cfg.vipp_w = win_cfg.width_input - win_cfg.vipp_hoff;
if ((win_cfg.vipp_h + win_cfg.vipp_voff > win_cfg.height_input) || (win_cfg.vipp_h == 0))
win_cfg.vipp_h = win_cfg.height_input - win_cfg.vipp_voff;
sel.target = V4L2_SEL_TGT_CROP;
sel.pad = SCALER_PAD_SINK;
sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;
sel.r.width = win_cfg.vipp_w;
sel.r.height = win_cfg.vipp_h;
sel.r.left = win_cfg.vipp_hoff;
sel.r.top = win_cfg.vipp_voff;
ret = v4l2_subdev_call(cap->pipe.sd[VIN_IND_SCALER],
pad, set_selection, NULL, &sel);
if (ret < 0) {
vin_err("vipp set_selection crop error!\n");
goto out;
}
}
/* vipp shrink */
if ((win_cfg.vipp_wshrink != 0) && (win_cfg.vipp_hshrink != 0)) {
sel.target = V4L2_SEL_TGT_CROP;
sel.pad = SCALER_PAD_SINK;
sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;
sel.reserved[0] = VIPP_ONLY_SHRINK;
sel.r.width = win_cfg.vipp_wshrink;
sel.r.height = win_cfg.vipp_hshrink;
sel.r.left = 0;
sel.r.top = 0;
ret = v4l2_subdev_call(cap->pipe.sd[VIN_IND_SCALER],
pad, set_selection, NULL, &sel);
if (ret < 0) {
vin_err("vipp set_selection shrink error!\n");
goto out;
}
}
} else {
ret = 0;
vinc->vin_status.width = mf.width;
vinc->vin_status.height = mf.height;
vinc->vin_status.h_off = 0;
vinc->vin_status.v_off = 0;
vin_warn("get sensor win_cfg failed!\n");
}
if (vinc->vid_cap.frame.fmt.mbus_type == V4L2_MBUS_SUBLVDS ||
vinc->vid_cap.frame.fmt.mbus_type == V4L2_MBUS_HISPI) {
struct combo_sync_code sync;
struct combo_lane_map map;
struct combo_wdr_cfg wdr;
memset(&sync, 0, sizeof(sync));
ret = v4l2_subdev_call(cap->pipe.sd[VIN_IND_SENSOR], core,
ioctl, GET_COMBO_SYNC_CODE, &sync);
if (ret < 0) {
vin_err("get combo sync code error!\n");
goto out;
}
sunxi_combo_set_sync_code(cap->pipe.sd[VIN_IND_MIPI], &sync);
memset(&map, 0, sizeof(map));
ret = v4l2_subdev_call(cap->pipe.sd[VIN_IND_SENSOR], core,
ioctl, GET_COMBO_LANE_MAP, &map);
if (ret < 0) {
vin_err("get combo lane map error!\n");
goto out;
}
sunxi_combo_set_lane_map(cap->pipe.sd[VIN_IND_MIPI], &map);
if (res->res_combo_mode & 0xf) {
memset(&wdr, 0, sizeof(wdr));
ret = v4l2_subdev_call(cap->pipe.sd[VIN_IND_SENSOR], core,
ioctl, GET_COMBO_WDR_CFG, &wdr);
if (ret < 0) {
vin_err("get combo wdr cfg error!\n");
goto out;
}
sunxi_combo_wdr_config(cap->pipe.sd[VIN_IND_MIPI], &wdr);
}
}
cap->isp_wdr_mode = res->res_wdr_mode;
if (cap->capture_mode == V4L2_MODE_IMAGE) {
sunxi_flash_check_to_start(cap->pipe.sd[VIN_IND_FLASH],
SW_CTRL_FLASH_ON);
} else {
sunxi_flash_stop(vinc->vid_cap.pipe.sd[VIN_IND_FLASH]);
}
if ((mf.width < f->fmt.pix_mp.width) || (mf.height < f->fmt.pix_mp.height)) {
f->fmt.pix_mp.width = mf.width;
f->fmt.pix_mp.height = mf.height;
}
/* for csi dma size set */
cap->frame.offs_h = (mf.width - f->fmt.pix_mp.width) / 2;
cap->frame.offs_v = (mf.height - f->fmt.pix_mp.height) / 2;
cap->frame.o_width = f->fmt.pix_mp.width;
cap->frame.o_height = f->fmt.pix_mp.height;
pixm->colorspace = vinc->vid_cap.frame.fmt.color;
pixm->num_planes = vinc->vid_cap.frame.fmt.memplanes;
for (i = 0; i < pixm->num_planes; ++i) {
pixm->plane_fmt[i].bytesperline = f->fmt.pix_mp.width * cap->frame.fmt.depth[i] / 8;
pixm->plane_fmt[i].sizeimage = f->fmt.pix_mp.width * f->fmt.pix_mp.height * cap->frame.fmt.depth[i] / 8;
}
out:
return ret;
}
static int vidioc_s_fmt_vid_cap_mplane(struct file *file, void *priv,
struct v4l2_format *f)
{
struct vin_core *vinc = video_drvdata(file);
int ret;
__maybe_unused struct vin_core *vinc_bind = NULL;
if (vinc->dma_merge_mode == 1) {
if (vinc->id % 4 == 1 && vin_core_gbl[vinc->id - 1]) {
vinc_bind = vin_core_gbl[vinc->id - 1];
ret = __vin_set_fmt(vinc_bind, f);
if (ret < 0) {
vin_err("set fmt0 error");
return ret;
}
}
}
ret = __vin_set_fmt(vinc, f);
if (ret < 0)
vin_err("set fmt error");
return ret;
}
int vidioc_s_selection(struct file *file, void *fh,
struct v4l2_selection *s)
{
struct vin_core *vinc = video_drvdata(file);
struct v4l2_subdev_selection sel;
int ret = 0;
__maybe_unused struct vin_core *vinc_bind = NULL;
__maybe_unused struct v4l2_subdev_selection sel_bind;
if (vinc->dma_merge_mode == 1) {
sel_bind.which = V4L2_SUBDEV_FORMAT_ACTIVE;
sel_bind.pad = SCALER_PAD_SINK;
sel_bind.target = s->target;
sel_bind.flags = s->flags;
sel_bind.r = s->r;
if (vinc->id % 4 == 1 && vin_core_gbl[vinc->id - 1]) {
vinc_bind = vin_core_gbl[vinc->id - 1];
ret = v4l2_subdev_call(vinc_bind->vid_cap.pipe.sd[VIN_IND_SCALER], pad,
set_selection, NULL, &sel_bind);
if (ret < 0) {
vin_err("v4l2 sub device scaler set_selection error!\n");
return ret;
}
}
}
sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;
sel.pad = SCALER_PAD_SINK;
sel.target = s->target;
sel.flags = s->flags;
sel.r = s->r;
ret = v4l2_subdev_call(vinc->vid_cap.pipe.sd[VIN_IND_SCALER], pad,
set_selection, NULL, &sel);
if (ret < 0)
vin_err("v4l2 sub device scaler set_selection error!\n");
return ret;
}
int vidioc_g_selection(struct file *file, void *fh,
struct v4l2_selection *s)
{
struct vin_core *vinc = video_drvdata(file);
struct v4l2_subdev_selection sel;
int ret = 0;
sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;
sel.pad = SCALER_PAD_SINK;
sel.target = s->target;
sel.flags = s->flags;
ret = v4l2_subdev_call(vinc->vid_cap.pipe.sd[VIN_IND_SCALER], pad,
get_selection, NULL, &sel);
if (ret < 0)
vin_err("v4l2 sub device scaler get_selection error!\n");
else
s->r = sel.r;
return ret;
}
static int vidioc_enum_fmt_vid_overlay(struct file *file, void *__fh,
struct v4l2_fmtdesc *f)
{
struct vin_fmt *fmt;
fmt = vin_find_format(NULL, NULL, VIN_FMT_OSD, f->index, false);
if (!fmt)
return -EINVAL;
strlcpy(f->description, fmt->name, sizeof(f->description));
f->pixelformat = fmt->fourcc;
return 0;
}
static int vidioc_g_fmt_vid_overlay(struct file *file, void *__fh,
struct v4l2_format *f)
{
struct vin_core *vinc = video_drvdata(file);
f->fmt.win.w.left = 0;
f->fmt.win.w.top = 0;
f->fmt.win.w.width = vinc->vid_cap.frame.o_width;
f->fmt.win.w.height = vinc->vid_cap.frame.o_height;
f->fmt.win.clipcount = vinc->vid_cap.osd.overlay_cnt;
f->fmt.win.chromakey = vinc->vid_cap.osd.chromakey;
return 0;
}
static void __osd_win_check(struct v4l2_window *win)
{
if (win->w.width > MAX_WIDTH)
win->w.width = MAX_WIDTH;
if (win->w.width < MIN_WIDTH)
win->w.width = MIN_WIDTH;
if (win->w.height > MAX_HEIGHT)
win->w.height = MAX_HEIGHT;
if (win->w.height < MIN_HEIGHT)
win->w.height = MIN_HEIGHT;
if (win->bitmap) {
if (win->clipcount > MAX_OVERLAY_NUM)
win->clipcount = MAX_OVERLAY_NUM;
} else {
if (MAX_ORL_NUM) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)
if (win->clipcount / 2 > MAX_ORL_NUM)
win->clipcount = MAX_ORL_NUM;
else
win->clipcount /= 2;
#else
if (win->clipcount > MAX_ORL_NUM)
win->clipcount = MAX_ORL_NUM;
#endif
} else {
if (win->clipcount > MAX_COVER_NUM)
win->clipcount = MAX_COVER_NUM;
}
}
}
static int vidioc_try_fmt_vid_overlay(struct file *file, void *__fh,
struct v4l2_format *f)
{
__osd_win_check(&f->fmt.win);
return 0;
}
void __osd_bitmap2dram(struct vin_osd *osd, void *databuf)
{
#if IS_ENABLED(CONFIG_ARCH_SUN8IW12P1)
int i, j, k, m = 0, n = 0;
int kend = 0, idx = 0, ww = 0, ysn = 0;
int y_num = 0, *y_temp = NULL;
int *hor_num = NULL, *hor_index = NULL;
int *x_temp = NULL, *xbuf = NULL, *x_idx = NULL;
int addr_offset = 0, pix_size = osd->fmt->depth[0]/8;
int cnt = osd->overlay_cnt;
void *dram_addr = osd->ov_mask[osd->ov_set_cnt % 2].vir_addr;
y_temp = (int *)kzalloc(2 * cnt * sizeof(int), GFP_KERNEL);
for (i = 0; i < cnt; i++) {
y_temp[i] = osd->ov_win[i].top;
y_temp[i + cnt] = osd->ov_win[i].top + osd->ov_win[i].height - 1;
}
sort(y_temp, 2 * cnt, sizeof(int), vin_cmp, vin_swap);
y_num = vin_unique(y_temp, 2 * cnt);
hor_num = (int *)kzalloc(y_num * sizeof(int), GFP_KERNEL); /* 0~y_num-1 */
hor_index = (int *)kzalloc(y_num * cnt * sizeof(int), GFP_KERNEL); /* (0~y_num-1) * (0~N+1) */
for (j = 0; j < y_num; j++) {
ysn = 0;
for (i = 0; i < cnt; i++) {
if (osd->ov_win[i].top <= y_temp[j] &&
(osd->ov_win[i].top + osd->ov_win[i].height) > y_temp[j]) {
hor_num[j]++;
hor_index[j * cnt + ysn] = i;
ysn = ysn + 1;
}
}
}
for (j = 0; j < y_num; j++) {
x_temp = (int *)kzalloc(hor_num[j] * sizeof(int), GFP_KERNEL);
xbuf = (int *)kzalloc(hor_num[j] * sizeof(int), GFP_KERNEL);
x_idx = (int *)kzalloc(hor_num[j] * sizeof(int), GFP_KERNEL);
for (k = 0; k < hor_num[j]; k++)
x_temp[k] = osd->ov_win[hor_index[j * cnt + k]].left;
memcpy(xbuf, x_temp, hor_num[j] * sizeof(int));
sort(x_temp, hor_num[j], sizeof(int), vin_cmp, vin_swap);
for (k = 0; k < hor_num[j]; k++) {
for (m = 0; m < hor_num[j]; m++) {
if (x_temp[k] == xbuf[m]) {
x_idx[k] = m;
break;
}
}
}
if (j == y_num - 1)
kend = y_temp[j];
else
kend = y_temp[j + 1] - 1;
for (k = y_temp[j]; k <= kend; k++) {
for (i = 0; i < hor_num[j]; i++) {
idx = hor_index[j * cnt + x_idx[i]];
addr_offset = 0;
for (n = 0; n < idx; n++)
addr_offset += (osd->ov_win[n].width * osd->ov_win[n].height) * pix_size;
ww = osd->ov_win[idx].width;
if (k < (osd->ov_win[idx].top + osd->ov_win[idx].height)) {
memcpy(dram_addr, databuf + addr_offset
+ ww * (k - osd->ov_win[idx].top) * pix_size,
ww * pix_size);
dram_addr += ww * pix_size;
}
}
}
kfree(x_temp);
kfree(xbuf);
kfree(x_idx);
x_temp = NULL;
xbuf = NULL;
x_idx = NULL;
}
kfree(hor_num);
kfree(hor_index);
kfree(y_temp);
y_temp = NULL;
hor_index = NULL;
hor_num = NULL;
#else
memcpy(osd->ov_mask[osd->ov_set_cnt % 2].vir_addr, databuf, osd->ov_mask[osd->ov_set_cnt % 2].size);
#endif
}
static void __osd_rgb_to_yuv(u8 r, u8 g, u8 b, u8 *y, u8 *u, u8 *v)
{
int jc0 = 0x00000132;
int jc1 = 0x00000259;
int jc2 = 0x00000075;
int jc3 = 0xffffff53;
int jc4 = 0xfffffead;
int jc5 = 0x00000200;
int jc6 = 0x00000200;
int jc7 = 0xfffffe53;
int jc8 = 0xffffffad;
int jc9 = 0x00000000;
int jc10 = 0x00000080;
int jc11 = 0x00000080;
u32 y_tmp, u_tmp, v_tmp;
y_tmp = (((jc0 * r >> 6) + (jc1 * g >> 6) + (jc2 * b >> 6)) >> 4) + jc9;
*y = clamp_val(y_tmp, 0, 255);
u_tmp = (((jc3 * r >> 6) + (jc4 * g >> 6) + (jc5 * b >> 6)) >> 4) + jc10;
*u = clamp_val(u_tmp, 0, 255);
v_tmp = (((jc6 * r >> 6) + (jc7 * g >> 6) + (jc8 * b >> 6)) >> 4) + jc11;
*v = clamp_val(v_tmp, 0, 255);
}
static void __osd_bmp_to_yuv(struct vin_osd *osd, void *databuf)
{
#if IS_ENABLED(CONFIG_ARCH_SUN8IW12P1)
u8 alpha, r, g, b, y, u, v;
int i, j, y_sum, bmp;
for (i = 0; i < osd->overlay_cnt; i++) {
int bmp_size = osd->ov_win[i].height * osd->ov_win[i].width;
int valid_pix = 1;
y_sum = 0;
for (j = 0; j < bmp_size; j++) {
switch (osd->overlay_fmt) {
case 0:
bmp = *(short *)databuf;
alpha = (int)((bmp >> 15) & 0x01) * 100;
r = (bmp >> 10) & 0x1f;
r = (r << 3) + (r >> 2);
g = (bmp >> 5) & 0x1f;
g = (g << 3) + (g >> 2);
b = bmp & 0x1f;
b = (b << 3) + (b >> 2);
databuf += 2;
break;
case 1:
bmp = *(short *)databuf;
alpha = (int)((bmp >> 12) & 0x0f) * 100 / 15;
r = (bmp >> 8) & 0x0f;
r = (r << 4) + r;
g = (bmp >> 4) & 0x0f;
g = (g << 4) + g;
b = bmp & 0x0f;
b = (b << 4) + b;
databuf += 2;
break;
case 2:
bmp = *(int *)databuf;
alpha = (int)((bmp >> 24) & 0xff) * 100 / 255;
r = (bmp >> 16) & 0xff;
g = (bmp >> 8) & 0xff;
b = bmp & 0xff;
databuf += 4;
break;
default:
bmp = *(int *)databuf;
alpha = (int)((bmp >> 24) & 0xff) * 100 / 255;
r = (bmp >> 16) & 0xff;
g = (bmp >> 8) & 0xff;
b = bmp & 0xff;
databuf += 4;
break;
}
if (alpha >= 80) {
__osd_rgb_to_yuv(r, g, b, &y, &u, &v);
y_sum += y;
valid_pix++;
}
}
osd->y_bmp_avp[i] = y_sum / valid_pix;
}
#endif
}
static int vidioc_s_fmt_vid_overlay(struct file *file, void *__fh,
struct v4l2_format *f)
{
struct vin_core *vinc = video_drvdata(file);
struct vin_osd *osd = &vinc->vid_cap.osd;
struct v4l2_clip *clip = NULL;
void *bitmap = NULL;
unsigned int bitmap_size = 0, pix_size = 0;
int ret = 0, i = 0;
__osd_win_check(&f->fmt.win);
osd->chromakey = f->fmt.win.chromakey;
if (f->fmt.win.bitmap) {
if (f->fmt.win.clipcount <= 0) {
osd->overlay_en = 0;
goto osd_reset;
} else {
if (MAX_OVERLAY_NUM) {
osd->overlay_en = 1;
osd->overlay_cnt = f->fmt.win.clipcount;
} else {
osd->overlay_en = 0;
vin_err("VIPP overlay is not exist!!\n");
goto osd_reset;
}
}
clip = vmalloc(sizeof(*clip) * osd->overlay_cnt * 2);
if (clip == NULL) {
vin_err("%s - Alloc of clip mask failed\n", __func__);
return -ENOMEM;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)
memcpy(clip, f->fmt.win.clips, sizeof(*clip) * f->fmt.win.clipcount * 2);
#else
if (copy_from_user(clip, f->fmt.win.clips,
sizeof(*clip) * osd->overlay_cnt * 2)) {
vfree(clip);
return -EFAULT;
}
#endif
/* save global alpha in the win top for diff overlay */
for (i = 0; i < osd->overlay_cnt; i++) {
osd->ov_win[i] = clip[i].c;
bitmap_size += clip[i].c.width * clip[i].c.height;
if (f->fmt.win.global_alpha == 255)
osd->global_alpha[i] = clamp_val(clip[i + osd->overlay_cnt].c.top, 0, 16);
else
osd->global_alpha[i] = clamp_val(f->fmt.win.global_alpha, 0, 16);
osd->inverse_close[i] = clip[i + osd->overlay_cnt].c.left & 0xff;
osd->inv_th = (clip[i + osd->overlay_cnt].c.left >> 8) & 0xff;
osd->inv_w_rgn[i] = clamp_val(clip[i + osd->overlay_cnt].c.width, 0, 15);
osd->inv_h_rgn[i] = clamp_val(clip[i + osd->overlay_cnt].c.height, 0, 15);
}
vfree(clip);
osd->fmt = vin_find_format(&f->fmt.win.chromakey, NULL,
VIN_FMT_OSD, -1, false);
if (osd->fmt == NULL) {
vin_err("osd is not support this chromakey\n");
return -EINVAL;
}
pix_size = osd->fmt->depth[0]/8;
bitmap = vmalloc(bitmap_size * pix_size);
if (bitmap == NULL) {
vin_err("%s - Alloc of bitmap buf failed\n", __func__);
return -ENOMEM;
}
if (copy_from_user(bitmap, f->fmt.win.bitmap,
bitmap_size * pix_size)) {
vfree(bitmap);
return -EFAULT;
}
osd->ov_set_cnt++;
if (osd->ov_mask[osd->ov_set_cnt % 2].size != bitmap_size * pix_size) {
if (osd->ov_mask[osd->ov_set_cnt % 2].phy_addr) {
os_mem_free(&vinc->pdev->dev, &osd->ov_mask[osd->ov_set_cnt % 2]);
osd->ov_mask[osd->ov_set_cnt % 2].phy_addr = NULL;
}
osd->ov_mask[osd->ov_set_cnt % 2].size = bitmap_size * pix_size;
ret = os_mem_alloc(&vinc->pdev->dev, &osd->ov_mask[osd->ov_set_cnt % 2]);
if (ret < 0) {
vin_err("osd bitmap load addr requset failed!\n");
vfree(bitmap);
return -ENOMEM;
}
}
memset(osd->ov_mask[osd->ov_set_cnt % 2].vir_addr, 0, bitmap_size * pix_size);
__osd_bitmap2dram(osd, bitmap);
switch (osd->chromakey) {
case V4L2_PIX_FMT_RGB555:
osd->overlay_fmt = ARGB1555;
break;
case V4L2_PIX_FMT_RGB444:
osd->overlay_fmt = ARGB4444;
break;
case V4L2_PIX_FMT_RGB32:
osd->overlay_fmt = ARGB8888;
break;
default:
osd->overlay_fmt = ARGB8888;
break;
}
__osd_bmp_to_yuv(osd, bitmap);
vfree(bitmap);
} else {
if (f->fmt.win.clipcount <= 0) {
osd->cover_en = 0;
osd->orl_en = 0;
goto osd_reset;
}
clip = vmalloc(sizeof(*clip) * f->fmt.win.clipcount * 2);
if (clip == NULL) {
vin_err("%s - Alloc of clip mask failed\n", __func__);
return -ENOMEM;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0)
memcpy(clip, f->fmt.win.clips, sizeof(*clip) * f->fmt.win.clipcount * 2);
#else
if (copy_from_user(clip, f->fmt.win.clips,
sizeof(*clip) * f->fmt.win.clipcount * 2)) {
vfree(clip);
return -EFAULT;
}
#endif
/* save rgb in the win top for diff cover */
osd->orl_width = clip[f->fmt.win.clipcount].c.width;
if (osd->orl_width) {
if (MAX_ORL_NUM) {
osd->orl_en = 1;
osd->orl_cnt = f->fmt.win.clipcount;
} else {
osd->orl_en = 0;
vin_err("VIPP orl is not exist!!\n");
goto osd_reset;
}
} else {
if (MAX_COVER_NUM) {
osd->cover_en = 1;
osd->cover_cnt = f->fmt.win.clipcount;
} else {
osd->cover_en = 0;
vin_err("VIPP cover is not exist!!\n");
goto osd_reset;
}
}
if (osd->orl_en) {
for (i = 0; i < osd->orl_cnt; i++) {
u8 r, g, b;
osd->orl_win[i] = clip[i].c;
osd->rgb_orl[i] = clip[i + osd->orl_cnt].c.top;
r = (osd->rgb_orl[i] >> 16) & 0xff;
g = (osd->rgb_orl[i] >> 8) & 0xff;
b = osd->rgb_orl[i] & 0xff;
__osd_rgb_to_yuv(r, g, b, &osd->yuv_orl[0][i],
&osd->yuv_orl[1][i], &osd->yuv_orl[2][i]);
}
}
if (osd->cover_en) {
for (i = 0; i < osd->cover_cnt; i++) {
u8 r, g, b;
osd->cv_win[i] = clip[i].c;
osd->rgb_cover[i] = clip[i + osd->cover_cnt].c.top;
r = (osd->rgb_cover[i] >> 16) & 0xff;
g = (osd->rgb_cover[i] >> 8) & 0xff;
b = osd->rgb_cover[i] & 0xff;
__osd_rgb_to_yuv(r, g, b, &osd->yuv_cover[0][i],
&osd->yuv_cover[1][i], &osd->yuv_cover[2][i]);
}
}
vfree(clip);
}
osd_reset:
osd->is_set = 0;
return ret;
}
static int __osd_reg_setup(struct vin_core *vinc, struct vin_osd *osd)
{
struct vipp_osd_config *osd_cfg = NULL;
struct vipp_osd_para_config *para = NULL;
struct vipp_rgb2yuv_factor rgb2yuv_def = {
.jc0 = 0x00000132,
.jc1 = 0x00000259,
.jc2 = 0x00000075,
.jc3 = 0xffffff53,
.jc4 = 0xfffffead,
.jc5 = 0x00000200,
.jc6 = 0x00000200,
.jc7 = 0xfffffe53,
.jc8 = 0xffffffad,
.jc9 = 0x00000000,
.jc10 = 0x00000080,
.jc11 = 0x00000080,
};
struct scaler_dev *scaler = container_of(vinc->vid_cap.pipe.sd[VIN_IND_SCALER], struct scaler_dev, subdev);
int id = vinc->vipp_sel;
int i;
int act_width;
osd_cfg = kzalloc(sizeof(*osd_cfg), GFP_KERNEL);
if (osd_cfg == NULL) {
vin_err("%s - Alloc of osd_cfg failed\n", __func__);
return -ENOMEM;
}
para = kzalloc(sizeof(*para), GFP_KERNEL);
if (para == NULL) {
vin_err("%s - Alloc of osd_para failed\n", __func__);
kfree(osd_cfg);
return -ENOMEM;
}
if (osd->overlay_en == 1) {
osd_cfg->osd_argb_mode = osd->overlay_fmt;
osd_cfg->osd_ov_num = osd->overlay_cnt - 1;
osd_cfg->osd_ov_en = 1;
osd_cfg->osd_stat_en = 1;
for (i = 0; i < osd->overlay_cnt; i++) {
if (vinc->hflip)
para->overlay_cfg[i].h_start = vinc->vid_cap.frame.o_width - osd->ov_win[i].width - osd->ov_win[i].left;
else
para->overlay_cfg[i].h_start = osd->ov_win[i].left;
para->overlay_cfg[i].h_end = para->overlay_cfg[i].h_start + osd->ov_win[i].width - 1;
if (vinc->vflip)
para->overlay_cfg[i].v_start = vinc->vid_cap.frame.o_height - osd->ov_win[i].height - osd->ov_win[i].top;
else
para->overlay_cfg[i].v_start = osd->ov_win[i].top;
para->overlay_cfg[i].v_end = para->overlay_cfg[i].v_start + osd->ov_win[i].height - 1;
para->overlay_cfg[i].alpha = osd->global_alpha[i];
para->overlay_cfg[i].inv_en = !osd->inverse_close[i];
para->overlay_cfg[i].inv_th = osd->inv_th;
para->overlay_cfg[i].inv_w_rgn = osd->inv_w_rgn[i];
para->overlay_cfg[i].inv_h_rgn = osd->inv_h_rgn[i];
}
vipp_set_osd_bm_load_addr(id, (unsigned long)osd->ov_mask[osd->ov_set_cnt % 2].dma_addr);
} else {
osd_cfg->osd_ov_num = -1;
}
if (osd->cover_en == 1) {
osd_cfg->osd_cv_num = osd->cover_cnt - 1;
osd_cfg->osd_cv_en = 1;
for (i = 0; i < osd->cover_cnt; i++) {
if (vinc->hflip)
para->cover_cfg[i].h_start = vinc->vid_cap.frame.o_width - osd->cv_win[i].width - osd->cv_win[i].left;
else
para->cover_cfg[i].h_start = osd->cv_win[i].left;
para->cover_cfg[i].h_end = para->cover_cfg[i].h_start + osd->cv_win[i].width - 1;
if (vinc->vflip)
para->cover_cfg[i].v_start = vinc->vid_cap.frame.o_height - osd->cv_win[i].height - osd->cv_win[i].top;
else
para->cover_cfg[i].v_start = osd->cv_win[i].top;
para->cover_cfg[i].v_end = para->cover_cfg[i].v_start + osd->cv_win[i].height - 1;
para->cover_data[i].y = osd->yuv_cover[0][i];
para->cover_data[i].u = osd->yuv_cover[1][i];
para->cover_data[i].v = osd->yuv_cover[2][i];
}
} else {
osd_cfg->osd_cv_num = -1;
}
if (osd->orl_en == 1) {
osd_cfg->osd_orl_num = osd->orl_cnt - 1;
osd_cfg->osd_orl_en = 1;
osd_cfg->osd_orl_width = osd->orl_width;
act_width = 2 * (osd_cfg->osd_orl_width + 1);
for (i = 0; i < osd->orl_cnt; i++) {
if (osd->orl_win[i].height < 2 * act_width)
osd->orl_win[i].height = 2 * act_width;
if (osd->orl_win[i].width < 2 * act_width)
osd->orl_win[i].width = 2 * act_width;
if (vinc->hflip)
para->orl_cfg[i].h_start = vinc->vid_cap.frame.o_width - osd->orl_win[i].width - osd->orl_win[i].left;
else
para->orl_cfg[i].h_start = osd->orl_win[i].left;
para->orl_cfg[i].h_end = para->orl_cfg[i].h_start + osd->orl_win[i].width - 1;
if (vinc->vflip)
para->orl_cfg[i].v_start = vinc->vid_cap.frame.o_height - osd->orl_win[i].height - osd->orl_win[i].top;
else
para->orl_cfg[i].v_start = osd->orl_win[i].top;
para->orl_cfg[i].v_end = para->orl_cfg[i].v_start + osd->orl_win[i].height - 1;
para->orl_data[i].y = osd->yuv_orl[0][i];
para->orl_data[i].u = osd->yuv_orl[1][i];
para->orl_data[i].v = osd->yuv_orl[2][i];
}
} else {
osd_cfg->osd_orl_num = -1;
}
if (!scaler->noneed_register) {
vipp_osd_cfg(id, osd_cfg);
vipp_osd_rgb2yuv(id, &rgb2yuv_def);
vipp_osd_para_cfg(id, para, osd_cfg);
vipp_osd_hvflip(id, vinc->hflip, vinc->vflip);
#if defined VIPP_200
vipp_clear_status(id,
CHN0_REG_LOAD_PD << (vipp_virtual_find_ch[id] * VIPP_CHN_INT_AMONG_OFFSET));
vipp_irq_enable(id,
CHN0_REG_LOAD_EN << (vipp_virtual_find_ch[id] * VIPP_CHN_INT_AMONG_OFFSET));
#endif
}
kfree(osd_cfg);
kfree(para);
return 0;
}
static int vidioc_overlay(struct file *file, void *__fh, unsigned int on)
{
struct vin_core *vinc = video_drvdata(file);
struct vin_osd *osd = &vinc->vid_cap.osd;
int i;
int ret = 0;
if (!on) {
for (i = 0; i < 2; i++) {
if (osd->ov_mask[i].phy_addr) {
os_mem_free(&vinc->pdev->dev, &osd->ov_mask[i]);
osd->ov_mask[i].phy_addr = NULL;
osd->ov_mask[i].size = 0;
}
}
osd->ov_set_cnt = 0;
osd->overlay_en = 0;
osd->cover_en = 0;
osd->orl_en = 0;
} else {
if (osd->is_set)
return ret;
}
ret = __osd_reg_setup(vinc, osd);
osd->is_set = 1;
return ret;
}
int vin_timer_init(struct vin_core *vinc)
{
return 0;
}
void vin_timer_del(struct vin_core *vinc)
{
}
void vin_timer_update(struct vin_core *vinc, int ms)
{
}
static int __vin_sensor_setup_link(struct vin_core *vinc, struct modules_config *module,
int i, int en)
{
struct vin_md *vind = dev_get_drvdata(vinc->v4l2_dev->dev);
struct v4l2_subdev *sensor = module->modules.sensor[i].sd;
struct v4l2_subdev *subdev;
struct media_entity *entity = NULL;
struct media_link *link = NULL;
__maybe_unused struct sensor_info *info = to_state(sensor);
int ret;
if (sensor == NULL)
return -1;
if (vinc->mipi_sel != 0xff)
subdev = vind->mipi[vinc->mipi_sel].sd;
else
subdev = vind->csi[vinc->csi_sel].sd;
entity = &sensor->entity;
list_for_each_entry(link, &entity->links, list) {
if (link->source->entity == entity && link->sink->entity == &subdev->entity)
break;
}
if (link == NULL)
return -1;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
if (info->stream_count >= 1)
return 0;
#else
if (sensor->entity.stream_count >= 1)
return 0;
#endif
vin_log(VIN_LOG_VIDEO, "setup link: [%s] %c> [%s]\n",
sensor->name, en ? '=' : '-', link->sink->entity->name);
if (en)
ret = media_entity_setup_link(link, MEDIA_LNK_FL_ENABLED);
else
ret = __media_entity_setup_link(link, 0);
/* When the this function is called by the close
function, the mutex conflicts with the close mutex,
so the function without the mutex is used. */
if (ret) {
vin_warn("%s setup link %s fail!\n", sensor->name,
link->sink->entity->name);
return -1;
}
return 0;
}
static int __csi_isp_setup_link(struct vin_core *vinc, int en)
{
#ifndef SUPPORT_ISP_TDM
struct vin_md *vind = dev_get_drvdata(vinc->v4l2_dev->dev);
struct v4l2_subdev *csi, *isp;
struct media_link *link = NULL;
__maybe_unused struct csi_dev *csi_dev = NULL;
int ret;
/* CSI */
if (vinc->csi_sel == 0xff)
csi = NULL;
else
csi = vind->csi[vinc->csi_sel].sd;
/* ISP */
if (vinc->isp_sel == 0xff)
isp = NULL;
else
isp = vind->isp[vinc->isp_sel].sd;
if (csi && isp) {
link = media_entity_find_link(&csi->entity.pads[CSI_PAD_SOURCE],
&isp->entity.pads[ISP_PAD_SINK]);
}
if (link == NULL) {
vin_err("%s:media_entity_find_link null\n", __func__);
return -1;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
csi_dev = v4l2_get_subdevdata(csi);
if (csi_dev->stream_count >= 1)
return 0;
#else
if (csi->entity.stream_count >= 1)
return 0;
#endif
vin_log(VIN_LOG_MD, "link: source %s sink %s\n",
link->source->entity->name,
link->sink->entity->name);
if (en)
ret = __media_entity_setup_link(link, MEDIA_LNK_FL_ENABLED);
else
ret = __media_entity_setup_link(link, 0);
/* When the this function is called by the close
function, the mutex conflicts with the close mutex,
so the function without the mutex is used. */
if (ret) {
vin_warn("%s setup link %s fail!\n", link->source->entity->name,
link->sink->entity->name);
return -1;
}
#else
struct vin_md *vind = dev_get_drvdata(vinc->v4l2_dev->dev);
struct v4l2_subdev *csi, *tdm_rx;
struct media_link *link = NULL;
__maybe_unused struct tdm_rx_dev *tdm_dev = NULL;
int ret;
/*CSI*/
if (vinc->csi_sel == 0xff)
csi = NULL;
else
csi = vind->csi[vinc->csi_sel].sd;
/*TDM_RX*/
if (vinc->tdm_rx_sel == 0xff)
tdm_rx = NULL;
else
tdm_rx = vind->tdm[vinc->tdm_rx_sel/TDM_RX_NUM].tdm_rx[vinc->tdm_rx_sel].sd;
if (csi && tdm_rx)
link = media_entity_find_link(&csi->entity.pads[CSI_PAD_SOURCE],
&tdm_rx->entity.pads[ISP_PAD_SINK]);
if (link == NULL) {
vin_err("%s:media_entity_find_link null\n", __func__);
return -1;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
tdm_dev = v4l2_get_subdevdata(tdm_rx);
if (tdm_dev->stream_count >= 1)
return 0;
#else
if (tdm_rx->entity.stream_count >= 1)
return 0;
#endif
vin_log(VIN_LOG_MD, "setup link: [%s] %c> [%s]\n",
link->source->entity->name, en ? '=' : '-',
link->sink->entity->name);
if (en)
ret = __media_entity_setup_link(link, MEDIA_LNK_FL_ENABLED);
else
ret = __media_entity_setup_link(link, 0);
/* When the this function is called by the close
function, the mutex conflicts with the close mutex,
so the function without the mutex is used. */
if (ret) {
vin_warn("%s setup link %s fail!\n", link->source->entity->name,
link->sink->entity->name);
return -1;
}
#endif
return 0;
}
static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct vin_core *vinc = video_drvdata(file);
struct vin_vid_cap *cap = &vinc->vid_cap;
struct vin_md *vind = dev_get_drvdata(vinc->v4l2_dev->dev);
struct modules_config *module = &vind->modules[vinc->sensor_sel];
int valid_idx = module->sensors.valid_idx;
int ret = 0;
__maybe_unused struct vin_core *vinc_bind = NULL;
if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
ret = -EINVAL;
goto streamon_error;
}
if (vin_streaming(cap)) {
vin_err("video%d has already stream on\n", vinc->id);
ret = -1;
goto streamon_error;
}
ret = vb2_ioctl_streamon(file, priv, i);
if (ret)
goto streamon_error;
if (vinc->large_image == 1) {
#ifdef SUPPORT_PTN
vinc->ptn_cfg.ptn_w = cap->frame.o_width;
vinc->ptn_cfg.ptn_h = cap->frame.o_height;
vinc->ptn_cfg.ptn_mode = 12;
vinc->ptn_cfg.ptn_buf.size = cap->buf_byte_size;
os_mem_alloc(&vinc->pdev->dev, &vinc->ptn_cfg.ptn_buf);
if (vinc->ptn_cfg.ptn_buf.vir_addr == NULL) {
vin_err("ptn buffer 0x%x alloc failed!\n", cap->buf_byte_size);
return -ENOMEM;
}
csic_dma_buffer_address(vinc->vipp_sel, CSI_BUF_0_A, (unsigned long)vinc->ptn_cfg.ptn_buf.dma_addr);
#endif
}
#if VIN_FALSE
schedule_work(&vinc->vid_cap.s_stream_task);
#else
if (vin_lpm(cap)) {
if (__vin_sensor_setup_link(vinc, module, valid_idx, 1) < 0) {
vin_err("sensor setup link failed\n");
return -EINVAL;
}
if (__csi_isp_setup_link(vinc, 1) < 0) {
vin_err("csi&isp setup link failed\n");
return -EINVAL;
}
clear_bit(VIN_LPM, &cap->state);
}
mutex_lock(&cap->vdev.entity.graph_obj.mdev->graph_mutex);
vin_timer_init(cap->vinc);
if (vinc->dma_merge_mode == 1) {
vin_set_next_buf_addr(cap->vinc);
if (vinc->id % 4 == 1 && vin_core_gbl[vinc->id - 1]) {
vinc_bind = vin_core_gbl[vinc->id - 1];
ret = vin_pipeline_call(vinc_bind, set_stream, &vinc_bind->vid_cap.pipe, vinc_bind->stream_idx);
if (ret < 0)
vin_err("video%d %s error!\n", vinc_bind->id, __func__);
set_bit(VIN_STREAM, &vinc_bind->vid_cap.state);
}
}
ret = vin_pipeline_call(cap->vinc, set_stream, &cap->pipe, cap->vinc->stream_idx);
if (ret < 0)
vin_err("video%d %s error!\n", vinc->id, __func__);
set_bit(VIN_STREAM, &cap->state);
/* set saved exp and gain for reopen, you can call the api in sensor_reg_init */
/*
if (cap->vinc->exp_gain.exp_val && cap->vinc->exp_gain.gain_val) {
v4l2_subdev_call(cap->pipe.sd[VIN_IND_SENSOR], core, ioctl,
VIDIOC_VIN_SENSOR_EXP_GAIN, &cap->vinc->exp_gain);
}
*/
mutex_unlock(&cap->vdev.entity.graph_obj.mdev->graph_mutex);
#if VIN_FALSE
#if IS_ENABLED(CONFIG_ISP_SERVER_MELIS)
isp = container_of(cap->pipe.sd[VIN_IND_ISP], struct isp_dev, subdev);
if (isp->gtm_type == 4) {
if ((vinc->id == 0) && (!check_ldci_video_relate(vinc->id, LDCI0_VIDEO_CHN))) {
enable_ldci_video(LDCI0_VIDEO_CHN);
} else if ((vinc->id == 1) && (!check_ldci_video_relate(vinc->id, LDCI1_VIDEO_CHN))) {
enable_ldci_video(LDCI1_VIDEO_CHN);
}
}
#endif
#endif
#endif
streamon_error:
return ret;
}
static void vin_queue_free(struct file *file)
{
struct video_device *vdev = video_devdata(file);
if (file->private_data == vdev->queue->owner) {
vb2_queue_release(vdev->queue);
vdev->queue->owner = NULL;
}
}
static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
struct vin_core *vinc = video_drvdata(file);
struct vin_md *vind = dev_get_drvdata(vinc->v4l2_dev->dev);
struct vin_vid_cap *cap = &vinc->vid_cap;
struct modules_config *module = &vind->modules[vinc->sensor_sel];
int valid_idx = module->sensors.valid_idx;
int ret = 0;
__maybe_unused struct vin_core *vinc_bind = NULL;
if (!vin_streaming(cap)) {
vin_err("video%d has already stream off\n", vinc->id);
goto streamoff_error;
}
vin_timer_del(vinc);
#if VIN_FALSE
#if IS_ENABLED(CONFIG_ISP_SERVER_MELIS)
isp = container_of(cap->pipe.sd[VIN_IND_ISP], struct isp_dev, subdev);
if (isp->gtm_type == 4) {
if ((vinc->id == 0) && (!check_ldci_video_relate(vinc->id, LDCI0_VIDEO_CHN))) {
disable_ldci_video(LDCI0_VIDEO_CHN);
} else if ((vinc->id == 1) && (!check_ldci_video_relate(vinc->id, LDCI1_VIDEO_CHN))) {
disable_ldci_video(LDCI1_VIDEO_CHN);
}
}
#endif
#endif
mutex_lock(&cap->vdev.entity.graph_obj.mdev->graph_mutex);
clear_bit(VIN_STREAM, &cap->state);
vin_pipeline_call(vinc, set_stream, &cap->pipe, 0);
if (vinc->dma_merge_mode == 1) {
if (vinc->id % 4 == 1 && vin_core_gbl[vinc->id - 1]) {
vinc_bind = vin_core_gbl[vinc->id - 1];
clear_bit(VIN_STREAM, &vinc_bind->vid_cap.state);
vin_pipeline_call(vinc_bind, set_stream, &vinc_bind->vid_cap.pipe, 0);
__csi_isp_setup_link(vinc_bind, 0);
__vin_sensor_setup_link(vinc_bind, module, valid_idx, 0);
}
}
set_bit(VIN_LPM, &cap->state);
__csi_isp_setup_link(vinc, 0);
__vin_sensor_setup_link(vinc, module, valid_idx, 0);
mutex_unlock(&cap->vdev.entity.graph_obj.mdev->graph_mutex);
if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
ret = -EINVAL;
goto streamoff_error;
}
ret = vb2_ioctl_streamoff(file, priv, i);
if (ret != 0) {
vin_err("video%d stream off error!\n", vinc->id);
goto streamoff_error;
}
vin_queue_free(file);
streamoff_error:
return ret;
}
static int vidioc_enum_input(struct file *file, void *priv,
struct v4l2_input *inp)
{
if (inp->index != 0)
return -EINVAL;
inp->type = V4L2_INPUT_TYPE_CAMERA;
inp->std = V4L2_STD_UNKNOWN;
strcpy(inp->name, "sunxi-vin");
return 0;
}
static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
{
struct vin_core *vinc = video_drvdata(file);
if (vinc->sensor_sel == vinc->rear_sensor)
*i = 0;
else
*i = 1;
return 0;
}
static int __vin_actuator_set_power(struct v4l2_subdev *sd, int on)
{
int *use_count;
int ret;
if (sd == NULL)
return -ENXIO;
use_count = &sd->entity.use_count;
if (on && (*use_count)++ > 0)
return 0;
else if (!on && (*use_count == 0 || --(*use_count) > 0))
return 0;
ret = v4l2_subdev_call(sd, core, ioctl, ACT_SOFT_PWDN, 0);
return ret != -ENOIOCTLCMD ? ret : 0;
}
static int __vin_s_input(struct vin_core *vinc, unsigned int i)
{
struct vin_md *vind = dev_get_drvdata(vinc->v4l2_dev->dev);
struct vin_vid_cap *cap = &vinc->vid_cap;
struct modules_config *module = NULL;
struct sensor_instance *inst = NULL;
struct sensor_info *info = NULL;
struct mipi_dev *mipi = NULL;
int valid_idx = -1;
int ret;
i = i > 1 ? 0 : i;
if (i == 0)
vinc->sensor_sel = vinc->rear_sensor;
else
vinc->sensor_sel = vinc->front_sensor;
module = &vind->modules[vinc->sensor_sel];
valid_idx = module->sensors.valid_idx;
if (valid_idx == NO_VALID_SENSOR) {
vin_err("there is no valid sensor\n");
return -EINVAL;
}
if (__vin_sensor_setup_link(vinc, module, valid_idx, 1) < 0) {
vin_err("sensor setup link failed\n");
return -EINVAL;
}
if (__csi_isp_setup_link(vinc, 1) < 0) {
vin_err("csi&isp setup link failed\n");
return -EINVAL;
}
inst = &module->sensors.inst[valid_idx];
sunxi_isp_sensor_type(cap->pipe.sd[VIN_IND_ISP], inst->is_isp_used);
vinc->support_raw = inst->is_isp_used;
mutex_lock(&cap->vdev.entity.graph_obj.mdev->graph_mutex);
ret = vin_pipeline_call(vinc, open, &cap->pipe, &cap->vdev.entity, true);
if (ret < 0) {
vin_err("vin pipeline open failed (%d)!\n", ret);
return ret;
}
mutex_unlock(&cap->vdev.entity.graph_obj.mdev->graph_mutex);
if (module->modules.act[valid_idx].sd != NULL) {
cap->pipe.sd[VIN_IND_ACTUATOR] = module->modules.act[valid_idx].sd;
ret = __vin_actuator_set_power(cap->pipe.sd[VIN_IND_ACTUATOR], 1);
if (ret < 0) {
vin_err("actutor power off failed (%d)!\n", ret);
return ret;
}
}
if (module->modules.flash.sd != NULL)
cap->pipe.sd[VIN_IND_FLASH] = module->modules.flash.sd;
ret = v4l2_subdev_call(cap->pipe.sd[VIN_IND_ISP], core, init, 1);
if (ret < 0) {
vin_err("ISP init error at %s\n", __func__);
return ret;
}
ret = v4l2_subdev_call(cap->pipe.sd[VIN_IND_SCALER], core, init, 1);
if (ret < 0) {
vin_err("SCALER init error at %s\n", __func__);
return ret;
}
/* save exp and gain for reopen, sensor init may reset gain to 0, so save before init! */
info = container_of(cap->pipe.sd[VIN_IND_SENSOR], struct sensor_info, sd);
if (info) {
vinc->exp_gain.exp_val = info->exp;
vinc->exp_gain.gain_val = info->gain;
vinc->stream_idx = info->stream_seq + 2;
#ifdef CSIC_SDRAM_DFS
vinc->vin_dfs.stable_frame_cnt = info->stable_frame_cnt;
vinc->vin_dfs.sensor_status = info->sdram_dfs_flag;
#endif
}
#ifdef CSIC_SDRAM_DFS
if (vinc->vin_dfs.sensor_status == SENSOR_NOT_DEBUG)
vin_warn("the sensor is not debugged. Please debug it before using the sdram dfs function\n");
if (vin_core_gbl[dma_virtual_find_logic[vinc->id]]->work_mode == BK_ONLINE)
vinc->vin_dfs.vinc_sdram_status = SDRAM_CAN_ENABLE;
else {
vinc->vin_dfs.vinc_sdram_status = SDRAM_NOT_USED;
vin_warn("video%d works in online mode, csic sdeam function is prohibited\n", vinc->id);
}
#endif
if (cap->pipe.sd[VIN_IND_MIPI] != NULL) {
mipi = container_of(cap->pipe.sd[VIN_IND_MIPI], struct mipi_dev, subdev);
if (mipi)
mipi->sensor_flags = vinc->sensor_sel;
}
if (!vinc->ptn_cfg.ptn_en) {
ret = v4l2_subdev_call(cap->pipe.sd[VIN_IND_SENSOR], core, init, 1);
if (ret) {
vin_err("sensor initial error when selecting target device!\n");
return ret;
}
}
clear_bit(VIN_LPM, &cap->state);
/* setup the current ctrl value */
/*
v4l2_ctrl_handler_setup(&vinc->vid_cap.ctrl_handler);
v4l2_ctrl_handler_setup(cap->pipe.sd[VIN_IND_SENSOR]->ctrl_handler);
*/
vinc->hflip = inst->hflip;
vinc->vflip = inst->vflip;
return ret;
}
static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
{
struct vin_core *vinc = video_drvdata(file);
int ret;
__maybe_unused struct vin_core *vinc_bind = NULL;
if (vinc->dma_merge_mode == 1) {
if (vinc->id % 4 == 1 && vin_core_gbl[vinc->id - 1]) {
vinc_bind = vin_core_gbl[vinc->id - 1];
ret = __vin_s_input(vinc_bind, i);
if (ret < 0)
return ret;
}
}
ret = __vin_s_input(vinc, i);
if (ret < 0)
return ret;
return 0;
}
static const char *const sensor_info_type[] = {
"YUV",
"RAW",
NULL,
};
static int vidioc_g_pixelaspect(struct file *file, void *fh,
int buf_type, struct v4l2_fract *fract)
{
struct vin_core *vinc = video_drvdata(file);
struct v4l2_fract sensor_fract;
int ret = 0;
ret = v4l2_subdev_call(vinc->vid_cap.pipe.sd[VIN_IND_SENSOR], video,
g_pixelaspect, &sensor_fract);
if (ret < 0) {
vin_warn("sensor g_pixelaspect fail!\n");
return ret;
}
fract->numerator = sensor_fract.numerator;
fract->denominator = sensor_fract.denominator;
return 0;
}
static int vidioc_g_parm(struct file *file, void *priv,
struct v4l2_streamparm *parms)
{
struct vin_core *vinc = video_drvdata(file);
struct vin_vid_cap *cap = &vinc->vid_cap;
int ret;
ret = sensor_g_parm(cap->pipe.sd[VIN_IND_SENSOR], parms);
if (ret < 0)
vin_warn("v4l2 sub device g_parm fail!\n");
return ret;
}
static int __vin_s_parm(struct vin_core *vinc, struct v4l2_streamparm *parms)
{
struct vin_vid_cap *cap = &vinc->vid_cap;
struct sensor_instance *inst = get_valid_sensor(vinc);
int ret = 0;
if (parms->parm.capture.capturemode != V4L2_MODE_VIDEO &&
parms->parm.capture.capturemode != V4L2_MODE_IMAGE &&
parms->parm.capture.capturemode != V4L2_MODE_PREVIEW) {
parms->parm.capture.capturemode = V4L2_MODE_PREVIEW;
}
if (vinc->dma_merge_mode == 1)
parms->parm.capture.reserved[2] = 3;
if ((vinc->csi_ch != 0xff) && (vinc->csi_ch & 0x20))
sunxi_csi_set_ch_mode(cap->pipe.sd[VIN_IND_CSI]);
cap->capture_mode = parms->parm.capture.capturemode;
vinc->large_image = parms->parm.capture.reserved[2];
if (WARN_ON(!cap->pipe.sd[VIN_IND_SENSOR] || !cap->pipe.sd[VIN_IND_CSI]))
return -EINVAL;
ret = sensor_s_parm(cap->pipe.sd[VIN_IND_SENSOR], parms);
if (ret < 0)
vin_warn("v4l2 subdev sensor s_parm error!\n");
ret = sunxi_csi_subdev_s_parm(cap->pipe.sd[VIN_IND_CSI], parms);
if (ret < 0)
vin_warn("v4l2 subdev csi s_parm error!\n");
#ifdef SUPPORT_ISP_TDM
if (vinc->tdm_rx_sel != 0xff) {
ret = sunxi_tdm_subdev_s_parm(cap->pipe.sd[VIN_IND_TDM_RX], parms);
if (ret < 0)
vin_warn("v4l2 subdev tdm s_parm error!\n");
}
#endif
if (inst->is_isp_used && cap->pipe.sd[VIN_IND_ISP]) {
ret = sunxi_isp_s_parm(cap->pipe.sd[VIN_IND_ISP], parms);
if (ret < 0)
vin_warn("v4l2 subdev isp s_parm error!\n");
}
ret = sunxi_scaler_subdev_s_parm(cap->pipe.sd[VIN_IND_SCALER], parms);
if (ret < 0)
vin_warn("v4l2 subdev scaler s_parm error!\n");
return ret;
}
static int vidioc_s_parm(struct file *file, void *priv,
struct v4l2_streamparm *parms)
{
struct vin_core *vinc = video_drvdata(file);
int ret;
__maybe_unused struct vin_core *vinc_bind = NULL;
if (vinc->dma_merge_mode == 1) {
if (vinc->id % 4 == 1 && vin_core_gbl[vinc->id - 1]) {
vinc_bind = vin_core_gbl[vinc->id - 1];
ret = __vin_s_parm(vinc_bind, parms);
if (ret < 0)
return ret;
}
}
ret = __vin_s_parm(vinc, parms);
if (ret < 0)
return ret;
return 0;
}
static int vidioc_s_dv_timings(struct file *file, void *fh,
struct v4l2_dv_timings *timings)
{
struct vin_core *vinc = video_drvdata(file);
struct vin_vid_cap *cap = &vinc->vid_cap;
int ret = 0;
ret = v4l2_subdev_call(cap->pipe.sd[VIN_IND_SENSOR], video, s_dv_timings, timings);
if (ret)
vin_err("sensor set dv timings error!\n");
return ret;
}
static int vidioc_g_dv_timings(struct file *file, void *fh,
struct v4l2_dv_timings *timings)
{
struct vin_core *vinc = video_drvdata(file);
struct vin_vid_cap *cap = &vinc->vid_cap;
int ret = 0;
ret = v4l2_subdev_call(cap->pipe.sd[VIN_IND_SENSOR], video, g_dv_timings, timings);
if (ret)
vin_err("sensor set dv timings error!\n");
return ret;
}
static int vidioc_query_dv_timings(struct file *file, void *fh,
struct v4l2_dv_timings *timings)
{
struct vin_core *vinc = video_drvdata(file);
struct vin_vid_cap *cap = &vinc->vid_cap;
int ret = 0;
ret = v4l2_subdev_call(cap->pipe.sd[VIN_IND_SENSOR], video, query_dv_timings, timings);
if (ret)
vin_err("sensor query dv timings error!\n");
return ret;
}
static int vidioc_enum_dv_timings(struct file *file, void *fh,
struct v4l2_enum_dv_timings *timings)
{
struct vin_core *vinc = video_drvdata(file);
struct vin_vid_cap *cap = &vinc->vid_cap;
int ret = 0;
ret = v4l2_subdev_call(cap->pipe.sd[VIN_IND_SENSOR], pad, enum_dv_timings, timings);
if (ret)
vin_err("sensor enum dv timings error!\n");
return ret;
}
static int vidioc_dv_timings_cap(struct file *file, void *fh,
struct v4l2_dv_timings_cap *cap)
{
struct vin_core *vinc = video_drvdata(file);
struct vin_vid_cap *vid_cap = &vinc->vid_cap;
int ret = 0;
ret = v4l2_subdev_call(vid_cap->pipe.sd[VIN_IND_SENSOR], pad, dv_timings_cap, cap);
if (ret)
vin_err("sensor dv timings cap error!\n");
return ret;
}
static int __vin_sensor_line2time(struct v4l2_subdev *sd, u32 exp_line)
{
struct sensor_info *info = to_state(sd);
u32 overflow = 0xffffffff / 1000000, pclk = 0;
int exp_time = 0;
if (NULL == info->current_wins) {
vin_err("%s format is not initialized\n", sd->name);
return 0;
}
if ((exp_line / 16) > overflow) {
exp_line = exp_line / 16;
pclk = info->current_wins->pclk / 1000000;
} else if ((exp_line / 16) > (overflow / 10)) {
exp_line = exp_line * 10 / 16;
pclk = info->current_wins->pclk / 100000;
} else if ((exp_line / 16) > (overflow / 100)) {
exp_line = exp_line * 100 / 16;
pclk = info->current_wins->pclk / 10000;
} else if ((exp_line / 16) > (overflow / 1000)) {
exp_line = exp_line * 1000 / 16;
pclk = info->current_wins->pclk / 1000;
} else {
exp_line = exp_line * 10000 / 16;
pclk = info->current_wins->pclk / 100;
}
if (pclk)
exp_time = exp_line * info->current_wins->hts / pclk;
return exp_time;
}
static int __vin_sensor_set_af_win(struct vin_vid_cap *cap)
{
struct vin_pipeline *pipe = &cap->pipe;
struct v4l2_win_setting af_win;
int ret = 0;
af_win.coor.x1 = cap->af_win[0]->val;
af_win.coor.y1 = cap->af_win[1]->val;
af_win.coor.x2 = cap->af_win[2]->val;
af_win.coor.y2 = cap->af_win[3]->val;
ret = v4l2_subdev_call(pipe->sd[VIN_IND_SENSOR],
core, ioctl, SET_AUTO_FOCUS_WIN, &af_win);
return ret;
}
static int __vin_sensor_set_ae_win(struct vin_vid_cap *cap)
{
struct vin_pipeline *pipe = &cap->pipe;
struct v4l2_win_setting ae_win;
int ret = 0;
ae_win.coor.x1 = cap->ae_win[0]->val;
ae_win.coor.y1 = cap->ae_win[1]->val;
ae_win.coor.x2 = cap->ae_win[2]->val;
ae_win.coor.y2 = cap->ae_win[3]->val;
ret = v4l2_subdev_call(pipe->sd[VIN_IND_SENSOR],
core, ioctl, SET_AUTO_EXPOSURE_WIN, &ae_win);
return ret;
}
int vidioc_sync_ctrl(struct file *file, struct v4l2_fh *fh,
struct csi_sync_ctrl *sync)
{
struct vin_core *vinc = video_drvdata(file);
if (!sync->type) {
csic_prs_sync_en_cfg(vinc->csi_sel, sync);
csic_prs_sync_cfg(vinc->csi_sel, sync);
csic_prs_sync_wait_N(vinc->csi_sel, sync);
csic_prs_sync_wait_M(vinc->csi_sel, sync);
csic_frame_cnt_enable(vinc->vipp_sel);
csic_dma_frm_cnt(vinc->vipp_sel, sync);
csic_prs_sync_en(vinc->csi_sel, sync);
} else {
csic_prs_xs_en(vinc->csi_sel, sync);
csic_prs_xs_period_len_register(vinc->csi_sel, sync);
}
return 0;
}
static int vidioc_set_top_clk(struct file *file, struct v4l2_fh *fh,
struct vin_top_clk *clk)
{
struct vin_core *vinc = video_drvdata(file);
vinc->vin_clk = clk->clk_rate;
return 0;
}
static int vidioc_set_fps_ds(struct file *file, struct v4l2_fh *fh,
struct vin_fps_ds *fps_down_sample)
{
struct vin_core *vinc = video_drvdata(file);
vinc->fps_ds = fps_down_sample->fps_ds;
return 0;
}
static int vidioc_set_isp_debug(struct file *file, struct v4l2_fh *fh,
struct isp_debug_mode *isp_debug)
{
struct vin_core *vinc = video_drvdata(file);
vinc->isp_dbg = *isp_debug;
sunxi_isp_debug(vinc->vid_cap.pipe.sd[VIN_IND_ISP], isp_debug);
return 0;
}
static int vidioc_vin_ptn_config(struct file *file, struct v4l2_fh *fh,
struct vin_pattern_config *ptn)
{
/* SUPPORT_PTN */
struct vin_core *vinc = video_drvdata(file);
struct isp_dev *isp = v4l2_get_subdevdata(vinc->vid_cap.pipe.sd[VIN_IND_ISP]);
int ret = 0;
int i;
printk("vidioc_vin_ptn_config work!\n");
if (ptn->ptn_en) {
vinc->ptn_cfg.ptn_en = 1;
vinc->ptn_cfg.ptn_w = ptn->ptn_w;
vinc->ptn_cfg.ptn_h = ptn->ptn_h;
vinc->ptn_cfg.ptn_mode = 12;
vinc->ptn_cfg.ptn_buf.size = ptn->ptn_size + 64; /* read 16word for vblanks */
/* ptn_type: 0:recycle the first picture 1:recycle all picture */
vinc->ptn_cfg.ptn_type = ptn->ptn_type;
sunxi_isp_ptn(vinc->vid_cap.pipe.sd[VIN_IND_ISP], vinc->ptn_cfg.ptn_type);
isp->ptn_cfg = &vinc->ptn_cfg;
switch (ptn->ptn_fmt) {
/* the data needs to be converted to 10bit format */
case V4L2_PIX_FMT_SBGGR8:
case V4L2_PIX_FMT_SGBRG8:
case V4L2_PIX_FMT_SGRBG8:
case V4L2_PIX_FMT_SRGGB8:
vinc->ptn_cfg.ptn_dw = 0;
vinc->ptn_cfg.ptn_size = ptn->ptn_w * ptn->ptn_h * 2;
break;
case V4L2_PIX_FMT_SBGGR10:
case V4L2_PIX_FMT_SGBRG10:
case V4L2_PIX_FMT_SGRBG10:
case V4L2_PIX_FMT_SRGGB10:
vinc->ptn_cfg.ptn_dw = 1;
vinc->ptn_cfg.ptn_size = ptn->ptn_w * ptn->ptn_h * 2;
break;
case V4L2_PIX_FMT_SBGGR12:
case V4L2_PIX_FMT_SGBRG12:
case V4L2_PIX_FMT_SGRBG12:
case V4L2_PIX_FMT_SRGGB12:
vinc->ptn_cfg.ptn_dw = 2;
vinc->ptn_cfg.ptn_size = ptn->ptn_w * ptn->ptn_h * 2;
break;
default:
vinc->ptn_cfg.ptn_dw = 0;
vinc->ptn_cfg.ptn_size = ptn->ptn_w * ptn->ptn_h * 2;
break;
}
if (vinc->ptn_cfg.ptn_buf.size < vinc->ptn_cfg.ptn_size) {
vin_err("ptn need size is 0x%x, but intput picture size is 0x%x\n", (unsigned int)vinc->ptn_cfg.ptn_size, (unsigned int)vinc->ptn_cfg.ptn_buf.size);
vinc->ptn_cfg.ptn_en = 0;
return -EINVAL;
}
vinc->ptn_cfg.ptn_cnt = vinc->ptn_cfg.ptn_buf.size / vinc->ptn_cfg.ptn_size;
vin_print("ptn_cnt is %d\n", vinc->ptn_cfg.ptn_cnt);
/* 1080P:ptn_gen_dly:0xf:2fps 0x4:8fps 0x2:15fps 0x1:27fps 0x0:125fsp */
if (ptn->ptn_w >= 2560)
vinc->ptn_cfg.ptn_gen_dly = 0x1;
else if (ptn->ptn_w >= 1920)
vinc->ptn_cfg.ptn_gen_dly = 0x0;
else if (ptn->ptn_w >= 1280)
vinc->ptn_cfg.ptn_gen_dly = 0x4;
else if (ptn->ptn_w >= 640)
vinc->ptn_cfg.ptn_gen_dly = 0x8;
else
vinc->ptn_cfg.ptn_gen_dly = 0xf;
if (ptn->ptn_addr) {
if (vinc->ptn_cfg.ptn_buf.vir_addr == NULL)
os_mem_alloc(&vinc->pdev->dev, &vinc->ptn_cfg.ptn_buf);
if (vinc->ptn_cfg.ptn_buf.vir_addr == NULL) {
vin_err("ptn buffer 0x%x alloc failed!\n", ptn->ptn_size);
vinc->ptn_cfg.ptn_en = 0;
return -ENOMEM;
}
vin_print("ptn dma_addr is 0x%p, size is 0x%x\n", vinc->ptn_cfg.ptn_buf.dma_addr, (unsigned int)vinc->ptn_cfg.ptn_buf.size);
for (i = 0; i < vinc->ptn_cfg.ptn_cnt; i++) {
ret = copy_from_user(vinc->ptn_cfg.ptn_buf.vir_addr + vinc->ptn_cfg.ptn_size * i, (void *)ptn->ptn_addr + vinc->ptn_cfg.ptn_size * i, vinc->ptn_cfg.ptn_size);
if (ret < 0) {
vin_err("copy ptn buffer from usr error!\n");
vinc->ptn_cfg.ptn_en = 0;
return ret;
}
}
}
/*set ptn driver output size*/
if (vinc->vid_cap.pipe.sd[VIN_IND_SENSOR]) {
ret = v4l2_subdev_call(vinc->vid_cap.pipe.sd[VIN_IND_SENSOR],
core, ioctl, SET_PTN, ptn);
if (ret)
vin_err("set %s ptn size fail", vinc->vid_cap.pipe.sd[VIN_IND_SENSOR]->name);
}
} else {
vinc->ptn_cfg.ptn_en = 0;
if (vinc->ptn_cfg.ptn_buf.vir_addr)
os_mem_free(&vinc->pdev->dev, &vinc->ptn_cfg.ptn_buf);
}
// #endif
return 0;
}
static int vidioc_vin_set_reset_time(struct file *file, struct v4l2_fh *fh,
struct vin_reset_time *time)
{
struct vin_core *vinc = video_drvdata(file);
struct csi_dev *csi = v4l2_get_subdevdata(vinc->vid_cap.pipe.sd[VIN_IND_CSI]);
csi->reset_time = time->reset_time;
return 0;
}
static int vidioc_set_parser_fps(struct file *file, struct v4l2_fh *fh,
struct parser_fps_ds *parser_fps_ds)
{
struct vin_core *vinc = video_drvdata(file);
struct csi_dev *csi = v4l2_get_subdevdata(vinc->vid_cap.pipe.sd[VIN_IND_CSI]);
csi->prs_fps_ds.ch0_fps_ds = parser_fps_ds->ch0_fps_ds & 0xf;
csi->prs_fps_ds.ch1_fps_ds = parser_fps_ds->ch1_fps_ds & 0xf;
csi->prs_fps_ds.ch2_fps_ds = parser_fps_ds->ch2_fps_ds & 0xf;
csi->prs_fps_ds.ch3_fps_ds = parser_fps_ds->ch3_fps_ds & 0xf;
return 0;
}
static int vidioc_set_standby(struct file *file, struct v4l2_fh *fh,
struct sensor_standby_status *stby_status)
{
struct vin_core *vinc = video_drvdata(file);
int ret;
ret = v4l2_subdev_call(vinc->vid_cap.pipe.sd[VIN_IND_SENSOR], core,
s_power, stby_status->stby_stat);
if (ret != 0) {
vin_warn("%s:sensor failed to set sensor standby !\n",
__func__);
return ret;
}
return 0;
}
/* must set after VIDIOC_S_PARM and before VIDIOC_S_FMT */
static int vidioc_set_sensor_isp_cfg(struct file *file, struct v4l2_fh *fh,
struct sensor_isp_cfg *sensor_isp_cfg)
{
struct vin_core *vinc = video_drvdata(file);
struct isp_dev *isp = v4l2_get_subdevdata(vinc->vid_cap.pipe.sd[VIN_IND_ISP]);
struct v4l2_subdev *sd = vinc->vid_cap.pipe.sd[VIN_IND_SENSOR];
struct sensor_info *info = container_of(sd, struct sensor_info, sd);
if (vinc->dma_merge_mode != 1) {
isp->large_image = sensor_isp_cfg->large_image;
}
info->isp_wdr_mode = sensor_isp_cfg->isp_wdr_mode;
return 0;
}
/* must set after VIDIOC_S_INPUT and before VIDIOC_S_PARM */
static int vidioc_set_ve_online_cfg(struct file *file, struct v4l2_fh *fh,
struct csi_ve_online_cfg *cfg)
{
struct vin_core *vinc = video_drvdata(file);
if (!cfg->ve_online_en) {
vinc->ve_online_cfg.ve_online_en = 0;
vinc->ve_online_cfg.dma_buf_num = BK_MUL_BUFFER;
vin_print("ve_online close\n");
return 0;
}
if (vinc->work_mode == BK_OFFLINE) {
vin_err("ve online mode need video%d work in online\n", vinc->id);
return -1;
}
if (vinc->id == CSI_VE_ONLINE_VIDEO) {
memcpy(&vinc->ve_online_cfg, cfg, sizeof(struct csi_ve_online_cfg));
} else {
vin_err("only video%d supply ve online\n", CSI_VE_ONLINE_VIDEO);
return -1;
}
vin_print("ve_online %s, buffer_num is %d\n", vinc->ve_online_cfg.ve_online_en ? "open" : "close", vinc->ve_online_cfg.dma_buf_num);
return 0;
}
/* must set after VIDIOC_S_FMT and before VIDIOC_STREAMON */
static int vidioc_set_vipp_shrink_cfg(struct file *file, struct v4l2_fh *fh,
struct vipp_shrink_cfg *cfg)
{
struct vin_core *vinc = video_drvdata(file);
struct v4l2_subdev_selection sel;
int ret;
sel.target = V4L2_SEL_TGT_CROP;
sel.pad = SCALER_PAD_SINK;
sel.which = V4L2_SUBDEV_FORMAT_ACTIVE;
sel.reserved[0] = VIPP_ONLY_SHRINK;
sel.r.width = vinc->vid_cap.frame.o_width;
sel.r.height = vinc->vid_cap.frame.o_height;
sel.r.left = cfg->left;
sel.r.top = cfg->top;
ret = v4l2_subdev_call(vinc->vid_cap.pipe.sd[VIN_IND_SCALER],
pad, set_selection, NULL, &sel);
if (ret < 0) {
vin_err("vipp set_selection shrink error!\n");
}
return ret;
}
/* must set after VIDIOC_S_FMT and before VIDIOC_STREAMON */
static int vidioc_set_tdm_speeddn_cfg(struct file *file, struct v4l2_fh *fh,
struct tdm_speeddn_cfg *cfg)
{
#if defined SUPPORT_ISP_TDM && defined TDM_V200
struct vin_core *vinc = video_drvdata(file);
struct vin_vid_cap *cap = &vinc->vid_cap;
struct mipi_dev *mipi = container_of(cap->pipe.sd[VIN_IND_MIPI], struct mipi_dev, subdev);
struct tdm_rx_dev *tdm_rx = container_of(cap->pipe.sd[VIN_IND_TDM_RX], struct tdm_rx_dev, subdev);
struct tdm_dev *tdm = container_of(tdm_rx, struct tdm_dev, tdm_rx[tdm_rx->id]);
struct csi_dev *csi = v4l2_get_subdevdata(cap->pipe.sd[VIN_IND_CSI]);
if (csi->bus_info.bus_if == V4L2_MBUS_CSI2_DPHY) {
if (cfg->pix_num == MIPI_TWO_PIXEL && mipi->cmb_csi_cfg.lane_num != 4) {
cfg->pix_num = MIPI_ONE_PIXEL;
vin_warn("mipi %d lane cannot support two pixel, set to one pexel\n", mipi->cmb_csi_cfg.lane_num);
}
if (cfg->pix_num == MIPI_TWO_PIXEL && !cfg->tdm_speed_down_en) {
cfg->tdm_speed_down_en = 1;
vin_warn("when mipi set to two pixel, must open tdm speed_dn\n");
}
mipi->cmb_csi_cfg.pix_num = (enum cmb_csi_pix_num)cfg->pix_num;
}
tdm->ws.speed_dn_en = cfg->tdm_speed_down_en;
if (cfg->tdm_tx_valid_num || cfg->tdm_tx_invalid_num) {
tdm->tx_cfg.valid_num = cfg->tdm_tx_valid_num;
tdm->tx_cfg.invalid_num = cfg->tdm_tx_invalid_num;
} else {
tdm->tx_cfg.valid_num = 1;
tdm->tx_cfg.invalid_num = 0;
}
#endif
return 0;
}
#if IS_ENABLED(CONFIG_ISP_SERVER_MELIS)
static int vin_get_isp_encpp_attr_cfg(struct file *file, struct v4l2_fh *fh, struct isp_encpp_cfg_attr_data *encpp_attr_cfg)
{
struct vin_core *vinc = video_drvdata(file);
struct isp_dev *isp = v4l2_get_subdevdata(vinc->vid_cap.pipe.sd[VIN_IND_ISP]);
if (encpp_attr_cfg != NULL) {
if (isp->h3a_stat.state == ISPSTAT_ENABLED) {
encpp_attr_cfg->encpp_en = isp->encpp_en;
/* encpp_static_sharp_config */
memcpy(&encpp_attr_cfg->encpp_static_sharp_cfg, &isp->encpp_static_sharp_cfg,
sizeof(struct encpp_static_sharp_config));
/* encpp_dynamic_sharp_config */
memcpy(&encpp_attr_cfg->encpp_dynamic_sharp_cfg, &isp->encpp_dynamic_sharp_cfg,
sizeof(struct encpp_dynamic_sharp_config));
/* encoder_3dnr_config */
memcpy(&encpp_attr_cfg->encoder_3dnr_cfg, &isp->encoder_3dnr_cfg,
sizeof(struct encoder_3dnr_config));
/* encoder_2dnr_config */
memcpy(&encpp_attr_cfg->encoder_2dnr_cfg, &isp->encoder_2dnr_cfg,
sizeof(struct encoder_2dnr_config));
} else {
vin_warn("h3a_stat.state is ISPSTAT_DISABLED, will not get isp encpp attr cfg\n");
}
} else {
vin_err("encpp_attr_cfg is NULL!!!\n");
return -1;
}
return 0;
}
static int vin_set_isp_param_bin_cfg(struct isp_dev *isp, char *bin_path)
{
int idx;
struct file *file_fd = NULL;
struct isp_param_config *param = NULL;
unsigned int size, len;
char time[20], notes[50], fdstr[100];
int rpmsg_data[110];
char *param_start = NULL;
loff_t pos = 0;
sprintf(fdstr, "%s/isp_param_config.bin", bin_path);
file_fd = filp_open(fdstr, O_RDONLY, 0);
if (IS_ERR(file_fd)) {
vin_err("open %s failed.\n", fdstr);
return -1;
} else {
vfs_read(file_fd, (char *)&size, sizeof(unsigned int), &pos);
if (size != sizeof(struct isp_param_config)) {
vin_err("%s -- read size %d != isp_param size %ld!\n", fdstr, size, sizeof(struct isp_param_config));
filp_close(file_fd, NULL);
return -1;
} else {
param = kzalloc(sizeof(struct isp_param_config), GFP_KERNEL);
vfs_read(file_fd, (char *)time, 20, &pos);
vfs_read(file_fd, (char *)notes, 50, &pos);
vfs_read(file_fd, (char *)param, size, &pos);
rpmsg_data[0] = VIN_SET_ATTR_IOCTL;
rpmsg_data[1] = ISP_CTRL_READ_BIN_PARAM;
//isp_test_settings + isp_3a_settings + isp_tunning_settings
len = sizeof(struct isp_test_param) + sizeof(struct isp_3a_param) + sizeof(struct isp_tunning_param);
param_start = (char *)&param->isp_test_settings;
rpmsg_data[2] = SET_BIN_TEST_3A_TUNING;//flag;
idx = 0;
while (len) {
rpmsg_data[3] = idx;
if (len >= 400) {
rpmsg_data[4] = 400;
len -= 400;
} else {
rpmsg_data[4] = len;
len = 0;
}
memcpy(&rpmsg_data[5], &param_start[idx * 400], rpmsg_data[4]);
idx++;
isp_rpmsg_send(isp, rpmsg_data, 110 * 4);
usleep_range(500, 550);
}
//isp_iso_settings
//triger
rpmsg_data[2] = SET_BIN_ISO_TRIGER;//flag;
rpmsg_data[3] = 0;
rpmsg_data[4] = sizeof(param->isp_iso_settings.triger);
rpmsg_data[5] = param->isp_iso_settings.triger.sharp_triger;
#ifdef USE_ENCPP
rpmsg_data[6] = param->isp_iso_settings.triger.encpp_sharp_triger;
rpmsg_data[7] = param->isp_iso_settings.triger.encoder_denoise_triger;
#endif
rpmsg_data[8] = param->isp_iso_settings.triger.denoise_triger;
rpmsg_data[9] = param->isp_iso_settings.triger.black_level_triger;
rpmsg_data[10] = param->isp_iso_settings.triger.dpc_triger;
rpmsg_data[11] = param->isp_iso_settings.triger.defog_value_triger;
rpmsg_data[12] = param->isp_iso_settings.triger.pltm_dynamic_triger;
rpmsg_data[13] = param->isp_iso_settings.triger.brightness_triger;
rpmsg_data[14] = param->isp_iso_settings.triger.gcontrast_triger;
rpmsg_data[15] = param->isp_iso_settings.triger.cem_triger;
rpmsg_data[16] = param->isp_iso_settings.triger.tdf_triger;
rpmsg_data[17] = param->isp_iso_settings.triger.color_denoise_triger;
rpmsg_data[18] = param->isp_iso_settings.triger.ae_cfg_triger;
rpmsg_data[19] = param->isp_iso_settings.triger.gtm_cfg_triger;
rpmsg_data[20] = param->isp_iso_settings.triger.lca_cfg_triger;
rpmsg_data[21] = param->isp_iso_settings.triger.wdr_cfg_triger;
rpmsg_data[22] = param->isp_iso_settings.triger.cfa_triger;
rpmsg_data[23] = param->isp_iso_settings.triger.shading_triger;
isp_rpmsg_send(isp, rpmsg_data, 24 * 4);
usleep_range(500, 550);
//isp_lum_mapping_point + isp_gain_mapping_point + isp_dynamic_cfg
len = sizeof(param->isp_iso_settings.isp_lum_mapping_point) +
sizeof(param->isp_iso_settings.isp_gain_mapping_point) +
sizeof(param->isp_iso_settings.isp_dynamic_cfg);
param_start = (char *)&param->isp_iso_settings.isp_lum_mapping_point[0];
rpmsg_data[2] = SET_BIN_ISO_OTHER;//flag;
idx = 0;
while (len) {
rpmsg_data[3] = idx;
if (len >= 400) {
rpmsg_data[4] = 400;
len -= 400;
} else {
rpmsg_data[4] = len;
len = 0;
}
memcpy(&rpmsg_data[5], &param_start[idx * 400], rpmsg_data[4]);
idx++;
isp_rpmsg_send(isp, rpmsg_data, 110 * 4);
usleep_range(500, 550);
}
//finish
rpmsg_data[2] = SET_BIN_TUNING_UPDATE;//flag
rpmsg_data[3] = 0;
rpmsg_data[4] = 0;
isp_rpmsg_send(isp, rpmsg_data, 5 * 4);
vin_print("Read %s seccess... Time:%s Notes:%s\n", fdstr, time, notes);
kfree(param);
}
}
filp_close(file_fd, NULL);
return 0;
}
static int vin_set_isp_attr_cfg_ctrl(struct vin_core *vinc, struct isp_cfg_attr_data *attr_cfg)
{
struct isp_dev *isp = v4l2_get_subdevdata(vinc->vid_cap.pipe.sd[VIN_IND_ISP]);
struct ae_table_info *user_ae_table;
int data[124] = {0};
int *ptr = NULL;
unsigned char *ptr_u8 = NULL;
int ret = 0;
int i = 0;
if (isp->h3a_stat.state == ISPSTAT_ENABLED) {
data[0] = VIN_SET_ATTR_IOCTL;
data[1] = attr_cfg->cfg_id;
switch (attr_cfg->cfg_id) {
case ISP_CTRL_DN_STR:
data[2] = attr_cfg->denoise_level;
isp_rpmsg_send(isp, data, 3*4);
break;
case ISP_CTRL_3DN_STR:
data[2] = attr_cfg->tdf_level;
isp_rpmsg_send(isp, data, 3*4);
break;
case ISP_CTRL_PLTMWDR_STR:
data[2] = attr_cfg->pltmwdr_level;
isp_rpmsg_send(isp, data, 3*4);
break;
case ISP_CTRL_IR_STATUS:
data[2] = attr_cfg->ir_status;
isp->isp_cfg_attr.ir_status = attr_cfg->ir_status;
isp_rpmsg_send(isp, data, 3*4);
break;
case ISP_CTRL_EV_IDX:
data[2] = attr_cfg->ae_ev_idx;
isp_rpmsg_send(isp, data, 3*4);
break;
case ISP_CTRL_AE_LOCK:
data[2] = attr_cfg->ae_lock;
isp_rpmsg_send(isp, data, 3*4);
break;
case ISP_CTRL_AE_TABLE:
user_ae_table = (struct ae_table_info *)kzalloc(sizeof(struct ae_table_info), GFP_KERNEL);
if (user_ae_table == NULL) {
vin_err("kzalloc user_ae_table error!!!\n");
return -1;
}
ret = copy_from_user(user_ae_table, attr_cfg->ae_table, sizeof(struct ae_table_info));
if (ret != 0) {
vin_err("copy attr_cfg->ae_table from usr error!\n");
return -1;
}
ptr = &(user_ae_table->ae_tbl[0].min_exp);
for (i = 2; i < (user_ae_table->length*6 + 3); i++, ptr++) {
data[i] = *ptr;
}
data[63] = user_ae_table->length;
data[64] = user_ae_table->ev_step;
data[65] = user_ae_table->shutter_shift;
kfree(user_ae_table);
isp_rpmsg_send(isp, data, 66*4);
break;
case ISP_CTRL_READ_BIN_PARAM:
vin_set_isp_param_bin_cfg(isp, attr_cfg->path);
break;
case ISP_CTRL_AE_ROI_TARGET:
data[2] = attr_cfg->ae_roi_area.enable;
data[3] = attr_cfg->ae_roi_area.force_ae_target;
data[4] = attr_cfg->ae_roi_area.coor.x1;
data[5] = attr_cfg->ae_roi_area.coor.y1;
data[6] = attr_cfg->ae_roi_area.coor.x2;
data[7] = attr_cfg->ae_roi_area.coor.y2;
isp_rpmsg_send(isp, data, 8*4);
break;
case ISP_CTRL_VENC2ISP_PARAM:
ptr_u8 = (unsigned char *)&data[2];
ptr_u8[0] = attr_cfg->VencVe2IspParam.mMovingLevelInfo.is_overflow;
for (i = 0; i < ISP_MSC_TBL_SIZE; i++) {
ptr_u8[i + 1] = attr_cfg->VencVe2IspParam.mMovingLevelInfo.moving_level_table[i] >> 4;
}
isp_rpmsg_send(isp, data, 496);
break;
}
} else {
vin_warn("h3a_stat.state is ISPSTAT_DISABLED, will not set isp attr cfg\n");
}
return 0;
}
static int vin_get_isp_attr_cfg_ctrl(struct vin_core *vinc, struct isp_cfg_attr_data *attr_cfg)
{
struct isp_dev *isp = v4l2_get_subdevdata(vinc->vid_cap.pipe.sd[VIN_IND_ISP]);
unsigned int data[2];
unsigned int timeout_cnt = 100;
int i;
if (isp->h3a_stat.state == ISPSTAT_ENABLED) {
data[0] = VIN_REQUEST_ATTR_IOCTL;
data[1] = attr_cfg->cfg_id;
/* reset update_flag for update isp_cfg action start */
isp->isp_cfg_attr.update_flag = 0;
isp_rpmsg_send(isp, data, 2*4);
for (i = 0; i < timeout_cnt; i++) {
/* wait for update_flag ok */
if (isp->isp_cfg_attr.update_flag)
break;
usleep_range(500, 550);
}
if (i == timeout_cnt) {
vin_err("VIDIOC_GET_ISP_CFG_ATTR timeout!!!\n");
return -1;
} else {
/* update isp_attr_cfg */
memcpy(attr_cfg, &(isp->isp_cfg_attr), sizeof(struct isp_cfg_attr_data));
}
} else {
vin_warn("h3a_stat.state is ISPSTAT_DISABLED, will not update isp attr cfg\n");
}
return 0;
}
#endif
static int vidioc_set_isp_attr_cfg(struct file *file, struct v4l2_fh *fh,
struct isp_cfg_attr_data *attr_cfg)
{
int ret = 0;
#if IS_ENABLED(CONFIG_ISP_SERVER_MELIS)
struct vin_core *vinc = video_drvdata(file);
ret = vin_set_isp_attr_cfg_ctrl(vinc, attr_cfg);
#endif
return ret;
}
static int vidioc_get_isp_attr_cfg(struct file *file, struct v4l2_fh *fh,
struct isp_cfg_attr_data *attr_cfg)
{
int ret = 0;
#if IS_ENABLED(CONFIG_ISP_SERVER_MELIS)
struct vin_core *vinc = video_drvdata(file);
ret = vin_get_isp_attr_cfg_ctrl(vinc, attr_cfg);
#endif
return ret;
}
static int vidioc_set_phy2vir_cfg(struct file *file, struct v4l2_fh *fh,
struct isp_memremap_cfg *isp_memremap)
{
#if IS_ENABLED(CONFIG_VIN_INIT_MELIS)
__maybe_unused struct vin_core *vinc = video_drvdata(file);
unsigned long viraddr;
struct vm_area_struct *vma;
void *vaddr = NULL;
__maybe_unused struct isp_autoflash_config_s *isp_autoflash_cfg = NULL;
__maybe_unused unsigned int map_addr = 0;
__maybe_unused unsigned int check_sign = 0;
#ifndef GET_RV_YUV
if (vinc->mipi_sel == 0) {
map_addr = VIN_SENSOR0_RESERVE_ADDR;
check_sign = 0xAA11AA11;
} else {
map_addr = VIN_SENSOR1_RESERVE_ADDR;
check_sign = 0xBB11BB11;
}
vaddr = vin_map_kernel(map_addr, VIN_RESERVE_SIZE + VIN_THRESHOLD_PARAM_SIZE); /* map unit is page, page is align of 4k */
if (vaddr == NULL) {
vin_err("%s:map 0x%x paddr err!!!", __func__, map_addr);
return -EFAULT;
}
isp_autoflash_cfg = (struct isp_autoflash_config_s *)(vaddr + VIN_RESERVE_SIZE);
/* check id */
if (isp_autoflash_cfg->melisyuv_sign_id != check_sign) {
vin_warn("%s:sign is 0x%x but not 0x%x\n", __func__, isp_autoflash_cfg->sensorlist_sign_id, check_sign);
vin_unmap_kernel(vaddr);
return -EFAULT;
}
if (isp_memremap->en) {
viraddr = vm_mmap(NULL, 0, isp_autoflash_cfg->melisyuv_size, PROT_READ, MAP_SHARED | MAP_NORESERVE, 0);
vma = find_vma(current->mm, viraddr);
remap_pfn_range(vma, vma->vm_start, __phys_to_pfn(isp_autoflash_cfg->melisyuv_paddr), isp_autoflash_cfg->melisyuv_size, vma->vm_page_prot);
isp_memremap->vir_addr = (void *)viraddr;
isp_memremap->size = isp_autoflash_cfg->melisyuv_size;
vin_print("0x%x mmap viraddr is 0x%lx\n", isp_autoflash_cfg->melisyuv_paddr, viraddr);
} else {
if (isp_memremap->vir_addr && isp_memremap->size) {
vm_munmap((unsigned long)isp_memremap->vir_addr, isp_memremap->size);
vin_print("0x%x ummap viraddr is 0x%lx\n", isp_autoflash_cfg->melisyuv_paddr, (unsigned long)isp_memremap->vir_addr);
isp_autoflash_cfg->melisyuv_sign_id = 0XFFFFFFFF;
memblock_free(isp_autoflash_cfg->melisyuv_paddr, isp_autoflash_cfg->melisyuv_size);
free_reserved_area(__va(isp_autoflash_cfg->melisyuv_paddr), __va(isp_autoflash_cfg->melisyuv_paddr + isp_autoflash_cfg->melisyuv_size), -1, "isp_reserved");
}
}
#else
vaddr = vin_map_kernel(YUV_MEMRESERVE, YUV_MEMRESERVE_SIZE); /* map unit is page, page is align of 4k */ /* map unit is page, page is align of 4k */
if (vaddr == NULL) {
vin_err("%s:map 0x%x paddr err!!!", __func__, map_addr);
return -EFAULT;
}
if (isp_memremap->en) {
viraddr = vm_mmap(NULL, 0, YUV_MEMRESERVE_SIZE, PROT_READ, MAP_SHARED | MAP_NORESERVE, 0);
vma = find_vma(current->mm, viraddr);
remap_pfn_range(vma, vma->vm_start, __phys_to_pfn(YUV_MEMRESERVE), YUV_MEMRESERVE_SIZE, vma->vm_page_prot);
isp_memremap->vir_addr = (void *)viraddr;
isp_memremap->size = YUV_MEMRESERVE_SIZE;
vin_print("0x%x mmap viraddr is 0x%lx\n", YUV_MEMRESERVE, viraddr);
} else {
if (isp_memremap->vir_addr && isp_memremap->size) {
vm_munmap((unsigned long)isp_memremap->vir_addr, isp_memremap->size);
vin_print("0x%x ummap viraddr is 0x%lx\n", YUV_MEMRESERVE, (unsigned long)isp_memremap->vir_addr);
memblock_free(YUV_MEMRESERVE, YUV_MEMRESERVE_SIZE);
free_reserved_area(__va(YUV_MEMRESERVE), __va(YUV_MEMRESERVE + YUV_MEMRESERVE_SIZE), -1, "isp_reserved");
}
}
#endif
vin_unmap_kernel(vaddr);
#endif
return 0;
}
/* must set before VIDIOC_S_INPUT */
static int vidioc_merge_int_ch_cfg(struct file *file, struct v4l2_fh *fh,
struct mrg_int_ch_cfg *cfg)
{
#if defined MULTI_FRM_MERGE_INT
struct vin_core *vinc = video_drvdata(file);
struct vin_md *vind = dev_get_drvdata(vinc->v4l2_dev->dev);
unsigned int bk_ch_sel = cfg->mrg_ch_sel;
int i = 0;
#if IS_ENABLED(CONFIG_ARCH_SUN60IW1)
int bk_ch_intpool_sel[VIN_MAX_DEV] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20,
24, 25, 26, 27, 28, 29, 30, 31, 32, 36, 40};
#elif IS_ENABLED(CONFIG_ARCH_SUN60IW2)
int bk_ch_intpool_sel[VIN_MAX_DEV] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17};
#elif IS_ENABLED(CONFIG_ARCH_SUN55IW6)
int bk_ch_intpool_sel[VIN_MAX_DEV] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
#endif
if (cfg->trig_level < 1) {
cfg->trig_level = 1;
vin_warn("trig_level must be greater than or equal to 1\n");
}
/* max trigger level is 63 */
vind->bk_intpool.trig_level = cfg->trig_level > 63 ? 63 : cfg->trig_level;
for (i = 0; i < VIN_MAX_DEV; i++) {
if (GET_BIT(bk_ch_sel, i)) {
if (bk_ch_intpool_sel[i] < 16)
vind->bk_intpool.mask_cfg0 |= 1 << bk_ch_intpool_sel[i];
else
vind->bk_intpool.mask_cfg1 |= 1 << (bk_ch_intpool_sel[i] - 16);
}
}
#endif
return 0;
}
static int __tvin_info_check(struct tvin_init_info *info)
{
if (info->work_mode >= Tvd_Input_Type_SIZE) {
vin_err("tvin not support this work mode\n");
return -1;
}
if (info->ch_id >= TVIN_CH_SIZE) {
vin_err("[%s]ch_id can not over %d\n", __func__, TVIN_CH_SIZE);
return -1;
}
if (info->input_fmt[info->ch_id] > INPUT_FMT_SIZE) {
vin_err("tvin not support ch%d fmt = %d\n", info->ch_id, info->input_fmt[info->ch_id]);
return -1;
}
return 0;
}
static int vidioc_tvin_init(struct file *file,
struct v4l2_fh *fh, struct tvin_init_info *info)
{
struct vin_core *vinc = video_drvdata(file);
int ret = 0;
if (__tvin_info_check(info))
return -1;
vinc->tvin.work_mode = info->work_mode;
vinc->tvin.input_fmt = info->input_fmt[info->ch_id];
vinc->tvin.flag = true;
ret = v4l2_subdev_call(vinc->vid_cap.pipe.sd[VIN_IND_SENSOR], core, ioctl,
SENSOR_TVIN_INIT, info);
if (ret)
vin_err("sensor tvin init fail!\n");
if (vinc->vid_cap.pipe.sd[VIN_IND_MIPI] != NULL) {
ret = v4l2_subdev_call(vinc->vid_cap.pipe.sd[VIN_IND_MIPI], core, ioctl,
MIPI_TVIN_INIT, info);
if (ret)
vin_err("mipi tvin init fail!\n");
}
ret = v4l2_subdev_call(vinc->vid_cap.pipe.sd[VIN_IND_CSI], core, ioctl,
PARSER_TVIN_INIT, info);
if (ret)
vin_err("csi tvin init fail!\n");
vin_log(VIN_LOG_FMT, "%s mode %d, fmt %d\n", __func__,
vinc->tvin.work_mode, vinc->tvin.input_fmt);
return ret;
}
static int vidioc_set_dma_merge(struct file *file, struct v4l2_fh *fh,
unsigned char *dma_merge)
{
struct vin_core *vinc = video_drvdata(file);
__maybe_unused struct vin_core *vinc_bind = NULL;
vinc->dma_merge_mode = *dma_merge;
if (vinc->dma_merge_mode == 1) {
if (vinc->id % 4 == 1 && vin_core_gbl[vinc->id - 1]) {
vinc_bind = vin_core_gbl[vinc->id - 1];
vinc_bind->dma_merge_mode = *dma_merge;
}
}
return 0;
}
static int vidioc_set_bk_cache_invalid(struct file *file, struct v4l2_fh *fh,
unsigned char *index)
{
struct vin_core *vinc = video_drvdata(file);
struct vin_vid_cap *cap = &vinc->vid_cap;
struct vb2_queue *q = cap->vdev.queue;
struct vb2_buffer *vb;
dma_addr_t dma_addr;
if (*index >= q->num_buffers) {
vin_err("buffer index out of range\n");
return -EINVAL;
}
//vin_print("vidioc_set_bk_cache_invalid: index: %d\n", *index);
vb = q->bufs[*index];
dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
dma_sync_single_for_cpu(cap->dev, dma_addr, cap->buf_byte_size, DMA_FROM_DEVICE);
return 0;
}
static long vin_param_handler(struct file *file, void *priv,
bool valid_prio, unsigned int cmd, void *param)
{
int ret = 0;
struct v4l2_fh *fh = (struct v4l2_fh *)priv;
switch (cmd) {
case VIDIOC_ISP_EXIF_REQ:
break;
case VIDIOC_SYNC_CTRL:
ret = vidioc_sync_ctrl(file, fh, param);
break;
case VIDIOC_SET_TOP_CLK:
ret = vidioc_set_top_clk(file, fh, param);
break;
case VIDIOC_SET_FPS_DS:
ret = vidioc_set_fps_ds(file, fh, param);
break;
case VIDIOC_ISP_DEBUG:
ret = vidioc_set_isp_debug(file, fh, param);
break;
case VIDIOC_VIN_PTN_CFG:
ret = vidioc_vin_ptn_config(file, fh, param);
break;
case VIDIOC_VIN_RESET_TIME:
ret = vidioc_vin_set_reset_time(file, fh, param);
break;
case VIDIOC_SET_PARSER_FPS:
ret = vidioc_set_parser_fps(file, fh, param);
break;
case VIDIOC_SET_SENSOR_ISP_CFG:
ret = vidioc_set_sensor_isp_cfg(file, fh, param);
break;
case VIDIOC_SET_STANDBY:
ret = vidioc_set_standby(file, fh, param);
break;
case VIDIOC_SET_VE_ONLINE:
ret = vidioc_set_ve_online_cfg(file, fh, param);
break;
case VIDIOC_SET_VIPP_SHRINK:
ret = vidioc_set_vipp_shrink_cfg(file, fh, param);
break;
case VIDIOC_SET_TDM_SPEEDDN_CFG:
ret = vidioc_set_tdm_speeddn_cfg(file, fh, param);
break;
case VIDIOC_SET_ISP_CFG_ATTR:
ret = vidioc_set_isp_attr_cfg(file, fh, param);
break;
case VIDIOC_GET_ISP_CFG_ATTR:
ret = vidioc_get_isp_attr_cfg(file, fh, param);
break;
#if IS_ENABLED(CONFIG_ISP_SERVER_MELIS)
case VIDIOC_GET_ISP_ENCPP_CFG_ATTR:
ret = vin_get_isp_encpp_attr_cfg(file, fh, param);
break;
#endif
case VIDIOC_SET_PHY2VIR:
ret = vidioc_set_phy2vir_cfg(file, fh, param);
break;
case VIDIOC_MERGE_INT_CH_CFG:
ret = vidioc_merge_int_ch_cfg(file, fh, param);
break;
case VIDIOC_TVIN_INIT:
ret = vidioc_tvin_init(file, fh, param);
break;
case VIDIOC_SET_DMA_MERGE:
ret = vidioc_set_dma_merge(file, fh, param);
break;
case VIDIOC_SET_BK_CACHE_INVALID:
ret = vidioc_set_bk_cache_invalid(file, fh, param);
break;
default:
ret = -ENOTTY;
}
return ret;
}
static int vin_subscribe_event(struct v4l2_fh *fh,
const struct v4l2_event_subscription *sub)
{
if (sub->type == V4L2_EVENT_CTRL)
return v4l2_ctrl_subscribe_event(fh, sub);
else
return v4l2_event_subscribe(fh, sub, 1, NULL);
}
int vidioc_s_edid(struct file *file, void *fh, struct v4l2_edid *edid)
{
struct vin_core *vinc = video_drvdata(file);
struct vin_vid_cap *cap = &vinc->vid_cap;
int ret = 0;
ret = v4l2_subdev_call(cap->pipe.sd[VIN_IND_SENSOR], pad, set_edid, edid);
if (ret)
vin_err("sensor set edid error!\n");
return ret;
}
/* when camera on, close dfs */
#if IS_ENABLED(CONFIG_AW_DMC_DEVFREQ) && (IS_ENABLED(CONFIG_ARCH_SUN50IW10) || !defined CSIC_SDRAM_DFS)
static int vin_dfs_handler(struct vin_md *vind, bool en)
{
struct device *csi_dfs_dev = vind->v4l2_dev.dev;
char *envp[3] = {
"SYSTEM=CAMERA",
NULL,
NULL};
if (en)
envp[1] = "EVENT=ON";
else
envp[1] = "EVENT=OFF";
kobject_uevent_env(&csi_dfs_dev->kobj, KOBJ_CHANGE, envp);
vin_print("camera is %s, %s dfs\n",
en ? "on" : "off", en ? "close" : "open");
return 0;
}
#endif
static int vin_open(struct file *file)
{
struct vin_core *vinc = video_drvdata(file);
struct vin_vid_cap *cap = &vinc->vid_cap;
__maybe_unused struct vin_md *vind = dev_get_drvdata(vinc->v4l2_dev->dev);
if (vin_busy(cap)) {
vin_err("video%d open busy\n", vinc->id);
return -EBUSY;
}
#if IS_ENABLED(CONFIG_RV_RUN_CAR_REVERSE) && !defined (CONFIG_ARCH_SUN60IW2)
if (CONTROL_BY_RTOS == vinc->rpmsg.control) {
vin_err("video%d is controlling by rtos\n", vinc->id);
return -EBUSY;
}
vinc_status_rpmsg_send(ARM_VIN_START, &vinc->rpmsg);
#endif
mutex_lock(&cap->vdev.entity.graph_obj.mdev->graph_mutex);
set_bit(VIN_LPM, &cap->state);
set_bit(VIN_BUSY, &cap->state);
v4l2_fh_open(file);/* create event queue */
#if IS_ENABLED(CONFIG_AW_DMC_DEVFREQ) && (IS_ENABLED(CONFIG_ARCH_SUN50IW10) || !defined CSIC_SDRAM_DFS)
vin_dfs_handler(vind, 1);
#endif
#if IS_ENABLED(CONFIG_DEVFREQ_DRAM_FREQ_WITH_SOFT_NOTIFY)
dramfreq_master_access(MASTER_CSI, true);
#endif
vin_log(VIN_LOG_VIDEO, "video%d open\n", vinc->id);
mutex_unlock(&cap->vdev.entity.graph_obj.mdev->graph_mutex);
return 0;
}
static int vin_close(struct file *file)
{
struct vin_core *vinc = video_drvdata(file);
struct vin_md *vind = dev_get_drvdata(vinc->v4l2_dev->dev);
struct vin_vid_cap *cap = &vinc->vid_cap;
struct modules_config *module = &vind->modules[vinc->sensor_sel];
int valid_idx = module->sensors.valid_idx;
int ret;
__maybe_unused struct vin_core *vinc_bind = NULL;
if (!vin_busy(cap)) {
vin_warn("video%d have been closed!\n", vinc->id);
return 0;
}
#if IS_ENABLED(CONFIG_RV_RUN_CAR_REVERSE) && !defined (CONFIG_ARCH_SUN60IW2)
vinc_status_rpmsg_send(ARM_VIN_STOP, &vinc->rpmsg);
#endif
if (vin_streaming(cap))
vin_timer_del(vinc);
mutex_lock(&cap->vdev.entity.graph_obj.mdev->graph_mutex);
if (!cap->pipe.sd[VIN_IND_SENSOR] || !cap->pipe.sd[VIN_IND_SENSOR]->entity.use_count) {
mutex_unlock(&cap->vdev.entity.graph_obj.mdev->graph_mutex);
vb2_fop_release(file);
set_bit(VIN_LPM, &cap->state);
clear_bit(VIN_BUSY, &cap->state);
if (cap->pipe.sd[VIN_IND_SENSOR])
vin_err("%s is not used, video%d cannot be close!\n", cap->pipe.sd[VIN_IND_SENSOR]->name, vinc->id);
return -1;
}
if (vin_streaming(cap)) {
clear_bit(VIN_STREAM, &cap->state);
vin_pipeline_call(vinc, set_stream, &cap->pipe, 0);
if (vinc->dma_merge_mode == 1) {
if (vinc->id % 4 == 1 && vin_core_gbl[vinc->id - 1]) {
vinc_bind = vin_core_gbl[vinc->id - 1];
clear_bit(VIN_STREAM, &vinc_bind->vid_cap.state);
vin_pipeline_call(vinc_bind, set_stream, &vinc_bind->vid_cap.pipe, 0);
}
}
vb2_ioctl_streamoff(file, NULL, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
}
if (!vin_lpm(cap)) {
set_bit(VIN_LPM, &cap->state);
__csi_isp_setup_link(vinc, 0);
__vin_sensor_setup_link(vinc, module, valid_idx, 0);
if (vinc->dma_merge_mode == 1) {
if (vinc->id % 4 == 1 && vin_core_gbl[vinc->id - 1]) {
vinc_bind = vin_core_gbl[vinc->id - 1];
__csi_isp_setup_link(vinc_bind, 0);
__vin_sensor_setup_link(vinc_bind, module, valid_idx, 0);
}
}
}
if (cap->pipe.sd[VIN_IND_ACTUATOR] != NULL) {
ret = __vin_actuator_set_power(cap->pipe.sd[VIN_IND_ACTUATOR], 0);
if (ret < 0)
vin_err("actutor power off failed (%d)!\n", ret);
}
if (cap->pipe.sd[VIN_IND_FLASH] != NULL)
io_set_flash_ctrl(cap->pipe.sd[VIN_IND_FLASH], SW_CTRL_FLASH_OFF);
ret = vin_pipeline_call(vinc, close, &cap->pipe);
if (ret)
vin_err("vin pipeline close failed!\n");
v4l2_subdev_call(cap->pipe.sd[VIN_IND_ISP], core, init, 0);
if (vinc->dma_merge_mode == 1) {
if (vinc->id % 4 == 1 && vin_core_gbl[vinc->id - 1]) {
vinc_bind = vin_core_gbl[vinc->id - 1];
ret = vin_pipeline_call(vinc_bind, close, &vinc_bind->vid_cap.pipe);
if (ret)
vin_err("vin pipeline close failed!\n");
v4l2_subdev_call(vinc_bind->vid_cap.pipe.sd[VIN_IND_ISP], core, init, 0);
vinc_bind->dma_merge_mode = 0;
}
}
vinc->dma_merge_mode = 0;
#ifdef SUPPORT_PTN
if ((vinc->large_image == 2) && vinc->ptn_cfg.ptn_en) {
os_mem_free(&vinc->pdev->dev, &vinc->ptn_cfg.ptn_buf);
vinc->ptn_cfg.ptn_en = 0;
}
#endif
/* vb2_fop_release will use graph_mutex */
mutex_unlock(&cap->vdev.entity.graph_obj.mdev->graph_mutex);
ret = vb2_fop_release(file); /* vb2_queue_release(&cap->vb_vidq); */
#if IS_ENABLED(CONFIG_DEVFREQ_DRAM_FREQ_WITH_SOFT_NOTIFY)
dramfreq_master_access(MASTER_CSI, false);
#endif
#if IS_ENABLED(CONFIG_AW_DMC_DEVFREQ) && (IS_ENABLED(CONFIG_ARCH_SUN50IW10) || !defined CSIC_SDRAM_DFS)
vin_dfs_handler(vind, 0);
#endif
clear_bit(VIN_BUSY, &cap->state);
vin_log(VIN_LOG_VIDEO, "video%d close\n", vinc->id);
return 0;
}
static unsigned int vin_poll(struct file *file, poll_table *wait)
{
struct vin_core *vinc = video_drvdata(file);
struct vin_vid_cap *cap = &vinc->vid_cap;
if (!vin_streaming(cap))
return 0;
return vb2_fop_poll(file, wait);
}
static int vin_try_ctrl(struct v4l2_ctrl *ctrl)
{
/*
* to cheat control framework, because of when ctrl->cur.val == ctrl->val
* s_ctrl would not be called
*/
if ((ctrl->minimum == 0) && (ctrl->maximum == 1)) {
if (ctrl->val)
ctrl->cur.val = 0;
else
ctrl->cur.val = 1;
} else {
if (ctrl->val == ctrl->maximum)
ctrl->cur.val = ctrl->val - 1;
else
ctrl->cur.val = ctrl->val + 1;
}
/*
* to cheat control framework, because of when ctrl->flags is
* V4L2_CTRL_FLAG_VOLATILE, s_ctrl would not be called
*/
switch (ctrl->id) {
case V4L2_CID_VFLIP:
case V4L2_CID_HFLIP:
case V4L2_CID_EXPOSURE:
case V4L2_CID_EXPOSURE_ABSOLUTE:
case V4L2_CID_GAIN:
if (ctrl->val != ctrl->cur.val)
ctrl->flags &= ~V4L2_CTRL_FLAG_VOLATILE;
break;
default:
break;
}
return 0;
}
static int vin_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
struct vin_vid_cap *cap = container_of(ctrl->handler, struct vin_vid_cap, ctrl_handler);
struct sensor_instance *inst = get_valid_sensor(cap->vinc);
struct v4l2_subdev *sensor = cap->pipe.sd[VIN_IND_SENSOR];
struct v4l2_subdev *flash = cap->pipe.sd[VIN_IND_FLASH];
struct v4l2_control c;
int ret = 0;
if (!cap->vinc || !inst)
return -ENODEV;
c.id = ctrl->id;
if (inst->is_isp_used && inst->is_bayer_raw) {
switch (ctrl->id) {
case V4L2_CID_EXPOSURE:
if (!sensor || !sensor->ctrl_handler)
return -ENODEV;
v4l2_g_ctrl(sensor->ctrl_handler, &c);
ctrl->val = c.value;
break;
case V4L2_CID_EXPOSURE_ABSOLUTE:
if (!sensor || !sensor->ctrl_handler)
return -ENODEV;
c.id = V4L2_CID_EXPOSURE;
v4l2_g_ctrl(sensor->ctrl_handler, &c);
ctrl->val = __vin_sensor_line2time(sensor, c.value);
break;
case V4L2_CID_GAIN:
if (!sensor || !sensor->ctrl_handler)
return -ENODEV;
v4l2_g_ctrl(sensor->ctrl_handler, &c);
ctrl->val = c.value;
break;
case V4L2_CID_AE_WIN_X1:
break;
case V4L2_CID_AF_WIN_X1:
break;
case V4L2_CID_HOR_VISUAL_ANGLE:
case V4L2_CID_VER_VISUAL_ANGLE:
case V4L2_CID_FOCUS_LENGTH:
case V4L2_CID_3A_LOCK:
case V4L2_CID_AUTO_FOCUS_STATUS: /* Read-Only */
break;
case V4L2_CID_SENSOR_TYPE:
ctrl->val = inst->is_bayer_raw;
break;
case V4L2_CID_VFLIP:
ctrl->val = cap->vinc->vflip;
break;
case V4L2_CID_HFLIP:
ctrl->val = cap->vinc->hflip;
break;
default:
return -EINVAL;
}
return ret;
} else {
switch (ctrl->id) {
case V4L2_CID_SENSOR_TYPE:
c.value = inst->is_bayer_raw;
break;
case V4L2_CID_FLASH_LED_MODE:
if (!flash || !flash->ctrl_handler)
return -ENODEV;
ret = v4l2_g_ctrl(flash->ctrl_handler, &c);
break;
case V4L2_CID_AUTO_FOCUS_STATUS:
if (!sensor || !sensor->ctrl_handler)
return -ENODEV;
ret = v4l2_g_ctrl(sensor->ctrl_handler, &c);
if (flash && c.value != V4L2_AUTO_FOCUS_STATUS_BUSY)
sunxi_flash_stop(flash);
break;
case V4L2_CID_VFLIP:
ctrl->val = cap->vinc->vflip;
break;
case V4L2_CID_HFLIP:
ctrl->val = cap->vinc->hflip;
break;
default:
if (!sensor || !sensor->ctrl_handler)
return -ENODEV;
ret = v4l2_g_ctrl(sensor->ctrl_handler, &c);
break;
}
ctrl->val = c.value;
if (ret < 0)
vin_warn("v4l2 sub device g_ctrl fail!\n");
}
return ret;
}
int sensor_flip_option(struct vin_vid_cap *cap, struct v4l2_control c)
{
#if !defined ISP_600
struct v4l2_subdev *isp = cap->pipe.sd[VIN_IND_ISP];
struct v4l2_subdev *csi = cap->pipe.sd[VIN_IND_CSI];
struct v4l2_subdev *sensor = cap->pipe.sd[VIN_IND_SENSOR];
struct isp_dev *isp_device = v4l2_get_subdevdata(isp);
struct csi_dev *csi_device = v4l2_get_subdevdata(csi);
struct vin_md *vind = dev_get_drvdata(isp->v4l2_dev->dev);
struct vin_core *vinc = NULL;
struct prs_cap_mode mode = {.mode = VCAP};
unsigned int isp_stream_count;
int i = 0;
int input_seq = 0;
int sensor_fmt_code = 0;
int ret;
mutex_lock(&cap->vdev.entity.graph_obj.mdev->graph_mutex);
isp_stream_count = isp_device->subdev.entity.stream_count;
isp_device->subdev.entity.stream_count = 0;
csic_prs_capture_stop(csi_device->id);
ret = v4l2_s_ctrl(NULL, sensor->ctrl_handler, &c);
v4l2_subdev_call(sensor, core, ioctl, VIDIOC_VIN_GET_SENSOR_CODE, &sensor_fmt_code);
switch (sensor_fmt_code) {
case MEDIA_BUS_FMT_SBGGR8_1X8:
case MEDIA_BUS_FMT_SBGGR10_1X10:
case MEDIA_BUS_FMT_SBGGR12_1X12:
input_seq = ISP_BGGR;
break;
case MEDIA_BUS_FMT_SGBRG8_1X8:
case MEDIA_BUS_FMT_SGBRG10_1X10:
case MEDIA_BUS_FMT_SGBRG12_1X12:
input_seq = ISP_GBRG;
break;
case MEDIA_BUS_FMT_SGRBG8_1X8:
case MEDIA_BUS_FMT_SGRBG10_1X10:
case MEDIA_BUS_FMT_SGRBG12_1X12:
input_seq = ISP_GRBG;
break;
case MEDIA_BUS_FMT_SRGGB8_1X8:
case MEDIA_BUS_FMT_SRGGB10_1X10:
case MEDIA_BUS_FMT_SRGGB12_1X12:
input_seq = ISP_RGGB;
break;
default:
input_seq = ISP_BGGR;
break;
}
if (isp_device->use_isp) {
vin_print("%s:isp%d reset!!!\n", __func__, isp_device->id);
bsp_isp_set_para_ready(isp_device->id, PARA_NOT_READY);
}
#if IS_ENABLED(CONFIG_D3D)
if (isp_device->use_isp && (isp_device->load_shadow[0x2d4 + 0x3]) & (1<<1)) {
/* clear D3D rec_en 0x2d4 bit25 */
isp_device->load_shadow[0x2d4 + 0x3] = (isp_device->load_shadow[0x2d4 + 0x3]) & (~(1<<1));
memcpy(isp_device->isp_load.vir_addr, &isp_device->load_shadow[0], ISP_LOAD_DRAM_SIZE);
}
#endif
/* ****************stop****************** */
#if IS_ENABLED(CONFIG_ARCH_SUN8IW16P1)
if (csi_device->id == 0)
cmb_rx_disable(csi_device->id);
#endif
csic_prs_disable(csi_device->id);
if (isp_device->use_isp) {
csic_isp_bridge_disable(0);
bsp_isp_clr_irq_status(isp_device->id, ISP_IRQ_EN_ALL);
bsp_isp_enable(isp_device->id, 0);
bsp_isp_capture_stop(isp_device->id);
}
for (i = 0; i < VIN_MAX_DEV; i++) {
if (vind->vinc[i] == NULL)
continue;
if (!vin_streaming(&vind->vinc[i]->vid_cap))
continue;
if (vind->vinc[i]->csi_sel == csi_device->id) {
vinc = vind->vinc[i];
vinc->vid_cap.frame_delay_cnt = 2;
vipp_disable(vinc->vipp_sel);
vipp_top_clk_en(vinc->vipp_sel, 0);
csic_dma_int_clear_status(vinc->vipp_sel, DMA_INT_ALL);
csic_dma_top_disable(vinc->vipp_sel);
}
}
/* ****************start****************** */
for (i = 0; i < VIN_MAX_DEV; i++) {
if (vind->vinc[i] == NULL)
continue;
if (!vin_streaming(&vind->vinc[i]->vid_cap))
continue;
if (vind->vinc[i]->csi_sel == csi_device->id) {
vinc = vind->vinc[i];
csic_dma_top_enable(vinc->vipp_sel);
vipp_top_clk_en(vinc->vipp_sel, 1);
vipp_enable(vinc->vipp_sel);
vinc->vin_status.frame_cnt = 0;
vinc->vin_status.lost_cnt = 0;
}
}
if (isp_device->use_isp) {
bsp_isp_enable(isp_device->id, 1);
bsp_isp_set_para_ready(isp_device->id, PARA_READY);
bsp_isp_set_input_fmt(isp_device->id, input_seq);
bsp_isp_capture_start(isp_device->id);
isp_device->isp_frame_number = 0;
csic_isp_bridge_enable(0);
}
csic_prs_enable(csi_device->id);
#if IS_ENABLED(CONFIG_ARCH_SUN8IW16P1)
if (vinc->mipi_sel == 0)
cmb_rx_enable(vinc->mipi_sel);
#endif
csic_prs_capture_start(csi_device->id, csi_device->bus_info.ch_total_num, &mode);
isp_device->subdev.entity.stream_count = isp_stream_count;
mutex_unlock(&cap->vdev.entity.graph_obj.mdev->graph_mutex);
#else /* else ISP_600 */
struct v4l2_subdev *isp = cap->pipe.sd[VIN_IND_ISP];
struct v4l2_subdev *sensor = cap->pipe.sd[VIN_IND_SENSOR];
struct isp_dev *isp_device = v4l2_get_subdevdata(isp);
int input_seq;
int sensor_fmt_code = 0;
int ret;
ret = v4l2_s_ctrl(NULL, sensor->ctrl_handler, &c);
v4l2_subdev_call(sensor, core, ioctl, VIDIOC_VIN_GET_SENSOR_CODE, &sensor_fmt_code);
if (sensor_fmt_code <= 0) {
vin_err("cannot get sensor code!\n");
} else {
switch (sensor_fmt_code) {
case MEDIA_BUS_FMT_SBGGR8_1X8:
case MEDIA_BUS_FMT_SBGGR10_1X10:
case MEDIA_BUS_FMT_SBGGR12_1X12:
input_seq = ISP_BGGR;
break;
case MEDIA_BUS_FMT_SGBRG8_1X8:
case MEDIA_BUS_FMT_SGBRG10_1X10:
case MEDIA_BUS_FMT_SGBRG12_1X12:
input_seq = ISP_GBRG;
break;
case MEDIA_BUS_FMT_SGRBG8_1X8:
case MEDIA_BUS_FMT_SGRBG10_1X10:
case MEDIA_BUS_FMT_SGRBG12_1X12:
input_seq = ISP_GRBG;
break;
case MEDIA_BUS_FMT_SRGGB8_1X8:
case MEDIA_BUS_FMT_SRGGB10_1X10:
case MEDIA_BUS_FMT_SRGGB12_1X12:
input_seq = ISP_RGGB;
break;
default:
input_seq = ISP_BGGR;
break;
}
vin_log(VIN_LOG_VIDEO, "sensor_flip_option sensor_fmt=%d\n", input_seq);
isp_device->isp_fmt->infmt = input_seq;
}
cap->frame_delay_cnt = 3;
#endif
return ret;
}
static int vin_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct vin_vid_cap *cap = container_of(ctrl->handler, struct vin_vid_cap, ctrl_handler);
struct sensor_instance *inst = get_valid_sensor(cap->vinc);
struct v4l2_subdev *sensor = cap->pipe.sd[VIN_IND_SENSOR];
struct v4l2_subdev *flash = cap->pipe.sd[VIN_IND_FLASH];
struct v4l2_subdev *act = cap->pipe.sd[VIN_IND_ACTUATOR];
struct v4l2_subdev *isp = cap->pipe.sd[VIN_IND_ISP];
struct actuator_ctrl_word_t vcm_ctrl;
struct v4l2_control c;
#if !IS_ENABLED(CONFIG_ENABLE_SENSOR_FLIP_OPTION)
struct csic_dma_flip flip;
#endif
int ret = 0;
/*
* make sure g_ctrl will get the value that hardware is using
* so that ctrl->flags should be V4L2_CTRL_FLAG_VOLATILE, after s_ctrl
*/
switch (ctrl->id) {
case V4L2_CID_VFLIP:
case V4L2_CID_HFLIP:
case V4L2_CID_EXPOSURE:
case V4L2_CID_EXPOSURE_ABSOLUTE:
case V4L2_CID_GAIN:
ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
break;
default:
break;
}
c.id = ctrl->id;
c.value = ctrl->val;
switch (ctrl->id) {
#if IS_ENABLED(CONFIG_ENABLE_SENSOR_FLIP_OPTION)
case V4L2_CID_VFLIP:
if (!vin_streaming(cap)) {
vin_err("cannot set sensor flip before stream on!\n");
return -1;
}
cap->vinc->sensor_vflip = c.value;
ret = sensor_flip_option(cap, c);
return ret;
case V4L2_CID_HFLIP:
if (!vin_streaming(cap)) {
vin_err("cannot set sensor flip before stream on!\n");
return -1;
}
cap->vinc->sensor_hflip = c.value;
ret = sensor_flip_option(cap, c);
return ret;
#else
case V4L2_CID_VFLIP:
if (cap->frame.fmt.fourcc == V4L2_PIX_FMT_LBC_2_0X ||
cap->frame.fmt.fourcc == V4L2_PIX_FMT_LBC_2_5X ||
cap->frame.fmt.fourcc == V4L2_PIX_FMT_LBC_1_0X) {
vin_warn("when out fmt is LBC, FLIP is not support!\n");
return -1;
}
cap->vinc->vflip = c.value;
if (!vin_lpm(cap)) {
flip.hflip_en = cap->vinc->hflip;
flip.vflip_en = cap->vinc->vflip;
csic_dma_flip_en(cap->vinc->vipp_sel, &flip);
__osd_reg_setup(cap->vinc, &cap->osd);
return 0;
} else {
vin_err("cannot set vflip before s_input, in low power mode!\n");
return -1;
}
case V4L2_CID_HFLIP:
if (cap->frame.fmt.fourcc == V4L2_PIX_FMT_LBC_2_0X ||
cap->frame.fmt.fourcc == V4L2_PIX_FMT_LBC_2_5X ||
cap->frame.fmt.fourcc == V4L2_PIX_FMT_LBC_1_0X) {
vin_warn("when out fmt is LBC, FLIP is not support!\n");
return -1;
}
cap->vinc->hflip = c.value;
if (!vin_lpm(cap)) {
flip.hflip_en = cap->vinc->hflip;
flip.vflip_en = cap->vinc->vflip;
csic_dma_flip_en(cap->vinc->vipp_sel, &flip);
__osd_reg_setup(cap->vinc, &cap->osd);
return 0;
} else {
vin_err("cannot set hflip before s_input, in low power mode!\n");
return -1;
}
#endif
default:
break;
}
/*
* make sure g_ctrl will get the value that hardware is using
* so that ctrl->flags should be V4L2_CTRL_FLAG_VOLATILE, after s_ctrl
*/
switch (ctrl->id) {
case V4L2_CID_EXPOSURE:
case V4L2_CID_EXPOSURE_ABSOLUTE:
case V4L2_CID_GAIN:
ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
break;
default:
break;
}
if (inst->is_isp_used && inst->is_bayer_raw) {
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
case V4L2_CID_CONTRAST:
case V4L2_CID_SATURATION:
case V4L2_CID_HUE:
case V4L2_CID_AUTO_WHITE_BALANCE:
case V4L2_CID_EXPOSURE:
case V4L2_CID_AUTOGAIN:
case V4L2_CID_GAIN:
case V4L2_CID_POWER_LINE_FREQUENCY:
case V4L2_CID_HUE_AUTO:
case V4L2_CID_WHITE_BALANCE_TEMPERATURE:
case V4L2_CID_SHARPNESS:
case V4L2_CID_CHROMA_AGC:
case V4L2_CID_COLORFX:
case V4L2_CID_AUTOBRIGHTNESS:
case V4L2_CID_BAND_STOP_FILTER:
case V4L2_CID_ILLUMINATORS_1:
case V4L2_CID_ILLUMINATORS_2:
case V4L2_CID_EXPOSURE_AUTO:
case V4L2_CID_EXPOSURE_ABSOLUTE:
case V4L2_CID_EXPOSURE_AUTO_PRIORITY:
case V4L2_CID_FOCUS_ABSOLUTE:
case V4L2_CID_FOCUS_RELATIVE:
case V4L2_CID_FOCUS_AUTO:
case V4L2_CID_AUTO_EXPOSURE_BIAS:
case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE:
case V4L2_CID_WIDE_DYNAMIC_RANGE:
case V4L2_CID_IMAGE_STABILIZATION:
case V4L2_CID_ISO_SENSITIVITY:
case V4L2_CID_ISO_SENSITIVITY_AUTO:
case V4L2_CID_EXPOSURE_METERING:
case V4L2_CID_SCENE_MODE:
case V4L2_CID_3A_LOCK:
case V4L2_CID_AUTO_FOCUS_START:
case V4L2_CID_AUTO_FOCUS_STOP:
case V4L2_CID_AUTO_FOCUS_RANGE:
case V4L2_CID_AUTO_FOCUS_INIT:
case V4L2_CID_AUTO_FOCUS_RELEASE:
case V4L2_CID_GSENSOR_ROTATION:
case V4L2_CID_TAKE_PICTURE:
ret = v4l2_s_ctrl(NULL, isp->ctrl_handler, &c);
break;
case V4L2_CID_FLASH_LED_MODE:
ret = v4l2_s_ctrl(NULL, isp->ctrl_handler, &c);
if (flash)
ret = v4l2_s_ctrl(NULL, flash->ctrl_handler, &c);
break;
case V4L2_CID_FLASH_LED_MODE_V1:
ret = v4l2_s_ctrl(NULL, isp->ctrl_handler, &c);
if (flash)
ret = v4l2_s_ctrl(NULL, flash->ctrl_handler, &c);
break;
default:
ret = -EINVAL;
break;
}
} else {
switch (ctrl->id) {
case V4L2_CID_FOCUS_ABSOLUTE:
vcm_ctrl.code = ctrl->val;
vcm_ctrl.sr = 0x0;
ret = v4l2_subdev_call(act, core, ioctl, ACT_SET_CODE, &vcm_ctrl);
break;
case V4L2_CID_FLASH_LED_MODE:
if (flash)
ret = v4l2_s_ctrl(NULL, flash->ctrl_handler, &c);
break;
case V4L2_CID_AUTO_FOCUS_START:
if (flash)
sunxi_flash_check_to_start(flash, SW_CTRL_TORCH_ON);
ret = v4l2_s_ctrl(NULL, sensor->ctrl_handler, &c);
break;
case V4L2_CID_AUTO_FOCUS_STOP:
if (flash)
sunxi_flash_stop(flash);
ret = v4l2_s_ctrl(NULL, sensor->ctrl_handler, &c);
break;
case V4L2_CID_AE_WIN_X1:
ret = __vin_sensor_set_ae_win(cap);
break;
case V4L2_CID_AF_WIN_X1:
ret = __vin_sensor_set_af_win(cap);
break;
case V4L2_CID_AUTO_EXPOSURE_BIAS:
c.value = ctrl->val;
ret = v4l2_s_ctrl(NULL, sensor->ctrl_handler, &c);
break;
default:
ret = v4l2_s_ctrl(NULL, sensor->ctrl_handler, &c);
break;
}
}
return ret;
}
#if IS_ENABLED(CONFIG_COMPAT)
static long vin_compat_ioctl32(struct file *file, unsigned int cmd,
unsigned long arg)
{
void __user *up = compat_ptr(arg);
long err = 0;
err = video_ioctl2(file, cmd, (unsigned long)up);
return err;
}
#endif
/* ------------------------------------------------------------------
*File operations for the device
*------------------------------------------------------------------ */
static const struct v4l2_ctrl_ops vin_ctrl_ops = {
.g_volatile_ctrl = vin_g_volatile_ctrl,
.s_ctrl = vin_s_ctrl,
.try_ctrl = vin_try_ctrl,
};
static const struct v4l2_file_operations vin_fops = {
.owner = THIS_MODULE,
.open = vin_open,
.release = vin_close,
.read = vb2_fop_read,
.poll = vin_poll,
.unlocked_ioctl = video_ioctl2,
#if IS_ENABLED(CONFIG_COMPAT)
.compat_ioctl32 = vin_compat_ioctl32,
#endif
.mmap = vb2_fop_mmap,
};
static const struct v4l2_ioctl_ops vin_ioctl_ops = {
.vidioc_querycap = vidioc_querycap,
.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap_mplane,
.vidioc_enum_framesizes = vidioc_enum_framesizes,
.vidioc_enum_frameintervals = vidioc_enum_frameintervals,
.vidioc_g_fmt_vid_cap_mplane = vidioc_g_fmt_vid_cap_mplane,
.vidioc_try_fmt_vid_cap_mplane = vidioc_try_fmt_vid_cap_mplane,
.vidioc_s_fmt_vid_cap_mplane = vidioc_s_fmt_vid_cap_mplane,
.vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_overlay,
.vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay,
.vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay,
.vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay,
.vidioc_overlay = vidioc_overlay,
.vidioc_reqbufs = vb2_ioctl_reqbufs,
.vidioc_querybuf = vb2_ioctl_querybuf,
.vidioc_qbuf = vb2_ioctl_qbuf,
.vidioc_dqbuf = vb2_ioctl_dqbuf,
.vidioc_expbuf = vb2_ioctl_expbuf,
.vidioc_enum_input = vidioc_enum_input,
.vidioc_g_input = vidioc_g_input,
.vidioc_s_input = vidioc_s_input,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
.vidioc_g_pixelaspect = vidioc_g_pixelaspect,
.vidioc_g_parm = vidioc_g_parm,
.vidioc_s_parm = vidioc_s_parm,
.vidioc_g_selection = vidioc_g_selection,
.vidioc_s_selection = vidioc_s_selection,
.vidioc_s_dv_timings = vidioc_s_dv_timings,
.vidioc_g_dv_timings = vidioc_g_dv_timings,
.vidioc_query_dv_timings = vidioc_query_dv_timings,
.vidioc_enum_dv_timings = vidioc_enum_dv_timings,
.vidioc_dv_timings_cap = vidioc_dv_timings_cap,
.vidioc_default = vin_param_handler,
.vidioc_subscribe_event = vin_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
.vidioc_s_edid = vidioc_s_edid,
};
#if IS_ENABLED(CONFIG_VIDEO_SUNXI_VIN_SPECIAL)
#define VIN_VIDEO_SOURCE_WIDTH_DEFAULT 1280
#define VIN_VIDEO_SOURCE_HEIGHT_DEFAULT 720
struct device *vin_get_dev(int id)
{
struct vin_core *vinc = vin_core_gbl[id];
return get_device(&vinc->pdev->dev);
}
EXPORT_SYMBOL(vin_get_dev);
int vin_s_ctrl_special(int id, unsigned int ctrl_id, int val)
{
struct vin_core *vinc = vin_core_gbl[id];
struct vin_vid_cap *cap = &vinc->vid_cap;
struct v4l2_control c;
c.id = ctrl_id;
c.value = val;
return v4l2_s_ctrl(NULL, &cap->ctrl_handler, &c);
}
EXPORT_SYMBOL(vin_s_ctrl_special);
int vin_open_special(int id)
{
struct vin_core *vinc = vin_core_gbl[id];
struct list_head *active = &vinc->vid_cap.vidq_active;
struct list_head *done = &vinc->vid_cap.vidq_done;
struct vin_vid_cap *cap = &vinc->vid_cap;
if (vin_busy(&vinc->vid_cap)) {
vin_err("device open busy\n");
return -EBUSY;
}
#if IS_ENABLED(CONFIG_RV_RUN_CAR_REVERSE)
if (CONTROL_BY_RTOS == vinc->rpmsg.control) {
vin_err("video%d is controlling by rtos\n", vinc->id);
return -EBUSY;
}
vinc_status_rpmsg_send(ARM_VIN_START, &vinc->rpmsg);
#endif
INIT_LIST_HEAD(active);
INIT_LIST_HEAD(done);
vinc->vid_cap.special_active = 1;
set_bit(VIN_BUSY, &vinc->vid_cap.state);
set_bit(VIN_LPM, &cap->state);
#if IS_ENABLED(CONFIG_DEVFREQ_DRAM_FREQ_WITH_SOFT_NOTIFY)
dramfreq_master_access(MASTER_CSI, true);
#endif
return 0;
}
EXPORT_SYMBOL(vin_open_special);
int vin_s_input_special(int id, int i)
{
struct vin_core *vinc = vin_core_gbl[id];
struct vin_md *vind = NULL;
struct vin_vid_cap *cap = NULL;
struct modules_config *module = NULL;
struct sensor_instance *inst = NULL;
struct sensor_info *info = NULL;
int valid_idx = -1;
int ret;
if (!vinc) {
vin_err("%s vinc %px\n", __FUNCTION__, vinc);
return -EINVAL;
}
if (!vinc->v4l2_dev) {
vin_err("%s vinc->v4l2_dev %px\n", __FUNCTION__, vinc->v4l2_dev);
return -EINVAL;
}
if (!vinc->v4l2_dev->dev) {
vin_err("%s vinc->v4l2_dev->dev %px\n", __FUNCTION__, vinc->v4l2_dev->dev);
return -EINVAL;
}
vind = dev_get_drvdata(vinc->v4l2_dev->dev);
cap = &vinc->vid_cap;
i = i > 1 ? 0 : i;
if (i == 0)
vinc->sensor_sel = vinc->rear_sensor;
else
vinc->sensor_sel = vinc->front_sensor;
module = &vind->modules[vinc->sensor_sel];
valid_idx = module->sensors.valid_idx;
if (valid_idx == NO_VALID_SENSOR) {
vin_err("there is no valid sensor\n");
return -EINVAL;
}
if (__vin_sensor_setup_link(vinc, module, valid_idx, 1) < 0) {
vin_err("sensor setup link failed\n");
return -EINVAL;
}
if (__csi_isp_setup_link(vinc, 1) < 0) {
vin_err("csi&isp setup link failed\n");
return -EINVAL;
}
ret = vin_pipeline_call(vinc, open, &cap->pipe, &cap->vdev.entity, true);
if (ret < 0) {
vin_err("vin pipeline open failed (%d)!\n", ret);
return ret;
}
inst = &module->sensors.inst[valid_idx];
sunxi_isp_sensor_type(cap->pipe.sd[VIN_IND_ISP], inst->is_isp_used);
vinc->support_raw = inst->is_isp_used;
ret = v4l2_subdev_call(cap->pipe.sd[VIN_IND_ISP], core, init, 1);
if (ret < 0) {
vin_err("ISP init error at %s\n", __func__);
return ret;
}
ret = v4l2_subdev_call(cap->pipe.sd[VIN_IND_SCALER], core, init, 1);
if (ret < 0) {
vin_err("SCALER init error at %s\n", __func__);
return ret;
}
/* save exp and gain for reopen, sensor init may reset gain to 0, so save before init! */
info = container_of(cap->pipe.sd[VIN_IND_SENSOR], struct sensor_info, sd);
if (info) {
vinc->exp_gain.exp_val = info->exp;
vinc->exp_gain.gain_val = info->gain;
vinc->stream_idx = info->stream_seq + 2;
}
ret = v4l2_subdev_call(cap->pipe.sd[VIN_IND_SENSOR], core, init, 1);
if (ret) {
vin_err("sensor initial error when selecting target device!\n");
return ret;
}
clear_bit(VIN_LPM, &cap->state);
vinc->hflip = inst->hflip;
vinc->vflip = inst->vflip;
return ret;
}
EXPORT_SYMBOL(vin_s_input_special);
int vin_s_parm_special(int id, void *priv,
struct v4l2_streamparm *parms)
{
struct vin_core *vinc = vin_core_gbl[id];
struct vin_vid_cap *cap = &vinc->vid_cap;
struct sensor_instance *inst = get_valid_sensor(vinc);
int ret = 0;
if (parms->parm.capture.capturemode != V4L2_MODE_VIDEO &&
parms->parm.capture.capturemode != V4L2_MODE_IMAGE &&
parms->parm.capture.capturemode != V4L2_MODE_PREVIEW) {
parms->parm.capture.capturemode = V4L2_MODE_PREVIEW;
}
cap->capture_mode = parms->parm.capture.capturemode;
vinc->large_image = parms->parm.capture.reserved[2];
ret = sensor_s_parm(cap->pipe.sd[VIN_IND_SENSOR], parms);
if (ret < 0)
vin_warn("v4l2 subdev sensor s_parm error!\n");
ret = sunxi_csi_subdev_s_parm(cap->pipe.sd[VIN_IND_CSI], parms);
if (ret < 0)
vin_warn("v4l2 subdev csi s_parm error!\n");
if (inst->is_isp_used) {
ret = sunxi_isp_s_parm(cap->pipe.sd[VIN_IND_ISP], parms);
if (ret < 0)
vin_warn("v4l2 subdev isp s_parm error!\n");
}
return ret;
}
EXPORT_SYMBOL(vin_s_parm_special);
int vin_tvin_special(int id, struct tvin_init_info *info)
{
struct vin_core *vinc = vin_core_gbl[id];
int ret = 0;
if (__tvin_info_check(info))
return -1;
vinc->tvin.work_mode = info->work_mode;
vinc->tvin.input_fmt = info->input_fmt[info->ch_id];
vinc->tvin.flag = true;
ret = v4l2_subdev_call(vinc->vid_cap.pipe.sd[VIN_IND_SENSOR], core, ioctl,
SENSOR_TVIN_INIT, info);
if (ret)
vin_err("sensor tvin init fail!\n");
if (vinc->vid_cap.pipe.sd[VIN_IND_MIPI] != NULL) {
ret = v4l2_subdev_call(vinc->vid_cap.pipe.sd[VIN_IND_MIPI], core, ioctl,
MIPI_TVIN_INIT, info);
if (ret)
vin_err("mipi tvin init fail!\n");
}
ret = v4l2_subdev_call(vinc->vid_cap.pipe.sd[VIN_IND_CSI], core, ioctl,
PARSER_TVIN_INIT, info);
if (ret)
vin_err("csi tvin init fail!\n");
vin_log(VIN_LOG_FMT, "%s mode %d, fmt %d\n", __func__,
vinc->tvin.work_mode, vinc->tvin.input_fmt);
return ret;
}
EXPORT_SYMBOL(vin_tvin_special);
int vin_close_special(int id)
{
struct vin_core *vinc = vin_core_gbl[id];
struct vin_md *vind = dev_get_drvdata(vinc->v4l2_dev->dev);
struct vin_vid_cap *cap = &vinc->vid_cap;
struct list_head *active = &vinc->vid_cap.vidq_active;
struct list_head *done = &vinc->vid_cap.vidq_done;
struct modules_config *module = &vind->modules[vinc->sensor_sel];
int valid_idx = module->sensors.valid_idx;
int ret;
INIT_LIST_HEAD(active);
INIT_LIST_HEAD(done);
vinc->vid_cap.special_active = 0;
if (!vin_busy(cap)) {
vin_warn("video%d device have been closed!\n", vinc->id);
return 0;
}
#if IS_ENABLED(CONFIG_RV_RUN_CAR_REVERSE)
vinc_status_rpmsg_send(ARM_VIN_STOP, &vinc->rpmsg);
#endif
mutex_lock(&cap->vdev.entity.graph_obj.mdev->graph_mutex);
if (!cap->pipe.sd[VIN_IND_SENSOR] || !cap->pipe.sd[VIN_IND_SENSOR]->entity.use_count) {
clear_bit(VIN_BUSY, &cap->state);
vin_err("%s is not used, video%d cannot be close!\n", cap->pipe.sd[VIN_IND_SENSOR]->name, vinc->id);
mutex_unlock(&cap->vdev.entity.graph_obj.mdev->graph_mutex);
return -1;
}
if (vin_streaming(cap)) {
#if VIN_FALSE
#if IS_ENABLED(CONFIG_ISP_SERVER_MELIS)
isp = container_of(cap->pipe.sd[VIN_IND_ISP], struct isp_dev, subdev);
if (isp->gtm_type == 4) {
if ((vinc->id == 0) && (!check_ldci_video_relate(vinc->id, LDCI0_VIDEO_CHN))) {
disable_ldci_video(LDCI0_VIDEO_CHN);
} else if ((vinc->id == 1) && (!check_ldci_video_relate(vinc->id, LDCI1_VIDEO_CHN))) {
disable_ldci_video(LDCI1_VIDEO_CHN);
}
}
#endif
#endif
clear_bit(VIN_STREAM, &cap->state);
vin_pipeline_call(vinc, set_stream, &cap->pipe, 0);
}
if (!vin_lpm(cap)) {
set_bit(VIN_LPM, &cap->state);
__csi_isp_setup_link(vinc, 0);
__vin_sensor_setup_link(vinc, module, valid_idx, 0);
}
ret = vin_pipeline_call(vinc, close, &cap->pipe);
if (ret)
vin_err("vin pipeline close failed!\n");
ret = v4l2_subdev_call(cap->pipe.sd[VIN_IND_ISP], core, init, 0);
/* software */
clear_bit(VIN_BUSY, &cap->state);
mutex_unlock(&cap->vdev.entity.graph_obj.mdev->graph_mutex);
#if IS_ENABLED(CONFIG_DEVFREQ_DRAM_FREQ_WITH_SOFT_NOTIFY)
dramfreq_master_access(MASTER_CSI, false);
#endif
vin_log(VIN_LOG_VIDEO, "video%d close\n", vinc->id);
return 0;
}
EXPORT_SYMBOL(vin_close_special);
int vin_s_fmt_special(int id, struct v4l2_format *f)
{
struct vin_core *vinc = vin_core_gbl[id];
return __vin_set_fmt(vinc, f);
}
EXPORT_SYMBOL(vin_s_fmt_special);
int vin_g_fmt_special(int id, struct v4l2_format *f)
{
struct vin_core *vinc = vin_core_gbl[id];
struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
int i;
pixm->width = vinc->vid_cap.frame.o_width;
pixm->height = vinc->vid_cap.frame.o_height;
pixm->field = V4L2_FIELD_NONE;
pixm->pixelformat = vinc->vid_cap.frame.fmt.fourcc;
pixm->colorspace = vinc->vid_cap.frame.fmt.color;/* V4L2_COLORSPACE_JPEG; */
pixm->num_planes = vinc->vid_cap.frame.fmt.memplanes;
for (i = 0; i < pixm->num_planes; ++i) {
pixm->plane_fmt[i].bytesperline = vinc->vid_cap.frame.bytesperline[i];
pixm->plane_fmt[i].sizeimage = vinc->vid_cap.frame.payload[i];
}
return 0;
}
EXPORT_SYMBOL(vin_g_fmt_special);
int vin_g_fmt_special_ext(int id, struct v4l2_format *f)
{
struct vin_core *vinc = vin_core_gbl[id];
/* size resolution should be configurated flexiable for various platform*/
#if defined VIN_VIDEO_SOURCE_WIDTH
f->fmt.pix.width = VIN_VIDEO_SOURCE_WIDTH;
#else
f->fmt.pix.width = VIN_VIDEO_SOURCE_WIDTH_DEFAULT;
#endif
#if defined VIN_VIDEO_SOURCE_HEIGHT
f->fmt.pix.height = VIN_VIDEO_SOURCE_HEIGHT;
#else
f->fmt.pix.height = VIN_VIDEO_SOURCE_HEIGHT_DEFAULT;
#endif
f->fmt.pix.field = vinc->vid_cap.frame.fmt.field;
f->fmt.pix.pixelformat = vinc->vid_cap.frame.fmt.mbus_code;
return 0;
}
EXPORT_SYMBOL(vin_g_fmt_special_ext);
int vin_dqbuffer_special(int id, struct vin_buffer **buf)
{
struct vin_core *vinc = vin_core_gbl[id];
struct list_head *done = &vinc->vid_cap.vidq_done;
struct vin_vid_cap *cap = &vinc->vid_cap;
int ret = 0;
unsigned long flags = 0;
spin_lock_irqsave(&cap->slock, flags);
/* Dequeue buffers have handled done */
if (!list_empty(done)) {
*buf = list_first_entry(done, struct vin_buffer, list);
list_del(&((*buf)->list));
(*buf)->state = VB2_BUF_STATE_DEQUEUED;
spin_unlock_irqrestore(&cap->slock, flags);
dma_buf_unmap_attachment((*buf)->attachment, (*buf)->sgt, DMA_FROM_DEVICE);
dma_buf_detach((*buf)->dmabuf, (*buf)->attachment);
dma_buf_put((*buf)->dmabuf);
} else {
spin_unlock_irqrestore(&cap->slock, flags);
ret = -1;
}
return ret;
}
EXPORT_SYMBOL(vin_dqbuffer_special);
int vin_qbuffer_special(int id, struct vin_buffer *buf)
{
struct vin_core *vinc = vin_core_gbl[id];
struct vin_vid_cap *cap = &vinc->vid_cap;
struct dma_buf_attachment *attachment;
struct device *dev = vin_get_dev(id);
struct sg_table *sgt;
unsigned long flags = 0;
int ret = 0;
if (buf == NULL) {
vin_err("vin buf is NULL, cannot qbuf\n");
return -1;
}
buf->dmabuf = dma_buf_get(buf->dmabuf_fd);
attachment = dma_buf_attach(buf->dmabuf, dev);
if (IS_ERR(attachment)) {
pr_err("dma_buf_attach failed\n");
goto err_buf_put;
}
sgt = dma_buf_map_attachment(attachment, DMA_BIDIRECTIONAL);
if (IS_ERR_OR_NULL(sgt)) {
pr_warn("dma_buf_map_attachment failed\n");
goto err_buf_detach;
}
buf->attachment = attachment;
buf->sgt = sgt;
buf->paddr = (void *)sg_dma_address(sgt->sgl);
spin_lock_irqsave(&cap->slock, flags);
list_add_tail(&buf->list, &cap->vidq_active);
buf->state = VB2_BUF_STATE_QUEUED;
spin_unlock_irqrestore(&cap->slock, flags);
return ret;
err_buf_detach:
dma_buf_detach(buf->dmabuf, attachment);
err_buf_put:
dma_buf_put(buf->dmabuf);
return -ENOMEM;
}
EXPORT_SYMBOL(vin_qbuffer_special);
int vin_rt_dqbuffer_special(int id, struct vin_buffer **buf)
{
struct vin_core *vinc = vin_core_gbl[id];
struct list_head *done = &vinc->vid_cap.vidq_done;
struct vin_vid_cap *cap = &vinc->vid_cap;
int ret = 0;
unsigned long flags = 0;
spin_lock_irqsave(&cap->slock, flags);
/* Release all active buffers */
if (!list_empty(done)) {
*buf = list_first_entry(done, struct vin_buffer, list);
list_del(&((*buf)->list));
(*buf)->state = VB2_BUF_STATE_DEQUEUED;
} else {
ret = -1;
}
spin_unlock_irqrestore(&cap->slock, flags);
return ret;
}
EXPORT_SYMBOL(vin_rt_dqbuffer_special);
int vin_rt_qbuffer_special(int id, struct vin_buffer *buf)
{
struct vin_core *vinc = vin_core_gbl[id];
struct vin_vid_cap *cap = &vinc->vid_cap;
unsigned long flags = 0;
int ret = 0;
if (buf == NULL) {
vin_err("buf is NULL, cannot qbuf\n");
return -1;
}
spin_lock_irqsave(&cap->slock, flags);
list_add_tail(&buf->list, &cap->vidq_active);
buf->qbufed = 1;
buf->state = VB2_BUF_STATE_QUEUED;
spin_unlock_irqrestore(&cap->slock, flags);
return ret;
}
EXPORT_SYMBOL(vin_rt_qbuffer_special);
int vin_streamon_special(int video_id, enum v4l2_buf_type i)
{
struct vin_core *vinc = vin_core_gbl[video_id];
struct vin_vid_cap *cap = &vinc->vid_cap;
struct vin_md *vind = dev_get_drvdata(vinc->v4l2_dev->dev);
struct modules_config *module = &vind->modules[vinc->sensor_sel];
int valid_idx = module->sensors.valid_idx;
__maybe_unused struct isp_dev *isp = NULL;
int ret = 0;
int wth;
if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
ret = -EINVAL;
goto streamon_error;
}
if (vin_streaming(cap)) {
vin_err("stream has been already on\n");
ret = -1;
goto streamon_error;
}
switch (cap->frame.fmt.fourcc) {
case V4L2_PIX_FMT_LBC_2_0X:
case V4L2_PIX_FMT_LBC_2_5X:
case V4L2_PIX_FMT_LBC_1_0X:
case V4L2_PIX_FMT_LBC_1_5X:
lbc_mode_select(&cap->lbc_cmp, cap->frame.fmt.fourcc);
wth = roundup(cap->frame.o_width, 32);
if (cap->lbc_cmp.is_lossy) {
cap->lbc_cmp.line_tar_bits[0] = roundup(cap->lbc_cmp.cmp_ratio_even * wth * cap->lbc_cmp.bit_depth/1000, 512);
cap->lbc_cmp.line_tar_bits[1] = roundup(cap->lbc_cmp.cmp_ratio_odd * wth * cap->lbc_cmp.bit_depth/500, 512);
} else {
cap->lbc_cmp.line_tar_bits[0] = roundup(wth * cap->lbc_cmp.bit_depth * 1 + (wth * 1 / 16 * 2), 512);
cap->lbc_cmp.line_tar_bits[1] = roundup(wth * cap->lbc_cmp.bit_depth * 2 + (wth * 2 / 16 * 2), 512);
}
break;
default:
break;
}
#if VIN_FALSE
schedule_work(&vinc->vid_cap.s_stream_task);
#else
if (vin_lpm(cap)) {
if (__vin_sensor_setup_link(vinc, module, valid_idx, 1) < 0) {
vin_err("sensor setup link failed\n");
return -EINVAL;
}
if (__csi_isp_setup_link(vinc, 1) < 0) {
vin_err("csi&isp setup link failed\n");
return -EINVAL;
}
clear_bit(VIN_LPM, &cap->state);
}
mutex_lock(&cap->vdev.entity.graph_obj.mdev->graph_mutex);
ret = vin_pipeline_call(cap->vinc, set_stream, &cap->pipe, cap->vinc->stream_idx);
if (ret < 0)
vin_err("video%d %s error!\n", vinc->id, __func__);
set_bit(VIN_STREAM, &cap->state);
/* set saved exp and gain for reopen, you can call the api in sensor_reg_init */
/*
if (cap->vinc->exp_gain.exp_val && cap->vinc->exp_gain.gain_val) {
v4l2_subdev_call(cap->pipe.sd[VIN_IND_SENSOR], core, ioctl,
VIDIOC_VIN_SENSOR_EXP_GAIN, &cap->vinc->exp_gain);
}
*/
mutex_unlock(&cap->vdev.entity.graph_obj.mdev->graph_mutex);
#if IS_ENABLED(CONFIG_ISP_SERVER_MELIS)
isp = container_of(cap->pipe.sd[VIN_IND_ISP], struct isp_dev, subdev);
if (isp->gtm_type == 4) {
if ((vinc->id == 0) && (!check_ldci_video_relate(vinc->id, LDCI0_VIDEO_CHN))) {
enable_ldci_video(LDCI0_VIDEO_CHN);
} else if ((vinc->id == 1) && (!check_ldci_video_relate(vinc->id, LDCI1_VIDEO_CHN))) {
enable_ldci_video(LDCI1_VIDEO_CHN);
} else if ((vinc->id == 2) && (!check_ldci_video_relate(vinc->id, LDCI2_VIDEO_CHN))) {
enable_ldci_video(LDCI2_VIDEO_CHN);
}
}
#endif
#endif
streamon_error:
return ret;
}
EXPORT_SYMBOL(vin_streamon_special);
int vin_force_reset_buffer(int video_id)
{
struct vin_core *vinc = vin_core_gbl[video_id];
struct vin_vid_cap *cap = &vinc->vid_cap;
struct vin_buffer *buf;
unsigned long flags = 0;
spin_lock_irqsave(&cap->slock, flags);
while (!list_empty(&cap->vidq_active)) {
buf =
list_first_entry(&cap->vidq_active, struct vin_buffer, list);
list_del(&buf->list);
list_add(&buf->list, &cap->vidq_done);
}
spin_unlock_irqrestore(&cap->slock, flags);
return 0;
}
EXPORT_SYMBOL(vin_force_reset_buffer);
int vin_streamoff_special(int video_id, enum v4l2_buf_type i)
{
struct vin_core *vinc = vin_core_gbl[video_id];
struct vin_md *vind = dev_get_drvdata(vinc->v4l2_dev->dev);
struct vin_vid_cap *cap = &vinc->vid_cap;
struct modules_config *module = &vind->modules[vinc->sensor_sel];
int valid_idx = module->sensors.valid_idx;
__maybe_unused struct isp_dev *isp = NULL;
int ret = 0;
if (!vin_streaming(cap)) {
vin_err("video%d has been already streaming off\n", vinc->id);
goto streamoff_error;
}
#if IS_ENABLED(CONFIG_ISP_SERVER_MELIS)
isp = container_of(cap->pipe.sd[VIN_IND_ISP], struct isp_dev, subdev);
if (isp->gtm_type == 4) {
if ((vinc->id == 0) && (!check_ldci_video_relate(vinc->id, LDCI0_VIDEO_CHN))) {
disable_ldci_video(LDCI0_VIDEO_CHN);
} else if ((vinc->id == 1) && (!check_ldci_video_relate(vinc->id, LDCI1_VIDEO_CHN))) {
disable_ldci_video(LDCI1_VIDEO_CHN);
} else if ((vinc->id == 2) && (!check_ldci_video_relate(vinc->id, LDCI2_VIDEO_CHN))) {
disable_ldci_video(LDCI2_VIDEO_CHN);
}
}
#endif
mutex_lock(&cap->vdev.entity.graph_obj.mdev->graph_mutex);
clear_bit(VIN_STREAM, &cap->state);
vin_pipeline_call(vinc, set_stream, &cap->pipe, 0);
set_bit(VIN_LPM, &cap->state);
__csi_isp_setup_link(vinc, 0);
__vin_sensor_setup_link(vinc, module, valid_idx, 0);
mutex_unlock(&cap->vdev.entity.graph_obj.mdev->graph_mutex);
if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
ret = -EINVAL;
goto streamoff_error;
}
streamoff_error:
return ret;
}
EXPORT_SYMBOL(vin_streamoff_special);
#endif
void vin_register_buffer_done_callback(int id, void *func)
{
struct vin_core *vinc = vin_core_gbl[id];
vinc->vid_cap.vin_buffer_process = func;
}
EXPORT_SYMBOL(vin_register_buffer_done_callback);
int vin_get_encpp_cfg(int id, unsigned char ctrl_id, void *value)
{
#if IS_ENABLED(CONFIG_ISP_SERVER_MELIS)
struct vin_core *vinc = vin_core_gbl[id];
struct isp_dev *isp = v4l2_get_subdevdata(vinc->vid_cap.pipe.sd[VIN_IND_ISP]);
switch (ctrl_id) {
case ISP_CTRL_ENCPP_EN:
*(int *)value = isp->encpp_en;
break;
case ISP_CTRL_ENCPP_STATIC_CFG:
*(struct encpp_static_sharp_config *)value = isp->encpp_static_sharp_cfg;
break;
case ISP_CTRL_ENCPP_DYNAMIC_CFG:
*(struct encpp_dynamic_sharp_config *)value = isp->encpp_dynamic_sharp_cfg;
break;
case ISP_CTRL_ENCODER_3DNR_CFG:
*(struct encoder_3dnr_config *)value = isp->encoder_3dnr_cfg;
break;
case ISP_CTRL_ENCODER_2DNR_CFG:
*(struct encoder_2dnr_config *)value = isp->encoder_2dnr_cfg;
break;
default:
vin_err("%s: Unknown ctrl.\n", __FUNCTION__);
return -1;
}
#endif
return 0;
}
EXPORT_SYMBOL(vin_get_encpp_cfg);
/* must set after vin_s_input_special and before vin_s_parm_special */
int vin_set_ve_online_cfg_special(int id, struct csi_ve_online_cfg *cfg)
{
struct vin_core *vinc = vin_core_gbl[id];
if (!cfg->ve_online_en) {
vinc->ve_online_cfg.ve_online_en = 0;
vinc->ve_online_cfg.dma_buf_num = BK_MUL_BUFFER;
vin_print("ve_online close\n");
return 0;
}
if (vinc->work_mode == BK_OFFLINE) {
vin_err("ve online mode need video%d work in online\n", vinc->id);
return -1;
}
if (vinc->id == CSI_VE_ONLINE_VIDEO) {
memcpy(&vinc->ve_online_cfg, cfg, sizeof(struct csi_ve_online_cfg));
} else {
vin_err("only video%d supply ve online\n", CSI_VE_ONLINE_VIDEO);
return -1;
}
vin_print("ve_online %s, buffer_num is %d\n", vinc->ve_online_cfg.ve_online_en ? "open" : "close", vinc->ve_online_cfg.dma_buf_num);
return 0;
}
EXPORT_SYMBOL(vin_set_ve_online_cfg_special);
/* must set after vin_s_fmt_special and before vin_streamon_special */
int vin_set_tdm_speeddn_cfg_special(int id, struct tdm_speeddn_cfg *cfg)
{
#if defined SUPPORT_ISP_TDM && defined TDM_V200
struct vin_core *vinc = vin_core_gbl[id];
struct vin_vid_cap *cap = &vinc->vid_cap;
struct mipi_dev *mipi = NULL;
struct tdm_rx_dev *tdm_rx = NULL;
struct tdm_dev *tdm = NULL;
if (vinc->mipi_sel != 0xff) {
if (cfg->pix_num == MIPI_TWO_PIXEL && mipi->cmb_csi_cfg.lane_num != 4) {
cfg->pix_num = MIPI_ONE_PIXEL;
vin_warn("mipi %d lane cannot support two pixel, set to one pexel\n", mipi->cmb_csi_cfg.lane_num);
}
if (cfg->pix_num == MIPI_TWO_PIXEL && !cfg->tdm_speed_down_en) {
cfg->tdm_speed_down_en = 1;
vin_warn("when mipi set to two pixel, must open tdm speed_dn\n");
}
mipi = container_of(cap->pipe.sd[VIN_IND_MIPI], struct mipi_dev, subdev);
mipi->cmb_csi_cfg.pix_num = (enum cmb_csi_pix_num)cfg->pix_num;
}
if (vinc->tdm_rx_sel != 0xff) {
tdm_rx = container_of(cap->pipe.sd[VIN_IND_TDM_RX], struct tdm_rx_dev, subdev);
tdm = container_of(tdm_rx, struct tdm_dev, tdm_rx[tdm_rx->id]);
tdm->ws.speed_dn_en = cfg->tdm_speed_down_en;
if (cfg->tdm_tx_valid_num || cfg->tdm_tx_invalid_num) {
tdm->tx_cfg.valid_num = cfg->tdm_tx_valid_num;
tdm->tx_cfg.invalid_num = cfg->tdm_tx_invalid_num;
} else {
tdm->tx_cfg.valid_num = 1;
tdm->tx_cfg.invalid_num = 0;
}
}
#endif
return 0;
}
EXPORT_SYMBOL(vin_set_tdm_speeddn_cfg_special);
int vin_s_fmt_overlay_special(int id, struct v4l2_format *f)
{
struct vin_core *vinc = vin_core_gbl[id];
struct vin_osd *osd = &vinc->vid_cap.osd;
struct v4l2_clip *clip = NULL;
int ret = 0, i = 0;
__osd_win_check(&f->fmt.win);
osd->overlay_en = 0;
osd->cover_en = 0;
if (!f->fmt.win.bitmap) {
if (f->fmt.win.clipcount <= 0) {
osd->orl_en = 0;
goto osd_reset;
}
clip = vmalloc(sizeof(struct v4l2_clip) * f->fmt.win.clipcount * 2);
if (clip == NULL) {
vin_err("%s - Alloc of clip mask failed\n", __func__);
return -ENOMEM;
}
if (!memcpy(clip, f->fmt.win.clips,
sizeof(struct v4l2_clip) * f->fmt.win.clipcount * 2)) {
vfree(clip);
return -EFAULT;
}
/*save rgb in the win top for diff cover*/
osd->orl_width = clip[f->fmt.win.clipcount].c.width;
if (osd->orl_width) {
if (MAX_ORL_NUM) {
osd->orl_en = 1;
osd->orl_cnt = f->fmt.win.clipcount;
} else {
osd->orl_en = 0;
vin_err("VIPP orl is not exist!!\n");
goto osd_reset;
}
}
if (osd->orl_en) {
for (i = 0; i < osd->orl_cnt; i++) {
u8 r, g, b;
osd->orl_win[i] = clip[i].c;
osd->rgb_orl[i] = clip[i + osd->orl_cnt].c.top;
r = (osd->rgb_orl[i] >> 16) & 0xff;
g = (osd->rgb_orl[i] >> 8) & 0xff;
b = osd->rgb_orl[i] & 0xff;
__osd_rgb_to_yuv(r, g, b, &osd->yuv_orl[0][i],
&osd->yuv_orl[1][i], &osd->yuv_orl[2][i]);
}
}
vfree(clip);
}
osd_reset:
osd->is_set = 0;
return ret;
}
EXPORT_SYMBOL(vin_s_fmt_overlay_special);
int vin_overlay_special(int id, unsigned int on)
{
struct vin_core *vinc = vin_core_gbl[id];
struct vin_osd *osd = &vinc->vid_cap.osd;
int i;
int ret = 0;
if (!on) {
for (i = 0; i < 2; i++) {
if (osd->ov_mask[i].phy_addr) {
os_mem_free(&vinc->pdev->dev, &osd->ov_mask[i]);
osd->ov_mask[i].phy_addr = NULL;
osd->ov_mask[i].size = 0;
}
}
osd->ov_set_cnt = 0;
osd->overlay_en = 0;
osd->cover_en = 0;
osd->orl_en = 0;
} else {
if (osd->is_set)
return ret;
}
ret = __osd_reg_setup(vinc, osd);
osd->is_set = 1;
return ret;
}
EXPORT_SYMBOL(vin_overlay_special);
void vin_server_reset_special(int id, int mode_flag, int ir_on, int ir_flash_on)
{
#if IS_ENABLED(CONFIG_ISP_SERVER_MELIS)
struct vin_core *vinc = vin_core_gbl[id];
struct vin_vid_cap *cap = &vinc->vid_cap;
struct ir_switch ir_switch;
unsigned int data[2];
struct isp_dev *isp = container_of(cap->pipe.sd[VIN_IND_ISP], struct isp_dev, subdev);
struct sensor_info *info = container_of(cap->pipe.sd[VIN_IND_SENSOR], struct sensor_info, sd);
if (ir_on && mode_flag == 2) {
if (info->ir_state == NIGHT_STATE)
return;
data[0] = VIN_SET_SERVER_RESET;
data[1] = mode_flag;
isp_rpmsg_send(isp, data, 2 * sizeof(unsigned int));
usleep_range(250000, 260000);/* make sure isp change to black&white mode and then open ircut*/
ir_switch.ir_hold = 0;
ir_switch.ir_on = ir_on;
ir_switch.ir_flash_on = ir_flash_on;
v4l2_subdev_call(cap->pipe.sd[VIN_IND_SENSOR], core, ioctl, VIDIOC_VIN_SET_IR, &ir_switch);
} else if (!ir_on && mode_flag != 2) {
if (info->ir_state == DAY_STATE)
return;
ir_switch.ir_hold = 0;
ir_switch.ir_on = ir_on;
ir_switch.ir_flash_on = ir_flash_on;
v4l2_subdev_call(cap->pipe.sd[VIN_IND_SENSOR], core, ioctl, VIDIOC_VIN_SET_IR, &ir_switch);
usleep_range(350000, 360000);/* make sure ircut off and then change isp to color mode*/
data[0] = VIN_SET_SERVER_RESET;
data[1] = mode_flag;
isp_rpmsg_send(isp, data, 2 * sizeof(unsigned int));
}
#endif
}
EXPORT_SYMBOL(vin_server_reset_special);
int vin_set_isp_attr_cfg_special(int id, void *value)
{
#if IS_ENABLED(CONFIG_ISP_SERVER_MELIS)
int ret = 0;
struct vin_core *vinc = vin_core_gbl[id];
struct isp_cfg_attr_data *attr_cfg_ptr = (struct isp_cfg_attr_data *)value;
ret = vin_set_isp_attr_cfg_ctrl(vinc, attr_cfg_ptr);
return ret;
#else
return 0;
#endif
}
EXPORT_SYMBOL(vin_set_isp_attr_cfg_special);
int vin_get_isp_attr_cfg_special(int id, void *value)
{
#if IS_ENABLED(CONFIG_ISP_SERVER_MELIS)
int ret = 0;
struct vin_core *vinc = vin_core_gbl[id];
struct isp_cfg_attr_data *attr_cfg_ptr = (struct isp_cfg_attr_data *)value;
ret = vin_get_isp_attr_cfg_ctrl(vinc, attr_cfg_ptr);
return ret;
#else
return 0;
#endif
}
EXPORT_SYMBOL(vin_get_isp_attr_cfg_special);
void vin_get_sensor_resolution_special(int id, struct sensor_resolution *sensor_resolution)
{
struct vin_core *vinc = vin_core_gbl[id];
sensor_get_resolution(vinc->vid_cap.pipe.sd[VIN_IND_SENSOR], sensor_resolution);
}
EXPORT_SYMBOL(vin_get_sensor_resolution_special);
void vin_sensor_fps_change_callback(int id, void *func)
{
struct vin_core *vinc = vin_core_gbl[id];
struct vin_vid_cap *cap = &vinc->vid_cap;
struct sensor_info *info = container_of(cap->pipe.sd[VIN_IND_SENSOR], struct sensor_info, sd);
info->sensor_fps_change_callback = func;
}
EXPORT_SYMBOL(vin_sensor_fps_change_callback);
int vin_isp_get_hist_special(int id, unsigned int *hist)
{
#if IS_ENABLED(CONFIG_ISP_SERVER_MELIS)
struct vin_core *vinc = vin_core_gbl[id];
struct isp_dev *isp = container_of(vinc->vid_cap.pipe.sd[VIN_IND_ISP], struct isp_dev, subdev);
return vin_isp_get_hist(isp, hist);
#else
return 0;
#endif
}
EXPORT_SYMBOL(vin_isp_get_hist_special);
int vin_set_phy2vir_special(int id, struct isp_memremap_cfg *isp_memremap)
{
#if IS_ENABLED(CONFIG_VIN_INIT_MELIS)
struct vin_core *vinc = vin_core_gbl[id];
unsigned long viraddr;
struct vm_area_struct *vma;
void *vaddr = NULL;
struct isp_autoflash_config_s *isp_autoflash_cfg = NULL;
unsigned int map_addr = 0;
unsigned int check_sign = 0;
if (vinc->mipi_sel == 0) {
map_addr = VIN_SENSOR0_RESERVE_ADDR;
check_sign = 0xAA11AA11;
} else {
map_addr = VIN_SENSOR1_RESERVE_ADDR;
check_sign = 0xBB11BB11;
}
vaddr = vin_map_kernel(map_addr, VIN_RESERVE_SIZE + VIN_THRESHOLD_PARAM_SIZE); /* map unit is page, page is align of 4k */
if (vaddr == NULL) {
vin_err("%s:map 0x%x paddr err!!!", __func__, map_addr);
return -EFAULT;
}
isp_autoflash_cfg = (struct isp_autoflash_config_s *)(vaddr + VIN_RESERVE_SIZE);
/* check id */
if (isp_autoflash_cfg->melisyuv_sign_id != check_sign) {
vin_warn("%s:sign is 0x%x but not 0x%x\n", __func__, isp_autoflash_cfg->sensorlist_sign_id, check_sign);
vin_unmap_kernel(vaddr);
return -EFAULT;
}
if (isp_memremap->en) {
viraddr = vm_mmap(NULL, 0, isp_autoflash_cfg->melisyuv_size, PROT_READ, MAP_SHARED | MAP_NORESERVE, 0);
vma = find_vma(current->mm, viraddr);
remap_pfn_range(vma, vma->vm_start, __phys_to_pfn(isp_autoflash_cfg->melisyuv_paddr), isp_autoflash_cfg->melisyuv_size, vma->vm_page_prot);
isp_memremap->vir_addr = (void *)viraddr;
isp_memremap->size = isp_autoflash_cfg->melisyuv_size;
vin_print("0x%x mmap viraddr is 0x%lx\n", isp_autoflash_cfg->melisyuv_paddr, viraddr);
} else {
if (isp_memremap->vir_addr && isp_memremap->size) {
vm_munmap((unsigned long)isp_memremap->vir_addr, isp_memremap->size);
vin_print("0x%x ummap viraddr is 0x%lx\n", isp_autoflash_cfg->melisyuv_paddr, (unsigned long)isp_memremap->vir_addr);
isp_autoflash_cfg->melisyuv_sign_id = 0XFFFFFFFF;
memblock_free(isp_autoflash_cfg->melisyuv_paddr, isp_autoflash_cfg->melisyuv_size);
free_reserved_area(__va(isp_autoflash_cfg->melisyuv_paddr), __va(isp_autoflash_cfg->melisyuv_paddr + isp_autoflash_cfg->melisyuv_size), -1, "isp_reserved");
}
}
vin_unmap_kernel(vaddr);
#endif
return 0;
}
EXPORT_SYMBOL(vin_set_phy2vir_special);
void vin_isp_reset_done_callback(int id, void *func)
{
struct vin_core *vinc = vin_core_gbl[id];
vinc->vid_cap.online_csi_reset_callback = func;
}
EXPORT_SYMBOL(vin_isp_reset_done_callback);
static const struct v4l2_ctrl_config ae_win_ctrls[] = {
{
.ops = &vin_ctrl_ops,
.id = V4L2_CID_AE_WIN_X1,
.name = "AutoExposure Win X1",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 32,
.max = 3264,
.step = 16,
.def = 256,
.flags = V4L2_CTRL_FLAG_VOLATILE,
}, {
.ops = &vin_ctrl_ops,
.id = V4L2_CID_AE_WIN_Y1,
.name = "AutoExposure Win Y1",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 32,
.max = 3264,
.step = 16,
.def = 256,
.flags = V4L2_CTRL_FLAG_VOLATILE,
}, {
.ops = &vin_ctrl_ops,
.id = V4L2_CID_AE_WIN_X2,
.name = "AutoExposure Win X2",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 32,
.max = 3264,
.step = 16,
.def = 256,
.flags = V4L2_CTRL_FLAG_VOLATILE,
}, {
.ops = &vin_ctrl_ops,
.id = V4L2_CID_AE_WIN_Y2,
.name = "AutoExposure Win Y2",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 32,
.max = 3264,
.step = 16,
.def = 256,
.flags = V4L2_CTRL_FLAG_VOLATILE,
}
};
static const struct v4l2_ctrl_config af_win_ctrls[] = {
{
.ops = &vin_ctrl_ops,
.id = V4L2_CID_AF_WIN_X1,
.name = "AutoFocus Win X1",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 32,
.max = 3264,
.step = 16,
.def = 256,
.flags = V4L2_CTRL_FLAG_VOLATILE,
}, {
.ops = &vin_ctrl_ops,
.id = V4L2_CID_AF_WIN_Y1,
.name = "AutoFocus Win Y1",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 32,
.max = 3264,
.step = 16,
.def = 256,
.flags = V4L2_CTRL_FLAG_VOLATILE,
}, {
.ops = &vin_ctrl_ops,
.id = V4L2_CID_AF_WIN_X2,
.name = "AutoFocus Win X2",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 32,
.max = 3264,
.step = 16,
.def = 256,
.flags = V4L2_CTRL_FLAG_VOLATILE,
}, {
.ops = &vin_ctrl_ops,
.id = V4L2_CID_AF_WIN_Y2,
.name = "AutoFocus Win Y2",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 32,
.max = 3264,
.step = 16,
.def = 256,
.flags = V4L2_CTRL_FLAG_VOLATILE,
}
};
static const struct v4l2_ctrl_config custom_ctrls[] = {
{
.ops = &vin_ctrl_ops,
.id = V4L2_CID_HOR_VISUAL_ANGLE,
.name = "Horizontal Visual Angle",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 0,
.max = 360,
.step = 1,
.def = 60,
.flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
}, {
.ops = &vin_ctrl_ops,
.id = V4L2_CID_VER_VISUAL_ANGLE,
.name = "Vertical Visual Angle",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 0,
.max = 360,
.step = 1,
.def = 60,
.flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
}, {
.ops = &vin_ctrl_ops,
.id = V4L2_CID_FOCUS_LENGTH,
.name = "Focus Length",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 0,
.max = 1000,
.step = 1,
.def = 280,
.flags = V4L2_CTRL_FLAG_VOLATILE,
}, {
.ops = &vin_ctrl_ops,
.id = V4L2_CID_AUTO_FOCUS_INIT,
.name = "AutoFocus Initial",
.type = V4L2_CTRL_TYPE_BUTTON,
.min = 0,
.max = 0,
.step = 0,
.def = 0,
}, {
.ops = &vin_ctrl_ops,
.id = V4L2_CID_AUTO_FOCUS_RELEASE,
.name = "AutoFocus Release",
.type = V4L2_CTRL_TYPE_BUTTON,
.min = 0,
.max = 0,
.step = 0,
.def = 0,
}, {
.ops = &vin_ctrl_ops,
.id = V4L2_CID_GSENSOR_ROTATION,
.name = "Gsensor Rotaion",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = -180,
.max = 180,
.step = 90,
.def = 0,
}, {
.ops = &vin_ctrl_ops,
.id = V4L2_CID_TAKE_PICTURE,
.name = "Take Picture",
.type = V4L2_CTRL_TYPE_INTEGER,
.min = 0,
.max = 16,
.step = 1,
.def = 0,
}, {
.ops = &vin_ctrl_ops,
.id = V4L2_CID_SENSOR_TYPE,
.name = "Sensor type",
.type = V4L2_CTRL_TYPE_MENU,
.min = 0,
.max = 1,
.def = 0,
.menu_skip_mask = 0x0,
.qmenu = sensor_info_type,
.flags = V4L2_CTRL_FLAG_VOLATILE,
}, {
.ops = &vin_ctrl_ops,
.id = V4L2_CID_FLASH_LED_MODE_V1,
.name = "VIN Flash ctrl",
.type = V4L2_CTRL_TYPE_MENU,
.min = 0,
.max = 2,
.def = 0,
.menu_skip_mask = 0x0,
.qmenu = flash_led_mode_v1,
.flags = 0,
.step = 0,
},
};
static const s64 iso_qmenu[] = {
100, 200, 400, 800, 1600, 3200, 6400,
};
static const s64 exp_bias_qmenu[] = {
-4, -3, -2, -1, 0, 1, 2, 3, 4,
};
int vin_init_controls(struct v4l2_ctrl_handler *hdl, struct vin_vid_cap *cap)
{
struct v4l2_ctrl *ctrl;
unsigned int i, ret = 0;
v4l2_ctrl_handler_init(hdl, 40 + ARRAY_SIZE(custom_ctrls)
+ ARRAY_SIZE(ae_win_ctrls) + ARRAY_SIZE(af_win_ctrls));
v4l2_ctrl_new_std(hdl, &vin_ctrl_ops, V4L2_CID_BRIGHTNESS, -128, 512, 1, 0);
v4l2_ctrl_new_std(hdl, &vin_ctrl_ops, V4L2_CID_CONTRAST, -128, 512, 1, 0);
v4l2_ctrl_new_std(hdl, &vin_ctrl_ops, V4L2_CID_SATURATION, -256, 512, 1, 0);
v4l2_ctrl_new_std(hdl, &vin_ctrl_ops, V4L2_CID_HUE, -180, 180, 1, 0);
v4l2_ctrl_new_std(hdl, &vin_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
ctrl = v4l2_ctrl_new_std(hdl, &vin_ctrl_ops, V4L2_CID_EXPOSURE, 1, 65536 * 16, 1, 1);
if (ctrl != NULL)
ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
v4l2_ctrl_new_std(hdl, &vin_ctrl_ops, V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
ctrl = v4l2_ctrl_new_std(hdl, &vin_ctrl_ops, V4L2_CID_GAIN, 16, 6000 * 16, 1, 16);
if (ctrl != NULL)
ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
ctrl = v4l2_ctrl_new_std(hdl, &vin_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
if (ctrl != NULL)
ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
ctrl = v4l2_ctrl_new_std(hdl, &vin_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
if (ctrl != NULL)
ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
v4l2_ctrl_new_std_menu(hdl, &vin_ctrl_ops,
V4L2_CID_POWER_LINE_FREQUENCY,
V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0,
V4L2_CID_POWER_LINE_FREQUENCY_AUTO);
v4l2_ctrl_new_std(hdl, &vin_ctrl_ops, V4L2_CID_HUE_AUTO, 0, 1, 1, 1);
v4l2_ctrl_new_std(hdl, &vin_ctrl_ops,
V4L2_CID_WHITE_BALANCE_TEMPERATURE, 2800, 10000, 1, 6500);
v4l2_ctrl_new_std(hdl, &vin_ctrl_ops, V4L2_CID_SHARPNESS, 0, 1000, 1, 0);
v4l2_ctrl_new_std(hdl, &vin_ctrl_ops, V4L2_CID_CHROMA_AGC, 0, 1, 1, 1);
v4l2_ctrl_new_std_menu(hdl, &vin_ctrl_ops, V4L2_CID_COLORFX,
V4L2_COLORFX_SET_CBCR, 0, V4L2_COLORFX_NONE);
v4l2_ctrl_new_std(hdl, &vin_ctrl_ops, V4L2_CID_AUTOBRIGHTNESS, 0, 1, 1, 1);
v4l2_ctrl_new_std(hdl, &vin_ctrl_ops, V4L2_CID_BAND_STOP_FILTER, 0, 1, 1, 1);
v4l2_ctrl_new_std(hdl, &vin_ctrl_ops, V4L2_CID_ILLUMINATORS_1, 0, 1, 1, 0);
v4l2_ctrl_new_std(hdl, &vin_ctrl_ops, V4L2_CID_ILLUMINATORS_2, 0, 1, 1, 0);
v4l2_ctrl_new_std_menu(hdl, &vin_ctrl_ops, V4L2_CID_EXPOSURE_AUTO,
V4L2_EXPOSURE_APERTURE_PRIORITY, 0,
V4L2_EXPOSURE_AUTO);
ctrl = v4l2_ctrl_new_std(hdl, &vin_ctrl_ops, V4L2_CID_EXPOSURE_ABSOLUTE, 1, 30 * 1000000, 1, 1);
if (ctrl != NULL)
ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
v4l2_ctrl_new_std(hdl, &vin_ctrl_ops, V4L2_CID_EXPOSURE_AUTO_PRIORITY, 0, 1, 1, 0);
v4l2_ctrl_new_std(hdl, &vin_ctrl_ops, V4L2_CID_FOCUS_ABSOLUTE, 0, 127, 1, 0);
v4l2_ctrl_new_std(hdl, &vin_ctrl_ops, V4L2_CID_FOCUS_RELATIVE, -127, 127, 1, 0);
v4l2_ctrl_new_std(hdl, &vin_ctrl_ops, V4L2_CID_FOCUS_AUTO, 0, 1, 1, 1);
v4l2_ctrl_new_int_menu(hdl, &vin_ctrl_ops, V4L2_CID_AUTO_EXPOSURE_BIAS,
ARRAY_SIZE(exp_bias_qmenu) - 1,
ARRAY_SIZE(exp_bias_qmenu) / 2, exp_bias_qmenu);
v4l2_ctrl_new_std_menu(hdl, &vin_ctrl_ops,
V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE,
V4L2_WHITE_BALANCE_SHADE, 0,
V4L2_WHITE_BALANCE_AUTO);
v4l2_ctrl_new_std(hdl, &vin_ctrl_ops, V4L2_CID_WIDE_DYNAMIC_RANGE, 0, 1, 1, 0);
v4l2_ctrl_new_std(hdl, &vin_ctrl_ops, V4L2_CID_IMAGE_STABILIZATION, 0, 1, 1, 0);
v4l2_ctrl_new_int_menu(hdl, &vin_ctrl_ops, V4L2_CID_ISO_SENSITIVITY,
ARRAY_SIZE(iso_qmenu) - 1,
ARRAY_SIZE(iso_qmenu) / 2 - 1, iso_qmenu);
v4l2_ctrl_new_std_menu(hdl, &vin_ctrl_ops,
V4L2_CID_ISO_SENSITIVITY_AUTO,
V4L2_ISO_SENSITIVITY_AUTO, 0,
V4L2_ISO_SENSITIVITY_AUTO);
v4l2_ctrl_new_std_menu(hdl, &vin_ctrl_ops,
V4L2_CID_EXPOSURE_METERING,
V4L2_EXPOSURE_METERING_MATRIX, 0,
V4L2_EXPOSURE_METERING_AVERAGE);
v4l2_ctrl_new_std_menu(hdl, &vin_ctrl_ops, V4L2_CID_SCENE_MODE,
V4L2_SCENE_MODE_TEXT, 0, V4L2_SCENE_MODE_NONE);
ctrl = v4l2_ctrl_new_std(hdl, &vin_ctrl_ops, V4L2_CID_3A_LOCK, 0, 7, 0, 0);
if (ctrl != NULL)
ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
v4l2_ctrl_new_std(hdl, &vin_ctrl_ops, V4L2_CID_AUTO_FOCUS_START, 0, 0, 0, 0);
v4l2_ctrl_new_std(hdl, &vin_ctrl_ops, V4L2_CID_AUTO_FOCUS_STOP, 0, 0, 0, 0);
ctrl = v4l2_ctrl_new_std(hdl, &vin_ctrl_ops, V4L2_CID_AUTO_FOCUS_STATUS, 0, 7, 0, 0);
if (ctrl != NULL)
ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
v4l2_ctrl_new_std_menu(hdl, &vin_ctrl_ops, V4L2_CID_AUTO_FOCUS_RANGE,
V4L2_AUTO_FOCUS_RANGE_INFINITY, 0,
V4L2_AUTO_FOCUS_RANGE_AUTO);
v4l2_ctrl_new_std_menu(hdl, &vin_ctrl_ops, V4L2_CID_FLASH_LED_MODE,
V4L2_FLASH_LED_MODE_RED_EYE, 0,
V4L2_FLASH_LED_MODE_NONE);
for (i = 0; i < ARRAY_SIZE(custom_ctrls); i++)
v4l2_ctrl_new_custom(hdl, &custom_ctrls[i], NULL);
for (i = 0; i < ARRAY_SIZE(ae_win_ctrls); i++)
cap->ae_win[i] = v4l2_ctrl_new_custom(hdl,
&ae_win_ctrls[i], NULL);
v4l2_ctrl_cluster(ARRAY_SIZE(ae_win_ctrls), &cap->ae_win[0]);
for (i = 0; i < ARRAY_SIZE(af_win_ctrls); i++)
cap->af_win[i] = v4l2_ctrl_new_custom(hdl,
&af_win_ctrls[i], NULL);
v4l2_ctrl_cluster(ARRAY_SIZE(af_win_ctrls), &cap->af_win[0]);
if (hdl->error) {
ret = hdl->error;
v4l2_ctrl_handler_free(hdl);
}
return ret;
}
int vin_init_video(struct v4l2_device *v4l2_dev, struct vin_vid_cap *cap)
{
int ret = 0;
struct vb2_queue *q;
struct vin_buffer *vin_buffer_size;
snprintf(cap->vdev.name, sizeof(cap->vdev.name),
"vin_video%d", cap->vinc->id);
cap->vdev.fops = &vin_fops;
cap->vdev.ioctl_ops = &vin_ioctl_ops;
cap->vdev.device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
V4L2_CAP_STREAMING | V4L2_CAP_READWRITE;
cap->vdev.release = video_device_release_empty;
cap->vdev.ctrl_handler = &cap->ctrl_handler;
cap->vdev.v4l2_dev = v4l2_dev;
cap->vdev.queue = &cap->vb_vidq;
cap->vdev.lock = &cap->lock;
cap->vdev.flags = V4L2_FL_USES_V4L2_FH;
ret = video_register_device(&cap->vdev, VFL_TYPE_VIDEO, cap->vinc->id);
if (ret < 0) {
vin_err("Error video_register_device!!\n");
return -1;
}
video_set_drvdata(&cap->vdev, cap->vinc);
vin_log(VIN_LOG_VIDEO, "V4L2 device registered as %s\n",
video_device_node_name(&cap->vdev));
/* Initialize videobuf2 queue as per the buffer type */
ret = dma_set_mask(&cap->vinc->pdev->dev, DMA_BIT_MASK(32));
if (ret < 0) {
vin_err("Error dma_set_mask!!\n");
return -1;
}
ret = dma_set_coherent_mask(&cap->vinc->pdev->dev, DMA_BIT_MASK(32));
if (ret < 0) {
vin_err("Error dma_set_coherent_mask!!\n");
return -1;
}
cap->dev = &cap->vinc->pdev->dev;
if (!cap->dev->dma_parms) {
ret = vb2_dma_contig_set_max_seg_size(&cap->vinc->pdev->dev, DMA_BIT_MASK(32));
if (ret < 0 || IS_ERR_OR_NULL(cap->dev->dma_parms)) {
vin_err("Failed to get the context\n");
return -1;
}
cap->dma_parms_alloc = true;
}
/* initialize queue */
q = &cap->vb_vidq;
q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
q->drv_priv = cap;
q->buf_struct_size = sizeof(*vin_buffer_size);
q->ops = &vin_video_qops;
q->mem_ops = &vb2_dma_contig_memops;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->lock = &cap->lock;
ret = vb2_queue_init(q);
if (ret) {
vin_err("vb2_queue_init() failed\n");
if (cap->dma_parms_alloc)
vb2_dma_contig_clear_max_seg_size(cap->dev);
return ret;
}
cap->vd_pad.flags = MEDIA_PAD_FL_SINK;
ret = media_entity_pads_init(&cap->vdev.entity, 1, &cap->vd_pad);
if (ret)
return ret;
INIT_WORK(&cap->s_stream_task, __vin_s_stream_handle);
cap->state = 0;
cap->registered = 1;
/* initial state */
cap->capture_mode = V4L2_MODE_PREVIEW;
/* init video dma queues */
INIT_LIST_HEAD(&cap->vidq_active);
mutex_init(&cap->lock);
spin_lock_init(&cap->slock);
return 0;
}
static int vin_link_setup(struct media_entity *entity,
const struct media_pad *local,
const struct media_pad *remote, u32 flags)
{
return 0;
}
static const struct media_entity_operations vin_sd_media_ops = {
.link_setup = vin_link_setup,
};
static int vin_video_core_s_power(struct v4l2_subdev *sd, int on)
{
struct vin_core *vinc = v4l2_get_subdevdata(sd);
if (on) {
pm_runtime_get_sync(&vinc->pdev->dev);/* call pm_runtime resume */
} else {
pm_runtime_put_sync(&vinc->pdev->dev);/* call pm_runtime suspend */
}
return 0;
}
static int vin_subdev_logic_s_stream(unsigned char virtual_id, int on)
{
#if defined CSIC_DMA_VER_140_000
unsigned char logic_id = dma_virtual_find_logic[virtual_id];
struct vin_core *logic_vinc = vin_core_gbl[logic_id];
if (logic_vinc->work_mode == BK_ONLINE && virtual_id != logic_id) {
vin_err("video%d work on online mode, video%d cannot to work!!\n", logic_id, virtual_id);
return -1;
}
if (on && (logic_vinc->logic_top_stream_count)++ > 0)
return 0;
else if (!on && (logic_vinc->logic_top_stream_count == 0 || --(logic_vinc->logic_top_stream_count) > 0))
return 0;
if (on) {
csic_dma_top_enable(logic_id);
csic_dma_mul_ch_enable(logic_id, logic_vinc->work_mode);
if (logic_vinc->id == CSI_VE_ONLINE_VIDEO && logic_vinc->ve_online_cfg.ve_online_en) {
csic_ve_online_hs_enable(logic_id);
logic_vinc->ve_ol_ch = CSI_VE_ONLINE_VIDEO;
csic_ve_online_ch_sel(logic_id, logic_vinc->ve_ol_ch);
}
csic_dma_buf_length_software_enable(logic_vinc->vipp_sel, 0);
csi_dam_flip_software_enable(logic_vinc->vipp_sel, 0);
//csic_dma_top_interrupt_en(logic_id, VIDEO_INPUT_TO_INT | CLR_FS_FRM_CNT_INT | FS_PUL_INT);//for debug
} else {
//csic_dma_top_interrupt_disable(logic_id, DMA_TOP_INT_ALL);
csic_ve_online_hs_disable(logic_id);
csic_dma_top_disable(logic_id);
}
vin_log(VIN_LOG_FMT, "dma%d top init by video%d, %s\n", logic_id, virtual_id, on ? "steram on" : "steam off");
#endif
return 0;
}
static int vin_subdev_s_stream(struct v4l2_subdev *sd, int enable)
{
struct vin_core *vinc = v4l2_get_subdevdata(sd);
struct vin_vid_cap *cap = &vinc->vid_cap;
struct csic_dma_cfg cfg;
struct csic_dma_flip flip;
struct dma_output_size size;
struct dma_buf_len buf_len;
struct dma_flip_size flip_size;
struct sensor_output_fmt sensor_fmt;
int flag = 0;
int flip_mul = 2;
int ch_id = 0;
if (enable) {
memset(&cfg, 0, sizeof(cfg));
memset(&size, 0, sizeof(size));
memset(&buf_len, 0, sizeof(buf_len));
switch (cap->frame.fmt.field) {
case V4L2_FIELD_ANY:
case V4L2_FIELD_NONE:
cfg.field = FIELD_EITHER;
break;
case V4L2_FIELD_TOP:
cfg.field = FIELD_1;
flag = 1;
break;
case V4L2_FIELD_BOTTOM:
cfg.field = FIELD_2;
flag = 1;
break;
case V4L2_FIELD_INTERLACED:
cfg.field = FIELD_EITHER;
flag = 1;
break;
default:
cfg.field = FIELD_EITHER;
break;
}
if (vinc->tvin.flag) {
#if IS_ENABLED(CONFIG_ARCH_SUN55IW3) || IS_ENABLED(CONFIG_ARCH_SUN60IW2)
if (vinc->id > TVIN_VIDEO_MAX)
ch_id = vinc->id / TVIN_VIDEO_STRIP + vinc->id % TVIN_VIDEO_STRIP;
else
ch_id = vinc->id / TVIN_VIDEO_STRIP;
#else
ch_id = vinc->id;
#endif
if (ch_id < TVIN_SEPARATE)
sensor_fmt.ch_id = ch_id;
else
sensor_fmt.ch_id = ch_id - TVIN_SEPARATE;
if (!v4l2_subdev_call(cap->pipe.sd[VIN_IND_SENSOR],
core, ioctl, GET_SENSOR_CH_OUTPUT_FMT, &sensor_fmt))
flag = sensor_fmt.field;
}
switch (cap->frame.fmt.fourcc) {
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV12M:
case V4L2_PIX_FMT_FBC:
cfg.fmt = flag ? FRAME_UV_CB_YUV420 : FIELD_UV_CB_YUV420;
buf_len.buf_len_y = cap->frame.o_width;
buf_len.buf_len_c = buf_len.buf_len_y;
break;
case V4L2_PIX_FMT_LBC_2_0X:
case V4L2_PIX_FMT_LBC_2_5X:
case V4L2_PIX_FMT_LBC_1_0X:
case V4L2_PIX_FMT_LBC_1_5X:
cfg.fmt = LBC_MODE_OUTPUT;
buf_len.buf_len_y = cap->lbc_cmp.line_tar_bits[1] >> 3;
buf_len.buf_len_c = cap->lbc_cmp.line_tar_bits[0] >> 3;
break;
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_NV21M:
cfg.fmt = flag ? FRAME_VU_CB_YUV420 : FIELD_VU_CB_YUV420;
buf_len.buf_len_y = cap->frame.o_width;
buf_len.buf_len_c = buf_len.buf_len_y;
break;
case V4L2_PIX_FMT_YVU420:
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_YUV420M:
cfg.fmt = flag ? FRAME_PLANAR_YUV420 : FIELD_PLANAR_YUV420;
buf_len.buf_len_y = cap->frame.o_width;
buf_len.buf_len_c = buf_len.buf_len_y >> 1;
break;
case V4L2_PIX_FMT_GREY:
cfg.fmt = flag ? FRAME_CB_YUV400 : FIELD_CB_YUV400;
buf_len.buf_len_y = cap->frame.o_width;
break;
case V4L2_PIX_FMT_YUV422P:
cfg.fmt = flag ? FRAME_PLANAR_YUV422 : FIELD_PLANAR_YUV422;
buf_len.buf_len_y = cap->frame.o_width;
buf_len.buf_len_c = buf_len.buf_len_y >> 1;
break;
case V4L2_PIX_FMT_NV61:
case V4L2_PIX_FMT_NV61M:
cfg.fmt = flag ? FRAME_VU_CB_YUV422 : FIELD_VU_CB_YUV422;
buf_len.buf_len_y = cap->frame.o_width;
buf_len.buf_len_c = buf_len.buf_len_y;
break;
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV16M:
cfg.fmt = flag ? FRAME_UV_CB_YUV422 : FIELD_UV_CB_YUV422;
buf_len.buf_len_y = cap->frame.o_width;
buf_len.buf_len_c = buf_len.buf_len_y;
break;
case V4L2_PIX_FMT_SBGGR8:
case V4L2_PIX_FMT_SGBRG8:
case V4L2_PIX_FMT_SGRBG8:
case V4L2_PIX_FMT_SRGGB8:
flip_mul = 1;
cfg.fmt = flag ? FRAME_RAW_8 : FIELD_RAW_8;
buf_len.buf_len_y = cap->frame.o_width;
buf_len.buf_len_c = buf_len.buf_len_y;
break;
case V4L2_PIX_FMT_SBGGR10:
case V4L2_PIX_FMT_SGBRG10:
case V4L2_PIX_FMT_SGRBG10:
case V4L2_PIX_FMT_SRGGB10:
flip_mul = 1;
cfg.fmt = flag ? FRAME_RAW_10 : FIELD_RAW_10;
buf_len.buf_len_y = cap->frame.o_width * 2;
buf_len.buf_len_c = buf_len.buf_len_y;
break;
case V4L2_PIX_FMT_SBGGR12:
case V4L2_PIX_FMT_SGBRG12:
case V4L2_PIX_FMT_SGRBG12:
case V4L2_PIX_FMT_SRGGB12:
flip_mul = 1;
cfg.fmt = flag ? FRAME_RAW_12 : FIELD_RAW_12;
buf_len.buf_len_y = cap->frame.o_width * 2;
buf_len.buf_len_c = buf_len.buf_len_y;
break;
#if defined CSIC_DMA_VER_140_000
case V4L2_PIX_FMT_SBGGR14:
case V4L2_PIX_FMT_SGBRG14:
case V4L2_PIX_FMT_SGRBG14:
case V4L2_PIX_FMT_SRGGB14:
flip_mul = 1;
cfg.fmt = flag ? FRAME_RAW_14 : FIELD_RAW_14;
buf_len.buf_len_y = cap->frame.o_width * 2;
buf_len.buf_len_c = buf_len.buf_len_y;
break;
case V4L2_PIX_FMT_SBGGR16:
case V4L2_PIX_FMT_SGBRG16:
case V4L2_PIX_FMT_SGRBG16:
case V4L2_PIX_FMT_SRGGB16:
flip_mul = 1;
cfg.fmt = flag ? FRAME_RAW_16 : FIELD_RAW_16;
buf_len.buf_len_y = cap->frame.o_width * 2;
buf_len.buf_len_c = buf_len.buf_len_y;
break;
#endif
case V4L2_PIX_FMT_RGB24:
case V4L2_PIX_FMT_BGR24:
cfg.fmt = FIELD_RGB888;
buf_len.buf_len_y = cap->frame.o_width * 3;
buf_len.buf_len_c = buf_len.buf_len_y;
break;
case V4L2_PIX_FMT_RGB565:
cfg.fmt = FIELD_RGB565;
buf_len.buf_len_y = cap->frame.o_width * 2;
buf_len.buf_len_c = buf_len.buf_len_y;
break;
default:
cfg.fmt = flag ? FRAME_UV_CB_YUV420 : FIELD_UV_CB_YUV420;
buf_len.buf_len_y = cap->frame.o_width;
buf_len.buf_len_c = buf_len.buf_len_y;
break;
}
if (vinc->isp_dbg.debug_en) {
buf_len.buf_len_y = 0;
buf_len.buf_len_c = 0;
}
cfg.ds = vinc->fps_ds;
if (vin_subdev_logic_s_stream(vinc->id, enable))
return -1;
csic_dma_config(vinc->vipp_sel, &cfg);
size.hor_len = vinc->isp_dbg.debug_en ? 0 : cap->frame.o_width;
size.ver_len = vinc->isp_dbg.debug_en ? 0 : cap->frame.o_height;
size.hor_start = vinc->isp_dbg.debug_en ? 0 : cap->frame.offs_h;
size.ver_start = vinc->isp_dbg.debug_en ? 0 : cap->frame.offs_v;
flip_size.hor_len = vinc->isp_dbg.debug_en ? 0 : cap->frame.o_width * flip_mul;
flip_size.ver_len = vinc->isp_dbg.debug_en ? 0 : cap->frame.o_height;
flip.hflip_en = vinc->hflip;
flip.vflip_en = vinc->vflip;
if (vinc->large_image == 2) {
size.hor_len /= 2;
flip_size.hor_len /= 2;
}
csic_dma_output_size_cfg(vinc->vipp_sel, &size);
/* csic_dma_10bit_cut2_8bit_enable(vinc->vipp_sel); */
#if !defined CSIC_DMA_VER_140_000
switch (cap->frame.fmt.fourcc) {
case V4L2_PIX_FMT_FBC:
csic_dma_buf_length_software_enable(vinc->vipp_sel, 0);
csi_dam_flip_software_enable(vinc->vipp_sel, 0);
csic_dma_flip_en(vinc->vipp_sel, &flip);
csic_fbc_enable(vinc->vipp_sel);
break;
case V4L2_PIX_FMT_LBC_2_0X:
case V4L2_PIX_FMT_LBC_2_5X:
case V4L2_PIX_FMT_LBC_1_0X:
case V4L2_PIX_FMT_LBC_1_5X:
csi_dam_flip_software_enable(vinc->vipp_sel, 1);
#if IS_ENABLED(CONFIG_ARCH_SUN8IW16P1)
csic_dma_buf_length_software_enable(vinc->vipp_sel, 1);
#else
csic_dma_buf_length_software_enable(vinc->vipp_sel, 0);
#endif
csic_lbc_enable(vinc->vipp_sel);
csic_lbc_cmp_ratio(vinc->vipp_sel, &cap->lbc_cmp);
break;
default:
csic_dma_buf_length_software_enable(vinc->vipp_sel, 0);
csi_dam_flip_software_enable(vinc->vipp_sel, 0);
csic_dma_flip_en(vinc->vipp_sel, &flip);
csic_dma_enable(vinc->vipp_sel);
break;
}
csic_dma_buffer_length(vinc->vipp_sel, &buf_len);
csic_dma_flip_size(vinc->vipp_sel, &flip_size);
/* give up line_cnt interrupt. process in vsync and frame_done isr. */
/* csic_dma_line_cnt(vinc->vipp_sel, cap->frame.o_height / 16 * 12); */
csic_frame_cnt_enable(vinc->vipp_sel);
#ifndef BUF_AUTO_UPDATE
vin_set_next_buf_addr(vinc);
csic_dma_top_enable(vinc->vipp_sel);
csic_dma_int_clear_status(vinc->vipp_sel, DMA_INT_ALL);
csic_dma_int_enable(vinc->vipp_sel, DMA_INT_BUF_0_OVERFLOW | DMA_INT_BUF_1_OVERFLOW |
DMA_INT_BUF_2_OVERFLOW | DMA_INT_HBLANK_OVERFLOW | DMA_INT_VSYNC_TRIG |
DMA_INT_CAPTURE_DONE | DMA_INT_FRAME_DONE | DMA_INT_LBC_HB);
#else
csic_dma_top_enable(vinc->vipp_sel);
vin_set_next_buf_addr(vinc);
csic_dma_int_clear_status(vinc->vipp_sel, DMA_INT_ALL);
csic_dma_int_enable(vinc->vipp_sel, DMA_INT_BUF_0_OVERFLOW | DMA_INT_BUF_1_OVERFLOW |
DMA_INT_BUF_2_OVERFLOW | DMA_INT_HBLANK_OVERFLOW | DMA_INT_VSYNC_TRIG |
DMA_INT_CAPTURE_DONE | DMA_INT_STORED_FRM_CNT | DMA_INT_FRM_LOST | DMA_INT_LBC_HB);
#endif
#else /* else CSIC_DMA_VER_140_000 */
csic_dma_buffer_length(vinc->vipp_sel, &buf_len);
csic_dma_flip_size(vinc->vipp_sel, &flip_size);
#ifndef BUF_AUTO_UPDATE
if (vinc->large_image != 3)
vin_set_next_buf_addr(vinc);
csic_dma_int_clear_status(vinc->vipp_sel, DMA_INT_ALL);
csic_dma_int_enable(vinc->vipp_sel, DMA_INT_BUF_0_OVERFLOW | DMA_INT_HBLANK_OVERFLOW |
DMA_INT_VSYNC_TRIG | DMA_INT_CAPTURE_DONE | DMA_INT_FRAME_DONE | DMA_INT_LBC_HB);
#else
if (vinc->large_image != 3)
vin_set_next_buf_addr(vinc);
csic_dma_int_clear_status(vinc->vipp_sel, DMA_INT_ALL);
csic_dma_int_enable(vinc->vipp_sel, DMA_INT_BUF_0_OVERFLOW | DMA_INT_HBLANK_OVERFLOW |
DMA_INT_VSYNC_TRIG | DMA_INT_CAPTURE_DONE | DMA_INT_STORED_FRM_CNT |
DMA_INT_FRM_LOST | DMA_INT_LBC_HB);
#endif
if ((vinc->dma_merge_mode == 1) && (vinc->vipp_sel % 4 == 0))
csic_dma_int_disable(vinc->vipp_sel, DMA_INT_ALL);
csic_dma_int_enable(vinc->vipp_sel, DMA_INT_ADDR_NO_READY | DMA_INT_ADDR_OVERFLOW);
if (vinc->large_image != 3)
csic_dma_int_enable(vinc->vipp_sel, DMA_INT_PIXEL_MISS | DMA_INT_LINE_MISS);
switch (cap->frame.fmt.fourcc) {
case V4L2_PIX_FMT_LBC_2_0X:
case V4L2_PIX_FMT_LBC_2_5X:
case V4L2_PIX_FMT_LBC_1_5X:
case V4L2_PIX_FMT_LBC_1_0X:
csic_lbc_enable(vinc->vipp_sel);
csic_lbc_cmp_ratio(vinc->vipp_sel, &cap->lbc_cmp);
break;
default:
csic_dma_flip_en(vinc->vipp_sel, &flip);
csic_dma_enable(vinc->vipp_sel);
break;
}
#endif
} else {
vinc->tvin.flag = false;
#if !defined CSIC_DMA_VER_140_000
csic_dma_top_disable(vinc->vipp_sel);
#endif
csic_dma_int_disable(vinc->vipp_sel, DMA_INT_ALL);
csic_dma_int_clear_status(vinc->vipp_sel, DMA_INT_ALL);
switch (cap->frame.fmt.fourcc) {
case V4L2_PIX_FMT_FBC:
csic_fbc_disable(vinc->vipp_sel);
break;
case V4L2_PIX_FMT_LBC_2_0X:
case V4L2_PIX_FMT_LBC_2_5X:
case V4L2_PIX_FMT_LBC_1_0X:
case V4L2_PIX_FMT_LBC_1_5X:
csic_lbc_disable(vinc->vipp_sel);
break;
default:
csic_dma_disable(vinc->vipp_sel);
break;
}
vin_subdev_logic_s_stream(vinc->id, enable);
}
#if IS_ENABLED(CONFIG_ISP_SERVER_MELIS)
cancel_work_sync(&vinc->ldci_buf_send_task);
#endif
vin_log(VIN_LOG_FMT, "csic_dma%d %s, %d*%d hoff: %d voff: %d\n",
vinc->id, enable ? "stream on" : "stream off",
cap->frame.o_width, cap->frame.o_height,
cap->frame.offs_h, cap->frame.offs_v);
return 0;
}
static struct v4l2_subdev_core_ops vin_subdev_core_ops = {
.s_power = vin_video_core_s_power,
};
static const struct v4l2_subdev_video_ops vin_subdev_video_ops = {
.s_stream = vin_subdev_s_stream,
};
static struct v4l2_subdev_ops vin_subdev_ops = {
.core = &vin_subdev_core_ops,
.video = &vin_subdev_video_ops,
};
static int vin_capture_subdev_registered(struct v4l2_subdev *sd)
{
struct vin_core *vinc = v4l2_get_subdevdata(sd);
int ret;
vinc->vid_cap.vinc = vinc;
if (vin_init_controls(&vinc->vid_cap.ctrl_handler, &vinc->vid_cap)) {
vin_err("Error v4l2 ctrls new!!\n");
return -1;
}
vinc->pipeline_ops = v4l2_get_subdev_hostdata(sd);
if (vin_init_video(sd->v4l2_dev, &vinc->vid_cap)) {
vin_err("vin init video!!!!\n");
vinc->pipeline_ops = NULL;
}
ret = sysfs_create_link(&vinc->vid_cap.vdev.dev.kobj,
&vinc->pdev->dev.kobj, "vin_dbg");
if (ret)
vin_err("sysfs_create_link failed\n");
return 0;
}
static void vin_capture_subdev_unregistered(struct v4l2_subdev *sd)
{
struct vin_core *vinc = v4l2_get_subdevdata(sd);
if (vinc == NULL)
return;
if (video_is_registered(&vinc->vid_cap.vdev)) {
sysfs_remove_link(&vinc->vid_cap.vdev.dev.kobj, "vin_dbg");
vin_log(VIN_LOG_VIDEO, "unregistering %s\n",
video_device_node_name(&vinc->vid_cap.vdev));
media_entity_cleanup(&vinc->vid_cap.vdev.entity);
if (vinc->vid_cap.dma_parms_alloc && !IS_ERR_OR_NULL(vinc->vid_cap.dev->dma_parms))
vb2_dma_contig_clear_max_seg_size(vinc->vid_cap.dev);
video_unregister_device(&vinc->vid_cap.vdev);
mutex_destroy(&vinc->vid_cap.lock);
}
v4l2_ctrl_handler_free(&vinc->vid_cap.ctrl_handler);
vinc->pipeline_ops = NULL;
}
static const struct v4l2_subdev_internal_ops vin_capture_sd_internal_ops = {
.registered = vin_capture_subdev_registered,
.unregistered = vin_capture_subdev_unregistered,
};
int vin_initialize_capture_subdev(struct vin_core *vinc)
{
struct v4l2_subdev *sd = &vinc->vid_cap.subdev;
int ret;
v4l2_subdev_init(sd, &vin_subdev_ops);
sd->grp_id = VIN_GRP_ID_CAPTURE;
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
snprintf(sd->name, sizeof(sd->name), "vin_cap.%d", vinc->id);
vinc->vid_cap.sd_pads[VIN_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
vinc->vid_cap.sd_pads[VIN_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
sd->entity.function = MEDIA_ENT_F_IO_V4L;
ret = media_entity_pads_init(&sd->entity, VIN_SD_PADS_NUM,
vinc->vid_cap.sd_pads);
if (ret)
return ret;
sd->entity.ops = &vin_sd_media_ops;
sd->internal_ops = &vin_capture_sd_internal_ops;
v4l2_set_subdevdata(sd, vinc);
return 0;
}
void vin_cleanup_capture_subdev(struct vin_core *vinc)
{
struct v4l2_subdev *sd = &vinc->vid_cap.subdev;
media_entity_cleanup(&sd->entity);
v4l2_set_subdevdata(sd, NULL);
}