mirror of
https://github.com/nxp-imx/linux-imx.git
synced 2025-10-22 23:23:03 +02:00
AAUTO-2163: Split rdacm20 initialization using rdacm20_core_ops.command()
In case of error during ov10635 init the I2C commands need to be resend. The serial link however suffers spurious I2C acknowledges during OV10635 init and it is required to raise reverse channel amplitude to get reliable ACK/NACK response.
This commit is contained in:
parent
d5e6026b67
commit
8db9f5c61a
|
@ -12,7 +12,7 @@
|
|||
#size-cells = <0>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_i2c_mipi_csi0>;
|
||||
clock-frequency = <100000>;
|
||||
clock-frequency = <300000>;
|
||||
status = "okay";
|
||||
|
||||
max9286_csi0@6a {
|
||||
|
@ -23,7 +23,7 @@
|
|||
enable-gpios = <&lsio_gpio1 27 GPIO_ACTIVE_HIGH>;
|
||||
maxim,gpio-poc = <0 GPIO_ACTIVE_LOW>;
|
||||
|
||||
maxim,i2c-remote-bus-hz = <105000>;
|
||||
maxim,i2c-remote-bus-hz = <339000>;
|
||||
maxim,reverse-channel-microvolt = <100000>;
|
||||
|
||||
ports {
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#size-cells = <0>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_i2c_mipi_csi1>;
|
||||
clock-frequency = <100000>;
|
||||
clock-frequency = <300000>;
|
||||
status = "okay";
|
||||
|
||||
max9286_csi1@6a {
|
||||
|
@ -23,7 +23,7 @@
|
|||
enable-gpios = <&lsio_gpio1 30 GPIO_ACTIVE_HIGH>;
|
||||
maxim,gpio-poc = <0 GPIO_ACTIVE_LOW>;
|
||||
|
||||
maxim,i2c-remote-bus-hz = <105000>;
|
||||
maxim,i2c-remote-bus-hz = <339000>;
|
||||
maxim,reverse-channel-microvolt = <100000>;
|
||||
|
||||
ports {
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#size-cells = <0>;
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&pinctrl_i2c_mipi_csi0>;
|
||||
clock-frequency = <100000>;
|
||||
clock-frequency = <300000>;
|
||||
status = "okay";
|
||||
|
||||
max9286_csi0@6a {
|
||||
|
@ -23,7 +23,7 @@
|
|||
enable-gpios = <&lsio_gpio3 7 GPIO_ACTIVE_HIGH>;
|
||||
maxim,gpio-poc = <0 GPIO_ACTIVE_LOW>;
|
||||
|
||||
maxim,i2c-remote-bus-hz = <105000>;
|
||||
maxim,i2c-remote-bus-hz = <339000>;
|
||||
maxim,reverse-channel-microvolt = <100000>;
|
||||
status = "okay";
|
||||
|
||||
|
|
|
@ -85,11 +85,6 @@ static int max9271_pclk_detect(struct max9271_device *dev)
|
|||
|
||||
void max9271_wake_up(struct max9271_device *dev)
|
||||
{
|
||||
/*
|
||||
* Use the chip default address as this function has to be called
|
||||
* before any other one.
|
||||
*/
|
||||
dev->client->addr = MAX9271_DEFAULT_ADDR;
|
||||
i2c_smbus_read_byte(dev->client);
|
||||
usleep_range(5000, 8000);
|
||||
}
|
||||
|
@ -153,15 +148,11 @@ int max9271_set_high_threshold(struct max9271_device *dev, bool enable)
|
|||
{
|
||||
int ret;
|
||||
|
||||
ret = max9271_read(dev, 0x08);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Enable or disable reverse channel high threshold to increase
|
||||
* immunity to power supply noise.
|
||||
*/
|
||||
ret = max9271_write(dev, 0x08, enable ? ret | BIT(0) : ret & ~BIT(0));
|
||||
ret = max9271_write(dev, 0x08, enable ? 1 : 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -198,8 +189,7 @@ int max9271_configure_gmsl_link(struct max9271_device *dev)
|
|||
*/
|
||||
ret = max9271_write(dev, 0x02,
|
||||
MAX9271_SPREAD_SPECT_4 | MAX9271_R02_RES |
|
||||
MAX9271_PCLK_AUTODETECT |
|
||||
MAX9271_SERIAL_AUTODETECT);
|
||||
MAX9271_PCLK_AUTODETECT | MAX9271_SERIAL_AUTODETECT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <media/v4l2-device.h>
|
||||
#include <media/v4l2-fwnode.h>
|
||||
#include <media/v4l2-subdev.h>
|
||||
#include "rdacm.h"
|
||||
|
||||
/* Register 0x00 */
|
||||
#define MAX9286_MSTLINKSEL_AUTO (7 << 5)
|
||||
|
@ -321,7 +322,6 @@ static int max9286_write(struct max9286_priv *priv, u8 reg, u8 val)
|
|||
static void max9286_i2c_mux_configure(struct max9286_priv *priv, u8 conf)
|
||||
{
|
||||
max9286_write(priv, 0x0a, conf);
|
||||
|
||||
/*
|
||||
* We must sleep after any change to the forward or reverse channel
|
||||
* configuration.
|
||||
|
@ -404,7 +404,7 @@ error:
|
|||
static void max9286_configure_i2c(struct max9286_priv *priv, bool localack)
|
||||
{
|
||||
u8 config = MAX9286_I2CSLVSH_469NS_234NS | MAX9286_I2CSLVTO_1024US |
|
||||
priv->i2c_mstbt;
|
||||
priv->i2c_mstbt;
|
||||
|
||||
if (localack)
|
||||
config |= MAX9286_I2CLOCACK;
|
||||
|
@ -487,9 +487,8 @@ static int max9286_check_video_links(struct max9286_priv *priv, u64 streams_mask
|
|||
for (i = 0; i < 10; i++) {
|
||||
ret = max9286_read(priv, 0x27);
|
||||
if (ret < 0)
|
||||
return -EIO;
|
||||
|
||||
if (ret & MAX9286_LOCKED)
|
||||
dev_err(&priv->client->dev, "max9286_read [0x27] failed\n");
|
||||
else if (ret & MAX9286_LOCKED)
|
||||
break;
|
||||
|
||||
usleep_range(350, 450);
|
||||
|
@ -511,7 +510,7 @@ static int max9286_check_video_links(struct max9286_priv *priv, u64 streams_mask
|
|||
* Returns 0 for success, -EIO for errors.
|
||||
*/
|
||||
static int max9286_check_config_link(struct max9286_priv *priv,
|
||||
unsigned int source_mask)
|
||||
unsigned int source_mask, unsigned int retries)
|
||||
{
|
||||
unsigned int conflink_mask = (source_mask & 0x0f) << 4;
|
||||
unsigned int i;
|
||||
|
@ -522,7 +521,7 @@ static int max9286_check_config_link(struct max9286_priv *priv,
|
|||
* The delay is not characterized in the chip manual: wait up
|
||||
* to 5 milliseconds.
|
||||
*/
|
||||
for (i = 0; i < 10; i++) {
|
||||
for (i = 0; i < retries; i++) {
|
||||
ret = max9286_read(priv, 0x49);
|
||||
if (ret < 0)
|
||||
return -EIO;
|
||||
|
@ -715,27 +714,68 @@ static int max9286_notify_bound(struct v4l2_async_notifier *notifier,
|
|||
if (priv->bound_sources != priv->source_mask)
|
||||
return 0;
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* Don't increase reverse channel amplitude. This workarounds the
|
||||
* init of device after reboot command.
|
||||
*/
|
||||
/*
|
||||
* All enabled sources have probed and enabled their reverse control
|
||||
* channels:
|
||||
*
|
||||
* - Increase the reverse channel amplitude to compensate for the
|
||||
* remote ends high threshold
|
||||
* - Verify all configuration links are properly detected
|
||||
* - Disable auto-ack as communication on the control channel are now
|
||||
* stable.
|
||||
*/
|
||||
/* Increase the reverse channel amplitude and remote ends high threshold. */
|
||||
source = NULL;
|
||||
for_each_source(priv, source) {
|
||||
ret = v4l2_subdev_call(source->sd, core, command,
|
||||
RDACM_CORE_CMD_INIT_HIGH_AMPL, NULL);
|
||||
if ((ret != 0) && (ret != EALREADY)) {
|
||||
dev_err(&priv->client->dev,
|
||||
"Unable to RDACM_CORE_CMD_INIT_HIGH_AMPL at Low ampl [%s -> %s:%ld]\n",
|
||||
source->sd->name, priv->sd.name, to_index(priv, source));
|
||||
}
|
||||
}
|
||||
max9286_reverse_channel_setup(priv, MAX9286_REV_AMP_HIGH);
|
||||
#endif
|
||||
max9286_check_config_link(priv, priv->source_mask);
|
||||
max9286_configure_i2c(priv, false);
|
||||
|
||||
return max9286_set_pixelrate(priv);
|
||||
/* Configure communication links of the serializers. */
|
||||
source = NULL;
|
||||
for_each_source(priv, source) {
|
||||
ret = v4l2_subdev_call(source->sd, core, command,
|
||||
RDACM_CORE_CMD_INIT_SERIALIZER, NULL);
|
||||
if ((ret != 0) && (ret != EALREADY)) {
|
||||
dev_err(&priv->client->dev,
|
||||
"Unable to RDACM_CORE_CMD_INIT_SERIALIZER at high ampl [%s -> %s:%ld]\n",
|
||||
source->sd->name, priv->sd.name, to_index(priv, source));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
/* Unique I2C addresses have been configured. */
|
||||
max9286_i2c_mux_open(priv);
|
||||
/* Verify all configuration links are properly detected. */
|
||||
max9286_check_config_link(priv, priv->source_mask, 10);
|
||||
/* Disable auto-ack. */
|
||||
max9286_configure_i2c(priv, false);
|
||||
max9286_set_pixelrate(priv);
|
||||
|
||||
/* Load OV10635 firmware. */
|
||||
source = NULL;
|
||||
for_each_source(priv, source) {
|
||||
ret = v4l2_subdev_call(source->sd, core, command,
|
||||
RDACM_CORE_CMD_INIT_CAMERA, NULL);
|
||||
if ((ret != 0) && (ret != EALREADY)) {
|
||||
dev_err(&priv->client->dev,
|
||||
"Unable to RDACM_CORE_CMD_INIT_CAMERA [%s -> %s:%ld]\n",
|
||||
source->sd->name, priv->sd.name, to_index(priv, source));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable auto-ack. */
|
||||
max9286_configure_i2c(priv, true);
|
||||
|
||||
/* Start camera stream. */
|
||||
source = NULL;
|
||||
for_each_source(priv, source) {
|
||||
ret = v4l2_subdev_call(source->sd, core, command,
|
||||
RDACM_CORE_CMD_S_STREAM, NULL);
|
||||
if ((ret != 0) && (ret != EALREADY)) {
|
||||
dev_err(&priv->client->dev,
|
||||
"Unable to RDACM_CORE_CMD_S_STREAM %s -> %s:%ld\n",
|
||||
source->sd->name, priv->sd.name, to_index(priv, source));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void max9286_notify_unbind(struct v4l2_async_notifier *notifier,
|
||||
|
@ -869,7 +909,7 @@ static int max9286_enable_cams(struct max9286_priv *priv, struct v4l2_subdev_sta
|
|||
(enable) ? "enable" : "disable",
|
||||
sink_streams, remote_sd->name, remote_pad, ret);
|
||||
break;
|
||||
case 0: // Success
|
||||
case 0 /* Success */ :
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "failed to %s streams 0x%llx on '%s':%u: %d\n",
|
||||
|
@ -925,6 +965,8 @@ static int max9286_enable_streams(struct v4l2_subdev *sd, struct v4l2_subdev_sta
|
|||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
/* Wait for more than 2 frame time */
|
||||
msleep(200);
|
||||
ret = max9286_check_video_links(priv, priv->route_mask);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
@ -968,6 +1010,7 @@ static int max9286_enable_streams(struct v4l2_subdev *sd, struct v4l2_subdev_sta
|
|||
max9286_write(priv, 0x15, MAX9286_CSI_IMAGE_TYP | MAX9286_VCTYPE |
|
||||
MAX9286_CSIOUTEN | MAX9286_EN_CCBSYB_CLK_STR |
|
||||
MAX9286_EN_GPI_CCBSYB);
|
||||
msleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1402,7 +1445,7 @@ static int max9286_setup(struct max9286_priv *priv)
|
|||
max9286_write(priv, 0x69, (0xf & ~priv->route_mask));
|
||||
|
||||
max9286_set_video_format(priv, &max9286_default_format);
|
||||
|
||||
/* FRAMESYNC is taken from the slowest link. */
|
||||
cfg = max9286_read(priv, 0x1c);
|
||||
if (cfg < 0)
|
||||
return cfg;
|
||||
|
|
36
drivers/media/i2c/rdacm.h
Normal file
36
drivers/media/i2c/rdacm.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* IMI RDACM GMSL Camera Driver
|
||||
*
|
||||
* Copyright 2025 NXP
|
||||
*/
|
||||
#ifndef RDACM_H
|
||||
#define RDACM_H
|
||||
|
||||
|
||||
/* The RDACM state transitions
|
||||
*
|
||||
* 1. Linux boot (two possible HW states):
|
||||
* - low ampl (HW reset)
|
||||
* - high ampl (only OS reset)
|
||||
*
|
||||
* 2. RDACM_CORE_CMD_INIT_HIGH_AMPL:
|
||||
* - MAX9271 low ampl -> high ampl communication
|
||||
*
|
||||
* 3. RDACM_CORE_CMD_INIT_SERIALIZER:
|
||||
* - MAX9271 GSML link and address translation
|
||||
*
|
||||
* 4. RDACM_CORE_CMD_INIT_CAMERA:
|
||||
* - OV10635 configured
|
||||
*
|
||||
* 5. RDACM_CORE_CMD_S_STREAM:
|
||||
* - OV10635 streaming
|
||||
*/
|
||||
|
||||
/* These ioctls are internal to the kernel */
|
||||
#define RDACM_CORE_CMD_INIT_HIGH_AMPL _IO('R', 1)
|
||||
#define RDACM_CORE_CMD_INIT_SERIALIZER _IO('R', 2)
|
||||
#define RDACM_CORE_CMD_INIT_CAMERA _IO('R', 3)
|
||||
#define RDACM_CORE_CMD_S_STREAM _IO('R', 4)
|
||||
|
||||
#endif /* RDACM_H */
|
|
@ -27,7 +27,7 @@
|
|||
#include <media/v4l2-async.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/v4l2-subdev.h>
|
||||
|
||||
#include "rdacm.h"
|
||||
#include "max9271.h"
|
||||
|
||||
#define OV10635_I2C_ADDRESS 0x30
|
||||
|
@ -60,10 +60,13 @@
|
|||
*/
|
||||
#define OV10635_PIXEL_RATE (44000000)
|
||||
|
||||
static const struct ov10635_reg {
|
||||
struct ov10635_reg {
|
||||
u16 reg;
|
||||
u8 val;
|
||||
} ov10635_regs_wizard[] = {
|
||||
};
|
||||
|
||||
static const struct ov10635_reg ov10635_regs_wizard[] = {
|
||||
{ 0x0100, 0x01 },
|
||||
{ 0x301b, 0xff }, { 0x301c, 0xff }, { 0x301a, 0xff }, { 0x3011, 0x42 },
|
||||
{ 0x6900, 0x0c }, { 0x6901, 0x19 }, { 0x3503, 0x10 }, { 0x3025, 0x03 },
|
||||
{ 0x3003, 0x16 }, { 0x3004, 0x30 }, { 0x3005, 0x40 }, { 0x3006, 0x91 },
|
||||
|
@ -308,18 +311,19 @@ static const struct ov10635_reg {
|
|||
{ 0x3832, (0x0d + 2 * 0x20 + 0x15 + 38) >> 8 },
|
||||
{ 0x3833, (0x0d + 2 * 0x20 + 0x15 + 38) & 0xff },
|
||||
{ 0x3834, OV10635_VTS >> 8 }, { 0x3835, OV10635_VTS & 0xff },
|
||||
{ 0x302e, 0x01 },
|
||||
{ 0x302e, 0x01 }, { 0x302e, 0x01 }
|
||||
};
|
||||
|
||||
struct rdacm20_device {
|
||||
struct device *dev;
|
||||
struct device *dev;
|
||||
struct max9271_device serializer;
|
||||
struct i2c_client *sensor;
|
||||
struct v4l2_subdev sd;
|
||||
struct media_pad pad;
|
||||
struct i2c_client *sensor;
|
||||
struct v4l2_subdev sd;
|
||||
struct media_pad pad;
|
||||
struct v4l2_ctrl_handler ctrls;
|
||||
u32 addrs[2];
|
||||
u32 addrs[2];
|
||||
bool no_poc;
|
||||
bool initialised;
|
||||
};
|
||||
|
||||
static inline struct rdacm20_device *sd_to_rdacm20(struct v4l2_subdev *sd)
|
||||
|
@ -360,30 +364,19 @@ static int __ov10635_write(struct rdacm20_device *dev, u16 reg, u8 val)
|
|||
int ret;
|
||||
|
||||
dev_dbg(dev->dev, "%s(0x%04x, 0x%02x)\n", __func__, reg, val);
|
||||
|
||||
ret = i2c_master_send(dev->sensor, buf, 3);
|
||||
return ret < 0 ? ret : 0;
|
||||
for (unsigned retry = 10; retry > 0; --retry) {
|
||||
ret = i2c_master_send(dev->sensor, buf, 3);
|
||||
if (ret >= 0)
|
||||
return 0;
|
||||
else
|
||||
dev_info(dev->dev, "retry %s(0x%04x, 0x%02x)\n", __func__, reg, val);
|
||||
}
|
||||
return (ret < 0) ? ret : 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Unused when I2C address is set via max9271.*/
|
||||
|
||||
static int ov10635_write(struct rdacm20_device *dev, u16 reg, u8 val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = __ov10635_write(dev, reg, val);
|
||||
if (ret < 0)
|
||||
dev_err(dev->dev, "%s: register 0x%04x write failed (%d)\n",
|
||||
__func__, reg, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int ov10635_set_regs(struct rdacm20_device *dev,
|
||||
const struct ov10635_reg *regs,
|
||||
unsigned int nr_regs)
|
||||
const struct ov10635_reg *regs,
|
||||
unsigned int nr_regs)
|
||||
{
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
@ -391,9 +384,8 @@ static int ov10635_set_regs(struct rdacm20_device *dev,
|
|||
for (i = 0; i < nr_regs; i++) {
|
||||
ret = __ov10635_write(dev, regs[i].reg, regs[i].val);
|
||||
if (ret) {
|
||||
dev_err(dev->dev,
|
||||
"%s: register %u (0x%04x) write failed (%d)\n",
|
||||
__func__, i, regs[i].reg, ret);
|
||||
dev_err(dev->dev, "%s: register %u (0x%04x) write failed (%d)\n",
|
||||
__func__, i, regs[i].reg, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@ -403,9 +395,7 @@ static int ov10635_set_regs(struct rdacm20_device *dev,
|
|||
|
||||
static int rdacm20_s_stream(struct v4l2_subdev *sd, int enable)
|
||||
{
|
||||
struct rdacm20_device *dev = sd_to_rdacm20(sd);
|
||||
|
||||
return max9271_set_serial_link(&dev->serializer, enable);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rdacm20_enum_mbus_code(struct v4l2_subdev *sd,
|
||||
|
@ -479,34 +469,23 @@ static int rdacm20_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct v4l2_subdev_video_ops rdacm20_video_ops = {
|
||||
.s_stream = rdacm20_s_stream,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_pad_ops rdacm20_subdev_pad_ops = {
|
||||
.enum_mbus_code = rdacm20_enum_mbus_code,
|
||||
.get_fmt = rdacm20_get_fmt,
|
||||
.set_fmt = rdacm20_get_fmt,
|
||||
.enum_frame_size = rdacm20_enum_frame_size,
|
||||
.get_frame_desc = rdacm20_get_frame_desc,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_ops rdacm20_subdev_ops = {
|
||||
.video = &rdacm20_video_ops,
|
||||
.pad = &rdacm20_subdev_pad_ops,
|
||||
};
|
||||
|
||||
static int rdacm20_initialize(struct rdacm20_device *dev)
|
||||
static int rdacm20_init_serial_link(struct rdacm20_device *dev)
|
||||
{
|
||||
unsigned int retry = 3;
|
||||
int ret;
|
||||
|
||||
int ret, ret2;
|
||||
max9271_wake_up(&dev->serializer);
|
||||
|
||||
/* Serial link disabled during config as it needs a valid pixel clock. */
|
||||
ret = max9271_set_serial_link(&dev->serializer, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
/* We don't know why the command failed. MAX9271 could have received it though. */
|
||||
ret2 = max9271_set_high_threshold(&dev->serializer, true);
|
||||
|
||||
msleep(2);
|
||||
return ret ? ret : ret2;
|
||||
}
|
||||
|
||||
static int rdacm20_initialize(struct rdacm20_device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Ensure that we have a good link configuration before attempting to
|
||||
|
@ -515,7 +494,7 @@ static int rdacm20_initialize(struct rdacm20_device *dev)
|
|||
ret = max9271_configure_i2c(&dev->serializer,
|
||||
MAX9271_I2CSLVSH_469NS_234NS |
|
||||
MAX9271_I2CSLVTO_1024US |
|
||||
MAX9271_I2CMSTBT_105KBPS);
|
||||
MAX9271_I2CMSTBT_339KBPS);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -529,25 +508,27 @@ static int rdacm20_initialize(struct rdacm20_device *dev)
|
|||
*/
|
||||
if (dev->no_poc) {
|
||||
/*
|
||||
* Since we failed identifying the serializer on default address, try
|
||||
* looking for it on the programmed address.
|
||||
*/
|
||||
* Since we failed identifying the serializer on default address, try
|
||||
* looking for it on the programmed address.
|
||||
*/
|
||||
dev->serializer.client->addr = dev->addrs[0];
|
||||
ret = max9271_verify_id(&dev->serializer);
|
||||
if (!ret) {
|
||||
dev_info(dev->dev, "Identified already initialized RDACM20 camera module");
|
||||
ret = max9271_set_serial_link(&dev->serializer, false);
|
||||
if (ret) {
|
||||
dev_info(dev->dev, "max9271_set_serial_link failed.");
|
||||
return ret;
|
||||
dev_warn(dev->dev, "max9271_set_serial_link failed.");
|
||||
/* This might not be fatal. Program walks here for
|
||||
* already configured device.
|
||||
*/
|
||||
usleep_range(5000, 8000);
|
||||
}
|
||||
ret = max9271_configure_i2c(&dev->serializer,
|
||||
MAX9271_I2CSLVSH_469NS_234NS |
|
||||
MAX9271_I2CSLVTO_1024US |
|
||||
MAX9271_I2CMSTBT_105KBPS);
|
||||
|
||||
MAX9271_I2CSLVSH_469NS_234NS |
|
||||
MAX9271_I2CSLVTO_1024US |
|
||||
MAX9271_I2CMSTBT_339KBPS);
|
||||
if (ret) {
|
||||
dev_info(dev->dev, "Failed max9271_configure_i2c.");
|
||||
dev_warn(dev->dev, "Failed max9271_configure_i2c.");
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
|
@ -556,99 +537,158 @@ static int rdacm20_initialize(struct rdacm20_device *dev)
|
|||
} else {
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = max9271_set_address(&dev->serializer, dev->addrs[0]);
|
||||
if (ret < 0) {
|
||||
dev_err(dev->dev, "MAX9271 I2C address change failed (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
dev->serializer.client->addr = dev->addrs[0];
|
||||
}
|
||||
ret = max9271_set_address(&dev->serializer, dev->addrs[0]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
dev->serializer.client->addr = dev->addrs[0];
|
||||
|
||||
/*
|
||||
* Hold OV10635 in reset during max9271 configuration. The reset signal
|
||||
* has to be asserted for at least 200 microseconds.
|
||||
*/
|
||||
ret = max9271_enable_gpios(&dev->serializer, MAX9271_GPIO1OUT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = max9271_clear_gpios(&dev->serializer, MAX9271_GPIO1OUT);
|
||||
if (ret)
|
||||
return ret;
|
||||
usleep_range(200, 500);
|
||||
|
||||
ret = max9271_configure_gmsl_link(&dev->serializer);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Release ov10635 from reset and initialize it. The image sensor
|
||||
* requires at least 2048 XVCLK cycles (85 micro-seconds at 24MHz)
|
||||
* before being available. Stay safe and wait up to 500 micro-seconds.
|
||||
*/
|
||||
ret = max9271_set_gpios(&dev->serializer, MAX9271_GPIO1OUT);
|
||||
if (ret)
|
||||
return ret;
|
||||
usleep_range(100, 500);
|
||||
|
||||
/* Add unique alias I2C address for the sensor. */
|
||||
ret = max9271_set_translation(&dev->serializer, dev->addrs[1], OV10635_I2C_ADDRESS);
|
||||
if (ret < 0) {
|
||||
dev_err(dev->dev,
|
||||
"OV10635 I2C address change failed (%d)\n", ret);
|
||||
dev_err(dev->dev, "OV10635 I2C address change failed (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
dev->sensor->addr = dev->addrs[1];
|
||||
usleep_range(3500, 5000);
|
||||
if (!dev->no_poc) {
|
||||
/*
|
||||
* Hold OV10635 in reset during max9271 configuration. The reset signal
|
||||
* has to be asserted for at least 200 microseconds.
|
||||
*/
|
||||
ret = max9271_enable_gpios(&dev->serializer, MAX9271_GPIO1OUT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
again:
|
||||
ret = ov10635_read16(dev, OV10635_PID);
|
||||
if (ret < 0) {
|
||||
if (retry--)
|
||||
goto again;
|
||||
|
||||
dev_err(dev->dev, "OV10635 ID read failed (%d)\n",
|
||||
ret);
|
||||
return -ENXIO;
|
||||
ret = max9271_clear_gpios(&dev->serializer, MAX9271_GPIO1OUT);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ret != OV10635_VERSION) {
|
||||
if (retry--)
|
||||
goto again;
|
||||
|
||||
dev_err(dev->dev, "OV10635 ID mismatch (0x%04x)\n",
|
||||
ret);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/* Program the 0V10635 initial configuration. */
|
||||
ret = ov10635_set_regs(dev, ov10635_regs_wizard,
|
||||
ARRAY_SIZE(ov10635_regs_wizard));
|
||||
if (ret)
|
||||
ret = __ov10635_write(dev, OV10635_SOFTWARE_RESET, 0x1);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "Failed OV10635_SOFTWARE_RESET\n");
|
||||
return ret;
|
||||
}
|
||||
msleep(1);
|
||||
|
||||
ret = max9271_configure_gmsl_link(&dev->serializer);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "Failed max9271_configure_gmsl_link\n");
|
||||
return ret;
|
||||
}
|
||||
msleep(100);
|
||||
ret = max9271_configure_i2c(&dev->serializer,
|
||||
MAX9271_I2CSLVSH_469NS_234NS |
|
||||
MAX9271_I2CSLVTO_1024US |
|
||||
MAX9271_I2CMSTBT_339KBPS);
|
||||
|
||||
if (!dev->no_poc) {
|
||||
/*
|
||||
* Release ov10635 from reset and initialize it. The image sensor
|
||||
* requires at least 2048 XVCLK cycles (85 micro-seconds at 24MHz)
|
||||
* before being available. Stay safe and wait up to 500 micro-seconds.
|
||||
*/
|
||||
ret = max9271_set_gpios(&dev->serializer, MAX9271_GPIO1OUT);
|
||||
if (ret)
|
||||
return ret;
|
||||
usleep_range(100, 500);
|
||||
}
|
||||
dev_info(dev->dev, "Identified RDACM20 camera module\n");
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* Don't increase reverse channel amplitude. This workarounds the
|
||||
* init of device after reboot command.
|
||||
*/
|
||||
/*
|
||||
* Set reverse channel high threshold to increase noise immunity.
|
||||
*
|
||||
* This should be compensated by increasing the reverse channel
|
||||
* amplitude on the remote deserializer side.
|
||||
*
|
||||
* TODO Inspect the embedded MCU programming sequence to make sure
|
||||
* there are no conflicts with the configuration applied here.
|
||||
*
|
||||
* TODO Clarify the embedded MCU startup delay to avoid write
|
||||
* collisions on the I2C bus.
|
||||
*/
|
||||
return max9271_set_high_threshold(&dev->serializer, true);
|
||||
#endif
|
||||
msleep(5);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long rdacm20_command(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
|
||||
{
|
||||
struct rdacm20_device *dev = sd_to_rdacm20(sd);
|
||||
unsigned ret = -EINVAL;
|
||||
|
||||
switch (cmd) {
|
||||
case RDACM_CORE_CMD_INIT_HIGH_AMPL:
|
||||
if (dev->initialised)
|
||||
return EALREADY;
|
||||
dev->serializer.client->addr = MAX9271_DEFAULT_ADDR;
|
||||
ret = rdacm20_init_serial_link(dev);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "Failed to rdacm20_init_serial_link (%d). Retrying.\n", ret);
|
||||
dev->serializer.client->addr = dev->addrs[0];
|
||||
ret = rdacm20_init_serial_link(dev);
|
||||
}
|
||||
if (ret == 0)
|
||||
dev->initialised = true;
|
||||
break;
|
||||
|
||||
case RDACM_CORE_CMD_INIT_SERIALIZER:
|
||||
dev->serializer.client->addr = MAX9271_DEFAULT_ADDR;
|
||||
ret = rdacm20_initialize(dev);
|
||||
break;
|
||||
|
||||
case RDACM_CORE_CMD_INIT_CAMERA:
|
||||
for (unsigned retry = 3; retry > 0; --retry) {
|
||||
ret = ov10635_read16(dev, OV10635_PID);
|
||||
if (ret < 0) {
|
||||
dev_warn(dev->dev, "OV10635 ID read failed (%d)\n", ret);
|
||||
ret = -ENXIO;
|
||||
continue;
|
||||
}
|
||||
if (ret != OV10635_VERSION) {
|
||||
dev_warn(dev->dev, "OV10635 ID mismatch (0x%04x)\n", ret);
|
||||
ret = -ENXIO;
|
||||
continue;
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (ret !=0) {
|
||||
dev_err(dev->dev, "Failed id cam(%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = ov10635_set_regs(dev, ov10635_regs_wizard,
|
||||
ARRAY_SIZE(ov10635_regs_wizard));
|
||||
if (ret !=0)
|
||||
dev_err(dev->dev, "ov10635_regs_wizard failed (%d)\n", ret);
|
||||
break;
|
||||
|
||||
case RDACM_CORE_CMD_S_STREAM:
|
||||
/* MAX9271: Enable Serial Links and Disable Configuration Link */
|
||||
ret = max9271_set_serial_link(&dev->serializer, true);
|
||||
/* Wait for more than 2 frame time */
|
||||
msleep(10);
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_warn(dev->dev, "CMD_default\n");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct v4l2_subdev_core_ops rdacm20_core_ops = {
|
||||
.command = &rdacm20_command,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_video_ops rdacm20_video_ops = {
|
||||
.s_stream = rdacm20_s_stream,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_pad_ops rdacm20_subdev_pad_ops = {
|
||||
.enum_mbus_code = rdacm20_enum_mbus_code,
|
||||
.get_fmt = rdacm20_get_fmt,
|
||||
.set_fmt = rdacm20_get_fmt,
|
||||
.enum_frame_size = rdacm20_enum_frame_size,
|
||||
.get_frame_desc = rdacm20_get_frame_desc,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_ops rdacm20_subdev_ops = {
|
||||
.core = &rdacm20_core_ops,
|
||||
.video = &rdacm20_video_ops,
|
||||
.pad = &rdacm20_subdev_pad_ops,
|
||||
};
|
||||
|
||||
|
||||
static int rdacm20_probe(struct i2c_client *client)
|
||||
{
|
||||
struct rdacm20_device *dev;
|
||||
|
@ -676,11 +716,7 @@ static int rdacm20_probe(struct i2c_client *client)
|
|||
}
|
||||
|
||||
dev->no_poc = of_property_read_bool(client->dev.of_node, "imi,no-poc");
|
||||
|
||||
/* Initialize the hardware. */
|
||||
ret = rdacm20_initialize(dev);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
dev->initialised = false;
|
||||
|
||||
/* Initialize and register the subdevice. */
|
||||
v4l2_i2c_subdev_init(&dev->sd, client, &rdacm20_subdev_ops);
|
||||
|
|
Loading…
Reference in New Issue
Block a user