Files
linux-nv-oot/drivers/media/i2c/max9295.c
Jon Hunter 3cadadc11c media: Fix build for Linux v6.3
Upstream Linux commit 03c835f498b5 ("i2c: Switch .probe() to not take an
id parameter") removes the 'id' argument from the I2C probe callback.
Update the various sensor drivers, which define an I2C probe callback
function, to fix building the drivers for Linux v6.3.

Bug 4014315
Bug 4075345

Change-Id: I2dc06188d1d19e086f108013c63439d9381d6c58
Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2889877
Reviewed-by: Rohit Khanna <rokhanna@nvidia.com>
GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
2023-04-19 10:58:01 -07:00

561 lines
13 KiB
C

// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2018-2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
/*
* max9295.c - max9295 GMSL Serializer driver
*/
#include <media/camera_common.h>
#include <linux/module.h>
#include <media/max9295.h>
/* register specifics */
#define MAX9295_MIPI_RX0_ADDR 0x330
#define MAX9295_MIPI_RX1_ADDR 0x331
#define MAX9295_MIPI_RX2_ADDR 0x332
#define MAX9295_MIPI_RX3_ADDR 0x333
#define MAX9295_PIPE_X_DT_ADDR 0x314
#define MAX9295_PIPE_Y_DT_ADDR 0x316
#define MAX9295_PIPE_Z_DT_ADDR 0x318
#define MAX9295_PIPE_U_DT_ADDR 0x31A
#define MAX9295_CTRL0_ADDR 0x10
#define MAX9295_SRC_CTRL_ADDR 0x2BF
#define MAX9295_SRC_PWDN_ADDR 0x02BE
#define MAX9295_SRC_OUT_RCLK_ADDR 0x3F1
#define MAX9295_START_PIPE_ADDR 0x311
#define MAX9295_PIPE_EN_ADDR 0x2
#define MAX9295_CSI_PORT_SEL_ADDR 0x308
#define MAX9295_I2C4_ADDR 0x44
#define MAX9295_I2C5_ADDR 0x45
#define MAX9295_DEV_ADDR 0x00
#define MAX9295_STREAM_PIPE_UNUSED 0x22
#define MAX9295_CSI_MODE_1X4 0x00
#define MAX9295_CSI_MODE_2X2 0x03
#define MAX9295_CSI_MODE_2X4 0x06
#define MAX9295_CSI_PORT_B(num_lanes) (((num_lanes) << 4) & 0xF0)
#define MAX9295_CSI_PORT_A(num_lanes) ((num_lanes) & 0x0F)
#define MAX9295_CSI_1X4_MODE_LANE_MAP1 0xE0
#define MAX9295_CSI_1X4_MODE_LANE_MAP2 0x04
#define MAX9295_CSI_2X4_MODE_LANE_MAP1 0xEE
#define MAX9295_CSI_2X4_MODE_LANE_MAP2 0xE4
#define MAX9295_CSI_2X2_MODE_LANE_MAP1 MAX9295_CSI_2X4_MODE_LANE_MAP1
#define MAX9295_CSI_2X2_MODE_LANE_MAP2 MAX9295_CSI_2X4_MODE_LANE_MAP2
#define MAX9295_ST_ID_0 0x0
#define MAX9295_ST_ID_1 0x1
#define MAX9295_ST_ID_2 0x2
#define MAX9295_ST_ID_3 0x3
#define MAX9295_PIPE_X_START_B 0x80
#define MAX9295_PIPE_Y_START_B 0x40
#define MAX9295_PIPE_Z_START_B 0x20
#define MAX9295_PIPE_U_START_B 0x10
#define MAX9295_PIPE_X_START_A 0x1
#define MAX9295_PIPE_Y_START_A 0x2
#define MAX9295_PIPE_Z_START_A 0x4
#define MAX9295_PIPE_U_START_A 0x8
#define MAX9295_START_PORT_A 0x10
#define MAX9295_START_PORT_B 0x20
#define MAX9295_CSI_LN2 0x1
#define MAX9295_CSI_LN4 0x3
#define MAX9295_EN_LINE_INFO 0x40
#define MAX9295_VID_TX_EN_X 0x10
#define MAX9295_VID_TX_EN_Y 0x20
#define MAX9295_VID_TX_EN_Z 0x40
#define MAX9295_VID_TX_EN_U 0x80
#define MAX9295_VID_INIT 0x3
#define MAX9295_SRC_RCLK 0x89
#define MAX9295_RESET_ALL 0x80
#define MAX9295_RESET_SRC 0x60
#define MAX9295_PWDN_GPIO 0x90
#define MAX9295_MAX_PIPES 0x4
struct max9295_client_ctx {
struct gmsl_link_ctx *g_ctx;
bool st_done;
};
struct max9295 {
struct i2c_client *i2c_client;
struct regmap *regmap;
struct max9295_client_ctx g_client;
struct mutex lock;
/* primary serializer properties */
__u32 def_addr;
__u32 pst2_ref;
};
static struct max9295 *prim_priv__;
struct map_ctx {
u8 dt;
u16 addr;
u8 val;
u8 st_id;
};
static int max9295_write_reg(struct device *dev, u16 addr, u8 val)
{
struct max9295 *priv = dev_get_drvdata(dev);
int err;
err = regmap_write(priv->regmap, addr, val);
if (err)
dev_err(dev, "%s:i2c write failed, 0x%x = %x\n",
__func__, addr, val);
/* delay before next i2c command as required for SERDES link */
usleep_range(100, 110);
return err;
}
int max9295_setup_streaming(struct device *dev)
{
struct max9295 *priv = dev_get_drvdata(dev);
int err = 0;
u32 csi_mode;
u32 lane_map1;
u32 lane_map2;
u32 port;
u32 rx1_lanes;
u32 st_pipe;
u32 pipe_en;
u32 port_sel = 0;
struct gmsl_link_ctx *g_ctx;
u32 i;
u32 j;
u32 st_en;
struct map_ctx map_pipe_dtype[] = {
{GMSL_CSI_DT_RAW_12, MAX9295_PIPE_Z_DT_ADDR, 0x2C,
MAX9295_ST_ID_2},
{GMSL_CSI_DT_UED_U1, MAX9295_PIPE_X_DT_ADDR, 0x30,
MAX9295_ST_ID_0},
{GMSL_CSI_DT_EMBED, MAX9295_PIPE_Y_DT_ADDR, 0x12,
MAX9295_ST_ID_1},
};
mutex_lock(&priv->lock);
if (!priv->g_client.g_ctx) {
dev_err(dev, "%s: no sdev client found\n", __func__);
err = -EINVAL;
goto error;
}
if (priv->g_client.st_done) {
dev_dbg(dev, "%s: stream setup is already done\n", __func__);
goto error;
}
g_ctx = priv->g_client.g_ctx;
switch (g_ctx->csi_mode) {
case GMSL_CSI_1X4_MODE:
csi_mode = MAX9295_CSI_MODE_1X4;
lane_map1 = MAX9295_CSI_1X4_MODE_LANE_MAP1;
lane_map2 = MAX9295_CSI_1X4_MODE_LANE_MAP2;
rx1_lanes = MAX9295_CSI_LN4;
break;
case GMSL_CSI_2X2_MODE:
csi_mode = MAX9295_CSI_MODE_2X2;
lane_map1 = MAX9295_CSI_2X2_MODE_LANE_MAP1;
lane_map2 = MAX9295_CSI_2X2_MODE_LANE_MAP2;
rx1_lanes = MAX9295_CSI_LN2;
break;
case GMSL_CSI_2X4_MODE:
csi_mode = MAX9295_CSI_MODE_2X4;
lane_map1 = MAX9295_CSI_2X4_MODE_LANE_MAP1;
lane_map2 = MAX9295_CSI_2X4_MODE_LANE_MAP2;
rx1_lanes = MAX9295_CSI_LN4;
break;
default:
dev_err(dev, "%s: invalid csi mode\n", __func__);
err = -EINVAL;
goto error;
}
port = (g_ctx->src_csi_port == GMSL_CSI_PORT_B) ?
MAX9295_CSI_PORT_B(rx1_lanes) :
MAX9295_CSI_PORT_A(rx1_lanes);
max9295_write_reg(dev, MAX9295_MIPI_RX0_ADDR, csi_mode);
max9295_write_reg(dev, MAX9295_MIPI_RX1_ADDR, port);
max9295_write_reg(dev, MAX9295_MIPI_RX2_ADDR, lane_map1);
max9295_write_reg(dev, MAX9295_MIPI_RX3_ADDR, lane_map2);
for (i = 0; i < g_ctx->num_streams; i++) {
struct gmsl_stream *g_stream = &g_ctx->streams[i];
g_stream->st_id_sel = GMSL_ST_ID_UNUSED;
for (j = 0; j < ARRAY_SIZE(map_pipe_dtype); j++) {
if (map_pipe_dtype[j].dt == g_stream->st_data_type) {
/*
* TODO:
* 1) Remove link specific overrides, depends
* on #2.
* 2) Add support for vc id based stream sel
* overrides TX_SRC_SEL. would be useful in
* using same mappings in all ser devs.
*/
if (g_ctx->serdes_csi_link ==
GMSL_SERDES_CSI_LINK_B) {
map_pipe_dtype[j].addr += 2;
map_pipe_dtype[j].st_id += 1;
}
g_stream->st_id_sel = map_pipe_dtype[j].st_id;
st_en = (map_pipe_dtype[j].addr ==
MAX9295_PIPE_X_DT_ADDR) ?
0xC0 : 0x40;
max9295_write_reg(dev, map_pipe_dtype[j].addr,
(st_en | map_pipe_dtype[j].val));
}
}
}
for (i = 0; i < g_ctx->num_streams; i++)
if (g_ctx->streams[i].st_id_sel != GMSL_ST_ID_UNUSED)
port_sel |= (1 << g_ctx->streams[i].st_id_sel);
if (g_ctx->src_csi_port == GMSL_CSI_PORT_B) {
st_pipe = (MAX9295_PIPE_X_START_B | MAX9295_PIPE_Y_START_B |
MAX9295_PIPE_Z_START_B | MAX9295_PIPE_U_START_B);
port_sel |= (MAX9295_EN_LINE_INFO | MAX9295_START_PORT_B);
} else {
st_pipe = MAX9295_PIPE_X_START_A | MAX9295_PIPE_Y_START_A |
MAX9295_PIPE_Z_START_A | MAX9295_PIPE_U_START_A;
port_sel |= (MAX9295_EN_LINE_INFO | MAX9295_START_PORT_A);
}
pipe_en = (MAX9295_VID_TX_EN_X | MAX9295_VID_TX_EN_Y |
MAX9295_VID_TX_EN_Z | MAX9295_VID_TX_EN_U | MAX9295_VID_INIT);
max9295_write_reg(dev, MAX9295_START_PIPE_ADDR, st_pipe);
max9295_write_reg(dev, MAX9295_CSI_PORT_SEL_ADDR, port_sel);
max9295_write_reg(dev, MAX9295_PIPE_EN_ADDR, pipe_en);
priv->g_client.st_done = true;
error:
mutex_unlock(&priv->lock);
return err;
}
EXPORT_SYMBOL(max9295_setup_streaming);
int max9295_setup_control(struct device *dev)
{
struct max9295 *priv = dev_get_drvdata(dev);
int err = 0;
struct gmsl_link_ctx *g_ctx;
u32 offset1 = 0;
u32 offset2 = 0;
u32 i;
u8 i2c_ovrd[] = {
0x6B, 0x10,
0x73, 0x11,
0x7B, 0x30,
0x83, 0x30,
0x93, 0x30,
0x9B, 0x30,
0xA3, 0x30,
0xAB, 0x30,
0x8B, 0x30,
};
u8 addr_offset[] = {
0x80, 0x00, 0x00,
0x84, 0x00, 0x01,
0xC0, 0x02, 0x02,
0xC4, 0x02, 0x03,
};
mutex_lock(&priv->lock);
if (!priv->g_client.g_ctx) {
dev_err(dev, "%s: no sensor dev client found\n", __func__);
err = -EINVAL;
goto error;
}
g_ctx = priv->g_client.g_ctx;
if (prim_priv__) {
/* update address reassingment */
max9295_write_reg(&prim_priv__->i2c_client->dev,
MAX9295_DEV_ADDR, (g_ctx->ser_reg << 1));
}
if (g_ctx->serdes_csi_link == GMSL_SERDES_CSI_LINK_A)
err = max9295_write_reg(dev, MAX9295_CTRL0_ADDR, 0x21);
else
err = max9295_write_reg(dev, MAX9295_CTRL0_ADDR, 0x22);
/* check if serializer device exists */
if (err) {
dev_err(dev, "%s: ERROR: ser device not found\n", __func__);
goto error;
}
/* delay to settle link */
msleep(100);
for (i = 0; i < ARRAY_SIZE(addr_offset); i += 3) {
if ((g_ctx->ser_reg << 1) == addr_offset[i]) {
offset1 = addr_offset[i+1];
offset2 = addr_offset[i+2];
break;
}
}
if (i == ARRAY_SIZE(addr_offset)) {
dev_err(dev, "%s: invalid ser slave address\n", __func__);
err = -EINVAL;
goto error;
}
for (i = 0; i < ARRAY_SIZE(i2c_ovrd); i += 2) {
/* update address overrides */
i2c_ovrd[i+1] += (i < 4) ? offset1 : offset2;
/* i2c passthrough2 must be configured once for all devices */
if ((i2c_ovrd[i] == 0x8B) && prim_priv__ &&
prim_priv__->pst2_ref)
continue;
max9295_write_reg(dev, i2c_ovrd[i], i2c_ovrd[i+1]);
}
/* dev addr pass-through2 ref */
if (prim_priv__)
prim_priv__->pst2_ref++;
max9295_write_reg(dev, MAX9295_I2C4_ADDR, (g_ctx->sdev_reg << 1));
max9295_write_reg(dev, MAX9295_I2C5_ADDR, (g_ctx->sdev_def << 1));
max9295_write_reg(dev, MAX9295_SRC_PWDN_ADDR, MAX9295_PWDN_GPIO);
max9295_write_reg(dev, MAX9295_SRC_CTRL_ADDR, MAX9295_RESET_SRC);
max9295_write_reg(dev, MAX9295_SRC_OUT_RCLK_ADDR, MAX9295_SRC_RCLK);
g_ctx->serdev_found = true;
error:
mutex_unlock(&priv->lock);
return err;
}
EXPORT_SYMBOL(max9295_setup_control);
int max9295_reset_control(struct device *dev)
{
struct max9295 *priv = dev_get_drvdata(dev);
int err = 0;
mutex_lock(&priv->lock);
if (!priv->g_client.g_ctx) {
dev_err(dev, "%s: no sdev client found\n", __func__);
err = -EINVAL;
goto error;
}
priv->g_client.st_done = false;
if (prim_priv__) {
prim_priv__->pst2_ref--;
max9295_write_reg(dev, MAX9295_DEV_ADDR,
(prim_priv__->def_addr << 1));
max9295_write_reg(&prim_priv__->i2c_client->dev,
MAX9295_CTRL0_ADDR, MAX9295_RESET_ALL);
}
error:
mutex_unlock(&priv->lock);
return err;
}
EXPORT_SYMBOL(max9295_reset_control);
int max9295_sdev_pair(struct device *dev, struct gmsl_link_ctx *g_ctx)
{
struct max9295 *priv;
int err = 0;
if (!dev || !g_ctx || !g_ctx->s_dev) {
dev_err(dev, "%s: invalid input params\n", __func__);
return -EINVAL;
}
priv = dev_get_drvdata(dev);
mutex_lock(&priv->lock);
if (priv->g_client.g_ctx) {
dev_err(dev, "%s: device already paired\n", __func__);
err = -EINVAL;
goto error;
}
priv->g_client.st_done = false;
priv->g_client.g_ctx = g_ctx;
error:
mutex_unlock(&priv->lock);
return 0;
}
EXPORT_SYMBOL(max9295_sdev_pair);
int max9295_sdev_unpair(struct device *dev, struct device *s_dev)
{
struct max9295 *priv = NULL;
int err = 0;
if (!dev || !s_dev) {
dev_err(dev, "%s: invalid input params\n", __func__);
return -EINVAL;
}
priv = dev_get_drvdata(dev);
mutex_lock(&priv->lock);
if (!priv->g_client.g_ctx) {
dev_err(dev, "%s: device is not paired\n", __func__);
err = -ENOMEM;
goto error;
}
if (priv->g_client.g_ctx->s_dev != s_dev) {
dev_err(dev, "%s: invalid device\n", __func__);
err = -EINVAL;
goto error;
}
priv->g_client.g_ctx = NULL;
priv->g_client.st_done = false;
error:
mutex_unlock(&priv->lock);
return err;
}
EXPORT_SYMBOL(max9295_sdev_unpair);
static struct regmap_config max9295_regmap_config = {
.reg_bits = 16,
.val_bits = 8,
.cache_type = REGCACHE_RBTREE,
};
#if KERNEL_VERSION(6, 3, 0) <= LINUX_VERSION_CODE
static int max9295_probe(struct i2c_client *client)
#else
static int max9295_probe(struct i2c_client *client,
const struct i2c_device_id *id)
#endif
{
struct max9295 *priv;
int err = 0;
struct device_node *node = client->dev.of_node;
dev_info(&client->dev, "[MAX9295]: probing GMSL Serializer\n");
priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
priv->i2c_client = client;
priv->regmap = devm_regmap_init_i2c(priv->i2c_client,
&max9295_regmap_config);
if (IS_ERR(priv->regmap)) {
dev_err(&client->dev,
"regmap init failed: %ld\n", PTR_ERR(priv->regmap));
return -ENODEV;
}
mutex_init(&priv->lock);
if (of_get_property(node, "is-prim-ser", NULL)) {
if (prim_priv__) {
dev_err(&client->dev,
"prim-ser already exists\n");
return -EEXIST;
}
err = of_property_read_u32(node, "reg", &priv->def_addr);
if (err < 0) {
dev_err(&client->dev, "reg not found\n");
return -EINVAL;
}
prim_priv__ = priv;
}
dev_set_drvdata(&client->dev, priv);
/* dev communication gets validated when GMSL link setup is done */
dev_info(&client->dev, "%s: success\n", __func__);
return err;
}
#if (KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE)
static int max9295_remove(struct i2c_client *client)
#else
static void max9295_remove(struct i2c_client *client)
#endif
{
struct max9295 *priv;
if (client != NULL) {
priv = dev_get_drvdata(&client->dev);
mutex_destroy(&priv->lock);
i2c_unregister_device(client);
client = NULL;
}
#if (KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE)
return 0;
#endif
}
static const struct i2c_device_id max9295_id[] = {
{ "max9295", 0 },
{ },
};
static const struct of_device_id max9295_of_match[] = {
{ .compatible = "maxim,max9295", },
{ },
};
MODULE_DEVICE_TABLE(of, max9295_of_match);
MODULE_DEVICE_TABLE(i2c, max9295_id);
static struct i2c_driver max9295_i2c_driver = {
.driver = {
.name = "max9295",
.owner = THIS_MODULE,
},
.probe = max9295_probe,
.remove = max9295_remove,
.id_table = max9295_id,
};
module_i2c_driver(max9295_i2c_driver);
MODULE_DESCRIPTION("GMSL Serializer driver max9295");
MODULE_AUTHOR("Sudhir Vyas <svyas@nvidia.com>");
MODULE_LICENSE("GPL v2");