Files
linux-nv-oot/drivers/video/tegra/camera/tegra_camera_dev_mfi.c
Frank Chen 92ac7bc35a media: camera: Build tegra-camera as OOT module
Port camera drivers below from /kenrel/nvidia to
/kernel/nvidia-oot as OOT modules:
- Fusa-capture driver
- Tegra V4L2 framework driver
- vi/csi driver
- tegra camera platform driver

Change-Id: I390af27096425bb11e0934201dd1a90f001bb3fa
Signed-off-by: Frank Chen <frankc@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2780698
Reviewed-by: FNU Raunak <fraunak@nvidia.com>
Reviewed-by: Ankur Pawar <ankurp@nvidia.com>
Reviewed-by: Shiva Dubey <sdubey@nvidia.com>
GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
2022-12-13 06:15:42 -08:00

261 lines
6.1 KiB
C

// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2015-2022, NVIDIA CORPORATION. All rights reserved.
#include <linux/slab.h>
#include <media/tegra_camera_dev_mfi.h>
static LIST_HEAD(cmfidev_list);
static DEFINE_MUTEX(cmfidev_mutex);
static int tegra_camera_dev_mfi_init(void)
{
INIT_LIST_HEAD(&cmfidev_list);
return 0;
}
void tegra_camera_dev_mfi_cb(void *stub)
{
u32 idx = 0;
struct camera_mfi_dev *itr = NULL;
int err = 0;
mutex_lock(&cmfidev_mutex);
list_for_each_entry(itr, &cmfidev_list, list) {
if (itr->regmap) {
/* MFI driver has to delay the focuser writes by one
* frame, which is required to get sync in focus
* position and sharpness.
* So write previous frame focuser settings in current
* frame's callback, and then save current frame focuser
* writes for next callback.
*/
for (idx = 0; idx < itr->prev_num_used; idx++) {
err = regmap_write(itr->regmap,
itr->prev_reg[idx].addr,
itr->prev_reg[idx].val);
if (err)
pr_err("%s: [%s] regmap_write failed\n",
__func__, itr->name);
}
/* Consume current settings, which would be programmed
* in next frame callback.
*/
for (idx = 0; idx < itr->num_used; idx++) {
itr->prev_reg[idx].addr = itr->reg[idx].addr;
itr->prev_reg[idx].val = itr->reg[idx].val;
}
itr->prev_num_used = itr->num_used;
} else if (itr->i2c_client) {
for (idx = 0; idx < itr->num_used; idx++) {
err = i2c_transfer(itr->i2c_client->adapter,
&itr->msg[idx].msg, 1);
if (err != 1)
pr_err("%s: [%s] i2c_transfer failed\n",
__func__, itr->name);
}
} else {
pr_err("%s [%s] Unknown device mechanism\n",
__func__, itr->name);
}
itr->num_used = 0;
}
mutex_unlock(&cmfidev_mutex);
}
EXPORT_SYMBOL(tegra_camera_dev_mfi_cb);
int tegra_camera_dev_mfi_wr_add_i2c(
struct camera_mfi_dev *cmfidev,
struct i2c_msg *msg, int num)
{
int err = -ENODEV;
int i = 0;
struct camera_mfi_dev *itr = NULL;
if (!strcmp(cmfidev->name, "")) {
err = -EINVAL;
goto cmfi_wr_add_i2c_end;
}
mutex_lock(&cmfidev_mutex);
list_for_each_entry(itr, &cmfidev_list, list) {
if (!strcmp(itr->name, cmfidev->name)) {
if (itr->num_used == CAMERA_REGCACHE_MAX)
err = -ENOSPC;
else {
for (i = 0; i < num; i++) {
itr->msg[itr->num_used].msg = msg[i];
memcpy(itr->msg[itr->num_used].buf,
msg[i].buf,
msg[i].len);
itr->msg[itr->num_used].msg.buf =
itr->msg[itr->num_used].buf;
itr->num_used++;
}
err = 0;
}
}
}
mutex_unlock(&cmfidev_mutex);
cmfi_wr_add_i2c_end:
return err;
}
EXPORT_SYMBOL(tegra_camera_dev_mfi_wr_add_i2c);
int tegra_camera_dev_mfi_wr_add(
struct camera_mfi_dev *cmfidev,
u32 offset, u32 val)
{
int err = -ENODEV;
struct camera_mfi_dev *itr = NULL;
if (!strcmp(cmfidev->name, "")) {
err = -EINVAL;
goto cmfi_wr_add_end;
}
mutex_lock(&cmfidev_mutex);
list_for_each_entry(itr, &cmfidev_list, list) {
if (!strcmp(itr->name, cmfidev->name)) {
if (itr->num_used == CAMERA_REGCACHE_MAX) {
err = -ENOSPC;
} else {
itr->reg[itr->num_used].addr = offset;
itr->reg[itr->num_used].val = val;
itr->num_used++;
err = 0;
}
}
}
mutex_unlock(&cmfidev_mutex);
cmfi_wr_add_end:
return err;
}
EXPORT_SYMBOL(tegra_camera_dev_mfi_wr_add);
int tegra_camera_dev_mfi_clear(struct camera_mfi_dev *cmfidev)
{
int err = -ENODEV;
struct camera_mfi_dev *itr = NULL;
if (cmfidev == NULL) {
err = -EINVAL;
goto cmfidev_clear_end;
}
if (!strcmp(cmfidev->name, "")) {
err = -EINVAL;
goto cmfidev_clear_end;
}
mutex_lock(&cmfidev_mutex);
list_for_each_entry(itr, &cmfidev_list, list) {
if (!strcmp(itr->name, cmfidev->name)) {
if (itr->num_used > 0)
pr_info("%s [%s] force clear Q pending writes\n",
__func__, itr->name);
itr->num_used = 0;
err = 0;
}
}
mutex_unlock(&cmfidev_mutex);
cmfidev_clear_end:
return err;
}
EXPORT_SYMBOL(tegra_camera_dev_mfi_clear);
int tegra_camera_dev_mfi_add_i2cclient(
struct camera_mfi_dev **cmfidev,
u8 *name,
struct i2c_client *i2c_client)
{
int err = 0;
struct camera_mfi_dev *itr = NULL;
struct camera_mfi_dev *new_cmfidev = NULL;
if (name == NULL || !strcmp(name, ""))
return -EINVAL;
mutex_lock(&cmfidev_mutex);
list_for_each_entry(itr, &cmfidev_list, list) {
if (!strcmp(itr->name, name)) {
err = -EEXIST;
goto cmfidev_add_i2c_unlock;
}
}
if (!err) {
new_cmfidev =
kzalloc(sizeof(struct camera_mfi_dev), GFP_KERNEL);
if (!new_cmfidev) {
pr_err("%s memory low!\n", __func__);
err = -ENOMEM;
goto cmfidev_add_i2c_unlock;
}
memset(new_cmfidev, 0, sizeof(struct camera_mfi_dev));
strncpy(new_cmfidev->name, name, sizeof(new_cmfidev->name)-1);
INIT_LIST_HEAD(&new_cmfidev->list);
new_cmfidev->i2c_client = i2c_client;
new_cmfidev->num_used = 0;
list_add(&new_cmfidev->list, &cmfidev_list);
}
*cmfidev = new_cmfidev;
cmfidev_add_i2c_unlock:
mutex_unlock(&cmfidev_mutex);
return err;
}
EXPORT_SYMBOL(tegra_camera_dev_mfi_add_i2cclient);
int tegra_camera_dev_mfi_add_regmap(
struct camera_mfi_dev **cmfidev,
u8 *name,
struct regmap *regmap)
{
int err = 0;
struct camera_mfi_dev *itr = NULL;
struct camera_mfi_dev *new_cmfidev = NULL;
if (name == NULL || !strcmp(name, ""))
return -EINVAL;
mutex_lock(&cmfidev_mutex);
list_for_each_entry(itr, &cmfidev_list, list) {
if (!strcmp(itr->name, name)) {
err = -EEXIST;
goto cmfidev_add_regmap_unlock;
}
}
if (!err) {
new_cmfidev =
kzalloc(sizeof(struct camera_mfi_dev), GFP_KERNEL);
if (!new_cmfidev) {
pr_err("%s memory low!\n", __func__);
err = -ENOMEM;
goto cmfidev_add_regmap_unlock;
}
memset(new_cmfidev, 0, sizeof(struct camera_mfi_dev));
strncpy(new_cmfidev->name, name, sizeof(new_cmfidev->name)-1);
INIT_LIST_HEAD(&new_cmfidev->list);
new_cmfidev->regmap = regmap;
new_cmfidev->num_used = 0;
new_cmfidev->prev_num_used = 0;
if (list_empty(&cmfidev_list))
tegra_camera_dev_mfi_init();
list_add(&new_cmfidev->list, &cmfidev_list);
}
*cmfidev = new_cmfidev;
cmfidev_add_regmap_unlock:
mutex_unlock(&cmfidev_mutex);
return err;
}
EXPORT_SYMBOL(tegra_camera_dev_mfi_add_regmap);
MODULE_LICENSE("GPL");