1c158d0d4STomi Valkeinen // SPDX-License-Identifier: GPL-2.0 2c158d0d4STomi Valkeinen /* 3c158d0d4STomi Valkeinen * Driver for the Texas Instruments DS90UB913 video serializer 4c158d0d4STomi Valkeinen * 5c158d0d4STomi Valkeinen * Based on a driver from Luca Ceresoli <luca@lucaceresoli.net> 6c158d0d4STomi Valkeinen * 7c158d0d4STomi Valkeinen * Copyright (c) 2019 Luca Ceresoli <luca@lucaceresoli.net> 8c158d0d4STomi Valkeinen * Copyright (c) 2023 Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> 9c158d0d4STomi Valkeinen */ 10c158d0d4STomi Valkeinen 11c158d0d4STomi Valkeinen #include <linux/clk-provider.h> 12c158d0d4STomi Valkeinen #include <linux/clk.h> 13c158d0d4STomi Valkeinen #include <linux/delay.h> 14c158d0d4STomi Valkeinen #include <linux/fwnode.h> 15c158d0d4STomi Valkeinen #include <linux/gpio/driver.h> 16c158d0d4STomi Valkeinen #include <linux/i2c-atr.h> 17c158d0d4STomi Valkeinen #include <linux/i2c.h> 18c158d0d4STomi Valkeinen #include <linux/kernel.h> 19c158d0d4STomi Valkeinen #include <linux/module.h> 20c158d0d4STomi Valkeinen #include <linux/property.h> 21c158d0d4STomi Valkeinen #include <linux/regmap.h> 22c158d0d4STomi Valkeinen 23c158d0d4STomi Valkeinen #include <media/i2c/ds90ub9xx.h> 24c158d0d4STomi Valkeinen #include <media/v4l2-subdev.h> 25c158d0d4STomi Valkeinen 26c158d0d4STomi Valkeinen #define UB913_PAD_SINK 0 27c158d0d4STomi Valkeinen #define UB913_PAD_SOURCE 1 28c158d0d4STomi Valkeinen 29c158d0d4STomi Valkeinen /* 30c158d0d4STomi Valkeinen * UB913 has 4 gpios, but gpios 3 and 4 are reserved for external oscillator 31c158d0d4STomi Valkeinen * mode. Thus we only support 2 gpios for now. 32c158d0d4STomi Valkeinen */ 33c158d0d4STomi Valkeinen #define UB913_NUM_GPIOS 2 34c158d0d4STomi Valkeinen 35c158d0d4STomi Valkeinen #define UB913_REG_RESET_CTL 0x01 36c158d0d4STomi Valkeinen #define UB913_REG_RESET_CTL_DIGITAL_RESET_1 BIT(1) 37c158d0d4STomi Valkeinen #define UB913_REG_RESET_CTL_DIGITAL_RESET_0 BIT(0) 38c158d0d4STomi Valkeinen 39c158d0d4STomi Valkeinen #define UB913_REG_GENERAL_CFG 0x03 40c158d0d4STomi Valkeinen #define UB913_REG_GENERAL_CFG_CRC_ERR_RESET BIT(5) 41c158d0d4STomi Valkeinen #define UB913_REG_GENERAL_CFG_PCLK_RISING BIT(0) 42c158d0d4STomi Valkeinen 43c158d0d4STomi Valkeinen #define UB913_REG_MODE_SEL 0x05 44c158d0d4STomi Valkeinen #define UB913_REG_MODE_SEL_MODE_OVERRIDE BIT(5) 45c158d0d4STomi Valkeinen #define UB913_REG_MODE_SEL_MODE_UP_TO_DATE BIT(4) 46c158d0d4STomi Valkeinen #define UB913_REG_MODE_SEL_MODE_MASK GENMASK(3, 0) 47c158d0d4STomi Valkeinen 48c158d0d4STomi Valkeinen #define UB913_REG_CRC_ERRORS_LSB 0x0a 49c158d0d4STomi Valkeinen #define UB913_REG_CRC_ERRORS_MSB 0x0b 50c158d0d4STomi Valkeinen 51c158d0d4STomi Valkeinen #define UB913_REG_GENERAL_STATUS 0x0c 52c158d0d4STomi Valkeinen 53c158d0d4STomi Valkeinen #define UB913_REG_GPIO_CFG(n) (0x0d + (n)) 54c158d0d4STomi Valkeinen #define UB913_REG_GPIO_CFG_ENABLE(n) BIT(0 + (n) * 4) 55c158d0d4STomi Valkeinen #define UB913_REG_GPIO_CFG_DIR_INPUT(n) BIT(1 + (n) * 4) 56c158d0d4STomi Valkeinen #define UB913_REG_GPIO_CFG_REMOTE_EN(n) BIT(2 + (n) * 4) 57c158d0d4STomi Valkeinen #define UB913_REG_GPIO_CFG_OUT_VAL(n) BIT(3 + (n) * 4) 58c158d0d4STomi Valkeinen #define UB913_REG_GPIO_CFG_MASK(n) (0xf << ((n) * 4)) 59c158d0d4STomi Valkeinen 60c158d0d4STomi Valkeinen #define UB913_REG_SCL_HIGH_TIME 0x11 61c158d0d4STomi Valkeinen #define UB913_REG_SCL_LOW_TIME 0x12 62c158d0d4STomi Valkeinen 63c158d0d4STomi Valkeinen #define UB913_REG_PLL_OVR 0x35 64c158d0d4STomi Valkeinen 65c158d0d4STomi Valkeinen struct ub913_data { 66c158d0d4STomi Valkeinen struct i2c_client *client; 67c158d0d4STomi Valkeinen struct regmap *regmap; 68c158d0d4STomi Valkeinen struct clk *clkin; 69c158d0d4STomi Valkeinen 70c158d0d4STomi Valkeinen struct gpio_chip gpio_chip; 71c158d0d4STomi Valkeinen 72c158d0d4STomi Valkeinen struct v4l2_subdev sd; 73c158d0d4STomi Valkeinen struct media_pad pads[2]; 74c158d0d4STomi Valkeinen 75c158d0d4STomi Valkeinen struct v4l2_async_notifier notifier; 76c158d0d4STomi Valkeinen 77c158d0d4STomi Valkeinen struct v4l2_subdev *source_sd; 78c158d0d4STomi Valkeinen u16 source_sd_pad; 79c158d0d4STomi Valkeinen 80c158d0d4STomi Valkeinen u64 enabled_source_streams; 81c158d0d4STomi Valkeinen 82c158d0d4STomi Valkeinen struct clk_hw *clkout_clk_hw; 83c158d0d4STomi Valkeinen 84c158d0d4STomi Valkeinen struct ds90ub9xx_platform_data *plat_data; 85c158d0d4STomi Valkeinen 86c158d0d4STomi Valkeinen u32 pclk_polarity; 87c158d0d4STomi Valkeinen }; 88c158d0d4STomi Valkeinen 89c158d0d4STomi Valkeinen static inline struct ub913_data *sd_to_ub913(struct v4l2_subdev *sd) 90c158d0d4STomi Valkeinen { 91c158d0d4STomi Valkeinen return container_of(sd, struct ub913_data, sd); 92c158d0d4STomi Valkeinen } 93c158d0d4STomi Valkeinen 94c158d0d4STomi Valkeinen struct ub913_format_info { 95c158d0d4STomi Valkeinen u32 incode; 96c158d0d4STomi Valkeinen u32 outcode; 97c158d0d4STomi Valkeinen }; 98c158d0d4STomi Valkeinen 99c158d0d4STomi Valkeinen static const struct ub913_format_info ub913_formats[] = { 100c158d0d4STomi Valkeinen /* Only RAW10 with 8-bit payload is supported at the moment */ 101c158d0d4STomi Valkeinen { .incode = MEDIA_BUS_FMT_YUYV8_2X8, .outcode = MEDIA_BUS_FMT_YUYV8_1X16 }, 102c158d0d4STomi Valkeinen { .incode = MEDIA_BUS_FMT_UYVY8_2X8, .outcode = MEDIA_BUS_FMT_UYVY8_1X16 }, 103c158d0d4STomi Valkeinen { .incode = MEDIA_BUS_FMT_VYUY8_2X8, .outcode = MEDIA_BUS_FMT_VYUY8_1X16 }, 104c158d0d4STomi Valkeinen { .incode = MEDIA_BUS_FMT_YVYU8_2X8, .outcode = MEDIA_BUS_FMT_YVYU8_1X16 }, 105c158d0d4STomi Valkeinen }; 106c158d0d4STomi Valkeinen 107c158d0d4STomi Valkeinen static const struct ub913_format_info *ub913_find_format(u32 incode) 108c158d0d4STomi Valkeinen { 109c158d0d4STomi Valkeinen unsigned int i; 110c158d0d4STomi Valkeinen 111c158d0d4STomi Valkeinen for (i = 0; i < ARRAY_SIZE(ub913_formats); i++) { 112c158d0d4STomi Valkeinen if (ub913_formats[i].incode == incode) 113c158d0d4STomi Valkeinen return &ub913_formats[i]; 114c158d0d4STomi Valkeinen } 115c158d0d4STomi Valkeinen 116c158d0d4STomi Valkeinen return NULL; 117c158d0d4STomi Valkeinen } 118c158d0d4STomi Valkeinen 119c158d0d4STomi Valkeinen static int ub913_read(const struct ub913_data *priv, u8 reg, u8 *val) 120c158d0d4STomi Valkeinen { 121c158d0d4STomi Valkeinen unsigned int v; 122c158d0d4STomi Valkeinen int ret; 123c158d0d4STomi Valkeinen 124c158d0d4STomi Valkeinen ret = regmap_read(priv->regmap, reg, &v); 125c158d0d4STomi Valkeinen if (ret < 0) { 126c158d0d4STomi Valkeinen dev_err(&priv->client->dev, 127c158d0d4STomi Valkeinen "Cannot read register 0x%02x: %d!\n", reg, ret); 128c158d0d4STomi Valkeinen return ret; 129c158d0d4STomi Valkeinen } 130c158d0d4STomi Valkeinen 131c158d0d4STomi Valkeinen *val = v; 132c158d0d4STomi Valkeinen return 0; 133c158d0d4STomi Valkeinen } 134c158d0d4STomi Valkeinen 135c158d0d4STomi Valkeinen static int ub913_write(const struct ub913_data *priv, u8 reg, u8 val) 136c158d0d4STomi Valkeinen { 137c158d0d4STomi Valkeinen int ret; 138c158d0d4STomi Valkeinen 139c158d0d4STomi Valkeinen ret = regmap_write(priv->regmap, reg, val); 140c158d0d4STomi Valkeinen if (ret < 0) 141c158d0d4STomi Valkeinen dev_err(&priv->client->dev, 142c158d0d4STomi Valkeinen "Cannot write register 0x%02x: %d!\n", reg, ret); 143c158d0d4STomi Valkeinen 144c158d0d4STomi Valkeinen return ret; 145c158d0d4STomi Valkeinen } 146c158d0d4STomi Valkeinen 147c158d0d4STomi Valkeinen /* 148c158d0d4STomi Valkeinen * GPIO chip 149c158d0d4STomi Valkeinen */ 150c158d0d4STomi Valkeinen static int ub913_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) 151c158d0d4STomi Valkeinen { 152c158d0d4STomi Valkeinen return GPIO_LINE_DIRECTION_OUT; 153c158d0d4STomi Valkeinen } 154c158d0d4STomi Valkeinen 155c158d0d4STomi Valkeinen static int ub913_gpio_direction_out(struct gpio_chip *gc, unsigned int offset, 156c158d0d4STomi Valkeinen int value) 157c158d0d4STomi Valkeinen { 158c158d0d4STomi Valkeinen struct ub913_data *priv = gpiochip_get_data(gc); 159c158d0d4STomi Valkeinen unsigned int reg_idx = offset / 2; 160c158d0d4STomi Valkeinen unsigned int field_idx = offset % 2; 161c158d0d4STomi Valkeinen 162c158d0d4STomi Valkeinen return regmap_update_bits(priv->regmap, UB913_REG_GPIO_CFG(reg_idx), 163c158d0d4STomi Valkeinen UB913_REG_GPIO_CFG_MASK(field_idx), 164c158d0d4STomi Valkeinen UB913_REG_GPIO_CFG_ENABLE(field_idx) | 165c158d0d4STomi Valkeinen (value ? UB913_REG_GPIO_CFG_OUT_VAL(field_idx) : 166c158d0d4STomi Valkeinen 0)); 167c158d0d4STomi Valkeinen } 168c158d0d4STomi Valkeinen 169c158d0d4STomi Valkeinen static void ub913_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) 170c158d0d4STomi Valkeinen { 171c158d0d4STomi Valkeinen ub913_gpio_direction_out(gc, offset, value); 172c158d0d4STomi Valkeinen } 173c158d0d4STomi Valkeinen 174c158d0d4STomi Valkeinen static int ub913_gpio_of_xlate(struct gpio_chip *gc, 175c158d0d4STomi Valkeinen const struct of_phandle_args *gpiospec, 176c158d0d4STomi Valkeinen u32 *flags) 177c158d0d4STomi Valkeinen { 178c158d0d4STomi Valkeinen if (flags) 179c158d0d4STomi Valkeinen *flags = gpiospec->args[1]; 180c158d0d4STomi Valkeinen 181c158d0d4STomi Valkeinen return gpiospec->args[0]; 182c158d0d4STomi Valkeinen } 183c158d0d4STomi Valkeinen 184c158d0d4STomi Valkeinen static int ub913_gpiochip_probe(struct ub913_data *priv) 185c158d0d4STomi Valkeinen { 186c158d0d4STomi Valkeinen struct device *dev = &priv->client->dev; 187c158d0d4STomi Valkeinen struct gpio_chip *gc = &priv->gpio_chip; 188c158d0d4STomi Valkeinen int ret; 189c158d0d4STomi Valkeinen 190c158d0d4STomi Valkeinen /* Initialize GPIOs 0 and 1 to local control, tri-state */ 191c158d0d4STomi Valkeinen ub913_write(priv, UB913_REG_GPIO_CFG(0), 0); 192c158d0d4STomi Valkeinen 193c158d0d4STomi Valkeinen gc->label = dev_name(dev); 194c158d0d4STomi Valkeinen gc->parent = dev; 195c158d0d4STomi Valkeinen gc->owner = THIS_MODULE; 196c158d0d4STomi Valkeinen gc->base = -1; 197c158d0d4STomi Valkeinen gc->can_sleep = true; 198c158d0d4STomi Valkeinen gc->ngpio = UB913_NUM_GPIOS; 199c158d0d4STomi Valkeinen gc->get_direction = ub913_gpio_get_direction; 200c158d0d4STomi Valkeinen gc->direction_output = ub913_gpio_direction_out; 201c158d0d4STomi Valkeinen gc->set = ub913_gpio_set; 202c158d0d4STomi Valkeinen gc->of_xlate = ub913_gpio_of_xlate; 203c158d0d4STomi Valkeinen gc->of_gpio_n_cells = 2; 204c158d0d4STomi Valkeinen 205c158d0d4STomi Valkeinen ret = gpiochip_add_data(gc, priv); 206c158d0d4STomi Valkeinen if (ret) { 207c158d0d4STomi Valkeinen dev_err(dev, "Failed to add GPIOs: %d\n", ret); 208c158d0d4STomi Valkeinen return ret; 209c158d0d4STomi Valkeinen } 210c158d0d4STomi Valkeinen 211c158d0d4STomi Valkeinen return 0; 212c158d0d4STomi Valkeinen } 213c158d0d4STomi Valkeinen 214c158d0d4STomi Valkeinen static void ub913_gpiochip_remove(struct ub913_data *priv) 215c158d0d4STomi Valkeinen { 216c158d0d4STomi Valkeinen gpiochip_remove(&priv->gpio_chip); 217c158d0d4STomi Valkeinen } 218c158d0d4STomi Valkeinen 219c158d0d4STomi Valkeinen static const struct regmap_config ub913_regmap_config = { 220c158d0d4STomi Valkeinen .name = "ds90ub913", 221c158d0d4STomi Valkeinen .reg_bits = 8, 222c158d0d4STomi Valkeinen .val_bits = 8, 223c158d0d4STomi Valkeinen .reg_format_endian = REGMAP_ENDIAN_DEFAULT, 224c158d0d4STomi Valkeinen .val_format_endian = REGMAP_ENDIAN_DEFAULT, 225c158d0d4STomi Valkeinen }; 226c158d0d4STomi Valkeinen 227c158d0d4STomi Valkeinen /* 228c158d0d4STomi Valkeinen * V4L2 229c158d0d4STomi Valkeinen */ 230c158d0d4STomi Valkeinen 231c158d0d4STomi Valkeinen static int ub913_enable_streams(struct v4l2_subdev *sd, 232c158d0d4STomi Valkeinen struct v4l2_subdev_state *state, u32 pad, 233c158d0d4STomi Valkeinen u64 streams_mask) 234c158d0d4STomi Valkeinen { 235c158d0d4STomi Valkeinen struct ub913_data *priv = sd_to_ub913(sd); 236c158d0d4STomi Valkeinen u64 sink_streams; 237c158d0d4STomi Valkeinen int ret; 238c158d0d4STomi Valkeinen 239c158d0d4STomi Valkeinen sink_streams = v4l2_subdev_state_xlate_streams(state, UB913_PAD_SOURCE, 240c158d0d4STomi Valkeinen UB913_PAD_SINK, 241c158d0d4STomi Valkeinen &streams_mask); 242c158d0d4STomi Valkeinen 243c158d0d4STomi Valkeinen ret = v4l2_subdev_enable_streams(priv->source_sd, priv->source_sd_pad, 244c158d0d4STomi Valkeinen sink_streams); 245c158d0d4STomi Valkeinen if (ret) 246c158d0d4STomi Valkeinen return ret; 247c158d0d4STomi Valkeinen 248c158d0d4STomi Valkeinen priv->enabled_source_streams |= streams_mask; 249c158d0d4STomi Valkeinen 250c158d0d4STomi Valkeinen return 0; 251c158d0d4STomi Valkeinen } 252c158d0d4STomi Valkeinen 253c158d0d4STomi Valkeinen static int ub913_disable_streams(struct v4l2_subdev *sd, 254c158d0d4STomi Valkeinen struct v4l2_subdev_state *state, u32 pad, 255c158d0d4STomi Valkeinen u64 streams_mask) 256c158d0d4STomi Valkeinen { 257c158d0d4STomi Valkeinen struct ub913_data *priv = sd_to_ub913(sd); 258c158d0d4STomi Valkeinen u64 sink_streams; 259c158d0d4STomi Valkeinen int ret; 260c158d0d4STomi Valkeinen 261c158d0d4STomi Valkeinen sink_streams = v4l2_subdev_state_xlate_streams(state, UB913_PAD_SOURCE, 262c158d0d4STomi Valkeinen UB913_PAD_SINK, 263c158d0d4STomi Valkeinen &streams_mask); 264c158d0d4STomi Valkeinen 265c158d0d4STomi Valkeinen ret = v4l2_subdev_disable_streams(priv->source_sd, priv->source_sd_pad, 266c158d0d4STomi Valkeinen sink_streams); 267c158d0d4STomi Valkeinen if (ret) 268c158d0d4STomi Valkeinen return ret; 269c158d0d4STomi Valkeinen 270c158d0d4STomi Valkeinen priv->enabled_source_streams &= ~streams_mask; 271c158d0d4STomi Valkeinen 272c158d0d4STomi Valkeinen return 0; 273c158d0d4STomi Valkeinen } 274c158d0d4STomi Valkeinen 275c158d0d4STomi Valkeinen static int _ub913_set_routing(struct v4l2_subdev *sd, 276c158d0d4STomi Valkeinen struct v4l2_subdev_state *state, 277c158d0d4STomi Valkeinen struct v4l2_subdev_krouting *routing) 278c158d0d4STomi Valkeinen { 279c158d0d4STomi Valkeinen static const struct v4l2_mbus_framefmt in_format = { 280c158d0d4STomi Valkeinen .width = 640, 281c158d0d4STomi Valkeinen .height = 480, 282c158d0d4STomi Valkeinen .code = MEDIA_BUS_FMT_UYVY8_2X8, 283c158d0d4STomi Valkeinen .field = V4L2_FIELD_NONE, 284c158d0d4STomi Valkeinen .colorspace = V4L2_COLORSPACE_SRGB, 285c158d0d4STomi Valkeinen .ycbcr_enc = V4L2_YCBCR_ENC_601, 286c158d0d4STomi Valkeinen .quantization = V4L2_QUANTIZATION_LIM_RANGE, 287c158d0d4STomi Valkeinen .xfer_func = V4L2_XFER_FUNC_SRGB, 288c158d0d4STomi Valkeinen }; 289c158d0d4STomi Valkeinen static const struct v4l2_mbus_framefmt out_format = { 290c158d0d4STomi Valkeinen .width = 640, 291c158d0d4STomi Valkeinen .height = 480, 292c158d0d4STomi Valkeinen .code = MEDIA_BUS_FMT_UYVY8_1X16, 293c158d0d4STomi Valkeinen .field = V4L2_FIELD_NONE, 294c158d0d4STomi Valkeinen .colorspace = V4L2_COLORSPACE_SRGB, 295c158d0d4STomi Valkeinen .ycbcr_enc = V4L2_YCBCR_ENC_601, 296c158d0d4STomi Valkeinen .quantization = V4L2_QUANTIZATION_LIM_RANGE, 297c158d0d4STomi Valkeinen .xfer_func = V4L2_XFER_FUNC_SRGB, 298c158d0d4STomi Valkeinen }; 299c158d0d4STomi Valkeinen struct v4l2_subdev_stream_configs *stream_configs; 300c158d0d4STomi Valkeinen unsigned int i; 301c158d0d4STomi Valkeinen int ret; 302c158d0d4STomi Valkeinen 303c158d0d4STomi Valkeinen /* 304c158d0d4STomi Valkeinen * Note: we can only support up to V4L2_FRAME_DESC_ENTRY_MAX, until 305c158d0d4STomi Valkeinen * frame desc is made dynamically allocated. 306c158d0d4STomi Valkeinen */ 307c158d0d4STomi Valkeinen 308c158d0d4STomi Valkeinen if (routing->num_routes > V4L2_FRAME_DESC_ENTRY_MAX) 309c158d0d4STomi Valkeinen return -EINVAL; 310c158d0d4STomi Valkeinen 311c158d0d4STomi Valkeinen ret = v4l2_subdev_routing_validate(sd, routing, 312c158d0d4STomi Valkeinen V4L2_SUBDEV_ROUTING_ONLY_1_TO_1); 313c158d0d4STomi Valkeinen if (ret) 314c158d0d4STomi Valkeinen return ret; 315c158d0d4STomi Valkeinen 316c158d0d4STomi Valkeinen ret = v4l2_subdev_set_routing(sd, state, routing); 317c158d0d4STomi Valkeinen if (ret) 318c158d0d4STomi Valkeinen return ret; 319c158d0d4STomi Valkeinen 320c158d0d4STomi Valkeinen stream_configs = &state->stream_configs; 321c158d0d4STomi Valkeinen 322c158d0d4STomi Valkeinen for (i = 0; i < stream_configs->num_configs; i++) { 323c158d0d4STomi Valkeinen if (stream_configs->configs[i].pad == UB913_PAD_SINK) 324c158d0d4STomi Valkeinen stream_configs->configs[i].fmt = in_format; 325c158d0d4STomi Valkeinen else 326c158d0d4STomi Valkeinen stream_configs->configs[i].fmt = out_format; 327c158d0d4STomi Valkeinen } 328c158d0d4STomi Valkeinen 329c158d0d4STomi Valkeinen return 0; 330c158d0d4STomi Valkeinen } 331c158d0d4STomi Valkeinen 332c158d0d4STomi Valkeinen static int ub913_set_routing(struct v4l2_subdev *sd, 333c158d0d4STomi Valkeinen struct v4l2_subdev_state *state, 334c158d0d4STomi Valkeinen enum v4l2_subdev_format_whence which, 335c158d0d4STomi Valkeinen struct v4l2_subdev_krouting *routing) 336c158d0d4STomi Valkeinen { 337c158d0d4STomi Valkeinen struct ub913_data *priv = sd_to_ub913(sd); 338c158d0d4STomi Valkeinen 339c158d0d4STomi Valkeinen if (which == V4L2_SUBDEV_FORMAT_ACTIVE && priv->enabled_source_streams) 340c158d0d4STomi Valkeinen return -EBUSY; 341c158d0d4STomi Valkeinen 342c158d0d4STomi Valkeinen return _ub913_set_routing(sd, state, routing); 343c158d0d4STomi Valkeinen } 344c158d0d4STomi Valkeinen 345c158d0d4STomi Valkeinen static int ub913_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, 346c158d0d4STomi Valkeinen struct v4l2_mbus_frame_desc *fd) 347c158d0d4STomi Valkeinen { 348c158d0d4STomi Valkeinen struct ub913_data *priv = sd_to_ub913(sd); 349c158d0d4STomi Valkeinen const struct v4l2_subdev_krouting *routing; 350c158d0d4STomi Valkeinen struct v4l2_mbus_frame_desc source_fd; 351c158d0d4STomi Valkeinen struct v4l2_subdev_route *route; 352c158d0d4STomi Valkeinen struct v4l2_subdev_state *state; 353c158d0d4STomi Valkeinen int ret; 354c158d0d4STomi Valkeinen 355c158d0d4STomi Valkeinen if (pad != UB913_PAD_SOURCE) 356c158d0d4STomi Valkeinen return -EINVAL; 357c158d0d4STomi Valkeinen 358c158d0d4STomi Valkeinen ret = v4l2_subdev_call(priv->source_sd, pad, get_frame_desc, 359c158d0d4STomi Valkeinen priv->source_sd_pad, &source_fd); 360c158d0d4STomi Valkeinen if (ret) 361c158d0d4STomi Valkeinen return ret; 362c158d0d4STomi Valkeinen 363c158d0d4STomi Valkeinen memset(fd, 0, sizeof(*fd)); 364c158d0d4STomi Valkeinen 365c158d0d4STomi Valkeinen fd->type = V4L2_MBUS_FRAME_DESC_TYPE_PARALLEL; 366c158d0d4STomi Valkeinen 367c158d0d4STomi Valkeinen state = v4l2_subdev_lock_and_get_active_state(sd); 368c158d0d4STomi Valkeinen 369c158d0d4STomi Valkeinen routing = &state->routing; 370c158d0d4STomi Valkeinen 371c158d0d4STomi Valkeinen for_each_active_route(routing, route) { 372c158d0d4STomi Valkeinen unsigned int i; 373c158d0d4STomi Valkeinen 374c158d0d4STomi Valkeinen if (route->source_pad != pad) 375c158d0d4STomi Valkeinen continue; 376c158d0d4STomi Valkeinen 377c158d0d4STomi Valkeinen for (i = 0; i < source_fd.num_entries; i++) { 378c158d0d4STomi Valkeinen if (source_fd.entry[i].stream == route->sink_stream) 379c158d0d4STomi Valkeinen break; 380c158d0d4STomi Valkeinen } 381c158d0d4STomi Valkeinen 382c158d0d4STomi Valkeinen if (i == source_fd.num_entries) { 383c158d0d4STomi Valkeinen dev_err(&priv->client->dev, 384c158d0d4STomi Valkeinen "Failed to find stream from source frame desc\n"); 385c158d0d4STomi Valkeinen ret = -EPIPE; 386c158d0d4STomi Valkeinen goto out_unlock; 387c158d0d4STomi Valkeinen } 388c158d0d4STomi Valkeinen 389c158d0d4STomi Valkeinen fd->entry[fd->num_entries].stream = route->source_stream; 390c158d0d4STomi Valkeinen fd->entry[fd->num_entries].flags = source_fd.entry[i].flags; 391c158d0d4STomi Valkeinen fd->entry[fd->num_entries].length = source_fd.entry[i].length; 392c158d0d4STomi Valkeinen fd->entry[fd->num_entries].pixelcode = 393c158d0d4STomi Valkeinen source_fd.entry[i].pixelcode; 394c158d0d4STomi Valkeinen 395c158d0d4STomi Valkeinen fd->num_entries++; 396c158d0d4STomi Valkeinen } 397c158d0d4STomi Valkeinen 398c158d0d4STomi Valkeinen out_unlock: 399c158d0d4STomi Valkeinen v4l2_subdev_unlock_state(state); 400c158d0d4STomi Valkeinen 401c158d0d4STomi Valkeinen return ret; 402c158d0d4STomi Valkeinen } 403c158d0d4STomi Valkeinen 404c158d0d4STomi Valkeinen static int ub913_set_fmt(struct v4l2_subdev *sd, 405c158d0d4STomi Valkeinen struct v4l2_subdev_state *state, 406c158d0d4STomi Valkeinen struct v4l2_subdev_format *format) 407c158d0d4STomi Valkeinen { 408c158d0d4STomi Valkeinen struct ub913_data *priv = sd_to_ub913(sd); 409c158d0d4STomi Valkeinen struct v4l2_mbus_framefmt *fmt; 410c158d0d4STomi Valkeinen const struct ub913_format_info *finfo; 411c158d0d4STomi Valkeinen 412c158d0d4STomi Valkeinen if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE && 413c158d0d4STomi Valkeinen priv->enabled_source_streams) 414c158d0d4STomi Valkeinen return -EBUSY; 415c158d0d4STomi Valkeinen 416c158d0d4STomi Valkeinen /* Source format is fully defined by the sink format, so not settable */ 417c158d0d4STomi Valkeinen if (format->pad == UB913_PAD_SOURCE) 418c158d0d4STomi Valkeinen return v4l2_subdev_get_fmt(sd, state, format); 419c158d0d4STomi Valkeinen 420c158d0d4STomi Valkeinen finfo = ub913_find_format(format->format.code); 421c158d0d4STomi Valkeinen if (!finfo) { 422c158d0d4STomi Valkeinen finfo = &ub913_formats[0]; 423c158d0d4STomi Valkeinen format->format.code = finfo->incode; 424c158d0d4STomi Valkeinen } 425c158d0d4STomi Valkeinen 426c158d0d4STomi Valkeinen /* Set sink format */ 427c158d0d4STomi Valkeinen fmt = v4l2_subdev_state_get_stream_format(state, format->pad, 428c158d0d4STomi Valkeinen format->stream); 429c158d0d4STomi Valkeinen if (!fmt) 430c158d0d4STomi Valkeinen return -EINVAL; 431c158d0d4STomi Valkeinen 432c158d0d4STomi Valkeinen *fmt = format->format; 433c158d0d4STomi Valkeinen 434c158d0d4STomi Valkeinen /* Propagate to source format, and adjust the mbus code */ 435c158d0d4STomi Valkeinen fmt = v4l2_subdev_state_get_opposite_stream_format(state, format->pad, 436c158d0d4STomi Valkeinen format->stream); 437c158d0d4STomi Valkeinen if (!fmt) 438c158d0d4STomi Valkeinen return -EINVAL; 439c158d0d4STomi Valkeinen 440c158d0d4STomi Valkeinen format->format.code = finfo->outcode; 441c158d0d4STomi Valkeinen 442c158d0d4STomi Valkeinen *fmt = format->format; 443c158d0d4STomi Valkeinen 444c158d0d4STomi Valkeinen return 0; 445c158d0d4STomi Valkeinen } 446c158d0d4STomi Valkeinen 447c158d0d4STomi Valkeinen static int ub913_init_cfg(struct v4l2_subdev *sd, 448c158d0d4STomi Valkeinen struct v4l2_subdev_state *state) 449c158d0d4STomi Valkeinen { 450c158d0d4STomi Valkeinen struct v4l2_subdev_route routes[] = { 451c158d0d4STomi Valkeinen { 452c158d0d4STomi Valkeinen .sink_pad = UB913_PAD_SINK, 453c158d0d4STomi Valkeinen .sink_stream = 0, 454c158d0d4STomi Valkeinen .source_pad = UB913_PAD_SOURCE, 455c158d0d4STomi Valkeinen .source_stream = 0, 456c158d0d4STomi Valkeinen .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE, 457c158d0d4STomi Valkeinen }, 458c158d0d4STomi Valkeinen }; 459c158d0d4STomi Valkeinen 460c158d0d4STomi Valkeinen struct v4l2_subdev_krouting routing = { 461c158d0d4STomi Valkeinen .num_routes = ARRAY_SIZE(routes), 462c158d0d4STomi Valkeinen .routes = routes, 463c158d0d4STomi Valkeinen }; 464c158d0d4STomi Valkeinen 465c158d0d4STomi Valkeinen return _ub913_set_routing(sd, state, &routing); 466c158d0d4STomi Valkeinen } 467c158d0d4STomi Valkeinen 468c158d0d4STomi Valkeinen static int ub913_log_status(struct v4l2_subdev *sd) 469c158d0d4STomi Valkeinen { 470c158d0d4STomi Valkeinen struct ub913_data *priv = sd_to_ub913(sd); 471c158d0d4STomi Valkeinen struct device *dev = &priv->client->dev; 472c158d0d4STomi Valkeinen u8 v, v1, v2; 473c158d0d4STomi Valkeinen 474c158d0d4STomi Valkeinen ub913_read(priv, UB913_REG_MODE_SEL, &v); 475c158d0d4STomi Valkeinen dev_info(dev, "MODE_SEL %#02x\n", v); 476c158d0d4STomi Valkeinen 477c158d0d4STomi Valkeinen ub913_read(priv, UB913_REG_CRC_ERRORS_LSB, &v1); 478c158d0d4STomi Valkeinen ub913_read(priv, UB913_REG_CRC_ERRORS_MSB, &v2); 479c158d0d4STomi Valkeinen dev_info(dev, "CRC errors %u\n", v1 | (v2 << 8)); 480c158d0d4STomi Valkeinen 481c158d0d4STomi Valkeinen /* clear CRC errors */ 482c158d0d4STomi Valkeinen ub913_read(priv, UB913_REG_GENERAL_CFG, &v); 483c158d0d4STomi Valkeinen ub913_write(priv, UB913_REG_GENERAL_CFG, 484c158d0d4STomi Valkeinen v | UB913_REG_GENERAL_CFG_CRC_ERR_RESET); 485c158d0d4STomi Valkeinen ub913_write(priv, UB913_REG_GENERAL_CFG, v); 486c158d0d4STomi Valkeinen 487c158d0d4STomi Valkeinen ub913_read(priv, UB913_REG_GENERAL_STATUS, &v); 488c158d0d4STomi Valkeinen dev_info(dev, "GENERAL_STATUS %#02x\n", v); 489c158d0d4STomi Valkeinen 490c158d0d4STomi Valkeinen ub913_read(priv, UB913_REG_PLL_OVR, &v); 491c158d0d4STomi Valkeinen dev_info(dev, "PLL_OVR %#02x\n", v); 492c158d0d4STomi Valkeinen 493c158d0d4STomi Valkeinen return 0; 494c158d0d4STomi Valkeinen } 495c158d0d4STomi Valkeinen 496c158d0d4STomi Valkeinen static const struct v4l2_subdev_core_ops ub913_subdev_core_ops = { 497c158d0d4STomi Valkeinen .log_status = ub913_log_status, 498c158d0d4STomi Valkeinen }; 499c158d0d4STomi Valkeinen 500c158d0d4STomi Valkeinen static const struct v4l2_subdev_pad_ops ub913_pad_ops = { 501c158d0d4STomi Valkeinen .enable_streams = ub913_enable_streams, 502c158d0d4STomi Valkeinen .disable_streams = ub913_disable_streams, 503c158d0d4STomi Valkeinen .set_routing = ub913_set_routing, 504c158d0d4STomi Valkeinen .get_frame_desc = ub913_get_frame_desc, 505c158d0d4STomi Valkeinen .get_fmt = v4l2_subdev_get_fmt, 506c158d0d4STomi Valkeinen .set_fmt = ub913_set_fmt, 507c158d0d4STomi Valkeinen .init_cfg = ub913_init_cfg, 508c158d0d4STomi Valkeinen }; 509c158d0d4STomi Valkeinen 510c158d0d4STomi Valkeinen static const struct v4l2_subdev_ops ub913_subdev_ops = { 511c158d0d4STomi Valkeinen .core = &ub913_subdev_core_ops, 512c158d0d4STomi Valkeinen .pad = &ub913_pad_ops, 513c158d0d4STomi Valkeinen }; 514c158d0d4STomi Valkeinen 515c158d0d4STomi Valkeinen static const struct media_entity_operations ub913_entity_ops = { 516c158d0d4STomi Valkeinen .link_validate = v4l2_subdev_link_validate, 517c158d0d4STomi Valkeinen }; 518c158d0d4STomi Valkeinen 519c158d0d4STomi Valkeinen static int ub913_notify_bound(struct v4l2_async_notifier *notifier, 520c158d0d4STomi Valkeinen struct v4l2_subdev *source_subdev, 521c158d0d4STomi Valkeinen struct v4l2_async_subdev *asd) 522c158d0d4STomi Valkeinen { 523c158d0d4STomi Valkeinen struct ub913_data *priv = sd_to_ub913(notifier->sd); 524c158d0d4STomi Valkeinen struct device *dev = &priv->client->dev; 525c158d0d4STomi Valkeinen int ret; 526c158d0d4STomi Valkeinen 527c158d0d4STomi Valkeinen ret = media_entity_get_fwnode_pad(&source_subdev->entity, 528c158d0d4STomi Valkeinen source_subdev->fwnode, 529c158d0d4STomi Valkeinen MEDIA_PAD_FL_SOURCE); 530c158d0d4STomi Valkeinen if (ret < 0) { 531c158d0d4STomi Valkeinen dev_err(dev, "Failed to find pad for %s\n", 532c158d0d4STomi Valkeinen source_subdev->name); 533c158d0d4STomi Valkeinen return ret; 534c158d0d4STomi Valkeinen } 535c158d0d4STomi Valkeinen 536c158d0d4STomi Valkeinen priv->source_sd = source_subdev; 537c158d0d4STomi Valkeinen priv->source_sd_pad = ret; 538c158d0d4STomi Valkeinen 539c158d0d4STomi Valkeinen ret = media_create_pad_link(&source_subdev->entity, priv->source_sd_pad, 540c158d0d4STomi Valkeinen &priv->sd.entity, UB913_PAD_SINK, 541c158d0d4STomi Valkeinen MEDIA_LNK_FL_ENABLED | 542c158d0d4STomi Valkeinen MEDIA_LNK_FL_IMMUTABLE); 543c158d0d4STomi Valkeinen if (ret) { 544c158d0d4STomi Valkeinen dev_err(dev, "Unable to link %s:%u -> %s:0\n", 545c158d0d4STomi Valkeinen source_subdev->name, priv->source_sd_pad, 546c158d0d4STomi Valkeinen priv->sd.name); 547c158d0d4STomi Valkeinen return ret; 548c158d0d4STomi Valkeinen } 549c158d0d4STomi Valkeinen 550c158d0d4STomi Valkeinen return 0; 551c158d0d4STomi Valkeinen } 552c158d0d4STomi Valkeinen 553c158d0d4STomi Valkeinen static const struct v4l2_async_notifier_operations ub913_notify_ops = { 554c158d0d4STomi Valkeinen .bound = ub913_notify_bound, 555c158d0d4STomi Valkeinen }; 556c158d0d4STomi Valkeinen 557c158d0d4STomi Valkeinen static int ub913_v4l2_notifier_register(struct ub913_data *priv) 558c158d0d4STomi Valkeinen { 559c158d0d4STomi Valkeinen struct device *dev = &priv->client->dev; 560c158d0d4STomi Valkeinen struct v4l2_async_subdev *asd; 561c158d0d4STomi Valkeinen struct fwnode_handle *ep_fwnode; 562c158d0d4STomi Valkeinen int ret; 563c158d0d4STomi Valkeinen 564c158d0d4STomi Valkeinen ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 565c158d0d4STomi Valkeinen UB913_PAD_SINK, 0, 0); 566c158d0d4STomi Valkeinen if (!ep_fwnode) { 567c158d0d4STomi Valkeinen dev_err(dev, "No graph endpoint\n"); 568c158d0d4STomi Valkeinen return -ENODEV; 569c158d0d4STomi Valkeinen } 570c158d0d4STomi Valkeinen 571c158d0d4STomi Valkeinen v4l2_async_nf_init(&priv->notifier); 572c158d0d4STomi Valkeinen 573c158d0d4STomi Valkeinen asd = v4l2_async_nf_add_fwnode_remote(&priv->notifier, ep_fwnode, 574c158d0d4STomi Valkeinen struct v4l2_async_subdev); 575c158d0d4STomi Valkeinen 576c158d0d4STomi Valkeinen fwnode_handle_put(ep_fwnode); 577c158d0d4STomi Valkeinen 578c158d0d4STomi Valkeinen if (IS_ERR(asd)) { 579c158d0d4STomi Valkeinen dev_err(dev, "Failed to add subdev: %ld", PTR_ERR(asd)); 580c158d0d4STomi Valkeinen v4l2_async_nf_cleanup(&priv->notifier); 581c158d0d4STomi Valkeinen return PTR_ERR(asd); 582c158d0d4STomi Valkeinen } 583c158d0d4STomi Valkeinen 584c158d0d4STomi Valkeinen priv->notifier.ops = &ub913_notify_ops; 585c158d0d4STomi Valkeinen 586c158d0d4STomi Valkeinen ret = v4l2_async_subdev_nf_register(&priv->sd, &priv->notifier); 587c158d0d4STomi Valkeinen if (ret) { 588c158d0d4STomi Valkeinen dev_err(dev, "Failed to register subdev_notifier"); 589c158d0d4STomi Valkeinen v4l2_async_nf_cleanup(&priv->notifier); 590c158d0d4STomi Valkeinen return ret; 591c158d0d4STomi Valkeinen } 592c158d0d4STomi Valkeinen 593c158d0d4STomi Valkeinen return 0; 594c158d0d4STomi Valkeinen } 595c158d0d4STomi Valkeinen 596c158d0d4STomi Valkeinen static void ub913_v4l2_nf_unregister(struct ub913_data *priv) 597c158d0d4STomi Valkeinen { 598c158d0d4STomi Valkeinen v4l2_async_nf_unregister(&priv->notifier); 599c158d0d4STomi Valkeinen v4l2_async_nf_cleanup(&priv->notifier); 600c158d0d4STomi Valkeinen } 601c158d0d4STomi Valkeinen 602c158d0d4STomi Valkeinen static int ub913_register_clkout(struct ub913_data *priv) 603c158d0d4STomi Valkeinen { 604c158d0d4STomi Valkeinen struct device *dev = &priv->client->dev; 605c158d0d4STomi Valkeinen const char *name; 606c158d0d4STomi Valkeinen int ret; 607c158d0d4STomi Valkeinen 608c158d0d4STomi Valkeinen name = kasprintf(GFP_KERNEL, "ds90ub913.%s.clk_out", dev_name(dev)); 609c158d0d4STomi Valkeinen if (!name) 610c158d0d4STomi Valkeinen return -ENOMEM; 611c158d0d4STomi Valkeinen 612c158d0d4STomi Valkeinen priv->clkout_clk_hw = devm_clk_hw_register_fixed_factor(dev, name, 613c158d0d4STomi Valkeinen __clk_get_name(priv->clkin), 0, 1, 2); 614c158d0d4STomi Valkeinen 615c158d0d4STomi Valkeinen kfree(name); 616c158d0d4STomi Valkeinen 617c158d0d4STomi Valkeinen if (IS_ERR(priv->clkout_clk_hw)) 618c158d0d4STomi Valkeinen return dev_err_probe(dev, PTR_ERR(priv->clkout_clk_hw), 619c158d0d4STomi Valkeinen "Cannot register clkout hw\n"); 620c158d0d4STomi Valkeinen 621c158d0d4STomi Valkeinen ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, 622c158d0d4STomi Valkeinen priv->clkout_clk_hw); 623c158d0d4STomi Valkeinen if (ret) 624c158d0d4STomi Valkeinen return dev_err_probe(dev, ret, 625c158d0d4STomi Valkeinen "Cannot add OF clock provider\n"); 626c158d0d4STomi Valkeinen 627c158d0d4STomi Valkeinen return 0; 628c158d0d4STomi Valkeinen } 629c158d0d4STomi Valkeinen 630c158d0d4STomi Valkeinen static int ub913_i2c_master_init(struct ub913_data *priv) 631c158d0d4STomi Valkeinen { 632c158d0d4STomi Valkeinen /* i2c fast mode */ 633c158d0d4STomi Valkeinen u32 scl_high = 600 + 300; /* high period + rise time, ns */ 634c158d0d4STomi Valkeinen u32 scl_low = 1300 + 300; /* low period + fall time, ns */ 635c158d0d4STomi Valkeinen unsigned long ref; 636c158d0d4STomi Valkeinen int ret; 637c158d0d4STomi Valkeinen 638c158d0d4STomi Valkeinen ref = clk_get_rate(priv->clkin) / 2; 639c158d0d4STomi Valkeinen 640c158d0d4STomi Valkeinen scl_high = div64_u64((u64)scl_high * ref, 1000000000); 641c158d0d4STomi Valkeinen scl_low = div64_u64((u64)scl_low * ref, 1000000000); 642c158d0d4STomi Valkeinen 643c158d0d4STomi Valkeinen ret = ub913_write(priv, UB913_REG_SCL_HIGH_TIME, scl_high); 644c158d0d4STomi Valkeinen if (ret) 645c158d0d4STomi Valkeinen return ret; 646c158d0d4STomi Valkeinen 647c158d0d4STomi Valkeinen ret = ub913_write(priv, UB913_REG_SCL_LOW_TIME, scl_low); 648c158d0d4STomi Valkeinen if (ret) 649c158d0d4STomi Valkeinen return ret; 650c158d0d4STomi Valkeinen 651c158d0d4STomi Valkeinen return 0; 652c158d0d4STomi Valkeinen } 653c158d0d4STomi Valkeinen 654c158d0d4STomi Valkeinen static int ub913_add_i2c_adapter(struct ub913_data *priv) 655c158d0d4STomi Valkeinen { 656c158d0d4STomi Valkeinen struct device *dev = &priv->client->dev; 657c158d0d4STomi Valkeinen struct fwnode_handle *i2c_handle; 658c158d0d4STomi Valkeinen int ret; 659c158d0d4STomi Valkeinen 660c158d0d4STomi Valkeinen i2c_handle = device_get_named_child_node(dev, "i2c"); 661c158d0d4STomi Valkeinen if (!i2c_handle) 662c158d0d4STomi Valkeinen return 0; 663c158d0d4STomi Valkeinen 664c158d0d4STomi Valkeinen ret = i2c_atr_add_adapter(priv->plat_data->atr, priv->plat_data->port, 665c158d0d4STomi Valkeinen dev, i2c_handle); 666c158d0d4STomi Valkeinen 667c158d0d4STomi Valkeinen fwnode_handle_put(i2c_handle); 668c158d0d4STomi Valkeinen 669c158d0d4STomi Valkeinen if (ret) 670c158d0d4STomi Valkeinen return ret; 671c158d0d4STomi Valkeinen 672c158d0d4STomi Valkeinen return 0; 673c158d0d4STomi Valkeinen } 674c158d0d4STomi Valkeinen 675c158d0d4STomi Valkeinen static int ub913_parse_dt(struct ub913_data *priv) 676c158d0d4STomi Valkeinen { 677c158d0d4STomi Valkeinen struct device *dev = &priv->client->dev; 678c158d0d4STomi Valkeinen struct fwnode_handle *ep_fwnode; 679c158d0d4STomi Valkeinen int ret; 680c158d0d4STomi Valkeinen 681c158d0d4STomi Valkeinen ep_fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 682c158d0d4STomi Valkeinen UB913_PAD_SINK, 0, 0); 683c158d0d4STomi Valkeinen if (!ep_fwnode) { 684c158d0d4STomi Valkeinen dev_err_probe(dev, -ENOENT, "No sink endpoint\n"); 685c158d0d4STomi Valkeinen return -ENOENT; 686c158d0d4STomi Valkeinen } 687c158d0d4STomi Valkeinen 688c158d0d4STomi Valkeinen ret = fwnode_property_read_u32(ep_fwnode, "pclk-sample", 689c158d0d4STomi Valkeinen &priv->pclk_polarity); 690c158d0d4STomi Valkeinen 691c158d0d4STomi Valkeinen fwnode_handle_put(ep_fwnode); 692c158d0d4STomi Valkeinen 693c158d0d4STomi Valkeinen if (ret) { 694c158d0d4STomi Valkeinen dev_err_probe(dev, ret, "failed to parse 'pclk-sample'\n"); 695c158d0d4STomi Valkeinen return ret; 696c158d0d4STomi Valkeinen } 697c158d0d4STomi Valkeinen 698c158d0d4STomi Valkeinen return 0; 699c158d0d4STomi Valkeinen } 700c158d0d4STomi Valkeinen 701c158d0d4STomi Valkeinen static int ub913_hw_init(struct ub913_data *priv) 702c158d0d4STomi Valkeinen { 703c158d0d4STomi Valkeinen struct device *dev = &priv->client->dev; 704c158d0d4STomi Valkeinen bool mode_override; 705c158d0d4STomi Valkeinen u8 mode; 706c158d0d4STomi Valkeinen int ret; 707c158d0d4STomi Valkeinen u8 v; 708c158d0d4STomi Valkeinen 709c158d0d4STomi Valkeinen ret = ub913_read(priv, UB913_REG_MODE_SEL, &v); 710c158d0d4STomi Valkeinen if (ret) 711c158d0d4STomi Valkeinen return ret; 712c158d0d4STomi Valkeinen 713c158d0d4STomi Valkeinen if (!(v & UB913_REG_MODE_SEL_MODE_UP_TO_DATE)) 714c158d0d4STomi Valkeinen return dev_err_probe(dev, -ENODEV, 715c158d0d4STomi Valkeinen "Mode value not stabilized\n"); 716c158d0d4STomi Valkeinen 717c158d0d4STomi Valkeinen mode_override = v & UB913_REG_MODE_SEL_MODE_OVERRIDE; 718c158d0d4STomi Valkeinen mode = v & UB913_REG_MODE_SEL_MODE_MASK; 719c158d0d4STomi Valkeinen 720c158d0d4STomi Valkeinen dev_dbg(dev, "mode from %s: %#x\n", 721c158d0d4STomi Valkeinen mode_override ? "reg" : "deserializer", mode); 722c158d0d4STomi Valkeinen 723c158d0d4STomi Valkeinen ret = ub913_i2c_master_init(priv); 724c158d0d4STomi Valkeinen if (ret) 725c158d0d4STomi Valkeinen return dev_err_probe(dev, ret, "i2c master init failed\n"); 726c158d0d4STomi Valkeinen 727c158d0d4STomi Valkeinen ub913_read(priv, UB913_REG_GENERAL_CFG, &v); 728c158d0d4STomi Valkeinen v &= ~UB913_REG_GENERAL_CFG_PCLK_RISING; 729c158d0d4STomi Valkeinen v |= priv->pclk_polarity ? UB913_REG_GENERAL_CFG_PCLK_RISING : 0; 730c158d0d4STomi Valkeinen ub913_write(priv, UB913_REG_GENERAL_CFG, v); 731c158d0d4STomi Valkeinen 732c158d0d4STomi Valkeinen return 0; 733c158d0d4STomi Valkeinen } 734c158d0d4STomi Valkeinen 735c158d0d4STomi Valkeinen static int ub913_subdev_init(struct ub913_data *priv) 736c158d0d4STomi Valkeinen { 737c158d0d4STomi Valkeinen struct device *dev = &priv->client->dev; 738c158d0d4STomi Valkeinen int ret; 739c158d0d4STomi Valkeinen 740c158d0d4STomi Valkeinen v4l2_i2c_subdev_init(&priv->sd, priv->client, &ub913_subdev_ops); 741c158d0d4STomi Valkeinen priv->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS; 742c158d0d4STomi Valkeinen priv->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE; 743c158d0d4STomi Valkeinen priv->sd.entity.ops = &ub913_entity_ops; 744c158d0d4STomi Valkeinen 745c158d0d4STomi Valkeinen priv->pads[0].flags = MEDIA_PAD_FL_SINK; 746c158d0d4STomi Valkeinen priv->pads[1].flags = MEDIA_PAD_FL_SOURCE; 747c158d0d4STomi Valkeinen 748c158d0d4STomi Valkeinen ret = media_entity_pads_init(&priv->sd.entity, 2, priv->pads); 749c158d0d4STomi Valkeinen if (ret) 750c158d0d4STomi Valkeinen return dev_err_probe(dev, ret, "Failed to init pads\n"); 751c158d0d4STomi Valkeinen 752c158d0d4STomi Valkeinen priv->sd.fwnode = fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 753c158d0d4STomi Valkeinen UB913_PAD_SOURCE, 0, 754c158d0d4STomi Valkeinen 0); 755c158d0d4STomi Valkeinen 756c158d0d4STomi Valkeinen if (!priv->sd.fwnode) { 757c158d0d4STomi Valkeinen ret = -ENODEV; 758c158d0d4STomi Valkeinen dev_err_probe(dev, ret, "Missing TX endpoint\n"); 759c158d0d4STomi Valkeinen goto err_entity_cleanup; 760c158d0d4STomi Valkeinen } 761c158d0d4STomi Valkeinen 762c158d0d4STomi Valkeinen ret = v4l2_subdev_init_finalize(&priv->sd); 763c158d0d4STomi Valkeinen if (ret) 764c158d0d4STomi Valkeinen goto err_fwnode_put; 765c158d0d4STomi Valkeinen 766c158d0d4STomi Valkeinen ret = ub913_v4l2_notifier_register(priv); 767c158d0d4STomi Valkeinen if (ret) { 768c158d0d4STomi Valkeinen dev_err_probe(dev, ret, 769c158d0d4STomi Valkeinen "v4l2 subdev notifier register failed\n"); 770c158d0d4STomi Valkeinen goto err_subdev_cleanup; 771c158d0d4STomi Valkeinen } 772c158d0d4STomi Valkeinen 773c158d0d4STomi Valkeinen ret = v4l2_async_register_subdev(&priv->sd); 774c158d0d4STomi Valkeinen if (ret) { 775c158d0d4STomi Valkeinen dev_err_probe(dev, ret, "v4l2_async_register_subdev error\n"); 776c158d0d4STomi Valkeinen goto err_unreg_notif; 777c158d0d4STomi Valkeinen } 778c158d0d4STomi Valkeinen 779c158d0d4STomi Valkeinen return 0; 780c158d0d4STomi Valkeinen 781c158d0d4STomi Valkeinen err_unreg_notif: 782c158d0d4STomi Valkeinen ub913_v4l2_nf_unregister(priv); 783c158d0d4STomi Valkeinen err_subdev_cleanup: 784c158d0d4STomi Valkeinen v4l2_subdev_cleanup(&priv->sd); 785c158d0d4STomi Valkeinen err_fwnode_put: 786c158d0d4STomi Valkeinen fwnode_handle_put(priv->sd.fwnode); 787c158d0d4STomi Valkeinen err_entity_cleanup: 788c158d0d4STomi Valkeinen media_entity_cleanup(&priv->sd.entity); 789c158d0d4STomi Valkeinen 790c158d0d4STomi Valkeinen return ret; 791c158d0d4STomi Valkeinen } 792c158d0d4STomi Valkeinen 793c158d0d4STomi Valkeinen static void ub913_subdev_uninit(struct ub913_data *priv) 794c158d0d4STomi Valkeinen { 795c158d0d4STomi Valkeinen v4l2_async_unregister_subdev(&priv->sd); 796c158d0d4STomi Valkeinen ub913_v4l2_nf_unregister(priv); 797c158d0d4STomi Valkeinen v4l2_subdev_cleanup(&priv->sd); 798c158d0d4STomi Valkeinen fwnode_handle_put(priv->sd.fwnode); 799c158d0d4STomi Valkeinen media_entity_cleanup(&priv->sd.entity); 800c158d0d4STomi Valkeinen } 801c158d0d4STomi Valkeinen 802c158d0d4STomi Valkeinen static int ub913_probe(struct i2c_client *client) 803c158d0d4STomi Valkeinen { 804c158d0d4STomi Valkeinen struct device *dev = &client->dev; 805c158d0d4STomi Valkeinen struct ub913_data *priv; 806c158d0d4STomi Valkeinen int ret; 807c158d0d4STomi Valkeinen 808c158d0d4STomi Valkeinen priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 809c158d0d4STomi Valkeinen if (!priv) 810c158d0d4STomi Valkeinen return -ENOMEM; 811c158d0d4STomi Valkeinen 812c158d0d4STomi Valkeinen priv->client = client; 813c158d0d4STomi Valkeinen 814c158d0d4STomi Valkeinen priv->plat_data = dev_get_platdata(&client->dev); 815c158d0d4STomi Valkeinen if (!priv->plat_data) 816c158d0d4STomi Valkeinen return dev_err_probe(dev, -ENODEV, "Platform data missing\n"); 817c158d0d4STomi Valkeinen 818c158d0d4STomi Valkeinen priv->regmap = devm_regmap_init_i2c(client, &ub913_regmap_config); 819c158d0d4STomi Valkeinen if (IS_ERR(priv->regmap)) 820c158d0d4STomi Valkeinen return dev_err_probe(dev, PTR_ERR(priv->regmap), 821c158d0d4STomi Valkeinen "Failed to init regmap\n"); 822c158d0d4STomi Valkeinen 823c158d0d4STomi Valkeinen /* 824c158d0d4STomi Valkeinen * ub913 can also work without ext clock, but that is not supported by 825c158d0d4STomi Valkeinen * the driver yet. 826c158d0d4STomi Valkeinen */ 827c158d0d4STomi Valkeinen priv->clkin = devm_clk_get(dev, "clkin"); 828c158d0d4STomi Valkeinen if (IS_ERR(priv->clkin)) 829c158d0d4STomi Valkeinen return dev_err_probe(dev, PTR_ERR(priv->clkin), 830c158d0d4STomi Valkeinen "Cannot get CLKIN\n"); 831c158d0d4STomi Valkeinen 832c158d0d4STomi Valkeinen ret = ub913_parse_dt(priv); 833c158d0d4STomi Valkeinen if (ret) 834c158d0d4STomi Valkeinen return ret; 835c158d0d4STomi Valkeinen 836c158d0d4STomi Valkeinen ret = ub913_hw_init(priv); 837c158d0d4STomi Valkeinen if (ret) 838c158d0d4STomi Valkeinen return ret; 839c158d0d4STomi Valkeinen 840c158d0d4STomi Valkeinen ret = ub913_gpiochip_probe(priv); 841c158d0d4STomi Valkeinen if (ret) 842c158d0d4STomi Valkeinen return dev_err_probe(dev, ret, "Failed to init gpiochip\n"); 843c158d0d4STomi Valkeinen 844c158d0d4STomi Valkeinen ret = ub913_register_clkout(priv); 845c158d0d4STomi Valkeinen if (ret) { 846c158d0d4STomi Valkeinen dev_err_probe(dev, ret, "Failed to register clkout\n"); 847c158d0d4STomi Valkeinen goto err_gpiochip_remove; 848c158d0d4STomi Valkeinen } 849c158d0d4STomi Valkeinen 850c158d0d4STomi Valkeinen ret = ub913_subdev_init(priv); 851c158d0d4STomi Valkeinen if (ret) 852c158d0d4STomi Valkeinen goto err_gpiochip_remove; 853c158d0d4STomi Valkeinen 854c158d0d4STomi Valkeinen ret = ub913_add_i2c_adapter(priv); 855c158d0d4STomi Valkeinen if (ret) { 856c158d0d4STomi Valkeinen dev_err_probe(dev, ret, "failed to add remote i2c adapter\n"); 857c158d0d4STomi Valkeinen goto err_subdev_uninit; 858c158d0d4STomi Valkeinen } 859c158d0d4STomi Valkeinen 860c158d0d4STomi Valkeinen return 0; 861c158d0d4STomi Valkeinen 862c158d0d4STomi Valkeinen err_subdev_uninit: 863c158d0d4STomi Valkeinen ub913_subdev_uninit(priv); 864c158d0d4STomi Valkeinen err_gpiochip_remove: 865c158d0d4STomi Valkeinen ub913_gpiochip_remove(priv); 866c158d0d4STomi Valkeinen 867c158d0d4STomi Valkeinen return ret; 868c158d0d4STomi Valkeinen } 869c158d0d4STomi Valkeinen 870c158d0d4STomi Valkeinen static void ub913_remove(struct i2c_client *client) 871c158d0d4STomi Valkeinen { 872c158d0d4STomi Valkeinen struct v4l2_subdev *sd = i2c_get_clientdata(client); 873c158d0d4STomi Valkeinen struct ub913_data *priv = sd_to_ub913(sd); 874c158d0d4STomi Valkeinen 875c158d0d4STomi Valkeinen i2c_atr_del_adapter(priv->plat_data->atr, priv->plat_data->port); 876c158d0d4STomi Valkeinen 877c158d0d4STomi Valkeinen ub913_subdev_uninit(priv); 878c158d0d4STomi Valkeinen 879c158d0d4STomi Valkeinen ub913_gpiochip_remove(priv); 880c158d0d4STomi Valkeinen } 881c158d0d4STomi Valkeinen 882c158d0d4STomi Valkeinen static const struct i2c_device_id ub913_id[] = { { "ds90ub913a-q1", 0 }, {} }; 883c158d0d4STomi Valkeinen MODULE_DEVICE_TABLE(i2c, ub913_id); 884c158d0d4STomi Valkeinen 885c158d0d4STomi Valkeinen static const struct of_device_id ub913_dt_ids[] = { 886c158d0d4STomi Valkeinen { .compatible = "ti,ds90ub913a-q1" }, 887c158d0d4STomi Valkeinen {} 888c158d0d4STomi Valkeinen }; 889c158d0d4STomi Valkeinen MODULE_DEVICE_TABLE(of, ub913_dt_ids); 890c158d0d4STomi Valkeinen 891c158d0d4STomi Valkeinen static struct i2c_driver ds90ub913_driver = { 892*f2183847SUwe Kleine-König .probe = ub913_probe, 893c158d0d4STomi Valkeinen .remove = ub913_remove, 894c158d0d4STomi Valkeinen .id_table = ub913_id, 895c158d0d4STomi Valkeinen .driver = { 896c158d0d4STomi Valkeinen .name = "ds90ub913a", 897c158d0d4STomi Valkeinen .of_match_table = ub913_dt_ids, 898c158d0d4STomi Valkeinen }, 899c158d0d4STomi Valkeinen }; 900c158d0d4STomi Valkeinen module_i2c_driver(ds90ub913_driver); 901c158d0d4STomi Valkeinen 902c158d0d4STomi Valkeinen MODULE_LICENSE("GPL"); 903c158d0d4STomi Valkeinen MODULE_DESCRIPTION("Texas Instruments DS90UB913 FPD-Link III Serializer Driver"); 904c158d0d4STomi Valkeinen MODULE_AUTHOR("Luca Ceresoli <luca@lucaceresoli.net>"); 905c158d0d4STomi Valkeinen MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>"); 906c158d0d4STomi Valkeinen MODULE_IMPORT_NS(I2C_ATR); 907