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

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;
}