777 lines
19 KiB
C
Executable file
777 lines
19 KiB
C
Executable file
/*
|
|
* Samsung Exynos SoC series Pablo driver
|
|
*
|
|
* Copyright (c) 2019 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/errno.h>
|
|
|
|
#include "votf/camerapp-votf.h"
|
|
#include "is-votfmgr.h"
|
|
#include "is-votf-id-table.h"
|
|
#include "is-groupmgr.h"
|
|
#include "is-device-ischain.h"
|
|
#include "is-device-sensor.h"
|
|
|
|
/* This function should be called in TRS */
|
|
static int is_votf_get_votf_info(struct is_group *group, struct votf_info *src_info,
|
|
struct votf_info *dst_info, char *caller_fn)
|
|
{
|
|
int ret;
|
|
unsigned int src_id, src_entry, dst_id, dst_entry;
|
|
struct is_device_sensor *sensor;
|
|
struct is_group *src_gr = NULL;
|
|
struct is_group *dst_gr = NULL;
|
|
int dma_ch;
|
|
struct is_sensor_cfg *sensor_cfg;
|
|
u32 votf_mode = VOTF_NORMAL;
|
|
|
|
FIMC_BUG(!group);
|
|
|
|
is_votf_get_master_vinfo(group, &src_gr, &src_id, &src_entry);
|
|
if (!src_gr) {
|
|
mgerr("Failed to get master vinfo(%s)", group, group, caller_fn);
|
|
return -EINVAL;
|
|
}
|
|
|
|
is_votf_get_slave_vinfo(group, &dst_gr, &dst_id, &dst_entry);
|
|
|
|
if (src_gr->device_type == IS_DEVICE_SENSOR) {
|
|
/*
|
|
* The sensor group id should be re calculated,
|
|
* because CSIS WDMA is not matched with sensor group id.
|
|
*/
|
|
sensor = src_gr->sensor;
|
|
|
|
sensor_cfg = sensor->cfg;
|
|
if (!sensor_cfg) {
|
|
mgerr("failed to get sensor_cfg(%s)", group, group, caller_fn);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = v4l2_subdev_call(sensor->subdev_csi, core, ioctl,
|
|
SENSOR_IOCTL_G_DMA_CH, &dma_ch);
|
|
if (ret) {
|
|
mgerr("failed to get dma_ch(%s)", group, group, caller_fn);
|
|
return ret;
|
|
}
|
|
|
|
src_id = GROUP_ID_SS0 + dma_ch;
|
|
|
|
if (sensor_cfg->ex_mode == EX_DUALFPS_480 ||
|
|
sensor_cfg->ex_mode == EX_DUALFPS_960)
|
|
votf_mode = VOTF_FRS;
|
|
} else {
|
|
src_id = src_gr->id;
|
|
}
|
|
|
|
src_info->service = TWS;
|
|
src_info->ip = is_get_votf_ip(src_id);
|
|
src_info->id = is_get_votf_id(src_id, src_entry);
|
|
src_info->mode = votf_mode;
|
|
|
|
dst_info->service = TRS;
|
|
dst_info->ip = is_get_votf_ip(dst_id);
|
|
dst_info->id = is_get_votf_id(dst_id, dst_entry);
|
|
dst_info->mode = votf_mode;
|
|
|
|
if (caller_fn)
|
|
mginfo("[%s] IMG: %s(TWS[ip/id:0x%x/%d]-TRS[ip/id:0x%x:%d] mode %d)\n",
|
|
group, group, caller_fn, __func__, src_info->ip, src_info->id, dst_info->ip, dst_info->id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int is_votf_get_pd_votf_info(struct is_group *group, struct votf_info *src_info,
|
|
struct votf_info *dst_info, char *caller_fn)
|
|
{
|
|
int ret;
|
|
struct is_device_sensor *sensor;
|
|
struct is_group *src_gr;
|
|
unsigned int src_gr_id;
|
|
struct is_subdev *src_sd;
|
|
int dma_ch;
|
|
struct is_sensor_cfg *sensor_cfg;
|
|
int pd_mode;
|
|
u32 votf_mode = VOTF_NORMAL;
|
|
|
|
FIMC_BUG(!group);
|
|
FIMC_BUG(!group->prev);
|
|
FIMC_BUG(!group->prev->junction);
|
|
|
|
src_gr = group->prev;
|
|
src_sd = group->prev->junction;
|
|
|
|
if (src_gr->device_type == IS_DEVICE_SENSOR &&
|
|
group->id >= GROUP_ID_PAF0 &&
|
|
group->id <= GROUP_ID_PAF3) {
|
|
/*
|
|
* The sensor group id should be re calculated,
|
|
* because CSIS WDMA is not matched with sensor group id.
|
|
*/
|
|
sensor = src_gr->sensor;
|
|
|
|
sensor_cfg = sensor->cfg;
|
|
if (!sensor_cfg) {
|
|
mgerr("failed to get sensor_cfg(%s)", group, group, caller_fn);
|
|
return -EINVAL;
|
|
}
|
|
|
|
pd_mode = sensor_cfg->pd_mode;
|
|
if (pd_mode != PD_MOD3 && pd_mode != PD_MSPD_TAIL)
|
|
return -ECHILD;
|
|
|
|
ret = v4l2_subdev_call(sensor->subdev_csi, core, ioctl,
|
|
SENSOR_IOCTL_G_DMA_CH, &dma_ch);
|
|
if (ret) {
|
|
mgerr("failed to get dma_ch(%s)", group, group, caller_fn);
|
|
return ret;
|
|
}
|
|
|
|
src_gr_id = GROUP_ID_SS0 + dma_ch;
|
|
|
|
if (sensor_cfg->ex_mode == EX_DUALFPS_480 ||
|
|
sensor_cfg->ex_mode == EX_DUALFPS_960)
|
|
votf_mode = VOTF_FRS;
|
|
} else {
|
|
return -ECHILD;
|
|
}
|
|
|
|
src_info->service = TWS;
|
|
src_info->ip = is_get_votf_ip(src_gr_id);
|
|
src_info->id = is_get_votf_id(src_gr_id, ENTRY_SSVC1);
|
|
src_info->mode = votf_mode;
|
|
|
|
dst_info->service = TRS;
|
|
dst_info->ip = is_get_votf_ip(group->id);
|
|
dst_info->id = is_get_votf_id(group->id, ENTRY_PDAF);
|
|
dst_info->mode = votf_mode;
|
|
|
|
if (caller_fn)
|
|
mginfo("PD: %s(TWS[%s:%s]-TRS[%s:%s])\n",
|
|
group, group, caller_fn,
|
|
group_id_name[src_gr->id], src_sd->name,
|
|
group_id_name[group->id], group->leader.name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int is_votf_flush(struct is_group *group)
|
|
{
|
|
int ret;
|
|
struct votf_info src_info, dst_info;
|
|
|
|
ret = is_votf_get_votf_info(group, &src_info, &dst_info, (char *)__func__);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = __is_votf_flush(&src_info, &dst_info);
|
|
if (ret)
|
|
mgerr("__is_votf_flush is fail(%d)", group, group, ret);
|
|
|
|
if (group->prev->device_type == IS_DEVICE_SENSOR) {
|
|
ret = is_votf_get_pd_votf_info(group, &src_info, &dst_info, (char *)__func__);
|
|
if (ret) {
|
|
if (ret == -ECHILD)
|
|
return 0;
|
|
else
|
|
return ret;
|
|
}
|
|
|
|
ret = __is_votf_flush(&src_info, &dst_info);
|
|
if (ret)
|
|
mgerr("__is_votf_flush of subdev_pdaf is fail(%d)", group, group, ret);
|
|
}
|
|
|
|
is_votf_subdev_flush(group);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int is_votf_alloc_internal_buffer(struct is_group *group, u32 width, u32 height)
|
|
{
|
|
int ret, ret_err;
|
|
struct is_subdev *leader = &group->leader;
|
|
struct is_device_ischain *device;
|
|
struct is_device_sensor *sensor;
|
|
struct is_module_enum *module;
|
|
struct is_sensor_cfg *sensor_cfg;
|
|
u32 bpp;
|
|
u32 sbwc_type, lossy_byte32num;
|
|
struct is_subdev *subdev_pdaf;
|
|
u32 pd_width, pd_height, stat_vc;
|
|
struct votf_info src_info, dst_info;
|
|
|
|
device = group->device;
|
|
if (!device) {
|
|
mgerr("failed to get devcie", group, group);
|
|
return -ENODEV;
|
|
}
|
|
|
|
/* TRS always alloc internal buffer */
|
|
set_bit(IS_SUBDEV_INTERNAL_USE, &leader->state);
|
|
|
|
sensor = device->sensor;
|
|
module = (struct is_module_enum *)v4l2_get_subdevdata(sensor->subdev_module);
|
|
sensor_cfg = sensor->cfg;
|
|
stat_vc = module->stat_vc;
|
|
|
|
bpp = is_subdev_internal_g_bpp(device, IS_DEVICE_ISCHAIN,
|
|
leader, sensor_cfg);
|
|
|
|
is_subdev_internal_get_sbwc_type(leader, &sbwc_type, &lossy_byte32num);
|
|
|
|
ret = is_subdev_internal_s_format(device, IS_DEVICE_ISCHAIN,
|
|
leader, width, height, bpp, sbwc_type, lossy_byte32num,
|
|
NUM_OF_VOTF_BUF, "VOTF");
|
|
if (ret) {
|
|
merr("is_subdev_internal_s_format is fail(%d)", device, ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = is_subdev_internal_start(device, IS_DEVICE_ISCHAIN, leader);
|
|
if (ret) {
|
|
merr("subdev internal start is fail(%d)", device, ret);
|
|
return ret;
|
|
}
|
|
|
|
/* PD node */
|
|
ret = is_votf_get_pd_votf_info(group, &src_info, &dst_info, NULL);
|
|
if (ret) {
|
|
if (ret == -ECHILD)
|
|
return 0;
|
|
else
|
|
return ret;
|
|
}
|
|
|
|
subdev_pdaf = &device->pdaf;
|
|
pd_width = sensor_cfg->input[stat_vc].width;
|
|
pd_height = sensor_cfg->input[stat_vc].height;
|
|
|
|
is_subdev_internal_get_sbwc_type(subdev_pdaf, &sbwc_type, &lossy_byte32num);
|
|
|
|
ret = is_subdev_internal_s_format(device, 0, subdev_pdaf,
|
|
pd_width, pd_height, 16, sbwc_type, lossy_byte32num,
|
|
NUM_OF_VOTF_BUF, "VOTF");
|
|
if (ret) {
|
|
merr("is_subdev_internal_s_format is fail(%d)", device, ret);
|
|
goto err_subdev_pdaf_s_format;
|
|
}
|
|
|
|
ret = is_subdev_internal_start(device, IS_DEVICE_ISCHAIN, subdev_pdaf);
|
|
if (ret) {
|
|
merr("subdev_pdaf internal start is fail(%d)", device, ret);
|
|
goto err_subdev_pdaf_start;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_subdev_pdaf_start:
|
|
err_subdev_pdaf_s_format:
|
|
ret_err = is_subdev_internal_stop(device, IS_DEVICE_ISCHAIN, leader);
|
|
if (ret_err)
|
|
merr("subdev internal stop is fail(%d)", device, ret_err);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int is_votf_free_internal_buffer(struct is_group *group)
|
|
{
|
|
int ret;
|
|
struct is_subdev *leader = &group->leader;
|
|
struct is_device_ischain *device;
|
|
struct is_subdev *subdev_pdaf;
|
|
struct votf_info src_info, dst_info;
|
|
|
|
device = group->device;
|
|
if (!device) {
|
|
mgerr("failed to get devcie", group, group);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = is_subdev_internal_stop(device, 0, leader);
|
|
if (ret)
|
|
merr("subdev internal stop is fail(%d)", device, ret);
|
|
|
|
clear_bit(IS_SUBDEV_INTERNAL_USE, &leader->state);
|
|
|
|
/* PD node */
|
|
ret = is_votf_get_pd_votf_info(group, &src_info, &dst_info, NULL);
|
|
if (ret) {
|
|
if (ret == -ECHILD)
|
|
return 0;
|
|
else
|
|
return ret;
|
|
}
|
|
|
|
subdev_pdaf = &device->pdaf;
|
|
|
|
ret = is_subdev_internal_stop(device, 0, subdev_pdaf);
|
|
if (ret)
|
|
merr("subdev internal stop is fail(%d)", device, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int is_votf_check_wait_con(struct is_group *group)
|
|
{
|
|
int ret;
|
|
struct votf_info src_info, dst_info;
|
|
|
|
ret = is_votf_get_votf_info(group, &src_info, &dst_info, NULL);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = votfitf_check_wait_con(&src_info, &dst_info);
|
|
if (ret)
|
|
mgerr("votfitf_check_wait_con is fail(%d)", group, group, ret);
|
|
|
|
if (group->prev->device_type == IS_DEVICE_SENSOR) {
|
|
ret = is_votf_get_pd_votf_info(group, &src_info, &dst_info, NULL);
|
|
if (ret) {
|
|
if (ret == -ECHILD)
|
|
return 0;
|
|
else
|
|
return ret;
|
|
}
|
|
|
|
ret = votfitf_check_wait_con(&src_info, &dst_info);
|
|
if (ret)
|
|
mgerr("votfitf_check_wait_con of subdev_pdaf is fail(%d)",
|
|
group, group, ret);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int is_votf_check_invalid_state(struct is_group *group)
|
|
{
|
|
int ret;
|
|
struct votf_info src_info, dst_info;
|
|
|
|
ret = is_votf_get_votf_info(group, &src_info, &dst_info, NULL);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = votfitf_check_invalid_state(&src_info, &dst_info);
|
|
if (ret)
|
|
mgerr("votfitf_check_invalid_state is fail(%d)", group, group, ret);
|
|
|
|
if (group->prev->device_type == IS_DEVICE_SENSOR) {
|
|
ret = is_votf_get_pd_votf_info(group, &src_info, &dst_info, NULL);
|
|
if (ret) {
|
|
if (ret == -ECHILD)
|
|
return 0;
|
|
else
|
|
return ret;
|
|
}
|
|
|
|
ret = votfitf_check_invalid_state(&src_info, &dst_info);
|
|
if (ret)
|
|
mgerr("votfitf_check_invalid_state of subdev_pdaf is fail(%d)",
|
|
group, group, ret);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int is_votf_link_set_service_cfg(struct votf_info *src_info,
|
|
struct votf_info *dst_info,
|
|
u32 width, u32 height, u32 change)
|
|
{
|
|
int ret = 0;
|
|
struct votf_service_cfg cfg;
|
|
struct votf_lost_cfg lost_cfg;
|
|
|
|
memset(&cfg, 0, sizeof(struct votf_service_cfg));
|
|
|
|
/* TWS: Master */
|
|
src_info->service = TWS;
|
|
|
|
cfg.enable = 0x1;
|
|
/*
|
|
* 0xFF is max value.
|
|
* Buffer size is (limit x token_size).
|
|
* But VOTF can hold only 1 frame.
|
|
*/
|
|
cfg.limit = 0xFF;
|
|
cfg.width = width;
|
|
cfg.height = height & 0xFFFF;
|
|
cfg.token_size = is_votf_get_token_size(src_info);
|
|
cfg.connected_ip = dst_info->ip;
|
|
cfg.connected_id = dst_info->id;
|
|
cfg.option = change;
|
|
|
|
ret = votfitf_set_service_cfg(src_info, &cfg);
|
|
if (ret < 0) {
|
|
ret = -EINVAL;
|
|
err("TWS votf set_service_cfg fail. TWS 0x%04X-%d TRS 0x%04X-%d",
|
|
src_info->ip, src_info->id,
|
|
dst_info->ip, dst_info->id);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* TRS: Slave */
|
|
dst_info->service = TRS;
|
|
|
|
cfg.enable = 0x1;
|
|
/*
|
|
* 0xFF is max value.
|
|
* Buffer size is (limit x token_size).
|
|
* But VOTF can hold only 1 frame.
|
|
*/
|
|
cfg.limit = 0xFF;
|
|
cfg.width = width;
|
|
cfg.height = (height >> 16) ? (height >> 16) : height;
|
|
cfg.token_size = is_votf_get_token_size(dst_info);
|
|
cfg.connected_ip = src_info->ip;
|
|
cfg.connected_id = src_info->id;
|
|
cfg.option = change;
|
|
|
|
ret = votfitf_set_service_cfg(dst_info, &cfg);
|
|
if (ret < 0) {
|
|
ret = -EINVAL;
|
|
err("TRS votf set_service_cfg fail. TWS 0x%04X-%d TRS 0x%04X-%d",
|
|
src_info->ip, src_info->id,
|
|
dst_info->ip, dst_info->id);
|
|
|
|
return ret;
|
|
}
|
|
|
|
memset(&lost_cfg, 0, sizeof(struct votf_lost_cfg));
|
|
|
|
lost_cfg.recover_enable = 0x1;
|
|
/* lost_cfg.flush_enable = 0x1; */
|
|
ret = votfitf_set_trs_lost_cfg(dst_info, &lost_cfg);
|
|
if (ret < 0) {
|
|
ret = -EINVAL;
|
|
err("TRS votf set_lost_cfg fail. TWS 0x%04X-%d TRS 0x%04X-%d",
|
|
src_info->ip, src_info->id,
|
|
dst_info->ip, dst_info->id);
|
|
return ret;
|
|
}
|
|
|
|
info(" VOTF create link size %dx%d TWS 0x%04X-%d(%d) TRS 0x%04X-%d(%d)\n",
|
|
width, height,
|
|
src_info->ip, src_info->id, src_info->mode,
|
|
dst_info->ip, dst_info->id, dst_info->mode);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int __is_votf_flush(struct votf_info *src_info,
|
|
struct votf_info *dst_info)
|
|
{
|
|
int ret = 0;
|
|
|
|
/* TWS: Master */
|
|
src_info->service = TWS;
|
|
|
|
ret = votfitf_set_flush(src_info);
|
|
if (ret < 0)
|
|
err("TWS votf_flush fail. TWS 0x%04X-%d TRS 0x%04X-%d",
|
|
src_info->ip, src_info->id,
|
|
dst_info->ip, dst_info->id);
|
|
|
|
/* TRS: Slave */
|
|
dst_info->service = TRS;
|
|
|
|
ret = votfitf_set_flush(dst_info);
|
|
if (ret < 0)
|
|
err("TRS votf_flush fail. TWS 0x%04X-%d TRS 0x%04X-%d",
|
|
src_info->ip, src_info->id,
|
|
dst_info->ip, dst_info->id);
|
|
|
|
info("VOTF flush TWS 0x%04X-%d TRS 0x%04X-%d\n",
|
|
src_info->ip, src_info->id,
|
|
dst_info->ip, dst_info->id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int __is_votf_force_flush(struct votf_info *src_info,
|
|
struct votf_info *dst_info)
|
|
{
|
|
int ret = 0;
|
|
|
|
/* TWS: Master */
|
|
src_info->service = TWS;
|
|
|
|
ret = votfitf_set_flush_alone(src_info);
|
|
if (ret < 0)
|
|
err("TWS votf_flush fail. TWS 0x%04X-%d TRS 0x%04X-%d",
|
|
src_info->ip, src_info->id,
|
|
dst_info->ip, dst_info->id);
|
|
|
|
/* TRS: Slave */
|
|
dst_info->service = TRS;
|
|
|
|
ret = votfitf_set_flush_alone(dst_info);
|
|
if (ret < 0)
|
|
err("TRS votf_flush fail. TWS 0x%04X-%d TRS 0x%04X-%d",
|
|
src_info->ip, src_info->id,
|
|
dst_info->ip, dst_info->id);
|
|
|
|
info("VOTF flush TWS 0x%04X-%d TRS 0x%04X-%d\n",
|
|
src_info->ip, src_info->id,
|
|
dst_info->ip, dst_info->id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int is_votf_create_link_sensor(struct is_group *group, u32 width, u32 height)
|
|
{
|
|
int ret = 0;
|
|
struct votf_info src_info, dst_info;
|
|
|
|
if (group->prev && group->prev->device_type == IS_DEVICE_ISCHAIN)
|
|
return 0;
|
|
|
|
ret = is_votf_get_votf_info(group, &src_info, &dst_info, (char *)__func__);
|
|
if (ret)
|
|
return ret;
|
|
|
|
#if defined(USE_VOTF_AXI_APB)
|
|
votfitf_create_link(src_info.ip, dst_info.ip);
|
|
#elif
|
|
votfitf_create_ring();
|
|
#endif
|
|
|
|
/* is_votf_link_set_service_cfg() is moved variant part */
|
|
|
|
/* Allocate VOTF internal buffer */
|
|
ret = is_votf_alloc_internal_buffer(group, width, height);
|
|
if (ret) {
|
|
mgerr("failed to create suvdev link", group, group);
|
|
goto err_alloc_internal_buffer;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_alloc_internal_buffer:
|
|
#if defined(USE_VOTF_AXI_APB)
|
|
votfitf_destroy_link(src_info.ip, dst_info.ip);
|
|
#elif
|
|
votfitf_destroy_ring();
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
int is_votf_destroy_link_sensor(struct is_group *group)
|
|
{
|
|
int ret = 0;
|
|
struct votf_info src_info, dst_info;
|
|
struct is_framemgr *votf_fmgr;
|
|
|
|
if (group->id >= GROUP_ID_MAX) {
|
|
mgerr("group ID is invalid(%d)", group, group);
|
|
|
|
/* Do not access C2SERV after power off */
|
|
return 0;
|
|
}
|
|
|
|
if (group->prev && group->prev->device_type == IS_DEVICE_ISCHAIN)
|
|
return 0;
|
|
|
|
ret = is_votf_get_votf_info(group, &src_info, &dst_info, (char *)__func__);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = is_votf_flush(group);
|
|
if (ret)
|
|
mgerr("is_votf_flush is fail(%d)", group, group, ret);
|
|
|
|
/*
|
|
* All VOTF control such as flush must be set in ring clock enable and ring enable.
|
|
* So, calling votfitf_destroy_ring must be called at the last.
|
|
*/
|
|
#if defined(USE_VOTF_AXI_APB)
|
|
votfitf_destroy_link(src_info.ip, dst_info.ip);
|
|
#else
|
|
votfitf_destroy_ring();
|
|
#endif
|
|
|
|
/*
|
|
* TRS wait idle after flush
|
|
* TWS wait idle is not necessary.
|
|
*/
|
|
votf_fmgr = is_votf_get_framemgr(group, TRS, group->leader.id);
|
|
votf_fmgr_call(votf_fmgr, slave, wait_idle);
|
|
|
|
/* Free VOTF internal buffer */
|
|
ret = is_votf_free_internal_buffer(group);
|
|
if (ret)
|
|
mgerr("failed to create suvdev link", group, group);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int is_votf_create_link(struct is_group *group, u32 width, u32 height)
|
|
{
|
|
int ret = 0;
|
|
struct votf_info src_info, dst_info;
|
|
u32 change_option = VOTF_OPTION_MSK_COUNT;
|
|
|
|
if (group->prev && group->prev->device_type == IS_DEVICE_SENSOR)
|
|
return 0;
|
|
|
|
ret = is_votf_get_votf_info(group, &src_info, &dst_info, (char *)__func__);
|
|
if (ret)
|
|
return ret;
|
|
|
|
#if defined(USE_VOTF_AXI_APB)
|
|
votfitf_create_link(src_info.ip, dst_info.ip);
|
|
|
|
ret = is_votf_subdev_create_link(group);
|
|
if (ret) {
|
|
mgerr("failed to create suvdev link", group, group);
|
|
goto err_subdev_create_link;
|
|
}
|
|
#endif
|
|
|
|
ret = is_votf_link_set_service_cfg(&src_info, &dst_info,
|
|
width, height, change_option);
|
|
if (ret)
|
|
goto err_link_set_service_cfg;
|
|
|
|
/* Allocate VOTF internal buffer */
|
|
ret = is_votf_alloc_internal_buffer(group, width, height);
|
|
if (ret) {
|
|
mgerr("failed to create suvdev link", group, group);
|
|
goto err_alloc_internal_buffer;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_alloc_internal_buffer:
|
|
err_link_set_service_cfg:
|
|
#if defined(USE_VOTF_AXI_APB)
|
|
is_votf_subdev_destroy_link(group);
|
|
|
|
err_subdev_create_link:
|
|
votfitf_destroy_link(src_info.ip, dst_info.ip);
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
int is_votf_destroy_link(struct is_group *group)
|
|
{
|
|
int ret = 0;
|
|
struct votf_info src_info, dst_info;
|
|
|
|
if (group->id >= GROUP_ID_MAX) {
|
|
mgerr("group ID is invalid(%d)", group, group);
|
|
|
|
/* Do not access C2SERV after power off */
|
|
return 0;
|
|
}
|
|
|
|
if (group->prev && group->prev->device_type == IS_DEVICE_SENSOR)
|
|
return 0;
|
|
|
|
ret = is_votf_get_votf_info(group, &src_info, &dst_info, (char *)__func__);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = is_votf_flush(group);
|
|
if (ret)
|
|
mgerr("is_votf_flush is fail(%d)", group, group, ret);
|
|
|
|
/*
|
|
* All VOTF control such as flush must be set in ring clock enable and ring enable.
|
|
* So, calling votfitf_destroy_ring must be called at the last.
|
|
*/
|
|
#if defined(USE_VOTF_AXI_APB)
|
|
votfitf_destroy_link(src_info.ip, dst_info.ip);
|
|
|
|
is_votf_subdev_destroy_link(group);
|
|
#endif
|
|
|
|
/* Free VOTF internal buffer */
|
|
ret = is_votf_free_internal_buffer(group);
|
|
if (ret)
|
|
mgerr("failed to create suvdev link", group, group);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int is_votf_check_link(struct is_group *group)
|
|
{
|
|
return test_bit(IS_GROUP_VOTF_CONN_LINK, &group->state);
|
|
}
|
|
|
|
struct is_framemgr *is_votf_get_framemgr(struct is_group *group, enum votf_service type,
|
|
unsigned long subdev_id)
|
|
{
|
|
struct is_framemgr *framemgr;
|
|
struct is_subdev *ldr_sd;
|
|
struct is_device_ischain *device;
|
|
struct is_subdev *subdev_pdaf;
|
|
|
|
FIMC_BUG_NULL(!group);
|
|
|
|
ldr_sd = is_votf_get_slave_leader_subdev(group, type);
|
|
|
|
framemgr = GET_SUBDEV_I_FRAMEMGR(ldr_sd);
|
|
/* FIXME */
|
|
if (subdev_id == ENTRY_SSVC1 || subdev_id == ENTRY_PDAF) {
|
|
device = group->device;
|
|
subdev_pdaf = &device->pdaf;
|
|
|
|
framemgr = GET_SUBDEV_I_FRAMEMGR(subdev_pdaf);
|
|
}
|
|
|
|
FIMC_BUG_NULL(!framemgr);
|
|
|
|
return framemgr;
|
|
}
|
|
|
|
struct is_frame *is_votf_get_frame(struct is_group *group, enum votf_service type,
|
|
unsigned long subdev_id, u32 fcount)
|
|
{
|
|
struct is_framemgr *framemgr;
|
|
struct is_frame *frame;
|
|
|
|
framemgr = is_votf_get_framemgr(group, type, subdev_id);
|
|
if (!framemgr) {
|
|
mgerr("framemgr is NULL (subdev_id: %ld)\n", group, group, subdev_id);
|
|
return NULL;
|
|
}
|
|
|
|
frame = &framemgr->frames[framemgr->num_frames ? (fcount % framemgr->num_frames) : 0];
|
|
|
|
return frame;
|
|
}
|
|
|
|
int is_votf_register_framemgr(struct is_group *group, enum votf_service type,
|
|
void *data, const struct votf_ops *ops, unsigned long subdev_id)
|
|
{
|
|
struct is_framemgr *framemgr;
|
|
|
|
framemgr = is_votf_get_framemgr(group, type, subdev_id);
|
|
if (!framemgr) {
|
|
mgerr("framemgr is NULL. (subdev_id: %ld)\n", group, group, subdev_id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (type == TWS) {
|
|
framemgr->master.id = subdev_id;
|
|
framemgr->master.data = data;
|
|
framemgr->master.ops = ops;
|
|
mginfo("Register VOTF master callback (subdev_id: %ld)\n", group, group, subdev_id);
|
|
} else {
|
|
framemgr->slave.id = subdev_id;
|
|
framemgr->slave.data = data;
|
|
framemgr->slave.ops = ops;
|
|
mginfo("Register VOTF slave callback (subdev_id: %ld)\n", group, group, subdev_id);
|
|
}
|
|
|
|
return 0;
|
|
}
|