Files
linux-nv-oot/drivers/media/platform/tegra/camera/fusa-capture/capture-vi.c
Mohit Ingale 794163ce8e media: tegra: Conditionally disable L4T-specific drivers in embedded Linux builds
This change adds conditional compilation blocks to disable L4T-specific
camera and multimedia drivers when building for embedded Linux platforms.
Key changes include:

- Add ifneq ($(NV_BUILD_SYSTEM_TYPE),embedded-linux) checks in multiple
  Makefiles to exclude L4T-specific components
- Introduce NV_IS_L4T define to conditionally include L4T functionality
- Reorganize header includes to support both L4T and embedded Linux builds
- Conditionally wrap L4T-specific struct members and function calls
- Update module dependencies (cdi_mgr now depends on cdi_dev instead of cdi_pwm)

Jira CAMERASW-32251

Change-Id: I934fdd0188e914c07b456c0f6ad379d2a08555ca
Signed-off-by: Mohit Ingale <mohiti@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/3294869
GVS: buildbot_gerritrpt <buildbot_gerritrpt@nvidia.com>
Reviewed-by: Anubhav Rai <arai@nvidia.com>
Reviewed-by: Jagadeesh Kinni <jkinni@nvidia.com>
Reviewed-by: Vincent Chung <vincentc@nvidia.com>
Reviewed-by: Frank Chen <frankc@nvidia.com>
2025-07-24 10:19:15 +00:00

1929 lines
50 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/* SPDX-FileCopyrightText: Copyright (c) 2017-2025 NVIDIA CORPORATION & AFFILIATES.
* All rights reserved.
*
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
/**
* @file drivers/media/platform/tegra/camera/fusa-capture/capture-vi.c
*
* @brief VI channel operations for the T234 Camera RTCPU platform.
*/
#include <nvidia/conftest.h>
#include <linux/completion.h>
#include <linux/nospec.h>
#include <linux/nvhost.h>
#include <linux/of_platform.h>
#include <linux/printk.h>
#include <linux/vmalloc.h>
#include <linux/dma-mapping.h>
#include <linux/of.h>
#include <linux/tegra-capture-ivc.h>
#include <linux/tegra-camera-rtcpu.h>
#include <asm/arch_timer.h>
#include <uapi/linux/nvhost_events.h>
#include <camera/nvcamera_log.h>
#include "soc/tegra/camrtc-capture.h"
#include "soc/tegra/camrtc-capture-messages.h"
#include <media/fusa-capture/capture-vi-channel.h>
#include <media/fusa-capture/capture-common.h>
#include <media/fusa-capture/capture-vi.h>
#ifdef NV_IS_L4T
#include <media/vi.h>
#include <media/mc_common.h>
#include <media/tegra_camera_platform.h>
#include "camera/vi/vi5_fops.h"
#endif
/**
* @brief Invalid VI channel ID; the channel is not initialized.
*/
#define CAPTURE_CHANNEL_INVALID_ID U16_C(0xFFFF)
/**
* @brief Invalid VI channel mask; no channels are allocated.
*/
#define CAPTURE_CHANNEL_INVALID_MASK U64_C(0x0)
/**
* @brief Invalid NVCSI stream ID; the stream is not initialized.
*/
#define NVCSI_STREAM_INVALID_ID U32_C(0xFFFF)
/**
* @brief INVALID NVCSI TPG virtual channel ID; the TPG stream is not enabled.
*/
#define NVCSI_STREAM_INVALID_TPG_VC_ID U32_C(0xFFFF)
/**
* @brief The default number of VI channels to be used if not specified in
* the device tree.
*/
#define DEFAULT_VI_CHANNELS U32_C(64)
/**
* @brief Maximum number of VI channels supported by KMD in total
*/
#define NUM_VI_CHANNELS U32_C(72)
/**
* @brief Maximum number of VI devices supported.
*/
#define MAX_VI_UNITS U32_C(0x2)
/**
* @brief Invalid VI unit ID, to initialize vi-mapping table before parsing DT.
*/
#define INVALID_VI_UNIT_ID U32_C(0xFFFF)
/**
* @brief Maximum number of NVCSI streams supported.
*/
#define MAX_NVCSI_STREAM_IDS U32_C(0x6)
/**
* @brief Maximum number of virtual channel supported per stream.
*/
#define MAX_VIRTUAL_CHANNEL_PER_STREAM U32_C(16)
/**
* @brief A 2-D array for storing all possible tegra_vi_channel struct pointers.
*/
static struct tegra_vi_channel *channels[MAX_NVCSI_STREAM_IDS][MAX_VIRTUAL_CHANNEL_PER_STREAM];
/**
* @brief Names of VI-unit and CSI-stream mapping elements in device-tree node
*/
static const char * const vi_mapping_elements[] = {
"csi-stream-id",
"vi-unit-id"
};
/**
* @brief The Capture-VI standalone driver context.
*/
struct tegra_capture_vi_data {
#ifdef NV_IS_L4T
struct vi vi_common; /**< VI device context */
#endif
uint32_t num_vi_devices; /**< Number of available VI devices */
struct platform_device *vi_pdevices[MAX_VI_UNITS];
/**< VI nvhost client platform device for each VI instance */
uint32_t max_vi_channels;
/**< Maximum number of VI capture channel devices */
uint32_t num_csi_vi_maps;
/**< Number of NVCSI to VI mapping elements in the table */
uint32_t vi_instance_table[MAX_NVCSI_STREAM_IDS];
/**< NVCSI stream-id & VI instance mapping, read from the DT */
};
/**
* @brief Initialize a VI syncpoint and get its GoS backing.
*
* @param[in] chan VI channel context
* @param[in] name Syncpoint name
* @param[in] enable Whether to initialize or just clear @a sp
* @param[out] sp Syncpoint handle
*
* @returns 0 (success), neg. errno (failure)
*/
static int vi_capture_setup_syncpt(
struct tegra_vi_channel *chan,
const char *name,
bool enable,
struct syncpoint_info *sp)
{
struct platform_device *pdev = chan->ndev;
uint32_t gos_index, gos_offset;
int err;
memset(sp, 0, sizeof(*sp));
sp->gos_index = GOS_INDEX_INVALID;
if (!enable)
return 0;
err = chan->ops->alloc_syncpt(pdev, name, &sp->id);
if (err)
return err;
err = nvhost_syncpt_read_ext_check(pdev, sp->id, &sp->threshold);
if (err)
goto cleanup;
err = chan->ops->get_syncpt_gos_backing(pdev, sp->id, &sp->shim_addr,
&gos_index, &gos_offset);
if (err)
goto cleanup;
sp->gos_index = gos_index;
sp->gos_offset = gos_offset;
return 0;
cleanup:
chan->ops->release_syncpt(pdev, sp->id);
memset(sp, 0, sizeof(*sp));
return err;
}
/**
* @brief Fast forward a VI syncpoint to its threshold value.
*
* @param[in] chan VI channel context
*/
static void vi_capture_fastforward_syncpt(
struct tegra_vi_channel *chan)
{
struct vi_capture *capture = chan->capture_data;
if (capture->progress_sp.id)
chan->ops->fast_forward_syncpt(chan->ndev,
capture->progress_sp.id,
capture->progress_sp.threshold);
}
/**
* @brief Release a VI syncpoint and clear its handle.
*
* @param[in] chan VI channel context
* @param[out] sp Syncpoint handle
*/
static void vi_capture_release_syncpt(
struct tegra_vi_channel *chan,
struct syncpoint_info *sp)
{
if (sp->id)
chan->ops->release_syncpt(chan->ndev, sp->id);
memset(sp, 0, sizeof(*sp));
}
/**
* @brief Release the VI channel progress, embedded data and line timer
* syncpoints.
*
* @param[in] chan VI channel context
*/
static void vi_capture_release_syncpts(
struct tegra_vi_channel *chan)
{
struct vi_capture *capture = chan->capture_data;
vi_capture_release_syncpt(chan, &capture->progress_sp);
vi_capture_release_syncpt(chan, &capture->embdata_sp);
vi_capture_release_syncpt(chan, &capture->linetimer_sp);
}
/**
* @brief Set up the VI channel progress, embedded data and line timer
* syncpoints.
*
* @param[in] chan VI channel context
* @param[in] flags Bitmask for channel flags, see
* @ref CAPTURE_CHANNEL_FLAGS
*
* @returns 0 (success), neg. errno (failure)
*/
static int vi_capture_setup_syncpts(
struct tegra_vi_channel *chan,
uint32_t flags)
{
struct vi_capture *capture = chan->capture_data;
int err = 0;
chan->ops->get_gos_table(chan->ndev,
&capture->num_gos_tables,
&capture->gos_tables);
err = vi_capture_setup_syncpt(chan, "progress", true,
&capture->progress_sp);
if (err < 0)
goto fail;
err = vi_capture_setup_syncpt(chan, "embdata",
(flags & CAPTURE_CHANNEL_FLAG_EMBDATA) != 0,
&capture->embdata_sp);
if (err < 0)
goto fail;
err = vi_capture_setup_syncpt(chan, "linetimer",
(flags & CAPTURE_CHANNEL_FLAG_LINETIMER) != 0,
&capture->linetimer_sp);
if (err < 0)
goto fail;
return 0;
fail:
vi_capture_release_syncpts(chan);
return err;
}
/**
* @brief Read the value of a VI channel syncpoint.
*
* @param[in] chan VI channel context
* @param[in] sp Syncpoint handle
* @param[out] val Syncpoint value
*
* @returns 0 (success), neg. errno (failure)
*/
static int vi_capture_read_syncpt(
struct tegra_vi_channel *chan,
struct syncpoint_info *sp,
uint32_t *val)
{
int err;
if (sp->id) {
err = nvhost_syncpt_read_ext_check(chan->ndev,
sp->id, val);
if (err < 0) {
dev_err(chan->dev,
"%s: get syncpt %i val failed\n", __func__,
sp->id);
return -EINVAL;
}
}
return 0;
}
/**
* @brief VI channel callback function for @em capture IVC messages.
*
* @param[in] ivc_resp IVC @ref CAPTURE_MSG from RCE
* @param[in] pcontext VI channel capture context
*/
static void vi_capture_ivc_status_callback(
const void *ivc_resp,
const void *pcontext)
{
struct CAPTURE_MSG *status_msg = (struct CAPTURE_MSG *)ivc_resp;
struct vi_capture *capture = (struct vi_capture *)pcontext;
struct tegra_vi_channel *chan = capture->vi_channel;
uint32_t buffer_index;
if (unlikely(capture == NULL)) {
dev_err(chan->dev, "%s: invalid context", __func__);
return;
}
if (unlikely(status_msg == NULL)) {
dev_err(chan->dev, "%s: invalid response", __func__);
return;
}
switch (status_msg->header.msg_id) {
case CAPTURE_STATUS_IND:
buffer_index = status_msg->capture_status_ind.buffer_index;
if (capture->is_mem_pinned)
vi_capture_request_unpin(chan, buffer_index);
dma_sync_single_range_for_cpu(capture->rtcpu_dev,
capture->requests.iova,
buffer_index * capture->request_size,
capture->request_size, DMA_FROM_DEVICE);
if (capture->is_progress_status_notifier_set) {
capture_common_set_progress_status(
&capture->progress_status_notifier,
buffer_index,
capture->progress_status_buffer_depth,
PROGRESS_STATUS_DONE);
} else {
/*
* Only fire completions if not using
* the new progress status buffer mechanism
*/
complete(&capture->capture_resp);
}
dev_dbg(chan->dev, "%s: status chan_id %u msg_id %u\n",
__func__, status_msg->header.channel_id,
status_msg->header.msg_id);
break;
default:
dev_err(chan->dev,
"%s: unknown capture resp", __func__);
break;
}
}
/**
* @brief Send a @em capture-control IVC message to RCE on a VI channel, and
* block w/ timeout, waiting for the RCE response.
*
* @param[in] chan VI channel context
* @param[in] msg IVC message payload
* @param[in] size Size of @a msg [byte]
* @param[in] resp_id IVC message identifier, see @CAPTURE_MSG_IDS
*
* @returns 0 (success), neg. errno (failure)
*/
static int vi_capture_ivc_send_control(
struct tegra_vi_channel *chan,
const struct CAPTURE_CONTROL_MSG *msg,
size_t size,
uint32_t resp_id)
{
struct vi_capture *capture = chan->capture_data;
struct CAPTURE_MSG_HEADER resp_header = msg->header;
uint32_t timeout = HZ;
int err = 0;
dev_dbg(chan->dev, "%s: sending chan_id %u msg_id %u\n",
__func__, resp_header.channel_id, resp_header.msg_id);
resp_header.msg_id = resp_id;
/* Send capture control IVC message */
mutex_lock(&capture->control_msg_lock);
err = tegra_capture_ivc_control_submit(msg, size);
if (err < 0) {
dev_err(chan->dev, "IVC control submit failed\n");
goto fail;
}
timeout = wait_for_completion_timeout(
&capture->control_resp, timeout);
if (timeout <= 0) {
dev_err(chan->dev,
"capture control message timed out\n");
err = -ETIMEDOUT;
goto fail;
}
if (memcmp(&resp_header, &capture->control_resp_msg.header,
sizeof(resp_header)) != 0) {
dev_err(chan->dev,
"unexpected response from camera processor\n");
err = -EINVAL;
goto fail;
}
mutex_unlock(&capture->control_msg_lock);
dev_dbg(chan->dev, "%s: response chan_id %u msg_id %u\n",
__func__, capture->control_resp_msg.header.channel_id,
capture->control_resp_msg.header.msg_id);
return 0;
fail:
mutex_unlock(&capture->control_msg_lock);
return err;
}
/**
* @brief VI channel callback function for @em capture-control IVC messages,
* this unblocks the channel's @em capture-control completion.
*
* @param[in] ivc_resp IVC @ref CAPTURE_CONTROL_MSG from RCE
* @param[in] pcontext VI channel capture context
*/
static void vi_capture_ivc_control_callback(
const void *ivc_resp,
const void *pcontext)
{
const struct CAPTURE_CONTROL_MSG *control_msg = ivc_resp;
struct vi_capture *capture = (struct vi_capture *)pcontext;
struct tegra_vi_channel *chan = capture->vi_channel;
if (unlikely(capture == NULL)) {
dev_err(chan->dev, "%s: invalid context", __func__);
return;
}
if (unlikely(control_msg == NULL)) {
dev_err(chan->dev, "%s: invalid response", __func__);
return;
}
switch (control_msg->header.msg_id) {
case CAPTURE_CHANNEL_SETUP_RESP:
case CAPTURE_CHANNEL_RESET_RESP:
case CAPTURE_CHANNEL_RELEASE_RESP:
case CAPTURE_COMPAND_CONFIG_RESP:
case CAPTURE_PDAF_CONFIG_RESP:
case CAPTURE_SYNCGEN_ENABLE_RESP:
case CAPTURE_SYNCGEN_DISABLE_RESP:
case CAPTURE_PHY_STREAM_OPEN_RESP:
case CAPTURE_PHY_STREAM_CLOSE_RESP:
case CAPTURE_PHY_STREAM_DUMPREGS_RESP:
case CAPTURE_CSI_STREAM_SET_CONFIG_RESP:
case CAPTURE_CSI_STREAM_SET_PARAM_RESP:
case CAPTURE_CSI_STREAM_TPG_SET_CONFIG_RESP:
case CAPTURE_CSI_STREAM_TPG_START_RESP:
case CAPTURE_CSI_STREAM_TPG_START_RATE_RESP:
case CAPTURE_CSI_STREAM_TPG_APPLY_GAIN_RESP:
case CAPTURE_CSI_STREAM_TPG_STOP_RESP:
case CAPTURE_CHANNEL_EI_RESP:
case CAPTURE_HSM_CHANSEL_ERROR_MASK_RESP:
memcpy(&capture->control_resp_msg, control_msg,
sizeof(*control_msg));
complete(&capture->control_resp);
break;
default:
dev_err(chan->dev,
"%s: unknown capture control resp 0x%x", __func__,
control_msg->header.msg_id);
break;
}
}
int vi_capture_init(
struct tegra_vi_channel *chan,
bool is_mem_pinned)
{
struct vi_capture *capture;
struct device_node *dn;
struct platform_device *rtc_pdev;
struct device *dev;
dev = &chan->vi_capture_pdev->dev;
dev_dbg(dev, "%s++\n", __func__);
dn = of_find_node_by_path("tegra-camera-rtcpu");
if (of_device_is_available(dn) == 0) {
dev_err(dev, "failed to find rtcpu device node\n");
return -ENODEV;
}
rtc_pdev = of_find_device_by_node(dn);
if (rtc_pdev == NULL) {
dev_err(dev, "failed to find rtcpu platform\n");
return -ENODEV;
}
capture = kzalloc(sizeof(*capture), GFP_KERNEL);
if (unlikely(capture == NULL)) {
dev_err(dev, "failed to allocate capture channel\n");
return -ENOMEM;
}
capture->rtcpu_dev = &rtc_pdev->dev;
init_completion(&capture->control_resp);
init_completion(&capture->capture_resp);
mutex_init(&capture->reset_lock);
mutex_init(&capture->control_msg_lock);
mutex_init(&capture->unpins_list_lock);
capture->vi_channel = chan;
chan->capture_data = capture;
chan->rtcpu_dev = capture->rtcpu_dev;
capture->is_mem_pinned = is_mem_pinned;
capture->channel_id = CAPTURE_CHANNEL_INVALID_ID;
capture->stream_id = NVCSI_STREAM_INVALID_ID;
capture->csi_port = NVCSI_PORT_UNSPECIFIED;
capture->virtual_channel_id = NVCSI_STREAM_INVALID_TPG_VC_ID;
return 0;
}
EXPORT_SYMBOL_GPL(vi_capture_init);
void vi_capture_shutdown(
struct tegra_vi_channel *chan)
{
struct vi_capture *capture;
if (unlikely(chan == NULL)) {
pr_err("%s: vi channel pointer is NULL\n", __func__);
return;
}
dev_dbg(chan->dev, "%s--\n", __func__);
capture = chan->capture_data;
if (unlikely(capture == NULL)) {
dev_err(chan->dev, "%s: invalid context", __func__);
return;
}
if (capture->channel_id != CAPTURE_CHANNEL_INVALID_ID)
vi_capture_reset(chan,
CAPTURE_CHANNEL_RESET_FLAG_IMMEDIATE);
if (capture->stream_id != NVCSI_STREAM_INVALID_ID)
csi_stream_release(chan);
if (capture->channel_id != CAPTURE_CHANNEL_INVALID_ID) {
int i;
vi_capture_release(chan,
CAPTURE_CHANNEL_RESET_FLAG_IMMEDIATE);
if (capture->is_mem_pinned) {
for (i = 0; i < capture->queue_depth; i++)
vi_capture_request_unpin(chan, i);
}
capture_common_unpin_memory(&capture->requests);
if (capture->buf_ctx != NULL) {
destroy_buffer_table(capture->buf_ctx);
capture->buf_ctx = NULL;
}
vfree(capture->unpins_list);
capture->unpins_list = NULL;
}
kfree(capture);
chan->capture_data = NULL;
}
EXPORT_SYMBOL_GPL(vi_capture_shutdown);
void vi_get_nvhost_device(
struct tegra_vi_channel *chan,
struct vi_capture_setup *setup)
{
uint32_t vi_inst = 0;
struct tegra_capture_vi_data *info =
platform_get_drvdata(chan->vi_capture_pdev);
if (setup->csi_stream_id >= MAX_NVCSI_STREAM_IDS) {
dev_err(&chan->vi_capture_pdev->dev, "CSI stream ID over the limit\n");
return;
}
vi_inst = info->vi_instance_table[setup->csi_stream_id];
if (vi_inst >= MAX_VI_UNITS) {
dev_err(&chan->vi_capture_pdev->dev, "Invalid VI device Id\n");
chan->dev = NULL;
chan->ndev = NULL;
return;
}
vi_inst = array_index_nospec(vi_inst, MAX_VI_UNITS);
chan->dev = &info->vi_pdevices[vi_inst]->dev;
chan->ndev = info->vi_pdevices[vi_inst];
}
EXPORT_SYMBOL_GPL(vi_get_nvhost_device);
struct device *vi_csi_stream_to_nvhost_device(
struct platform_device *pdev,
uint32_t csi_stream_id)
{
struct tegra_capture_vi_data *info = platform_get_drvdata(pdev);
uint32_t vi_inst_id = 0;
if (csi_stream_id >= MAX_NVCSI_STREAM_IDS) {
dev_err(&pdev->dev, "Invalid NVCSI stream Id\n");
return NULL;
}
vi_inst_id = info->vi_instance_table[csi_stream_id];
return &info->vi_pdevices[vi_inst_id]->dev;
}
EXPORT_SYMBOL(vi_csi_stream_to_nvhost_device);
int vi_capture_setup(
struct tegra_vi_channel *chan,
struct vi_capture_setup *setup)
{
struct vi_capture *capture = chan->capture_data;
struct tegra_capture_vi_data *info;
uint32_t transaction;
struct CAPTURE_CONTROL_MSG control_desc;
struct CAPTURE_CONTROL_MSG *resp_msg = &capture->control_resp_msg;
struct capture_channel_config *config =
&control_desc.channel_setup_req.channel_config;
int err = 0;
#ifdef HAVE_VI_GOS_TABLES
int i;
#endif
uint32_t vi_inst = 0;
struct device *dev;
dev = &chan->vi_capture_pdev->dev;
if (setup->csi_stream_id >= MAX_NVCSI_STREAM_IDS ||
setup->virtual_channel_id >= MAX_VIRTUAL_CHANNEL_PER_STREAM) {
dev_err(dev, "Invalid stream id or virtual channel id\n");
return -EINVAL;
}
if (chan->vi_capture_pdev == NULL) {
dev_err(dev,
"%s: channel capture device is NULL", __func__);
return -EINVAL;
}
info = platform_get_drvdata(chan->vi_capture_pdev);
vi_inst = info->vi_instance_table[setup->csi_stream_id];
/* V4L2 directly calls this function. So need to make sure the
* correct VI5 instance is associated with the VI capture channel.
*/
if (chan->dev == NULL) {
vi_get_nvhost_device(chan, setup);
if (chan->dev == NULL) {
dev_err(&chan->vi_capture_pdev->dev,
"%s: channel device is NULL", __func__);
return -EINVAL;
}
}
nv_camera_log(chan->ndev,
__arch_counter_get_cntvct(),
NVHOST_CAMERA_VI_CAPTURE_SETUP);
if (setup->mem == 0 && setup->iova == 0) {
dev_err(chan->dev,
"%s: request buffer is NULL\n", __func__);
return -EINVAL;
}
if (capture == NULL) {
dev_err(chan->dev,
"%s: vi capture uninitialized\n", __func__);
return -ENODEV;
}
if (capture->channel_id != CAPTURE_CHANNEL_INVALID_ID) {
dev_err(chan->dev,
"%s: already setup, release first\n", __func__);
return -EEXIST;
}
dev_dbg(chan->dev, "chan flags %u\n", setup->channel_flags);
dev_dbg(chan->dev, "chan mask %llx\n", setup->vi_channel_mask);
dev_dbg(chan->dev, "queue depth %u\n", setup->queue_depth);
dev_dbg(chan->dev, "request size %u\n", setup->request_size);
dev_dbg(chan->dev, "csi_stream_id %u\n", setup->csi_stream_id);
dev_dbg(chan->dev, "vi unit id %u\n", vi_inst);
dev_dbg(chan->dev, "vi2 chan mask %llx\n", setup->vi2_channel_mask);
if (WARN_ON(vi_inst == VI_UNIT_VI &&
setup->vi_channel_mask == CAPTURE_CHANNEL_INVALID_MASK) ||
WARN_ON(vi_inst == VI_UNIT_VI2 &&
setup->vi2_channel_mask == CAPTURE_CHANNEL_INVALID_MASK) ||
WARN_ON(setup->channel_flags == 0) ||
WARN_ON(setup->queue_depth == 0) ||
WARN_ON(setup->request_size == 0) ||
WARN_ON(setup->csi_stream_id == NVCSI_STREAM_INVALID_ID)) {
dev_err(chan->dev, "%s: invalid setup parameters\n", __func__);
return -EINVAL;
}
capture->queue_depth = setup->queue_depth;
capture->request_size = setup->request_size;
capture->request_buf_size = setup->request_size * setup->queue_depth;
capture->stream_id = setup->csi_stream_id;
capture->csi_port = setup->csi_port;
capture->virtual_channel_id = setup->virtual_channel_id;
err = vi_capture_setup_syncpts(chan, setup->channel_flags);
if (err < 0) {
dev_err(chan->dev, "failed to setup syncpts\n");
goto syncpt_fail;
}
err = tegra_capture_ivc_register_control_cb(
&vi_capture_ivc_control_callback,
&transaction, capture);
if (err < 0) {
dev_err(chan->dev, "failed to register control callback\n");
goto control_cb_fail;
}
memset(&control_desc, 0, sizeof(control_desc));
control_desc.header.msg_id = CAPTURE_CHANNEL_SETUP_REQ;
control_desc.header.transaction = transaction;
/* Allocate memoryinfo ringbuffer */
capture->requests_memoryinfo = dma_alloc_coherent(capture->rtcpu_dev,
setup->queue_depth * sizeof(*capture->requests_memoryinfo),
&capture->requests_memoryinfo_iova, GFP_KERNEL);
if (!capture->requests_memoryinfo) {
dev_err(chan->dev,
"%s: memoryinfo ringbuffer alloc failed\n", __func__);
goto memoryinfo_alloc_fail;
}
WARN_ON(capture->unpins_list != NULL);
capture->unpins_list =
vzalloc(setup->queue_depth * sizeof(*capture->unpins_list));
if (!capture->unpins_list) {
dev_err(chan->dev,
"%s: channel_unpins alloc failed\n", __func__);
goto unpin_alloc_fail;
}
config->requests_memoryinfo = capture->requests_memoryinfo_iova;
config->request_memoryinfo_size =
sizeof(struct capture_descriptor_memoryinfo);
config->channel_flags = setup->channel_flags;
config->vi_channel_mask = setup->vi_channel_mask;
config->vi2_channel_mask = setup->vi2_channel_mask;
config->slvsec_stream_main = setup->slvsec_stream_main;
config->slvsec_stream_sub = setup->slvsec_stream_sub;
config->vi_unit_id = vi_inst;
config->csi_stream.stream_id = setup->csi_stream_id;
config->csi_stream.csi_port = setup->csi_port;
config->csi_stream.virtual_channel = setup->virtual_channel_id;
config->queue_depth = setup->queue_depth;
config->request_size = setup->request_size;
config->requests = setup->iova;
config->error_mask_correctable = setup->error_mask_correctable;
config->error_mask_uncorrectable = setup->error_mask_uncorrectable;
config->stop_on_error_notify_bits = setup->stop_on_error_notify_bits;
#ifdef HAVE_VI_GOS_TABLES
dev_dbg(chan->dev, "%u GoS tables configured.\n",
capture->num_gos_tables);
for (i = 0; i < capture->num_gos_tables; i++) {
config->vi_gos_tables[i] = (iova_t)capture->gos_tables[i];
dev_dbg(chan->dev, "gos[%d] = 0x%08llx\n",
i, (u64)capture->gos_tables[i]);
}
config->num_vi_gos_tables = capture->num_gos_tables;
#endif
config->progress_sp = capture->progress_sp;
config->embdata_sp = capture->embdata_sp;
config->linetimer_sp = capture->linetimer_sp;
err = vi_capture_ivc_send_control(chan, &control_desc,
sizeof(control_desc), CAPTURE_CHANNEL_SETUP_RESP);
if (err < 0)
goto submit_fail;
if (resp_msg->channel_setup_resp.result != CAPTURE_OK) {
dev_err(chan->dev, "%s: control failed, errno %d", __func__,
resp_msg->channel_setup_resp.result);
err = -EINVAL;
goto resp_fail;
}
capture->channel_id = resp_msg->channel_setup_resp.channel_id;
if (vi_inst == VI_UNIT_VI)
capture->vi_channel_mask =
resp_msg->channel_setup_resp.vi_channel_mask;
else if (vi_inst == VI_UNIT_VI2)
capture->vi2_channel_mask =
resp_msg->channel_setup_resp.vi_channel_mask;
else {
dev_err(chan->dev, "failed response for vi:%u\n", vi_inst);
err = -EINVAL;
goto resp_fail;
}
err = tegra_capture_ivc_notify_chan_id(capture->channel_id,
transaction);
if (err < 0) {
dev_err(chan->dev, "failed to update control callback\n");
goto cb_fail;
}
err = tegra_capture_ivc_register_capture_cb(
&vi_capture_ivc_status_callback,
capture->channel_id, capture);
if (err < 0) {
dev_err(chan->dev, "failed to register capture callback\n");
goto cb_fail;
}
channels[setup->csi_stream_id][setup->virtual_channel_id] = chan;
return 0;
cb_fail:
resp_fail:
submit_fail:
vfree(capture->unpins_list);
capture->unpins_list = NULL;
unpin_alloc_fail:
/* Release memoryinfo ringbuffer */
dma_free_coherent(capture->rtcpu_dev,
capture->queue_depth *
sizeof(struct capture_descriptor_memoryinfo),
capture->requests_memoryinfo,
capture->requests_memoryinfo_iova);
capture->requests_memoryinfo = NULL;
memoryinfo_alloc_fail:
tegra_capture_ivc_unregister_control_cb(transaction);
control_cb_fail:
vi_capture_release_syncpts(chan);
syncpt_fail:
return err;
}
EXPORT_SYMBOL_GPL(vi_capture_setup);
struct tegra_vi_channel *get_tegra_vi_channel(
unsigned int stream_id,
unsigned int virtual_channel_id)
{
if (stream_id >= MAX_NVCSI_STREAM_IDS || virtual_channel_id >= MAX_VIRTUAL_CHANNEL_PER_STREAM)
return NULL;
return channels[stream_id][virtual_channel_id];
}
int vi_capture_reset(
struct tegra_vi_channel *chan,
uint32_t reset_flags)
{
struct vi_capture *capture = chan->capture_data;
struct CAPTURE_CONTROL_MSG control_desc;
#ifdef CAPTURE_RESET_BARRIER_IND
struct CAPTURE_MSG capture_desc;
#endif
struct CAPTURE_CONTROL_MSG *resp_msg = &capture->control_resp_msg;
int err = 0;
nv_camera_log(chan->ndev,
__arch_counter_get_cntvct(),
NVHOST_CAMERA_VI_CAPTURE_RESET);
if (capture == NULL) {
dev_err(chan->dev,
"%s: vi capture uninitialized\n", __func__);
return -ENODEV;
}
if (capture->channel_id == CAPTURE_CHANNEL_INVALID_ID) {
dev_err(chan->dev,
"%s: setup channel first\n", __func__);
return -ENODEV;
}
mutex_lock(&capture->reset_lock);
#ifdef CAPTURE_RESET_BARRIER_IND
memset(&capture_desc, 0, sizeof(capture_desc));
capture_desc.header.msg_id = CAPTURE_RESET_BARRIER_IND;
capture_desc.header.channel_id = capture->channel_id;
err = tegra_capture_ivc_capture_submit(&capture_desc,
sizeof(capture_desc));
if (err < 0) {
dev_err(chan->dev, "%s:IVC capture submit failed\n", __func__);
goto submit_fail;
}
#endif
memset(&control_desc, 0, sizeof(control_desc));
control_desc.header.msg_id = CAPTURE_CHANNEL_RESET_REQ;
control_desc.header.channel_id = capture->channel_id;
control_desc.channel_reset_req.reset_flags = reset_flags;
err = vi_capture_ivc_send_control(chan, &control_desc,
sizeof(control_desc), CAPTURE_CHANNEL_RESET_RESP);
if (err < 0)
goto submit_fail;
#ifdef CAPTURE_RESET_BARRIER_IND
if (resp_msg->channel_reset_resp.result == CAPTURE_ERROR_TIMEOUT) {
dev_dbg(chan->dev, "%s:reset timeout\n", __func__);
err = -EAGAIN;
goto submit_fail;
}
#endif
if (resp_msg->channel_reset_resp.result != CAPTURE_OK) {
dev_err(chan->dev, "%s: control failed, errno %d", __func__,
resp_msg->channel_reset_resp.result);
err = -EINVAL;
}
vi_capture_fastforward_syncpt(chan);
submit_fail:
mutex_unlock(&capture->reset_lock);
return err;
}
EXPORT_SYMBOL_GPL(vi_capture_reset);
int vi_capture_release(
struct tegra_vi_channel *chan,
uint32_t reset_flags)
{
struct vi_capture *capture;
struct CAPTURE_CONTROL_MSG control_desc;
struct CAPTURE_CONTROL_MSG *resp_msg;
int err = 0;
int ret = 0;
int i = 0;
if (unlikely(chan == NULL)) {
pr_err("%s: vi channel pointer is NULL\n", __func__);
return -ENODEV;
}
nv_camera_log(chan->ndev,
__arch_counter_get_cntvct(),
NVHOST_CAMERA_VI_CAPTURE_RELEASE);
capture = chan->capture_data;
if (unlikely(capture == NULL)) {
dev_err(chan->dev, "%s: vi capture uninitialized\n", __func__);
return -ENODEV;
}
resp_msg = &capture->control_resp_msg;
if (capture->channel_id == CAPTURE_CHANNEL_INVALID_ID) {
dev_err(chan->dev,
"%s: setup channel first\n", __func__);
return -ENODEV;
}
memset(&control_desc, 0, sizeof(control_desc));
control_desc.header.msg_id = CAPTURE_CHANNEL_RELEASE_REQ;
control_desc.header.channel_id = capture->channel_id;
control_desc.channel_release_req.reset_flags = reset_flags;
err = vi_capture_ivc_send_control(chan, &control_desc,
sizeof(control_desc), CAPTURE_CHANNEL_RELEASE_RESP);
if (err < 0) {
dev_err(chan->dev,
"%s: release channel IVC failed\n", __func__);
WARN_ON("RTCPU is in a bad state. Reboot to recover");
tegra_camrtc_reboot(chan->rtcpu_dev);
err = -EIO;
} else if (resp_msg->channel_release_resp.result != CAPTURE_OK) {
dev_err(chan->dev, "%s: control failed, errno %d", __func__,
resp_msg->channel_release_resp.result);
err = -EIO;
}
if (capture->requests_memoryinfo) {
/* Release memoryinfo ringbuffer */
dma_free_coherent(capture->rtcpu_dev,
capture->queue_depth * sizeof(struct capture_descriptor_memoryinfo),
capture->requests_memoryinfo, capture->requests_memoryinfo_iova);
capture->requests_memoryinfo = NULL;
}
ret = tegra_capture_ivc_unregister_capture_cb(capture->channel_id);
if (ret < 0 && err == 0) {
dev_err(chan->dev,
"failed to unregister capture callback\n");
err = ret;
}
ret = tegra_capture_ivc_unregister_control_cb(capture->channel_id);
if (ret < 0 && err == 0) {
dev_err(chan->dev,
"failed to unregister control callback\n");
err = ret;
}
for (i = 0; i < capture->queue_depth; i++)
complete(&capture->capture_resp);
vi_capture_release_syncpts(chan);
if (capture->stream_id < MAX_NVCSI_STREAM_IDS &&
capture->virtual_channel_id < MAX_VIRTUAL_CHANNEL_PER_STREAM) {
channels[capture->stream_id][capture->virtual_channel_id] = NULL;
}
capture->channel_id = CAPTURE_CHANNEL_INVALID_ID;
capture->stream_id = NVCSI_STREAM_INVALID_ID;
capture->csi_port = NVCSI_PORT_UNSPECIFIED;
capture->virtual_channel_id = NVCSI_STREAM_INVALID_TPG_VC_ID;
if (capture->is_progress_status_notifier_set) {
capture_common_release_progress_status_notifier(
&capture->progress_status_notifier);
}
return err;
}
EXPORT_SYMBOL_GPL(vi_capture_release);
static int vi_capture_control_send_message(
struct tegra_vi_channel *chan,
struct CAPTURE_CONTROL_MSG *msg_cpy,
size_t size)
{
int err = 0;
struct vi_capture *capture = chan->capture_data;
uint32_t resp_id;
struct CAPTURE_MSG_HEADER *header = &msg_cpy->header;
header->channel_id = capture->channel_id;
switch (header->msg_id) {
case CAPTURE_COMPAND_CONFIG_REQ:
resp_id = CAPTURE_COMPAND_CONFIG_RESP;
break;
case CAPTURE_PDAF_CONFIG_REQ:
resp_id = CAPTURE_PDAF_CONFIG_RESP;
break;
case CAPTURE_SYNCGEN_ENABLE_REQ:
resp_id = CAPTURE_SYNCGEN_ENABLE_RESP;
break;
case CAPTURE_SYNCGEN_DISABLE_REQ:
resp_id = CAPTURE_SYNCGEN_DISABLE_RESP;
break;
case CAPTURE_PHY_STREAM_OPEN_REQ:
if (chan->is_stream_opened) {
dev_dbg(chan->dev,
"%s: NVCSI stream is already opened for this VI channel",
__func__);
return 0;
}
resp_id = CAPTURE_PHY_STREAM_OPEN_RESP;
capture->stream_id = msg_cpy->phy_stream_open_req.stream_id;
capture->csi_port = msg_cpy->phy_stream_open_req.csi_port;
break;
case CAPTURE_PHY_STREAM_CLOSE_REQ:
if (!chan->is_stream_opened) {
dev_dbg(chan->dev,
"%s: NVCSI stream is already closed for this VI channel",
__func__);
return 0;
}
resp_id = CAPTURE_PHY_STREAM_CLOSE_RESP;
break;
case CAPTURE_PHY_STREAM_DUMPREGS_REQ:
resp_id = CAPTURE_PHY_STREAM_DUMPREGS_RESP;
break;
case CAPTURE_CSI_STREAM_SET_CONFIG_REQ:
resp_id = CAPTURE_CSI_STREAM_SET_CONFIG_RESP;
break;
case CAPTURE_CSI_STREAM_SET_PARAM_REQ:
resp_id = CAPTURE_CSI_STREAM_SET_PARAM_RESP;
break;
case CAPTURE_CSI_STREAM_TPG_SET_CONFIG_REQ:
resp_id = CAPTURE_CSI_STREAM_TPG_SET_CONFIG_RESP;
break;
case CAPTURE_CSI_STREAM_TPG_START_REQ:
resp_id = CAPTURE_CSI_STREAM_TPG_START_RESP;
capture->virtual_channel_id =
msg_cpy->csi_stream_tpg_start_req.virtual_channel_id;
break;
case CAPTURE_CSI_STREAM_TPG_START_RATE_REQ:
resp_id = CAPTURE_CSI_STREAM_TPG_START_RATE_RESP;
capture->virtual_channel_id = msg_cpy->
csi_stream_tpg_start_rate_req.virtual_channel_id;
break;
case CAPTURE_CSI_STREAM_TPG_APPLY_GAIN_REQ:
resp_id = CAPTURE_CSI_STREAM_TPG_APPLY_GAIN_RESP;
break;
case CAPTURE_CSI_STREAM_TPG_STOP_REQ:
resp_id = CAPTURE_CSI_STREAM_TPG_STOP_RESP;
break;
case CAPTURE_CHANNEL_EI_REQ:
resp_id = CAPTURE_CHANNEL_EI_RESP;
break;
case CAPTURE_HSM_CHANSEL_ERROR_MASK_REQ:
resp_id = CAPTURE_HSM_CHANSEL_ERROR_MASK_RESP;
break;
default:
dev_err(chan->dev, "%s: unknown capture control req 0x%x",
__func__, header->msg_id);
return -EINVAL;
}
err = vi_capture_ivc_send_control(chan, msg_cpy, size, resp_id);
if (err < 0) {
dev_err(chan->dev, "%s: failed to send IVC control message", __func__);
return err;
}
if (header->msg_id == CAPTURE_PHY_STREAM_OPEN_REQ)
chan->is_stream_opened = true;
else if (header->msg_id == CAPTURE_PHY_STREAM_CLOSE_REQ)
chan->is_stream_opened = false;
return err;
}
/**
* @brief Disable the VI channel's NVCSI TPG stream in RCE.
*
* @param[in] chan VI channel context
*
* @returns 0 (success), neg. errno (failure)
*/
static int csi_stream_tpg_disable(
struct tegra_vi_channel *chan)
{
struct vi_capture *capture = chan->capture_data;
struct CAPTURE_CONTROL_MSG control_desc;
struct CAPTURE_CONTROL_MSG *resp_msg = &capture->control_resp_msg;
int err = 0;
memset(&control_desc, 0, sizeof(control_desc));
control_desc.header.msg_id = CAPTURE_CSI_STREAM_TPG_STOP_REQ;
control_desc.header.channel_id = capture->channel_id;
control_desc.csi_stream_tpg_stop_req.stream_id = capture->stream_id;
control_desc.csi_stream_tpg_stop_req.virtual_channel_id =
capture->virtual_channel_id;
err = vi_capture_ivc_send_control(chan, &control_desc,
sizeof(control_desc), CAPTURE_CSI_STREAM_TPG_STOP_RESP);
if ((err < 0) ||
(resp_msg->csi_stream_tpg_stop_resp.result
!= CAPTURE_OK))
return err;
return 0;
}
/**
* @brief Disable the VI channel's NVCSI stream in RCE.
*
* @param[in] chan VI channel context
*
* @returns 0 (success), neg. errno (failure)
*/
static int csi_stream_close(
struct tegra_vi_channel *chan)
{
struct vi_capture *capture = chan->capture_data;
struct CAPTURE_CONTROL_MSG control_desc;
struct CAPTURE_CONTROL_MSG *resp_msg = &capture->control_resp_msg;
int err = 0;
memset(&control_desc, 0, sizeof(control_desc));
control_desc.header.msg_id = CAPTURE_PHY_STREAM_CLOSE_REQ;
control_desc.header.channel_id = capture->channel_id;
control_desc.phy_stream_close_req.phy_type = NVPHY_TYPE_CSI;
control_desc.phy_stream_close_req.stream_id = capture->stream_id;
control_desc.phy_stream_close_req.csi_port = capture->csi_port;
err = vi_capture_control_send_message(chan, &control_desc,
sizeof(control_desc));
if ((err < 0) ||
(resp_msg->phy_stream_close_resp.result != CAPTURE_OK))
return err;
return 0;
}
int csi_stream_release(
struct tegra_vi_channel *chan)
{
struct vi_capture *capture = chan->capture_data;
int err = 0;
if (capture->stream_id == NVCSI_STREAM_INVALID_ID)
return 0;
if (capture->virtual_channel_id != NVCSI_STREAM_INVALID_TPG_VC_ID) {
err = csi_stream_tpg_disable(chan);
if (err < 0) {
dev_err(chan->dev,
"%s: failed to disable nvcsi tpg on stream %u virtual channel %u\n",
__func__, capture->stream_id,
capture->virtual_channel_id);
return err;
}
}
if (chan->is_stream_opened) {
err = csi_stream_close(chan);
if (err < 0)
dev_err(chan->dev,
"%s: failed to close nvcsi stream %u\n",
__func__, capture->stream_id);
}
return err;
}
int vi_capture_control_message_from_user(
struct tegra_vi_channel *chan,
struct vi_capture_control_msg *msg)
{
struct vi_capture *capture;
const void __user *msg_ptr;
void __user *response;
void *msg_cpy;
struct CAPTURE_CONTROL_MSG *resp_msg;
int err = 0;
if (chan == NULL) {
dev_err(NULL, "%s: NULL VI channel received\n", __func__);
return -ENODEV;
}
if (msg == NULL) {
dev_err(NULL, "%s: NULL vi capture control message received\n", __func__);
return -EINVAL;
}
nv_camera_log(chan->ndev,
__arch_counter_get_cntvct(),
NVHOST_CAMERA_VI_CAPTURE_SET_CONFIG);
capture = chan->capture_data;
if (capture == NULL) {
dev_err(chan->dev,
"%s: vi capture uninitialized\n", __func__);
return -ENODEV;
}
resp_msg = &capture->control_resp_msg;
if (msg->ptr == 0ull || msg->response == 0ull || msg->size == 0)
return -EINVAL;
msg_ptr = (const void __user *)(uintptr_t)msg->ptr;
response = (void __user *)(uintptr_t)msg->response;
msg_cpy = kzalloc(msg->size, GFP_KERNEL);
if (unlikely(msg_cpy == NULL))
return -ENOMEM;
err = copy_from_user(msg_cpy, msg_ptr, msg->size) ? -EFAULT : 0;
if (err < 0)
goto fail;
err = vi_capture_control_send_message(chan, msg_cpy, msg->size);
if (err < 0)
goto fail;
err = copy_to_user(response, resp_msg,
sizeof(*resp_msg)) ? -EFAULT : 0;
if (err < 0)
goto fail;
fail:
kfree(msg_cpy);
return err;
}
EXPORT_SYMBOL_GPL(vi_capture_control_message_from_user);
int vi_capture_control_message(
struct tegra_vi_channel *chan,
struct vi_capture_control_msg *msg)
{
struct vi_capture *capture;
void *msg_cpy;
struct CAPTURE_CONTROL_MSG *resp_msg;
int err = 0;
if (chan == NULL) {
dev_err(NULL,"%s: NULL VI channel received\n", __func__);
return -ENODEV;
}
nv_camera_log(chan->ndev,
__arch_counter_get_cntvct(),
NVHOST_CAMERA_VI_CAPTURE_SET_CONFIG);
capture = chan->capture_data;
if (capture == NULL) {
dev_err(chan->dev,
"%s: vi capture uninitialized\n", __func__);
return -ENODEV;
}
resp_msg = &capture->control_resp_msg;
if (msg->ptr == 0ull || msg->response == 0ull || msg->size == 0)
return -EINVAL;
msg_cpy = kzalloc(msg->size, GFP_KERNEL);
if (unlikely(msg_cpy == NULL))
return -ENOMEM;
memcpy(msg_cpy, (const void *)(uintptr_t)msg->ptr,
msg->size);
err = vi_capture_control_send_message(chan, msg_cpy, msg->size);
if (err < 0)
goto fail;
memcpy((void *)(uintptr_t)msg->response, resp_msg,
sizeof(*resp_msg));
fail:
kfree(msg_cpy);
return err;
}
int vi_capture_get_info(
struct tegra_vi_channel *chan,
struct vi_capture_info *info)
{
struct vi_capture *capture = chan->capture_data;
int err;
nv_camera_log(chan->ndev,
__arch_counter_get_cntvct(),
NVHOST_CAMERA_VI_CAPTURE_GET_INFO);
if (capture == NULL) {
dev_err(chan->dev,
"%s: vi capture uninitialized\n", __func__);
return -ENODEV;
}
if (capture->channel_id == CAPTURE_CHANNEL_INVALID_ID) {
dev_err(chan->dev,
"%s: setup channel first\n", __func__);
return -ENODEV;
}
if (info == NULL)
return -EINVAL;
info->syncpts.progress_syncpt = capture->progress_sp.id;
info->syncpts.emb_data_syncpt = capture->embdata_sp.id;
info->syncpts.line_timer_syncpt = capture->linetimer_sp.id;
err = vi_capture_read_syncpt(chan, &capture->progress_sp,
&info->syncpts.progress_syncpt_val);
if (err < 0)
return err;
err = vi_capture_read_syncpt(chan, &capture->embdata_sp,
&info->syncpts.emb_data_syncpt_val);
if (err < 0)
return err;
err = vi_capture_read_syncpt(chan, &capture->linetimer_sp,
&info->syncpts.line_timer_syncpt_val);
if (err < 0)
return err;
info->hw_channel_id = capture->channel_id;
info->vi_channel_mask = capture->vi_channel_mask;
info->vi2_channel_mask = capture->vi2_channel_mask;
return 0;
}
EXPORT_SYMBOL_GPL(vi_capture_get_info);
static uint32_t vi_capture_get_num_progress(
struct tegra_vi_channel *chan,
struct vi_capture_req *req)
{
struct vi_capture *capture = chan->capture_data;
const uint16_t minProgress = 2U;
uint16_t numProgress = minProgress;
struct capture_descriptor *desc;
struct vi_channel_config *config;
unsigned int mul_value = 0;
if (check_mul_overflow(req->buffer_index, capture->request_size, &mul_value)) {
dev_err(chan->dev,
"%s:capture descriptor offset failed due to an overflow\n", __func__);
return minProgress;
}
desc = (struct capture_descriptor *)(capture->requests.va + mul_value);
config = &desc->ch_cfg;
/* Minimum of two progress fences for PXL_SOF and PXL_EOF */
if (config->flush_enable == 0x1UL)
{
if (config->flush_periodic == 0x1UL)
{
if (config->flush_first != 0U)
{
numProgress++;
}
numProgress += (config->frame.frame_y - config->flush_first) /
config->flush;
}
else
{
numProgress++;
}
/**
* if (HEIGHT % TRIPLINE == 0) then TRIPLINE is reached at the same
* time as PXL_EOF. EOF occurs on the cropped last line, hence EOF and
* EOL are simultaneous. In this case, final PXL_EOF tag have NLINES bit
* set in its payload, while there is no NLINE_DONE tag. Therefore,
* number of progress fences should be decreased by one.
*/
if (((config->frame.frame_y - config->flush_first) % config->flush) == 0U)
{
if (numProgress < minProgress) {
dev_err(chan->dev,
"%s:numProgress is less than the minimum value\n",
__func__);
numProgress = minProgress;
} else {
numProgress--;
}
}
}
return (uint32_t)numProgress;
}
int vi_capture_request(
struct tegra_vi_channel *chan,
struct vi_capture_req *req)
{
struct vi_capture *capture = chan->capture_data;
struct CAPTURE_MSG capture_desc;
int err = 0;
uint32_t sum_value = 0;
nv_camera_log(chan->ndev,
__arch_counter_get_cntvct(),
NVHOST_CAMERA_VI_CAPTURE_REQUEST);
if (capture == NULL) {
dev_err(chan->dev,
"%s: vi capture uninitialized\n", __func__);
return -ENODEV;
}
if (capture->channel_id == CAPTURE_CHANNEL_INVALID_ID) {
dev_err(chan->dev,
"%s: setup channel first\n", __func__);
return -ENODEV;
}
if (req == NULL) {
dev_err(chan->dev,
"%s: Invalid req\n", __func__);
return -EINVAL;
}
mutex_lock(&capture->reset_lock);
memset(&capture_desc, 0, sizeof(capture_desc));
capture_desc.header.msg_id = CAPTURE_REQUEST_REQ;
capture_desc.header.channel_id = capture->channel_id;
capture_desc.capture_request_req.buffer_index = req->buffer_index;
nv_camera_log_vi_submit(
chan->ndev,
capture->progress_sp.id,
capture->progress_sp.threshold,
capture_desc.header.channel_id,
__arch_counter_get_cntvct());
dev_dbg(chan->dev, "%s: sending chan_id %u msg_id %u buf:%u\n",
__func__, capture_desc.header.channel_id,
capture_desc.header.msg_id, req->buffer_index);
err = tegra_capture_ivc_capture_submit(&capture_desc,
sizeof(capture_desc));
if (err < 0) {
mutex_unlock(&capture->reset_lock);
dev_err(chan->dev, "IVC capture submit failed\n");
return err;
}
// Progress syncpoints + 1 for status syncpoint
if (check_add_overflow(vi_capture_get_num_progress(chan, req), 1U, &sum_value)) {
dev_err(chan->dev, "%s: check_sub failed due to an overflow\n", __func__);
} else if (check_add_overflow(capture->progress_sp.threshold, sum_value,
&capture->progress_sp.threshold)) {
dev_err(chan->dev, "%s: procress_sp failed due to an overflow\n", __func__);
}
mutex_unlock(&capture->reset_lock);
return 0;
}
EXPORT_SYMBOL_GPL(vi_capture_request);
int vi_capture_status(
struct tegra_vi_channel *chan,
int32_t timeout_ms)
{
struct vi_capture *capture = chan->capture_data;
int ret = 0;
nv_camera_log(chan->ndev,
__arch_counter_get_cntvct(),
NVHOST_CAMERA_VI_CAPTURE_STATUS);
if (capture == NULL) {
dev_err(chan->dev,
"%s: vi capture uninitialized\n", __func__);
return -ENODEV;
}
if (capture->channel_id == CAPTURE_CHANNEL_INVALID_ID) {
dev_err(chan->dev,
"%s: setup channel first\n", __func__);
return -ENODEV;
}
dev_dbg(chan->dev, "%s: waiting for status, timeout:%d ms\n",
__func__, timeout_ms);
/* negative timeout means wait forever */
if (timeout_ms < 0) {
ret = wait_for_completion_interruptible(&capture->capture_resp);
if (ret == -ERESTARTSYS) {
dev_dbg(chan->dev,
"capture status interrupted\n");
return -ETIMEDOUT;
}
} else {
ret = wait_for_completion_timeout(
&capture->capture_resp,
msecs_to_jiffies(timeout_ms));
if (ret == 0) {
dev_dbg(chan->dev,
"capture status timed out\n");
return -ETIMEDOUT;
}
}
if (ret < 0) {
dev_err(chan->dev,
"wait for capture status failed\n");
return ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(vi_capture_status);
int vi_capture_set_progress_status_notifier(
struct tegra_vi_channel *chan,
struct vi_capture_progress_status_req *req)
{
int err = 0;
struct vi_capture *capture = chan->capture_data;
nv_camera_log(chan->ndev,
__arch_counter_get_cntvct(),
NVHOST_CAMERA_VI_CAPTURE_SET_PROGRESS_STATUS);
if (req->mem == 0 ||
req->buffer_depth == 0) {
dev_err(chan->dev,
"%s: request buffer is invalid\n", __func__);
return -EINVAL;
}
if (capture == NULL) {
dev_err(chan->dev,
"%s: vi capture uninitialized\n", __func__);
return -ENODEV;
}
if (req->buffer_depth < capture->queue_depth) {
dev_err(chan->dev,
"Progress status buffer is smaller than queue depth");
return -EINVAL;
}
/* Setup the progress status buffer */
err = capture_common_setup_progress_status_notifier(
&capture->progress_status_notifier,
req->mem,
sizeof(uint32_t) * req->buffer_depth,
req->mem_offset);
if (err < 0) {
dev_err(chan->dev, "%s: memory setup failed\n", __func__);
return -EFAULT;
}
dev_dbg(chan->dev, "mem offset %u\n", req->mem_offset);
dev_dbg(chan->dev, "buffer depth %u\n", req->buffer_depth);
capture->progress_status_buffer_depth = req->buffer_depth;
capture->is_progress_status_notifier_set = true;
return err;
}
EXPORT_SYMBOL_GPL(vi_capture_set_progress_status_notifier);
static int csi_vi_get_mapping_table(struct platform_device *pdev)
{
uint32_t index = 0;
int err = 0;
struct device *dev = &pdev->dev;
struct tegra_capture_vi_data *info = platform_get_drvdata(pdev);
int nmap_elems;
uint32_t map_table_size;
uint32_t *map_table = info->vi_instance_table;
const struct device_node *np = dev->of_node;
(void)of_property_read_u32(np,
"nvidia,vi-mapping-size", &map_table_size);
if (map_table_size > MAX_NVCSI_STREAM_IDS) {
dev_err(dev, "invalid mapping table size %u\n", map_table_size);
return -EINVAL;
}
info->num_csi_vi_maps = map_table_size;
nmap_elems = of_property_count_strings(np, "nvidia,vi-mapping-names");
if (nmap_elems != ARRAY_SIZE(vi_mapping_elements))
return -EINVAL;
/* check for order of csi-stream-id and vi-unit-id in DT entry */
for (index = 0; index < ARRAY_SIZE(vi_mapping_elements); index++) {
int map_elem = of_property_match_string(np,
"nvidia,vi-mapping-names", vi_mapping_elements[index]);
if (map_elem != index) {
dev_err(dev, "invalid mapping order\n");
return -EINVAL;
}
}
for (index = 0; index < map_table_size; index++)
map_table[index] = INVALID_VI_UNIT_ID;
for (index = 0; index < map_table_size; index++) {
uint32_t stream_index = NVCSI_STREAM_INVALID_ID;
uint32_t vi_unit_id = INVALID_VI_UNIT_ID;
err = of_property_read_u32_index(np,
"nvidia,vi-mapping",
2 * index,
&stream_index);
if (err) {
dev_err(dev,
"%s: ERR %d: missing property nvidia,vi-mapping or csi_stream_id at index %d",
__func__, err, index);
return err;
}
/* Check for valid/duplicate csi-stream-id */
if (stream_index >= MAX_NVCSI_STREAM_IDS ||
map_table[stream_index] != INVALID_VI_UNIT_ID) {
dev_err(dev, "%s: mapping invalid csi_stream_id: %u\n",
__func__, stream_index);
return -EINVAL;
}
err = of_property_read_u32_index(np,
"nvidia,vi-mapping",
2 * index + 1,
&vi_unit_id);
if (err) {
dev_err(dev,
"%s: ERR %d: missing property nvidia,vi-mapping or vi_unit_id at index %d",
__func__, err, index);
return err;
}
/* check for valid vi-unit-id */
if (vi_unit_id >= MAX_VI_UNITS) {
dev_err(dev, "%s: mapping invalid vi_unit_id: %u\n",
__func__, vi_unit_id);
return -EINVAL;
}
map_table[stream_index] = vi_unit_id;
}
dev_dbg(dev, "%s: csi-stream to vi-instance mapping table size: %u\n",
__func__, info->num_csi_vi_maps);
for (index = 0; index < ARRAY_SIZE(info->vi_instance_table); index++)
dev_dbg(dev, "%s: vi_instance_table[%d] = %d\n",
__func__, index, info->vi_instance_table[index]);
return 0;
}
static int capture_vi_probe(struct platform_device *pdev)
{
uint32_t ii;
int err = 0;
struct tegra_capture_vi_data *info;
struct device *dev = &pdev->dev;
dev_dbg(dev, "%s: tegra-camrtc-capture-vi probe\n", __func__);
info = devm_kzalloc(dev,
sizeof(*info), GFP_KERNEL);
if (info == NULL)
return -ENOMEM;
info->num_vi_devices = 0;
err = of_property_read_u32(dev->of_node, "nvidia,vi-max-channels",
&info->max_vi_channels);
if (err || info->max_vi_channels > NUM_VI_CHANNELS) {
err = -EINVAL;
goto cleanup;
}
if (info->max_vi_channels == 0)
info->max_vi_channels = DEFAULT_VI_CHANNELS;
for (ii = 0; ; ii++) {
struct device_node *np;
struct platform_device *pvidev;
np = of_parse_phandle(dev->of_node, "nvidia,vi-devices", ii);
if (np == NULL)
break;
if (info->num_vi_devices >= ARRAY_SIZE(info->vi_pdevices)) {
of_node_put(np);
err = -EINVAL;
goto cleanup;
}
pvidev = of_find_device_by_node(np);
of_node_put(np);
if (pvidev == NULL) {
dev_WARN(dev, "vi node %d has no device\n", ii);
err = -ENODEV;
goto cleanup;
}
info->vi_pdevices[ii] = pvidev;
info->num_vi_devices++;
}
if (info->num_vi_devices < 1)
return -EINVAL;
platform_set_drvdata(pdev, info);
if (info->num_vi_devices == 1) {
dev_dbg(dev, "default 0 vi-unit-id for all csi-stream-ids\n");
} else {
/* read mapping table from DT for multiple VIs */
err = csi_vi_get_mapping_table(pdev);
if (err) {
dev_err(dev,
"%s: reading csi-to-vi mapping failed\n",
__func__);
goto cleanup;
}
}
err = vi_channel_drv_register(pdev, info->max_vi_channels);
if (err) {
vi_channel_drv_exit();
goto cleanup;
}
#ifdef NV_IS_L4T
info->vi_common.mc_vi.vi = &info->vi_common;
info->vi_common.mc_vi.fops = &vi5_fops;
err = tegra_capture_vi_media_controller_init(
&info->vi_common.mc_vi, pdev);
if (err) {
dev_warn(&pdev->dev, "media controller init failed\n");
err = 0;
}
#endif
memset(channels, 0 , sizeof(channels));
return 0;
cleanup:
for (ii = 0; ii < info->num_vi_devices; ii++)
put_device(&info->vi_pdevices[ii]->dev);
dev_err(dev, "%s: tegra-camrtc-capture-vi probe failed\n", __func__);
return err;
}
static int capture_vi_remove(struct platform_device *pdev)
{
struct tegra_capture_vi_data *info;
uint32_t ii;
struct device *dev = &pdev->dev;
dev_dbg(dev, "%s:tegra-camrtc-capture-vi remove\n", __func__);
info = platform_get_drvdata(pdev);
for (ii = 0; ii < info->num_vi_devices; ii++)
put_device(&info->vi_pdevices[ii]->dev);
vi_channel_drv_unregister(&pdev->dev);
#ifdef NV_IS_L4T
tegra_vi_media_controller_cleanup(&info->vi_common.mc_vi);
#endif
vi_channel_drv_exit();
return 0;
}
static const struct of_device_id capture_vi_of_match[] = {
{ .compatible = "nvidia,tegra-camrtc-capture-vi" },
{ },
};
MODULE_DEVICE_TABLE(of, capture_vi_of_match);
#if defined(NV_PLATFORM_DRIVER_STRUCT_REMOVE_RETURNS_VOID) /* Linux v6.11 */
static void capture_vi_remove_wrapper(struct platform_device *pdev)
{
capture_vi_remove(pdev);
}
#else
static int capture_vi_remove_wrapper(struct platform_device *pdev)
{
return capture_vi_remove(pdev);
}
#endif
static struct platform_driver capture_vi_driver = {
.probe = capture_vi_probe,
.remove = capture_vi_remove_wrapper,
.driver = {
.owner = THIS_MODULE,
.name = "tegra-camrtc-capture-vi",
.of_match_table = capture_vi_of_match
}
};
static int __init capture_vi_init(void)
{
int err;
err = vi_channel_drv_init();
if (err)
return err;
err = platform_driver_register(&capture_vi_driver);
if (err) {
vi_channel_drv_exit();
return err;
}
return 0;
}
static void __exit capture_vi_exit(void)
{
vi_channel_drv_exit();
platform_driver_unregister(&capture_vi_driver);
}
module_init(capture_vi_init);
module_exit(capture_vi_exit);
#if defined(NV_MODULE_IMPORT_NS_CALLS_STRINGIFY)
MODULE_IMPORT_NS(DMA_BUF);
#else
MODULE_IMPORT_NS("DMA_BUF");
#endif
MODULE_DESCRIPTION("tegra fusa-capture driver");
MODULE_LICENSE("GPL");