kernel_samsung_a53x/drivers/media/platform/exynos/camera/is-subdev-ctrl.c
2024-06-15 16:02:09 -03:00

1671 lines
40 KiB
C
Executable file

/*
* Samsung Exynos5 SoC series FIMC-IS driver
*
* exynos5 fimc-is video functions
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd
*
* 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 <linux/module.h>
#include <asm/cacheflush.h>
#include "is-core.h"
#include "is-param.h"
#include "is-device-ischain.h"
#include "is-debug.h"
#include "pablo-mem.h"
#if defined(SDC_HEADER_GEN)
#include "is-sdc-header.h"
#endif
#include "is-hw-common-dma.h"
bool is_subdev_check_vid(uint32_t vid)
{
bool is_valid = true;
if (vid == 0)
is_valid = false;
else if (vid >= IS_VIDEO_MAX_NUM)
is_valid = false;
else if (IS_ENABLED(LOGICAL_VIDEO_NODE)
&& (vid >= IS_VIDEO_LVN_BASE))
is_valid = false;
return is_valid;
}
struct is_subdev * video2subdev(enum is_subdev_device_type device_type,
void *device, u32 vid)
{
struct is_subdev *subdev = NULL;
struct is_device_sensor *sensor = NULL;
struct is_device_ischain *ischain = NULL;
if (device_type == IS_SENSOR_SUBDEV) {
sensor = (struct is_device_sensor *)device;
} else {
ischain = (struct is_device_ischain *)device;
sensor = ischain->sensor;
}
switch (vid) {
case IS_VIDEO_SS0VC0_NUM:
case IS_VIDEO_SS1VC0_NUM:
case IS_VIDEO_SS2VC0_NUM:
case IS_VIDEO_SS3VC0_NUM:
case IS_VIDEO_SS4VC0_NUM:
case IS_VIDEO_SS5VC0_NUM:
subdev = &sensor->ssvc0;
break;
case IS_VIDEO_SS0VC1_NUM:
case IS_VIDEO_SS1VC1_NUM:
case IS_VIDEO_SS2VC1_NUM:
case IS_VIDEO_SS3VC1_NUM:
case IS_VIDEO_SS4VC1_NUM:
case IS_VIDEO_SS5VC1_NUM:
subdev = &sensor->ssvc1;
break;
case IS_VIDEO_SS0VC2_NUM:
case IS_VIDEO_SS1VC2_NUM:
case IS_VIDEO_SS2VC2_NUM:
case IS_VIDEO_SS3VC2_NUM:
case IS_VIDEO_SS4VC2_NUM:
case IS_VIDEO_SS5VC2_NUM:
subdev = &sensor->ssvc2;
break;
case IS_VIDEO_SS0VC3_NUM:
case IS_VIDEO_SS1VC3_NUM:
case IS_VIDEO_SS2VC3_NUM:
case IS_VIDEO_SS3VC3_NUM:
case IS_VIDEO_SS4VC3_NUM:
case IS_VIDEO_SS5VC3_NUM:
subdev = &sensor->ssvc3;
break;
case IS_VIDEO_30S_NUM:
case IS_VIDEO_31S_NUM:
case IS_VIDEO_32S_NUM:
case IS_VIDEO_33S_NUM:
subdev = &ischain->group_3aa.leader;
break;
case IS_VIDEO_30C_NUM:
case IS_VIDEO_31C_NUM:
case IS_VIDEO_32C_NUM:
case IS_VIDEO_33C_NUM:
subdev = &ischain->txc;
break;
case IS_VIDEO_30P_NUM:
case IS_VIDEO_31P_NUM:
case IS_VIDEO_32P_NUM:
case IS_VIDEO_33P_NUM:
subdev = &ischain->txp;
break;
case IS_VIDEO_30F_NUM:
case IS_VIDEO_31F_NUM:
case IS_VIDEO_32F_NUM:
case IS_VIDEO_33F_NUM:
subdev = &ischain->txf;
break;
case IS_VIDEO_30G_NUM:
case IS_VIDEO_31G_NUM:
case IS_VIDEO_32G_NUM:
case IS_VIDEO_33G_NUM:
subdev = &ischain->txg;
break;
case IS_VIDEO_30O_NUM:
case IS_VIDEO_31O_NUM:
case IS_VIDEO_32O_NUM:
case IS_VIDEO_33O_NUM:
subdev = &ischain->txo;
break;
case IS_VIDEO_30L_NUM:
case IS_VIDEO_31L_NUM:
case IS_VIDEO_32L_NUM:
case IS_VIDEO_33L_NUM:
subdev = &ischain->txl;
break;
case IS_VIDEO_LME0_NUM:
case IS_VIDEO_LME1_NUM:
subdev = &ischain->group_lme.leader;
break;
case IS_VIDEO_LME0S_NUM:
case IS_VIDEO_LME1S_NUM:
subdev = &ischain->lmes;
break;
case IS_VIDEO_LME0C_NUM:
case IS_VIDEO_LME1C_NUM:
subdev = &ischain->lmec;
break;
case IS_VIDEO_ORB0_NUM:
case IS_VIDEO_ORB1_NUM:
subdev = &ischain->group_orb.leader;
break;
case IS_VIDEO_ORB0C_NUM:
case IS_VIDEO_ORB1C_NUM:
subdev = &ischain->orbxc;
break;
case IS_VIDEO_I0S_NUM:
case IS_VIDEO_I1S_NUM:
subdev = &ischain->group_isp.leader;
break;
case IS_VIDEO_I0C_NUM:
case IS_VIDEO_I1C_NUM:
subdev = &ischain->ixc;
break;
case IS_VIDEO_I0P_NUM:
case IS_VIDEO_I1P_NUM:
subdev = &ischain->ixp;
break;
case IS_VIDEO_I0T_NUM:
subdev = &ischain->ixt;
break;
case IS_VIDEO_I0G_NUM:
subdev = &ischain->ixg;
break;
case IS_VIDEO_I0V_NUM:
subdev = &ischain->ixv;
break;
case IS_VIDEO_I0W_NUM:
subdev = &ischain->ixw;
break;
case IS_VIDEO_ME0C_NUM:
case IS_VIDEO_ME1C_NUM:
subdev = &ischain->mexc;
break;
case IS_VIDEO_M0S_NUM:
case IS_VIDEO_M1S_NUM:
subdev = &ischain->group_mcs.leader;
break;
case IS_VIDEO_M0P_NUM:
subdev = &ischain->m0p;
break;
case IS_VIDEO_M1P_NUM:
subdev = &ischain->m1p;
break;
case IS_VIDEO_M2P_NUM:
subdev = &ischain->m2p;
break;
case IS_VIDEO_M3P_NUM:
subdev = &ischain->m3p;
break;
case IS_VIDEO_M4P_NUM:
subdev = &ischain->m4p;
break;
case IS_VIDEO_M5P_NUM:
subdev = &ischain->m5p;
break;
case IS_VIDEO_VRA_NUM:
subdev = &ischain->group_vra.leader;
break;
default:
err("[%d] vid %d is NOT found", ((device_type == IS_SENSOR_SUBDEV) ?
(ischain ? ischain->instance : 0) : (sensor ? sensor->device_id : 0)), vid);
break;
}
return subdev;
}
int is_subdev_probe(struct is_subdev *subdev,
u32 instance,
u32 id,
const char *name,
const struct is_subdev_ops *sops)
{
FIMC_BUG(!subdev);
FIMC_BUG(!name);
subdev->id = id;
subdev->wq_id = is_subdev_wq_id[id];
subdev->instance = instance;
subdev->ops = sops;
memset(subdev->name, 0x0, sizeof(subdev->name));
strncpy(subdev->name, name, sizeof(char[3]));
subdev->state = 0L;
/* for internal use */
frame_manager_probe(&subdev->internal_framemgr, subdev->id, name);
return 0;
}
int is_subdev_open(struct is_subdev *subdev,
struct is_video_ctx *vctx,
void *ctl_data,
u32 instance)
{
int ret = 0;
struct is_video *video;
const struct param_control *init_ctl = (const struct param_control *)ctl_data;
FIMC_BUG(!subdev);
if (test_bit(IS_SUBDEV_OPEN, &subdev->state)) {
mserr("already open", subdev, subdev);
ret = -EPERM;
goto p_err;
}
/* If it is internal VC, skip vctx setting. */
if (vctx) {
subdev->vctx = vctx;
video = GET_VIDEO(vctx);
subdev->vid = (video) ? video->id : 0;
}
subdev->instance = instance;
subdev->cid = CAPTURE_NODE_MAX;
subdev->input.width = 0;
subdev->input.height = 0;
subdev->input.crop.x = 0;
subdev->input.crop.y = 0;
subdev->input.crop.w = 0;
subdev->input.crop.h = 0;
subdev->output.width = 0;
subdev->output.height = 0;
subdev->output.crop.x = 0;
subdev->output.crop.y = 0;
subdev->output.crop.w = 0;
subdev->output.crop.h = 0;
subdev->bits_per_pixel = 0;
subdev->memory_bitwidth = 0;
subdev->sbwc_type = 0;
subdev->lossy_byte32num = 0;
if (init_ctl) {
set_bit(IS_SUBDEV_START, &subdev->state);
if (subdev->id == ENTRY_VRA) {
/* vra only control by command for enabling or disabling */
if (init_ctl->cmd == CONTROL_COMMAND_STOP)
clear_bit(IS_SUBDEV_RUN, &subdev->state);
else
set_bit(IS_SUBDEV_RUN, &subdev->state);
} else {
if (init_ctl->bypass == CONTROL_BYPASS_ENABLE)
clear_bit(IS_SUBDEV_RUN, &subdev->state);
else
set_bit(IS_SUBDEV_RUN, &subdev->state);
}
} else {
clear_bit(IS_SUBDEV_START, &subdev->state);
clear_bit(IS_SUBDEV_RUN, &subdev->state);
}
set_bit(IS_SUBDEV_OPEN, &subdev->state);
p_err:
return ret;
}
int is_sensor_subdev_open(struct is_device_sensor *device,
struct is_video_ctx *vctx)
{
int ret = 0;
struct is_subdev *subdev;
FIMC_BUG(!device);
FIMC_BUG(!vctx);
FIMC_BUG(!GET_VIDEO(vctx));
subdev = vctx->subdev;
ret = is_subdev_open(subdev, vctx, NULL, device->instance);
if (ret) {
mserr("is_subdev_open is fail(%d)", subdev, subdev, ret);
goto err_subdev_open;
}
set_bit(IS_SUBDEV_EXTERNAL_USE, &subdev->state);
return 0;
err_subdev_open:
return ret;
}
int is_ischain_subdev_open(struct is_device_ischain *device,
struct is_video_ctx *vctx)
{
int ret = 0;
int ret_err = 0;
struct is_subdev *subdev;
FIMC_BUG(!device);
FIMC_BUG(!vctx);
FIMC_BUG(!GET_VIDEO(vctx));
subdev = vctx->subdev;
ret = is_subdev_open(subdev, vctx, NULL, device->instance);
if (ret) {
merr("is_subdev_open is fail(%d)", device, ret);
goto err_subdev_open;
}
set_bit(IS_SUBDEV_EXTERNAL_USE, &subdev->state);
ret = is_ischain_open_wrap(device, false);
if (ret) {
merr("is_ischain_open_wrap is fail(%d)", device, ret);
goto err_ischain_open;
}
return 0;
err_ischain_open:
ret_err = is_subdev_close(subdev);
if (ret_err)
merr("is_subdev_close is fail(%d)", device, ret_err);
clear_bit(IS_SUBDEV_EXTERNAL_USE, &subdev->state);
err_subdev_open:
return ret;
}
int is_subdev_close(struct is_subdev *subdev)
{
int ret = 0;
if (!test_bit(IS_SUBDEV_OPEN, &subdev->state)) {
mserr("subdev is already close", subdev, subdev);
ret = -EINVAL;
goto p_err;
}
subdev->leader = NULL;
subdev->vctx = NULL;
subdev->vid = 0;
clear_bit(IS_SUBDEV_OPEN, &subdev->state);
clear_bit(IS_SUBDEV_RUN, &subdev->state);
clear_bit(IS_SUBDEV_START, &subdev->state);
clear_bit(IS_SUBDEV_FORCE_SET, &subdev->state);
clear_bit(IS_SUBDEV_VOTF_USE, &subdev->state);
/* for internal use */
clear_bit(IS_SUBDEV_INTERNAL_S_FMT, &subdev->state);
p_err:
return 0;
}
int is_sensor_subdev_close(struct is_device_sensor *device,
struct is_video_ctx *vctx)
{
int ret = 0;
struct is_subdev *subdev;
FIMC_BUG(!device);
FIMC_BUG(!vctx);
subdev = vctx->subdev;
if (!subdev) {
merr("subdev is NULL", device);
ret = -EINVAL;
goto p_err;
}
vctx->subdev = NULL;
if (test_bit(IS_SENSOR_FRONT_START, &device->state)) {
merr("sudden close, call sensor_front_stop()\n", device);
ret = is_sensor_front_stop(device, true);
if (ret)
merr("is_sensor_front_stop is fail(%d)", device, ret);
}
ret = is_subdev_close(subdev);
if (ret)
merr("is_subdev_close is fail(%d)", device, ret);
clear_bit(IS_SUBDEV_EXTERNAL_USE, &subdev->state);
p_err:
return ret;
}
int is_ischain_subdev_close(struct is_device_ischain *device,
struct is_video_ctx *vctx)
{
int ret = 0;
struct is_subdev *subdev;
FIMC_BUG(!device);
FIMC_BUG(!vctx);
subdev = vctx->subdev;
if (!subdev) {
merr("subdev is NULL", device);
ret = -EINVAL;
goto p_err;
}
vctx->subdev = NULL;
ret = is_subdev_close(subdev);
if (ret) {
merr("is_subdev_close is fail(%d)", device, ret);
goto p_err;
}
clear_bit(IS_SUBDEV_EXTERNAL_USE, &subdev->state);
ret = is_ischain_close_wrap(device);
if (ret) {
merr("is_ischain_close_wrap is fail(%d)", device, ret);
goto p_err;
}
p_err:
return ret;
}
int pablo_subdev_buffer_init(struct is_subdev *is, struct vb2_buffer *vb)
{
unsigned long flags;
struct is_framemgr *framemgr = GET_SUBDEV_FRAMEMGR(is);
struct is_frame *frame;
unsigned int index = vb->index;
struct is_device_ischain *idi = GET_DEVICE(is->vctx);
if (index >= framemgr->num_frames) {
mserr("out of range (%d >= %d)", idi, is, index, framemgr->num_frames);
return -EINVAL;
}
framemgr_e_barrier_irqs(framemgr, FMGR_IDX_17, flags);
frame = &framemgr->frames[index];
memset(&frame->prfi, 0x0, sizeof(struct pablo_rta_frame_info));
frame->prfi.magic = 0x5F4D5246;
framemgr_x_barrier_irqr(framemgr, FMGR_IDX_17, flags);
return 0;
}
int is_subdev_buffer_queue(struct is_subdev *subdev,
struct vb2_buffer *vb)
{
int ret = 0;
unsigned long flags;
struct is_framemgr *framemgr = GET_SUBDEV_FRAMEMGR(subdev);
struct is_frame *frame;
unsigned int index = vb->index;
FIMC_BUG(!subdev);
FIMC_BUG(!framemgr);
FIMC_BUG(index >= framemgr->num_frames);
frame = &framemgr->frames[index];
/* 1. check frame validation */
if (unlikely(!test_bit(FRAME_MEM_INIT, &frame->mem_state))) {
mserr("frame %d is NOT init", subdev, subdev, index);
ret = EINVAL;
goto p_err;
}
/* 2. update frame manager */
framemgr_e_barrier_irqs(framemgr, FMGR_IDX_17, flags);
if (frame->state == FS_FREE) {
trans_frame(framemgr, frame, FS_REQUEST);
} else {
mserr("frame %d is invalid state(%d)\n", subdev, subdev, index, frame->state);
frame_manager_print_queues(framemgr);
ret = -EINVAL;
}
framemgr_x_barrier_irqr(framemgr, FMGR_IDX_17, flags);
p_err:
return ret;
}
int is_subdev_buffer_finish(struct is_subdev *subdev,
struct vb2_buffer *vb)
{
int ret = 0;
struct is_device_ischain *device;
struct is_framemgr *framemgr;
struct is_frame *frame;
unsigned int index = vb->index;
if (!subdev) {
warn("subdev is NULL(%d)", index);
ret = -EINVAL;
return ret;
}
if (unlikely(!test_bit(IS_SUBDEV_OPEN, &subdev->state))) {
warn("subdev was closed..(%d)", index);
ret = -EINVAL;
return ret;
}
FIMC_BUG(!subdev->vctx);
device = GET_DEVICE(subdev->vctx);
framemgr = GET_SUBDEV_FRAMEMGR(subdev);
if (unlikely(!framemgr)) {
warn("subdev's framemgr is null..(%d)", index);
ret = -EINVAL;
return ret;
}
FIMC_BUG(index >= framemgr->num_frames);
frame = &framemgr->frames[index];
framemgr_e_barrier_irq(framemgr, FMGR_IDX_18);
if (frame->state == FS_COMPLETE) {
trans_frame(framemgr, frame, FS_FREE);
} else {
merr("frame is empty from complete", device);
merr("frame(%d) is not com state(%d)", device,
index, frame->state);
frame_manager_print_queues(framemgr);
ret = -EINVAL;
}
framemgr_x_barrier_irq(framemgr, FMGR_IDX_18);
return ret;
}
static int is_subdev_start(struct is_subdev *is)
{
struct is_subdev *leader;
FIMC_BUG(!is->leader);
if (test_bit(IS_SUBDEV_START, &is->state)) {
mserr("already start", is, is);
return 0;
}
leader = is->leader;
if (test_bit(IS_SUBDEV_START, &leader->state)) {
mserr("leader%d is ALREADY started", is, is, leader->id);
return -EINVAL;
}
set_bit(IS_SUBDEV_START, &is->state);
return 0;
}
static int is_subdev_s_fmt(struct is_subdev *is, struct is_queue *iq)
{
u32 pixelformat, width, height;
FIMC_BUG(!is->vctx);
FIMC_BUG(!iq->framecfg.format);
pixelformat = iq->framecfg.format->pixelformat;
width = iq->framecfg.width;
height = iq->framecfg.height;
switch (is->id) {
case ENTRY_M0P:
case ENTRY_M1P:
case ENTRY_M2P:
case ENTRY_M3P:
case ENTRY_M4P:
if (width % 8)
mserr("width(%d) of format(%c%c%c%c) is not multiple of 8: need to check size",
is, is, width,
(char)((pixelformat >> 0) & 0xFF),
(char)((pixelformat >> 8) & 0xFF),
(char)((pixelformat >> 16) & 0xFF),
(char)((pixelformat >> 24) & 0xFF));
break;
default:
break;
}
is->output.width = width;
is->output.height = height;
is->output.crop.x = 0;
is->output.crop.y = 0;
is->output.crop.w = is->output.width;
is->output.crop.h = is->output.height;
is->bits_per_pixel = iq->framecfg.format->bitsperpixel[0];
is->memory_bitwidth = iq->framecfg.format->hw_bitwidth;
is->sbwc_type = (iq->framecfg.hw_pixeltype & PIXEL_TYPE_EXTRA_MASK)
>> PIXEL_TYPE_EXTRA_SHIFT;
return 0;
}
static int is_sensor_subdev_start(void *device, struct is_queue *iq)
{
struct is_device_sensor *ids = (struct is_device_sensor *)device;
struct is_video_ctx *ivc = container_of(iq, struct is_video_ctx, queue);
struct is_subdev *is = ivc->subdev;
if (!is) {
merr("subdev is NULL", ids);
return -EINVAL;
}
if (!test_bit(IS_SENSOR_S_INPUT, &ids->state)) {
mserr("device is not yet init", ids, is);
return -EINVAL;
}
if (test_bit(IS_SUBDEV_START, &is->state)) {
mserr("already start", is, is);
return 0;
}
set_bit(IS_SUBDEV_START, &is->state);
return 0;
}
static int is_sensor_subdev_stop(void *device, struct is_queue *iq)
{
struct is_device_sensor *ids = (struct is_device_sensor *)device;
struct is_video_ctx *ivc = container_of(iq, struct is_video_ctx, queue);
struct is_subdev *is = ivc->subdev;
struct is_framemgr *framemgr;
unsigned long flags;
struct is_frame *frame;
if (!is) {
merr("subdev is NULL", ids);
return -EINVAL;
}
if (!test_bit(IS_SUBDEV_START, &is->state)) {
merr("already stop", ids);
return 0;
}
framemgr = GET_SUBDEV_FRAMEMGR(is);
if (!framemgr) {
merr("framemgr is NULL", ids);
return -EINVAL;
}
framemgr_e_barrier_irqs(framemgr, FMGR_IDX_16, flags);
frame = peek_frame(framemgr, FS_PROCESS);
while (frame) {
CALL_VOPS(is->vctx, done, frame->index, VB2_BUF_STATE_ERROR);
trans_frame(framemgr, frame, FS_COMPLETE);
frame = peek_frame(framemgr, FS_PROCESS);
}
frame = peek_frame(framemgr, FS_REQUEST);
while (frame) {
CALL_VOPS(is->vctx, done, frame->index, VB2_BUF_STATE_ERROR);
trans_frame(framemgr, frame, FS_COMPLETE);
frame = peek_frame(framemgr, FS_REQUEST);
}
framemgr_x_barrier_irqr(framemgr, FMGR_IDX_16, flags);
clear_bit(IS_SUBDEV_RUN, &is->state);
clear_bit(IS_SUBDEV_START, &is->state);
clear_bit(IS_SUBDEV_VOTF_USE, &is->state);
return 0;
}
static int is_sensor_subdev_s_fmt(void *device, struct is_queue *iq)
{
struct is_device_sensor *ids = (struct is_device_sensor *)device;
struct is_video_ctx *ivc = container_of(iq, struct is_video_ctx, queue);
struct is_subdev *is = ivc->subdev;
int ret;
if (!is) {
merr("subdev is NULL", ids);
return -EINVAL;
}
if (test_bit(IS_SUBDEV_INTERNAL_USE, &is->state)) {
mswarn("%s: It is sharing with internal use.", is, is, __func__);
is->bits_per_pixel = iq->framecfg.format->bitsperpixel[0];
} else {
ret = is_subdev_s_fmt(is, iq);
if (ret) {
merr("is_subdev_s_format is fail(%d)", ids, ret);
return ret;
}
}
return 0;
}
int is_sensor_subdev_reqbufs(void *device, struct is_queue *iq, u32 count)
{
struct is_device_sensor *ids = (struct is_device_sensor *)device;
struct is_video_ctx *ivc = container_of(iq, struct is_video_ctx, queue);
struct is_subdev *is = ivc->subdev;
struct is_framemgr *framemgr;
struct is_frame *frame;
int i;
if (!is) {
merr("subdev is NULL", ids);
return -EINVAL;
}
framemgr = GET_SUBDEV_FRAMEMGR(is);
if (!framemgr) {
merr("framemgr is NULL", ids);
return -EINVAL;
}
for (i = 0; i < count; i++) {
frame = &framemgr->frames[i];
frame->subdev = is;
}
return 0;
}
static struct is_queue_ops is_sensor_subdev_qops = {
.start_streaming = is_sensor_subdev_start,
.stop_streaming = is_sensor_subdev_stop,
.s_fmt = is_sensor_subdev_s_fmt,
.reqbufs = is_sensor_subdev_reqbufs,
};
struct is_queue_ops *is_get_sensor_subdev_qops(void)
{
return &is_sensor_subdev_qops;
}
static int is_ischain_subdev_start(void *device, struct is_queue *iq)
{
int ret;
struct is_device_ischain *idi = (struct is_device_ischain *)device;
struct is_video_ctx *ivc = container_of(iq, struct is_video_ctx, queue);
struct is_subdev *is = ivc->subdev;
if (!is) {
merr("subdev is NULL", idi);
return -EINVAL;
}
if (!test_bit(IS_ISCHAIN_INIT, &idi->state)) {
mserr("device is not yet init", idi, is);
return -EINVAL;
}
ret = is_subdev_start(is);
if (ret) {
mserr("is_subdev_start is fail(%d)", idi, is, ret);
return ret;
}
return 0;
}
static int is_ischain_subdev_stop(void *device, struct is_queue *iq)
{
struct is_device_ischain *idi = (struct is_device_ischain *)device;
struct is_video_ctx *ivc = container_of(iq, struct is_video_ctx, queue);
struct is_subdev *is = ivc->subdev;
struct is_framemgr *framemgr;
unsigned long flags;
struct is_frame *frame;
if (!is) {
merr("subdev is NULL", idi);
return -EINVAL;
}
if (!test_bit(IS_SUBDEV_START, &is->state)) {
merr("already stop", idi);
return 0;
}
if (is->leader && test_bit(IS_SUBDEV_START, &is->leader->state)) {
merr("leader%d is NOT stopped", idi, is->leader->id);
return 0;
}
framemgr = GET_SUBDEV_FRAMEMGR(is);
if (!framemgr) {
merr("framemgr is NULL", idi);
return -EINVAL;
}
framemgr_e_barrier_irqs(framemgr, FMGR_IDX_16, flags);
if (framemgr->queued_count[FS_PROCESS] > 0) {
framemgr_x_barrier_irqr(framemgr, FMGR_IDX_16, flags);
merr("being processed, can't stop", idi);
return -EINVAL;
}
frame = peek_frame(framemgr, FS_REQUEST);
while (frame) {
CALL_VOPS(is->vctx, done, frame->index, VB2_BUF_STATE_ERROR);
trans_frame(framemgr, frame, FS_COMPLETE);
frame = peek_frame(framemgr, FS_REQUEST);
}
framemgr_x_barrier_irqr(framemgr, FMGR_IDX_16, flags);
clear_bit(IS_SUBDEV_RUN, &is->state);
clear_bit(IS_SUBDEV_START, &is->state);
clear_bit(IS_SUBDEV_VOTF_USE, &is->state);
return 0;
}
static int is_ischain_subdev_s_fmt(void *device, struct is_queue *iq)
{
int ret;
struct is_device_ischain *idi = (struct is_device_ischain *)device;
struct is_video_ctx *ivc = container_of(iq, struct is_video_ctx, queue);
struct is_subdev *is = ivc->subdev;
if (!is) {
merr("subdev is NULL", idi);
return -EINVAL;
}
ret = is_subdev_s_fmt(is, iq);
if (ret) {
merr("is_subdev_s_format is fail(%d)", idi, ret);
return ret;
}
return 0;
}
static struct is_queue_ops is_ischain_subdev_qops = {
.start_streaming = is_ischain_subdev_start,
.stop_streaming = is_ischain_subdev_stop,
.s_fmt = is_ischain_subdev_s_fmt,
};
struct is_queue_ops *is_get_ischain_subdev_qops(void)
{
return &is_ischain_subdev_qops;
}
static int is_subdev_internal_alloc_buffer(struct is_subdev *subdev)
{
int ret;
int i, j;
unsigned int flags = 0;
u32 width, height;
u32 out_buf_size; /* single buffer */
u32 cap_buf_size[IS_MAX_PLANES];
u32 total_size; /* multi-buffer for FRO */
u32 total_alloc_size = 0;
u32 num_planes;
struct is_core *core;
struct is_mem *mem;
struct is_frame *frame;
struct is_queue *queue;
struct is_framemgr *shared_framemgr = NULL;
int use_shared_framemgr;
int k;
struct is_sub_node *snode = NULL;
int cap_node_num;
u32 batch_num;
u32 payload_size, header_size;
u32 sbwc_block_width, sbwc_block_height;
char heapname[25];
FIMC_BUG(!subdev);
if (subdev->buffer_num > SUBDEV_INTERNAL_BUF_MAX || subdev->buffer_num <= 0) {
mserr("invalid internal buffer num size(%d)",
subdev, subdev, subdev->buffer_num);
return -EINVAL;
}
core = is_get_is_core();
if (!core) {
mserr("core is NULL", subdev, subdev);
return -ENODEV;
}
mem = is_hw_get_iommu_mem(subdev->vid);
queue = GET_SUBDEV_QUEUE(subdev);
subdev->use_shared_framemgr = is_subdev_internal_use_shared_framemgr(subdev);
ret = is_subdev_internal_get_buffer_size(subdev, &width, &height,
&sbwc_block_width, &sbwc_block_height);
if (ret) {
mserr("is_subdev_internal_get_buffer_size is fail(%d)", subdev, subdev, ret);
return -EINVAL;
}
switch (subdev->sbwc_type) {
case COMP:
payload_size = is_hw_dma_get_payload_stride(
COMP, subdev->bits_per_pixel, width,
0, 0, sbwc_block_width, sbwc_block_height) *
DIV_ROUND_UP(height, sbwc_block_height);
header_size = is_hw_dma_get_header_stride(width, sbwc_block_width, 32) *
DIV_ROUND_UP(height, sbwc_block_height);
out_buf_size = payload_size + header_size;
break;
case COMP_LOSS:
payload_size = is_hw_dma_get_payload_stride(
COMP_LOSS, subdev->bits_per_pixel, width,
0, subdev->lossy_byte32num, sbwc_block_width, sbwc_block_height) *
DIV_ROUND_UP(height, sbwc_block_height);
out_buf_size = payload_size;
break;
default:
out_buf_size =
ALIGN(DIV_ROUND_UP(width * subdev->memory_bitwidth, BITS_PER_BYTE), 32) * height;
break;
}
if (out_buf_size <= 0) {
mserr("wrong internal subdev buffer size(%d)",
subdev, subdev, out_buf_size);
return -EINVAL;
}
batch_num = subdev->batch_num;
memset(heapname, '\0', sizeof(heapname));
ret = is_subdev_internal_get_out_node_info(subdev, &num_planes, core->scenario, heapname);
if (ret)
return ret;
cap_node_num = is_subdev_internal_get_cap_node_num(subdev);
if (cap_node_num < 0)
return -EINVAL;
total_size = out_buf_size * batch_num * num_planes;
if (test_bit(IS_SUBDEV_IOVA_EXTENSION_USE, &subdev->state)) {
if (IS_ENABLED(USE_IOVA_EXTENSION))
flags |= ION_EXYNOS_FLAG_IOVA_EXTENSION;
else
total_size *= 2;
msinfo(" %s IOVA_EXTENSION_USE\n", subdev, subdev, __func__);
}
if (!strncmp(heapname, SECURE_HEAPNAME, 20)) {
if (IS_ENABLED(CONFIG_ION)) {
flags |= ION_EXYNOS_FLAG_PROTECTED;
msinfo(" %s scenario(%d) ION flag(0x%x)\n", subdev, subdev,
__func__, core->scenario, flags);
}
}
ret = frame_manager_open(&subdev->internal_framemgr, subdev->buffer_num);
if (ret) {
mserr("is_frame_open is fail(%d)", subdev, subdev, ret);
ret = -EINVAL;
goto err_open_framemgr;
}
is_subdev_internal_lock_shared_framemgr(subdev);
use_shared_framemgr = is_subdev_internal_get_shared_framemgr(subdev, &shared_framemgr, width, height);
if (use_shared_framemgr < 0) {
mserr("is_subdev_internal_get_shared_framemgr is fail(%d)", subdev, subdev, use_shared_framemgr);
ret = -EINVAL;
goto err_open_shared_framemgr;
}
#if defined(USE_CAMERA_HEAP)
if (core->scenario != IS_SCENARIO_SECURE && subdev->instance == 0) {
memset(heapname, '\0', sizeof(heapname));
strcpy(heapname, CAMERA_HEAP_NAME);
}
#endif
for (i = 0; i < subdev->buffer_num; i++) {
if (use_shared_framemgr > 0) {
subdev->pb_subdev[i] = shared_framemgr->frames[i].pb_output;
} else {
if (num_planes) {
#if defined(USE_CAMERA_HEAP)
subdev->pb_subdev[i] =
CALL_PTR_MEMOP(mem, alloc, mem->priv, total_size, heapname,
flags);
#else
subdev->pb_subdev[i] =
CALL_PTR_MEMOP(mem, alloc, mem->priv, total_size, heapname,
flags);
#endif
if (IS_ERR_OR_NULL(subdev->pb_subdev[i])) {
mserr("failed to allocate buffer for internal subdev",
subdev, subdev);
subdev->pb_subdev[i] = NULL;
ret = -ENOMEM;
goto err_allocate_pb_subdev;
}
} else {
subdev->pb_subdev[i] = NULL;
}
if (shared_framemgr)
shared_framemgr->frames[i].pb_output = subdev->pb_subdev[i];
}
total_alloc_size += total_size;
}
for (i = 0; i < subdev->buffer_num; i++) {
frame = &subdev->internal_framemgr.frames[i];
frame->subdev = subdev;
frame->pb_output = subdev->pb_subdev[i];
/* TODO : support multi-plane */
frame->planes = 1;
frame->num_buffers = batch_num;
if (IS_ENABLED(LOGICAL_VIDEO_NODE)) {
snode = &frame->out_node;
if (subdev->pb_subdev[i]) {
snode->sframe[0].dva[0] = frame->dvaddr_buffer[0] =
CALL_BUFOP(subdev->pb_subdev[i], dvaddr,
subdev->pb_subdev[i]);
if (!strncmp(heapname, SECURE_HEAPNAME, 20))
frame->kvaddr_buffer[0] = 0;
else
frame->kvaddr_buffer[0] =
CALL_BUFOP(subdev->pb_subdev[i], kvaddr,
subdev->pb_subdev[i]);
}
frame->size[0] = out_buf_size;
set_bit(FRAME_MEM_INIT, &frame->mem_state);
for (j = 1; j < batch_num * num_planes; j++) {
snode->sframe[0].dva[j] = frame->dvaddr_buffer[j] =
frame->dvaddr_buffer[j - 1] + out_buf_size;
frame->kvaddr_buffer[j] =
frame->kvaddr_buffer[j - 1] + out_buf_size;
}
snode->sframe[0].id = subdev->vid;
snode->sframe[0].num_planes = num_planes;
if (queue)
snode->sframe[0].hw_pixeltype =
queue->framecfg.hw_pixeltype;
snode = &frame->cap_node;
for (j = 0; j < cap_node_num; j++) {
memset(heapname, '\0', sizeof(heapname));
ret = is_subdev_internal_get_cap_node_info(subdev,
&snode->sframe[j].id,
&snode->sframe[j].num_planes,
cap_buf_size, j, core->scenario, heapname);
if (ret) {
subdev->pb_capture_subdev[i][j] = NULL;
goto err_allocate_pb_capture_subdev;
}
if (queue)
snode->sframe[j].hw_pixeltype =
queue->framecfg.hw_pixeltype;
total_size = 0;
for (k = 0; k < snode->sframe[j].num_planes; k++)
total_size += cap_buf_size[k];
if (!strncmp(heapname, SECURE_HEAPNAME, 20))
flags |= ION_EXYNOS_FLAG_PROTECTED;
else
flags = 0;
if (use_shared_framemgr > 0) {
subdev->pb_capture_subdev[i][j] =
shared_framemgr->frames[i].pb_capture[j];
} else {
if (total_size) {
#if defined(USE_CAMERA_HEAP)
if (core->scenario != IS_SCENARIO_SECURE && subdev->instance == 0)
strcpy(heapname, CAMERA_HEAP_NAME);
#endif
subdev->pb_capture_subdev[i][j] =
CALL_PTR_MEMOP(mem, alloc,
mem->priv, total_size,
heapname, flags);
if (IS_ERR_OR_NULL(subdev->pb_capture_subdev[i][j])) {
mserr("failed to allocate buffer for internal subdev",
subdev, subdev);
subdev->pb_capture_subdev[i][j] = NULL;
ret = -ENOMEM;
goto err_allocate_pb_capture_subdev;
}
} else {
subdev->pb_capture_subdev[i][j] = NULL;
}
if (shared_framemgr)
shared_framemgr->frames[i].pb_capture[j] =
subdev->pb_capture_subdev[i][j];
}
frame->pb_capture[j] = subdev->pb_capture_subdev[i][j];
total_alloc_size += total_size;
if (subdev->pb_capture_subdev[i][j]) {
snode->sframe[j].backup_dva[0] = snode->sframe[j].dva[0] =
CALL_BUFOP(subdev->pb_capture_subdev[i][j],
dvaddr, subdev->pb_capture_subdev[i][j]);
if (!strncmp(heapname, SECURE_HEAPNAME, 20))
snode->sframe[j].kva[0] = 0;
else
snode->sframe[j].kva[0] =
CALL_BUFOP(subdev->pb_capture_subdev[i][j],
kvaddr, subdev->pb_capture_subdev[i][j]);
}
for (k = 1; k < snode->sframe[j].num_planes; k++) {
snode->sframe[j].backup_dva[k] =
snode->sframe[j].dva[k] =
snode->sframe[j].dva[k - 1] + cap_buf_size[k - 1];
snode->sframe[j].kva[k] =
snode->sframe[j].kva[k - 1] + cap_buf_size[k - 1];
}
}
} else {
frame->dvaddr_buffer[0] =
CALL_BUFOP(subdev->pb_subdev[i], dvaddr,
subdev->pb_subdev[i]);
frame->kvaddr_buffer[0] =
CALL_BUFOP(subdev->pb_subdev[i],
kvaddr, subdev->pb_subdev[i]);
frame->size[0] = out_buf_size;
set_bit(FRAME_MEM_INIT, &frame->mem_state);
for (j = 1; j < batch_num; j++) {
frame->dvaddr_buffer[j] =
frame->dvaddr_buffer[j - 1] + out_buf_size;
frame->kvaddr_buffer[j] =
frame->kvaddr_buffer[j - 1] + out_buf_size;
}
}
#if defined(SDC_HEADER_GEN)
if (subdev->id == ENTRY_PAF) {
const u32 *header = NULL;
u32 byte_per_line;
u32 header_size;
u32 width = subdev->output.width;
if (width == SDC_WIDTH_HD)
header = is_sdc_header_hd;
else if (width == SDC_WIDTH_FHD)
header = is_sdc_header_fhd;
else
mserr("invalid SDC size: width(%d)", subdev, subdev, width);
if (header) {
byte_per_line = ALIGN(width / 2 * 10 / BITS_PER_BYTE, 16);
header_size = byte_per_line * SDC_HEADER_LINE;
memcpy((void *)frame->kvaddr_buffer[0], header, header_size);
#if !defined(MODULE)
__flush_dcache_area((void *)frame->kvaddr_buffer[0], header_size);
#endif
msinfo("Write SDC header: width(%d) size(%d)\n",
subdev, subdev, width, header_size);
}
}
#endif
}
is_subdev_internal_unlock_shared_framemgr(subdev);
msinfo(" %s (size: %dx%d, bpp: %d, total: %d, buffer_num: %d, batch_num: %d, shared_framemgr(ref: %d))",
subdev, subdev, __func__,
width, height, subdev->bits_per_pixel,
total_alloc_size, subdev->buffer_num, batch_num, use_shared_framemgr);
return 0;
err_allocate_pb_capture_subdev:
if (IS_ENABLED(LOGICAL_VIDEO_NODE)) {
do {
while (j-- > 0) {
if (subdev->pb_capture_subdev[i][j])
CALL_VOID_BUFOP(subdev->pb_capture_subdev[i][j],
free, subdev->pb_capture_subdev[i][j]);
}
j = cap_node_num;
} while (i-- > 0);
}
err_allocate_pb_subdev:
while (i-- > 0) {
if (subdev->pb_subdev[i])
CALL_VOID_BUFOP(subdev->pb_subdev[i], free, subdev->pb_subdev[i]);
}
is_subdev_internal_put_shared_framemgr(subdev);
err_open_shared_framemgr:
is_subdev_internal_unlock_shared_framemgr(subdev);
frame_manager_close(&subdev->internal_framemgr);
err_open_framemgr:
return ret;
};
static int is_subdev_internal_free_buffer(struct is_subdev *subdev)
{
int ret = 0;
int i;
int j;
u32 cap_node_num;
FIMC_BUG(!subdev);
if (subdev->internal_framemgr.num_frames == 0) {
mswarn(" already free internal buffer", subdev, subdev);
return -EINVAL;
}
frame_manager_close(&subdev->internal_framemgr);
is_subdev_internal_lock_shared_framemgr(subdev);
ret = is_subdev_internal_put_shared_framemgr(subdev);
if (!ret) {
for (i = 0; i < subdev->buffer_num; i++) {
if (subdev->pb_subdev[i])
CALL_VOID_BUFOP(subdev->pb_subdev[i], free, subdev->pb_subdev[i]);
}
if (IS_ENABLED(LOGICAL_VIDEO_NODE)) {
cap_node_num = is_subdev_internal_get_cap_node_num(subdev);
for (i = 0; i < subdev->buffer_num; i++) {
for (j = 0; j < cap_node_num; j++) {
if (subdev->pb_capture_subdev[i][j])
CALL_VOID_BUFOP(subdev->pb_capture_subdev[i][j],
free, subdev->pb_capture_subdev[i][j]);
}
}
}
}
is_subdev_internal_unlock_shared_framemgr(subdev);
msinfo(" %s. shared_framemgr refcount: %d\n", subdev, subdev, __func__, ret);
return 0;
};
static int _is_subdev_internal_start(struct is_subdev *subdev)
{
int ret = 0;
int j;
struct is_framemgr *framemgr;
struct is_frame *frame;
unsigned long flags;
if (test_bit(IS_SUBDEV_START, &subdev->state)) {
mswarn(" subdev already start", subdev, subdev);
goto p_err;
}
/* qbuf a setting num of buffers before stream on */
framemgr = GET_SUBDEV_I_FRAMEMGR(subdev);
if (unlikely(!framemgr)) {
mserr(" subdev's framemgr is null", subdev, subdev);
ret = -EINVAL;
goto p_err;
}
for (j = 0; j < framemgr->num_frames; j++) {
frame = &framemgr->frames[j];
/* 1. check frame validation */
if (unlikely(!test_bit(FRAME_MEM_INIT, &frame->mem_state))) {
mserr("frame %d is NOT init", subdev, subdev, j);
ret = -EINVAL;
goto p_err;
}
/* 2. update frame manager */
framemgr_e_barrier_irqs(framemgr, FMGR_IDX_17, flags);
if (frame->state != FS_FREE) {
mserr("frame %d is invalid state(%d)\n",
subdev, subdev, j, frame->state);
frame_manager_print_queues(framemgr);
ret = -EINVAL;
goto p_err;
}
framemgr_x_barrier_irqr(framemgr, FMGR_IDX_17, flags);
}
set_bit(IS_SUBDEV_START, &subdev->state);
p_err:
return ret;
}
static int _is_subdev_internal_stop(struct is_subdev *subdev)
{
int ret = 0;
struct is_framemgr *framemgr;
enum is_frame_state state;
if (!test_bit(IS_SUBDEV_START, &subdev->state)) {
mserr("already stopped", subdev, subdev);
ret = -EINVAL;
goto p_err;
}
framemgr = GET_SUBDEV_I_FRAMEMGR(subdev);
if (unlikely(!framemgr)) {
mserr(" subdev's framemgr is null", subdev, subdev);
ret = -EINVAL;
goto p_err;
}
for (state = FS_REQUEST; state <= FS_COMPLETE; state++) {
unsigned int retry = 10;
unsigned int q_count = framemgr->queued_count[state];
while (--retry && q_count) {
mswarn("%s(%d) waiting...", subdev, subdev,
frame_state_name[state], q_count);
usleep_range(5000, 5100);
q_count = framemgr->queued_count[state];
}
if (!retry)
mswarn("waiting(until frame empty) is fail", subdev, subdev);
}
clear_bit(IS_SUBDEV_START, &subdev->state);
clear_bit(IS_SUBDEV_VOTF_USE, &subdev->state);
p_err:
return ret;
}
int is_subdev_internal_start(void *device, enum is_device_type type, struct is_subdev *subdev)
{
int ret = 0;
FIMC_BUG(!device);
if (!test_bit(IS_SUBDEV_INTERNAL_USE, &subdev->state)) {
mserr("subdev is not in INTERNAL_USE state.", subdev, subdev);
return -EINVAL;
}
if (!test_bit(IS_SUBDEV_INTERNAL_S_FMT, &subdev->state)) {
mserr("subdev is not in s_fmt state.", subdev, subdev);
return -EINVAL;
}
if (subdev->internal_framemgr.num_frames > 0) {
mswarn("%s already internal buffer alloced, re-alloc after free",
subdev, subdev, __func__);
ret = is_subdev_internal_free_buffer(subdev);
if (ret) {
mserr("subdev internal free buffer is fail", subdev, subdev);
ret = -EINVAL;
goto p_err;
}
}
ret = is_subdev_internal_alloc_buffer(subdev);
if (ret) {
mserr("ischain internal alloc buffer fail(%d)", subdev, subdev, ret);
goto p_err;
}
ret = _is_subdev_internal_start(subdev);
if (ret) {
mserr("_is_subdev_internal_start fail(%d)", subdev, subdev, ret);
goto p_err;
}
p_err:
msinfo(" %s(%s)(%d)\n", subdev, subdev, __func__, subdev->data_type, ret);
return ret;
};
KUNIT_EXPORT_SYMBOL(is_subdev_internal_start);
int is_subdev_internal_stop(void *device, enum is_device_type type, struct is_subdev *subdev)
{
int ret = 0;
if (!test_bit(IS_SUBDEV_INTERNAL_USE, &subdev->state)) {
mserr("subdev is not in INTERNAL_USE state.", subdev, subdev);
return -EINVAL;
}
ret = _is_subdev_internal_stop(subdev);
if (ret) {
mserr("_is_subdev_internal_stop fail(%d)", subdev, subdev, ret);
goto p_err;
}
ret = is_subdev_internal_free_buffer(subdev);
if (ret) {
mserr("subdev internal free buffer is fail(%d)", subdev, subdev, ret);
ret = -EINVAL;
goto p_err;
}
p_err:
msinfo(" %s(%s)(%d)\n", subdev, subdev, __func__, subdev->data_type, ret);
return ret;
};
KUNIT_EXPORT_SYMBOL(is_subdev_internal_stop);
int is_subdev_internal_s_format(void *device, enum is_device_type type, struct is_subdev *subdev,
u32 width, u32 height, u32 bits_per_pixel, u32 sbwc_type, u32 lossy_byte32num,
u32 buffer_num, const char *type_name)
{
int ret = 0;
struct is_device_ischain *ischain;
struct is_device_sensor *sensor;
struct is_sensor_cfg *sensor_cfg;
u32 batch_num = 1;
if (!test_bit(IS_SUBDEV_INTERNAL_USE, &subdev->state)) {
mserr("subdev is not in INTERNAL_USE state.", subdev, subdev);
return -EINVAL;
}
if (!test_bit(IS_SUBDEV_OPEN, &subdev->state)) {
mserr("subdev is not in OPEN state.", subdev, subdev);
return -EINVAL;
}
if (width == 0 || height == 0) {
mserr("wrong internal vc size(%d x %d)", subdev, subdev, width, height);
return -EINVAL;
}
switch (type) {
case IS_DEVICE_SENSOR:
break;
case IS_DEVICE_ISCHAIN:
FIMC_BUG(!device);
ischain = (struct is_device_ischain *)device;
if (subdev->id == ENTRY_PAF) {
if (test_bit(IS_ISCHAIN_REPROCESSING, &ischain->state))
break;
sensor = ischain->sensor;
if (!sensor) {
mserr("failed to get sensor", subdev, subdev);
return -EINVAL;
}
sensor_cfg = sensor->cfg;
if (!sensor_cfg) {
mserr("failed to get senso_cfgr", subdev, subdev);
return -EINVAL;
}
if (sensor_cfg->ex_mode == EX_DUALFPS_960)
batch_num = 16;
else if (sensor_cfg->ex_mode == EX_DUALFPS_480)
batch_num = 8;
}
break;
default:
err("invalid device_type(%d)", type);
ret = -EINVAL;
break;
}
subdev->output.width = width;
subdev->output.height = height;
subdev->output.crop.x = 0;
subdev->output.crop.y = 0;
subdev->output.crop.w = subdev->output.width;
subdev->output.crop.h = subdev->output.height;
if (!subdev->bits_per_pixel)
subdev->bits_per_pixel = bits_per_pixel;
subdev->memory_bitwidth = bits_per_pixel;
subdev->sbwc_type = sbwc_type;
subdev->lossy_byte32num = lossy_byte32num;
subdev->buffer_num = buffer_num;
subdev->batch_num = batch_num;
snprintf(subdev->data_type, sizeof(subdev->data_type), "%s", type_name);
set_bit(IS_SUBDEV_INTERNAL_S_FMT, &subdev->state);
return ret;
};
int is_subdev_internal_g_bpp(void *device, enum is_device_type type, struct is_subdev *subdev,
struct is_sensor_cfg *sensor_cfg)
{
int bpp;
switch (subdev->id) {
case ENTRY_YPP:
return 10;
case ENTRY_MCS:
return 10;
case ENTRY_PAF:
if (!sensor_cfg)
return BITS_PER_BYTE *2;
switch (sensor_cfg->output[CSI_VIRTUAL_CH_0].hwformat) {
case HW_FORMAT_RAW10:
bpp = 10;
break;
case HW_FORMAT_RAW12:
bpp = 12;
break;
default:
bpp = BITS_PER_BYTE * 2;
break;
}
if (sensor_cfg->votf && sensor_cfg->img_pd_ratio == IS_IMG_PD_RATIO_1_1)
set_bit(IS_SUBDEV_IOVA_EXTENSION_USE, &subdev->state);
else
clear_bit(IS_SUBDEV_IOVA_EXTENSION_USE, &subdev->state);
return bpp;
default:
return BITS_PER_BYTE * 2; /* 2byte = 16bits */
}
}
int is_subdev_internal_open(void *device, enum is_device_type type, int vid,
struct is_subdev *subdev)
{
int ret = 0;
struct is_device_sensor *sensor;
struct is_device_ischain *ischain;
if (test_bit(IS_SUBDEV_INTERNAL_USE, &subdev->state)) {
mswarn("already INTERNAL_USE state", subdev, subdev);
goto p_err;
}
switch (type) {
case IS_DEVICE_SENSOR:
sensor = (struct is_device_sensor *)device;
if (!test_bit(IS_SUBDEV_OPEN, &subdev->state)) {
ret = is_subdev_open(subdev, NULL, NULL, sensor->instance);
if (ret) {
mserr("is_subdev_open is fail(%d)", subdev, subdev, ret);
goto p_err;
}
}
msinfo("[SS%d][V:%d] %s\n", sensor, subdev, sensor->device_id, vid, __func__);
break;
case IS_DEVICE_ISCHAIN:
ischain = (struct is_device_ischain *)device;
ret = is_subdev_open(subdev, NULL, NULL, ischain->instance);
if (ret) {
mserr("is_subdev_open is fail(%d)", subdev, subdev, ret);
goto p_err;
}
msinfo("[V:%d] %s\n", subdev, subdev, vid, __func__);
break;
default:
err("invalid device_type(%d)", type);
ret = -EINVAL;
break;
}
subdev->vid = vid;
set_bit(IS_SUBDEV_INTERNAL_USE, &subdev->state);
p_err:
return ret;
};
int is_subdev_internal_close(void *device, enum is_device_type type, struct is_subdev *subdev)
{
int ret = 0;
if (!test_bit(IS_SUBDEV_INTERNAL_USE, &subdev->state)) {
mserr("subdev is not in INTERNAL_USE state.", subdev, subdev);
return -EINVAL;
}
if (!test_bit(IS_SUBDEV_EXTERNAL_USE, &subdev->state)) {
ret = is_subdev_close(subdev);
if (ret)
mserr("is_subdev_close is fail(%d)", subdev, subdev, ret);
}
clear_bit(IS_SUBDEV_INTERNAL_USE, &subdev->state);
return ret;
};