linux/drivers/media/i2c/ds90ub913.c
Tomi Valkeinen ea90034e8f media: i2c: ds90ub913: Fix use of uninitialized variables
smatch reports some uninitialized variables:

drivers/media/i2c/ds90ub913.c:481 ub913_log_status() error: uninitialized symbol 'v1'.
drivers/media/i2c/ds90ub913.c:481 ub913_log_status() error: uninitialized symbol 'v2'.

These are used only for printing debug information, and the use of an
uninitialized variable only happens if an i2c transaction has failed,
which will print an error. Thus, fix the errors just by initializing the
variables to 0.

Closes: https://lore.kernel.org/all/8d6daeb1-b62a-bbb2-b840-8759c84f2085@xs4all.nl/

Fixes: c158d0d4ff ("media: i2c: add DS90UB913 driver")
Reported-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
2023-08-14 20:27:58 +02:00

904 lines
22 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Driver for the Texas Instruments DS90UB913 video serializer
*
* Based on a driver from Luca Ceresoli <luca@lucaceresoli.net>
*
* Copyright (c) 2019 Luca Ceresoli <luca@lucaceresoli.net>
* Copyright (c) 2023 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
*/
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/fwnode.h>
#include <linux/gpio/driver.h>
#include <linux/i2c-atr.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <media/i2c/ds90ub9xx.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-mediabus.h>
#include <media/v4l2-subdev.h>
#define UB913_PAD_SINK 0
#define UB913_PAD_SOURCE 1
/*
* UB913 has 4 gpios, but gpios 3 and 4 are reserved for external oscillator
* mode. Thus we only support 2 gpios for now.
*/
#define UB913_NUM_GPIOS 2
#define UB913_REG_RESET_CTL 0x01
#define UB913_REG_RESET_CTL_DIGITAL_RESET_1 BIT(1)
#define UB913_REG_RESET_CTL_DIGITAL_RESET_0 BIT(0)
#define UB913_REG_GENERAL_CFG 0x03
#define UB913_REG_GENERAL_CFG_CRC_ERR_RESET BIT(5)
#define UB913_REG_GENERAL_CFG_PCLK_RISING BIT(0)
#define UB913_REG_MODE_SEL 0x05
#define UB913_REG_MODE_SEL_MODE_OVERRIDE BIT(5)
#define UB913_REG_MODE_SEL_MODE_UP_TO_DATE BIT(4)
#define UB913_REG_MODE_SEL_MODE_MASK GENMASK(3, 0)
#define UB913_REG_CRC_ERRORS_LSB 0x0a
#define UB913_REG_CRC_ERRORS_MSB 0x0b
#define UB913_REG_GENERAL_STATUS 0x0c
#define UB913_REG_GPIO_CFG(n) (0x0d + (n))
#define UB913_REG_GPIO_CFG_ENABLE(n) BIT(0 + (n) * 4)
#define UB913_REG_GPIO_CFG_DIR_INPUT(n) BIT(1 + (n) * 4)
#define UB913_REG_GPIO_CFG_REMOTE_EN(n) BIT(2 + (n) * 4)
#define UB913_REG_GPIO_CFG_OUT_VAL(n) BIT(3 + (n) * 4)
#define UB913_REG_GPIO_CFG_MASK(n) (0xf << ((n) * 4))
#define UB913_REG_SCL_HIGH_TIME 0x11
#define UB913_REG_SCL_LOW_TIME 0x12
#define UB913_REG_PLL_OVR 0x35
struct ub913_data {
struct i2c_client *client;
struct regmap *regmap;
struct clk *clkin;
struct gpio_chip gpio_chip;
struct v4l2_subdev sd;
struct media_pad pads[2];
struct v4l2_async_notifier notifier;
struct v4l2_subdev *source_sd;
u16 source_sd_pad;
u64 enabled_source_streams;
struct clk_hw *clkout_clk_hw;
struct ds90ub9xx_platform_data *plat_data;
bool pclk_polarity_rising;
};
static inline struct ub913_data *sd_to_ub913(struct v4l2_subdev *sd)
{
return container_of(sd, struct ub913_data, sd);
}
struct ub913_format_info {
u32 incode;
u32 outcode;
};
static const struct ub913_format_info ub913_formats[] = {
/* Only RAW10 with 8-bit payload is supported at the moment */
{ .incode = MEDIA_BUS_FMT_YUYV8_2X8, .outcode = MEDIA_BUS_FMT_YUYV8_1X16 },
{ .incode = MEDIA_BUS_FMT_UYVY8_2X8, .outcode = MEDIA_BUS_FMT_UYVY8_1X16 },
{ .incode = MEDIA_BUS_FMT_VYUY8_2X8, .outcode = MEDIA_BUS_FMT_VYUY8_1X16 },
{ .incode = MEDIA_BUS_FMT_YVYU8_2X8, .outcode = MEDIA_BUS_FMT_YVYU8_1X16 },
};
static const struct ub913_format_info *ub913_find_format(u32 incode)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(ub913_formats); i++) {
if (ub913_formats[i].incode == incode)
return &ub913_formats[i];
}
return NULL;
}
static int ub913_read(const struct ub913_data *priv, u8 reg, u8 *val)
{
unsigned int v;
int ret;
ret = regmap_read(priv->regmap, reg, &v);
if (ret < 0) {
dev_err(&priv->client->dev,
"Cannot read register 0x%02x: %d!\n", reg, ret);
return ret;
}
*val = v;
return 0;
}
static int ub913_write(const struct ub913_data *priv, u8 reg, u8 val)
{
int ret;
ret = regmap_write(priv->regmap, reg, val);
if (ret < 0)
dev_err(&priv->client->dev,
"Cannot write register 0x%02x: %d!\n", reg, ret);
return ret;
}
/*
* GPIO chip
*/
static int ub913_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
{
return GPIO_LINE_DIRECTION_OUT;
}
static int ub913_gpio_direction_out(struct gpio_chip *gc, unsigned int offset,
int value)
{
struct ub913_data *priv = gpiochip_get_data(gc);
unsigned int reg_idx = offset / 2;
unsigned int field_idx = offset % 2;
return regmap_update_bits(priv->regmap, UB913_REG_GPIO_CFG(reg_idx),
UB913_REG_GPIO_CFG_MASK(field_idx),
UB913_REG_GPIO_CFG_ENABLE(field_idx) |
(value ? UB913_REG_GPIO_CFG_OUT_VAL(field_idx) :
0));
}
static void ub913_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
{
ub913_gpio_direction_out(gc, offset, value);
}
static int ub913_gpio_of_xlate(struct gpio_chip *gc,
const struct of_phandle_args *gpiospec,
u32 *flags)
{
if (flags)
*flags = gpiospec->args[1];
return gpiospec->args[0];
}
static int ub913_gpiochip_probe(struct ub913_data *priv)
{
struct device *dev = &priv->client->dev;
struct gpio_chip *gc = &priv->gpio_chip;
int ret;
/* Initialize GPIOs 0 and 1 to local control, tri-state */
ub913_write(priv, UB913_REG_GPIO_CFG(0), 0);
gc->label = dev_name(dev);
gc->parent = dev;
gc->owner = THIS_MODULE;
gc->base = -1;
gc->can_sleep = true;
gc->ngpio = UB913_NUM_GPIOS;
gc->get_direction = ub913_gpio_get_direction;
gc->direction_output = ub913_gpio_direction_out;
gc->set = ub913_gpio_set;
gc->of_xlate = ub913_gpio_of_xlate;
gc->of_gpio_n_cells = 2;
ret = gpiochip_add_data(gc, priv);
if (ret) {
dev_err(dev, "Failed to add GPIOs: %d\n", ret);
return ret;
}
return 0;
}
static void ub913_gpiochip_remove(struct ub913_data *priv)
{
gpiochip_remove(&priv->gpio_chip);
}
static const struct regmap_config ub913_regmap_config = {
.name = "ds90ub913",
.reg_bits = 8,
.val_bits = 8,
.reg_format_endian = REGMAP_ENDIAN_DEFAULT,
.val_format_endian = REGMAP_ENDIAN_DEFAULT,
};
/*
* V4L2
*/
static int ub913_enable_streams(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state, u32 pad,
u64 streams_mask)
{
struct ub913_data *priv = sd_to_ub913(sd);
u64 sink_streams;
int ret;
sink_streams = v4l2_subdev_state_xlate_streams(state, UB913_PAD_SOURCE,
UB913_PAD_SINK,
&streams_mask);
ret = v4l2_subdev_enable_streams(priv->source_sd, priv->source_sd_pad,
sink_streams);
if (ret)
return ret;
priv->enabled_source_streams |= streams_mask;
return 0;
}
static int ub913_disable_streams(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state, u32 pad,
u64 streams_mask)
{
struct ub913_data *priv = sd_to_ub913(sd);
u64 sink_streams;
int ret;
sink_streams = v4l2_subdev_state_xlate_streams(state, UB913_PAD_SOURCE,
UB913_PAD_SINK,
&streams_mask);
ret = v4l2_subdev_disable_streams(priv->source_sd, priv->source_sd_pad,
sink_streams);
if (ret)
return ret;
priv->enabled_source_streams &= ~streams_mask;
return 0;
}
static int _ub913_set_routing(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_krouting *routing)
{
static const struct v4l2_mbus_framefmt in_format = {
.width = 640,
.height = 480,
.code = MEDIA_BUS_FMT_UYVY8_2X8,
.field = V4L2_FIELD_NONE,
.colorspace = V4L2_COLORSPACE_SRGB,
.ycbcr_enc = V4L2_YCBCR_ENC_601,
.quantization = V4L2_QUANTIZATION_LIM_RANGE,
.xfer_func = V4L2_XFER_FUNC_SRGB,
};
static const struct v4l2_mbus_framefmt out_format = {
.width = 640,
.height = 480,
.code = MEDIA_BUS_FMT_UYVY8_1X16,
.field = V4L2_FIELD_NONE,
.colorspace = V4L2_COLORSPACE_SRGB,
.ycbcr_enc = V4L2_YCBCR_ENC_601,
.quantization = V4L2_QUANTIZATION_LIM_RANGE,
.xfer_func = V4L2_XFER_FUNC_SRGB,
};
struct v4l2_subdev_stream_configs *stream_configs;
unsigned int i;
int ret;
/*
* Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until
* frame desc is made dynamically allocated.
*/
if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX)
return -EINVAL;
ret = v4l2_subdev_routing_validate(sd, routing,
V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
if (ret)
return ret;
ret = v4l2_subdev_set_routing(sd, state, routing);
if (ret)
return ret;
stream_configs = &state->stream_configs;
for (i = 0; i < stream_configs->num_configs; i++) {
if (stream_configs->configs[i].pad == UB913_PAD_SINK)
stream_configs->configs[i].fmt = in_format;
else
stream_configs->configs[i].fmt = out_format;
}
return 0;
}
static int ub913_set_routing(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
enum v4l2_subdev_format_whence which,
struct v4l2_subdev_krouting *routing)
{
struct ub913_data *priv = sd_to_ub913(sd);
if (which == V4L2_SUBDEV_FORMAT_ACTIVE && priv->enabled_source_streams)
return -EBUSY;
return _ub913_set_routing(sd, state, routing);
}
static int ub913_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad,
struct v4l2_mbus_frame_desc *fd)
{
struct ub913_data *priv = sd_to_ub913(sd);
const struct v4l2_subdev_krouting *routing;
struct v4l2_mbus_frame_desc source_fd;
struct v4l2_subdev_route *route;
struct v4l2_subdev_state *state;
int ret;
if (pad != UB913_PAD_SOURCE)
return -EINVAL;
ret = v4l2_subdev_call(priv->source_sd, pad, get_frame_desc,
priv->source_sd_pad, &source_fd);
if (ret)
return ret;
memset(fd, 0, sizeof(*fd));
fd->type = V4L2_MBUS_FRAME_DESC_TYPE_PARALLEL;
state = v4l2_subdev_lock_and_get_active_state(sd);
routing = &state->routing;
for_each_active_route(routing, route) {
unsigned int i;
if (route->source_pad != pad)
continue;
for (i = 0; i < source_fd.num_entries; i++) {
if (source_fd.entry[i].stream == route->sink_stream)
break;
}
if (i == source_fd.num_entries) {
dev_err(&priv->client->dev,
"Failed to find stream from source frame desc\n");
ret = -EPIPE;
goto out_unlock;
}
fd->entry[fd->num_entries].stream = route->source_stream;
fd->entry[fd->num_entries].flags = source_fd.entry[i].flags;
fd->entry[fd->num_entries].length = source_fd.entry[i].length;
fd->entry[fd->num_entries].pixelcode =
source_fd.entry[i].pixelcode;
fd->num_entries++;
}
out_unlock:
v4l2_subdev_unlock_state(state);
return ret;
}
static int ub913_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_format *format)
{
struct ub913_data *priv = sd_to_ub913(sd);
struct v4l2_mbus_framefmt *fmt;
const struct ub913_format_info *finfo;
if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE &&
priv->enabled_source_streams)
return -EBUSY;
/* Source format is fully defined by the sink format, so not settable */
if (format->pad == UB913_PAD_SOURCE)
return v4l2_subdev_get_fmt(sd, state, format);
finfo = ub913_find_format(format->format.code);
if (!finfo) {
finfo = &ub913_formats[0];
format->format.code = finfo->incode;
}
/* Set sink format */
fmt = v4l2_subdev_state_get_stream_format(state, format->pad,
format->stream);
if (!fmt)
return -EINVAL;
*fmt = format->format;
/* Propagate to source format, and adjust the mbus code */
fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad,
format->stream);
if (!fmt)
return -EINVAL;
format->format.code = finfo->outcode;
*fmt = format->format;
return 0;
}
static int ub913_init_cfg(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state)
{
struct v4l2_subdev_route routes[] = {
{
.sink_pad = UB913_PAD_SINK,
.sink_stream = 0,
.source_pad = UB913_PAD_SOURCE,
.source_stream = 0,
.flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
},
};
struct v4l2_subdev_krouting routing = {
.num_routes = ARRAY_SIZE(routes),
.routes = routes,
};
return _ub913_set_routing(sd, state, &routing);
}
static int ub913_log_status(struct v4l2_subdev *sd)
{
struct ub913_data *priv = sd_to_ub913(sd);
struct device *dev = &priv->client->dev;
u8 v = 0, v1 = 0, v2 = 0;
ub913_read(priv, UB913_REG_MODE_SEL, &v);
dev_info(dev, "MODE_SEL %#02x\n", v);
ub913_read(priv, UB913_REG_CRC_ERRORS_LSB, &v1);
ub913_read(priv, UB913_REG_CRC_ERRORS_MSB, &v2);
dev_info(dev, "CRC errors %u\n", v1 | (v2 << 8));
/* clear CRC errors */
ub913_read(priv, UB913_REG_GENERAL_CFG, &v);
ub913_write(priv, UB913_REG_GENERAL_CFG,
v | UB913_REG_GENERAL_CFG_CRC_ERR_RESET);
ub913_write(priv, UB913_REG_GENERAL_CFG, v);
ub913_read(priv, UB913_REG_GENERAL_STATUS, &v);
dev_info(dev, "GENERAL_STATUS %#02x\n", v);
ub913_read(priv, UB913_REG_PLL_OVR, &v);
dev_info(dev, "PLL_OVR %#02x\n", v);
return 0;
}
static const struct v4l2_subdev_core_ops ub913_subdev_core_ops = {
.log_status = ub913_log_status,
};
static const struct v4l2_subdev_pad_ops ub913_pad_ops = {
.enable_streams = ub913_enable_streams,
.disable_streams = ub913_disable_streams,
.set_routing = ub913_set_routing,
.get_frame_desc = ub913_get_frame_desc,
.get_fmt = v4l2_subdev_get_fmt,
.set_fmt = ub913_set_fmt,
.init_cfg = ub913_init_cfg,
};
static const struct v4l2_subdev_ops ub913_subdev_ops = {
.core = &ub913_subdev_core_ops,
.pad = &ub913_pad_ops,
};
static const struct media_entity_operations ub913_entity_ops = {
.link_validate = v4l2_subdev_link_validate,
};
static int ub913_notify_bound(struct v4l2_async_notifier *notifier,
struct v4l2_subdev *source_subdev,
struct v4l2_async_connection *asd)
{
struct ub913_data *priv = sd_to_ub913(notifier->sd);
struct device *dev = &priv->client->dev;
int ret;
ret = media_entity_get_fwnode_pad(&source_subdev->entity,
source_subdev->fwnode,
MEDIA_PAD_FL_SOURCE);
if (ret < 0) {
dev_err(dev, "Failed to find pad for %s\n",
source_subdev->name);
return ret;
}
priv->source_sd = source_subdev;
priv->source_sd_pad = ret;
ret = media_create_pad_link(&source_subdev->entity, priv->source_sd_pad,
&priv->sd.entity, UB913_PAD_SINK,
MEDIA_LNK_FL_ENABLED |
MEDIA_LNK_FL_IMMUTABLE);
if (ret) {
dev_err(dev, "Unable to link %s:%u -> %s:0\n",
source_subdev->name, priv->source_sd_pad,
priv->sd.name);
return ret;
}
return 0;
}
static const struct v4l2_async_notifier_operations ub913_notify_ops = {
.bound = ub913_notify_bound,
};
static int ub913_v4l2_notifier_register(struct ub913_data *priv)
{
struct device *dev = &priv->client->dev;
struct v4l2_async_connection *asd;
struct fwnode_handle *ep_fwnode;
int ret;
ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev),
UB913_PAD_SINK, 0, 0);
if (!ep_fwnode) {
dev_err(dev, "No graph endpoint\n");
return -ENODEV;
}
v4l2_async_subdev_nf_init(&priv->notifier, &priv->sd);
asd = v4l2_async_nf_add_fwnode_remote(&priv->notifier, ep_fwnode,
struct v4l2_async_connection);
fwnode_handle_put(ep_fwnode);
if (IS_ERR(asd)) {
dev_err(dev, "Failed to add subdev: %ld", PTR_ERR(asd));
v4l2_async_nf_cleanup(&priv->notifier);
return PTR_ERR(asd);
}
priv->notifier.ops = &ub913_notify_ops;
ret = v4l2_async_nf_register(&priv->notifier);
if (ret) {
dev_err(dev, "Failed to register subdev_notifier");
v4l2_async_nf_cleanup(&priv->notifier);
return ret;
}
return 0;
}
static void ub913_v4l2_nf_unregister(struct ub913_data *priv)
{
v4l2_async_nf_unregister(&priv->notifier);
v4l2_async_nf_cleanup(&priv->notifier);
}
static int ub913_register_clkout(struct ub913_data *priv)
{
struct device *dev = &priv->client->dev;
const char *name;
int ret;
name = kasprintf(GFP_KERNEL, "ds90ub913.%s.clk_out", dev_name(dev));
if (!name)
return -ENOMEM;
priv->clkout_clk_hw = devm_clk_hw_register_fixed_factor(dev, name,
__clk_get_name(priv->clkin), 0, 1, 2);
kfree(name);
if (IS_ERR(priv->clkout_clk_hw))
return dev_err_probe(dev, PTR_ERR(priv->clkout_clk_hw),
"Cannot register clkout hw\n");
ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
priv->clkout_clk_hw);
if (ret)
return dev_err_probe(dev, ret,
"Cannot add OF clock provider\n");
return 0;
}
static int ub913_i2c_master_init(struct ub913_data *priv)
{
/* i2c fast mode */
u32 scl_high = 600 + 300; /* high period + rise time, ns */
u32 scl_low = 1300 + 300; /* low period + fall time, ns */
unsigned long ref;
int ret;
ref = clk_get_rate(priv->clkin) / 2;
scl_high = div64_u64((u64)scl_high * ref, 1000000000);
scl_low = div64_u64((u64)scl_low * ref, 1000000000);
ret = ub913_write(priv, UB913_REG_SCL_HIGH_TIME, scl_high);
if (ret)
return ret;
ret = ub913_write(priv, UB913_REG_SCL_LOW_TIME, scl_low);
if (ret)
return ret;
return 0;
}
static int ub913_add_i2c_adapter(struct ub913_data *priv)
{
struct device *dev = &priv->client->dev;
struct fwnode_handle *i2c_handle;
int ret;
i2c_handle = device_get_named_child_node(dev, "i2c");
if (!i2c_handle)
return 0;
ret = i2c_atr_add_adapter(priv->plat_data->atr, priv->plat_data->port,
dev, i2c_handle);
fwnode_handle_put(i2c_handle);
if (ret)
return ret;
return 0;
}
static int ub913_parse_dt(struct ub913_data *priv)
{
struct device *dev = &priv->client->dev;
struct v4l2_fwnode_endpoint vep = {
.bus_type = V4L2_MBUS_PARALLEL,
};
struct fwnode_handle *ep_fwnode;
int ret;
ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev),
UB913_PAD_SINK, 0, 0);
if (!ep_fwnode)
return dev_err_probe(dev, -ENOENT, "No sink endpoint\n");
ret = v4l2_fwnode_endpoint_parse(ep_fwnode, &vep);
fwnode_handle_put(ep_fwnode);
if (ret)
return dev_err_probe(dev, ret,
"failed to parse sink endpoint data\n");
if (vep.bus.parallel.flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
priv->pclk_polarity_rising = true;
else if (vep.bus.parallel.flags & V4L2_MBUS_PCLK_SAMPLE_FALLING)
priv->pclk_polarity_rising = false;
else
return dev_err_probe(dev, -EINVAL,
"bad value for 'pclk-sample'\n");
return 0;
}
static int ub913_hw_init(struct ub913_data *priv)
{
struct device *dev = &priv->client->dev;
bool mode_override;
u8 mode;
int ret;
u8 v;
ret = ub913_read(priv, UB913_REG_MODE_SEL, &v);
if (ret)
return ret;
if (!(v & UB913_REG_MODE_SEL_MODE_UP_TO_DATE))
return dev_err_probe(dev, -ENODEV,
"Mode value not stabilized\n");
mode_override = v & UB913_REG_MODE_SEL_MODE_OVERRIDE;
mode = v & UB913_REG_MODE_SEL_MODE_MASK;
dev_dbg(dev, "mode from %s: %#x\n",
mode_override ? "reg" : "deserializer", mode);
ret = ub913_i2c_master_init(priv);
if (ret)
return dev_err_probe(dev, ret, "i2c master init failed\n");
ub913_read(priv, UB913_REG_GENERAL_CFG, &v);
v &= ~UB913_REG_GENERAL_CFG_PCLK_RISING;
v |= priv->pclk_polarity_rising ? UB913_REG_GENERAL_CFG_PCLK_RISING : 0;
ub913_write(priv, UB913_REG_GENERAL_CFG, v);
return 0;
}
static int ub913_subdev_init(struct ub913_data *priv)
{
struct device *dev = &priv->client->dev;
int ret;
v4l2_i2c_subdev_init(&priv->sd, priv->client, &ub913_subdev_ops);
priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
priv->sd.entity.ops = &ub913_entity_ops;
priv->pads[0].flags = MEDIA_PAD_FL_SINK;
priv->pads[1].flags = MEDIA_PAD_FL_SOURCE;
ret = media_entity_pads_init(&priv->sd.entity, 2, priv->pads);
if (ret)
return dev_err_probe(dev, ret, "Failed to init pads\n");
ret = v4l2_subdev_init_finalize(&priv->sd);
if (ret)
goto err_entity_cleanup;
ret = ub913_v4l2_notifier_register(priv);
if (ret) {
dev_err_probe(dev, ret,
"v4l2 subdev notifier register failed\n");
goto err_subdev_cleanup;
}
ret = v4l2_async_register_subdev(&priv->sd);
if (ret) {
dev_err_probe(dev, ret, "v4l2_async_register_subdev error\n");
goto err_unreg_notif;
}
return 0;
err_unreg_notif:
ub913_v4l2_nf_unregister(priv);
err_subdev_cleanup:
v4l2_subdev_cleanup(&priv->sd);
err_entity_cleanup:
media_entity_cleanup(&priv->sd.entity);
return ret;
}
static void ub913_subdev_uninit(struct ub913_data *priv)
{
v4l2_async_unregister_subdev(&priv->sd);
ub913_v4l2_nf_unregister(priv);
v4l2_subdev_cleanup(&priv->sd);
fwnode_handle_put(priv->sd.fwnode);
media_entity_cleanup(&priv->sd.entity);
}
static int ub913_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct ub913_data *priv;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->client = client;
priv->plat_data = dev_get_platdata(&client->dev);
if (!priv->plat_data)
return dev_err_probe(dev, -ENODEV, "Platform data missing\n");
priv->regmap = devm_regmap_init_i2c(client, &ub913_regmap_config);
if (IS_ERR(priv->regmap))
return dev_err_probe(dev, PTR_ERR(priv->regmap),
"Failed to init regmap\n");
/*
* ub913 can also work without ext clock, but that is not supported by
* the driver yet.
*/
priv->clkin = devm_clk_get(dev, "clkin");
if (IS_ERR(priv->clkin))
return dev_err_probe(dev, PTR_ERR(priv->clkin),
"Cannot get CLKIN\n");
ret = ub913_parse_dt(priv);
if (ret)
return ret;
ret = ub913_hw_init(priv);
if (ret)
return ret;
ret = ub913_gpiochip_probe(priv);
if (ret)
return dev_err_probe(dev, ret, "Failed to init gpiochip\n");
ret = ub913_register_clkout(priv);
if (ret) {
dev_err_probe(dev, ret, "Failed to register clkout\n");
goto err_gpiochip_remove;
}
ret = ub913_subdev_init(priv);
if (ret)
goto err_gpiochip_remove;
ret = ub913_add_i2c_adapter(priv);
if (ret) {
dev_err_probe(dev, ret, "failed to add remote i2c adapter\n");
goto err_subdev_uninit;
}
return 0;
err_subdev_uninit:
ub913_subdev_uninit(priv);
err_gpiochip_remove:
ub913_gpiochip_remove(priv);
return ret;
}
static void ub913_remove(struct i2c_client *client)
{
struct v4l2_subdev *sd = i2c_get_clientdata(client);
struct ub913_data *priv = sd_to_ub913(sd);
i2c_atr_del_adapter(priv->plat_data->atr, priv->plat_data->port);
ub913_subdev_uninit(priv);
ub913_gpiochip_remove(priv);
}
static const struct i2c_device_id ub913_id[] = { { "ds90ub913a-q1", 0 }, {} };
MODULE_DEVICE_TABLE(i2c, ub913_id);
static const struct of_device_id ub913_dt_ids[] = {
{ .compatible = "ti,ds90ub913a-q1" },
{}
};
MODULE_DEVICE_TABLE(of, ub913_dt_ids);
static struct i2c_driver ds90ub913_driver = {
.probe = ub913_probe,
.remove = ub913_remove,
.id_table = ub913_id,
.driver = {
.name = "ds90ub913a",
.of_match_table = ub913_dt_ids,
},
};
module_i2c_driver(ds90ub913_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Texas Instruments DS90UB913 FPD-Link III Serializer Driver");
MODULE_AUTHOR("Luca Ceresoli <luca@lucaceresoli.net>");
MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>");
MODULE_IMPORT_NS(I2C_ATR);