mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-23 01:31:30 +03:00
Upstream Linux commit 03c835f498b5 ("i2c: Switch .probe() to not
take an id parameter") removes the 'id' argument from the I2C probe
callback. Update the ov5693 driver, which defines an I2C probe callback
function, to fix building the driver for Linux v6.3.
Bug 4014315
Change-Id: I8c8954b7c1e1e322fb3f560f6348caac54e5e43d
Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2919423
Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>
GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
542 lines
12 KiB
C
542 lines
12 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
// Copyright (c) 2020-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
/*
|
|
* Lontium LT6911UXC HDMI-CSI bridge driver
|
|
*/
|
|
|
|
#include <linux/slab.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/module.h>
|
|
#include <linux/seq_file.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/version.h>
|
|
|
|
#include <media/tegra_v4l2_camera.h>
|
|
#include <media/tegracam_core.h>
|
|
|
|
#include "../platform/tegra/camera/camera_gpio.h"
|
|
|
|
static const struct of_device_id lt6911uxc_of_match[] = {
|
|
{ .compatible = "nvidia,lt6911uxc", },
|
|
{ },
|
|
};
|
|
MODULE_DEVICE_TABLE(of, lt6911uxc_of_match);
|
|
|
|
static const u32 ctrl_cid_list[] = {
|
|
TEGRA_CAMERA_CID_SENSOR_MODE_ID,
|
|
};
|
|
|
|
static const int lt6911uxc_60fps[] = {
|
|
60,
|
|
};
|
|
|
|
static const int lt6911uxc_30fps[] = {
|
|
30,
|
|
};
|
|
|
|
struct lt6911uxc {
|
|
struct i2c_client *i2c_client;
|
|
struct v4l2_subdev *subdev;
|
|
u16 fine_integ_time;
|
|
u32 frame_length;
|
|
struct camera_common_data *s_data;
|
|
struct tegracam_device *tc_dev;
|
|
};
|
|
|
|
static const struct camera_common_frmfmt lt6911uxc_frmfmt[] = {
|
|
{{1920, 1080}, lt6911uxc_60fps, 1, 0, 0},
|
|
{{3840, 2160}, lt6911uxc_60fps, 1, 0, 1},
|
|
{{1280, 720}, lt6911uxc_60fps, 1, 0, 2},
|
|
};
|
|
|
|
static const struct regmap_config sensor_regmap_config = {
|
|
.reg_bits = 16,
|
|
.val_bits = 8,
|
|
.cache_type = REGCACHE_RBTREE,
|
|
};
|
|
|
|
static int lt6911uxc_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
|
|
{
|
|
struct i2c_client *client = v4l2_get_subdevdata(sd);
|
|
|
|
dev_dbg(&client->dev, "%s:\n", __func__);
|
|
|
|
return 0;
|
|
}
|
|
static int lt6911uxc_set_group_hold(struct tegracam_device *tc_dev, bool val)
|
|
{
|
|
/* lt6911uxc does not support group hold */
|
|
return 0;
|
|
}
|
|
|
|
/* As soon as the reset pin is released, the bridge starts streaming */
|
|
static int lt6911uxc_start_streaming(struct tegracam_device *tc_dev)
|
|
{
|
|
struct camera_common_data *s_data = tc_dev->s_data;
|
|
struct camera_common_power_rail *pw = s_data->power;
|
|
|
|
if (pw->reset_gpio) {
|
|
if (gpio_cansleep(pw->reset_gpio))
|
|
gpio_set_value_cansleep(pw->reset_gpio, 1);
|
|
else
|
|
gpio_set_value(pw->reset_gpio, 1);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int lt6911uxc_stop_streaming(struct tegracam_device *tc_dev)
|
|
{
|
|
struct camera_common_data *s_data = tc_dev->s_data;
|
|
struct camera_common_power_rail *pw = s_data->power;
|
|
|
|
if (pw->reset_gpio) {
|
|
if (gpio_cansleep(pw->reset_gpio))
|
|
gpio_set_value_cansleep(pw->reset_gpio, 0);
|
|
else
|
|
gpio_set_value(pw->reset_gpio, 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int lt6911uxc_set_mode(struct tegracam_device *tc_dev)
|
|
{
|
|
/* Width and Height taken care of by the firmware */
|
|
return 0;
|
|
}
|
|
|
|
static inline int lt6911uxc_read_reg(struct camera_common_data *s_data,
|
|
u16 addr, u8 *val)
|
|
{
|
|
int err = 0;
|
|
u32 reg_val = 0;
|
|
|
|
err = regmap_read(s_data->regmap, addr, ®_val);
|
|
*val = reg_val & 0xff;
|
|
|
|
return err;
|
|
}
|
|
|
|
static inline int lt6911uxc_write_reg(struct camera_common_data *s_data,
|
|
u16 addr, u8 val)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int lt6911uxc_power_get(struct tegracam_device *tc_dev)
|
|
{
|
|
struct device *dev = tc_dev->dev;
|
|
struct camera_common_data *s_data = tc_dev->s_data;
|
|
struct camera_common_power_rail *pw = s_data->power;
|
|
struct camera_common_pdata *pdata = s_data->pdata;
|
|
struct clk *parent;
|
|
int err = 0;
|
|
|
|
if (!pdata) {
|
|
dev_err(dev, "pdata missing\n");
|
|
return -EFAULT;
|
|
}
|
|
|
|
/* Sensor MCLK (aka. INCK) */
|
|
if (pdata->mclk_name) {
|
|
pw->mclk = devm_clk_get(dev, pdata->mclk_name);
|
|
if (IS_ERR(pw->mclk)) {
|
|
dev_err(dev, "unable to get clock %s\n",
|
|
pdata->mclk_name);
|
|
return PTR_ERR(pw->mclk);
|
|
}
|
|
|
|
if (pdata->parentclk_name) {
|
|
parent = devm_clk_get(dev, pdata->parentclk_name);
|
|
if (IS_ERR(parent)) {
|
|
dev_err(dev, "unable to get parent clock %s",
|
|
pdata->parentclk_name);
|
|
} else
|
|
clk_set_parent(pw->mclk, parent);
|
|
}
|
|
}
|
|
|
|
/* analog 3.3v */
|
|
if (pdata->regulators.avdd)
|
|
err |= camera_common_regulator_get(dev,
|
|
&pw->avdd, pdata->regulators.avdd);
|
|
if (err) {
|
|
dev_err(dev, "%s: unable to get regulator(s)\n", __func__);
|
|
goto done;
|
|
}
|
|
|
|
/* dig 1.2v */
|
|
if (pdata->regulators.dvdd)
|
|
err |= camera_common_regulator_get(dev,
|
|
&pw->dvdd, pdata->regulators.dvdd);
|
|
if (err) {
|
|
dev_err(dev, "%s: unable to get regulator(s)\n", __func__);
|
|
goto done;
|
|
}
|
|
|
|
/* vdd 1.8v */
|
|
if (pdata->regulators.iovdd)
|
|
err |= camera_common_regulator_get(dev,
|
|
&pw->avdd, pdata->regulators.iovdd);
|
|
if (err) {
|
|
dev_err(dev, "%s: unable to get regulator(s)\n", __func__);
|
|
goto done;
|
|
}
|
|
|
|
/* Reset or ENABLE GPIO */
|
|
pw->reset_gpio = pdata->reset_gpio;
|
|
err = gpio_request(pw->reset_gpio, "cam_reset_gpio");
|
|
if (err < 0) {
|
|
dev_err(dev, "%s: unable to request reset_gpio (%d)\n",
|
|
__func__, err);
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
pw->state = SWITCH_OFF;
|
|
|
|
return err;
|
|
}
|
|
|
|
static int lt6911uxc_power_put(struct tegracam_device *tc_dev)
|
|
{
|
|
struct camera_common_data *s_data = tc_dev->s_data;
|
|
struct camera_common_power_rail *pw = s_data->power;
|
|
|
|
if (unlikely(!pw))
|
|
return -EFAULT;
|
|
|
|
if (likely(pw->dvdd))
|
|
devm_regulator_put(pw->dvdd);
|
|
|
|
if (likely(pw->avdd))
|
|
devm_regulator_put(pw->avdd);
|
|
|
|
if (likely(pw->iovdd))
|
|
devm_regulator_put(pw->iovdd);
|
|
|
|
pw->dvdd = NULL;
|
|
pw->avdd = NULL;
|
|
pw->iovdd = NULL;
|
|
|
|
if (likely(pw->reset_gpio))
|
|
gpio_free(pw->reset_gpio);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct camera_common_pdata *lt6911uxc_parse_dt(
|
|
struct tegracam_device *tc_dev)
|
|
{
|
|
struct device *dev = tc_dev->dev;
|
|
struct device_node *np = dev->of_node;
|
|
struct camera_common_pdata *board_priv_pdata;
|
|
const struct of_device_id *match;
|
|
struct camera_common_pdata *ret = NULL;
|
|
int err = 0;
|
|
int gpio;
|
|
|
|
if (!np)
|
|
return NULL;
|
|
|
|
match = of_match_device(lt6911uxc_of_match, dev);
|
|
if (!match) {
|
|
dev_err(dev, "Failed to find matching dt id\n");
|
|
return NULL;
|
|
}
|
|
|
|
board_priv_pdata = devm_kzalloc(dev,
|
|
sizeof(*board_priv_pdata), GFP_KERNEL);
|
|
if (!board_priv_pdata)
|
|
return NULL;
|
|
|
|
gpio = of_get_named_gpio(np, "reset-gpios", 0);
|
|
if (gpio < 0) {
|
|
if (gpio == -EPROBE_DEFER)
|
|
ret = ERR_PTR(-EPROBE_DEFER);
|
|
dev_err(dev, "reset-gpios not found\n");
|
|
goto error;
|
|
}
|
|
board_priv_pdata->reset_gpio = (unsigned int)gpio;
|
|
|
|
err = of_property_read_string(np, "mclk", &board_priv_pdata->mclk_name);
|
|
if (err)
|
|
dev_err(dev, "mclk absent,assuming sensor driven externally\n");
|
|
|
|
err = of_property_read_string(np, "avdd-reg",
|
|
&board_priv_pdata->regulators.avdd);
|
|
err |= of_property_read_string(np, "iovdd-reg",
|
|
&board_priv_pdata->regulators.iovdd);
|
|
err |= of_property_read_string(np, "dvdd-reg",
|
|
&board_priv_pdata->regulators.dvdd);
|
|
if (err)
|
|
dev_dbg(dev, "avdd, iovdd and/or dvdd reglrs. not present, \
|
|
assume sensor powered independently\n");
|
|
|
|
board_priv_pdata->has_eeprom =
|
|
of_property_read_bool(np, "has-eeprom");
|
|
|
|
return board_priv_pdata;
|
|
|
|
error:
|
|
devm_kfree(dev, board_priv_pdata);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int lt6911uxc_power_on(struct camera_common_data *s_data)
|
|
{
|
|
int err = 0;
|
|
struct camera_common_power_rail *pw = s_data->power;
|
|
struct camera_common_pdata *pdata = s_data->pdata;
|
|
struct device *dev = s_data->dev;
|
|
|
|
if (pdata && pdata->power_on) {
|
|
err = pdata->power_on(pw);
|
|
if (err)
|
|
dev_err(dev, "%s failed.\n", __func__);
|
|
else
|
|
pw->state = SWITCH_ON;
|
|
return err;
|
|
}
|
|
|
|
if (pw->reset_gpio) {
|
|
if (gpio_cansleep(pw->reset_gpio))
|
|
gpio_set_value_cansleep(pw->reset_gpio, 0);
|
|
else
|
|
gpio_set_value(pw->reset_gpio, 0);
|
|
}
|
|
|
|
usleep_range(10, 20);
|
|
|
|
if (pw->avdd) {
|
|
err = regulator_enable(pw->avdd);
|
|
if (err)
|
|
goto lt6911uxc_avdd_fail;
|
|
}
|
|
|
|
if (pw->iovdd) {
|
|
err = regulator_enable(pw->iovdd);
|
|
if (err)
|
|
goto lt6911uxc_iovdd_fail;
|
|
}
|
|
/* Need to wait for atleat 1 ms before turning up 1.2v */
|
|
usleep_range(1000, 1200);
|
|
if (pw->dvdd) {
|
|
err = regulator_enable(pw->dvdd);
|
|
if (err)
|
|
goto lt6911uxc_dvdd_fail;
|
|
}
|
|
|
|
usleep_range(10, 20);
|
|
|
|
pw->state = SWITCH_ON;
|
|
|
|
return 0;
|
|
|
|
lt6911uxc_dvdd_fail:
|
|
regulator_disable(pw->iovdd);
|
|
|
|
lt6911uxc_iovdd_fail:
|
|
regulator_disable(pw->avdd);
|
|
|
|
lt6911uxc_avdd_fail:
|
|
dev_err(dev, "%s failed.\n", __func__);
|
|
|
|
return -ENODEV;
|
|
}
|
|
|
|
static int lt6911uxc_power_off(struct camera_common_data *s_data)
|
|
{
|
|
int err = 0;
|
|
struct camera_common_power_rail *pw = s_data->power;
|
|
struct camera_common_pdata *pdata = s_data->pdata;
|
|
struct device *dev = s_data->dev;
|
|
|
|
if (pdata && pdata->power_off) {
|
|
err = pdata->power_off(pw);
|
|
if (err) {
|
|
dev_err(dev, "%s failed.\n", __func__);
|
|
return err;
|
|
}
|
|
} else {
|
|
if (pw->reset_gpio) {
|
|
if (gpio_cansleep(pw->reset_gpio))
|
|
gpio_set_value_cansleep(pw->reset_gpio, 0);
|
|
else
|
|
gpio_set_value(pw->reset_gpio, 0);
|
|
}
|
|
}
|
|
|
|
usleep_range(10, 20);
|
|
|
|
if (pw->dvdd)
|
|
regulator_disable(pw->dvdd);
|
|
if (pw->iovdd)
|
|
regulator_disable(pw->iovdd);
|
|
if (pw->avdd)
|
|
regulator_disable(pw->avdd);
|
|
|
|
pw->state = SWITCH_OFF;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int lt6911uxc_board_setup(struct lt6911uxc *priv)
|
|
{
|
|
struct camera_common_data *s_data = priv->s_data;
|
|
struct camera_common_pdata *pdata = s_data->pdata;
|
|
struct device *dev = s_data->dev;
|
|
int err = 0;
|
|
|
|
if (pdata->mclk_name) {
|
|
err = camera_common_mclk_enable(s_data);
|
|
if (err) {
|
|
dev_err(dev, "error turning on mclk (%d)\n", err);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
err = lt6911uxc_power_on(s_data);
|
|
if (err) {
|
|
dev_err(dev, "error during power on sensor (%d)\n", err);
|
|
goto err_power_on;
|
|
}
|
|
|
|
err_power_on:
|
|
if (pdata->mclk_name)
|
|
camera_common_mclk_disable(s_data);
|
|
|
|
done:
|
|
return err;
|
|
}
|
|
|
|
static struct tegracam_ctrl_ops lt6911uxc_ctrl_ops = {
|
|
.set_group_hold = lt6911uxc_set_group_hold,
|
|
};
|
|
|
|
static const struct v4l2_subdev_internal_ops lt6911uxc_subdev_internal_ops = {
|
|
.open = lt6911uxc_open,
|
|
};
|
|
|
|
static struct camera_common_sensor_ops lt6911uxc_common_ops = {
|
|
.numfrmfmts = ARRAY_SIZE(lt6911uxc_frmfmt),
|
|
.frmfmt_table = lt6911uxc_frmfmt,
|
|
.power_on = lt6911uxc_power_on,
|
|
.power_off = lt6911uxc_power_off,
|
|
.write_reg = lt6911uxc_write_reg,
|
|
.read_reg = lt6911uxc_read_reg,
|
|
.parse_dt = lt6911uxc_parse_dt,
|
|
.power_get = lt6911uxc_power_get,
|
|
.power_put = lt6911uxc_power_put,
|
|
.set_mode = lt6911uxc_set_mode,
|
|
.start_streaming = lt6911uxc_start_streaming,
|
|
.stop_streaming = lt6911uxc_stop_streaming,
|
|
};
|
|
|
|
|
|
#if KERNEL_VERSION(6, 3, 0) <= LINUX_VERSION_CODE
|
|
static int lt6911uxc_probe(struct i2c_client *client)
|
|
#else
|
|
static int lt6911uxc_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
#endif
|
|
{
|
|
struct device *dev = &client->dev;
|
|
struct tegracam_device *tc_dev;
|
|
struct lt6911uxc *priv;
|
|
int err;
|
|
|
|
dev_err(dev, "probing lt6911uxc v4l2 sensor at addr 0x%0x\n",
|
|
client->addr);
|
|
|
|
if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node)
|
|
return -EINVAL;
|
|
|
|
priv = devm_kzalloc(dev,
|
|
sizeof(struct lt6911uxc), GFP_KERNEL);
|
|
if (!priv)
|
|
return -ENOMEM;
|
|
|
|
tc_dev = devm_kzalloc(dev,
|
|
sizeof(struct tegracam_device), GFP_KERNEL);
|
|
if (!tc_dev)
|
|
return -ENOMEM;
|
|
|
|
priv->i2c_client = tc_dev->client = client;
|
|
tc_dev->dev = dev;
|
|
strncpy(tc_dev->name, "lt6911uxc", sizeof(tc_dev->name));
|
|
tc_dev->dev_regmap_config = &sensor_regmap_config;
|
|
tc_dev->sensor_ops = <6911uxc_common_ops;
|
|
tc_dev->v4l2sd_internal_ops = <6911uxc_subdev_internal_ops;
|
|
tc_dev->tcctrl_ops = <6911uxc_ctrl_ops;
|
|
|
|
err = tegracam_device_register(tc_dev);
|
|
if (err) {
|
|
dev_err(dev, "tegra camera driver registration failed\n");
|
|
return err;
|
|
}
|
|
priv->tc_dev = tc_dev;
|
|
priv->s_data = tc_dev->s_data;
|
|
priv->subdev = &tc_dev->s_data->subdev;
|
|
tegracam_set_privdata(tc_dev, (void *)priv);
|
|
|
|
err = lt6911uxc_board_setup(priv);
|
|
if (err) {
|
|
tegracam_device_unregister(tc_dev);
|
|
dev_err(dev, "board setup failed\n");
|
|
return err;
|
|
}
|
|
|
|
err = tegracam_v4l2subdev_register(tc_dev, true);
|
|
if (err) {
|
|
dev_err(dev, "tegra camera subdev registration failed\n");
|
|
return err;
|
|
}
|
|
|
|
dev_err(dev, "detected lt6911uxc sensor\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if (KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE)
|
|
static int lt6911uxc_remove(struct i2c_client *client)
|
|
#else
|
|
static void lt6911uxc_remove(struct i2c_client *client)
|
|
#endif
|
|
{
|
|
struct camera_common_data *s_data = to_camera_common_data(&client->dev);
|
|
struct lt6911uxc *priv = (struct lt6911uxc *)s_data->priv;
|
|
|
|
tegracam_v4l2subdev_unregister(priv->tc_dev);
|
|
tegracam_device_unregister(priv->tc_dev);
|
|
|
|
#if (KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE)
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
static const struct i2c_device_id lt6911uxc_id[] = {
|
|
{ "lt6911uxc", 0 },
|
|
{ }
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(i2c, lt6911uxc_id);
|
|
|
|
static struct i2c_driver lt6911uxc_i2c_driver = {
|
|
.driver = {
|
|
.name = "lt6911uxc",
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = of_match_ptr(lt6911uxc_of_match),
|
|
},
|
|
.probe = lt6911uxc_probe,
|
|
.remove = lt6911uxc_remove,
|
|
.id_table = lt6911uxc_id,
|
|
};
|
|
module_i2c_driver(lt6911uxc_i2c_driver);
|
|
MODULE_DESCRIPTION("Media Controller driver for Lontium LTX6911UXC");
|
|
MODULE_AUTHOR("Anubhav Rai <arai@nvidia.com>");
|
|
MODULE_LICENSE("GPL v2");
|