diff options
Diffstat (limited to 'drivers/media/i2c')
74 files changed, 5546 insertions, 875 deletions
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 82af97430e5b..704af210e270 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -614,6 +614,28 @@ config VIDEO_IMX274 This is a V4L2 sensor driver for the Sony IMX274 CMOS image sensor. +config VIDEO_IMX319 + tristate "Sony IMX319 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + help + This is a Video4Linux2 sensor driver for the Sony + IMX319 camera. + + To compile this driver as a module, choose M here: the + module will be called imx319. + +config VIDEO_IMX355 + tristate "Sony IMX355 sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + help + This is a Video4Linux2 sensor driver for the Sony + IMX355 camera. + + To compile this driver as a module, choose M here: the + module will be called imx355. + config VIDEO_OV2640 tristate "OmniVision OV2640 sensor support" depends on VIDEO_V4L2 && I2C @@ -747,6 +769,7 @@ config VIDEO_OV772X tristate "OmniVision OV772x sensor support" depends on I2C && VIDEO_V4L2 depends on MEDIA_CAMERA_SUPPORT + select REGMAP_SCCB ---help--- This is a Video4Linux2 sensor driver for the OmniVision OV772x camera. @@ -786,6 +809,7 @@ config VIDEO_OV7740 config VIDEO_OV9650 tristate "OmniVision OV9650/OV9652 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + select REGMAP_SCCB ---help--- This is a V4L2 sensor driver for the Omnivision OV9650 and OV9652 camera sensors. diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index a94eb03d10d4..260d4d9ec2a1 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -108,5 +108,7 @@ obj-$(CONFIG_VIDEO_OV2659) += ov2659.o obj-$(CONFIG_VIDEO_TC358743) += tc358743.o obj-$(CONFIG_VIDEO_IMX258) += imx258.o obj-$(CONFIG_VIDEO_IMX274) += imx274.o +obj-$(CONFIG_VIDEO_IMX319) += imx319.o +obj-$(CONFIG_VIDEO_IMX355) += imx355.o obj-$(CONFIG_SDR_MAX2175) += max2175.o diff --git a/drivers/media/i2c/ad5820.c b/drivers/media/i2c/ad5820.c index 034ebf754007..907323f0ca3b 100644 --- a/drivers/media/i2c/ad5820.c +++ b/drivers/media/i2c/ad5820.c @@ -317,7 +317,7 @@ static int ad5820_probe(struct i2c_client *client, v4l2_i2c_subdev_init(&coil->subdev, client, &ad5820_ops); coil->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; coil->subdev.internal_ops = &ad5820_internal_ops; - strcpy(coil->subdev.name, "ad5820 focus"); + strscpy(coil->subdev.name, "ad5820 focus", sizeof(coil->subdev.name)); ret = media_entity_pads_init(&coil->subdev.entity, 0, NULL); if (ret < 0) diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index de10367d550b..99697baad2ea 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -1,19 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * adv7180.c Analog Devices ADV7180 video decoder driver * Copyright (c) 2009 Intel Corporation * Copyright (C) 2013 Cogent Embedded, Inc. * Copyright (C) 2013 Renesas Solutions Corp. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ - #include <linux/module.h> #include <linux/init.h> #include <linux/errno.h> @@ -761,7 +752,7 @@ static int adv7180_g_mbus_config(struct v4l2_subdev *sd, struct adv7180_state *state = to_state(sd); if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) { - cfg->type = V4L2_MBUS_CSI2; + cfg->type = V4L2_MBUS_CSI2_DPHY; cfg->flags = V4L2_MBUS_CSI2_1_LANE | V4L2_MBUS_CSI2_CHANNEL_0 | V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; diff --git a/drivers/media/i2c/adv748x/adv748x-afe.c b/drivers/media/i2c/adv748x/adv748x-afe.c index edd25e895e5d..71714634efb0 100644 --- a/drivers/media/i2c/adv748x/adv748x-afe.c +++ b/drivers/media/i2c/adv748x/adv748x-afe.c @@ -1,13 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for Analog Devices ADV748X 8 channel analog front end (AFE) receiver * with standard definition processor (SDP) * * Copyright (C) 2017 Renesas Electronics Corp. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. */ #include <linux/delay.h> @@ -286,7 +282,7 @@ static int adv748x_afe_s_stream(struct v4l2_subdev *sd, int enable) goto unlock; } - ret = adv748x_txb_power(state, enable); + ret = adv748x_tx_power(&state->txb, enable); if (ret) goto unlock; diff --git a/drivers/media/i2c/adv748x/adv748x-core.c b/drivers/media/i2c/adv748x/adv748x-core.c index 6ca88daa0ecd..6854d898fdd1 100644 --- a/drivers/media/i2c/adv748x/adv748x-core.c +++ b/drivers/media/i2c/adv748x/adv748x-core.c @@ -1,13 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for Analog Devices ADV748X HDMI receiver with AFE * * Copyright (C) 2017 Renesas Electronics Corp. * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * * Authors: * Koji Matsuoka <koji.matsuoka.xm@renesas.com> * Niklas Söderlund <niklas.soderlund@ragnatech.se> @@ -285,40 +281,23 @@ static const struct adv748x_reg_value adv748x_power_down_txb_1lane[] = { {ADV748X_PAGE_TXB, 0x31, 0x82}, /* ADI Required Write */ {ADV748X_PAGE_TXB, 0x1e, 0x00}, /* ADI Required Write */ - {ADV748X_PAGE_TXB, 0x00, 0x81}, /* Enable 4-lane MIPI */ + {ADV748X_PAGE_TXB, 0x00, 0x81}, /* Enable 1-lane MIPI */ {ADV748X_PAGE_TXB, 0xda, 0x01}, /* i2c_mipi_pll_en - 1'b1 */ {ADV748X_PAGE_TXB, 0xc1, 0x3b}, /* ADI Required Write */ {ADV748X_PAGE_EOR, 0xff, 0xff} /* End of register table */ }; -int adv748x_txa_power(struct adv748x_state *state, bool on) +int adv748x_tx_power(struct adv748x_csi2 *tx, bool on) { + struct adv748x_state *state = tx->state; + const struct adv748x_reg_value *reglist; int val; - val = txa_read(state, ADV748X_CSI_FS_AS_LS); - if (val < 0) - return val; - - /* - * This test against BIT(6) is not documented by the datasheet, but was - * specified in the downstream driver. - * Track with a WARN_ONCE to determine if it is ever set by HW. - */ - WARN_ONCE((on && val & ADV748X_CSI_FS_AS_LS_UNKNOWN), - "Enabling with unknown bit set"); + if (!is_tx_enabled(tx)) + return 0; - if (on) - return adv748x_write_regs(state, adv748x_power_up_txa_4lane); - - return adv748x_write_regs(state, adv748x_power_down_txa_4lane); -} - -int adv748x_txb_power(struct adv748x_state *state, bool on) -{ - int val; - - val = txb_read(state, ADV748X_CSI_FS_AS_LS); + val = tx_read(tx, ADV748X_CSI_FS_AS_LS); if (val < 0) return val; @@ -331,9 +310,13 @@ int adv748x_txb_power(struct adv748x_state *state, bool on) "Enabling with unknown bit set"); if (on) - return adv748x_write_regs(state, adv748x_power_up_txb_1lane); + reglist = is_txa(tx) ? adv748x_power_up_txa_4lane : + adv748x_power_up_txb_1lane; + else + reglist = is_txa(tx) ? adv748x_power_down_txa_4lane : + adv748x_power_down_txb_1lane; - return adv748x_write_regs(state, adv748x_power_down_txb_1lane); + return adv748x_write_regs(state, reglist); } /* ----------------------------------------------------------------------------- @@ -399,8 +382,6 @@ static const struct adv748x_reg_value adv748x_init_txa_4lane[] = { {ADV748X_PAGE_IO, 0x0c, 0xe0}, /* Enable LLC_DLL & Double LLC Timing */ {ADV748X_PAGE_IO, 0x0e, 0xdd}, /* LLC/PIX/SPI PINS TRISTATED AUD */ - /* Outputs Enabled */ - {ADV748X_PAGE_IO, 0x10, 0xa0}, /* Enable 4-lane CSI Tx & Pixel Port */ {ADV748X_PAGE_TXA, 0x00, 0x84}, /* Enable 4-lane MIPI */ {ADV748X_PAGE_TXA, 0x00, 0xa4}, /* Set Auto DPHY Timing */ @@ -454,10 +435,6 @@ static const struct adv748x_reg_value adv748x_init_txb_1lane[] = { {ADV748X_PAGE_SDP, 0x31, 0x12}, /* ADI Required Write */ {ADV748X_PAGE_SDP, 0xe6, 0x4f}, /* V bit end pos manually in NTSC */ - /* Enable 1-Lane MIPI Tx, */ - /* enable pixel output and route SD through Pixel port */ - {ADV748X_PAGE_IO, 0x10, 0x70}, - {ADV748X_PAGE_TXB, 0x00, 0x81}, /* Enable 1-lane MIPI */ {ADV748X_PAGE_TXB, 0x00, 0xa1}, /* Set Auto DPHY Timing */ {ADV748X_PAGE_TXB, 0xd2, 0x40}, /* ADI Required Write */ @@ -482,6 +459,7 @@ static const struct adv748x_reg_value adv748x_init_txb_1lane[] = { static int adv748x_reset(struct adv748x_state *state) { int ret; + u8 regval = 0; ret = adv748x_write_regs(state, adv748x_sw_reset); if (ret < 0) @@ -496,22 +474,24 @@ static int adv748x_reset(struct adv748x_state *state) if (ret) return ret; - adv748x_txa_power(state, 0); + adv748x_tx_power(&state->txa, 0); /* Init and power down TXB */ ret = adv748x_write_regs(state, adv748x_init_txb_1lane); if (ret) return ret; - adv748x_txb_power(state, 0); + adv748x_tx_power(&state->txb, 0); /* Disable chip powerdown & Enable HDMI Rx block */ io_write(state, ADV748X_IO_PD, ADV748X_IO_PD_RX_EN); - /* Enable 4-lane CSI Tx & Pixel Port */ - io_write(state, ADV748X_IO_10, ADV748X_IO_10_CSI4_EN | - ADV748X_IO_10_CSI1_EN | - ADV748X_IO_10_PIX_OUT_EN); + /* Conditionally enable TXa and TXb. */ + if (is_tx_enabled(&state->txa)) + regval |= ADV748X_IO_10_CSI4_EN; + if (is_tx_enabled(&state->txb)) + regval |= ADV748X_IO_10_CSI1_EN; + io_write(state, ADV748X_IO_10, regval); /* Use vid_std and v_freq as freerun resolution for CP */ cp_clrset(state, ADV748X_CP_CLMP_POS, ADV748X_CP_CLMP_POS_DIS_AUTO, @@ -569,7 +549,8 @@ static int adv748x_parse_dt(struct adv748x_state *state) { struct device_node *ep_np = NULL; struct of_endpoint ep; - bool found = false; + bool out_found = false; + bool in_found = false; for_each_endpoint_of_node(state->dev->of_node, ep_np) { of_graph_parse_endpoint(ep_np, &ep); @@ -592,10 +573,17 @@ static int adv748x_parse_dt(struct adv748x_state *state) of_node_get(ep_np); state->endpoints[ep.port] = ep_np; - found = true; + /* + * At least one input endpoint and one output endpoint shall + * be defined. + */ + if (ep.port < ADV748X_PORT_TXA) + in_found = true; + else + out_found = true; } - return found ? 0 : -ENODEV; + return in_found && out_found ? 0 : -ENODEV; } static void adv748x_dt_cleanup(struct adv748x_state *state) @@ -627,6 +615,17 @@ static int adv748x_probe(struct i2c_client *client, state->i2c_clients[ADV748X_PAGE_IO] = client; i2c_set_clientdata(client, state); + /* + * We can not use container_of to get back to the state with two TXs; + * Initialize the TXs's fields unconditionally on the endpoint + * presence to access them later. + */ + state->txa.state = state->txb.state = state; + state->txa.page = ADV748X_PAGE_TXA; + state->txb.page = ADV748X_PAGE_TXB; + state->txa.port = ADV748X_PORT_TXA; + state->txb.port = ADV748X_PORT_TXB; + /* Discover and process ports declared by the Device tree endpoints */ ret = adv748x_parse_dt(state); if (ret) { @@ -755,4 +754,4 @@ module_i2c_driver(adv748x_driver); MODULE_AUTHOR("Kieran Bingham <kieran.bingham@ideasonboard.com>"); MODULE_DESCRIPTION("ADV748X video decoder"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/adv748x/adv748x-csi2.c b/drivers/media/i2c/adv748x/adv748x-csi2.c index 469be87a3761..6ce21542ed48 100644 --- a/drivers/media/i2c/adv748x/adv748x-csi2.c +++ b/drivers/media/i2c/adv748x/adv748x-csi2.c @@ -1,12 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for Analog Devices ADV748X CSI-2 Transmitter * * Copyright (C) 2017 Renesas Electronics Corp. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. */ #include <linux/module.h> @@ -18,11 +14,6 @@ #include "adv748x.h" -static bool is_txa(struct adv748x_csi2 *tx) -{ - return tx == &tx->state->txa; -} - static int adv748x_csi2_set_virtual_channel(struct adv748x_csi2 *tx, unsigned int vc) { @@ -87,15 +78,15 @@ static int adv748x_csi2_registered(struct v4l2_subdev *sd) * * Link HDMI->TXA, and AFE->TXB directly. */ - if (is_txa(tx)) { + if (is_txa(tx) && is_hdmi_enabled(state)) return adv748x_csi2_register_link(tx, sd->v4l2_dev, &state->hdmi.sd, ADV748X_HDMI_SOURCE); - } else { + if (!is_txa(tx) && is_afe_enabled(state)) return adv748x_csi2_register_link(tx, sd->v4l2_dev, &state->afe.sd, ADV748X_AFE_SOURCE); - } + return 0; } static const struct v4l2_subdev_internal_ops adv748x_csi2_internal_ops = { @@ -266,19 +257,10 @@ static int adv748x_csi2_init_controls(struct adv748x_csi2 *tx) int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx) { - struct device_node *ep; int ret; - /* We can not use container_of to get back to the state with two TXs */ - tx->state = state; - tx->page = is_txa(tx) ? ADV748X_PAGE_TXA : ADV748X_PAGE_TXB; - - ep = state->endpoints[is_txa(tx) ? ADV748X_PORT_TXA : ADV748X_PORT_TXB]; - if (!ep) { - adv_err(state, "No endpoint found for %s\n", - is_txa(tx) ? "txa" : "txb"); - return -ENODEV; - } + if (!is_tx_enabled(tx)) + return 0; /* Initialise the virtual channel */ adv748x_csi2_set_virtual_channel(tx, 0); @@ -288,7 +270,7 @@ int adv748x_csi2_init(struct adv748x_state *state, struct adv748x_csi2 *tx) is_txa(tx) ? "txa" : "txb"); /* Ensure that matching is based upon the endpoint fwnodes */ - tx->sd.fwnode = of_fwnode_handle(ep); + tx->sd.fwnode = of_fwnode_handle(state->endpoints[tx->port]); /* Register internal ops for incremental subdev registration */ tx->sd.internal_ops = &adv748x_csi2_internal_ops; @@ -321,6 +303,9 @@ err_free_media: void adv748x_csi2_cleanup(struct adv748x_csi2 *tx) { + if (!is_tx_enabled(tx)) + return; + v4l2_async_unregister_subdev(&tx->sd); media_entity_cleanup(&tx->sd.entity); v4l2_ctrl_handler_free(&tx->ctrl_hdl); diff --git a/drivers/media/i2c/adv748x/adv748x-hdmi.c b/drivers/media/i2c/adv748x/adv748x-hdmi.c index aecc2a84dfec..35d027941482 100644 --- a/drivers/media/i2c/adv748x/adv748x-hdmi.c +++ b/drivers/media/i2c/adv748x/adv748x-hdmi.c @@ -1,12 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for Analog Devices ADV748X HDMI receiver and Component Processor (CP) * * Copyright (C) 2017 Renesas Electronics Corp. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. */ #include <linux/module.h> @@ -362,7 +358,7 @@ static int adv748x_hdmi_s_stream(struct v4l2_subdev *sd, int enable) mutex_lock(&state->mutex); - ret = adv748x_txa_power(state, enable); + ret = adv748x_tx_power(&state->txa, enable); if (ret) goto done; diff --git a/drivers/media/i2c/adv748x/adv748x.h b/drivers/media/i2c/adv748x/adv748x.h index 65f83741277e..39c2fdc3b416 100644 --- a/drivers/media/i2c/adv748x/adv748x.h +++ b/drivers/media/i2c/adv748x/adv748x.h @@ -1,13 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * Driver for Analog Devices ADV748X video decoder and HDMI receiver * * Copyright (C) 2017 Renesas Electronics Corp. * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * * Authors: * Koji Matsuoka <koji.matsuoka.xm@renesas.com> * Niklas Söderlund <niklas.soderlund@ragnatech.se> @@ -82,6 +78,7 @@ struct adv748x_csi2 { struct adv748x_state *state; struct v4l2_mbus_framefmt format; unsigned int page; + unsigned int port; struct media_pad pads[ADV748X_CSI2_NR_PADS]; struct v4l2_ctrl_handler ctrl_hdl; @@ -91,6 +88,18 @@ struct adv748x_csi2 { #define notifier_to_csi2(n) container_of(n, struct adv748x_csi2, notifier) #define adv748x_sd_to_csi2(sd) container_of(sd, struct adv748x_csi2, sd) +#define is_tx_enabled(_tx) ((_tx)->state->endpoints[(_tx)->port] != NULL) +#define is_txa(_tx) ((_tx) == &(_tx)->state->txa) +#define is_afe_enabled(_state) \ + ((_state)->endpoints[ADV748X_PORT_AIN0] != NULL || \ + (_state)->endpoints[ADV748X_PORT_AIN1] != NULL || \ + (_state)->endpoints[ADV748X_PORT_AIN2] != NULL || \ + (_state)->endpoints[ADV748X_PORT_AIN3] != NULL || \ + (_state)->endpoints[ADV748X_PORT_AIN4] != NULL || \ + (_state)->endpoints[ADV748X_PORT_AIN5] != NULL || \ + (_state)->endpoints[ADV748X_PORT_AIN6] != NULL || \ + (_state)->endpoints[ADV748X_PORT_AIN7] != NULL) +#define is_hdmi_enabled(_state) ((_state)->endpoints[ADV748X_PORT_HDMI] != NULL) enum adv748x_hdmi_pads { ADV748X_HDMI_SINK, @@ -376,9 +385,6 @@ int adv748x_write_block(struct adv748x_state *state, int client_page, #define cp_write(s, r, v) adv748x_write(s, ADV748X_PAGE_CP, r, v) #define cp_clrset(s, r, m, v) cp_write(s, r, (cp_read(s, r) & ~m) | v) -#define txa_read(s, r) adv748x_read(s, ADV748X_PAGE_TXA, r) -#define txb_read(s, r) adv748x_read(s, ADV748X_PAGE_TXB, r) - #define tx_read(t, r) adv748x_read(t->state, t->page, r) #define tx_write(t, r, v) adv748x_write(t->state, t->page, r, v) @@ -398,8 +404,7 @@ void adv748x_subdev_init(struct v4l2_subdev *sd, struct adv748x_state *state, int adv748x_register_subdevs(struct adv748x_state *state, struct v4l2_device *v4l2_dev); -int adv748x_txa_power(struct adv748x_state *state, bool on); -int adv748x_txb_power(struct adv748x_state *state, bool on); +int adv748x_tx_power(struct adv748x_csi2 *tx, bool on); int adv748x_afe_init(struct adv748x_afe *afe); void adv748x_afe_cleanup(struct adv748x_afe *afe); diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c index 55c2ea0720d9..f3899cc84e27 100644 --- a/drivers/media/i2c/adv7511.c +++ b/drivers/media/i2c/adv7511.c @@ -1355,10 +1355,10 @@ static int adv7511_set_fmt(struct v4l2_subdev *sd, state->xfer_func = format->format.xfer_func; switch (format->format.colorspace) { - case V4L2_COLORSPACE_ADOBERGB: + case V4L2_COLORSPACE_OPRGB: c = HDMI_COLORIMETRY_EXTENDED; - ec = y ? HDMI_EXTENDED_COLORIMETRY_ADOBE_YCC_601 : - HDMI_EXTENDED_COLORIMETRY_ADOBE_RGB; + ec = y ? HDMI_EXTENDED_COLORIMETRY_OPYCC_601 : + HDMI_EXTENDED_COLORIMETRY_OPRGB; break; case V4L2_COLORSPACE_SMPTE170M: c = y ? HDMI_COLORIMETRY_ITU_601 : HDMI_COLORIMETRY_NONE; diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 668be2bca57a..9eb7c70a7712 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -2284,8 +2284,10 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) state->aspect_ratio.numerator = 16; state->aspect_ratio.denominator = 9; - if (!state->edid.present) + if (!state->edid.present) { state->edid.blocks = 0; + cec_phys_addr_invalidate(state->cec_adap); + } v4l2_dbg(2, debug, sd, "%s: clear EDID pad %d, edid.present = 0x%x\n", __func__, edid->pad, state->edid.present); @@ -2295,8 +2297,8 @@ static int adv76xx_set_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) edid->blocks = 2; return -E2BIG; } - pa = cec_get_edid_phys_addr(edid->edid, edid->blocks * 128, &spa_loc); - err = cec_phys_addr_validate(pa, &pa, NULL); + pa = v4l2_get_edid_phys_addr(edid->edid, edid->blocks * 128, &spa_loc); + err = v4l2_phys_addr_validate(pa, &pa, NULL); if (err) return err; @@ -2474,7 +2476,7 @@ static int adv76xx_log_status(struct v4l2_subdev *sd) "YCbCr Bt.601 (16-235)", "YCbCr Bt.709 (16-235)", "xvYCC Bt.601", "xvYCC Bt.709", "YCbCr Bt.601 (0-255)", "YCbCr Bt.709 (0-255)", - "sYCC", "Adobe YCC 601", "AdobeRGB", "invalid", "invalid", + "sYCC", "opYCC 601", "opRGB", "invalid", "invalid", "invalid", "invalid", "invalid" }; static const char * const rgb_quantization_range_txt[] = { @@ -3093,7 +3095,7 @@ MODULE_DEVICE_TABLE(of, adv76xx_of_id); static int adv76xx_parse_dt(struct adv76xx_state *state) { - struct v4l2_fwnode_endpoint bus_cfg; + struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 }; struct device_node *endpoint; struct device_node *np; unsigned int flags; diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 4f8fbdd00e35..4721d49dcf0f 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -786,11 +786,13 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port) /* Disable I2C access to internal EDID ram from HDMI DDC ports */ rep_write_and_or(sd, 0x77, 0xf3, 0x00); - if (!state->hdmi_edid.present) + if (!state->hdmi_edid.present) { + cec_phys_addr_invalidate(state->cec_adap); return 0; + } - pa = cec_get_edid_phys_addr(edid, 256, &spa_loc); - err = cec_phys_addr_validate(pa, &pa, NULL); + pa = v4l2_get_edid_phys_addr(edid, 256, &spa_loc); + err = v4l2_phys_addr_validate(pa, &pa, NULL); if (err) return err; @@ -1525,6 +1527,7 @@ static void adv7842_fill_optional_dv_timings_fields(struct v4l2_subdev *sd, v4l2_find_dv_timings_cap(timings, adv7842_get_dv_timings_cap(sd), is_digital_input(sd) ? 250000 : 1000000, adv7842_check_dv_timings, NULL); + timings->bt.flags |= V4L2_DV_FL_CAN_DETECT_REDUCED_FPS; } static int adv7842_query_dv_timings(struct v4l2_subdev *sd, @@ -1596,6 +1599,14 @@ static int adv7842_query_dv_timings(struct v4l2_subdev *sd, bt->il_vbackporch = 0; } adv7842_fill_optional_dv_timings_fields(sd, timings); + if ((timings->bt.flags & V4L2_DV_FL_CAN_REDUCE_FPS) && + freq < bt->pixelclock) { + u32 reduced_freq = ((u32)bt->pixelclock / 1001) * 1000; + u32 delta_freq = abs(freq - reduced_freq); + + if (delta_freq < ((u32)bt->pixelclock - reduced_freq) / 2) + timings->bt.flags |= V4L2_DV_FL_REDUCED_FPS; + } } else { /* find format * Since LCVS values are inaccurate [REF_03, p. 339-340], diff --git a/drivers/media/i2c/ak881x.c b/drivers/media/i2c/ak881x.c index 16682c8477d1..30f9db1351b9 100644 --- a/drivers/media/i2c/ak881x.c +++ b/drivers/media/i2c/ak881x.c @@ -136,7 +136,6 @@ static int ak881x_get_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP_DEFAULT: sel->r.left = 0; sel->r.top = 0; sel->r.width = 720; diff --git a/drivers/media/i2c/cs53l32a.c b/drivers/media/i2c/cs53l32a.c index fd70fe2130a1..ef4bdbae4531 100644 --- a/drivers/media/i2c/cs53l32a.c +++ b/drivers/media/i2c/cs53l32a.c @@ -149,7 +149,7 @@ static int cs53l32a_probe(struct i2c_client *client, return -EIO; if (!id) - strlcpy(client->name, "cs53l32a", sizeof(client->name)); + strscpy(client->name, "cs53l32a", sizeof(client->name)); v4l_info(client, "chip found @ 0x%x (%s)\n", client->addr << 1, client->adapter->name); diff --git a/drivers/media/i2c/cx25840/cx25840-ir.c b/drivers/media/i2c/cx25840/cx25840-ir.c index ad7f66c7aac8..69cdc09981af 100644 --- a/drivers/media/i2c/cx25840/cx25840-ir.c +++ b/drivers/media/i2c/cx25840/cx25840-ir.c @@ -701,10 +701,8 @@ static int cx25840_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count, if (v > IR_MAX_DURATION) v = IR_MAX_DURATION; - init_ir_raw_event(&p->ir_core_data); - p->ir_core_data.pulse = u; - p->ir_core_data.duration = v; - p->ir_core_data.timeout = w; + p->ir_core_data = (struct ir_raw_event) + { .pulse = u, .duration = v, .timeout = w }; v4l2_dbg(2, ir_debug, sd, "rx read: %10u ns %s %s\n", v, u ? "mark" : "space", w ? "(timed out)" : ""); diff --git a/drivers/media/i2c/dw9714.c b/drivers/media/i2c/dw9714.c index 91fae01d052b..26d83693a681 100644 --- a/drivers/media/i2c/dw9714.c +++ b/drivers/media/i2c/dw9714.c @@ -169,8 +169,9 @@ static int dw9714_probe(struct i2c_client *client) return 0; err_cleanup: - dw9714_subdev_cleanup(dw9714_dev); - dev_err(&client->dev, "Probe failed: %d\n", rval); + v4l2_ctrl_handler_free(&dw9714_dev->ctrls_vcm); + media_entity_cleanup(&dw9714_dev->sd.entity); + return rval; } diff --git a/drivers/media/i2c/dw9807-vcm.c b/drivers/media/i2c/dw9807-vcm.c index 8ba3920b6e2f..b38a4e6d270d 100644 --- a/drivers/media/i2c/dw9807-vcm.c +++ b/drivers/media/i2c/dw9807-vcm.c @@ -218,7 +218,8 @@ static int dw9807_probe(struct i2c_client *client) return 0; err_cleanup: - dw9807_subdev_cleanup(dw9807_dev); + v4l2_ctrl_handler_free(&dw9807_dev->ctrls_vcm); + media_entity_cleanup(&dw9807_dev->sd.entity); return rval; } @@ -229,7 +230,6 @@ static int dw9807_remove(struct i2c_client *client) struct dw9807_device *dw9807_dev = sd_to_dw9807_vcm(sd); pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); dw9807_subdev_cleanup(dw9807_dev); diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index f8c70f1a34fe..11c69281692e 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * imx274.c - IMX274 CMOS Image Sensor driver * @@ -6,18 +7,6 @@ * Leon Luo <leonl@leopardimaging.com> * Edwin Zou <edwinz@leopardimaging.com> * Luca Ceresoli <luca@lucaceresoli.net> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <linux/clk.h> @@ -76,7 +65,7 @@ */ #define IMX274_MIN_EXPOSURE_TIME (4 * 260 / 72) -#define IMX274_DEFAULT_MODE IMX274_BINNING_OFF +#define IMX274_DEFAULT_BINNING IMX274_BINNING_OFF #define IMX274_MAX_WIDTH (3840) #define IMX274_MAX_HEIGHT (2160) #define IMX274_MAX_FRAME_RATE (120) @@ -178,7 +167,7 @@ enum imx274_binning { * @nocpiop: Number of clocks per internal offset period (see "Integration Time * in Each Readout Drive Mode (CSI-2)" in the datasheet) */ -struct imx274_frmfmt { +struct imx274_mode { const struct reg_8 *init_regs; unsigned int bin_ratio; int min_frame_len; @@ -349,20 +338,14 @@ static const struct reg_8 imx274_mode5_1280x720_raw10[] = { */ static const struct reg_8 imx274_start_1[] = { {IMX274_STANDBY_REG, 0x12}, - {IMX274_TABLE_END, 0x00} -}; -/* - * imx274 second step register configuration for - * starting stream - */ -static const struct reg_8 imx274_start_2[] = { - {0x3120, 0xF0}, /* clock settings */ - {0x3121, 0x00}, /* clock settings */ - {0x3122, 0x02}, /* clock settings */ - {0x3129, 0x9C}, /* clock settings */ - {0x312A, 0x02}, /* clock settings */ - {0x312D, 0x02}, /* clock settings */ + /* PLRD: clock settings */ + {0x3120, 0xF0}, + {0x3121, 0x00}, + {0x3122, 0x02}, + {0x3129, 0x9C}, + {0x312A, 0x02}, + {0x312D, 0x02}, {0x310B, 0x00}, @@ -407,20 +390,20 @@ static const struct reg_8 imx274_start_2[] = { }; /* - * imx274 third step register configuration for + * imx274 second step register configuration for * starting stream */ -static const struct reg_8 imx274_start_3[] = { +static const struct reg_8 imx274_start_2[] = { {IMX274_STANDBY_REG, 0x00}, {0x303E, 0x02}, /* SYS_MODE = 2 */ {IMX274_TABLE_END, 0x00} }; /* - * imx274 forth step register configuration for + * imx274 third step register configuration for * starting stream */ -static const struct reg_8 imx274_start_4[] = { +static const struct reg_8 imx274_start_3[] = { {0x30F4, 0x00}, {0x3018, 0xA2}, /* XHS VHS OUTUPT */ {IMX274_TABLE_END, 0x00} @@ -459,7 +442,7 @@ static const struct reg_8 imx274_tp_regs[] = { }; /* nocpiop happens to be the same number for the implemented modes */ -static const struct imx274_frmfmt imx274_formats[] = { +static const struct imx274_mode imx274_modes[] = { { /* mode 1, 4K */ .bin_ratio = 1, @@ -532,7 +515,7 @@ struct stimx274 { struct regmap *regmap; struct gpio_desc *reset_gpio; struct mutex lock; /* mutex lock for operations */ - const struct imx274_frmfmt *mode; + const struct imx274_mode *mode; }; #define IMX274_ROUND(dim, step, flags) \ @@ -666,6 +649,41 @@ static inline int imx274_write_reg(struct stimx274 *priv, u16 addr, u8 val) } /** + * Read a multibyte register. + * + * Uses a bulk read where possible. + * + * @priv: Pointer to device structure + * @addr: Address of the LSB register. Other registers must be + * consecutive, least-to-most significant. + * @val: Pointer to store the register value (cpu endianness) + * @nbytes: Number of bytes to read (range: [1..3]). + * Other bytes are zet to 0. + * + * Return: 0 on success, errors otherwise + */ +static int imx274_read_mbreg(struct stimx274 *priv, u16 addr, u32 *val, + size_t nbytes) +{ + __le32 val_le = 0; + int err; + + err = regmap_bulk_read(priv->regmap, addr, &val_le, nbytes); + if (err) { + dev_err(&priv->client->dev, + "%s : i2c bulk read failed, %x (%zu bytes)\n", + __func__, addr, nbytes); + } else { + *val = le32_to_cpu(val_le); + dev_dbg(&priv->client->dev, + "%s : addr 0x%x, val=0x%x (%zu bytes)\n", + __func__, addr, *val, nbytes); + } + + return err; +} + +/** * Write a multibyte register. * * Uses a bulk write where possible. @@ -674,7 +692,7 @@ static inline int imx274_write_reg(struct stimx274 *priv, u16 addr, u8 val) * @addr: Address of the LSB register. Other registers must be * consecutive, least-to-most significant. * @val: Value to be written to the register (cpu endianness) - * @nbytes: Number of bits to write (range: [1..3]) + * @nbytes: Number of bytes to write (range: [1..3]) */ static int imx274_write_mbreg(struct stimx274 *priv, u16 addr, u32 val, size_t nbytes) @@ -708,10 +726,6 @@ static int imx274_mode_regs(struct stimx274 *priv) if (err) return err; - err = imx274_write_table(priv, imx274_start_2); - if (err) - return err; - err = imx274_write_table(priv, priv->mode->init_regs); return err; @@ -733,7 +747,7 @@ static int imx274_start_stream(struct stimx274 *priv) * give it 1 extra ms for margin */ msleep_range(11); - err = imx274_write_table(priv, imx274_start_3); + err = imx274_write_table(priv, imx274_start_2); if (err) return err; @@ -743,7 +757,7 @@ static int imx274_start_stream(struct stimx274 *priv) * give it 1 extra ms for margin */ msleep_range(8); - err = imx274_write_table(priv, imx274_start_4); + err = imx274_write_table(priv, imx274_start_3); if (err) return err; @@ -881,7 +895,7 @@ static int __imx274_change_compose(struct stimx274 *imx274, const struct v4l2_rect *cur_crop; struct v4l2_mbus_framefmt *tgt_fmt; unsigned int i; - const struct imx274_frmfmt *best_mode = &imx274_formats[0]; + const struct imx274_mode *best_mode = &imx274_modes[0]; int best_goodness = INT_MIN; if (which == V4L2_SUBDEV_FORMAT_TRY) { @@ -892,8 +906,8 @@ static int __imx274_change_compose(struct stimx274 *imx274, tgt_fmt = &imx274->format; } - for (i = 0; i < ARRAY_SIZE(imx274_formats); i++) { - unsigned int ratio = imx274_formats[i].bin_ratio; + for (i = 0; i < ARRAY_SIZE(imx274_modes); i++) { + unsigned int ratio = imx274_modes[i].bin_ratio; int goodness = imx274_binning_goodness( imx274, @@ -903,7 +917,7 @@ static int __imx274_change_compose(struct stimx274 *imx274, if (goodness >= best_goodness) { best_goodness = goodness; - best_mode = &imx274_formats[i]; + best_mode = &imx274_modes[i]; } } @@ -1323,7 +1337,7 @@ static int imx274_s_stream(struct v4l2_subdev *sd, int on) dev_dbg(&imx274->client->dev, "%s : %s, mode index = %td\n", __func__, on ? "Stream Start" : "Stream Stop", - imx274->mode - &imx274_formats[0]); + imx274->mode - &imx274_modes[0]); mutex_lock(&imx274->lock); @@ -1387,37 +1401,17 @@ fail: static int imx274_get_frame_length(struct stimx274 *priv, u32 *val) { int err; - u16 svr; + u32 svr; u32 vmax; - u8 reg_val[3]; - - /* svr */ - err = imx274_read_reg(priv, IMX274_SVR_REG_LSB, ®_val[0]); - if (err) - goto fail; - err = imx274_read_reg(priv, IMX274_SVR_REG_MSB, ®_val[1]); + err = imx274_read_mbreg(priv, IMX274_SVR_REG_LSB, &svr, 2); if (err) goto fail; - svr = (reg_val[1] << IMX274_SHIFT_8_BITS) + reg_val[0]; - - /* vmax */ - err = imx274_read_reg(priv, IMX274_VMAX_REG_3, ®_val[0]); + err = imx274_read_mbreg(priv, IMX274_VMAX_REG_3, &vmax, 3); if (err) goto fail; - err = imx274_read_reg(priv, IMX274_VMAX_REG_2, ®_val[1]); - if (err) - goto fail; - - err = imx274_read_reg(priv, IMX274_VMAX_REG_1, ®_val[2]); - if (err) - goto fail; - - vmax = ((reg_val[2] & IMX274_MASK_LSB_3_BITS) << IMX274_SHIFT_16_BITS) - + (reg_val[1] << IMX274_SHIFT_8_BITS) + reg_val[0]; - *val = vmax * (svr + 1); return 0; @@ -1598,8 +1592,7 @@ fail: static int imx274_set_exposure(struct stimx274 *priv, int val) { int err; - u16 hmax; - u8 reg_val[2]; + u32 hmax; u32 coarse_time; /* exposure time in unit of line (HMAX)*/ dev_dbg(&priv->client->dev, @@ -1607,14 +1600,10 @@ static int imx274_set_exposure(struct stimx274 *priv, int val) /* step 1: convert input exposure_time (val) into number of 1[HMAX] */ - /* obtain HMAX value */ - err = imx274_read_reg(priv, IMX274_HMAX_REG_LSB, ®_val[0]); - if (err) - goto fail; - err = imx274_read_reg(priv, IMX274_HMAX_REG_MSB, ®_val[1]); + err = imx274_read_mbreg(priv, IMX274_HMAX_REG_LSB, &hmax, 2); if (err) goto fail; - hmax = (reg_val[1] << IMX274_SHIFT_8_BITS) + reg_val[0]; + if (hmax == 0) { err = -EINVAL; goto fail; @@ -1749,9 +1738,8 @@ static int imx274_set_frame_interval(struct stimx274 *priv, { int err; u32 frame_length, req_frame_rate; - u16 svr; - u16 hmax; - u8 reg_val[2]; + u32 svr; + u32 hmax; dev_dbg(&priv->client->dev, "%s: input frame interval = %d / %d", __func__, frame_interval.numerator, @@ -1779,25 +1767,17 @@ static int imx274_set_frame_interval(struct stimx274 *priv, * frame_length (i.e. VMAX) = (frame_interval) x 72M /(SVR+1) / HMAX */ - /* SVR */ - err = imx274_read_reg(priv, IMX274_SVR_REG_LSB, ®_val[0]); - if (err) - goto fail; - err = imx274_read_reg(priv, IMX274_SVR_REG_MSB, ®_val[1]); + err = imx274_read_mbreg(priv, IMX274_SVR_REG_LSB, &svr, 2); if (err) goto fail; - svr = (reg_val[1] << IMX274_SHIFT_8_BITS) + reg_val[0]; + dev_dbg(&priv->client->dev, "%s : register SVR = %d\n", __func__, svr); - /* HMAX */ - err = imx274_read_reg(priv, IMX274_HMAX_REG_LSB, ®_val[0]); - if (err) - goto fail; - err = imx274_read_reg(priv, IMX274_HMAX_REG_MSB, ®_val[1]); + err = imx274_read_mbreg(priv, IMX274_HMAX_REG_LSB, &hmax, 2); if (err) goto fail; - hmax = (reg_val[1] << IMX274_SHIFT_8_BITS) + reg_val[0]; + dev_dbg(&priv->client->dev, "%s : register HMAX = %d\n", __func__, hmax); @@ -1871,7 +1851,7 @@ static int imx274_probe(struct i2c_client *client, mutex_init(&imx274->lock); /* initialize format */ - imx274->mode = &imx274_formats[IMX274_DEFAULT_MODE]; + imx274->mode = &imx274_modes[IMX274_DEFAULT_BINNING]; imx274->crop.width = IMX274_MAX_WIDTH; imx274->crop.height = IMX274_MAX_HEIGHT; imx274->format.width = imx274->crop.width / imx274->mode->bin_ratio; @@ -1895,7 +1875,6 @@ static int imx274_probe(struct i2c_client *client, imx274->client = client; sd = &imx274->sd; v4l2_i2c_subdev_init(sd, client, &imx274_subdev_ops); - strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name)); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; /* initialize subdev media pad */ diff --git a/drivers/media/i2c/imx319.c b/drivers/media/i2c/imx319.c new file mode 100644 index 000000000000..0d3e27812b93 --- /dev/null +++ b/drivers/media/i2c/imx319.c @@ -0,0 +1,2560 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2018 Intel Corporation + +#include <asm/unaligned.h> +#include <linux/acpi.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fwnode.h> + +#define IMX319_REG_MODE_SELECT 0x0100 +#define IMX319_MODE_STANDBY 0x00 +#define IMX319_MODE_STREAMING 0x01 + +/* Chip ID */ +#define IMX319_REG_CHIP_ID 0x0016 +#define IMX319_CHIP_ID 0x0319 + +/* V_TIMING internal */ +#define IMX319_REG_FLL 0x0340 +#define IMX319_FLL_MAX 0xffff + +/* Exposure control */ +#define IMX319_REG_EXPOSURE 0x0202 +#define IMX319_EXPOSURE_MIN 1 +#define IMX319_EXPOSURE_STEP 1 +#define IMX319_EXPOSURE_DEFAULT 0x04f6 + +/* + * the digital control register for all color control looks like: + * +-----------------+------------------+ + * | [7:0] | [15:8] | + * +-----------------+------------------+ + * | 0x020f | 0x020e | + * -------------------------------------- + * it is used to calculate the digital gain times value(integral + fractional) + * the [15:8] bits is the fractional part and [7:0] bits is the integral + * calculation equation is: + * gain value (unit: times) = REG[15:8] + REG[7:0]/0x100 + * Only value in 0x0100 ~ 0x0FFF range is allowed. + * Analog gain use 10 bits in the registers and allowed range is 0 ~ 960 + */ +/* Analog gain control */ +#define IMX319_REG_ANALOG_GAIN 0x0204 +#define IMX319_ANA_GAIN_MIN 0 +#define IMX319_ANA_GAIN_MAX 960 +#define IMX319_ANA_GAIN_STEP 1 +#define IMX319_ANA_GAIN_DEFAULT 0 + +/* Digital gain control */ +#define IMX319_REG_DPGA_USE_GLOBAL_GAIN 0x3ff9 +#define IMX319_REG_DIG_GAIN_GLOBAL 0x020e +#define IMX319_DGTL_GAIN_MIN 256 +#define IMX319_DGTL_GAIN_MAX 4095 +#define IMX319_DGTL_GAIN_STEP 1 +#define IMX319_DGTL_GAIN_DEFAULT 256 + +/* Test Pattern Control */ +#define IMX319_REG_TEST_PATTERN 0x0600 +#define IMX319_TEST_PATTERN_DISABLED 0 +#define IMX319_TEST_PATTERN_SOLID_COLOR 1 +#define IMX319_TEST_PATTERN_COLOR_BARS 2 +#define IMX319_TEST_PATTERN_GRAY_COLOR_BARS 3 +#define IMX319_TEST_PATTERN_PN9 4 + +/* Flip Control */ +#define IMX319_REG_ORIENTATION 0x0101 + +/* default link frequency and external clock */ +#define IMX319_LINK_FREQ_DEFAULT 482400000 +#define IMX319_EXT_CLK 19200000 +#define IMX319_LINK_FREQ_INDEX 0 + +struct imx319_reg { + u16 address; + u8 val; +}; + +struct imx319_reg_list { + u32 num_of_regs; + const struct imx319_reg *regs; +}; + +/* Mode : resolution and related config&values */ +struct imx319_mode { + /* Frame width */ + u32 width; + /* Frame height */ + u32 height; + + /* V-timing */ + u32 fll_def; + u32 fll_min; + + /* H-timing */ + u32 llp; + + /* index of link frequency */ + u32 link_freq_index; + + /* Default register values */ + struct imx319_reg_list reg_list; +}; + +struct imx319_hwcfg { + u32 ext_clk; /* sensor external clk */ + s64 *link_freqs; /* CSI-2 link frequencies */ + unsigned int nr_of_link_freqs; +}; + +struct imx319 { + struct v4l2_subdev sd; + struct media_pad pad; + + struct v4l2_ctrl_handler ctrl_handler; + /* V4L2 Controls */ + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *hflip; + + /* Current mode */ + const struct imx319_mode *cur_mode; + + struct imx319_hwcfg *hwcfg; + s64 link_def_freq; /* CSI-2 link default frequency */ + + /* + * Mutex for serialized access: + * Protect sensor set pad format and start/stop streaming safely. + * Protect access to sensor v4l2 controls. + */ + struct mutex mutex; + + /* Streaming on/off */ + bool streaming; +}; + +static const struct imx319_reg imx319_global_regs[] = { + { 0x0136, 0x13 }, + { 0x0137, 0x33 }, + { 0x3c7e, 0x05 }, + { 0x3c7f, 0x07 }, + { 0x4d39, 0x0b }, + { 0x4d41, 0x33 }, + { 0x4d43, 0x0c }, + { 0x4d49, 0x89 }, + { 0x4e05, 0x0b }, + { 0x4e0d, 0x33 }, + { 0x4e0f, 0x0c }, + { 0x4e15, 0x89 }, + { 0x4e49, 0x2a }, + { 0x4e51, 0x33 }, + { 0x4e53, 0x0c }, + { 0x4e59, 0x89 }, + { 0x5601, 0x4f }, + { 0x560b, 0x45 }, + { 0x562f, 0x0a }, + { 0x5643, 0x0a }, + { 0x5645, 0x0c }, + { 0x56ef, 0x51 }, + { 0x586f, 0x33 }, + { 0x5873, 0x89 }, + { 0x5905, 0x33 }, + { 0x5907, 0x89 }, + { 0x590d, 0x33 }, + { 0x590f, 0x89 }, + { 0x5915, 0x33 }, + { 0x5917, 0x89 }, + { 0x5969, 0x1c }, + { 0x596b, 0x72 }, + { 0x5971, 0x33 }, + { 0x5973, 0x89 }, + { 0x5975, 0x33 }, + { 0x5977, 0x89 }, + { 0x5979, 0x1c }, + { 0x597b, 0x72 }, + { 0x5985, 0x33 }, + { 0x5987, 0x89 }, + { 0x5999, 0x1c }, + { 0x599b, 0x72 }, + { 0x59a5, 0x33 }, + { 0x59a7, 0x89 }, + { 0x7485, 0x08 }, + { 0x7487, 0x0c }, + { 0x7489, 0xc7 }, + { 0x748b, 0x8b }, + { 0x9004, 0x09 }, + { 0x9200, 0x6a }, + { 0x9201, 0x22 }, + { 0x9202, 0x6a }, + { 0x9203, 0x23 }, + { 0x9204, 0x5f }, + { 0x9205, 0x23 }, + { 0x9206, 0x5f }, + { 0x9207, 0x24 }, + { 0x9208, 0x5f }, + { 0x9209, 0x26 }, + { 0x920a, 0x5f }, + { 0x920b, 0x27 }, + { 0x920c, 0x5f }, + { 0x920d, 0x29 }, + { 0x920e, 0x5f }, + { 0x920f, 0x2a }, + { 0x9210, 0x5f }, + { 0x9211, 0x2c }, + { 0xbc22, 0x1a }, + { 0xf01f, 0x04 }, + { 0xf021, 0x03 }, + { 0xf023, 0x02 }, + { 0xf03d, 0x05 }, + { 0xf03f, 0x03 }, + { 0xf041, 0x02 }, + { 0xf0af, 0x04 }, + { 0xf0b1, 0x03 }, + { 0xf0b3, 0x02 }, + { 0xf0cd, 0x05 }, + { 0xf0cf, 0x03 }, + { 0xf0d1, 0x02 }, + { 0xf13f, 0x04 }, + { 0xf141, 0x03 }, + { 0xf143, 0x02 }, + { 0xf15d, 0x05 }, + { 0xf15f, 0x03 }, + { 0xf161, 0x02 }, + { 0xf1cf, 0x04 }, + { 0xf1d1, 0x03 }, + { 0xf1d3, 0x02 }, + { 0xf1ed, 0x05 }, + { 0xf1ef, 0x03 }, + { 0xf1f1, 0x02 }, + { 0xf287, 0x04 }, + { 0xf289, 0x03 }, + { 0xf28b, 0x02 }, + { 0xf2a5, 0x05 }, + { 0xf2a7, 0x03 }, + { 0xf2a9, 0x02 }, + { 0xf2b7, 0x04 }, + { 0xf2b9, 0x03 }, + { 0xf2bb, 0x02 }, + { 0xf2d5, 0x05 }, + { 0xf2d7, 0x03 }, + { 0xf2d9, 0x02 }, +}; + +static const struct imx319_reg_list imx319_global_setting = { + .num_of_regs = ARRAY_SIZE(imx319_global_regs), + .regs = imx319_global_regs, +}; + +static const struct imx319_reg mode_3264x2448_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x0f }, + { 0x0343, 0x80 }, + { 0x0340, 0x0c }, + { 0x0341, 0xaa }, + { 0x0344, 0x00 }, + { 0x0345, 0x00 }, + { 0x0346, 0x00 }, + { 0x0347, 0x00 }, + { 0x0348, 0x0c }, + { 0x0349, 0xcf }, + { 0x034a, 0x09 }, + { 0x034b, 0x9f }, + { 0x0220, 0x00 }, + { 0x0221, 0x11 }, + { 0x0381, 0x01 }, + { 0x0383, 0x01 }, + { 0x0385, 0x01 }, + { 0x0387, 0x01 }, + { 0x0900, 0x00 }, + { 0x0901, 0x11 }, + { 0x0902, 0x0a }, + { 0x3140, 0x02 }, + { 0x3141, 0x00 }, + { 0x3f0d, 0x0a }, + { 0x3f14, 0x01 }, + { 0x3f3c, 0x01 }, + { 0x3f4d, 0x01 }, + { 0x3f4c, 0x01 }, + { 0x4254, 0x7f }, + { 0x0401, 0x00 }, + { 0x0404, 0x00 }, + { 0x0405, 0x10 }, + { 0x0408, 0x00 }, + { 0x0409, 0x08 }, + { 0x040a, 0x00 }, + { 0x040b, 0x08 }, + { 0x040c, 0x0c }, + { 0x040d, 0xc0 }, + { 0x040e, 0x09 }, + { 0x040f, 0x90 }, + { 0x034c, 0x0c }, + { 0x034d, 0xc0 }, + { 0x034e, 0x09 }, + { 0x034f, 0x90 }, + { 0x3261, 0x00 }, + { 0x3264, 0x00 }, + { 0x3265, 0x10 }, + { 0x0301, 0x05 }, + { 0x0303, 0x04 }, + { 0x0305, 0x04 }, + { 0x0306, 0x01 }, + { 0x0307, 0x92 }, + { 0x0309, 0x0a }, + { 0x030b, 0x02 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0xfa }, + { 0x0310, 0x00 }, + { 0x0820, 0x0f }, + { 0x0821, 0x13 }, + { 0x0822, 0x33 }, + { 0x0823, 0x33 }, + { 0x3e20, 0x01 }, + { 0x3e37, 0x00 }, + { 0x3e3b, 0x01 }, + { 0x38a3, 0x01 }, + { 0x38a8, 0x00 }, + { 0x38a9, 0x00 }, + { 0x38aa, 0x00 }, + { 0x38ab, 0x00 }, + { 0x3234, 0x00 }, + { 0x3fc1, 0x00 }, + { 0x3235, 0x00 }, + { 0x3802, 0x00 }, + { 0x3143, 0x04 }, + { 0x360a, 0x00 }, + { 0x0b00, 0x00 }, + { 0x0106, 0x00 }, + { 0x0b05, 0x01 }, + { 0x0b06, 0x01 }, + { 0x3230, 0x00 }, + { 0x3602, 0x01 }, + { 0x3607, 0x01 }, + { 0x3c00, 0x00 }, + { 0x3c01, 0x48 }, + { 0x3c02, 0xc8 }, + { 0x3c03, 0xaa }, + { 0x3c04, 0x91 }, + { 0x3c05, 0x54 }, + { 0x3c06, 0x26 }, + { 0x3c07, 0x20 }, + { 0x3c08, 0x51 }, + { 0x3d80, 0x00 }, + { 0x3f50, 0x00 }, + { 0x3f56, 0x00 }, + { 0x3f57, 0x30 }, + { 0x3f78, 0x01 }, + { 0x3f79, 0x18 }, + { 0x3f7c, 0x00 }, + { 0x3f7d, 0x00 }, + { 0x3fba, 0x00 }, + { 0x3fbb, 0x00 }, + { 0xa081, 0x00 }, + { 0xe014, 0x00 }, + { 0x0202, 0x0a }, + { 0x0203, 0x7a }, + { 0x0224, 0x01 }, + { 0x0225, 0xf4 }, + { 0x0204, 0x00 }, + { 0x0205, 0x00 }, + { 0x0216, 0x00 }, + { 0x0217, 0x00 }, + { 0x020e, 0x01 }, + { 0x020f, 0x00 }, + { 0x0210, 0x01 }, + { 0x0211, 0x00 }, + { 0x0212, 0x01 }, + { 0x0213, 0x00 }, + { 0x0214, 0x01 }, + { 0x0215, 0x00 }, + { 0x0218, 0x01 }, + { 0x0219, 0x00 }, + { 0x3614, 0x00 }, + { 0x3616, 0x0d }, + { 0x3617, 0x56 }, + { 0xb612, 0x20 }, + { 0xb613, 0x20 }, + { 0xb614, 0x20 }, + { 0xb615, 0x20 }, + { 0xb616, 0x0a }, + { 0xb617, 0x0a }, + { 0xb618, 0x20 }, + { 0xb619, 0x20 }, + { 0xb61a, 0x20 }, + { 0xb61b, 0x20 }, + { 0xb61c, 0x0a }, + { 0xb61d, 0x0a }, + { 0xb666, 0x30 }, + { 0xb667, 0x30 }, + { 0xb668, 0x30 }, + { 0xb669, 0x30 }, + { 0xb66a, 0x14 }, + { 0xb66b, 0x14 }, + { 0xb66c, 0x20 }, + { 0xb66d, 0x20 }, + { 0xb66e, 0x20 }, + { 0xb66f, 0x20 }, + { 0xb670, 0x10 }, + { 0xb671, 0x10 }, + { 0x3237, 0x00 }, + { 0x3900, 0x00 }, + { 0x3901, 0x00 }, + { 0x3902, 0x00 }, + { 0x3904, 0x00 }, + { 0x3905, 0x00 }, + { 0x3906, 0x00 }, + { 0x3907, 0x00 }, + { 0x3908, 0x00 }, + { 0x3909, 0x00 }, + { 0x3912, 0x00 }, + { 0x3930, 0x00 }, + { 0x3931, 0x00 }, + { 0x3933, 0x00 }, + { 0x3934, 0x00 }, + { 0x3935, 0x00 }, + { 0x3936, 0x00 }, + { 0x3937, 0x00 }, + { 0x30ac, 0x00 }, +}; + +static const struct imx319_reg mode_3280x2464_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x0f }, + { 0x0343, 0x80 }, + { 0x0340, 0x0c }, + { 0x0341, 0xaa }, + { 0x0344, 0x00 }, + { 0x0345, 0x00 }, + { 0x0346, 0x00 }, + { 0x0347, 0x00 }, + { 0x0348, 0x0c }, + { 0x0349, 0xcf }, + { 0x034a, 0x09 }, + { 0x034b, 0x9f }, + { 0x0220, 0x00 }, + { 0x0221, 0x11 }, + { 0x0381, 0x01 }, + { 0x0383, 0x01 }, + { 0x0385, 0x01 }, + { 0x0387, 0x01 }, + { 0x0900, 0x00 }, + { 0x0901, 0x11 }, + { 0x0902, 0x0a }, + { 0x3140, 0x02 }, + { 0x3141, 0x00 }, + { 0x3f0d, 0x0a }, + { 0x3f14, 0x01 }, + { 0x3f3c, 0x01 }, + { 0x3f4d, 0x01 }, + { 0x3f4c, 0x01 }, + { 0x4254, 0x7f }, + { 0x0401, 0x00 }, + { 0x0404, 0x00 }, + { 0x0405, 0x10 }, + { 0x0408, 0x00 }, + { 0x0409, 0x00 }, + { 0x040a, 0x00 }, + { 0x040b, 0x00 }, + { 0x040c, 0x0c }, + { 0x040d, 0xd0 }, + { 0x040e, 0x09 }, + { 0x040f, 0xa0 }, + { 0x034c, 0x0c }, + { 0x034d, 0xd0 }, + { 0x034e, 0x09 }, + { 0x034f, 0xa0 }, + { 0x3261, 0x00 }, + { 0x3264, 0x00 }, + { 0x3265, 0x10 }, + { 0x0301, 0x05 }, + { 0x0303, 0x04 }, + { 0x0305, 0x04 }, + { 0x0306, 0x01 }, + { 0x0307, 0x92 }, + { 0x0309, 0x0a }, + { 0x030b, 0x02 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0xfa }, + { 0x0310, 0x00 }, + { 0x0820, 0x0f }, + { 0x0821, 0x13 }, + { 0x0822, 0x33 }, + { 0x0823, 0x33 }, + { 0x3e20, 0x01 }, + { 0x3e37, 0x00 }, + { 0x3e3b, 0x01 }, + { 0x38a3, 0x01 }, + { 0x38a8, 0x00 }, + { 0x38a9, 0x00 }, + { 0x38aa, 0x00 }, + { 0x38ab, 0x00 }, + { 0x3234, 0x00 }, + { 0x3fc1, 0x00 }, + { 0x3235, 0x00 }, + { 0x3802, 0x00 }, + { 0x3143, 0x04 }, + { 0x360a, 0x00 }, + { 0x0b00, 0x00 }, + { 0x0106, 0x00 }, + { 0x0b05, 0x01 }, + { 0x0b06, 0x01 }, + { 0x3230, 0x00 }, + { 0x3602, 0x01 }, + { 0x3607, 0x01 }, + { 0x3c00, 0x00 }, + { 0x3c01, 0x48 }, + { 0x3c02, 0xc8 }, + { 0x3c03, 0xaa }, + { 0x3c04, 0x91 }, + { 0x3c05, 0x54 }, + { 0x3c06, 0x26 }, + { 0x3c07, 0x20 }, + { 0x3c08, 0x51 }, + { 0x3d80, 0x00 }, + { 0x3f50, 0x00 }, + { 0x3f56, 0x00 }, + { 0x3f57, 0x30 }, + { 0x3f78, 0x01 }, + { 0x3f79, 0x18 }, + { 0x3f7c, 0x00 }, + { 0x3f7d, 0x00 }, + { 0x3fba, 0x00 }, + { 0x3fbb, 0x00 }, + { 0xa081, 0x00 }, + { 0xe014, 0x00 }, + { 0x0202, 0x0a }, + { 0x0203, 0x7a }, + { 0x0224, 0x01 }, + { 0x0225, 0xf4 }, + { 0x0204, 0x00 }, + { 0x0205, 0x00 }, + { 0x0216, 0x00 }, + { 0x0217, 0x00 }, + { 0x020e, 0x01 }, + { 0x020f, 0x00 }, + { 0x0210, 0x01 }, + { 0x0211, 0x00 }, + { 0x0212, 0x01 }, + { 0x0213, 0x00 }, + { 0x0214, 0x01 }, + { 0x0215, 0x00 }, + { 0x0218, 0x01 }, + { 0x0219, 0x00 }, + { 0x3614, 0x00 }, + { 0x3616, 0x0d }, + { 0x3617, 0x56 }, + { 0xb612, 0x20 }, + { 0xb613, 0x20 }, + { 0xb614, 0x20 }, + { 0xb615, 0x20 }, + { 0xb616, 0x0a }, + { 0xb617, 0x0a }, + { 0xb618, 0x20 }, + { 0xb619, 0x20 }, + { 0xb61a, 0x20 }, + { 0xb61b, 0x20 }, + { 0xb61c, 0x0a }, + { 0xb61d, 0x0a }, + { 0xb666, 0x30 }, + { 0xb667, 0x30 }, + { 0xb668, 0x30 }, + { 0xb669, 0x30 }, + { 0xb66a, 0x14 }, + { 0xb66b, 0x14 }, + { 0xb66c, 0x20 }, + { 0xb66d, 0x20 }, + { 0xb66e, 0x20 }, + { 0xb66f, 0x20 }, + { 0xb670, 0x10 }, + { 0xb671, 0x10 }, + { 0x3237, 0x00 }, + { 0x3900, 0x00 }, + { 0x3901, 0x00 }, + { 0x3902, 0x00 }, + { 0x3904, 0x00 }, + { 0x3905, 0x00 }, + { 0x3906, 0x00 }, + { 0x3907, 0x00 }, + { 0x3908, 0x00 }, + { 0x3909, 0x00 }, + { 0x3912, 0x00 }, + { 0x3930, 0x00 }, + { 0x3931, 0x00 }, + { 0x3933, 0x00 }, + { 0x3934, 0x00 }, + { 0x3935, 0x00 }, + { 0x3936, 0x00 }, + { 0x3937, 0x00 }, + { 0x30ac, 0x00 }, +}; + +static const struct imx319_reg mode_1936x1096_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x0f }, + { 0x0343, 0x80 }, + { 0x0340, 0x0c }, + { 0x0341, 0xaa }, + { 0x0344, 0x00 }, + { 0x0345, 0x00 }, + { 0x0346, 0x02 }, + { 0x0347, 0xac }, + { 0x0348, 0x0c }, + { 0x0349, 0xcf }, + { 0x034a, 0x06 }, + { 0x034b, 0xf3 }, + { 0x0220, 0x00 }, + { 0x0221, 0x11 }, + { 0x0381, 0x01 }, + { 0x0383, 0x01 }, + { 0x0385, 0x01 }, + { 0x0387, 0x01 }, + { 0x0900, 0x00 }, + { 0x0901, 0x11 }, + { 0x0902, 0x0a }, + { 0x3140, 0x02 }, + { 0x3141, 0x00 }, + { 0x3f0d, 0x0a }, + { 0x3f14, 0x01 }, + { 0x3f3c, 0x01 }, + { 0x3f4d, 0x01 }, + { 0x3f4c, 0x01 }, + { 0x4254, 0x7f }, + { 0x0401, 0x00 }, + { 0x0404, 0x00 }, + { 0x0405, 0x10 }, + { 0x0408, 0x02 }, + { 0x0409, 0xa0 }, + { 0x040a, 0x00 }, + { 0x040b, 0x00 }, + { 0x040c, 0x07 }, + { 0x040d, 0x90 }, + { 0x040e, 0x04 }, + { 0x040f, 0x48 }, + { 0x034c, 0x07 }, + { 0x034d, 0x90 }, + { 0x034e, 0x04 }, + { 0x034f, 0x48 }, + { 0x3261, 0x00 }, + { 0x3264, 0x00 }, + { 0x3265, 0x10 }, + { 0x0301, 0x05 }, + { 0x0303, 0x04 }, + { 0x0305, 0x04 }, + { 0x0306, 0x01 }, + { 0x0307, 0x92 }, + { 0x0309, 0x0a }, + { 0x030b, 0x02 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0xfa }, + { 0x0310, 0x00 }, + { 0x0820, 0x0f }, + { 0x0821, 0x13 }, + { 0x0822, 0x33 }, + { 0x0823, 0x33 }, + { 0x3e20, 0x01 }, + { 0x3e37, 0x00 }, + { 0x3e3b, 0x01 }, + { 0x38a3, 0x01 }, + { 0x38a8, 0x00 }, + { 0x38a9, 0x00 }, + { 0x38aa, 0x00 }, + { 0x38ab, 0x00 }, + { 0x3234, 0x00 }, + { 0x3fc1, 0x00 }, + { 0x3235, 0x00 }, + { 0x3802, 0x00 }, + { 0x3143, 0x04 }, + { 0x360a, 0x00 }, + { 0x0b00, 0x00 }, + { 0x0106, 0x00 }, + { 0x0b05, 0x01 }, + { 0x0b06, 0x01 }, + { 0x3230, 0x00 }, + { 0x3602, 0x01 }, + { 0x3607, 0x01 }, + { 0x3c00, 0x00 }, + { 0x3c01, 0x48 }, + { 0x3c02, 0xc8 }, + { 0x3c03, 0xaa }, + { 0x3c04, 0x91 }, + { 0x3c05, 0x54 }, + { 0x3c06, 0x26 }, + { 0x3c07, 0x20 }, + { 0x3c08, 0x51 }, + { 0x3d80, 0x00 }, + { 0x3f50, 0x00 }, + { 0x3f56, 0x00 }, + { 0x3f57, 0x30 }, + { 0x3f78, 0x01 }, + { 0x3f79, 0x18 }, + { 0x3f7c, 0x00 }, + { 0x3f7d, 0x00 }, + { 0x3fba, 0x00 }, + { 0x3fbb, 0x00 }, + { 0xa081, 0x00 }, + { 0xe014, 0x00 }, + { 0x0202, 0x05 }, + { 0x0203, 0x34 }, + { 0x0224, 0x01 }, + { 0x0225, 0xf4 }, + { 0x0204, 0x00 }, + { 0x0205, 0x00 }, + { 0x0216, 0x00 }, + { 0x0217, 0x00 }, + { 0x020e, 0x01 }, + { 0x020f, 0x00 }, + { 0x0210, 0x01 }, + { 0x0211, 0x00 }, + { 0x0212, 0x01 }, + { 0x0213, 0x00 }, + { 0x0214, 0x01 }, + { 0x0215, 0x00 }, + { 0x0218, 0x01 }, + { 0x0219, 0x00 }, + { 0x3614, 0x00 }, + { 0x3616, 0x0d }, + { 0x3617, 0x56 }, + { 0xb612, 0x20 }, + { 0xb613, 0x20 }, + { 0xb614, 0x20 }, + { 0xb615, 0x20 }, + { 0xb616, 0x0a }, + { 0xb617, 0x0a }, + { 0xb618, 0x20 }, + { 0xb619, 0x20 }, + { 0xb61a, 0x20 }, + { 0xb61b, 0x20 }, + { 0xb61c, 0x0a }, + { 0xb61d, 0x0a }, + { 0xb666, 0x30 }, + { 0xb667, 0x30 }, + { 0xb668, 0x30 }, + { 0xb669, 0x30 }, + { 0xb66a, 0x14 }, + { 0xb66b, 0x14 }, + { 0xb66c, 0x20 }, + { 0xb66d, 0x20 }, + { 0xb66e, 0x20 }, + { 0xb66f, 0x20 }, + { 0xb670, 0x10 }, + { 0xb671, 0x10 }, + { 0x3237, 0x00 }, + { 0x3900, 0x00 }, + { 0x3901, 0x00 }, + { 0x3902, 0x00 }, + { 0x3904, 0x00 }, + { 0x3905, 0x00 }, + { 0x3906, 0x00 }, + { 0x3907, 0x00 }, + { 0x3908, 0x00 }, + { 0x3909, 0x00 }, + { 0x3912, 0x00 }, + { 0x3930, 0x00 }, + { 0x3931, 0x00 }, + { 0x3933, 0x00 }, + { 0x3934, 0x00 }, + { 0x3935, 0x00 }, + { 0x3936, 0x00 }, + { 0x3937, 0x00 }, + { 0x30ac, 0x00 }, +}; + +static const struct imx319_reg mode_1920x1080_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x0f }, + { 0x0343, 0x80 }, + { 0x0340, 0x0c }, + { 0x0341, 0xaa }, + { 0x0344, 0x00 }, + { 0x0345, 0x00 }, + { 0x0346, 0x02 }, + { 0x0347, 0xb4 }, + { 0x0348, 0x0c }, + { 0x0349, 0xcf }, + { 0x034a, 0x06 }, + { 0x034b, 0xeb }, + { 0x0220, 0x00 }, + { 0x0221, 0x11 }, + { 0x0381, 0x01 }, + { 0x0383, 0x01 }, + { 0x0385, 0x01 }, + { 0x0387, 0x01 }, + { 0x0900, 0x00 }, + { 0x0901, 0x11 }, + { 0x0902, 0x0a }, + { 0x3140, 0x02 }, + { 0x3141, 0x00 }, + { 0x3f0d, 0x0a }, + { 0x3f14, 0x01 }, + { 0x3f3c, 0x01 }, + { 0x3f4d, 0x01 }, + { 0x3f4c, 0x01 }, + { 0x4254, 0x7f }, + { 0x0401, 0x00 }, + { 0x0404, 0x00 }, + { 0x0405, 0x10 }, + { 0x0408, 0x02 }, + { 0x0409, 0xa8 }, + { 0x040a, 0x00 }, + { 0x040b, 0x00 }, + { 0x040c, 0x07 }, + { 0x040d, 0x80 }, + { 0x040e, 0x04 }, + { 0x040f, 0x38 }, + { 0x034c, 0x07 }, + { 0x034d, 0x80 }, + { 0x034e, 0x04 }, + { 0x034f, 0x38 }, + { 0x3261, 0x00 }, + { 0x3264, 0x00 }, + { 0x3265, 0x10 }, + { 0x0301, 0x05 }, + { 0x0303, 0x04 }, + { 0x0305, 0x04 }, + { 0x0306, 0x01 }, + { 0x0307, 0x92 }, + { 0x0309, 0x0a }, + { 0x030b, 0x02 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0xfa }, + { 0x0310, 0x00 }, + { 0x0820, 0x0f }, + { 0x0821, 0x13 }, + { 0x0822, 0x33 }, + { 0x0823, 0x33 }, + { 0x3e20, 0x01 }, + { 0x3e37, 0x00 }, + { 0x3e3b, 0x01 }, + { 0x38a3, 0x01 }, + { 0x38a8, 0x00 }, + { 0x38a9, 0x00 }, + { 0x38aa, 0x00 }, + { 0x38ab, 0x00 }, + { 0x3234, 0x00 }, + { 0x3fc1, 0x00 }, + { 0x3235, 0x00 }, + { 0x3802, 0x00 }, + { 0x3143, 0x04 }, + { 0x360a, 0x00 }, + { 0x0b00, 0x00 }, + { 0x0106, 0x00 }, + { 0x0b05, 0x01 }, + { 0x0b06, 0x01 }, + { 0x3230, 0x00 }, + { 0x3602, 0x01 }, + { 0x3607, 0x01 }, + { 0x3c00, 0x00 }, + { 0x3c01, 0x48 }, + { 0x3c02, 0xc8 }, + { 0x3c03, 0xaa }, + { 0x3c04, 0x91 }, + { 0x3c05, 0x54 }, + { 0x3c06, 0x26 }, + { 0x3c07, 0x20 }, + { 0x3c08, 0x51 }, + { 0x3d80, 0x00 }, + { 0x3f50, 0x00 }, + { 0x3f56, 0x00 }, + { 0x3f57, 0x30 }, + { 0x3f78, 0x01 }, + { 0x3f79, 0x18 }, + { 0x3f7c, 0x00 }, + { 0x3f7d, 0x00 }, + { 0x3fba, 0x00 }, + { 0x3fbb, 0x00 }, + { 0xa081, 0x00 }, + { 0xe014, 0x00 }, + { 0x0202, 0x05 }, + { 0x0203, 0x34 }, + { 0x0224, 0x01 }, + { 0x0225, 0xf4 }, + { 0x0204, 0x00 }, + { 0x0205, 0x00 }, + { 0x0216, 0x00 }, + { 0x0217, 0x00 }, + { 0x020e, 0x01 }, + { 0x020f, 0x00 }, + { 0x0210, 0x01 }, + { 0x0211, 0x00 }, + { 0x0212, 0x01 }, + { 0x0213, 0x00 }, + { 0x0214, 0x01 }, + { 0x0215, 0x00 }, + { 0x0218, 0x01 }, + { 0x0219, 0x00 }, + { 0x3614, 0x00 }, + { 0x3616, 0x0d }, + { 0x3617, 0x56 }, + { 0xb612, 0x20 }, + { 0xb613, 0x20 }, + { 0xb614, 0x20 }, + { 0xb615, 0x20 }, + { 0xb616, 0x0a }, + { 0xb617, 0x0a }, + { 0xb618, 0x20 }, + { 0xb619, 0x20 }, + { 0xb61a, 0x20 }, + { 0xb61b, 0x20 }, + { 0xb61c, 0x0a }, + { 0xb61d, 0x0a }, + { 0xb666, 0x30 }, + { 0xb667, 0x30 }, + { 0xb668, 0x30 }, + { 0xb669, 0x30 }, + { 0xb66a, 0x14 }, + { 0xb66b, 0x14 }, + { 0xb66c, 0x20 }, + { 0xb66d, 0x20 }, + { 0xb66e, 0x20 }, + { 0xb66f, 0x20 }, + { 0xb670, 0x10 }, + { 0xb671, 0x10 }, + { 0x3237, 0x00 }, + { 0x3900, 0x00 }, + { 0x3901, 0x00 }, + { 0x3902, 0x00 }, + { 0x3904, 0x00 }, + { 0x3905, 0x00 }, + { 0x3906, 0x00 }, + { 0x3907, 0x00 }, + { 0x3908, 0x00 }, + { 0x3909, 0x00 }, + { 0x3912, 0x00 }, + { 0x3930, 0x00 }, + { 0x3931, 0x00 }, + { 0x3933, 0x00 }, + { 0x3934, 0x00 }, + { 0x3935, 0x00 }, + { 0x3936, 0x00 }, + { 0x3937, 0x00 }, + { 0x30ac, 0x00 }, +}; + +static const struct imx319_reg mode_1640x1232_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x08 }, + { 0x0343, 0x20 }, + { 0x0340, 0x18 }, + { 0x0341, 0x2a }, + { 0x0344, 0x00 }, + { 0x0345, 0x00 }, + { 0x0346, 0x00 }, + { 0x0347, 0x00 }, + { 0x0348, 0x0c }, + { 0x0349, 0xcf }, + { 0x034a, 0x09 }, + { 0x034b, 0x9f }, + { 0x0220, 0x00 }, + { 0x0221, 0x11 }, + { 0x0381, 0x01 }, + { 0x0383, 0x01 }, + { 0x0385, 0x01 }, + { 0x0387, 0x01 }, + { 0x0900, 0x01 }, + { 0x0901, 0x22 }, + { 0x0902, 0x0a }, + { 0x3140, 0x02 }, + { 0x3141, 0x00 }, + { 0x3f0d, 0x0a }, + { 0x3f14, 0x01 }, + { 0x3f3c, 0x02 }, + { 0x3f4d, 0x01 }, + { 0x3f4c, 0x01 }, + { 0x4254, 0x7f }, + { 0x0401, 0x00 }, + { 0x0404, 0x00 }, + { 0x0405, 0x10 }, + { 0x0408, 0x00 }, + { 0x0409, 0x00 }, + { 0x040a, 0x00 }, + { 0x040b, 0x00 }, + { 0x040c, 0x06 }, + { 0x040d, 0x68 }, + { 0x040e, 0x04 }, + { 0x040f, 0xd0 }, + { 0x034c, 0x06 }, + { 0x034d, 0x68 }, + { 0x034e, 0x04 }, + { 0x034f, 0xd0 }, + { 0x3261, 0x00 }, + { 0x3264, 0x00 }, + { 0x3265, 0x10 }, + { 0x0301, 0x05 }, + { 0x0303, 0x04 }, + { 0x0305, 0x04 }, + { 0x0306, 0x01 }, + { 0x0307, 0x92 }, + { 0x0309, 0x0a }, + { 0x030b, 0x02 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0xfa }, + { 0x0310, 0x00 }, + { 0x0820, 0x0f }, + { 0x0821, 0x13 }, + { 0x0822, 0x33 }, + { 0x0823, 0x33 }, + { 0x3e20, 0x01 }, + { 0x3e37, 0x00 }, + { 0x3e3b, 0x01 }, + { 0x38a3, 0x01 }, + { 0x38a8, 0x00 }, + { 0x38a9, 0x00 }, + { 0x38aa, 0x00 }, + { 0x38ab, 0x00 }, + { 0x3234, 0x00 }, + { 0x3fc1, 0x00 }, + { 0x3235, 0x00 }, + { 0x3802, 0x00 }, + { 0x3143, 0x04 }, + { 0x360a, 0x00 }, + { 0x0b00, 0x00 }, + { 0x0106, 0x00 }, + { 0x0b05, 0x01 }, + { 0x0b06, 0x01 }, + { 0x3230, 0x00 }, + { 0x3602, 0x01 }, + { 0x3607, 0x01 }, + { 0x3c00, 0x00 }, + { 0x3c01, 0xba }, + { 0x3c02, 0xc8 }, + { 0x3c03, 0xaa }, + { 0x3c04, 0x91 }, + { 0x3c05, 0x54 }, + { 0x3c06, 0x26 }, + { 0x3c07, 0x20 }, + { 0x3c08, 0x51 }, + { 0x3d80, 0x00 }, + { 0x3f50, 0x00 }, + { 0x3f56, 0x00 }, + { 0x3f57, 0x30 }, + { 0x3f78, 0x00 }, + { 0x3f79, 0x34 }, + { 0x3f7c, 0x00 }, + { 0x3f7d, 0x00 }, + { 0x3fba, 0x00 }, + { 0x3fbb, 0x00 }, + { 0xa081, 0x04 }, + { 0xe014, 0x00 }, + { 0x0202, 0x04 }, + { 0x0203, 0xf6 }, + { 0x0224, 0x01 }, + { 0x0225, 0xf4 }, + { 0x0204, 0x00 }, + { 0x0205, 0x00 }, + { 0x0216, 0x00 }, + { 0x0217, 0x00 }, + { 0x020e, 0x01 }, + { 0x020f, 0x00 }, + { 0x0210, 0x01 }, + { 0x0211, 0x00 }, + { 0x0212, 0x01 }, + { 0x0213, 0x00 }, + { 0x0214, 0x01 }, + { 0x0215, 0x00 }, + { 0x0218, 0x01 }, + { 0x0219, 0x00 }, + { 0x3614, 0x00 }, + { 0x3616, 0x0d }, + { 0x3617, 0x56 }, + { 0xb612, 0x20 }, + { 0xb613, 0x20 }, + { 0xb614, 0x20 }, + { 0xb615, 0x20 }, + { 0xb616, 0x0a }, + { 0xb617, 0x0a }, + { 0xb618, 0x20 }, + { 0xb619, 0x20 }, + { 0xb61a, 0x20 }, + { 0xb61b, 0x20 }, + { 0xb61c, 0x0a }, + { 0xb61d, 0x0a }, + { 0xb666, 0x30 }, + { 0xb667, 0x30 }, + { 0xb668, 0x30 }, + { 0xb669, 0x30 }, + { 0xb66a, 0x14 }, + { 0xb66b, 0x14 }, + { 0xb66c, 0x20 }, + { 0xb66d, 0x20 }, + { 0xb66e, 0x20 }, + { 0xb66f, 0x20 }, + { 0xb670, 0x10 }, + { 0xb671, 0x10 }, + { 0x3237, 0x00 }, + { 0x3900, 0x00 }, + { 0x3901, 0x00 }, + { 0x3902, 0x00 }, + { 0x3904, 0x00 }, + { 0x3905, 0x00 }, + { 0x3906, 0x00 }, + { 0x3907, 0x00 }, + { 0x3908, 0x00 }, + { 0x3909, 0x00 }, + { 0x3912, 0x00 }, + { 0x3930, 0x00 }, + { 0x3931, 0x00 }, + { 0x3933, 0x00 }, + { 0x3934, 0x00 }, + { 0x3935, 0x00 }, + { 0x3936, 0x00 }, + { 0x3937, 0x00 }, + { 0x30ac, 0x00 }, +}; + +static const struct imx319_reg mode_1640x922_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x08 }, + { 0x0343, 0x20 }, + { 0x0340, 0x18 }, + { 0x0341, 0x2a }, + { 0x0344, 0x00 }, + { 0x0345, 0x00 }, + { 0x0346, 0x01 }, + { 0x0347, 0x30 }, + { 0x0348, 0x0c }, + { 0x0349, 0xcf }, + { 0x034a, 0x08 }, + { 0x034b, 0x6f }, + { 0x0220, 0x00 }, + { 0x0221, 0x11 }, + { 0x0381, 0x01 }, + { 0x0383, 0x01 }, + { 0x0385, 0x01 }, + { 0x0387, 0x01 }, + { 0x0900, 0x01 }, + { 0x0901, 0x22 }, + { 0x0902, 0x0a }, + { 0x3140, 0x02 }, + { 0x3141, 0x00 }, + { 0x3f0d, 0x0a }, + { 0x3f14, 0x01 }, + { 0x3f3c, 0x02 }, + { 0x3f4d, 0x01 }, + { 0x3f4c, 0x01 }, + { 0x4254, 0x7f }, + { 0x0401, 0x00 }, + { 0x0404, 0x00 }, + { 0x0405, 0x10 }, + { 0x0408, 0x00 }, + { 0x0409, 0x00 }, + { 0x040a, 0x00 }, + { 0x040b, 0x02 }, + { 0x040c, 0x06 }, + { 0x040d, 0x68 }, + { 0x040e, 0x03 }, + { 0x040f, 0x9a }, + { 0x034c, 0x06 }, + { 0x034d, 0x68 }, + { 0x034e, 0x03 }, + { 0x034f, 0x9a }, + { 0x3261, 0x00 }, + { 0x3264, 0x00 }, + { 0x3265, 0x10 }, + { 0x0301, 0x05 }, + { 0x0303, 0x04 }, + { 0x0305, 0x04 }, + { 0x0306, 0x01 }, + { 0x0307, 0x92 }, + { 0x0309, 0x0a }, + { 0x030b, 0x02 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0xfa }, + { 0x0310, 0x00 }, + { 0x0820, 0x0f }, + { 0x0821, 0x13 }, + { 0x0822, 0x33 }, + { 0x0823, 0x33 }, + { 0x3e20, 0x01 }, + { 0x3e37, 0x00 }, + { 0x3e3b, 0x01 }, + { 0x38a3, 0x01 }, + { 0x38a8, 0x00 }, + { 0x38a9, 0x00 }, + { 0x38aa, 0x00 }, + { 0x38ab, 0x00 }, + { 0x3234, 0x00 }, + { 0x3fc1, 0x00 }, + { 0x3235, 0x00 }, + { 0x3802, 0x00 }, + { 0x3143, 0x04 }, + { 0x360a, 0x00 }, + { 0x0b00, 0x00 }, + { 0x0106, 0x00 }, + { 0x0b05, 0x01 }, + { 0x0b06, 0x01 }, + { 0x3230, 0x00 }, + { 0x3602, 0x01 }, + { 0x3607, 0x01 }, + { 0x3c00, 0x00 }, + { 0x3c01, 0xba }, + { 0x3c02, 0xc8 }, + { 0x3c03, 0xaa }, + { 0x3c04, 0x91 }, + { 0x3c05, 0x54 }, + { 0x3c06, 0x26 }, + { 0x3c07, 0x20 }, + { 0x3c08, 0x51 }, + { 0x3d80, 0x00 }, + { 0x3f50, 0x00 }, + { 0x3f56, 0x00 }, + { 0x3f57, 0x30 }, + { 0x3f78, 0x00 }, + { 0x3f79, 0x34 }, + { 0x3f7c, 0x00 }, + { 0x3f7d, 0x00 }, + { 0x3fba, 0x00 }, + { 0x3fbb, 0x00 }, + { 0xa081, 0x04 }, + { 0xe014, 0x00 }, + { 0x0202, 0x04 }, + { 0x0203, 0xf6 }, + { 0x0224, 0x01 }, + { 0x0225, 0xf4 }, + { 0x0204, 0x00 }, + { 0x0205, 0x00 }, + { 0x0216, 0x00 }, + { 0x0217, 0x00 }, + { 0x020e, 0x01 }, + { 0x020f, 0x00 }, + { 0x0210, 0x01 }, + { 0x0211, 0x00 }, + { 0x0212, 0x01 }, + { 0x0213, 0x00 }, + { 0x0214, 0x01 }, + { 0x0215, 0x00 }, + { 0x0218, 0x01 }, + { 0x0219, 0x00 }, + { 0x3614, 0x00 }, + { 0x3616, 0x0d }, + { 0x3617, 0x56 }, + { 0xb612, 0x20 }, + { 0xb613, 0x20 }, + { 0xb614, 0x20 }, + { 0xb615, 0x20 }, + { 0xb616, 0x0a }, + { 0xb617, 0x0a }, + { 0xb618, 0x20 }, + { 0xb619, 0x20 }, + { 0xb61a, 0x20 }, + { 0xb61b, 0x20 }, + { 0xb61c, 0x0a }, + { 0xb61d, 0x0a }, + { 0xb666, 0x30 }, + { 0xb667, 0x30 }, + { 0xb668, 0x30 }, + { 0xb669, 0x30 }, + { 0xb66a, 0x14 }, + { 0xb66b, 0x14 }, + { 0xb66c, 0x20 }, + { 0xb66d, 0x20 }, + { 0xb66e, 0x20 }, + { 0xb66f, 0x20 }, + { 0xb670, 0x10 }, + { 0xb671, 0x10 }, + { 0x3237, 0x00 }, + { 0x3900, 0x00 }, + { 0x3901, 0x00 }, + { 0x3902, 0x00 }, + { 0x3904, 0x00 }, + { 0x3905, 0x00 }, + { 0x3906, 0x00 }, + { 0x3907, 0x00 }, + { 0x3908, 0x00 }, + { 0x3909, 0x00 }, + { 0x3912, 0x00 }, + { 0x3930, 0x00 }, + { 0x3931, 0x00 }, + { 0x3933, 0x00 }, + { 0x3934, 0x00 }, + { 0x3935, 0x00 }, + { 0x3936, 0x00 }, + { 0x3937, 0x00 }, + { 0x30ac, 0x00 }, +}; + +static const struct imx319_reg mode_1296x736_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x08 }, + { 0x0343, 0x20 }, + { 0x0340, 0x18 }, + { 0x0341, 0x2a }, + { 0x0344, 0x00 }, + { 0x0345, 0x00 }, + { 0x0346, 0x01 }, + { 0x0347, 0xf0 }, + { 0x0348, 0x0c }, + { 0x0349, 0xcf }, + { 0x034a, 0x07 }, + { 0x034b, 0xaf }, + { 0x0220, 0x00 }, + { 0x0221, 0x11 }, + { 0x0381, 0x01 }, + { 0x0383, 0x01 }, + { 0x0385, 0x01 }, + { 0x0387, 0x01 }, + { 0x0900, 0x01 }, + { 0x0901, 0x22 }, + { 0x0902, 0x0a }, + { 0x3140, 0x02 }, + { 0x3141, 0x00 }, + { 0x3f0d, 0x0a }, + { 0x3f14, 0x01 }, + { 0x3f3c, 0x02 }, + { 0x3f4d, 0x01 }, + { 0x3f4c, 0x01 }, + { 0x4254, 0x7f }, + { 0x0401, 0x00 }, + { 0x0404, 0x00 }, + { 0x0405, 0x10 }, + { 0x0408, 0x00 }, + { 0x0409, 0xac }, + { 0x040a, 0x00 }, + { 0x040b, 0x00 }, + { 0x040c, 0x05 }, + { 0x040d, 0x10 }, + { 0x040e, 0x02 }, + { 0x040f, 0xe0 }, + { 0x034c, 0x05 }, + { 0x034d, 0x10 }, + { 0x034e, 0x02 }, + { 0x034f, 0xe0 }, + { 0x3261, 0x00 }, + { 0x3264, 0x00 }, + { 0x3265, 0x10 }, + { 0x0301, 0x05 }, + { 0x0303, 0x04 }, + { 0x0305, 0x04 }, + { 0x0306, 0x01 }, + { 0x0307, 0x92 }, + { 0x0309, 0x0a }, + { 0x030b, 0x02 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0xfa }, + { 0x0310, 0x00 }, + { 0x0820, 0x0f }, + { 0x0821, 0x13 }, + { 0x0822, 0x33 }, + { 0x0823, 0x33 }, + { 0x3e20, 0x01 }, + { 0x3e37, 0x00 }, + { 0x3e3b, 0x01 }, + { 0x38a3, 0x01 }, + { 0x38a8, 0x00 }, + { 0x38a9, 0x00 }, + { 0x38aa, 0x00 }, + { 0x38ab, 0x00 }, + { 0x3234, 0x00 }, + { 0x3fc1, 0x00 }, + { 0x3235, 0x00 }, + { 0x3802, 0x00 }, + { 0x3143, 0x04 }, + { 0x360a, 0x00 }, + { 0x0b00, 0x00 }, + { 0x0106, 0x00 }, + { 0x0b05, 0x01 }, + { 0x0b06, 0x01 }, + { 0x3230, 0x00 }, + { 0x3602, 0x01 }, + { 0x3607, 0x01 }, + { 0x3c00, 0x00 }, + { 0x3c01, 0xba }, + { 0x3c02, 0xc8 }, + { 0x3c03, 0xaa }, + { 0x3c04, 0x91 }, + { 0x3c05, 0x54 }, + { 0x3c06, 0x26 }, + { 0x3c07, 0x20 }, + { 0x3c08, 0x51 }, + { 0x3d80, 0x00 }, + { 0x3f50, 0x00 }, + { 0x3f56, 0x00 }, + { 0x3f57, 0x30 }, + { 0x3f78, 0x00 }, + { 0x3f79, 0x34 }, + { 0x3f7c, 0x00 }, + { 0x3f7d, 0x00 }, + { 0x3fba, 0x00 }, + { 0x3fbb, 0x00 }, + { 0xa081, 0x04 }, + { 0xe014, 0x00 }, + { 0x0202, 0x04 }, + { 0x0203, 0xf6 }, + { 0x0224, 0x01 }, + { 0x0225, 0xf4 }, + { 0x0204, 0x00 }, + { 0x0205, 0x00 }, + { 0x0216, 0x00 }, + { 0x0217, 0x00 }, + { 0x020e, 0x01 }, + { 0x020f, 0x00 }, + { 0x0210, 0x01 }, + { 0x0211, 0x00 }, + { 0x0212, 0x01 }, + { 0x0213, 0x00 }, + { 0x0214, 0x01 }, + { 0x0215, 0x00 }, + { 0x0218, 0x01 }, + { 0x0219, 0x00 }, + { 0x3614, 0x00 }, + { 0x3616, 0x0d }, + { 0x3617, 0x56 }, + { 0xb612, 0x20 }, + { 0xb613, 0x20 }, + { 0xb614, 0x20 }, + { 0xb615, 0x20 }, + { 0xb616, 0x0a }, + { 0xb617, 0x0a }, + { 0xb618, 0x20 }, + { 0xb619, 0x20 }, + { 0xb61a, 0x20 }, + { 0xb61b, 0x20 }, + { 0xb61c, 0x0a }, + { 0xb61d, 0x0a }, + { 0xb666, 0x30 }, + { 0xb667, 0x30 }, + { 0xb668, 0x30 }, + { 0xb669, 0x30 }, + { 0xb66a, 0x14 }, + { 0xb66b, 0x14 }, + { 0xb66c, 0x20 }, + { 0xb66d, 0x20 }, + { 0xb66e, 0x20 }, + { 0xb66f, 0x20 }, + { 0xb670, 0x10 }, + { 0xb671, 0x10 }, + { 0x3237, 0x00 }, + { 0x3900, 0x00 }, + { 0x3901, 0x00 }, + { 0x3902, 0x00 }, + { 0x3904, 0x00 }, + { 0x3905, 0x00 }, + { 0x3906, 0x00 }, + { 0x3907, 0x00 }, + { 0x3908, 0x00 }, + { 0x3909, 0x00 }, + { 0x3912, 0x00 }, + { 0x3930, 0x00 }, + { 0x3931, 0x00 }, + { 0x3933, 0x00 }, + { 0x3934, 0x00 }, + { 0x3935, 0x00 }, + { 0x3936, 0x00 }, + { 0x3937, 0x00 }, + { 0x30ac, 0x00 }, +}; + +static const struct imx319_reg mode_1280x720_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x08 }, + { 0x0343, 0x20 }, + { 0x0340, 0x18 }, + { 0x0341, 0x2a }, + { 0x0344, 0x00 }, + { 0x0345, 0x00 }, + { 0x0346, 0x02 }, + { 0x0347, 0x00 }, + { 0x0348, 0x0c }, + { 0x0349, 0xcf }, + { 0x034a, 0x07 }, + { 0x034b, 0x9f }, + { 0x0220, 0x00 }, + { 0x0221, 0x11 }, + { 0x0381, 0x01 }, + { 0x0383, 0x01 }, + { 0x0385, 0x01 }, + { 0x0387, 0x01 }, + { 0x0900, 0x01 }, + { 0x0901, 0x22 }, + { 0x0902, 0x0a }, + { 0x3140, 0x02 }, + { 0x3141, 0x00 }, + { 0x3f0d, 0x0a }, + { 0x3f14, 0x01 }, + { 0x3f3c, 0x02 }, + { 0x3f4d, 0x01 }, + { 0x3f4c, 0x01 }, + { 0x4254, 0x7f }, + { 0x0401, 0x00 }, + { 0x0404, 0x00 }, + { 0x0405, 0x10 }, + { 0x0408, 0x00 }, + { 0x0409, 0xb4 }, + { 0x040a, 0x00 }, + { 0x040b, 0x00 }, + { 0x040c, 0x05 }, + { 0x040d, 0x00 }, + { 0x040e, 0x02 }, + { 0x040f, 0xd0 }, + { 0x034c, 0x05 }, + { 0x034d, 0x00 }, + { 0x034e, 0x02 }, + { 0x034f, 0xd0 }, + { 0x3261, 0x00 }, + { 0x3264, 0x00 }, + { 0x3265, 0x10 }, + { 0x0301, 0x05 }, + { 0x0303, 0x04 }, + { 0x0305, 0x04 }, + { 0x0306, 0x01 }, + { 0x0307, 0x92 }, + { 0x0309, 0x0a }, + { 0x030b, 0x02 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0xfa }, + { 0x0310, 0x00 }, + { 0x0820, 0x0f }, + { 0x0821, 0x13 }, + { 0x0822, 0x33 }, + { 0x0823, 0x33 }, + { 0x3e20, 0x01 }, + { 0x3e37, 0x00 }, + { 0x3e3b, 0x01 }, + { 0x38a3, 0x01 }, + { 0x38a8, 0x00 }, + { 0x38a9, 0x00 }, + { 0x38aa, 0x00 }, + { 0x38ab, 0x00 }, + { 0x3234, 0x00 }, + { 0x3fc1, 0x00 }, + { 0x3235, 0x00 }, + { 0x3802, 0x00 }, + { 0x3143, 0x04 }, + { 0x360a, 0x00 }, + { 0x0b00, 0x00 }, + { 0x0106, 0x00 }, + { 0x0b05, 0x01 }, + { 0x0b06, 0x01 }, + { 0x3230, 0x00 }, + { 0x3602, 0x01 }, + { 0x3607, 0x01 }, + { 0x3c00, 0x00 }, + { 0x3c01, 0xba }, + { 0x3c02, 0xc8 }, + { 0x3c03, 0xaa }, + { 0x3c04, 0x91 }, + { 0x3c05, 0x54 }, + { 0x3c06, 0x26 }, + { 0x3c07, 0x20 }, + { 0x3c08, 0x51 }, + { 0x3d80, 0x00 }, + { 0x3f50, 0x00 }, + { 0x3f56, 0x00 }, + { 0x3f57, 0x30 }, + { 0x3f78, 0x00 }, + { 0x3f79, 0x34 }, + { 0x3f7c, 0x00 }, + { 0x3f7d, 0x00 }, + { 0x3fba, 0x00 }, + { 0x3fbb, 0x00 }, + { 0xa081, 0x04 }, + { 0xe014, 0x00 }, + { 0x0202, 0x04 }, + { 0x0203, 0xf6 }, + { 0x0224, 0x01 }, + { 0x0225, 0xf4 }, + { 0x0204, 0x00 }, + { 0x0205, 0x00 }, + { 0x0216, 0x00 }, + { 0x0217, 0x00 }, + { 0x020e, 0x01 }, + { 0x020f, 0x00 }, + { 0x0210, 0x01 }, + { 0x0211, 0x00 }, + { 0x0212, 0x01 }, + { 0x0213, 0x00 }, + { 0x0214, 0x01 }, + { 0x0215, 0x00 }, + { 0x0218, 0x01 }, + { 0x0219, 0x00 }, + { 0x3614, 0x00 }, + { 0x3616, 0x0d }, + { 0x3617, 0x56 }, + { 0xb612, 0x20 }, + { 0xb613, 0x20 }, + { 0xb614, 0x20 }, + { 0xb615, 0x20 }, + { 0xb616, 0x0a }, + { 0xb617, 0x0a }, + { 0xb618, 0x20 }, + { 0xb619, 0x20 }, + { 0xb61a, 0x20 }, + { 0xb61b, 0x20 }, + { 0xb61c, 0x0a }, + { 0xb61d, 0x0a }, + { 0xb666, 0x30 }, + { 0xb667, 0x30 }, + { 0xb668, 0x30 }, + { 0xb669, 0x30 }, + { 0xb66a, 0x14 }, + { 0xb66b, 0x14 }, + { 0xb66c, 0x20 }, + { 0xb66d, 0x20 }, + { 0xb66e, 0x20 }, + { 0xb66f, 0x20 }, + { 0xb670, 0x10 }, + { 0xb671, 0x10 }, + { 0x3237, 0x00 }, + { 0x3900, 0x00 }, + { 0x3901, 0x00 }, + { 0x3902, 0x00 }, + { 0x3904, 0x00 }, + { 0x3905, 0x00 }, + { 0x3906, 0x00 }, + { 0x3907, 0x00 }, + { 0x3908, 0x00 }, + { 0x3909, 0x00 }, + { 0x3912, 0x00 }, + { 0x3930, 0x00 }, + { 0x3931, 0x00 }, + { 0x3933, 0x00 }, + { 0x3934, 0x00 }, + { 0x3935, 0x00 }, + { 0x3936, 0x00 }, + { 0x3937, 0x00 }, + { 0x30ac, 0x00 }, +}; + +static const char * const imx319_test_pattern_menu[] = { + "Disabled", + "100% color bars", + "Solid color", + "Fade to gray color bars", + "PN9" +}; + +/* supported link frequencies */ +static const s64 link_freq_menu_items[] = { + IMX319_LINK_FREQ_DEFAULT, +}; + +/* Mode configs */ +static const struct imx319_mode supported_modes[] = { + { + .width = 3280, + .height = 2464, + .fll_def = 3242, + .fll_min = 3242, + .llp = 3968, + .link_freq_index = IMX319_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs), + .regs = mode_3280x2464_regs, + }, + }, + { + .width = 3264, + .height = 2448, + .fll_def = 3242, + .fll_min = 3242, + .llp = 3968, + .link_freq_index = IMX319_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_3264x2448_regs), + .regs = mode_3264x2448_regs, + }, + }, + { + .width = 1936, + .height = 1096, + .fll_def = 3242, + .fll_min = 3242, + .llp = 3968, + .link_freq_index = IMX319_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1936x1096_regs), + .regs = mode_1936x1096_regs, + }, + }, + { + .width = 1920, + .height = 1080, + .fll_def = 3242, + .fll_min = 3242, + .llp = 3968, + .link_freq_index = IMX319_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1920x1080_regs), + .regs = mode_1920x1080_regs, + }, + }, + { + .width = 1640, + .height = 1232, + .fll_def = 5146, + .fll_min = 5146, + .llp = 2500, + .link_freq_index = IMX319_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1640x1232_regs), + .regs = mode_1640x1232_regs, + }, + }, + { + .width = 1640, + .height = 922, + .fll_def = 5146, + .fll_min = 5146, + .llp = 2500, + .link_freq_index = IMX319_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1640x922_regs), + .regs = mode_1640x922_regs, + }, + }, + { + .width = 1296, + .height = 736, + .fll_def = 5146, + .fll_min = 5146, + .llp = 2500, + .link_freq_index = IMX319_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1296x736_regs), + .regs = mode_1296x736_regs, + }, + }, + { + .width = 1280, + .height = 720, + .fll_def = 5146, + .fll_min = 5146, + .llp = 2500, + .link_freq_index = IMX319_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1280x720_regs), + .regs = mode_1280x720_regs, + }, + }, +}; + +static inline struct imx319 *to_imx319(struct v4l2_subdev *_sd) +{ + return container_of(_sd, struct imx319, sd); +} + +/* Get bayer order based on flip setting. */ +static u32 imx319_get_format_code(struct imx319 *imx319) +{ + /* + * Only one bayer order is supported. + * It depends on the flip settings. + */ + u32 code; + static const u32 codes[2][2] = { + { MEDIA_BUS_FMT_SRGGB10_1X10, MEDIA_BUS_FMT_SGRBG10_1X10, }, + { MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SBGGR10_1X10, }, + }; + + lockdep_assert_held(&imx319->mutex); + code = codes[imx319->vflip->val][imx319->hflip->val]; + + return code; +} + +/* Read registers up to 4 at a time */ +static int imx319_read_reg(struct imx319 *imx319, u16 reg, u32 len, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx319->sd); + struct i2c_msg msgs[2]; + u8 addr_buf[2]; + u8 data_buf[4] = { 0 }; + int ret; + + if (len > 4) + return -EINVAL; + + put_unaligned_be16(reg, addr_buf); + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = ARRAY_SIZE(addr_buf); + msgs[0].buf = addr_buf; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_buf[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = get_unaligned_be32(data_buf); + + return 0; +} + +/* Write registers up to 4 at a time */ +static int imx319_write_reg(struct imx319 *imx319, u16 reg, u32 len, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx319->sd); + u8 buf[6]; + + if (len > 4) + return -EINVAL; + + put_unaligned_be16(reg, buf); + put_unaligned_be32(val << (8 * (4 - len)), buf + 2); + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +/* Write a list of registers */ +static int imx319_write_regs(struct imx319 *imx319, + const struct imx319_reg *regs, u32 len) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx319->sd); + int ret; + u32 i; + + for (i = 0; i < len; i++) { + ret = imx319_write_reg(imx319, regs[i].address, 1, regs[i].val); + if (ret) { + dev_err_ratelimited(&client->dev, + "write reg 0x%4.4x return err %d", + regs[i].address, ret); + return ret; + } + } + + return 0; +} + +/* Open sub-device */ +static int imx319_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct imx319 *imx319 = to_imx319(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + + mutex_lock(&imx319->mutex); + + /* Initialize try_fmt */ + try_fmt->width = imx319->cur_mode->width; + try_fmt->height = imx319->cur_mode->height; + try_fmt->code = imx319_get_format_code(imx319); + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&imx319->mutex); + + return 0; +} + +static int imx319_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx319 *imx319 = container_of(ctrl->handler, + struct imx319, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&imx319->sd); + s64 max; + int ret; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max = imx319->cur_mode->height + ctrl->val - 18; + __v4l2_ctrl_modify_range(imx319->exposure, + imx319->exposure->minimum, + max, imx319->exposure->step, max); + break; + } + + /* + * Applying V4L2 control value only happens + * when power is up for streaming + */ + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + /* Analog gain = 1024/(1024 - ctrl->val) times */ + ret = imx319_write_reg(imx319, IMX319_REG_ANALOG_GAIN, 2, + ctrl->val); + break; + case V4L2_CID_DIGITAL_GAIN: + ret = imx319_write_reg(imx319, IMX319_REG_DIG_GAIN_GLOBAL, 2, + ctrl->val); + break; + case V4L2_CID_EXPOSURE: + ret = imx319_write_reg(imx319, IMX319_REG_EXPOSURE, 2, + ctrl->val); + break; + case V4L2_CID_VBLANK: + /* Update FLL that meets expected vertical blanking */ + ret = imx319_write_reg(imx319, IMX319_REG_FLL, 2, + imx319->cur_mode->height + ctrl->val); + break; + case V4L2_CID_TEST_PATTERN: + ret = imx319_write_reg(imx319, IMX319_REG_TEST_PATTERN, + 2, ctrl->val); + break; + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + ret = imx319_write_reg(imx319, IMX319_REG_ORIENTATION, 1, + imx319->hflip->val | + imx319->vflip->val << 1); + break; + default: + ret = -EINVAL; + dev_info(&client->dev, "ctrl(id:0x%x,val:0x%x) is not handled", + ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops imx319_ctrl_ops = { + .s_ctrl = imx319_set_ctrl, +}; + +static int imx319_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct imx319 *imx319 = to_imx319(sd); + + if (code->index > 0) + return -EINVAL; + + mutex_lock(&imx319->mutex); + code->code = imx319_get_format_code(imx319); + mutex_unlock(&imx319->mutex); + + return 0; +} + +static int imx319_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct imx319 *imx319 = to_imx319(sd); + + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + mutex_lock(&imx319->mutex); + if (fse->code != imx319_get_format_code(imx319)) { + mutex_unlock(&imx319->mutex); + return -EINVAL; + } + mutex_unlock(&imx319->mutex); + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static void imx319_update_pad_format(struct imx319 *imx319, + const struct imx319_mode *mode, + struct v4l2_subdev_format *fmt) +{ + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = imx319_get_format_code(imx319); + fmt->format.field = V4L2_FIELD_NONE; +} + +static int imx319_do_get_pad_format(struct imx319 *imx319, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt; + struct v4l2_subdev *sd = &imx319->sd; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + fmt->format = *framefmt; + } else { + imx319_update_pad_format(imx319, imx319->cur_mode, fmt); + } + + return 0; +} + +static int imx319_get_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx319 *imx319 = to_imx319(sd); + int ret; + + mutex_lock(&imx319->mutex); + ret = imx319_do_get_pad_format(imx319, cfg, fmt); + mutex_unlock(&imx319->mutex); + + return ret; +} + +static int +imx319_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx319 *imx319 = to_imx319(sd); + const struct imx319_mode *mode; + struct v4l2_mbus_framefmt *framefmt; + s32 vblank_def; + s32 vblank_min; + s64 h_blank; + u64 pixel_rate; + u32 height; + + mutex_lock(&imx319->mutex); + + /* + * Only one bayer order is supported. + * It depends on the flip settings. + */ + fmt->format.code = imx319_get_format_code(imx319); + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, + fmt->format.width, fmt->format.height); + imx319_update_pad_format(imx319, mode, fmt); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + *framefmt = fmt->format; + } else { + imx319->cur_mode = mode; + pixel_rate = imx319->link_def_freq * 2 * 4; + do_div(pixel_rate, 10); + __v4l2_ctrl_s_ctrl_int64(imx319->pixel_rate, pixel_rate); + /* Update limits and set FPS to default */ + height = imx319->cur_mode->height; + vblank_def = imx319->cur_mode->fll_def - height; + vblank_min = imx319->cur_mode->fll_min - height; + height = IMX319_FLL_MAX - height; + __v4l2_ctrl_modify_range(imx319->vblank, vblank_min, height, 1, + vblank_def); + __v4l2_ctrl_s_ctrl(imx319->vblank, vblank_def); + h_blank = mode->llp - imx319->cur_mode->width; + /* + * Currently hblank is not changeable. + * So FPS control is done only by vblank. + */ + __v4l2_ctrl_modify_range(imx319->hblank, h_blank, + h_blank, 1, h_blank); + } + + mutex_unlock(&imx319->mutex); + + return 0; +} + +/* Start streaming */ +static int imx319_start_streaming(struct imx319 *imx319) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx319->sd); + const struct imx319_reg_list *reg_list; + int ret; + + /* Global Setting */ + reg_list = &imx319_global_setting; + ret = imx319_write_regs(imx319, reg_list->regs, reg_list->num_of_regs); + if (ret) { + dev_err(&client->dev, "failed to set global settings"); + return ret; + } + + /* Apply default values of current mode */ + reg_list = &imx319->cur_mode->reg_list; + ret = imx319_write_regs(imx319, reg_list->regs, reg_list->num_of_regs); + if (ret) { + dev_err(&client->dev, "failed to set mode"); + return ret; + } + + /* set digital gain control to all color mode */ + ret = imx319_write_reg(imx319, IMX319_REG_DPGA_USE_GLOBAL_GAIN, 1, 1); + if (ret) + return ret; + + /* Apply customized values from user */ + ret = __v4l2_ctrl_handler_setup(imx319->sd.ctrl_handler); + if (ret) + return ret; + + return imx319_write_reg(imx319, IMX319_REG_MODE_SELECT, + 1, IMX319_MODE_STREAMING); +} + +/* Stop streaming */ +static int imx319_stop_streaming(struct imx319 *imx319) +{ + return imx319_write_reg(imx319, IMX319_REG_MODE_SELECT, + 1, IMX319_MODE_STANDBY); +} + +static int imx319_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct imx319 *imx319 = to_imx319(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + + mutex_lock(&imx319->mutex); + if (imx319->streaming == enable) { + mutex_unlock(&imx319->mutex); + return 0; + } + + if (enable) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto err_unlock; + } + + /* + * Apply default & customized values + * and then start streaming. + */ + ret = imx319_start_streaming(imx319); + if (ret) + goto err_rpm_put; + } else { + imx319_stop_streaming(imx319); + pm_runtime_put(&client->dev); + } + + imx319->streaming = enable; + + /* vflip and hflip cannot change during streaming */ + __v4l2_ctrl_grab(imx319->vflip, enable); + __v4l2_ctrl_grab(imx319->hflip, enable); + + mutex_unlock(&imx319->mutex); + + return ret; + +err_rpm_put: + pm_runtime_put(&client->dev); +err_unlock: + mutex_unlock(&imx319->mutex); + + return ret; +} + +static int __maybe_unused imx319_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx319 *imx319 = to_imx319(sd); + + if (imx319->streaming) + imx319_stop_streaming(imx319); + + return 0; +} + +static int __maybe_unused imx319_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx319 *imx319 = to_imx319(sd); + int ret; + + if (imx319->streaming) { + ret = imx319_start_streaming(imx319); + if (ret) + goto error; + } + + return 0; + +error: + imx319_stop_streaming(imx319); + imx319->streaming = 0; + return ret; +} + +/* Verify chip ID */ +static int imx319_identify_module(struct imx319 *imx319) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx319->sd); + int ret; + u32 val; + + ret = imx319_read_reg(imx319, IMX319_REG_CHIP_ID, 2, &val); + if (ret) + return ret; + + if (val != IMX319_CHIP_ID) { + dev_err(&client->dev, "chip id mismatch: %x!=%x", + IMX319_CHIP_ID, val); + return -EIO; + } + + return 0; +} + +static const struct v4l2_subdev_core_ops imx319_subdev_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_video_ops imx319_video_ops = { + .s_stream = imx319_set_stream, +}; + +static const struct v4l2_subdev_pad_ops imx319_pad_ops = { + .enum_mbus_code = imx319_enum_mbus_code, + .get_fmt = imx319_get_pad_format, + .set_fmt = imx319_set_pad_format, + .enum_frame_size = imx319_enum_frame_size, +}; + +static const struct v4l2_subdev_ops imx319_subdev_ops = { + .core = &imx319_subdev_core_ops, + .video = &imx319_video_ops, + .pad = &imx319_pad_ops, +}; + +static const struct media_entity_operations imx319_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops imx319_internal_ops = { + .open = imx319_open, +}; + +/* Initialize control handlers */ +static int imx319_init_controls(struct imx319 *imx319) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx319->sd); + struct v4l2_ctrl_handler *ctrl_hdlr; + s64 exposure_max; + s64 vblank_def; + s64 vblank_min; + s64 hblank; + u64 pixel_rate; + const struct imx319_mode *mode; + u32 max; + int ret; + + ctrl_hdlr = &imx319->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10); + if (ret) + return ret; + + ctrl_hdlr->lock = &imx319->mutex; + max = ARRAY_SIZE(link_freq_menu_items) - 1; + imx319->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx319_ctrl_ops, + V4L2_CID_LINK_FREQ, max, 0, + link_freq_menu_items); + if (imx319->link_freq) + imx319->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + /* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ + pixel_rate = imx319->link_def_freq * 2 * 4; + do_div(pixel_rate, 10); + /* By default, PIXEL_RATE is read only */ + imx319->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx319_ctrl_ops, + V4L2_CID_PIXEL_RATE, pixel_rate, + pixel_rate, 1, pixel_rate); + + /* Initial vblank/hblank/exposure parameters based on current mode */ + mode = imx319->cur_mode; + vblank_def = mode->fll_def - mode->height; + vblank_min = mode->fll_min - mode->height; + imx319->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx319_ctrl_ops, + V4L2_CID_VBLANK, vblank_min, + IMX319_FLL_MAX - mode->height, + 1, vblank_def); + + hblank = mode->llp - mode->width; + imx319->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx319_ctrl_ops, + V4L2_CID_HBLANK, hblank, hblank, + 1, hblank); + if (imx319->hblank) + imx319->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + /* fll >= exposure time + adjust parameter (default value is 18) */ + exposure_max = mode->fll_def - 18; + imx319->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx319_ctrl_ops, + V4L2_CID_EXPOSURE, + IMX319_EXPOSURE_MIN, exposure_max, + IMX319_EXPOSURE_STEP, + IMX319_EXPOSURE_DEFAULT); + + imx319->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx319_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + imx319->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx319_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx319_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + IMX319_ANA_GAIN_MIN, IMX319_ANA_GAIN_MAX, + IMX319_ANA_GAIN_STEP, IMX319_ANA_GAIN_DEFAULT); + + /* Digital gain */ + v4l2_ctrl_new_std(ctrl_hdlr, &imx319_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + IMX319_DGTL_GAIN_MIN, IMX319_DGTL_GAIN_MAX, + IMX319_DGTL_GAIN_STEP, IMX319_DGTL_GAIN_DEFAULT); + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx319_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(imx319_test_pattern_menu) - 1, + 0, 0, imx319_test_pattern_menu); + if (ctrl_hdlr->error) { + ret = ctrl_hdlr->error; + dev_err(&client->dev, "control init failed: %d", ret); + goto error; + } + + imx319->sd.ctrl_handler = ctrl_hdlr; + + return 0; + +error: + v4l2_ctrl_handler_free(ctrl_hdlr); + + return ret; +} + +static struct imx319_hwcfg *imx319_get_hwcfg(struct device *dev) +{ + struct imx319_hwcfg *cfg; + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + struct fwnode_handle *ep; + struct fwnode_handle *fwnode = dev_fwnode(dev); + unsigned int i; + int ret; + + if (!fwnode) + return NULL; + + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) + return NULL; + + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + if (ret) + goto out_err; + + cfg = devm_kzalloc(dev, sizeof(*cfg), GFP_KERNEL); + if (!cfg) + goto out_err; + + ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", + &cfg->ext_clk); + if (ret) { + dev_err(dev, "can't get clock frequency"); + goto out_err; + } + + dev_dbg(dev, "ext clk: %d", cfg->ext_clk); + if (cfg->ext_clk != IMX319_EXT_CLK) { + dev_err(dev, "external clock %d is not supported", + cfg->ext_clk); + goto out_err; + } + + dev_dbg(dev, "num of link freqs: %d", bus_cfg.nr_of_link_frequencies); + if (!bus_cfg.nr_of_link_frequencies) { + dev_warn(dev, "no link frequencies defined"); + goto out_err; + } + + cfg->nr_of_link_freqs = bus_cfg.nr_of_link_frequencies; + cfg->link_freqs = devm_kcalloc(dev, + bus_cfg.nr_of_link_frequencies + 1, + sizeof(*cfg->link_freqs), GFP_KERNEL); + if (!cfg->link_freqs) + goto out_err; + + for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++) { + cfg->link_freqs[i] = bus_cfg.link_frequencies[i]; + dev_dbg(dev, "link_freq[%d] = %lld", i, cfg->link_freqs[i]); + } + + v4l2_fwnode_endpoint_free(&bus_cfg); + fwnode_handle_put(ep); + return cfg; + +out_err: + v4l2_fwnode_endpoint_free(&bus_cfg); + fwnode_handle_put(ep); + return NULL; +} + +static int imx319_probe(struct i2c_client *client) +{ + struct imx319 *imx319; + int ret; + u32 i; + + imx319 = devm_kzalloc(&client->dev, sizeof(*imx319), GFP_KERNEL); + if (!imx319) + return -ENOMEM; + + mutex_init(&imx319->mutex); + + /* Initialize subdev */ + v4l2_i2c_subdev_init(&imx319->sd, client, &imx319_subdev_ops); + + /* Check module identity */ + ret = imx319_identify_module(imx319); + if (ret) { + dev_err(&client->dev, "failed to find sensor: %d", ret); + goto error_probe; + } + + imx319->hwcfg = imx319_get_hwcfg(&client->dev); + if (!imx319->hwcfg) { + dev_err(&client->dev, "failed to get hwcfg"); + ret = -ENODEV; + goto error_probe; + } + + imx319->link_def_freq = link_freq_menu_items[IMX319_LINK_FREQ_INDEX]; + for (i = 0; i < imx319->hwcfg->nr_of_link_freqs; i++) { + if (imx319->hwcfg->link_freqs[i] == imx319->link_def_freq) { + dev_dbg(&client->dev, "link freq index %d matched", i); + break; + } + } + + if (i == imx319->hwcfg->nr_of_link_freqs) { + dev_err(&client->dev, "no link frequency supported"); + ret = -EINVAL; + goto error_probe; + } + + /* Set default mode to max resolution */ + imx319->cur_mode = &supported_modes[0]; + + ret = imx319_init_controls(imx319); + if (ret) { + dev_err(&client->dev, "failed to init controls: %d", ret); + goto error_probe; + } + + /* Initialize subdev */ + imx319->sd.internal_ops = &imx319_internal_ops; + imx319->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; + imx319->sd.entity.ops = &imx319_subdev_entity_ops; + imx319->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + /* Initialize source pad */ + imx319->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&imx319->sd.entity, 1, &imx319->pad); + if (ret) { + dev_err(&client->dev, "failed to init entity pads: %d", ret); + goto error_handler_free; + } + + ret = v4l2_async_register_subdev_sensor_common(&imx319->sd); + if (ret < 0) + goto error_media_entity; + + /* + * Device is already turned on by i2c-core with ACPI domain PM. + * Enable runtime PM and turn off the device. + */ + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); + + return 0; + +error_media_entity: + media_entity_cleanup(&imx319->sd.entity); + +error_handler_free: + v4l2_ctrl_handler_free(imx319->sd.ctrl_handler); + +error_probe: + mutex_destroy(&imx319->mutex); + + return ret; +} + +static int imx319_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx319 *imx319 = to_imx319(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + + mutex_destroy(&imx319->mutex); + + return 0; +} + +static const struct dev_pm_ops imx319_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(imx319_suspend, imx319_resume) +}; + +static const struct acpi_device_id imx319_acpi_ids[] = { + { "SONY319A" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(acpi, imx319_acpi_ids); + +static struct i2c_driver imx319_i2c_driver = { + .driver = { + .name = "imx319", + .pm = &imx319_pm_ops, + .acpi_match_table = ACPI_PTR(imx319_acpi_ids), + }, + .probe_new = imx319_probe, + .remove = imx319_remove, +}; +module_i2c_driver(imx319_i2c_driver); + +MODULE_AUTHOR("Qiu, Tianshu <tian.shu.qiu@intel.com>"); +MODULE_AUTHOR("Rapolu, Chiranjeevi <chiranjeevi.rapolu@intel.com>"); +MODULE_AUTHOR("Bingbu Cao <bingbu.cao@intel.com>"); +MODULE_AUTHOR("Yang, Hyungwoo <hyungwoo.yang@intel.com>"); +MODULE_DESCRIPTION("Sony imx319 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c new file mode 100644 index 000000000000..20c8eea5db4b --- /dev/null +++ b/drivers/media/i2c/imx355.c @@ -0,0 +1,1860 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2018 Intel Corporation + +#include <asm/unaligned.h> +#include <linux/acpi.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fwnode.h> + +#define IMX355_REG_MODE_SELECT 0x0100 +#define IMX355_MODE_STANDBY 0x00 +#define IMX355_MODE_STREAMING 0x01 + +/* Chip ID */ +#define IMX355_REG_CHIP_ID 0x0016 +#define IMX355_CHIP_ID 0x0355 + +/* V_TIMING internal */ +#define IMX355_REG_FLL 0x0340 +#define IMX355_FLL_MAX 0xffff + +/* Exposure control */ +#define IMX355_REG_EXPOSURE 0x0202 +#define IMX355_EXPOSURE_MIN 1 +#define IMX355_EXPOSURE_STEP 1 +#define IMX355_EXPOSURE_DEFAULT 0x0282 + +/* Analog gain control */ +#define IMX355_REG_ANALOG_GAIN 0x0204 +#define IMX355_ANA_GAIN_MIN 0 +#define IMX355_ANA_GAIN_MAX 960 +#define IMX355_ANA_GAIN_STEP 1 +#define IMX355_ANA_GAIN_DEFAULT 0 + +/* Digital gain control */ +#define IMX355_REG_DPGA_USE_GLOBAL_GAIN 0x3070 +#define IMX355_REG_DIG_GAIN_GLOBAL 0x020e +#define IMX355_DGTL_GAIN_MIN 256 +#define IMX355_DGTL_GAIN_MAX 4095 +#define IMX355_DGTL_GAIN_STEP 1 +#define IMX355_DGTL_GAIN_DEFAULT 256 + +/* Test Pattern Control */ +#define IMX355_REG_TEST_PATTERN 0x0600 +#define IMX355_TEST_PATTERN_DISABLED 0 +#define IMX355_TEST_PATTERN_SOLID_COLOR 1 +#define IMX355_TEST_PATTERN_COLOR_BARS 2 +#define IMX355_TEST_PATTERN_GRAY_COLOR_BARS 3 +#define IMX355_TEST_PATTERN_PN9 4 + +/* Flip Control */ +#define IMX355_REG_ORIENTATION 0x0101 + +/* default link frequency and external clock */ +#define IMX355_LINK_FREQ_DEFAULT 360000000 +#define IMX355_EXT_CLK 19200000 +#define IMX355_LINK_FREQ_INDEX 0 + +struct imx355_reg { + u16 address; + u8 val; +}; + +struct imx355_reg_list { + u32 num_of_regs; + const struct imx355_reg *regs; +}; + +/* Mode : resolution and related config&values */ +struct imx355_mode { + /* Frame width */ + u32 width; + /* Frame height */ + u32 height; + + /* V-timing */ + u32 fll_def; + u32 fll_min; + + /* H-timing */ + u32 llp; + + /* index of link frequency */ + u32 link_freq_index; + + /* Default register values */ + struct imx355_reg_list reg_list; +}; + +struct imx355_hwcfg { + u32 ext_clk; /* sensor external clk */ + s64 *link_freqs; /* CSI-2 link frequencies */ + unsigned int nr_of_link_freqs; +}; + +struct imx355 { + struct v4l2_subdev sd; + struct media_pad pad; + + struct v4l2_ctrl_handler ctrl_handler; + /* V4L2 Controls */ + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *hflip; + + /* Current mode */ + const struct imx355_mode *cur_mode; + + struct imx355_hwcfg *hwcfg; + s64 link_def_freq; /* CSI-2 link default frequency */ + + /* + * Mutex for serialized access: + * Protect sensor set pad format and start/stop streaming safely. + * Protect access to sensor v4l2 controls. + */ + struct mutex mutex; + + /* Streaming on/off */ + bool streaming; +}; + +static const struct imx355_reg imx355_global_regs[] = { + { 0x0136, 0x13 }, + { 0x0137, 0x33 }, + { 0x304e, 0x03 }, + { 0x4348, 0x16 }, + { 0x4350, 0x19 }, + { 0x4408, 0x0a }, + { 0x440c, 0x0b }, + { 0x4411, 0x5f }, + { 0x4412, 0x2c }, + { 0x4623, 0x00 }, + { 0x462c, 0x0f }, + { 0x462d, 0x00 }, + { 0x462e, 0x00 }, + { 0x4684, 0x54 }, + { 0x480a, 0x07 }, + { 0x4908, 0x07 }, + { 0x4909, 0x07 }, + { 0x490d, 0x0a }, + { 0x491e, 0x0f }, + { 0x4921, 0x06 }, + { 0x4923, 0x28 }, + { 0x4924, 0x28 }, + { 0x4925, 0x29 }, + { 0x4926, 0x29 }, + { 0x4927, 0x1f }, + { 0x4928, 0x20 }, + { 0x4929, 0x20 }, + { 0x492a, 0x20 }, + { 0x492c, 0x05 }, + { 0x492d, 0x06 }, + { 0x492e, 0x06 }, + { 0x492f, 0x06 }, + { 0x4930, 0x03 }, + { 0x4931, 0x04 }, + { 0x4932, 0x04 }, + { 0x4933, 0x05 }, + { 0x595e, 0x01 }, + { 0x5963, 0x01 }, + { 0x3030, 0x01 }, + { 0x3031, 0x01 }, + { 0x3045, 0x01 }, + { 0x4010, 0x00 }, + { 0x4011, 0x00 }, + { 0x4012, 0x00 }, + { 0x4013, 0x01 }, + { 0x68a8, 0xfe }, + { 0x68a9, 0xff }, + { 0x6888, 0x00 }, + { 0x6889, 0x00 }, + { 0x68b0, 0x00 }, + { 0x3058, 0x00 }, + { 0x305a, 0x00 }, +}; + +static const struct imx355_reg_list imx355_global_setting = { + .num_of_regs = ARRAY_SIZE(imx355_global_regs), + .regs = imx355_global_regs, +}; + +static const struct imx355_reg mode_3268x2448_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x0e }, + { 0x0343, 0x58 }, + { 0x0340, 0x0a }, + { 0x0341, 0x37 }, + { 0x0344, 0x00 }, + { 0x0345, 0x08 }, + { 0x0346, 0x00 }, + { 0x0347, 0x08 }, + { 0x0348, 0x0c }, + { 0x0349, 0xcb }, + { 0x034a, 0x09 }, + { 0x034b, 0x97 }, + { 0x0220, 0x00 }, + { 0x0222, 0x01 }, + { 0x0900, 0x00 }, + { 0x0901, 0x11 }, + { 0x0902, 0x00 }, + { 0x034c, 0x0c }, + { 0x034d, 0xc4 }, + { 0x034e, 0x09 }, + { 0x034f, 0x90 }, + { 0x0301, 0x05 }, + { 0x0303, 0x01 }, + { 0x0305, 0x02 }, + { 0x0306, 0x00 }, + { 0x0307, 0x78 }, + { 0x030b, 0x01 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0x4b }, + { 0x0310, 0x00 }, + { 0x0700, 0x00 }, + { 0x0701, 0x10 }, + { 0x0820, 0x0b }, + { 0x0821, 0x40 }, + { 0x3088, 0x04 }, + { 0x6813, 0x02 }, + { 0x6835, 0x07 }, + { 0x6836, 0x01 }, + { 0x6837, 0x04 }, + { 0x684d, 0x07 }, + { 0x684e, 0x01 }, + { 0x684f, 0x04 }, +}; + +static const struct imx355_reg mode_3264x2448_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x0e }, + { 0x0343, 0x58 }, + { 0x0340, 0x0a }, + { 0x0341, 0x37 }, + { 0x0344, 0x00 }, + { 0x0345, 0x08 }, + { 0x0346, 0x00 }, + { 0x0347, 0x08 }, + { 0x0348, 0x0c }, + { 0x0349, 0xc7 }, + { 0x034a, 0x09 }, + { 0x034b, 0x97 }, + { 0x0220, 0x00 }, + { 0x0222, 0x01 }, + { 0x0900, 0x00 }, + { 0x0901, 0x11 }, + { 0x0902, 0x00 }, + { 0x034c, 0x0c }, + { 0x034d, 0xc0 }, + { 0x034e, 0x09 }, + { 0x034f, 0x90 }, + { 0x0301, 0x05 }, + { 0x0303, 0x01 }, + { 0x0305, 0x02 }, + { 0x0306, 0x00 }, + { 0x0307, 0x78 }, + { 0x030b, 0x01 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0x4b }, + { 0x0310, 0x00 }, + { 0x0700, 0x00 }, + { 0x0701, 0x10 }, + { 0x0820, 0x0b }, + { 0x0821, 0x40 }, + { 0x3088, 0x04 }, + { 0x6813, 0x02 }, + { 0x6835, 0x07 }, + { 0x6836, 0x01 }, + { 0x6837, 0x04 }, + { 0x684d, 0x07 }, + { 0x684e, 0x01 }, + { 0x684f, 0x04 }, +}; + +static const struct imx355_reg mode_3280x2464_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x0e }, + { 0x0343, 0x58 }, + { 0x0340, 0x0a }, + { 0x0341, 0x37 }, + { 0x0344, 0x00 }, + { 0x0345, 0x00 }, + { 0x0346, 0x00 }, + { 0x0347, 0x00 }, + { 0x0348, 0x0c }, + { 0x0349, 0xcf }, + { 0x034a, 0x09 }, + { 0x034b, 0x9f }, + { 0x0220, 0x00 }, + { 0x0222, 0x01 }, + { 0x0900, 0x00 }, + { 0x0901, 0x11 }, + { 0x0902, 0x00 }, + { 0x034c, 0x0c }, + { 0x034d, 0xd0 }, + { 0x034e, 0x09 }, + { 0x034f, 0xa0 }, + { 0x0301, 0x05 }, + { 0x0303, 0x01 }, + { 0x0305, 0x02 }, + { 0x0306, 0x00 }, + { 0x0307, 0x78 }, + { 0x030b, 0x01 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0x4b }, + { 0x0310, 0x00 }, + { 0x0700, 0x00 }, + { 0x0701, 0x10 }, + { 0x0820, 0x0b }, + { 0x0821, 0x40 }, + { 0x3088, 0x04 }, + { 0x6813, 0x02 }, + { 0x6835, 0x07 }, + { 0x6836, 0x01 }, + { 0x6837, 0x04 }, + { 0x684d, 0x07 }, + { 0x684e, 0x01 }, + { 0x684f, 0x04 }, +}; + +static const struct imx355_reg mode_1940x1096_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x0e }, + { 0x0343, 0x58 }, + { 0x0340, 0x05 }, + { 0x0341, 0x1a }, + { 0x0344, 0x02 }, + { 0x0345, 0xa0 }, + { 0x0346, 0x02 }, + { 0x0347, 0xac }, + { 0x0348, 0x0a }, + { 0x0349, 0x33 }, + { 0x034a, 0x06 }, + { 0x034b, 0xf3 }, + { 0x0220, 0x00 }, + { 0x0222, 0x01 }, + { 0x0900, 0x00 }, + { 0x0901, 0x11 }, + { 0x0902, 0x00 }, + { 0x034c, 0x07 }, + { 0x034d, 0x94 }, + { 0x034e, 0x04 }, + { 0x034f, 0x48 }, + { 0x0301, 0x05 }, + { 0x0303, 0x01 }, + { 0x0305, 0x02 }, + { 0x0306, 0x00 }, + { 0x0307, 0x78 }, + { 0x030b, 0x01 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0x4b }, + { 0x0310, 0x00 }, + { 0x0700, 0x00 }, + { 0x0701, 0x10 }, + { 0x0820, 0x0b }, + { 0x0821, 0x40 }, + { 0x3088, 0x04 }, + { 0x6813, 0x02 }, + { 0x6835, 0x07 }, + { 0x6836, 0x01 }, + { 0x6837, 0x04 }, + { 0x684d, 0x07 }, + { 0x684e, 0x01 }, + { 0x684f, 0x04 }, +}; + +static const struct imx355_reg mode_1936x1096_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x0e }, + { 0x0343, 0x58 }, + { 0x0340, 0x05 }, + { 0x0341, 0x1a }, + { 0x0344, 0x02 }, + { 0x0345, 0xa0 }, + { 0x0346, 0x02 }, + { 0x0347, 0xac }, + { 0x0348, 0x0a }, + { 0x0349, 0x2f }, + { 0x034a, 0x06 }, + { 0x034b, 0xf3 }, + { 0x0220, 0x00 }, + { 0x0222, 0x01 }, + { 0x0900, 0x00 }, + { 0x0901, 0x11 }, + { 0x0902, 0x00 }, + { 0x034c, 0x07 }, + { 0x034d, 0x90 }, + { 0x034e, 0x04 }, + { 0x034f, 0x48 }, + { 0x0301, 0x05 }, + { 0x0303, 0x01 }, + { 0x0305, 0x02 }, + { 0x0306, 0x00 }, + { 0x0307, 0x78 }, + { 0x030b, 0x01 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0x4b }, + { 0x0310, 0x00 }, + { 0x0700, 0x00 }, + { 0x0701, 0x10 }, + { 0x0820, 0x0b }, + { 0x0821, 0x40 }, + { 0x3088, 0x04 }, + { 0x6813, 0x02 }, + { 0x6835, 0x07 }, + { 0x6836, 0x01 }, + { 0x6837, 0x04 }, + { 0x684d, 0x07 }, + { 0x684e, 0x01 }, + { 0x684f, 0x04 }, +}; + +static const struct imx355_reg mode_1924x1080_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x0e }, + { 0x0343, 0x58 }, + { 0x0340, 0x05 }, + { 0x0341, 0x1a }, + { 0x0344, 0x02 }, + { 0x0345, 0xa8 }, + { 0x0346, 0x02 }, + { 0x0347, 0xb4 }, + { 0x0348, 0x0a }, + { 0x0349, 0x2b }, + { 0x034a, 0x06 }, + { 0x034b, 0xeb }, + { 0x0220, 0x00 }, + { 0x0222, 0x01 }, + { 0x0900, 0x00 }, + { 0x0901, 0x11 }, + { 0x0902, 0x00 }, + { 0x034c, 0x07 }, + { 0x034d, 0x84 }, + { 0x034e, 0x04 }, + { 0x034f, 0x38 }, + { 0x0301, 0x05 }, + { 0x0303, 0x01 }, + { 0x0305, 0x02 }, + { 0x0306, 0x00 }, + { 0x0307, 0x78 }, + { 0x030b, 0x01 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0x4b }, + { 0x0310, 0x00 }, + { 0x0700, 0x00 }, + { 0x0701, 0x10 }, + { 0x0820, 0x0b }, + { 0x0821, 0x40 }, + { 0x3088, 0x04 }, + { 0x6813, 0x02 }, + { 0x6835, 0x07 }, + { 0x6836, 0x01 }, + { 0x6837, 0x04 }, + { 0x684d, 0x07 }, + { 0x684e, 0x01 }, + { 0x684f, 0x04 }, +}; + +static const struct imx355_reg mode_1920x1080_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x0e }, + { 0x0343, 0x58 }, + { 0x0340, 0x05 }, + { 0x0341, 0x1a }, + { 0x0344, 0x02 }, + { 0x0345, 0xa8 }, + { 0x0346, 0x02 }, + { 0x0347, 0xb4 }, + { 0x0348, 0x0a }, + { 0x0349, 0x27 }, + { 0x034a, 0x06 }, + { 0x034b, 0xeb }, + { 0x0220, 0x00 }, + { 0x0222, 0x01 }, + { 0x0900, 0x00 }, + { 0x0901, 0x11 }, + { 0x0902, 0x00 }, + { 0x034c, 0x07 }, + { 0x034d, 0x80 }, + { 0x034e, 0x04 }, + { 0x034f, 0x38 }, + { 0x0301, 0x05 }, + { 0x0303, 0x01 }, + { 0x0305, 0x02 }, + { 0x0306, 0x00 }, + { 0x0307, 0x78 }, + { 0x030b, 0x01 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0x4b }, + { 0x0310, 0x00 }, + { 0x0700, 0x00 }, + { 0x0701, 0x10 }, + { 0x0820, 0x0b }, + { 0x0821, 0x40 }, + { 0x3088, 0x04 }, + { 0x6813, 0x02 }, + { 0x6835, 0x07 }, + { 0x6836, 0x01 }, + { 0x6837, 0x04 }, + { 0x684d, 0x07 }, + { 0x684e, 0x01 }, + { 0x684f, 0x04 }, +}; + +static const struct imx355_reg mode_1640x1232_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x07 }, + { 0x0343, 0x2c }, + { 0x0340, 0x05 }, + { 0x0341, 0x1a }, + { 0x0344, 0x00 }, + { 0x0345, 0x00 }, + { 0x0346, 0x00 }, + { 0x0347, 0x00 }, + { 0x0348, 0x0c }, + { 0x0349, 0xcf }, + { 0x034a, 0x09 }, + { 0x034b, 0x9f }, + { 0x0220, 0x00 }, + { 0x0222, 0x01 }, + { 0x0900, 0x01 }, + { 0x0901, 0x22 }, + { 0x0902, 0x00 }, + { 0x034c, 0x06 }, + { 0x034d, 0x68 }, + { 0x034e, 0x04 }, + { 0x034f, 0xd0 }, + { 0x0301, 0x05 }, + { 0x0303, 0x01 }, + { 0x0305, 0x02 }, + { 0x0306, 0x00 }, + { 0x0307, 0x78 }, + { 0x030b, 0x01 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0x4b }, + { 0x0310, 0x00 }, + { 0x0700, 0x00 }, + { 0x0701, 0x10 }, + { 0x0820, 0x0b }, + { 0x0821, 0x40 }, + { 0x3088, 0x04 }, + { 0x6813, 0x02 }, + { 0x6835, 0x07 }, + { 0x6836, 0x01 }, + { 0x6837, 0x04 }, + { 0x684d, 0x07 }, + { 0x684e, 0x01 }, + { 0x684f, 0x04 }, +}; + +static const struct imx355_reg mode_1640x922_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x07 }, + { 0x0343, 0x2c }, + { 0x0340, 0x05 }, + { 0x0341, 0x1a }, + { 0x0344, 0x00 }, + { 0x0345, 0x00 }, + { 0x0346, 0x01 }, + { 0x0347, 0x30 }, + { 0x0348, 0x0c }, + { 0x0349, 0xcf }, + { 0x034a, 0x08 }, + { 0x034b, 0x63 }, + { 0x0220, 0x00 }, + { 0x0222, 0x01 }, + { 0x0900, 0x01 }, + { 0x0901, 0x22 }, + { 0x0902, 0x00 }, + { 0x034c, 0x06 }, + { 0x034d, 0x68 }, + { 0x034e, 0x03 }, + { 0x034f, 0x9a }, + { 0x0301, 0x05 }, + { 0x0303, 0x01 }, + { 0x0305, 0x02 }, + { 0x0306, 0x00 }, + { 0x0307, 0x78 }, + { 0x030b, 0x01 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0x4b }, + { 0x0310, 0x00 }, + { 0x0700, 0x00 }, + { 0x0701, 0x10 }, + { 0x0820, 0x0b }, + { 0x0821, 0x40 }, + { 0x3088, 0x04 }, + { 0x6813, 0x02 }, + { 0x6835, 0x07 }, + { 0x6836, 0x01 }, + { 0x6837, 0x04 }, + { 0x684d, 0x07 }, + { 0x684e, 0x01 }, + { 0x684f, 0x04 }, +}; + +static const struct imx355_reg mode_1300x736_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x07 }, + { 0x0343, 0x2c }, + { 0x0340, 0x05 }, + { 0x0341, 0x1a }, + { 0x0344, 0x01 }, + { 0x0345, 0x58 }, + { 0x0346, 0x01 }, + { 0x0347, 0xf0 }, + { 0x0348, 0x0b }, + { 0x0349, 0x7f }, + { 0x034a, 0x07 }, + { 0x034b, 0xaf }, + { 0x0220, 0x00 }, + { 0x0222, 0x01 }, + { 0x0900, 0x01 }, + { 0x0901, 0x22 }, + { 0x0902, 0x00 }, + { 0x034c, 0x05 }, + { 0x034d, 0x14 }, + { 0x034e, 0x02 }, + { 0x034f, 0xe0 }, + { 0x0301, 0x05 }, + { 0x0303, 0x01 }, + { 0x0305, 0x02 }, + { 0x0306, 0x00 }, + { 0x0307, 0x78 }, + { 0x030b, 0x01 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0x4b }, + { 0x0310, 0x00 }, + { 0x0700, 0x00 }, + { 0x0701, 0x10 }, + { 0x0820, 0x0b }, + { 0x0821, 0x40 }, + { 0x3088, 0x04 }, + { 0x6813, 0x02 }, + { 0x6835, 0x07 }, + { 0x6836, 0x01 }, + { 0x6837, 0x04 }, + { 0x684d, 0x07 }, + { 0x684e, 0x01 }, + { 0x684f, 0x04 }, +}; + +static const struct imx355_reg mode_1296x736_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x07 }, + { 0x0343, 0x2c }, + { 0x0340, 0x05 }, + { 0x0341, 0x1a }, + { 0x0344, 0x01 }, + { 0x0345, 0x58 }, + { 0x0346, 0x01 }, + { 0x0347, 0xf0 }, + { 0x0348, 0x0b }, + { 0x0349, 0x77 }, + { 0x034a, 0x07 }, + { 0x034b, 0xaf }, + { 0x0220, 0x00 }, + { 0x0222, 0x01 }, + { 0x0900, 0x01 }, + { 0x0901, 0x22 }, + { 0x0902, 0x00 }, + { 0x034c, 0x05 }, + { 0x034d, 0x10 }, + { 0x034e, 0x02 }, + { 0x034f, 0xe0 }, + { 0x0301, 0x05 }, + { 0x0303, 0x01 }, + { 0x0305, 0x02 }, + { 0x0306, 0x00 }, + { 0x0307, 0x78 }, + { 0x030b, 0x01 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0x4b }, + { 0x0310, 0x00 }, + { 0x0700, 0x00 }, + { 0x0701, 0x10 }, + { 0x0820, 0x0b }, + { 0x0821, 0x40 }, + { 0x3088, 0x04 }, + { 0x6813, 0x02 }, + { 0x6835, 0x07 }, + { 0x6836, 0x01 }, + { 0x6837, 0x04 }, + { 0x684d, 0x07 }, + { 0x684e, 0x01 }, + { 0x684f, 0x04 }, +}; + +static const struct imx355_reg mode_1284x720_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x07 }, + { 0x0343, 0x2c }, + { 0x0340, 0x05 }, + { 0x0341, 0x1a }, + { 0x0344, 0x01 }, + { 0x0345, 0x68 }, + { 0x0346, 0x02 }, + { 0x0347, 0x00 }, + { 0x0348, 0x0b }, + { 0x0349, 0x6f }, + { 0x034a, 0x07 }, + { 0x034b, 0x9f }, + { 0x0220, 0x00 }, + { 0x0222, 0x01 }, + { 0x0900, 0x01 }, + { 0x0901, 0x22 }, + { 0x0902, 0x00 }, + { 0x034c, 0x05 }, + { 0x034d, 0x04 }, + { 0x034e, 0x02 }, + { 0x034f, 0xd0 }, + { 0x0301, 0x05 }, + { 0x0303, 0x01 }, + { 0x0305, 0x02 }, + { 0x0306, 0x00 }, + { 0x0307, 0x78 }, + { 0x030b, 0x01 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0x4b }, + { 0x0310, 0x00 }, + { 0x0700, 0x00 }, + { 0x0701, 0x10 }, + { 0x0820, 0x0b }, + { 0x0821, 0x40 }, + { 0x3088, 0x04 }, + { 0x6813, 0x02 }, + { 0x6835, 0x07 }, + { 0x6836, 0x01 }, + { 0x6837, 0x04 }, + { 0x684d, 0x07 }, + { 0x684e, 0x01 }, + { 0x684f, 0x04 }, +}; + +static const struct imx355_reg mode_1280x720_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x07 }, + { 0x0343, 0x2c }, + { 0x0340, 0x05 }, + { 0x0341, 0x1a }, + { 0x0344, 0x01 }, + { 0x0345, 0x68 }, + { 0x0346, 0x02 }, + { 0x0347, 0x00 }, + { 0x0348, 0x0b }, + { 0x0349, 0x67 }, + { 0x034a, 0x07 }, + { 0x034b, 0x9f }, + { 0x0220, 0x00 }, + { 0x0222, 0x01 }, + { 0x0900, 0x01 }, + { 0x0901, 0x22 }, + { 0x0902, 0x00 }, + { 0x034c, 0x05 }, + { 0x034d, 0x00 }, + { 0x034e, 0x02 }, + { 0x034f, 0xd0 }, + { 0x0301, 0x05 }, + { 0x0303, 0x01 }, + { 0x0305, 0x02 }, + { 0x0306, 0x00 }, + { 0x0307, 0x78 }, + { 0x030b, 0x01 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0x4b }, + { 0x0310, 0x00 }, + { 0x0700, 0x00 }, + { 0x0701, 0x10 }, + { 0x0820, 0x0b }, + { 0x0821, 0x40 }, + { 0x3088, 0x04 }, + { 0x6813, 0x02 }, + { 0x6835, 0x07 }, + { 0x6836, 0x01 }, + { 0x6837, 0x04 }, + { 0x684d, 0x07 }, + { 0x684e, 0x01 }, + { 0x684f, 0x04 }, +}; + +static const struct imx355_reg mode_820x616_regs[] = { + { 0x0112, 0x0a }, + { 0x0113, 0x0a }, + { 0x0114, 0x03 }, + { 0x0342, 0x0e }, + { 0x0343, 0x58 }, + { 0x0340, 0x02 }, + { 0x0341, 0x8c }, + { 0x0344, 0x00 }, + { 0x0345, 0x00 }, + { 0x0346, 0x00 }, + { 0x0347, 0x00 }, + { 0x0348, 0x0c }, + { 0x0349, 0xcf }, + { 0x034a, 0x09 }, + { 0x034b, 0x9f }, + { 0x0220, 0x00 }, + { 0x0222, 0x01 }, + { 0x0900, 0x01 }, + { 0x0901, 0x44 }, + { 0x0902, 0x00 }, + { 0x034c, 0x03 }, + { 0x034d, 0x34 }, + { 0x034e, 0x02 }, + { 0x034f, 0x68 }, + { 0x0301, 0x05 }, + { 0x0303, 0x01 }, + { 0x0305, 0x02 }, + { 0x0306, 0x00 }, + { 0x0307, 0x78 }, + { 0x030b, 0x01 }, + { 0x030d, 0x02 }, + { 0x030e, 0x00 }, + { 0x030f, 0x4b }, + { 0x0310, 0x00 }, + { 0x0700, 0x02 }, + { 0x0701, 0x78 }, + { 0x0820, 0x0b }, + { 0x0821, 0x40 }, + { 0x3088, 0x04 }, + { 0x6813, 0x02 }, + { 0x6835, 0x07 }, + { 0x6836, 0x01 }, + { 0x6837, 0x04 }, + { 0x684d, 0x07 }, + { 0x684e, 0x01 }, + { 0x684f, 0x04 }, +}; + +static const char * const imx355_test_pattern_menu[] = { + "Disabled", + "100% color bars", + "Solid color", + "Fade to gray color bars", + "PN9" +}; + +/* supported link frequencies */ +static const s64 link_freq_menu_items[] = { + IMX355_LINK_FREQ_DEFAULT, +}; + +/* Mode configs */ +static const struct imx355_mode supported_modes[] = { + { + .width = 3280, + .height = 2464, + .fll_def = 2615, + .fll_min = 2615, + .llp = 3672, + .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs), + .regs = mode_3280x2464_regs, + }, + }, + { + .width = 3268, + .height = 2448, + .fll_def = 2615, + .fll_min = 2615, + .llp = 3672, + .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_3268x2448_regs), + .regs = mode_3268x2448_regs, + }, + }, + { + .width = 3264, + .height = 2448, + .fll_def = 2615, + .fll_min = 2615, + .llp = 3672, + .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_3264x2448_regs), + .regs = mode_3264x2448_regs, + }, + }, + { + .width = 1940, + .height = 1096, + .fll_def = 1306, + .fll_min = 1306, + .llp = 3672, + .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1940x1096_regs), + .regs = mode_1940x1096_regs, + }, + }, + { + .width = 1936, + .height = 1096, + .fll_def = 1306, + .fll_min = 1306, + .llp = 3672, + .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1936x1096_regs), + .regs = mode_1936x1096_regs, + }, + }, + { + .width = 1924, + .height = 1080, + .fll_def = 1306, + .fll_min = 1306, + .llp = 3672, + .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1924x1080_regs), + .regs = mode_1924x1080_regs, + }, + }, + { + .width = 1920, + .height = 1080, + .fll_def = 1306, + .fll_min = 1306, + .llp = 3672, + .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1920x1080_regs), + .regs = mode_1920x1080_regs, + }, + }, + { + .width = 1640, + .height = 1232, + .fll_def = 1306, + .fll_min = 1306, + .llp = 1836, + .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1640x1232_regs), + .regs = mode_1640x1232_regs, + }, + }, + { + .width = 1640, + .height = 922, + .fll_def = 1306, + .fll_min = 1306, + .llp = 1836, + .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1640x922_regs), + .regs = mode_1640x922_regs, + }, + }, + { + .width = 1300, + .height = 736, + .fll_def = 1306, + .fll_min = 1306, + .llp = 1836, + .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1300x736_regs), + .regs = mode_1300x736_regs, + }, + }, + { + .width = 1296, + .height = 736, + .fll_def = 1306, + .fll_min = 1306, + .llp = 1836, + .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1296x736_regs), + .regs = mode_1296x736_regs, + }, + }, + { + .width = 1284, + .height = 720, + .fll_def = 1306, + .fll_min = 1306, + .llp = 1836, + .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1284x720_regs), + .regs = mode_1284x720_regs, + }, + }, + { + .width = 1280, + .height = 720, + .fll_def = 1306, + .fll_min = 1306, + .llp = 1836, + .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1280x720_regs), + .regs = mode_1280x720_regs, + }, + }, + { + .width = 820, + .height = 616, + .fll_def = 652, + .fll_min = 652, + .llp = 3672, + .link_freq_index = IMX355_LINK_FREQ_INDEX, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_820x616_regs), + .regs = mode_820x616_regs, + }, + }, +}; + +static inline struct imx355 *to_imx355(struct v4l2_subdev *_sd) +{ + return container_of(_sd, struct imx355, sd); +} + +/* Get bayer order based on flip setting. */ +static u32 imx355_get_format_code(struct imx355 *imx355) +{ + /* + * Only one bayer order is supported. + * It depends on the flip settings. + */ + u32 code; + static const u32 codes[2][2] = { + { MEDIA_BUS_FMT_SRGGB10_1X10, MEDIA_BUS_FMT_SGRBG10_1X10, }, + { MEDIA_BUS_FMT_SGBRG10_1X10, MEDIA_BUS_FMT_SBGGR10_1X10, }, + }; + + lockdep_assert_held(&imx355->mutex); + code = codes[imx355->vflip->val][imx355->hflip->val]; + + return code; +} + +/* Read registers up to 4 at a time */ +static int imx355_read_reg(struct imx355 *imx355, u16 reg, u32 len, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx355->sd); + struct i2c_msg msgs[2]; + u8 addr_buf[2]; + u8 data_buf[4] = { 0 }; + int ret; + + if (len > 4) + return -EINVAL; + + put_unaligned_be16(reg, addr_buf); + /* Write register address */ + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = ARRAY_SIZE(addr_buf); + msgs[0].buf = addr_buf; + + /* Read data from register */ + msgs[1].addr = client->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = len; + msgs[1].buf = &data_buf[4 - len]; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + *val = get_unaligned_be32(data_buf); + + return 0; +} + +/* Write registers up to 4 at a time */ +static int imx355_write_reg(struct imx355 *imx355, u16 reg, u32 len, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx355->sd); + u8 buf[6]; + + if (len > 4) + return -EINVAL; + + put_unaligned_be16(reg, buf); + put_unaligned_be32(val << (8 * (4 - len)), buf + 2); + if (i2c_master_send(client, buf, len + 2) != len + 2) + return -EIO; + + return 0; +} + +/* Write a list of registers */ +static int imx355_write_regs(struct imx355 *imx355, + const struct imx355_reg *regs, u32 len) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx355->sd); + int ret; + u32 i; + + for (i = 0; i < len; i++) { + ret = imx355_write_reg(imx355, regs[i].address, 1, regs[i].val); + if (ret) { + dev_err_ratelimited(&client->dev, + "write reg 0x%4.4x return err %d", + regs[i].address, ret); + + return ret; + } + } + + return 0; +} + +/* Open sub-device */ +static int imx355_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct imx355 *imx355 = to_imx355(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + + mutex_lock(&imx355->mutex); + + /* Initialize try_fmt */ + try_fmt->width = imx355->cur_mode->width; + try_fmt->height = imx355->cur_mode->height; + try_fmt->code = imx355_get_format_code(imx355); + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&imx355->mutex); + + return 0; +} + +static int imx355_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx355 *imx355 = container_of(ctrl->handler, + struct imx355, ctrl_handler); + struct i2c_client *client = v4l2_get_subdevdata(&imx355->sd); + s64 max; + int ret; + + /* Propagate change of current control to all related controls */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update max exposure while meeting expected vblanking */ + max = imx355->cur_mode->height + ctrl->val - 10; + __v4l2_ctrl_modify_range(imx355->exposure, + imx355->exposure->minimum, + max, imx355->exposure->step, max); + break; + } + + /* + * Applying V4L2 control value only happens + * when power is up for streaming + */ + if (!pm_runtime_get_if_in_use(&client->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + /* Analog gain = 1024/(1024 - ctrl->val) times */ + ret = imx355_write_reg(imx355, IMX355_REG_ANALOG_GAIN, 2, + ctrl->val); + break; + case V4L2_CID_DIGITAL_GAIN: + ret = imx355_write_reg(imx355, IMX355_REG_DIG_GAIN_GLOBAL, 2, + ctrl->val); + break; + case V4L2_CID_EXPOSURE: + ret = imx355_write_reg(imx355, IMX355_REG_EXPOSURE, 2, + ctrl->val); + break; + case V4L2_CID_VBLANK: + /* Update FLL that meets expected vertical blanking */ + ret = imx355_write_reg(imx355, IMX355_REG_FLL, 2, + imx355->cur_mode->height + ctrl->val); + break; + case V4L2_CID_TEST_PATTERN: + ret = imx355_write_reg(imx355, IMX355_REG_TEST_PATTERN, + 2, ctrl->val); + break; + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + ret = imx355_write_reg(imx355, IMX355_REG_ORIENTATION, 1, + imx355->hflip->val | + imx355->vflip->val << 1); + break; + default: + ret = -EINVAL; + dev_info(&client->dev, "ctrl(id:0x%x,val:0x%x) is not handled", + ctrl->id, ctrl->val); + break; + } + + pm_runtime_put(&client->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops imx355_ctrl_ops = { + .s_ctrl = imx355_set_ctrl, +}; + +static int imx355_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct imx355 *imx355 = to_imx355(sd); + + if (code->index > 0) + return -EINVAL; + + mutex_lock(&imx355->mutex); + code->code = imx355_get_format_code(imx355); + mutex_unlock(&imx355->mutex); + + return 0; +} + +static int imx355_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct imx355 *imx355 = to_imx355(sd); + + if (fse->index >= ARRAY_SIZE(supported_modes)) + return -EINVAL; + + mutex_lock(&imx355->mutex); + if (fse->code != imx355_get_format_code(imx355)) { + mutex_unlock(&imx355->mutex); + return -EINVAL; + } + mutex_unlock(&imx355->mutex); + + fse->min_width = supported_modes[fse->index].width; + fse->max_width = fse->min_width; + fse->min_height = supported_modes[fse->index].height; + fse->max_height = fse->min_height; + + return 0; +} + +static void imx355_update_pad_format(struct imx355 *imx355, + const struct imx355_mode *mode, + struct v4l2_subdev_format *fmt) +{ + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = imx355_get_format_code(imx355); + fmt->format.field = V4L2_FIELD_NONE; +} + +static int imx355_do_get_pad_format(struct imx355 *imx355, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct v4l2_mbus_framefmt *framefmt; + struct v4l2_subdev *sd = &imx355->sd; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + fmt->format = *framefmt; + } else { + imx355_update_pad_format(imx355, imx355->cur_mode, fmt); + } + + return 0; +} + +static int imx355_get_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx355 *imx355 = to_imx355(sd); + int ret; + + mutex_lock(&imx355->mutex); + ret = imx355_do_get_pad_format(imx355, cfg, fmt); + mutex_unlock(&imx355->mutex); + + return ret; +} + +static int +imx355_set_pad_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct imx355 *imx355 = to_imx355(sd); + const struct imx355_mode *mode; + struct v4l2_mbus_framefmt *framefmt; + s32 vblank_def; + s32 vblank_min; + s64 h_blank; + u64 pixel_rate; + u32 height; + + mutex_lock(&imx355->mutex); + + /* + * Only one bayer order is supported. + * It depends on the flip settings. + */ + fmt->format.code = imx355_get_format_code(imx355); + + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, + fmt->format.width, fmt->format.height); + imx355_update_pad_format(imx355, mode, fmt); + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + framefmt = v4l2_subdev_get_try_format(sd, cfg, fmt->pad); + *framefmt = fmt->format; + } else { + imx355->cur_mode = mode; + pixel_rate = imx355->link_def_freq * 2 * 4; + do_div(pixel_rate, 10); + __v4l2_ctrl_s_ctrl_int64(imx355->pixel_rate, pixel_rate); + /* Update limits and set FPS to default */ + height = imx355->cur_mode->height; + vblank_def = imx355->cur_mode->fll_def - height; + vblank_min = imx355->cur_mode->fll_min - height; + height = IMX355_FLL_MAX - height; + __v4l2_ctrl_modify_range(imx355->vblank, vblank_min, height, 1, + vblank_def); + __v4l2_ctrl_s_ctrl(imx355->vblank, vblank_def); + h_blank = mode->llp - imx355->cur_mode->width; + /* + * Currently hblank is not changeable. + * So FPS control is done only by vblank. + */ + __v4l2_ctrl_modify_range(imx355->hblank, h_blank, + h_blank, 1, h_blank); + } + + mutex_unlock(&imx355->mutex); + + return 0; +} + +/* Start streaming */ +static int imx355_start_streaming(struct imx355 *imx355) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx355->sd); + const struct imx355_reg_list *reg_list; + int ret; + + /* Global Setting */ + reg_list = &imx355_global_setting; + ret = imx355_write_regs(imx355, reg_list->regs, reg_list->num_of_regs); + if (ret) { + dev_err(&client->dev, "failed to set global settings"); + return ret; + } + + /* Apply default values of current mode */ + reg_list = &imx355->cur_mode->reg_list; + ret = imx355_write_regs(imx355, reg_list->regs, reg_list->num_of_regs); + if (ret) { + dev_err(&client->dev, "failed to set mode"); + return ret; + } + + /* set digital gain control to all color mode */ + ret = imx355_write_reg(imx355, IMX355_REG_DPGA_USE_GLOBAL_GAIN, 1, 1); + if (ret) + return ret; + + /* Apply customized values from user */ + ret = __v4l2_ctrl_handler_setup(imx355->sd.ctrl_handler); + if (ret) + return ret; + + return imx355_write_reg(imx355, IMX355_REG_MODE_SELECT, + 1, IMX355_MODE_STREAMING); +} + +/* Stop streaming */ +static int imx355_stop_streaming(struct imx355 *imx355) +{ + return imx355_write_reg(imx355, IMX355_REG_MODE_SELECT, + 1, IMX355_MODE_STANDBY); +} + +static int imx355_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct imx355 *imx355 = to_imx355(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = 0; + + mutex_lock(&imx355->mutex); + if (imx355->streaming == enable) { + mutex_unlock(&imx355->mutex); + return 0; + } + + if (enable) { + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto err_unlock; + } + + /* + * Apply default & customized values + * and then start streaming. + */ + ret = imx355_start_streaming(imx355); + if (ret) + goto err_rpm_put; + } else { + imx355_stop_streaming(imx355); + pm_runtime_put(&client->dev); + } + + imx355->streaming = enable; + + /* vflip and hflip cannot change during streaming */ + __v4l2_ctrl_grab(imx355->vflip, enable); + __v4l2_ctrl_grab(imx355->hflip, enable); + + mutex_unlock(&imx355->mutex); + + return ret; + +err_rpm_put: + pm_runtime_put(&client->dev); +err_unlock: + mutex_unlock(&imx355->mutex); + + return ret; +} + +static int __maybe_unused imx355_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx355 *imx355 = to_imx355(sd); + + if (imx355->streaming) + imx355_stop_streaming(imx355); + + return 0; +} + +static int __maybe_unused imx355_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx355 *imx355 = to_imx355(sd); + int ret; + + if (imx355->streaming) { + ret = imx355_start_streaming(imx355); + if (ret) + goto error; + } + + return 0; + +error: + imx355_stop_streaming(imx355); + imx355->streaming = 0; + return ret; +} + +/* Verify chip ID */ +static int imx355_identify_module(struct imx355 *imx355) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx355->sd); + int ret; + u32 val; + + ret = imx355_read_reg(imx355, IMX355_REG_CHIP_ID, 2, &val); + if (ret) + return ret; + + if (val != IMX355_CHIP_ID) { + dev_err(&client->dev, "chip id mismatch: %x!=%x", + IMX355_CHIP_ID, val); + return -EIO; + } + return 0; +} + +static const struct v4l2_subdev_core_ops imx355_subdev_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + +static const struct v4l2_subdev_video_ops imx355_video_ops = { + .s_stream = imx355_set_stream, +}; + +static const struct v4l2_subdev_pad_ops imx355_pad_ops = { + .enum_mbus_code = imx355_enum_mbus_code, + .get_fmt = imx355_get_pad_format, + .set_fmt = imx355_set_pad_format, + .enum_frame_size = imx355_enum_frame_size, +}; + +static const struct v4l2_subdev_ops imx355_subdev_ops = { + .core = &imx355_subdev_core_ops, + .video = &imx355_video_ops, + .pad = &imx355_pad_ops, +}; + +static const struct media_entity_operations imx355_subdev_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops imx355_internal_ops = { + .open = imx355_open, +}; + +/* Initialize control handlers */ +static int imx355_init_controls(struct imx355 *imx355) +{ + struct i2c_client *client = v4l2_get_subdevdata(&imx355->sd); + struct v4l2_ctrl_handler *ctrl_hdlr; + s64 exposure_max; + s64 vblank_def; + s64 vblank_min; + s64 hblank; + u64 pixel_rate; + const struct imx355_mode *mode; + u32 max; + int ret; + + ctrl_hdlr = &imx355->ctrl_handler; + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 10); + if (ret) + return ret; + + ctrl_hdlr->lock = &imx355->mutex; + max = ARRAY_SIZE(link_freq_menu_items) - 1; + imx355->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx355_ctrl_ops, + V4L2_CID_LINK_FREQ, max, 0, + link_freq_menu_items); + if (imx355->link_freq) + imx355->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + /* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ + pixel_rate = imx355->link_def_freq * 2 * 4; + do_div(pixel_rate, 10); + /* By default, PIXEL_RATE is read only */ + imx355->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, + V4L2_CID_PIXEL_RATE, pixel_rate, + pixel_rate, 1, pixel_rate); + + /* Initialize vblank/hblank/exposure parameters based on current mode */ + mode = imx355->cur_mode; + vblank_def = mode->fll_def - mode->height; + vblank_min = mode->fll_min - mode->height; + imx355->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, + V4L2_CID_VBLANK, vblank_min, + IMX355_FLL_MAX - mode->height, + 1, vblank_def); + + hblank = mode->llp - mode->width; + imx355->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, + V4L2_CID_HBLANK, hblank, hblank, + 1, hblank); + if (imx355->hblank) + imx355->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + /* fll >= exposure time + adjust parameter (default value is 10) */ + exposure_max = mode->fll_def - 10; + imx355->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, + V4L2_CID_EXPOSURE, + IMX355_EXPOSURE_MIN, exposure_max, + IMX355_EXPOSURE_STEP, + IMX355_EXPOSURE_DEFAULT); + + imx355->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + imx355->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, + IMX355_ANA_GAIN_MIN, IMX355_ANA_GAIN_MAX, + IMX355_ANA_GAIN_STEP, IMX355_ANA_GAIN_DEFAULT); + + /* Digital gain */ + v4l2_ctrl_new_std(ctrl_hdlr, &imx355_ctrl_ops, V4L2_CID_DIGITAL_GAIN, + IMX355_DGTL_GAIN_MIN, IMX355_DGTL_GAIN_MAX, + IMX355_DGTL_GAIN_STEP, IMX355_DGTL_GAIN_DEFAULT); + + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx355_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(imx355_test_pattern_menu) - 1, + 0, 0, imx355_test_pattern_menu); + if (ctrl_hdlr->error) { + ret = ctrl_hdlr->error; + dev_err(&client->dev, "control init failed: %d", ret); + goto error; + } + + imx355->sd.ctrl_handler = ctrl_hdlr; + + return 0; + +error: + v4l2_ctrl_handler_free(ctrl_hdlr); + + return ret; +} + +static struct imx355_hwcfg *imx355_get_hwcfg(struct device *dev) +{ + struct imx355_hwcfg *cfg; + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY + }; + struct fwnode_handle *ep; + struct fwnode_handle *fwnode = dev_fwnode(dev); + unsigned int i; + int ret; + + if (!fwnode) + return NULL; + + ep = fwnode_graph_get_next_endpoint(fwnode, NULL); + if (!ep) + return NULL; + + ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + if (ret) + goto out_err; + + cfg = devm_kzalloc(dev, sizeof(*cfg), GFP_KERNEL); + if (!cfg) + goto out_err; + + ret = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", + &cfg->ext_clk); + if (ret) { + dev_err(dev, "can't get clock frequency"); + goto out_err; + } + + dev_dbg(dev, "ext clk: %d", cfg->ext_clk); + if (cfg->ext_clk != IMX355_EXT_CLK) { + dev_err(dev, "external clock %d is not supported", + cfg->ext_clk); + goto out_err; + } + + dev_dbg(dev, "num of link freqs: %d", bus_cfg.nr_of_link_frequencies); + if (!bus_cfg.nr_of_link_frequencies) { + dev_warn(dev, "no link frequencies defined"); + goto out_err; + } + + cfg->nr_of_link_freqs = bus_cfg.nr_of_link_frequencies; + cfg->link_freqs = devm_kcalloc(dev, + bus_cfg.nr_of_link_frequencies + 1, + sizeof(*cfg->link_freqs), GFP_KERNEL); + if (!cfg->link_freqs) + goto out_err; + + for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++) { + cfg->link_freqs[i] = bus_cfg.link_frequencies[i]; + dev_dbg(dev, "link_freq[%d] = %lld", i, cfg->link_freqs[i]); + } + + v4l2_fwnode_endpoint_free(&bus_cfg); + fwnode_handle_put(ep); + return cfg; + +out_err: + v4l2_fwnode_endpoint_free(&bus_cfg); + fwnode_handle_put(ep); + return NULL; +} + +static int imx355_probe(struct i2c_client *client) +{ + struct imx355 *imx355; + int ret; + u32 i; + + imx355 = devm_kzalloc(&client->dev, sizeof(*imx355), GFP_KERNEL); + if (!imx355) + return -ENOMEM; + + mutex_init(&imx355->mutex); + + /* Initialize subdev */ + v4l2_i2c_subdev_init(&imx355->sd, client, &imx355_subdev_ops); + + /* Check module identity */ + ret = imx355_identify_module(imx355); + if (ret) { + dev_err(&client->dev, "failed to find sensor: %d", ret); + goto error_probe; + } + + imx355->hwcfg = imx355_get_hwcfg(&client->dev); + if (!imx355->hwcfg) { + dev_err(&client->dev, "failed to get hwcfg"); + ret = -ENODEV; + goto error_probe; + } + + imx355->link_def_freq = link_freq_menu_items[IMX355_LINK_FREQ_INDEX]; + for (i = 0; i < imx355->hwcfg->nr_of_link_freqs; i++) { + if (imx355->hwcfg->link_freqs[i] == imx355->link_def_freq) { + dev_dbg(&client->dev, "link freq index %d matched", i); + break; + } + } + + if (i == imx355->hwcfg->nr_of_link_freqs) { + dev_err(&client->dev, "no link frequency supported"); + ret = -EINVAL; + goto error_probe; + } + + /* Set default mode to max resolution */ + imx355->cur_mode = &supported_modes[0]; + + ret = imx355_init_controls(imx355); + if (ret) { + dev_err(&client->dev, "failed to init controls: %d", ret); + goto error_probe; + } + + /* Initialize subdev */ + imx355->sd.internal_ops = &imx355_internal_ops; + imx355->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; + imx355->sd.entity.ops = &imx355_subdev_entity_ops; + imx355->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + /* Initialize source pad */ + imx355->pad.flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_pads_init(&imx355->sd.entity, 1, &imx355->pad); + if (ret) { + dev_err(&client->dev, "failed to init entity pads: %d", ret); + goto error_handler_free; + } + + ret = v4l2_async_register_subdev_sensor_common(&imx355->sd); + if (ret < 0) + goto error_media_entity; + + /* + * Device is already turned on by i2c-core with ACPI domain PM. + * Enable runtime PM and turn off the device. + */ + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_idle(&client->dev); + + return 0; + +error_media_entity: + media_entity_cleanup(&imx355->sd.entity); + +error_handler_free: + v4l2_ctrl_handler_free(imx355->sd.ctrl_handler); + +error_probe: + mutex_destroy(&imx355->mutex); + + return ret; +} + +static int imx355_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx355 *imx355 = to_imx355(sd); + + v4l2_async_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(sd->ctrl_handler); + + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + + mutex_destroy(&imx355->mutex); + + return 0; +} + +static const struct dev_pm_ops imx355_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(imx355_suspend, imx355_resume) +}; + +static const struct acpi_device_id imx355_acpi_ids[] = { + { "SONY355A" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(acpi, imx355_acpi_ids); + +static struct i2c_driver imx355_i2c_driver = { + .driver = { + .name = "imx355", + .pm = &imx355_pm_ops, + .acpi_match_table = ACPI_PTR(imx355_acpi_ids), + }, + .probe_new = imx355_probe, + .remove = imx355_remove, +}; +module_i2c_driver(imx355_i2c_driver); + +MODULE_AUTHOR("Qiu, Tianshu <tian.shu.qiu@intel.com>"); +MODULE_AUTHOR("Rapolu, Chiranjeevi <chiranjeevi.rapolu@intel.com>"); +MODULE_AUTHOR("Bingbu Cao <bingbu.cao@intel.com>"); +MODULE_AUTHOR("Yang, Hyungwoo <hyungwoo.yang@intel.com>"); +MODULE_DESCRIPTION("Sony imx355 sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/lm3560.c b/drivers/media/i2c/lm3560.c index 49c6644cbba7..f122f03bd6b7 100644 --- a/drivers/media/i2c/lm3560.c +++ b/drivers/media/i2c/lm3560.c @@ -362,7 +362,8 @@ static int lm3560_subdev_init(struct lm3560_flash *flash, v4l2_i2c_subdev_init(&flash->subdev_led[led_no], client, &lm3560_ops); flash->subdev_led[led_no].flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - strcpy(flash->subdev_led[led_no].name, led_name); + strscpy(flash->subdev_led[led_no].name, led_name, + sizeof(flash->subdev_led[led_no].name)); rval = lm3560_init_controls(flash, led_no); if (rval) goto err_out; diff --git a/drivers/media/i2c/lm3646.c b/drivers/media/i2c/lm3646.c index 7e9967af36ec..12ef2653987b 100644 --- a/drivers/media/i2c/lm3646.c +++ b/drivers/media/i2c/lm3646.c @@ -278,7 +278,8 @@ static int lm3646_subdev_init(struct lm3646_flash *flash) v4l2_i2c_subdev_init(&flash->subdev_led, client, &lm3646_ops); flash->subdev_led.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - strcpy(flash->subdev_led.name, LM3646_NAME); + strscpy(flash->subdev_led.name, LM3646_NAME, + sizeof(flash->subdev_led.name)); rval = lm3646_init_controls(flash); if (rval) goto err_out; diff --git a/drivers/media/i2c/m5mols/m5mols_core.c b/drivers/media/i2c/m5mols/m5mols_core.c index 12e79f9e32d5..b8b2bf4cbfb2 100644 --- a/drivers/media/i2c/m5mols/m5mols_core.c +++ b/drivers/media/i2c/m5mols/m5mols_core.c @@ -987,7 +987,8 @@ static int m5mols_probe(struct i2c_client *client, sd = &info->sd; v4l2_i2c_subdev_init(sd, client, &m5mols_ops); - strlcpy(sd->name, MODULE_NAME, sizeof(sd->name)); + /* Static name; NEVER use in new drivers! */ + strscpy(sd->name, MODULE_NAME, sizeof(sd->name)); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; sd->internal_ops = &m5mols_subdev_internal_ops; diff --git a/drivers/media/i2c/max2175.c b/drivers/media/i2c/max2175.c index 008a082cb8ad..7b226fadcdb8 100644 --- a/drivers/media/i2c/max2175.c +++ b/drivers/media/i2c/max2175.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Maxim Integrated MAX2175 RF to Bits tuner driver * @@ -6,15 +7,6 @@ * * Copyright (C) 2016 Maxim Integrated Products * Copyright (C) 2017 Renesas Electronics Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/clk.h> @@ -1165,7 +1157,7 @@ static int max2175_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) if (vt->index > 0) return -EINVAL; - strlcpy(vt->name, "RF", sizeof(vt->name)); + strscpy(vt->name, "RF", sizeof(vt->name)); vt->type = V4L2_TUNER_RF; vt->capability = V4L2_TUNER_CAP_1HZ | V4L2_TUNER_CAP_FREQ_BANDS; vt->rangelow = ctx->bands_rf->rangelow; diff --git a/drivers/media/i2c/max2175.h b/drivers/media/i2c/max2175.h index eb43373ce7e2..1ece587c153d 100644 --- a/drivers/media/i2c/max2175.h +++ b/drivers/media/i2c/max2175.h @@ -1,4 +1,5 @@ -/* +/* SPDX-License-Identifier: GPL-2.0 + * * Maxim Integrated MAX2175 RF to Bits tuner driver * * This driver & most of the hard coded values are based on the reference @@ -6,15 +7,6 @@ * * Copyright (C) 2016 Maxim Integrated Products * Copyright (C) 2017 Renesas Electronics Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __MAX2175_H__ diff --git a/drivers/media/i2c/msp3400-driver.c b/drivers/media/i2c/msp3400-driver.c index 3db966db83eb..c63be01059b2 100644 --- a/drivers/media/i2c/msp3400-driver.c +++ b/drivers/media/i2c/msp3400-driver.c @@ -688,7 +688,7 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) #endif if (!id) - strlcpy(client->name, "msp3400", sizeof(client->name)); + strscpy(client->name, "msp3400", sizeof(client->name)); if (msp_reset(client) == -1) { dev_dbg_lvl(&client->dev, 1, msp_debug, "msp3400 not found\n"); @@ -703,8 +703,10 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) v4l2_i2c_subdev_init(sd, client, &msp_ops); #if defined(CONFIG_MEDIA_CONTROLLER) - state->pads[IF_AUD_DEC_PAD_IF_INPUT].flags = MEDIA_PAD_FL_SINK; - state->pads[IF_AUD_DEC_PAD_OUT].flags = MEDIA_PAD_FL_SOURCE; + state->pads[MSP3400_PAD_IF_INPUT].flags = MEDIA_PAD_FL_SINK; + state->pads[MSP3400_PAD_IF_INPUT].sig_type = PAD_SIGNAL_AUDIO; + state->pads[MSP3400_PAD_OUT].flags = MEDIA_PAD_FL_SOURCE; + state->pads[MSP3400_PAD_OUT].sig_type = PAD_SIGNAL_AUDIO; sd->entity.function = MEDIA_ENT_F_IF_AUD_DECODER; diff --git a/drivers/media/i2c/msp3400-driver.h b/drivers/media/i2c/msp3400-driver.h index b6c7698bce5a..2bb9d5ff1bbd 100644 --- a/drivers/media/i2c/msp3400-driver.h +++ b/drivers/media/i2c/msp3400-driver.h @@ -52,6 +52,12 @@ extern int msp_standard; extern bool msp_dolby; extern int msp_stereo_thresh; +enum msp3400_pads { + MSP3400_PAD_IF_INPUT, + MSP3400_PAD_OUT, + MSP3400_NUM_PADS +}; + struct msp_state { struct v4l2_subdev sd; struct v4l2_ctrl_handler hdl; @@ -106,7 +112,7 @@ struct msp_state { unsigned int watch_stereo:1; #if IS_ENABLED(CONFIG_MEDIA_CONTROLLER) - struct media_pad pads[IF_AUD_DEC_PAD_NUM_PADS]; + struct media_pad pads[MSP3400_NUM_PADS]; #endif }; diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c index efda1aa95ca0..1395986a07bb 100644 --- a/drivers/media/i2c/mt9m111.c +++ b/drivers/media/i2c/mt9m111.c @@ -445,7 +445,6 @@ static int mt9m111_get_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP_DEFAULT: sel->r.left = MT9M111_MIN_DARK_COLS; sel->r.top = MT9M111_MIN_DARK_ROWS; sel->r.width = MT9M111_MAX_WIDTH; diff --git a/drivers/media/i2c/mt9t112.c b/drivers/media/i2c/mt9t112.c index af8cca984215..ef353a244e33 100644 --- a/drivers/media/i2c/mt9t112.c +++ b/drivers/media/i2c/mt9t112.c @@ -888,12 +888,6 @@ static int mt9t112_get_selection(struct v4l2_subdev *sd, sel->r.width = MAX_WIDTH; sel->r.height = MAX_HEIGHT; return 0; - case V4L2_SEL_TGT_CROP_DEFAULT: - sel->r.left = 0; - sel->r.top = 0; - sel->r.width = VGA_WIDTH; - sel->r.height = VGA_HEIGHT; - return 0; case V4L2_SEL_TGT_CROP: sel->r = priv->frame; return 0; diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index f74730d24d8f..67f69ad6ecf4 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -989,7 +989,7 @@ static struct mt9v032_platform_data * mt9v032_get_pdata(struct i2c_client *client) { struct mt9v032_platform_data *pdata = NULL; - struct v4l2_fwnode_endpoint endpoint; + struct v4l2_fwnode_endpoint endpoint = { .bus_type = 0 }; struct device_node *np; struct property *prop; diff --git a/drivers/media/i2c/noon010pc30.c b/drivers/media/i2c/noon010pc30.c index 88c498ad45df..11479e65a9ae 100644 --- a/drivers/media/i2c/noon010pc30.c +++ b/drivers/media/i2c/noon010pc30.c @@ -720,7 +720,8 @@ static int noon010_probe(struct i2c_client *client, mutex_init(&info->lock); sd = &info->sd; v4l2_i2c_subdev_init(sd, client, &noon010_ops); - strlcpy(sd->name, MODULE_NAME, sizeof(sd->name)); + /* Static name; NEVER use in new drivers! */ + strscpy(sd->name, MODULE_NAME, sizeof(sd->name)); sd->internal_ops = &noon010_subdev_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; diff --git a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c index a66f6201f53c..c8bbc1f52261 100644 --- a/drivers/media/i2c/ov13858.c +++ b/drivers/media/i2c/ov13858.c @@ -1230,7 +1230,7 @@ static int ov13858_set_ctrl(struct v4l2_ctrl *ctrl) * Applying V4L2 control value only happens * when power is up for streaming */ - if (pm_runtime_get_if_in_use(&client->dev) <= 0) + if (!pm_runtime_get_if_in_use(&client->dev)) return 0; ret = 0; @@ -1735,10 +1735,9 @@ static int ov13858_probe(struct i2c_client *client, * Device is already turned on by i2c-core with ACPI domain PM. * Enable runtime PM and turn off the device. */ - pm_runtime_get_noresume(&client->dev); pm_runtime_set_active(&client->dev); pm_runtime_enable(&client->dev); - pm_runtime_put(&client->dev); + pm_runtime_idle(&client->dev); return 0; @@ -1761,14 +1760,7 @@ static int ov13858_remove(struct i2c_client *client) media_entity_cleanup(&sd->entity); ov13858_free_controls(ov13858); - /* - * Disable runtime PM but keep the device turned on. - * i2c-core with ACPI domain PM will turn off the device. - */ - pm_runtime_get_sync(&client->dev); pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); - pm_runtime_put_noidle(&client->dev); return 0; } diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c index beb722065152..20a8853ba1e2 100644 --- a/drivers/media/i2c/ov2640.c +++ b/drivers/media/i2c/ov2640.c @@ -1010,7 +1010,6 @@ static int ov2640_get_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP_DEFAULT: case V4L2_SEL_TGT_CROP: sel->r.left = 0; sel->r.top = 0; diff --git a/drivers/media/i2c/ov2659.c b/drivers/media/i2c/ov2659.c index 4715edc8ca33..799acce803fe 100644 --- a/drivers/media/i2c/ov2659.c +++ b/drivers/media/i2c/ov2659.c @@ -1347,8 +1347,9 @@ static struct ov2659_platform_data * ov2659_get_pdata(struct i2c_client *client) { struct ov2659_platform_data *pdata; - struct v4l2_fwnode_endpoint *bus_cfg; + struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 }; struct device_node *endpoint; + int ret; if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node) return client->dev.platform_data; @@ -1357,8 +1358,9 @@ ov2659_get_pdata(struct i2c_client *client) if (!endpoint) return NULL; - bus_cfg = v4l2_fwnode_endpoint_alloc_parse(of_fwnode_handle(endpoint)); - if (IS_ERR(bus_cfg)) { + ret = v4l2_fwnode_endpoint_alloc_parse(of_fwnode_handle(endpoint), + &bus_cfg); + if (ret) { pdata = NULL; goto done; } @@ -1367,17 +1369,17 @@ ov2659_get_pdata(struct i2c_client *client) if (!pdata) goto done; - if (!bus_cfg->nr_of_link_frequencies) { + if (!bus_cfg.nr_of_link_frequencies) { dev_err(&client->dev, "link-frequencies property not found or too many\n"); pdata = NULL; goto done; } - pdata->link_frequency = bus_cfg->link_frequencies[0]; + pdata->link_frequency = bus_cfg.link_frequencies[0]; done: - v4l2_fwnode_endpoint_free(bus_cfg); + v4l2_fwnode_endpoint_free(&bus_cfg); of_node_put(endpoint); return pdata; } diff --git a/drivers/media/i2c/ov2680.c b/drivers/media/i2c/ov2680.c index f753a1c333ef..0e34e15b67b3 100644 --- a/drivers/media/i2c/ov2680.c +++ b/drivers/media/i2c/ov2680.c @@ -926,7 +926,7 @@ static int ov2680_mode_init(struct ov2680_dev *sensor) return 0; } -static int ov2680_v4l2_init(struct ov2680_dev *sensor) +static int ov2680_v4l2_register(struct ov2680_dev *sensor) { const struct v4l2_ctrl_ops *ops = &ov2680_ctrl_ops; struct ov2680_ctrls *ctrls = &sensor->ctrls; @@ -1088,26 +1088,20 @@ static int ov2680_probe(struct i2c_client *client) mutex_init(&sensor->lock); - ret = ov2680_v4l2_init(sensor); + ret = ov2680_check_id(sensor); if (ret < 0) goto lock_destroy; - ret = ov2680_check_id(sensor); + ret = ov2680_v4l2_register(sensor); if (ret < 0) - goto error_cleanup; + goto lock_destroy; dev_info(dev, "ov2680 init correctly\n"); return 0; -error_cleanup: - dev_err(dev, "ov2680 init fail: %d\n", ret); - - media_entity_cleanup(&sensor->sd.entity); - v4l2_async_unregister_subdev(&sensor->sd); - v4l2_ctrl_handler_free(&sensor->ctrls.handler); - lock_destroy: + dev_err(dev, "ov2680 init fail: %d\n", ret); mutex_destroy(&sensor->lock); return ret; diff --git a/drivers/media/i2c/ov2685.c b/drivers/media/i2c/ov2685.c index 385c1886a947..98a1f2e312b5 100644 --- a/drivers/media/i2c/ov2685.c +++ b/drivers/media/i2c/ov2685.c @@ -549,7 +549,7 @@ static int ov2685_set_ctrl(struct v4l2_ctrl *ctrl) break; } - if (pm_runtime_get_if_in_use(&client->dev) <= 0) + if (!pm_runtime_get_if_in_use(&client->dev)) return 0; switch (ctrl->id) { diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index 071f4bc240ca..eaefdb58653b 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -223,8 +223,10 @@ struct ov5640_dev { int power_count; struct v4l2_mbus_framefmt fmt; + bool pending_fmt_change; const struct ov5640_mode_info *current_mode; + const struct ov5640_mode_info *last_mode; enum ov5640_frame_rate current_fr; struct v4l2_fract frame_interval; @@ -255,7 +257,7 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) * should be identified and removed to speed register load time * over i2c. */ - +/* YUV422 UYVY VGA@30fps */ static const struct reg_value ov5640_init_setting_30fps_VGA[] = { {0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0}, {0x3103, 0x03, 0, 0}, {0x3017, 0x00, 0, 0}, {0x3018, 0x00, 0, 0}, @@ -286,10 +288,10 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = { {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x3000, 0x00, 0, 0}, {0x3002, 0x1c, 0, 0}, {0x3004, 0xff, 0, 0}, {0x3006, 0xc3, 0, 0}, - {0x300e, 0x45, 0, 0}, {0x302e, 0x08, 0, 0}, {0x4300, 0x3f, 0, 0}, + {0x302e, 0x08, 0, 0}, {0x4300, 0x3f, 0, 0}, {0x501f, 0x00, 0, 0}, {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x440e, 0x00, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x4837, 0x0a, 0, 0}, {0x4800, 0x04, 0, 0}, {0x3824, 0x02, 0, 0}, + {0x4837, 0x0a, 0, 0}, {0x3824, 0x02, 0, 0}, {0x5000, 0xa7, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x5180, 0xff, 0, 0}, {0x5181, 0xf2, 0, 0}, {0x5182, 0x00, 0, 0}, {0x5183, 0x14, 0, 0}, {0x5184, 0x25, 0, 0}, {0x5185, 0x24, 0, 0}, {0x5186, 0x09, 0, 0}, @@ -606,7 +608,7 @@ static const struct reg_value ov5640_setting_15fps_720P_1280_720[] = { {0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x72, 0, 0}, {0x3a0e, 0x01, 0, 0}, {0x3a0d, 0x02, 0, 0}, {0x3a14, 0x02, 0, 0}, {0x3a15, 0xe4, 0, 0}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x02, 0, 0}, + {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0}, }; @@ -908,6 +910,26 @@ static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg, } /* download ov5640 settings to sensor through i2c */ +static int ov5640_set_timings(struct ov5640_dev *sensor, + const struct ov5640_mode_info *mode) +{ + int ret; + + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPHO, mode->hact); + if (ret < 0) + return ret; + + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPVO, mode->vact); + if (ret < 0) + return ret; + + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HTS, mode->htot); + if (ret < 0) + return ret; + + return ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS, mode->vtot); +} + static int ov5640_load_regs(struct ov5640_dev *sensor, const struct ov5640_mode_info *mode) { @@ -935,7 +957,13 @@ static int ov5640_load_regs(struct ov5640_dev *sensor, usleep_range(1000 * delay_ms, 1000 * delay_ms + 100); } - return ret; + return ov5640_set_timings(sensor, mode); +} + +static int ov5640_set_autoexposure(struct ov5640_dev *sensor, bool on) +{ + return ov5640_mod_reg(sensor, OV5640_REG_AEC_PK_MANUAL, + BIT(0), on ? 0 : BIT(0)); } /* read exposure, in number of line periods */ @@ -994,6 +1022,18 @@ static int ov5640_get_gain(struct ov5640_dev *sensor) return gain & 0x3ff; } +static int ov5640_set_gain(struct ov5640_dev *sensor, int gain) +{ + return ov5640_write_reg16(sensor, OV5640_REG_AEC_PK_REAL_GAIN, + (u16)gain & 0x3ff); +} + +static int ov5640_set_autogain(struct ov5640_dev *sensor, bool on) +{ + return ov5640_mod_reg(sensor, OV5640_REG_AEC_PK_MANUAL, + BIT(1), on ? 0 : BIT(1)); +} + static int ov5640_set_stream_dvp(struct ov5640_dev *sensor, bool on) { int ret; @@ -1102,12 +1142,25 @@ static int ov5640_set_stream_mipi(struct ov5640_dev *sensor, bool on) { int ret; - ret = ov5640_mod_reg(sensor, OV5640_REG_MIPI_CTRL00, BIT(5), - on ? 0 : BIT(5)); - if (ret) - return ret; - ret = ov5640_write_reg(sensor, OV5640_REG_PAD_OUTPUT00, - on ? 0x00 : 0x70); + /* + * Enable/disable the MIPI interface + * + * 0x300e = on ? 0x45 : 0x40 + * + * FIXME: the sensor manual (version 2.03) reports + * [7:5] = 000 : 1 data lane mode + * [7:5] = 001 : 2 data lanes mode + * But this settings do not work, while the following ones + * have been validated for 2 data lanes mode. + * + * [7:5] = 010 : 2 data lanes mode + * [4] = 0 : Power up MIPI HS Tx + * [3] = 0 : Power up MIPI LS Rx + * [2] = 1/0 : MIPI interface enable/disable + * [1:0] = 01/00: FIXME: 'debug' + */ + ret = ov5640_write_reg(sensor, OV5640_REG_IO_MIPI_CTRL00, + on ? 0x45 : 0x40); if (ret) return ret; @@ -1331,7 +1384,7 @@ static int ov5640_set_ae_target(struct ov5640_dev *sensor, int target) return ov5640_write_reg(sensor, OV5640_REG_AEC_CTRL1F, fast_low); } -static int ov5640_binning_on(struct ov5640_dev *sensor) +static int ov5640_get_binning(struct ov5640_dev *sensor) { u8 temp; int ret; @@ -1339,8 +1392,8 @@ static int ov5640_binning_on(struct ov5640_dev *sensor) ret = ov5640_read_reg(sensor, OV5640_REG_TIMING_TC_REG21, &temp); if (ret) return ret; - temp &= 0xfe; - return temp ? 1 : 0; + + return temp & BIT(0); } static int ov5640_set_binning(struct ov5640_dev *sensor, bool enable) @@ -1385,30 +1438,6 @@ static int ov5640_set_virtual_channel(struct ov5640_dev *sensor) return ov5640_write_reg(sensor, OV5640_REG_DEBUG_MODE, temp); } -static int ov5640_set_timings(struct ov5640_dev *sensor, - const struct ov5640_mode_info *mode) -{ - int ret; - - ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPHO, mode->hact); - if (ret < 0) - return ret; - - ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPVO, mode->vact); - if (ret < 0) - return ret; - - ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HTS, mode->htot); - if (ret < 0) - return ret; - - ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS, mode->vtot); - if (ret < 0) - return ret; - - return 0; -} - static const struct ov5640_mode_info * ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr, int width, int height, bool nearest) @@ -1450,7 +1479,7 @@ static int ov5640_set_mode_exposure_calc(struct ov5640_dev *sensor, if (ret < 0) return ret; prev_shutter = ret; - ret = ov5640_binning_on(sensor); + ret = ov5640_get_binning(sensor); if (ret < 0) return ret; if (ret && mode->id != OV5640_MODE_720P_1280_720 && @@ -1571,7 +1600,7 @@ static int ov5640_set_mode_exposure_calc(struct ov5640_dev *sensor, } /* set capture gain */ - ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.gain, cap_gain16); + ret = ov5640_set_gain(sensor, cap_gain16); if (ret) return ret; @@ -1584,7 +1613,7 @@ static int ov5640_set_mode_exposure_calc(struct ov5640_dev *sensor, } /* set exposure */ - return __v4l2_ctrl_s_ctrl(sensor->ctrls.exposure, cap_shutter); + return ov5640_set_exposure(sensor, cap_shutter); } /* @@ -1592,53 +1621,45 @@ static int ov5640_set_mode_exposure_calc(struct ov5640_dev *sensor, * change mode directly */ static int ov5640_set_mode_direct(struct ov5640_dev *sensor, - const struct ov5640_mode_info *mode, - s32 exposure) + const struct ov5640_mode_info *mode) { - int ret; - if (!mode->reg_data) return -EINVAL; /* Write capture setting */ - ret = ov5640_load_regs(sensor, mode); - if (ret < 0) - return ret; - - /* turn auto gain/exposure back on for direct mode */ - ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_gain, 1); - if (ret) - return ret; - - return __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_exp, exposure); + return ov5640_load_regs(sensor, mode); } -static int ov5640_set_mode(struct ov5640_dev *sensor, - const struct ov5640_mode_info *orig_mode) +static int ov5640_set_mode(struct ov5640_dev *sensor) { const struct ov5640_mode_info *mode = sensor->current_mode; + const struct ov5640_mode_info *orig_mode = sensor->last_mode; enum ov5640_downsize_mode dn_mode, orig_dn_mode; - s32 exposure; + bool auto_gain = sensor->ctrls.auto_gain->val == 1; + bool auto_exp = sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO; int ret; dn_mode = mode->dn_mode; orig_dn_mode = orig_mode->dn_mode; /* auto gain and exposure must be turned off when changing modes */ - ret = __v4l2_ctrl_s_ctrl(sensor->ctrls.auto_gain, 0); - if (ret) - return ret; + if (auto_gain) { + ret = ov5640_set_autogain(sensor, false); + if (ret) + return ret; + } - exposure = sensor->ctrls.auto_exp->val; - ret = ov5640_set_exposure(sensor, V4L2_EXPOSURE_MANUAL); - if (ret) - return ret; + if (auto_exp) { + ret = ov5640_set_autoexposure(sensor, false); + if (ret) + goto restore_auto_gain; + } if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) || (dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) { /* * change between subsampling and scaling - * go through exposure calucation + * go through exposure calculation */ ret = ov5640_set_mode_exposure_calc(sensor, mode); } else { @@ -1646,15 +1667,16 @@ static int ov5640_set_mode(struct ov5640_dev *sensor, * change inside subsampling or scaling * download firmware directly */ - ret = ov5640_set_mode_direct(sensor, mode, exposure); + ret = ov5640_set_mode_direct(sensor, mode); } - if (ret < 0) - return ret; + goto restore_auto_exp_gain; - ret = ov5640_set_timings(sensor, mode); - if (ret < 0) - return ret; + /* restore auto gain and exposure */ + if (auto_gain) + ov5640_set_autogain(sensor, true); + if (auto_exp) + ov5640_set_autoexposure(sensor, true); ret = ov5640_set_binning(sensor, dn_mode != SCALING); if (ret < 0) @@ -1673,8 +1695,18 @@ static int ov5640_set_mode(struct ov5640_dev *sensor, return ret; sensor->pending_mode_change = false; + sensor->last_mode = mode; return 0; + +restore_auto_exp_gain: + if (auto_exp) + ov5640_set_autoexposure(sensor, true); +restore_auto_gain: + if (auto_gain) + ov5640_set_autogain(sensor, true); + + return ret; } static int ov5640_set_framefmt(struct ov5640_dev *sensor, @@ -1689,6 +1721,7 @@ static int ov5640_restore_mode(struct ov5640_dev *sensor) ret = ov5640_load_regs(sensor, &ov5640_mode_init_data); if (ret < 0) return ret; + sensor->last_mode = &ov5640_mode_init_data; ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x3f, (ilog2(OV5640_SCLK2X_ROOT_DIVIDER_DEFAULT) << 2) | @@ -1697,7 +1730,7 @@ static int ov5640_restore_mode(struct ov5640_dev *sensor) return ret; /* now restore the last capture mode */ - ret = ov5640_set_mode(sensor, &ov5640_mode_init_data); + ret = ov5640_set_mode(sensor); if (ret < 0) return ret; @@ -1786,23 +1819,69 @@ static int ov5640_set_power(struct ov5640_dev *sensor, bool on) if (ret) goto power_off; - if (sensor->ep.bus_type == V4L2_MBUS_CSI2) { - /* - * start streaming briefly followed by stream off in - * order to coax the clock lane into LP-11 state. - */ - ret = ov5640_set_stream_mipi(sensor, true); - if (ret) - goto power_off; - usleep_range(1000, 2000); - ret = ov5640_set_stream_mipi(sensor, false); - if (ret) - goto power_off; + /* We're done here for DVP bus, while CSI-2 needs setup. */ + if (sensor->ep.bus_type != V4L2_MBUS_CSI2_DPHY) + return 0; + + /* + * Power up MIPI HS Tx and LS Rx; 2 data lanes mode + * + * 0x300e = 0x40 + * [7:5] = 010 : 2 data lanes mode (see FIXME note in + * "ov5640_set_stream_mipi()") + * [4] = 0 : Power up MIPI HS Tx + * [3] = 0 : Power up MIPI LS Rx + * [2] = 0 : MIPI interface disabled + */ + ret = ov5640_write_reg(sensor, + OV5640_REG_IO_MIPI_CTRL00, 0x40); + if (ret) + goto power_off; + + /* + * Gate clock and set LP11 in 'no packets mode' (idle) + * + * 0x4800 = 0x24 + * [5] = 1 : Gate clock when 'no packets' + * [2] = 1 : MIPI bus in LP11 when 'no packets' + */ + ret = ov5640_write_reg(sensor, + OV5640_REG_MIPI_CTRL00, 0x24); + if (ret) + goto power_off; + + /* + * Set data lanes and clock in LP11 when 'sleeping' + * + * 0x3019 = 0x70 + * [6] = 1 : MIPI data lane 2 in LP11 when 'sleeping' + * [5] = 1 : MIPI data lane 1 in LP11 when 'sleeping' + * [4] = 1 : MIPI clock lane in LP11 when 'sleeping' + */ + ret = ov5640_write_reg(sensor, + OV5640_REG_PAD_OUTPUT00, 0x70); + if (ret) + goto power_off; + + /* Give lanes some time to coax into LP11 state. */ + usleep_range(500, 1000); + + } else { + if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY) { + /* Reset MIPI bus settings to their default values. */ + ov5640_write_reg(sensor, + OV5640_REG_IO_MIPI_CTRL00, 0x58); + ov5640_write_reg(sensor, + OV5640_REG_MIPI_CTRL00, 0x04); + ov5640_write_reg(sensor, + OV5640_REG_PAD_OUTPUT00, 0x00); } - return 0; + ov5640_set_power_off(sensor); } + return 0; + power_off: ov5640_set_power_off(sensor); return ret; @@ -1968,9 +2047,12 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd, if (new_mode != sensor->current_mode) { sensor->current_mode = new_mode; - sensor->fmt = *mbus_fmt; sensor->pending_mode_change = true; } + if (mbus_fmt->code != sensor->fmt.code) { + sensor->fmt = *mbus_fmt; + sensor->pending_fmt_change = true; + } out: mutex_unlock(&sensor->lock); return ret; @@ -2137,20 +2219,20 @@ static int ov5640_set_ctrl_white_balance(struct ov5640_dev *sensor, int awb) return ret; } -static int ov5640_set_ctrl_exposure(struct ov5640_dev *sensor, int exp) +static int ov5640_set_ctrl_exposure(struct ov5640_dev *sensor, + enum v4l2_exposure_auto_type auto_exposure) { struct ov5640_ctrls *ctrls = &sensor->ctrls; - bool auto_exposure = (exp == V4L2_EXPOSURE_AUTO); + bool auto_exp = (auto_exposure == V4L2_EXPOSURE_AUTO); int ret = 0; if (ctrls->auto_exp->is_new) { - ret = ov5640_mod_reg(sensor, OV5640_REG_AEC_PK_MANUAL, - BIT(0), auto_exposure ? 0 : BIT(0)); + ret = ov5640_set_autoexposure(sensor, auto_exp); if (ret) return ret; } - if (!auto_exposure && ctrls->exposure->is_new) { + if (!auto_exp && ctrls->exposure->is_new) { u16 max_exp; ret = ov5640_read_reg16(sensor, OV5640_REG_AEC_PK_VTS, @@ -2170,25 +2252,19 @@ static int ov5640_set_ctrl_exposure(struct ov5640_dev *sensor, int exp) return ret; } -static int ov5640_set_ctrl_gain(struct ov5640_dev *sensor, int auto_gain) +static int ov5640_set_ctrl_gain(struct ov5640_dev *sensor, bool auto_gain) { struct ov5640_ctrls *ctrls = &sensor->ctrls; int ret = 0; if (ctrls->auto_gain->is_new) { - ret = ov5640_mod_reg(sensor, OV5640_REG_AEC_PK_MANUAL, - BIT(1), - ctrls->auto_gain->val ? 0 : BIT(1)); + ret = ov5640_set_autogain(sensor, auto_gain); if (ret) return ret; } - if (!auto_gain && ctrls->gain->is_new) { - u16 gain = (u16)ctrls->gain->val; - - ret = ov5640_write_reg16(sensor, OV5640_REG_AEC_PK_REAL_GAIN, - gain & 0x3ff); - } + if (!auto_gain && ctrls->gain->is_new) + ret = ov5640_set_gain(sensor, ctrls->gain->val); return ret; } @@ -2261,16 +2337,12 @@ static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_AUTOGAIN: - if (!ctrl->val) - return 0; val = ov5640_get_gain(sensor); if (val < 0) return val; sensor->ctrls.gain->val = val; break; case V4L2_CID_EXPOSURE_AUTO: - if (ctrl->val == V4L2_EXPOSURE_MANUAL) - return 0; val = ov5640_get_exposure(sensor); if (val < 0) return val; @@ -2501,8 +2573,6 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd, if (frame_rate < 0) frame_rate = OV5640_15_FPS; - sensor->current_fr = frame_rate; - sensor->frame_interval = fi->interval; mode = ov5640_find_mode(sensor, frame_rate, mode->hact, mode->vact, true); if (!mode) { @@ -2510,7 +2580,10 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd, goto out; } - if (mode != sensor->current_mode) { + if (mode != sensor->current_mode || + frame_rate != sensor->current_fr) { + sensor->current_fr = frame_rate; + sensor->frame_interval = fi->interval; sensor->current_mode = mode; sensor->pending_mode_change = true; } @@ -2541,16 +2614,19 @@ static int ov5640_s_stream(struct v4l2_subdev *sd, int enable) if (sensor->streaming == !enable) { if (enable && sensor->pending_mode_change) { - ret = ov5640_set_mode(sensor, sensor->current_mode); + ret = ov5640_set_mode(sensor); if (ret) goto out; + } + if (enable && sensor->pending_fmt_change) { ret = ov5640_set_framefmt(sensor, &sensor->fmt); if (ret) goto out; + sensor->pending_fmt_change = false; } - if (sensor->ep.bus_type == V4L2_MBUS_CSI2) + if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY) ret = ov5640_set_stream_mipi(sensor, enable); else ret = ov5640_set_stream_dvp(sensor, enable); @@ -2642,9 +2718,14 @@ static int ov5640_probe(struct i2c_client *client, return -ENOMEM; sensor->i2c_client = client; + + /* + * default init sequence initialize sensor to + * YUV422 UYVY VGA@30fps + */ fmt = &sensor->fmt; - fmt->code = ov5640_formats[0].code; - fmt->colorspace = ov5640_formats[0].colorspace; + fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; + fmt->colorspace = V4L2_COLORSPACE_SRGB; fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); @@ -2656,7 +2737,7 @@ static int ov5640_probe(struct i2c_client *client, sensor->current_fr = OV5640_30_FPS; sensor->current_mode = &ov5640_mode_data[OV5640_30_FPS][OV5640_MODE_VGA_640_480]; - sensor->pending_mode_change = true; + sensor->last_mode = sensor->current_mode; sensor->ae_target = 52; diff --git a/drivers/media/i2c/ov5645.c b/drivers/media/i2c/ov5645.c index 1722cdab0daf..5eba8dd7222b 100644 --- a/drivers/media/i2c/ov5645.c +++ b/drivers/media/i2c/ov5645.c @@ -1127,7 +1127,7 @@ static int ov5645_probe(struct i2c_client *client, return ret; } - if (ov5645->ep.bus_type != V4L2_MBUS_CSI2) { + if (ov5645->ep.bus_type != V4L2_MBUS_CSI2_DPHY) { dev_err(dev, "invalid bus type, must be CSI2\n"); return -EINVAL; } diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index da39c49de503..4589631798c9 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -532,7 +532,7 @@ static const struct v4l2_subdev_internal_ops ov5647_subdev_internal_ops = { static int ov5647_parse_dt(struct device_node *np) { - struct v4l2_fwnode_endpoint bus_cfg; + struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 }; struct device_node *ep; int ret; diff --git a/drivers/media/i2c/ov5670.c b/drivers/media/i2c/ov5670.c index 7b7c74d77370..041fcbb4eebd 100644 --- a/drivers/media/i2c/ov5670.c +++ b/drivers/media/i2c/ov5670.c @@ -2016,7 +2016,7 @@ static int ov5670_set_ctrl(struct v4l2_ctrl *ctrl) } /* V4L2 controls values will be applied only when power is already up */ - if (pm_runtime_get_if_in_use(&client->dev) <= 0) + if (!pm_runtime_get_if_in_use(&client->dev)) return 0; switch (ctrl->id) { @@ -2504,10 +2504,9 @@ static int ov5670_probe(struct i2c_client *client) * Device is already turned on by i2c-core with ACPI domain PM. * Enable runtime PM and turn off the device. */ - pm_runtime_get_noresume(&client->dev); pm_runtime_set_active(&client->dev); pm_runtime_enable(&client->dev); - pm_runtime_put(&client->dev); + pm_runtime_idle(&client->dev); return 0; @@ -2536,14 +2535,7 @@ static int ov5670_remove(struct i2c_client *client) v4l2_ctrl_handler_free(sd->ctrl_handler); mutex_destroy(&ov5670->mutex); - /* - * Disable runtime PM but keep the device turned on. - * i2c-core with ACPI domain PM will turn off the device. - */ - pm_runtime_get_sync(&client->dev); pm_runtime_disable(&client->dev); - pm_runtime_set_suspended(&client->dev); - pm_runtime_put_noidle(&client->dev); return 0; } diff --git a/drivers/media/i2c/ov5695.c b/drivers/media/i2c/ov5695.c index 9a80decd93d3..5d107c53364d 100644 --- a/drivers/media/i2c/ov5695.c +++ b/drivers/media/i2c/ov5695.c @@ -1110,7 +1110,7 @@ static int ov5695_set_ctrl(struct v4l2_ctrl *ctrl) break; } - if (pm_runtime_get_if_in_use(&client->dev) <= 0) + if (!pm_runtime_get_if_in_use(&client->dev)) return 0; switch (ctrl->id) { diff --git a/drivers/media/i2c/ov6650.c b/drivers/media/i2c/ov6650.c index 17a34b4a819d..5d1b218bb7f0 100644 --- a/drivers/media/i2c/ov6650.c +++ b/drivers/media/i2c/ov6650.c @@ -449,7 +449,6 @@ static int ov6650_get_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP_DEFAULT: sel->r.left = DEF_HSTRT << 1; sel->r.top = DEF_VSTRT << 1; sel->r.width = W_CIF; diff --git a/drivers/media/i2c/ov7251.c b/drivers/media/i2c/ov7251.c index d3ebb7529fca..0c10203f822b 100644 --- a/drivers/media/i2c/ov7251.c +++ b/drivers/media/i2c/ov7251.c @@ -1279,9 +1279,9 @@ static int ov7251_probe(struct i2c_client *client) return ret; } - if (ov7251->ep.bus_type != V4L2_MBUS_CSI2) { + if (ov7251->ep.bus_type != V4L2_MBUS_CSI2_DPHY) { dev_err(dev, "invalid bus type (%u), must be CSI2 (%u)\n", - ov7251->ep.bus_type, V4L2_MBUS_CSI2); + ov7251->ep.bus_type, V4L2_MBUS_CSI2_DPHY); return -EINVAL; } diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 31bf577b0bd3..bc68a3a5b4ec 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -1728,7 +1728,7 @@ static int ov7670_parse_dt(struct device *dev, struct ov7670_info *info) { struct fwnode_handle *fwnode = dev_fwnode(dev); - struct v4l2_fwnode_endpoint bus_cfg; + struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 }; struct fwnode_handle *ep; int ret; @@ -1808,17 +1808,24 @@ static int ov7670_probe(struct i2c_client *client, info->pclk_hb_disable = true; } - info->clk = devm_clk_get(&client->dev, "xclk"); - if (IS_ERR(info->clk)) - return PTR_ERR(info->clk); - ret = clk_prepare_enable(info->clk); - if (ret) - return ret; + info->clk = devm_clk_get(&client->dev, "xclk"); /* optional */ + if (IS_ERR(info->clk)) { + ret = PTR_ERR(info->clk); + if (ret == -ENOENT) + info->clk = NULL; + else + return ret; + } + if (info->clk) { + ret = clk_prepare_enable(info->clk); + if (ret) + return ret; - info->clock_speed = clk_get_rate(info->clk) / 1000000; - if (info->clock_speed < 10 || info->clock_speed > 48) { - ret = -EINVAL; - goto clk_disable; + info->clock_speed = clk_get_rate(info->clk) / 1000000; + if (info->clock_speed < 10 || info->clock_speed > 48) { + ret = -EINVAL; + goto clk_disable; + } } ret = ov7670_init_gpio(client, info); diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c index 7158c31d8403..fefff7fd7d68 100644 --- a/drivers/media/i2c/ov772x.c +++ b/drivers/media/i2c/ov772x.c @@ -21,6 +21,7 @@ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/regmap.h> #include <linux/slab.h> #include <linux/v4l2-mediabus.h> #include <linux/videodev2.h> @@ -414,6 +415,7 @@ struct ov772x_priv { struct v4l2_subdev subdev; struct v4l2_ctrl_handler hdl; struct clk *clk; + struct regmap *regmap; struct ov772x_camera_info *info; struct gpio_desc *pwdn_gpio; struct gpio_desc *rstb_gpio; @@ -549,51 +551,18 @@ static struct ov772x_priv *to_ov772x(struct v4l2_subdev *sd) return container_of(sd, struct ov772x_priv, subdev); } -static int ov772x_read(struct i2c_client *client, u8 addr) -{ - int ret; - u8 val; - - ret = i2c_master_send(client, &addr, 1); - if (ret < 0) - return ret; - ret = i2c_master_recv(client, &val, 1); - if (ret < 0) - return ret; - - return val; -} - -static inline int ov772x_write(struct i2c_client *client, u8 addr, u8 value) -{ - return i2c_smbus_write_byte_data(client, addr, value); -} - -static int ov772x_mask_set(struct i2c_client *client, u8 command, u8 mask, - u8 set) -{ - s32 val = ov772x_read(client, command); - - if (val < 0) - return val; - - val &= ~mask; - val |= set & mask; - - return ov772x_write(client, command, val); -} - -static int ov772x_reset(struct i2c_client *client) +static int ov772x_reset(struct ov772x_priv *priv) { int ret; - ret = ov772x_write(client, COM7, SCCB_RESET); + ret = regmap_write(priv->regmap, COM7, SCCB_RESET); if (ret < 0) return ret; usleep_range(1000, 5000); - return ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE); + return regmap_update_bits(priv->regmap, COM2, SOFT_SLEEP_MODE, + SOFT_SLEEP_MODE); } /* @@ -611,8 +580,8 @@ static int ov772x_s_stream(struct v4l2_subdev *sd, int enable) if (priv->streaming == enable) goto done; - ret = ov772x_mask_set(client, COM2, SOFT_SLEEP_MODE, - enable ? 0 : SOFT_SLEEP_MODE); + ret = regmap_update_bits(priv->regmap, COM2, SOFT_SLEEP_MODE, + enable ? 0 : SOFT_SLEEP_MODE); if (ret) goto done; @@ -657,7 +626,6 @@ static int ov772x_set_frame_rate(struct ov772x_priv *priv, const struct ov772x_color_format *cfmt, const struct ov772x_win_size *win) { - struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); unsigned long fin = clk_get_rate(priv->clk); unsigned int best_diff; unsigned int fsize; @@ -723,11 +691,11 @@ static int ov772x_set_frame_rate(struct ov772x_priv *priv, } } - ret = ov772x_write(client, COM4, com4 | COM4_RESERVED); + ret = regmap_write(priv->regmap, COM4, com4 | COM4_RESERVED); if (ret < 0) return ret; - ret = ov772x_write(client, CLKRC, clkrc | CLKRC_RESERVED); + ret = regmap_write(priv->regmap, CLKRC, clkrc | CLKRC_RESERVED); if (ret < 0) return ret; @@ -788,8 +756,7 @@ static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl) { struct ov772x_priv *priv = container_of(ctrl->handler, struct ov772x_priv, hdl); - struct v4l2_subdev *sd = &priv->subdev; - struct i2c_client *client = v4l2_get_subdevdata(sd); + struct regmap *regmap = priv->regmap; int ret = 0; u8 val; @@ -808,27 +775,27 @@ static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl) val = ctrl->val ? VFLIP_IMG : 0x00; if (priv->info && (priv->info->flags & OV772X_FLAG_VFLIP)) val ^= VFLIP_IMG; - return ov772x_mask_set(client, COM3, VFLIP_IMG, val); + return regmap_update_bits(regmap, COM3, VFLIP_IMG, val); case V4L2_CID_HFLIP: val = ctrl->val ? HFLIP_IMG : 0x00; if (priv->info && (priv->info->flags & OV772X_FLAG_HFLIP)) val ^= HFLIP_IMG; - return ov772x_mask_set(client, COM3, HFLIP_IMG, val); + return regmap_update_bits(regmap, COM3, HFLIP_IMG, val); case V4L2_CID_BAND_STOP_FILTER: if (!ctrl->val) { /* Switch the filter off, it is on now */ - ret = ov772x_mask_set(client, BDBASE, 0xff, 0xff); + ret = regmap_update_bits(regmap, BDBASE, 0xff, 0xff); if (!ret) - ret = ov772x_mask_set(client, COM8, - BNDF_ON_OFF, 0); + ret = regmap_update_bits(regmap, COM8, + BNDF_ON_OFF, 0); } else { /* Switch the filter on, set AEC low limit */ val = 256 - ctrl->val; - ret = ov772x_mask_set(client, COM8, - BNDF_ON_OFF, BNDF_ON_OFF); + ret = regmap_update_bits(regmap, COM8, + BNDF_ON_OFF, BNDF_ON_OFF); if (!ret) - ret = ov772x_mask_set(client, BDBASE, - 0xff, val); + ret = regmap_update_bits(regmap, BDBASE, + 0xff, val); } return ret; @@ -841,18 +808,19 @@ static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl) static int ov772x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { - struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov772x_priv *priv = to_ov772x(sd); int ret; + unsigned int val; reg->size = 1; if (reg->reg > 0xff) return -EINVAL; - ret = ov772x_read(client, reg->reg); + ret = regmap_read(priv->regmap, reg->reg, &val); if (ret < 0) return ret; - reg->val = (__u64)ret; + reg->val = (__u64)val; return 0; } @@ -860,13 +828,13 @@ static int ov772x_g_register(struct v4l2_subdev *sd, static int ov772x_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) { - struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov772x_priv *priv = to_ov772x(sd); if (reg->reg > 0xff || reg->val > 0xff) return -EINVAL; - return ov772x_write(client, reg->reg, reg->val); + return regmap_write(priv->regmap, reg->reg, reg->val); } #endif @@ -896,6 +864,7 @@ static int ov772x_power_on(struct ov772x_priv *priv) GPIOD_OUT_LOW); if (IS_ERR(priv->rstb_gpio)) { dev_info(&client->dev, "Unable to get GPIO \"reset\""); + clk_disable_unprepare(priv->clk); return PTR_ERR(priv->rstb_gpio); } @@ -1004,7 +973,7 @@ static void ov772x_select_params(const struct v4l2_mbus_framefmt *mf, static int ov772x_edgectrl(struct ov772x_priv *priv) { - struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); + struct regmap *regmap = priv->regmap; int ret; if (!priv->info) @@ -1018,19 +987,19 @@ static int ov772x_edgectrl(struct ov772x_priv *priv) * Remove it when manual mode. */ - ret = ov772x_mask_set(client, DSPAUTO, EDGE_ACTRL, 0x00); + ret = regmap_update_bits(regmap, DSPAUTO, EDGE_ACTRL, 0x00); if (ret < 0) return ret; - ret = ov772x_mask_set(client, - EDGE_TRSHLD, OV772X_EDGE_THRESHOLD_MASK, - priv->info->edgectrl.threshold); + ret = regmap_update_bits(regmap, EDGE_TRSHLD, + OV772X_EDGE_THRESHOLD_MASK, + priv->info->edgectrl.threshold); if (ret < 0) return ret; - ret = ov772x_mask_set(client, - EDGE_STRNGT, OV772X_EDGE_STRENGTH_MASK, - priv->info->edgectrl.strength); + ret = regmap_update_bits(regmap, EDGE_STRNGT, + OV772X_EDGE_STRENGTH_MASK, + priv->info->edgectrl.strength); if (ret < 0) return ret; @@ -1040,15 +1009,15 @@ static int ov772x_edgectrl(struct ov772x_priv *priv) * * Set upper and lower limit. */ - ret = ov772x_mask_set(client, - EDGE_UPPER, OV772X_EDGE_UPPER_MASK, - priv->info->edgectrl.upper); + ret = regmap_update_bits(regmap, EDGE_UPPER, + OV772X_EDGE_UPPER_MASK, + priv->info->edgectrl.upper); if (ret < 0) return ret; - ret = ov772x_mask_set(client, - EDGE_LOWER, OV772X_EDGE_LOWER_MASK, - priv->info->edgectrl.lower); + ret = regmap_update_bits(regmap, EDGE_LOWER, + OV772X_EDGE_LOWER_MASK, + priv->info->edgectrl.lower); if (ret < 0) return ret; } @@ -1060,12 +1029,11 @@ static int ov772x_set_params(struct ov772x_priv *priv, const struct ov772x_color_format *cfmt, const struct ov772x_win_size *win) { - struct i2c_client *client = v4l2_get_subdevdata(&priv->subdev); int ret; u8 val; /* Reset hardware. */ - ov772x_reset(client); + ov772x_reset(priv); /* Edge Ctrl. */ ret = ov772x_edgectrl(priv); @@ -1073,32 +1041,32 @@ static int ov772x_set_params(struct ov772x_priv *priv, return ret; /* Format and window size. */ - ret = ov772x_write(client, HSTART, win->rect.left >> 2); + ret = regmap_write(priv->regmap, HSTART, win->rect.left >> 2); if (ret < 0) goto ov772x_set_fmt_error; - ret = ov772x_write(client, HSIZE, win->rect.width >> 2); + ret = regmap_write(priv->regmap, HSIZE, win->rect.width >> 2); if (ret < 0) goto ov772x_set_fmt_error; - ret = ov772x_write(client, VSTART, win->rect.top >> 1); + ret = regmap_write(priv->regmap, VSTART, win->rect.top >> 1); if (ret < 0) goto ov772x_set_fmt_error; - ret = ov772x_write(client, VSIZE, win->rect.height >> 1); + ret = regmap_write(priv->regmap, VSIZE, win->rect.height >> 1); if (ret < 0) goto ov772x_set_fmt_error; - ret = ov772x_write(client, HOUTSIZE, win->rect.width >> 2); + ret = regmap_write(priv->regmap, HOUTSIZE, win->rect.width >> 2); if (ret < 0) goto ov772x_set_fmt_error; - ret = ov772x_write(client, VOUTSIZE, win->rect.height >> 1); + ret = regmap_write(priv->regmap, VOUTSIZE, win->rect.height >> 1); if (ret < 0) goto ov772x_set_fmt_error; - ret = ov772x_write(client, HREF, + ret = regmap_write(priv->regmap, HREF, ((win->rect.top & 1) << HREF_VSTART_SHIFT) | ((win->rect.left & 3) << HREF_HSTART_SHIFT) | ((win->rect.height & 1) << HREF_VSIZE_SHIFT) | ((win->rect.width & 3) << HREF_HSIZE_SHIFT)); if (ret < 0) goto ov772x_set_fmt_error; - ret = ov772x_write(client, EXHCH, + ret = regmap_write(priv->regmap, EXHCH, ((win->rect.height & 1) << EXHCH_VSIZE_SHIFT) | ((win->rect.width & 3) << EXHCH_HSIZE_SHIFT)); if (ret < 0) @@ -1107,15 +1075,14 @@ static int ov772x_set_params(struct ov772x_priv *priv, /* Set DSP_CTRL3. */ val = cfmt->dsp3; if (val) { - ret = ov772x_mask_set(client, - DSP_CTRL3, UV_MASK, val); + ret = regmap_update_bits(priv->regmap, DSP_CTRL3, UV_MASK, val); if (ret < 0) goto ov772x_set_fmt_error; } /* DSP_CTRL4: AEC reference point and DSP output format. */ if (cfmt->dsp4) { - ret = ov772x_write(client, DSP_CTRL4, cfmt->dsp4); + ret = regmap_write(priv->regmap, DSP_CTRL4, cfmt->dsp4); if (ret < 0) goto ov772x_set_fmt_error; } @@ -1131,13 +1098,12 @@ static int ov772x_set_params(struct ov772x_priv *priv, if (priv->hflip_ctrl->val) val ^= HFLIP_IMG; - ret = ov772x_mask_set(client, - COM3, SWAP_MASK | IMG_MASK, val); + ret = regmap_update_bits(priv->regmap, COM3, SWAP_MASK | IMG_MASK, val); if (ret < 0) goto ov772x_set_fmt_error; /* COM7: Sensor resolution and output format control. */ - ret = ov772x_write(client, COM7, win->com7_bit | cfmt->com7); + ret = regmap_write(priv->regmap, COM7, win->com7_bit | cfmt->com7); if (ret < 0) goto ov772x_set_fmt_error; @@ -1150,10 +1116,11 @@ static int ov772x_set_params(struct ov772x_priv *priv, if (priv->band_filter_ctrl->val) { unsigned short band_filter = priv->band_filter_ctrl->val; - ret = ov772x_mask_set(client, COM8, BNDF_ON_OFF, BNDF_ON_OFF); + ret = regmap_update_bits(priv->regmap, COM8, + BNDF_ON_OFF, BNDF_ON_OFF); if (!ret) - ret = ov772x_mask_set(client, BDBASE, - 0xff, 256 - band_filter); + ret = regmap_update_bits(priv->regmap, BDBASE, + 0xff, 256 - band_filter); if (ret < 0) goto ov772x_set_fmt_error; } @@ -1162,7 +1129,7 @@ static int ov772x_set_params(struct ov772x_priv *priv, ov772x_set_fmt_error: - ov772x_reset(client); + ov772x_reset(priv); return ret; } @@ -1180,7 +1147,6 @@ static int ov772x_get_selection(struct v4l2_subdev *sd, sel->r.top = 0; switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP_DEFAULT: case V4L2_SEL_TGT_CROP: sel->r.width = priv->win->rect.width; sel->r.height = priv->win->rect.height; @@ -1276,12 +1242,12 @@ static int ov772x_video_probe(struct ov772x_priv *priv) return ret; /* Check and show product ID and manufacturer ID. */ - pid = ov772x_read(client, PID); - if (pid < 0) - return pid; - ver = ov772x_read(client, VER); - if (ver < 0) - return ver; + ret = regmap_read(priv->regmap, PID, &pid); + if (ret < 0) + return ret; + ret = regmap_read(priv->regmap, VER, &ver); + if (ret < 0) + return ret; switch (VERSION(pid, ver)) { case OV7720: @@ -1297,12 +1263,12 @@ static int ov772x_video_probe(struct ov772x_priv *priv) goto done; } - midh = ov772x_read(client, MIDH); - if (midh < 0) - return midh; - midl = ov772x_read(client, MIDL); - if (midl < 0) - return midl; + ret = regmap_read(priv->regmap, MIDH, &midh); + if (ret < 0) + return ret; + ret = regmap_read(priv->regmap, MIDL, &midl); + if (ret < 0) + return ret; dev_info(&client->dev, "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", @@ -1386,8 +1352,12 @@ static int ov772x_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov772x_priv *priv; - struct i2c_adapter *adapter = client->adapter; int ret; + static const struct regmap_config ov772x_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = DSPAUTO, + }; if (!client->dev.of_node && !client->dev.platform_data) { dev_err(&client->dev, @@ -1395,16 +1365,16 @@ static int ov772x_probe(struct i2c_client *client, return -EINVAL; } - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - dev_err(&adapter->dev, - "I2C-Adapter doesn't support SMBUS_BYTE_DATA\n"); - return -EIO; - } - priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; + priv->regmap = devm_regmap_init_sccb(client, &ov772x_regmap_config); + if (IS_ERR(priv->regmap)) { + dev_err(&client->dev, "Failed to allocate register map\n"); + return PTR_ERR(priv->regmap); + } + priv->info = client->dev.platform_data; mutex_init(&priv->lock); diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c index 605f3e25ad82..6e9c233cfbe3 100644 --- a/drivers/media/i2c/ov7740.c +++ b/drivers/media/i2c/ov7740.c @@ -510,7 +510,7 @@ static int ov7740_set_ctrl(struct v4l2_ctrl *ctrl) int ret; u8 val = 0; - if (pm_runtime_get_if_in_use(&client->dev) <= 0) + if (!pm_runtime_get_if_in_use(&client->dev)) return 0; switch (ctrl->id) { diff --git a/drivers/media/i2c/ov9650.c b/drivers/media/i2c/ov9650.c index 5bea31cd41aa..f0587c0c0a72 100644 --- a/drivers/media/i2c/ov9650.c +++ b/drivers/media/i2c/ov9650.c @@ -20,6 +20,7 @@ #include <linux/media.h> #include <linux/module.h> #include <linux/ratelimit.h> +#include <linux/regmap.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/videodev2.h> @@ -259,7 +260,7 @@ struct ov965x { /* Protects the struct fields below */ struct mutex lock; - struct i2c_client *client; + struct regmap *regmap; /* Exposure row interval in us */ unsigned int exp_row_interval; @@ -424,51 +425,42 @@ static inline struct ov965x *to_ov965x(struct v4l2_subdev *sd) return container_of(sd, struct ov965x, sd); } -static int ov965x_read(struct i2c_client *client, u8 addr, u8 *val) +static int ov965x_read(struct ov965x *ov965x, u8 addr, u8 *val) { - u8 buf = addr; - struct i2c_msg msg = { - .addr = client->addr, - .flags = 0, - .len = 1, - .buf = &buf - }; int ret; + unsigned int buf; - ret = i2c_transfer(client->adapter, &msg, 1); - if (ret == 1) { - msg.flags = I2C_M_RD; - ret = i2c_transfer(client->adapter, &msg, 1); - - if (ret == 1) - *val = buf; - } + ret = regmap_read(ov965x->regmap, addr, &buf); + if (!ret) + *val = buf; + else + *val = -1; - v4l2_dbg(2, debug, client, "%s: 0x%02x @ 0x%02x. (%d)\n", + v4l2_dbg(2, debug, &ov965x->sd, "%s: 0x%02x @ 0x%02x. (%d)\n", __func__, *val, addr, ret); - return ret == 1 ? 0 : ret; + return ret; } -static int ov965x_write(struct i2c_client *client, u8 addr, u8 val) +static int ov965x_write(struct ov965x *ov965x, u8 addr, u8 val) { - u8 buf[2] = { addr, val }; + int ret; - int ret = i2c_master_send(client, buf, 2); + ret = regmap_write(ov965x->regmap, addr, val); - v4l2_dbg(2, debug, client, "%s: 0x%02x @ 0x%02X (%d)\n", + v4l2_dbg(2, debug, &ov965x->sd, "%s: 0x%02x @ 0x%02X (%d)\n", __func__, val, addr, ret); - return ret == 2 ? 0 : ret; + return ret; } -static int ov965x_write_array(struct i2c_client *client, +static int ov965x_write_array(struct ov965x *ov965x, const struct i2c_rv *regs) { int i, ret = 0; for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) - ret = ov965x_write(client, regs[i].addr, regs[i].value); + ret = ov965x_write(ov965x, regs[i].addr, regs[i].value); return ret; } @@ -486,7 +478,7 @@ static int ov965x_set_default_gamma_curve(struct ov965x *ov965x) unsigned int i; for (i = 0; i < ARRAY_SIZE(gamma_curve); i++) { - int ret = ov965x_write(ov965x->client, addr, gamma_curve[i]); + int ret = ov965x_write(ov965x, addr, gamma_curve[i]); if (ret < 0) return ret; @@ -506,7 +498,7 @@ static int ov965x_set_color_matrix(struct ov965x *ov965x) unsigned int i; for (i = 0; i < ARRAY_SIZE(mtx); i++) { - int ret = ov965x_write(ov965x->client, addr, mtx[i]); + int ret = ov965x_write(ov965x, addr, mtx[i]); if (ret < 0) return ret; @@ -542,16 +534,15 @@ static int __ov965x_set_power(struct ov965x *ov965x, int on) static int ov965x_s_power(struct v4l2_subdev *sd, int on) { struct ov965x *ov965x = to_ov965x(sd); - struct i2c_client *client = ov965x->client; int ret = 0; - v4l2_dbg(1, debug, client, "%s: on: %d\n", __func__, on); + v4l2_dbg(1, debug, sd, "%s: on: %d\n", __func__, on); mutex_lock(&ov965x->lock); if (ov965x->power == !on) { ret = __ov965x_set_power(ov965x, on); if (!ret && on) { - ret = ov965x_write_array(client, + ret = ov965x_write_array(ov965x, ov965x_init_regs); ov965x->apply_frame_fmt = 1; ov965x->ctrls.update = 1; @@ -609,13 +600,13 @@ static int ov965x_set_banding_filter(struct ov965x *ov965x, int value) int ret; u8 reg; - ret = ov965x_read(ov965x->client, REG_COM8, ®); + ret = ov965x_read(ov965x, REG_COM8, ®); if (!ret) { if (value == V4L2_CID_POWER_LINE_FREQUENCY_DISABLED) reg &= ~COM8_BFILT; else reg |= COM8_BFILT; - ret = ov965x_write(ov965x->client, REG_COM8, reg); + ret = ov965x_write(ov965x, REG_COM8, reg); } if (value == V4L2_CID_POWER_LINE_FREQUENCY_DISABLED) return 0; @@ -631,7 +622,7 @@ static int ov965x_set_banding_filter(struct ov965x *ov965x, int value) ov965x->fiv->interval.numerator; mbd = ((mbd / (light_freq * 2)) + 500) / 1000UL; - return ov965x_write(ov965x->client, REG_MBD, mbd); + return ov965x_write(ov965x, REG_MBD, mbd); } static int ov965x_set_white_balance(struct ov965x *ov965x, int awb) @@ -639,17 +630,17 @@ static int ov965x_set_white_balance(struct ov965x *ov965x, int awb) int ret; u8 reg; - ret = ov965x_read(ov965x->client, REG_COM8, ®); + ret = ov965x_read(ov965x, REG_COM8, ®); if (!ret) { reg = awb ? reg | REG_COM8 : reg & ~REG_COM8; - ret = ov965x_write(ov965x->client, REG_COM8, reg); + ret = ov965x_write(ov965x, REG_COM8, reg); } if (!ret && !awb) { - ret = ov965x_write(ov965x->client, REG_BLUE, + ret = ov965x_write(ov965x, REG_BLUE, ov965x->ctrls.blue_balance->val); if (ret < 0) return ret; - ret = ov965x_write(ov965x->client, REG_RED, + ret = ov965x_write(ov965x, REG_RED, ov965x->ctrls.red_balance->val); } return ret; @@ -677,14 +668,13 @@ static int ov965x_set_brightness(struct ov965x *ov965x, int val) return -EINVAL; for (i = 0; i < NUM_BR_REGS && !ret; i++) - ret = ov965x_write(ov965x->client, regs[0][i], + ret = ov965x_write(ov965x, regs[0][i], regs[val][i]); return ret; } static int ov965x_set_gain(struct ov965x *ov965x, int auto_gain) { - struct i2c_client *client = ov965x->client; struct ov965x_ctrls *ctrls = &ov965x->ctrls; int ret = 0; u8 reg; @@ -693,14 +683,14 @@ static int ov965x_set_gain(struct ov965x *ov965x, int auto_gain) * gain value in REG_VREF, REG_GAIN is not overwritten. */ if (ctrls->auto_gain->is_new) { - ret = ov965x_read(client, REG_COM8, ®); + ret = ov965x_read(ov965x, REG_COM8, ®); if (ret < 0) return ret; if (ctrls->auto_gain->val) reg |= COM8_AGC; else reg &= ~COM8_AGC; - ret = ov965x_write(client, REG_COM8, reg); + ret = ov965x_write(ov965x, REG_COM8, reg); if (ret < 0) return ret; } @@ -719,15 +709,15 @@ static int ov965x_set_gain(struct ov965x *ov965x, int auto_gain) rgain = (gain - ((1 << m) * 16)) / (1 << m); rgain |= (((1 << m) - 1) << 4); - ret = ov965x_write(client, REG_GAIN, rgain & 0xff); + ret = ov965x_write(ov965x, REG_GAIN, rgain & 0xff); if (ret < 0) return ret; - ret = ov965x_read(client, REG_VREF, ®); + ret = ov965x_read(ov965x, REG_VREF, ®); if (ret < 0) return ret; reg &= ~VREF_GAIN_MASK; reg |= (((rgain >> 8) & 0x3) << 6); - ret = ov965x_write(client, REG_VREF, reg); + ret = ov965x_write(ov965x, REG_VREF, reg); if (ret < 0) return ret; /* Return updated control's value to userspace */ @@ -742,10 +732,10 @@ static int ov965x_set_sharpness(struct ov965x *ov965x, unsigned int value) u8 com14, edge; int ret; - ret = ov965x_read(ov965x->client, REG_COM14, &com14); + ret = ov965x_read(ov965x, REG_COM14, &com14); if (ret < 0) return ret; - ret = ov965x_read(ov965x->client, REG_EDGE, &edge); + ret = ov965x_read(ov965x, REG_EDGE, &edge); if (ret < 0) return ret; com14 = value ? com14 | COM14_EDGE_EN : com14 & ~COM14_EDGE_EN; @@ -756,33 +746,32 @@ static int ov965x_set_sharpness(struct ov965x *ov965x, unsigned int value) } else { com14 &= ~COM14_EEF_X2; } - ret = ov965x_write(ov965x->client, REG_COM14, com14); + ret = ov965x_write(ov965x, REG_COM14, com14); if (ret < 0) return ret; edge &= ~EDGE_FACTOR_MASK; edge |= ((u8)value & 0x0f); - return ov965x_write(ov965x->client, REG_EDGE, edge); + return ov965x_write(ov965x, REG_EDGE, edge); } static int ov965x_set_exposure(struct ov965x *ov965x, int exp) { - struct i2c_client *client = ov965x->client; struct ov965x_ctrls *ctrls = &ov965x->ctrls; bool auto_exposure = (exp == V4L2_EXPOSURE_AUTO); int ret; u8 reg; if (ctrls->auto_exp->is_new) { - ret = ov965x_read(client, REG_COM8, ®); + ret = ov965x_read(ov965x, REG_COM8, ®); if (ret < 0) return ret; if (auto_exposure) reg |= (COM8_AEC | COM8_AGC); else reg &= ~(COM8_AEC | COM8_AGC); - ret = ov965x_write(client, REG_COM8, reg); + ret = ov965x_write(ov965x, REG_COM8, reg); if (ret < 0) return ret; } @@ -794,12 +783,12 @@ static int ov965x_set_exposure(struct ov965x *ov965x, int exp) * Manual exposure value * [b15:b0] - AECHM (b15:b10), AECH (b9:b2), COM1 (b1:b0) */ - ret = ov965x_write(client, REG_COM1, exposure & 0x3); + ret = ov965x_write(ov965x, REG_COM1, exposure & 0x3); if (!ret) - ret = ov965x_write(client, REG_AECH, + ret = ov965x_write(ov965x, REG_AECH, (exposure >> 2) & 0xff); if (!ret) - ret = ov965x_write(client, REG_AECHM, + ret = ov965x_write(ov965x, REG_AECHM, (exposure >> 10) & 0x3f); /* Update the value to minimize rounding errors */ ctrls->exposure->val = ((exposure * ov965x->exp_row_interval) @@ -822,7 +811,7 @@ static int ov965x_set_flip(struct ov965x *ov965x) if (ov965x->ctrls.vflip->val) mvfp |= MVFP_FLIP; - return ov965x_write(ov965x->client, REG_MVFP, mvfp); + return ov965x_write(ov965x, REG_MVFP, mvfp); } #define NUM_SAT_LEVELS 5 @@ -846,7 +835,7 @@ static int ov965x_set_saturation(struct ov965x *ov965x, int val) return -EINVAL; for (i = 0; i < NUM_SAT_REGS && !ret; i++) - ret = ov965x_write(ov965x->client, addr + i, regs[val][i]); + ret = ov965x_write(ov965x, addr + i, regs[val][i]); return ret; } @@ -856,16 +845,15 @@ static int ov965x_set_test_pattern(struct ov965x *ov965x, int value) int ret; u8 reg; - ret = ov965x_read(ov965x->client, REG_COM23, ®); + ret = ov965x_read(ov965x, REG_COM23, ®); if (ret < 0) return ret; reg = value ? reg | COM23_TEST_MODE : reg & ~COM23_TEST_MODE; - return ov965x_write(ov965x->client, REG_COM23, reg); + return ov965x_write(ov965x, REG_COM23, reg); } static int __g_volatile_ctrl(struct ov965x *ov965x, struct v4l2_ctrl *ctrl) { - struct i2c_client *client = ov965x->client; unsigned int exposure, gain, m; u8 reg0, reg1, reg2; int ret; @@ -877,10 +865,10 @@ static int __g_volatile_ctrl(struct ov965x *ov965x, struct v4l2_ctrl *ctrl) case V4L2_CID_AUTOGAIN: if (!ctrl->val) return 0; - ret = ov965x_read(client, REG_GAIN, ®0); + ret = ov965x_read(ov965x, REG_GAIN, ®0); if (ret < 0) return ret; - ret = ov965x_read(client, REG_VREF, ®1); + ret = ov965x_read(ov965x, REG_VREF, ®1); if (ret < 0) return ret; gain = ((reg1 >> 6) << 8) | reg0; @@ -891,13 +879,13 @@ static int __g_volatile_ctrl(struct ov965x *ov965x, struct v4l2_ctrl *ctrl) case V4L2_CID_EXPOSURE_AUTO: if (ctrl->val == V4L2_EXPOSURE_MANUAL) return 0; - ret = ov965x_read(client, REG_COM1, ®0); + ret = ov965x_read(ov965x, REG_COM1, ®0); if (ret < 0) return ret; - ret = ov965x_read(client, REG_AECH, ®1); + ret = ov965x_read(ov965x, REG_AECH, ®1); if (ret < 0) return ret; - ret = ov965x_read(client, REG_AECHM, ®2); + ret = ov965x_read(ov965x, REG_AECHM, ®2); if (ret < 0) return ret; exposure = ((reg2 & 0x3f) << 10) | (reg1 << 2) | @@ -1279,32 +1267,31 @@ static int ov965x_set_frame_size(struct ov965x *ov965x) int i, ret = 0; for (i = 0; ret == 0 && i < NUM_FMT_REGS; i++) - ret = ov965x_write(ov965x->client, frame_size_reg_addr[i], + ret = ov965x_write(ov965x, frame_size_reg_addr[i], ov965x->frame_size->regs[i]); return ret; } static int __ov965x_set_params(struct ov965x *ov965x) { - struct i2c_client *client = ov965x->client; struct ov965x_ctrls *ctrls = &ov965x->ctrls; int ret = 0; u8 reg; if (ov965x->apply_frame_fmt) { reg = DEF_CLKRC + ov965x->fiv->clkrc_div; - ret = ov965x_write(client, REG_CLKRC, reg); + ret = ov965x_write(ov965x, REG_CLKRC, reg); if (ret < 0) return ret; ret = ov965x_set_frame_size(ov965x); if (ret < 0) return ret; - ret = ov965x_read(client, REG_TSLB, ®); + ret = ov965x_read(ov965x, REG_TSLB, ®); if (ret < 0) return ret; reg &= ~TSLB_YUYV_MASK; reg |= ov965x->tslb_reg; - ret = ov965x_write(client, REG_TSLB, reg); + ret = ov965x_write(ov965x, REG_TSLB, reg); if (ret < 0) return ret; } @@ -1318,10 +1305,10 @@ static int __ov965x_set_params(struct ov965x *ov965x) * Select manual banding filter, the filter will * be enabled further if required. */ - ret = ov965x_read(client, REG_COM11, ®); + ret = ov965x_read(ov965x, REG_COM11, ®); if (!ret) reg |= COM11_BANDING; - ret = ov965x_write(client, REG_COM11, reg); + ret = ov965x_write(ov965x, REG_COM11, reg); if (ret < 0) return ret; /* @@ -1333,12 +1320,11 @@ static int __ov965x_set_params(struct ov965x *ov965x) static int ov965x_s_stream(struct v4l2_subdev *sd, int on) { - struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov965x *ov965x = to_ov965x(sd); struct ov965x_ctrls *ctrls = &ov965x->ctrls; int ret = 0; - v4l2_dbg(1, debug, client, "%s: on: %d\n", __func__, on); + v4l2_dbg(1, debug, sd, "%s: on: %d\n", __func__, on); mutex_lock(&ov965x->lock); if (ov965x->streaming == !on) { @@ -1358,7 +1344,7 @@ static int ov965x_s_stream(struct v4l2_subdev *sd, int on) ctrls->update = 0; } if (!ret) - ret = ov965x_write(client, REG_COM2, + ret = ov965x_write(ov965x, REG_COM2, on ? 0x01 : 0x11); } if (!ret) @@ -1421,6 +1407,7 @@ static int ov965x_configure_gpios_pdata(struct ov965x *ov965x, { int ret, i; int gpios[NUM_GPIOS]; + struct device *dev = regmap_get_device(ov965x->regmap); gpios[GPIO_PWDN] = pdata->gpio_pwdn; gpios[GPIO_RST] = pdata->gpio_reset; @@ -1430,7 +1417,7 @@ static int ov965x_configure_gpios_pdata(struct ov965x *ov965x, if (!gpio_is_valid(gpio)) continue; - ret = devm_gpio_request_one(&ov965x->client->dev, gpio, + ret = devm_gpio_request_one(dev, gpio, GPIOF_OUT_INIT_HIGH, "OV965X"); if (ret < 0) return ret; @@ -1446,7 +1433,7 @@ static int ov965x_configure_gpios_pdata(struct ov965x *ov965x, static int ov965x_configure_gpios(struct ov965x *ov965x) { - struct device *dev = &ov965x->client->dev; + struct device *dev = regmap_get_device(ov965x->regmap); ov965x->gpios[GPIO_PWDN] = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_HIGH); @@ -1467,7 +1454,6 @@ static int ov965x_configure_gpios(struct ov965x *ov965x) static int ov965x_detect_sensor(struct v4l2_subdev *sd) { - struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov965x *ov965x = to_ov965x(sd); u8 pid, ver; int ret; @@ -1480,9 +1466,9 @@ static int ov965x_detect_sensor(struct v4l2_subdev *sd) msleep(25); /* Check sensor revision */ - ret = ov965x_read(client, REG_PID, &pid); + ret = ov965x_read(ov965x, REG_PID, &pid); if (!ret) - ret = ov965x_read(client, REG_VER, &ver); + ret = ov965x_read(ov965x, REG_VER, &ver); __ov965x_set_power(ov965x, 0); @@ -1509,12 +1495,21 @@ static int ov965x_probe(struct i2c_client *client, struct v4l2_subdev *sd; struct ov965x *ov965x; int ret; + static const struct regmap_config ov965x_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xab, + }; ov965x = devm_kzalloc(&client->dev, sizeof(*ov965x), GFP_KERNEL); if (!ov965x) return -ENOMEM; - ov965x->client = client; + ov965x->regmap = devm_regmap_init_sccb(client, &ov965x_regmap_config); + if (IS_ERR(ov965x->regmap)) { + dev_err(&client->dev, "Failed to allocate register map\n"); + return PTR_ERR(ov965x->regmap); + } if (pdata) { if (pdata->mclk_frequency == 0) { @@ -1527,7 +1522,7 @@ static int ov965x_probe(struct i2c_client *client, if (ret < 0) return ret; } else if (dev_fwnode(&client->dev)) { - ov965x->clk = devm_clk_get(&ov965x->client->dev, NULL); + ov965x->clk = devm_clk_get(&client->dev, NULL); if (IS_ERR(ov965x->clk)) return PTR_ERR(ov965x->clk); ov965x->mclk_frequency = clk_get_rate(ov965x->clk); @@ -1546,7 +1541,7 @@ static int ov965x_probe(struct i2c_client *client, sd = &ov965x->sd; v4l2_i2c_subdev_init(sd, client, &ov965x_subdev_ops); - strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name)); + strscpy(sd->name, DRIVER_NAME, sizeof(sd->name)); sd->internal_ops = &ov965x_sd_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | diff --git a/drivers/media/i2c/rj54n1cb0c.c b/drivers/media/i2c/rj54n1cb0c.c index 6ad998ad1b16..4cc51e001874 100644 --- a/drivers/media/i2c/rj54n1cb0c.c +++ b/drivers/media/i2c/rj54n1cb0c.c @@ -589,7 +589,6 @@ static int rj54n1_get_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP_DEFAULT: sel->r.left = RJ54N1_COLUMN_SKIP; sel->r.top = RJ54N1_ROW_SKIP; sel->r.width = RJ54N1_MAX_WIDTH; diff --git a/drivers/media/i2c/s5c73m3/s5c73m3-core.c b/drivers/media/i2c/s5c73m3/s5c73m3-core.c index ce196b60f917..c461847ddae8 100644 --- a/drivers/media/i2c/s5c73m3/s5c73m3-core.c +++ b/drivers/media/i2c/s5c73m3/s5c73m3-core.c @@ -1603,7 +1603,7 @@ static int s5c73m3_get_platform_data(struct s5c73m3 *state) const struct s5c73m3_platform_data *pdata = dev->platform_data; struct device_node *node = dev->of_node; struct device_node *node_ep; - struct v4l2_fwnode_endpoint ep; + struct v4l2_fwnode_endpoint ep = { .bus_type = 0 }; int ret; if (!node) { @@ -1644,7 +1644,7 @@ static int s5c73m3_get_platform_data(struct s5c73m3 *state) if (ret) return ret; - if (ep.bus_type != V4L2_MBUS_CSI2) { + if (ep.bus_type != V4L2_MBUS_CSI2_DPHY) { dev_err(dev, "unsupported bus type\n"); return -EINVAL; } @@ -1683,7 +1683,7 @@ static int s5c73m3_probe(struct i2c_client *client, v4l2_subdev_init(sd, &s5c73m3_subdev_ops); sd->owner = client->dev.driver->owner; v4l2_set_subdevdata(sd, state); - strlcpy(sd->name, "S5C73M3", sizeof(sd->name)); + strscpy(sd->name, "S5C73M3", sizeof(sd->name)); sd->internal_ops = &s5c73m3_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; @@ -1698,7 +1698,8 @@ static int s5c73m3_probe(struct i2c_client *client, return ret; v4l2_i2c_subdev_init(oif_sd, client, &oif_subdev_ops); - strcpy(oif_sd->name, "S5C73M3-OIF"); + /* Static name; NEVER use in new drivers! */ + strscpy(oif_sd->name, "S5C73M3-OIF", sizeof(oif_sd->name)); oif_sd->internal_ops = &oif_internal_ops; oif_sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; diff --git a/drivers/media/i2c/s5k4ecgx.c b/drivers/media/i2c/s5k4ecgx.c index 6ebcf254989a..79aa2740edc4 100644 --- a/drivers/media/i2c/s5k4ecgx.c +++ b/drivers/media/i2c/s5k4ecgx.c @@ -954,7 +954,8 @@ static int s5k4ecgx_probe(struct i2c_client *client, sd = &priv->sd; /* Registering subdev */ v4l2_i2c_subdev_init(sd, client, &s5k4ecgx_ops); - strlcpy(sd->name, S5K4ECGX_DRIVER_NAME, sizeof(sd->name)); + /* Static name; NEVER use in new drivers! */ + strscpy(sd->name, S5K4ECGX_DRIVER_NAME, sizeof(sd->name)); sd->internal_ops = &s5k4ecgx_subdev_internal_ops; /* Support v4l2 sub-device user space API */ diff --git a/drivers/media/i2c/s5k5baf.c b/drivers/media/i2c/s5k5baf.c index 5007c9659342..727db7c0670a 100644 --- a/drivers/media/i2c/s5k5baf.c +++ b/drivers/media/i2c/s5k5baf.c @@ -766,7 +766,7 @@ static int s5k5baf_hw_set_video_bus(struct s5k5baf *state) { u16 en_pkts; - if (state->bus_type == V4L2_MBUS_CSI2) + if (state->bus_type == V4L2_MBUS_CSI2_DPHY) en_pkts = EN_PACKETS_CSI2; else en_pkts = 0; @@ -1841,7 +1841,7 @@ static int s5k5baf_parse_device_node(struct s5k5baf *state, struct device *dev) { struct device_node *node = dev->of_node; struct device_node *node_ep; - struct v4l2_fwnode_endpoint ep; + struct v4l2_fwnode_endpoint ep = { .bus_type = 0 }; int ret; if (!node) { @@ -1875,7 +1875,7 @@ static int s5k5baf_parse_device_node(struct s5k5baf *state, struct device *dev) state->bus_type = ep.bus_type; switch (state->bus_type) { - case V4L2_MBUS_CSI2: + case V4L2_MBUS_CSI2_DPHY: state->nlanes = ep.bus.mipi_csi2.num_data_lanes; break; case V4L2_MBUS_PARALLEL: diff --git a/drivers/media/i2c/s5k6aa.c b/drivers/media/i2c/s5k6aa.c index 13c10b5e2b45..ab26f549d716 100644 --- a/drivers/media/i2c/s5k6aa.c +++ b/drivers/media/i2c/s5k6aa.c @@ -688,7 +688,7 @@ static int s5k6aa_configure_video_bus(struct s5k6aa *s5k6aa, * but there is nothing indicating how to switch between both * in the datasheet. For now default BT.601 interface is assumed. */ - if (bus_type == V4L2_MBUS_CSI2) + if (bus_type == V4L2_MBUS_CSI2_DPHY) cfg = nlanes; else if (bus_type != V4L2_MBUS_PARALLEL) return -EINVAL; @@ -1576,7 +1576,8 @@ static int s5k6aa_probe(struct i2c_client *client, sd = &s5k6aa->sd; v4l2_i2c_subdev_init(sd, client, &s5k6aa_subdev_ops); - strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name)); + /* Static name; NEVER use in new drivers! */ + strscpy(sd->name, DRIVER_NAME, sizeof(sd->name)); sd->internal_ops = &s5k6aa_subdev_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; diff --git a/drivers/media/i2c/saa7115.c b/drivers/media/i2c/saa7115.c index b07114b5efb2..6bc278aa31fc 100644 --- a/drivers/media/i2c/saa7115.c +++ b/drivers/media/i2c/saa7115.c @@ -59,10 +59,16 @@ enum saa711x_model { SAA7118, }; +enum saa711x_pads { + SAA711X_PAD_IF_INPUT, + SAA711X_PAD_VID_OUT, + SAA711X_NUM_PADS +}; + struct saa711x_state { struct v4l2_subdev sd; #ifdef CONFIG_MEDIA_CONTROLLER - struct media_pad pads[DEMOD_NUM_PADS]; + struct media_pad pads[SAA711X_NUM_PADS]; #endif struct v4l2_ctrl_handler hdl; @@ -1765,7 +1771,7 @@ static int saa711x_detect_chip(struct i2c_client *client, * the lower nibble is a gm7113c. */ - strlcpy(name, "gm7113c", CHIP_VER_SIZE); + strscpy(name, "gm7113c", CHIP_VER_SIZE); if (!autodetect && strcmp(name, id->name)) return -EINVAL; @@ -1779,7 +1785,7 @@ static int saa711x_detect_chip(struct i2c_client *client, /* Check if it is a CJC7113 */ if (!memcmp(name, "1111111111111111", CHIP_VER_SIZE)) { - strlcpy(name, "cjc7113", CHIP_VER_SIZE); + strscpy(name, "cjc7113", CHIP_VER_SIZE); if (!autodetect && strcmp(name, id->name)) return -EINVAL; @@ -1825,7 +1831,7 @@ static int saa711x_probe(struct i2c_client *client, if (ident < 0) return ident; - strlcpy(client->name, name, sizeof(client->name)); + strscpy(client->name, name, sizeof(client->name)); state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL); if (state == NULL) @@ -1834,13 +1840,15 @@ static int saa711x_probe(struct i2c_client *client, v4l2_i2c_subdev_init(sd, client, &saa711x_ops); #if defined(CONFIG_MEDIA_CONTROLLER) - state->pads[DEMOD_PAD_IF_INPUT].flags = MEDIA_PAD_FL_SINK; - state->pads[DEMOD_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE; - state->pads[DEMOD_PAD_VBI_OUT].flags = MEDIA_PAD_FL_SOURCE; + state->pads[SAA711X_PAD_IF_INPUT].flags = MEDIA_PAD_FL_SINK; + state->pads[SAA711X_PAD_IF_INPUT].sig_type = PAD_SIGNAL_ANALOG; + state->pads[SAA711X_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE; + state->pads[SAA711X_PAD_VID_OUT].sig_type = PAD_SIGNAL_DV; sd->entity.function = MEDIA_ENT_F_ATV_DECODER; - ret = media_entity_pads_init(&sd->entity, DEMOD_NUM_PADS, state->pads); + ret = media_entity_pads_init(&sd->entity, SAA711X_NUM_PADS, + state->pads); if (ret < 0) return ret; #endif diff --git a/drivers/media/i2c/saa7127.c b/drivers/media/i2c/saa7127.c index e58a150cec5c..a67865b810c0 100644 --- a/drivers/media/i2c/saa7127.c +++ b/drivers/media/i2c/saa7127.c @@ -761,10 +761,10 @@ static int saa7127_probe(struct i2c_client *client, saa7127_write(sd, SAA7129_REG_FADE_KEY_COL2, read_result); state->ident = SAA7129; - strlcpy(client->name, "saa7129", I2C_NAME_SIZE); + strscpy(client->name, "saa7129", I2C_NAME_SIZE); } else { state->ident = SAA7127; - strlcpy(client->name, "saa7127", I2C_NAME_SIZE); + strscpy(client->name, "saa7127", I2C_NAME_SIZE); } } diff --git a/drivers/media/i2c/smiapp/smiapp-core.c b/drivers/media/i2c/smiapp/smiapp-core.c index 1236683da8f7..58a45c353e27 100644 --- a/drivers/media/i2c/smiapp/smiapp-core.c +++ b/drivers/media/i2c/smiapp/smiapp-core.c @@ -624,7 +624,7 @@ static int smiapp_init_late_controls(struct smiapp_sensor *sensor) { unsigned long *valid_link_freqs = &sensor->valid_link_freqs[ sensor->csi_format->compressed - sensor->compressed_min_bpp]; - unsigned int max, i; + unsigned int i; for (i = 0; i < ARRAY_SIZE(sensor->test_data); i++) { int max_value = (1 << sensor->csi_format->width) - 1; @@ -635,8 +635,6 @@ static int smiapp_init_late_controls(struct smiapp_sensor *sensor) 0, max_value, 1, max_value); } - for (max = 0; sensor->hwcfg->op_sys_clock[max + 1]; max++); - sensor->link_freq = v4l2_ctrl_new_int_menu( &sensor->src->ctrl_handler, &smiapp_ctrl_ops, V4L2_CID_LINK_FREQ, __fls(*valid_link_freqs), @@ -2617,9 +2615,7 @@ static void smiapp_create_subdev(struct smiapp_sensor *sensor, ssd->npads = num_pads; ssd->source_pad = num_pads - 1; - snprintf(ssd->sd.name, - sizeof(ssd->sd.name), "%s %s %d-%4.4x", sensor->minfo.name, - name, i2c_adapter_id(client->adapter), client->addr); + v4l2_i2c_subdev_set_name(&ssd->sd, client, sensor->minfo.name, name); smiapp_get_native_size(ssd, &ssd->sink_fmt); @@ -2761,7 +2757,7 @@ static int __maybe_unused smiapp_resume(struct device *dev) static struct smiapp_hwconfig *smiapp_get_hwconfig(struct device *dev) { struct smiapp_hwconfig *hwcfg; - struct v4l2_fwnode_endpoint *bus_cfg; + struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 }; struct fwnode_handle *ep; struct fwnode_handle *fwnode = dev_fwnode(dev); u32 rotation; @@ -2775,27 +2771,33 @@ static struct smiapp_hwconfig *smiapp_get_hwconfig(struct device *dev) if (!ep) return NULL; - bus_cfg = v4l2_fwnode_endpoint_alloc_parse(ep); - if (IS_ERR(bus_cfg)) + bus_cfg.bus_type = V4L2_MBUS_CSI2_DPHY; + rval = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + if (rval == -ENXIO) { + bus_cfg = (struct v4l2_fwnode_endpoint) + { .bus_type = V4L2_MBUS_CCP2 }; + rval = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); + } + if (rval) goto out_err; hwcfg = devm_kzalloc(dev, sizeof(*hwcfg), GFP_KERNEL); if (!hwcfg) goto out_err; - switch (bus_cfg->bus_type) { - case V4L2_MBUS_CSI2: + switch (bus_cfg.bus_type) { + case V4L2_MBUS_CSI2_DPHY: hwcfg->csi_signalling_mode = SMIAPP_CSI_SIGNALLING_MODE_CSI2; - hwcfg->lanes = bus_cfg->bus.mipi_csi2.num_data_lanes; + hwcfg->lanes = bus_cfg.bus.mipi_csi2.num_data_lanes; break; case V4L2_MBUS_CCP2: - hwcfg->csi_signalling_mode = (bus_cfg->bus.mipi_csi1.strobe) ? + hwcfg->csi_signalling_mode = (bus_cfg.bus.mipi_csi1.strobe) ? SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_STROBE : SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_CLOCK; hwcfg->lanes = 1; break; default: - dev_err(dev, "unsupported bus %u\n", bus_cfg->bus_type); + dev_err(dev, "unsupported bus %u\n", bus_cfg.bus_type); goto out_err; } @@ -2827,28 +2829,28 @@ static struct smiapp_hwconfig *smiapp_get_hwconfig(struct device *dev) dev_dbg(dev, "nvm %d, clk %d, mode %d\n", hwcfg->nvm_size, hwcfg->ext_clk, hwcfg->csi_signalling_mode); - if (!bus_cfg->nr_of_link_frequencies) { + if (!bus_cfg.nr_of_link_frequencies) { dev_warn(dev, "no link frequencies defined\n"); goto out_err; } hwcfg->op_sys_clock = devm_kcalloc( - dev, bus_cfg->nr_of_link_frequencies + 1 /* guardian */, + dev, bus_cfg.nr_of_link_frequencies + 1 /* guardian */, sizeof(*hwcfg->op_sys_clock), GFP_KERNEL); if (!hwcfg->op_sys_clock) goto out_err; - for (i = 0; i < bus_cfg->nr_of_link_frequencies; i++) { - hwcfg->op_sys_clock[i] = bus_cfg->link_frequencies[i]; + for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++) { + hwcfg->op_sys_clock[i] = bus_cfg.link_frequencies[i]; dev_dbg(dev, "freq %d: %lld\n", i, hwcfg->op_sys_clock[i]); } - v4l2_fwnode_endpoint_free(bus_cfg); + v4l2_fwnode_endpoint_free(&bus_cfg); fwnode_handle_put(ep); return hwcfg; out_err: - v4l2_fwnode_endpoint_free(bus_cfg); + v4l2_fwnode_endpoint_free(&bus_cfg); fwnode_handle_put(ep); return NULL; } @@ -3064,9 +3066,9 @@ static int smiapp_probe(struct i2c_client *client, if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) sensor->pll.flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS; - smiapp_create_subdev(sensor, sensor->scaler, "scaler", 2); - smiapp_create_subdev(sensor, sensor->binner, "binner", 2); - smiapp_create_subdev(sensor, sensor->pixel_array, "pixel_array", 1); + smiapp_create_subdev(sensor, sensor->scaler, " scaler", 2); + smiapp_create_subdev(sensor, sensor->binner, " binner", 2); + smiapp_create_subdev(sensor, sensor->pixel_array, " pixel_array", 1); dev_dbg(&client->dev, "profile %d\n", sensor->minfo.smiapp_profile); diff --git a/drivers/media/i2c/soc_camera/Makefile b/drivers/media/i2c/soc_camera/Makefile index 8c7770f62997..09ae483b96ef 100644 --- a/drivers/media/i2c/soc_camera/Makefile +++ b/drivers/media/i2c/soc_camera/Makefile @@ -1,10 +1,10 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o -obj-$(CONFIG_SOC_CAMERA_MT9T112) += mt9t112.o -obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o -obj-$(CONFIG_SOC_CAMERA_OV5642) += ov5642.o -obj-$(CONFIG_SOC_CAMERA_OV772X) += ov772x.o -obj-$(CONFIG_SOC_CAMERA_OV9640) += ov9640.o -obj-$(CONFIG_SOC_CAMERA_OV9740) += ov9740.o -obj-$(CONFIG_SOC_CAMERA_RJ54N1) += rj54n1cb0c.o -obj-$(CONFIG_SOC_CAMERA_TW9910) += tw9910.o +obj-$(CONFIG_SOC_CAMERA_MT9M001) += soc_mt9m001.o +obj-$(CONFIG_SOC_CAMERA_MT9T112) += soc_mt9t112.o +obj-$(CONFIG_SOC_CAMERA_MT9V022) += soc_mt9v022.o +obj-$(CONFIG_SOC_CAMERA_OV5642) += soc_ov5642.o +obj-$(CONFIG_SOC_CAMERA_OV772X) += soc_ov772x.o +obj-$(CONFIG_SOC_CAMERA_OV9640) += soc_ov9640.o +obj-$(CONFIG_SOC_CAMERA_OV9740) += soc_ov9740.o +obj-$(CONFIG_SOC_CAMERA_RJ54N1) += soc_rj54n1cb0c.o +obj-$(CONFIG_SOC_CAMERA_TW9910) += soc_tw9910.o diff --git a/drivers/media/i2c/soc_camera/mt9m001.c b/drivers/media/i2c/soc_camera/soc_mt9m001.c index 1bfb0d53059e..a1a85ff838c5 100644 --- a/drivers/media/i2c/soc_camera/mt9m001.c +++ b/drivers/media/i2c/soc_camera/soc_mt9m001.c @@ -243,7 +243,6 @@ static int mt9m001_get_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP_DEFAULT: sel->r.left = MT9M001_COLUMN_SKIP; sel->r.top = MT9M001_ROW_SKIP; sel->r.width = MT9M001_MAX_WIDTH; diff --git a/drivers/media/i2c/soc_camera/mt9t112.c b/drivers/media/i2c/soc_camera/soc_mt9t112.c index b53c36dfa469..ea1ff270bc2d 100644 --- a/drivers/media/i2c/soc_camera/mt9t112.c +++ b/drivers/media/i2c/soc_camera/soc_mt9t112.c @@ -884,12 +884,6 @@ static int mt9t112_get_selection(struct v4l2_subdev *sd, sel->r.width = MAX_WIDTH; sel->r.height = MAX_HEIGHT; return 0; - case V4L2_SEL_TGT_CROP_DEFAULT: - sel->r.left = 0; - sel->r.top = 0; - sel->r.width = VGA_WIDTH; - sel->r.height = VGA_HEIGHT; - return 0; case V4L2_SEL_TGT_CROP: sel->r = priv->frame; return 0; diff --git a/drivers/media/i2c/soc_camera/mt9v022.c b/drivers/media/i2c/soc_camera/soc_mt9v022.c index 762f06919329..6d922b17ea94 100644 --- a/drivers/media/i2c/soc_camera/mt9v022.c +++ b/drivers/media/i2c/soc_camera/soc_mt9v022.c @@ -368,7 +368,6 @@ static int mt9v022_get_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP_DEFAULT: sel->r.left = MT9V022_COLUMN_SKIP; sel->r.top = MT9V022_ROW_SKIP; sel->r.width = MT9V022_MAX_WIDTH; diff --git a/drivers/media/i2c/soc_camera/ov5642.c b/drivers/media/i2c/soc_camera/soc_ov5642.c index 39f420db9c70..0931898c79dd 100644 --- a/drivers/media/i2c/soc_camera/ov5642.c +++ b/drivers/media/i2c/soc_camera/soc_ov5642.c @@ -896,7 +896,6 @@ static int ov5642_get_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP_DEFAULT: sel->r.left = 0; sel->r.top = 0; sel->r.width = OV5642_MAX_WIDTH; @@ -913,7 +912,7 @@ static int ov5642_get_selection(struct v4l2_subdev *sd, static int ov5642_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { - cfg->type = V4L2_MBUS_CSI2; + cfg->type = V4L2_MBUS_CSI2_DPHY; cfg->flags = V4L2_MBUS_CSI2_2_LANE | V4L2_MBUS_CSI2_CHANNEL_0 | V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; diff --git a/drivers/media/i2c/soc_camera/ov772x.c b/drivers/media/i2c/soc_camera/soc_ov772x.c index 14377af7c888..fafd372527b2 100644 --- a/drivers/media/i2c/soc_camera/ov772x.c +++ b/drivers/media/i2c/soc_camera/soc_ov772x.c @@ -862,7 +862,6 @@ static int ov772x_get_selection(struct v4l2_subdev *sd, sel->r.top = 0; switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP_DEFAULT: sel->r.width = OV772X_MAX_WIDTH; sel->r.height = OV772X_MAX_HEIGHT; return 0; diff --git a/drivers/media/i2c/soc_camera/ov9640.c b/drivers/media/i2c/soc_camera/soc_ov9640.c index c63948989688..eb91b8240083 100644 --- a/drivers/media/i2c/soc_camera/ov9640.c +++ b/drivers/media/i2c/soc_camera/soc_ov9640.c @@ -554,7 +554,6 @@ static int ov9640_get_selection(struct v4l2_subdev *sd, sel->r.top = 0; switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP_DEFAULT: case V4L2_SEL_TGT_CROP: sel->r.width = W_SXGA; sel->r.height = H_SXGA; diff --git a/drivers/media/i2c/soc_camera/ov9740.c b/drivers/media/i2c/soc_camera/soc_ov9740.c index 755de2289c39..a07d3145d1b4 100644 --- a/drivers/media/i2c/soc_camera/ov9740.c +++ b/drivers/media/i2c/soc_camera/soc_ov9740.c @@ -730,7 +730,6 @@ static int ov9740_get_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP_DEFAULT: case V4L2_SEL_TGT_CROP: sel->r.left = 0; sel->r.top = 0; diff --git a/drivers/media/i2c/soc_camera/rj54n1cb0c.c b/drivers/media/i2c/soc_camera/soc_rj54n1cb0c.c index 02398d0bc649..f0cb49a6167b 100644 --- a/drivers/media/i2c/soc_camera/rj54n1cb0c.c +++ b/drivers/media/i2c/soc_camera/soc_rj54n1cb0c.c @@ -591,7 +591,6 @@ static int rj54n1_get_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP_DEFAULT: sel->r.left = RJ54N1_COLUMN_SKIP; sel->r.top = RJ54N1_ROW_SKIP; sel->r.width = RJ54N1_MAX_WIDTH; diff --git a/drivers/media/i2c/soc_camera/tw9910.c b/drivers/media/i2c/soc_camera/soc_tw9910.c index bdb5e0a431e9..bdb5e0a431e9 100644 --- a/drivers/media/i2c/soc_camera/tw9910.c +++ b/drivers/media/i2c/soc_camera/soc_tw9910.c diff --git a/drivers/media/i2c/sr030pc30.c b/drivers/media/i2c/sr030pc30.c index 2a4882cddc51..11f6c7a5e0e7 100644 --- a/drivers/media/i2c/sr030pc30.c +++ b/drivers/media/i2c/sr030pc30.c @@ -569,7 +569,7 @@ static int sr030pc30_base_config(struct v4l2_subdev *sd) if (!ret) ret = sr030pc30_pwr_ctrl(sd, false, false); - if (!ret && !info->pdata) + if (ret) return ret; expmin = EXPOS_MIN_MS * info->pdata->clk_rate / (8 * 1000); @@ -703,7 +703,6 @@ static int sr030pc30_probe(struct i2c_client *client, return -ENOMEM; sd = &info->sd; - strcpy(sd->name, MODULE_NAME); info->pdata = client->dev.platform_data; v4l2_i2c_subdev_init(sd, client, &sr030pc30_ops); diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 44c41933415a..ca5d92942820 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -1243,9 +1243,9 @@ static int tc358743_log_status(struct v4l2_subdev *sd) u8 vi_status3 = i2c_rd8(sd, VI_STATUS3); const int deep_color_mode[4] = { 8, 10, 12, 16 }; static const char * const input_color_space[] = { - "RGB", "YCbCr 601", "Adobe RGB", "YCbCr 709", "NA (4)", + "RGB", "YCbCr 601", "opRGB", "YCbCr 709", "NA (4)", "xvYCC 601", "NA(6)", "xvYCC 709", "NA(8)", "sYCC601", - "NA(10)", "NA(11)", "NA(12)", "Adobe YCC 601"}; + "NA(10)", "NA(11)", "NA(12)", "opYCC 601"}; v4l2_info(sd, "-----Chip status-----\n"); v4l2_info(sd, "Chip ID: 0x%02x\n", @@ -1607,7 +1607,7 @@ static int tc358743_g_mbus_config(struct v4l2_subdev *sd, { struct tc358743_state *state = to_state(sd); - cfg->type = V4L2_MBUS_CSI2; + cfg->type = V4L2_MBUS_CSI2_DPHY; /* Support for non-continuous CSI-2 clock is missing in the driver */ cfg->flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; @@ -1789,7 +1789,7 @@ static int tc358743_s_edid(struct v4l2_subdev *sd, return -E2BIG; } pa = cec_get_edid_phys_addr(edid->edid, edid->blocks * 128, NULL); - err = cec_phys_addr_validate(pa, &pa, NULL); + err = v4l2_phys_addr_validate(pa, &pa, NULL); if (err) return err; @@ -1895,11 +1895,11 @@ static void tc358743_gpio_reset(struct tc358743_state *state) static int tc358743_probe_of(struct tc358743_state *state) { struct device *dev = &state->i2c_client->dev; - struct v4l2_fwnode_endpoint *endpoint; + struct v4l2_fwnode_endpoint endpoint = { .bus_type = 0 }; struct device_node *ep; struct clk *refclk; u32 bps_pr_lane; - int ret = -EINVAL; + int ret; refclk = devm_clk_get(dev, "refclk"); if (IS_ERR(refclk)) { @@ -1915,26 +1915,28 @@ static int tc358743_probe_of(struct tc358743_state *state) return -EINVAL; } - endpoint = v4l2_fwnode_endpoint_alloc_parse(of_fwnode_handle(ep)); - if (IS_ERR(endpoint)) { + ret = v4l2_fwnode_endpoint_alloc_parse(of_fwnode_handle(ep), &endpoint); + if (ret) { dev_err(dev, "failed to parse endpoint\n"); - ret = PTR_ERR(endpoint); + ret = ret; goto put_node; } - if (endpoint->bus_type != V4L2_MBUS_CSI2 || - endpoint->bus.mipi_csi2.num_data_lanes == 0 || - endpoint->nr_of_link_frequencies == 0) { + if (endpoint.bus_type != V4L2_MBUS_CSI2_DPHY || + endpoint.bus.mipi_csi2.num_data_lanes == 0 || + endpoint.nr_of_link_frequencies == 0) { dev_err(dev, "missing CSI-2 properties in endpoint\n"); + ret = -EINVAL; goto free_endpoint; } - if (endpoint->bus.mipi_csi2.num_data_lanes > 4) { + if (endpoint.bus.mipi_csi2.num_data_lanes > 4) { dev_err(dev, "invalid number of lanes\n"); + ret = -EINVAL; goto free_endpoint; } - state->bus = endpoint->bus.mipi_csi2; + state->bus = endpoint.bus.mipi_csi2; ret = clk_prepare_enable(refclk); if (ret) { @@ -1967,7 +1969,7 @@ static int tc358743_probe_of(struct tc358743_state *state) * The CSI bps per lane must be between 62.5 Mbps and 1 Gbps. * The default is 594 Mbps for 4-lane 1080p60 or 2-lane 720p60. */ - bps_pr_lane = 2 * endpoint->link_frequencies[0]; + bps_pr_lane = 2 * endpoint.link_frequencies[0]; if (bps_pr_lane < 62500000U || bps_pr_lane > 1000000000U) { dev_err(dev, "unsupported bps per lane: %u bps\n", bps_pr_lane); goto disable_clk; @@ -2013,7 +2015,7 @@ static int tc358743_probe_of(struct tc358743_state *state) disable_clk: clk_disable_unprepare(refclk); free_endpoint: - v4l2_fwnode_endpoint_free(endpoint); + v4l2_fwnode_endpoint_free(&endpoint); put_node: of_node_put(ep); return ret; diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c index d114ac5243ec..c4c2a6134e1e 100644 --- a/drivers/media/i2c/tda1997x.c +++ b/drivers/media/i2c/tda1997x.c @@ -2265,7 +2265,7 @@ MODULE_DEVICE_TABLE(of, tda1997x_of_id); static int tda1997x_parse_dt(struct tda1997x_state *state) { struct tda1997x_platform_data *pdata = &state->pdata; - struct v4l2_fwnode_endpoint bus_cfg; + struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 }; struct device_node *ep; struct device_node *np; unsigned int flags; diff --git a/drivers/media/i2c/tvaudio.c b/drivers/media/i2c/tvaudio.c index 5919214a56bf..af2da977a685 100644 --- a/drivers/media/i2c/tvaudio.c +++ b/drivers/media/i2c/tvaudio.c @@ -1981,7 +1981,7 @@ static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id * /* fill required data structures */ if (!id) - strlcpy(client->name, desc->name, I2C_NAME_SIZE); + strscpy(client->name, desc->name, I2C_NAME_SIZE); chip->desc = desc; chip->shadow.count = desc->registers+1; chip->prevmode = -1; diff --git a/drivers/media/i2c/tvp514x.c b/drivers/media/i2c/tvp514x.c index 675b9ae212ab..1cc83cb934e2 100644 --- a/drivers/media/i2c/tvp514x.c +++ b/drivers/media/i2c/tvp514x.c @@ -989,7 +989,7 @@ static struct tvp514x_platform_data * tvp514x_get_pdata(struct i2c_client *client) { struct tvp514x_platform_data *pdata = NULL; - struct v4l2_fwnode_endpoint bus_cfg; + struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 }; struct device_node *endpoint; unsigned int flags; diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 76e6bed5a1da..0e91b9949c3a 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -10,8 +10,10 @@ #include <linux/videodev2.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> +#include <linux/interrupt.h> #include <linux/module.h> #include <linux/of_graph.h> +#include <linux/regmap.h> #include <media/v4l2-async.h> #include <media/v4l2-device.h> #include <media/v4l2-ctrls.h> @@ -26,6 +28,9 @@ #define TVP5150_MAX_CROP_LEFT 511 #define TVP5150_MAX_CROP_TOP 127 #define TVP5150_CROP_SHIFT 2 +#define TVP5150_MBUS_FMT MEDIA_BUS_FMT_UYVY8_2X8 +#define TVP5150_FIELD V4L2_FIELD_ALTERNATE +#define TVP5150_COLORSPACE V4L2_COLORSPACE_SMPTE170M MODULE_DESCRIPTION("Texas Instruments TVP5150A/TVP5150AM1/TVP5151 video decoder driver"); MODULE_AUTHOR("Mauro Carvalho Chehab"); @@ -38,20 +43,31 @@ MODULE_PARM_DESC(debug, "Debug level (0-2)"); #define dprintk0(__dev, __arg...) dev_dbg_lvl(__dev, 0, 0, __arg) +enum tvp5150_pads { + TVP5150_PAD_IF_INPUT, + TVP5150_PAD_VID_OUT, + TVP5150_NUM_PADS +}; + struct tvp5150 { struct v4l2_subdev sd; #ifdef CONFIG_MEDIA_CONTROLLER - struct media_pad pads[DEMOD_NUM_PADS]; + struct media_pad pads[TVP5150_NUM_PADS]; struct media_entity input_ent[TVP5150_INPUT_NUM]; struct media_pad input_pad[TVP5150_INPUT_NUM]; #endif struct v4l2_ctrl_handler hdl; struct v4l2_rect rect; + struct regmap *regmap; + int irq; v4l2_std_id norm; /* Current set standard */ + v4l2_std_id detected_norm; u32 input; u32 output; + u32 oe; int enable; + bool lock; u16 dev_id; u16 rom_ver; @@ -71,32 +87,14 @@ static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) static int tvp5150_read(struct v4l2_subdev *sd, unsigned char addr) { - struct i2c_client *c = v4l2_get_subdevdata(sd); - int rc; - - rc = i2c_smbus_read_byte_data(c, addr); - if (rc < 0) { - dev_err(sd->dev, "i2c i/o error: rc == %d\n", rc); - return rc; - } - - dev_dbg_lvl(sd->dev, 2, debug, "tvp5150: read 0x%02x = %02x\n", addr, rc); - - return rc; -} - -static int tvp5150_write(struct v4l2_subdev *sd, unsigned char addr, - unsigned char value) -{ - struct i2c_client *c = v4l2_get_subdevdata(sd); - int rc; + struct tvp5150 *decoder = to_tvp5150(sd); + int ret, val; - dev_dbg_lvl(sd->dev, 2, debug, "tvp5150: writing %02x %02x\n", addr, value); - rc = i2c_smbus_write_byte_data(c, addr, value); - if (rc < 0) - dev_err(sd->dev, "i2c i/o error: rc == %d\n", rc); + ret = regmap_read(decoder->regmap, addr, &val); + if (ret < 0) + return ret; - return rc; + return val; } static void dump_reg_range(struct v4l2_subdev *sd, char *s, u8 init, @@ -262,8 +260,8 @@ static void tvp5150_selmux(struct v4l2_subdev *sd) { int opmode = 0; struct tvp5150 *decoder = to_tvp5150(sd); + unsigned int mask, val; int input = 0; - int val; /* Only tvp5150am1 and tvp5151 have signal generator support */ if ((decoder->dev_id == 0x5150 && decoder->rom_ver == 0x0400) || @@ -288,8 +286,8 @@ static void tvp5150_selmux(struct v4l2_subdev *sd) decoder->input, decoder->output, input, opmode); - tvp5150_write(sd, TVP5150_OP_MODE_CTL, opmode); - tvp5150_write(sd, TVP5150_VD_IN_SRC_SEL_1, input); + regmap_write(decoder->regmap, TVP5150_OP_MODE_CTL, opmode); + regmap_write(decoder->regmap, TVP5150_VD_IN_SRC_SEL_1, input); /* * Setup the FID/GLCO/VLK/HVLK and INTREQ/GPCL/VBLK output signals. For @@ -298,17 +296,12 @@ static void tvp5150_selmux(struct v4l2_subdev *sd) * field indicator (FID) signal on FID/GLCO/VLK/HVLK and set * INTREQ/GPCL/VBLK to logic 1. */ - val = tvp5150_read(sd, TVP5150_MISC_CTL); - if (val < 0) { - dev_err(sd->dev, "%s: failed with error = %d\n", __func__, val); - return; - } - + mask = TVP5150_MISC_CTL_GPCL | TVP5150_MISC_CTL_HVLK; if (decoder->input == TVP5150_SVIDEO) - val = (val & ~TVP5150_MISC_CTL_GPCL) | TVP5150_MISC_CTL_HVLK; + val = TVP5150_MISC_CTL_HVLK; else - val = (val & ~TVP5150_MISC_CTL_HVLK) | TVP5150_MISC_CTL_GPCL; - tvp5150_write(sd, TVP5150_MISC_CTL, val); + val = TVP5150_MISC_CTL_GPCL; + regmap_update_bits(decoder->regmap, TVP5150_MISC_CTL, mask, val); }; struct i2c_reg_value { @@ -454,9 +447,7 @@ static const struct i2c_reg_value tvp5150_init_default[] = { /* Default values as sugested at TVP5150AM1 datasheet */ static const struct i2c_reg_value tvp5150_init_enable[] = { - { - TVP5150_CONF_SHARED_PIN, 2 - }, { /* Automatic offset and AGC enabled */ + { /* Automatic offset and AGC enabled */ TVP5150_ANAL_CHL_CTL, 0x15 }, { /* Activate YCrCb output 0x9 or 0xd ? */ TVP5150_MISC_CTL, TVP5150_MISC_CTL_GPCL | @@ -583,8 +574,10 @@ static struct i2c_vbi_ram_value vbi_ram_default[] = { static int tvp5150_write_inittab(struct v4l2_subdev *sd, const struct i2c_reg_value *regs) { + struct tvp5150 *decoder = to_tvp5150(sd); + while (regs->reg != 0xff) { - tvp5150_write(sd, regs->reg, regs->value); + regmap_write(decoder->regmap, regs->reg, regs->value); regs++; } return 0; @@ -592,15 +585,17 @@ static int tvp5150_write_inittab(struct v4l2_subdev *sd, static int tvp5150_vdp_init(struct v4l2_subdev *sd) { + struct tvp5150 *decoder = to_tvp5150(sd); + struct regmap *map = decoder->regmap; unsigned int i; int j; /* Disable Full Field */ - tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0); + regmap_write(map, TVP5150_FULL_FIELD_ENA, 0); /* Before programming, Line mode should be at 0xff */ for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++) - tvp5150_write(sd, i, 0xff); + regmap_write(map, i, 0xff); /* Load Ram Table */ for (j = 0; j < ARRAY_SIZE(vbi_ram_default); j++) { @@ -609,11 +604,12 @@ static int tvp5150_vdp_init(struct v4l2_subdev *sd) if (!regs->type.vbi_type) continue; - tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_HIGH, regs->reg >> 8); - tvp5150_write(sd, TVP5150_CONF_RAM_ADDR_LOW, regs->reg); + regmap_write(map, TVP5150_CONF_RAM_ADDR_HIGH, regs->reg >> 8); + regmap_write(map, TVP5150_CONF_RAM_ADDR_LOW, regs->reg); for (i = 0; i < 16; i++) - tvp5150_write(sd, TVP5150_VDP_CONF_RAM_DATA, regs->values[i]); + regmap_write(map, TVP5150_VDP_CONF_RAM_DATA, + regs->values[i]); } return 0; } @@ -693,10 +689,10 @@ static int tvp5150_set_vbi(struct v4l2_subdev *sd, reg = ((line - 6) << 1) + TVP5150_LINE_MODE_INI; if (fields & 1) - tvp5150_write(sd, reg, type); + regmap_write(decoder->regmap, reg, type); if (fields & 2) - tvp5150_write(sd, reg + 1, type); + regmap_write(decoder->regmap, reg + 1, type); return type; } @@ -742,8 +738,6 @@ static int tvp5150_set_std(struct v4l2_subdev *sd, v4l2_std_id std) struct tvp5150 *decoder = to_tvp5150(sd); int fmt = 0; - decoder->norm = std; - /* First tests should be against specific std */ if (std == V4L2_STD_NTSC_443) { @@ -763,7 +757,16 @@ static int tvp5150_set_std(struct v4l2_subdev *sd, v4l2_std_id std) } dev_dbg_lvl(sd->dev, 1, debug, "Set video std register to %d.\n", fmt); - tvp5150_write(sd, TVP5150_VIDEO_STD, fmt); + regmap_write(decoder->regmap, TVP5150_VIDEO_STD, fmt); + return 0; +} + +static int tvp5150_g_std(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + struct tvp5150 *decoder = to_tvp5150(sd); + + *std = decoder->norm; + return 0; } @@ -780,33 +783,166 @@ static int tvp5150_s_std(struct v4l2_subdev *sd, v4l2_std_id std) else decoder->rect.height = TVP5150_V_MAX_OTHERS; + decoder->norm = std; return tvp5150_set_std(sd, std); } +static v4l2_std_id tvp5150_read_std(struct v4l2_subdev *sd) +{ + int val = tvp5150_read(sd, TVP5150_STATUS_REG_5); + + switch (val & 0x0F) { + case 0x01: + return V4L2_STD_NTSC; + case 0x03: + return V4L2_STD_PAL; + case 0x05: + return V4L2_STD_PAL_M; + case 0x07: + return V4L2_STD_PAL_N | V4L2_STD_PAL_Nc; + case 0x09: + return V4L2_STD_NTSC_443; + case 0xb: + return V4L2_STD_SECAM; + default: + return V4L2_STD_UNKNOWN; + } +} + +static int query_lock(struct v4l2_subdev *sd) +{ + struct tvp5150 *decoder = to_tvp5150(sd); + int status; + + if (decoder->irq) + return decoder->lock; + + regmap_read(decoder->regmap, TVP5150_STATUS_REG_1, &status); + + /* For standard detection, we need the 3 locks */ + return (status & 0x0e) == 0x0e; +} + +static int tvp5150_querystd(struct v4l2_subdev *sd, v4l2_std_id *std_id) +{ + *std_id = query_lock(sd) ? tvp5150_read_std(sd) : V4L2_STD_UNKNOWN; + + return 0; +} + +static const struct v4l2_event tvp5150_ev_fmt = { + .type = V4L2_EVENT_SOURCE_CHANGE, + .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, +}; + +static irqreturn_t tvp5150_isr(int irq, void *dev_id) +{ + struct tvp5150 *decoder = dev_id; + struct regmap *map = decoder->regmap; + unsigned int mask, active = 0, status = 0; + + mask = TVP5150_MISC_CTL_YCBCR_OE | TVP5150_MISC_CTL_SYNC_OE | + TVP5150_MISC_CTL_CLOCK_OE; + + regmap_read(map, TVP5150_INT_STATUS_REG_A, &status); + if (status) { + regmap_write(map, TVP5150_INT_STATUS_REG_A, status); + + if (status & TVP5150_INT_A_LOCK) { + decoder->lock = !!(status & TVP5150_INT_A_LOCK_STATUS); + dev_dbg_lvl(decoder->sd.dev, 1, debug, + "sync lo%s signal\n", + decoder->lock ? "ck" : "ss"); + v4l2_subdev_notify_event(&decoder->sd, &tvp5150_ev_fmt); + regmap_update_bits(map, TVP5150_MISC_CTL, mask, + decoder->lock ? decoder->oe : 0); + } + + return IRQ_HANDLED; + } + + regmap_read(map, TVP5150_INT_ACTIVE_REG_B, &active); + if (active) { + status = 0; + regmap_read(map, TVP5150_INT_STATUS_REG_B, &status); + if (status) + regmap_write(map, TVP5150_INT_RESET_REG_B, status); + } + + return IRQ_HANDLED; +} + static int tvp5150_reset(struct v4l2_subdev *sd, u32 val) { struct tvp5150 *decoder = to_tvp5150(sd); + struct regmap *map = decoder->regmap; /* Initializes TVP5150 to its default values */ tvp5150_write_inittab(sd, tvp5150_init_default); + if (decoder->irq) { + /* Configure pins: FID, VSYNC, INTREQ, SCLK */ + regmap_write(map, TVP5150_CONF_SHARED_PIN, 0x0); + /* Set interrupt polarity to active high */ + regmap_write(map, TVP5150_INT_CONF, TVP5150_VDPOE | 0x1); + regmap_write(map, TVP5150_INTT_CONFIG_REG_B, 0x1); + } else { + /* Configure pins: FID, VSYNC, GPCL/VBLK, SCLK */ + regmap_write(map, TVP5150_CONF_SHARED_PIN, 0x2); + /* Keep interrupt polarity active low */ + regmap_write(map, TVP5150_INT_CONF, TVP5150_VDPOE); + regmap_write(map, TVP5150_INTT_CONFIG_REG_B, 0x0); + } + /* Initializes VDP registers */ tvp5150_vdp_init(sd); /* Selects decoder input */ tvp5150_selmux(sd); + /* Initialize image preferences */ + v4l2_ctrl_handler_setup(&decoder->hdl); + + return 0; +} + +static int tvp5150_enable(struct v4l2_subdev *sd) +{ + struct tvp5150 *decoder = to_tvp5150(sd); + v4l2_std_id std; + /* Initializes TVP5150 to stream enabled values */ tvp5150_write_inittab(sd, tvp5150_init_enable); - /* Initialize image preferences */ - v4l2_ctrl_handler_setup(&decoder->hdl); + if (decoder->norm == V4L2_STD_ALL) + std = tvp5150_read_std(sd); + else + std = decoder->norm; - tvp5150_set_std(sd, decoder->norm); + /* Disable autoswitch mode */ + tvp5150_set_std(sd, std); - if (decoder->mbus_type == V4L2_MBUS_PARALLEL) - tvp5150_write(sd, TVP5150_DATA_RATE_SEL, 0x40); + /* + * Enable the YCbCr and clock outputs. In discrete sync mode + * (non-BT.656) additionally enable the the sync outputs. + */ + switch (decoder->mbus_type) { + case V4L2_MBUS_PARALLEL: + /* 8-bit 4:2:2 YUV with discrete sync output */ + regmap_update_bits(decoder->regmap, TVP5150_DATA_RATE_SEL, + 0x7, 0x0); + decoder->oe = TVP5150_MISC_CTL_YCBCR_OE | + TVP5150_MISC_CTL_CLOCK_OE | + TVP5150_MISC_CTL_SYNC_OE; + break; + case V4L2_MBUS_BT656: + decoder->oe = TVP5150_MISC_CTL_YCBCR_OE | + TVP5150_MISC_CTL_CLOCK_OE; + break; + default: + return -EINVAL; + } return 0; }; @@ -818,17 +954,18 @@ static int tvp5150_s_ctrl(struct v4l2_ctrl *ctrl) switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - tvp5150_write(sd, TVP5150_BRIGHT_CTL, ctrl->val); + regmap_write(decoder->regmap, TVP5150_BRIGHT_CTL, ctrl->val); return 0; case V4L2_CID_CONTRAST: - tvp5150_write(sd, TVP5150_CONTRAST_CTL, ctrl->val); + regmap_write(decoder->regmap, TVP5150_CONTRAST_CTL, ctrl->val); return 0; case V4L2_CID_SATURATION: - tvp5150_write(sd, TVP5150_SATURATION_CTL, ctrl->val); + regmap_write(decoder->regmap, TVP5150_SATURATION_CTL, + ctrl->val); return 0; case V4L2_CID_HUE: - tvp5150_write(sd, TVP5150_HUE_CTL, ctrl->val); - break; + regmap_write(decoder->regmap, TVP5150_HUE_CTL, ctrl->val); + return 0; case V4L2_CID_TEST_PATTERN: decoder->enable = ctrl->val ? false : true; tvp5150_selmux(sd); @@ -837,36 +974,26 @@ static int tvp5150_s_ctrl(struct v4l2_ctrl *ctrl) return -EINVAL; } -static v4l2_std_id tvp5150_read_std(struct v4l2_subdev *sd) +static void tvp5150_set_default(v4l2_std_id std, struct v4l2_rect *crop) { - int val = tvp5150_read(sd, TVP5150_STATUS_REG_5); - - switch (val & 0x0F) { - case 0x01: - return V4L2_STD_NTSC; - case 0x03: - return V4L2_STD_PAL; - case 0x05: - return V4L2_STD_PAL_M; - case 0x07: - return V4L2_STD_PAL_N | V4L2_STD_PAL_Nc; - case 0x09: - return V4L2_STD_NTSC_443; - case 0xb: - return V4L2_STD_SECAM; - default: - return V4L2_STD_UNKNOWN; - } + /* Default is no cropping */ + crop->top = 0; + crop->left = 0; + crop->width = TVP5150_H_MAX; + if (std & V4L2_STD_525_60) + crop->height = TVP5150_V_MAX_525_60; + else + crop->height = TVP5150_V_MAX_OTHERS; } static int tvp5150_fill_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *f; struct tvp5150 *decoder = to_tvp5150(sd); - if (!format || (format->pad != DEMOD_PAD_VID_OUT)) + if (!format || (format->pad != TVP5150_PAD_VID_OUT)) return -EINVAL; f = &format->format; @@ -874,12 +1001,12 @@ static int tvp5150_fill_fmt(struct v4l2_subdev *sd, f->width = decoder->rect.width; f->height = decoder->rect.height / 2; - f->code = MEDIA_BUS_FMT_UYVY8_2X8; - f->field = V4L2_FIELD_ALTERNATE; - f->colorspace = V4L2_COLORSPACE_SMPTE170M; + f->code = TVP5150_MBUS_FMT; + f->field = TVP5150_FIELD; + f->colorspace = TVP5150_COLORSPACE; dev_dbg_lvl(sd->dev, 1, debug, "width = %d, height = %d\n", f->width, - f->height); + f->height); return 0; } @@ -901,9 +1028,6 @@ static int tvp5150_set_selection(struct v4l2_subdev *sd, /* tvp5150 has some special limits */ rect.left = clamp(rect.left, 0, TVP5150_MAX_CROP_LEFT); - rect.width = clamp_t(unsigned int, rect.width, - TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect.left, - TVP5150_H_MAX - rect.left); rect.top = clamp(rect.top, 0, TVP5150_MAX_CROP_TOP); /* Calculate height based on current standard */ @@ -917,22 +1041,29 @@ static int tvp5150_set_selection(struct v4l2_subdev *sd, else hmax = TVP5150_V_MAX_OTHERS; - rect.height = clamp_t(unsigned int, rect.height, + /* + * alignments: + * - width = 2 due to UYVY colorspace + * - height, image = no special alignment + */ + v4l_bound_align_image(&rect.width, + TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect.left, + TVP5150_H_MAX - rect.left, 1, &rect.height, hmax - TVP5150_MAX_CROP_TOP - rect.top, - hmax - rect.top); - - tvp5150_write(sd, TVP5150_VERT_BLANKING_START, rect.top); - tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, - rect.top + rect.height - hmax); - tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_MSB, - rect.left >> TVP5150_CROP_SHIFT); - tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_LSB, - rect.left | (1 << TVP5150_CROP_SHIFT)); - tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_MSB, - (rect.left + rect.width - TVP5150_MAX_CROP_LEFT) >> - TVP5150_CROP_SHIFT); - tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_LSB, - rect.left + rect.width - TVP5150_MAX_CROP_LEFT); + hmax - rect.top, 0, 0); + + regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_START, rect.top); + regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_STOP, + rect.top + rect.height - hmax); + regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_ST_MSB, + rect.left >> TVP5150_CROP_SHIFT); + regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_ST_LSB, + rect.left | (1 << TVP5150_CROP_SHIFT)); + regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_STP_MSB, + (rect.left + rect.width - TVP5150_MAX_CROP_LEFT) >> + TVP5150_CROP_SHIFT); + regmap_write(decoder->regmap, TVP5150_ACT_VD_CROP_STP_LSB, + rect.left + rect.width - TVP5150_MAX_CROP_LEFT); decoder->rect = rect; @@ -951,7 +1082,6 @@ static int tvp5150_get_selection(struct v4l2_subdev *sd, switch (sel->target) { case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP_DEFAULT: sel->r.left = 0; sel->r.top = 0; sel->r.width = TVP5150_H_MAX; @@ -989,6 +1119,27 @@ static int tvp5150_g_mbus_config(struct v4l2_subdev *sd, /**************************************************************************** V4L2 subdev pad ops ****************************************************************************/ +static int tvp5150_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg) +{ + struct tvp5150 *decoder = to_tvp5150(sd); + v4l2_std_id std; + + /* + * Reset selection to maximum on subdev_open() if autodetection is on + * and a standard change is detected. + */ + if (decoder->norm == V4L2_STD_ALL) { + std = tvp5150_read_std(sd); + if (std != decoder->detected_norm) { + decoder->detected_norm = std; + tvp5150_set_default(std, &decoder->rect); + } + } + + return 0; +} + static int tvp5150_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) @@ -996,7 +1147,7 @@ static int tvp5150_enum_mbus_code(struct v4l2_subdev *sd, if (code->pad || code->index) return -EINVAL; - code->code = MEDIA_BUS_FMT_UYVY8_2X8; + code->code = TVP5150_MBUS_FMT; return 0; } @@ -1006,10 +1157,10 @@ static int tvp5150_enum_frame_size(struct v4l2_subdev *sd, { struct tvp5150 *decoder = to_tvp5150(sd); - if (fse->index >= 8 || fse->code != MEDIA_BUS_FMT_UYVY8_2X8) + if (fse->index >= 8 || fse->code != TVP5150_MBUS_FMT) return -EINVAL; - fse->code = MEDIA_BUS_FMT_UYVY8_2X8; + fse->code = TVP5150_MBUS_FMT; fse->min_width = decoder->rect.width; fse->max_width = decoder->rect.width; fse->min_height = decoder->rect.height / 2; @@ -1059,27 +1210,28 @@ static const struct media_entity_operations tvp5150_sd_media_ops = { static int tvp5150_s_stream(struct v4l2_subdev *sd, int enable) { struct tvp5150 *decoder = to_tvp5150(sd); - int val; + unsigned int mask, val = 0, int_val = 0; - /* Enable or disable the video output signals. */ - val = tvp5150_read(sd, TVP5150_MISC_CTL); - if (val < 0) - return val; - - val &= ~(TVP5150_MISC_CTL_YCBCR_OE | TVP5150_MISC_CTL_SYNC_OE | - TVP5150_MISC_CTL_CLOCK_OE); + mask = TVP5150_MISC_CTL_YCBCR_OE | TVP5150_MISC_CTL_SYNC_OE | + TVP5150_MISC_CTL_CLOCK_OE; if (enable) { - /* - * Enable the YCbCr and clock outputs. In discrete sync mode - * (non-BT.656) additionally enable the the sync outputs. - */ - val |= TVP5150_MISC_CTL_YCBCR_OE | TVP5150_MISC_CTL_CLOCK_OE; - if (decoder->mbus_type == V4L2_MBUS_PARALLEL) - val |= TVP5150_MISC_CTL_SYNC_OE; + tvp5150_enable(sd); + + /* Enable outputs if decoder is locked */ + if (decoder->irq) + val = decoder->lock ? decoder->oe : 0; + else + val = decoder->oe; + int_val = TVP5150_INT_A_LOCK; + v4l2_subdev_notify_event(&decoder->sd, &tvp5150_ev_fmt); } - tvp5150_write(sd, TVP5150_MISC_CTL, val); + regmap_update_bits(decoder->regmap, TVP5150_MISC_CTL, mask, val); + if (decoder->irq) + /* Enable / Disable lock interrupt */ + regmap_update_bits(decoder->regmap, TVP5150_INT_ENABLE_REG_A, + TVP5150_INT_A_LOCK, int_val); return 0; } @@ -1103,6 +1255,8 @@ static int tvp5150_s_routing(struct v4l2_subdev *sd, static int tvp5150_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt) { + struct tvp5150 *decoder = to_tvp5150(sd); + /* * this is for capturing 36 raw vbi lines * if there's a way to cut off the beginning 2 vbi lines @@ -1112,16 +1266,18 @@ static int tvp5150_s_raw_fmt(struct v4l2_subdev *sd, struct v4l2_vbi_format *fmt */ if (fmt->sample_format == V4L2_PIX_FMT_GREY) - tvp5150_write(sd, TVP5150_LUMA_PROC_CTL_1, 0x70); + regmap_write(decoder->regmap, TVP5150_LUMA_PROC_CTL_1, 0x70); if (fmt->count[0] == 18 && fmt->count[1] == 18) { - tvp5150_write(sd, TVP5150_VERT_BLANKING_START, 0x00); - tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP, 0x01); + regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_START, + 0x00); + regmap_write(decoder->regmap, TVP5150_VERT_BLANKING_STOP, 0x01); } return 0; } static int tvp5150_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_format *svbi) { + struct tvp5150 *decoder = to_tvp5150(sd); int i; if (svbi->service_set != 0) { @@ -1132,17 +1288,17 @@ static int tvp5150_s_sliced_fmt(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_f 0xf0, i, 3); } /* Enables FIFO */ - tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 1); + regmap_write(decoder->regmap, TVP5150_FIFO_OUT_CTRL, 1); } else { /* Disables FIFO*/ - tvp5150_write(sd, TVP5150_FIFO_OUT_CTRL, 0); + regmap_write(decoder->regmap, TVP5150_FIFO_OUT_CTRL, 0); /* Disable Full Field */ - tvp5150_write(sd, TVP5150_FULL_FIELD_ENA, 0); + regmap_write(decoder->regmap, TVP5150_FULL_FIELD_ENA, 0); /* Disable Line modes */ for (i = TVP5150_LINE_MODE_INI; i <= TVP5150_LINE_MODE_END; i++) - tvp5150_write(sd, i, 0xff); + regmap_write(decoder->regmap, i, 0xff); } return 0; } @@ -1180,7 +1336,9 @@ static int tvp5150_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register * static int tvp5150_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) { - return tvp5150_write(sd, reg->reg & 0xff, reg->val & 0xff); + struct tvp5150 *decoder = to_tvp5150(sd); + + return regmap_write(decoder->regmap, reg->reg & 0xff, reg->val & 0xff); } #endif @@ -1217,7 +1375,7 @@ static int tvp5150_registered(struct v4l2_subdev *sd) return ret; ret = media_create_pad_link(input, 0, &sd->entity, - DEMOD_PAD_IF_INPUT, 0); + TVP5150_PAD_IF_INPUT, 0); if (ret < 0) { media_device_unregister_entity(input); return ret; @@ -1249,6 +1407,8 @@ static const struct v4l2_subdev_tuner_ops tvp5150_tuner_ops = { static const struct v4l2_subdev_video_ops tvp5150_video_ops = { .s_std = tvp5150_s_std, + .g_std = tvp5150_g_std, + .querystd = tvp5150_querystd, .s_stream = tvp5150_s_stream, .s_routing = tvp5150_s_routing, .g_mbus_config = tvp5150_g_mbus_config, @@ -1262,6 +1422,7 @@ static const struct v4l2_subdev_vbi_ops tvp5150_vbi_ops = { }; static const struct v4l2_subdev_pad_ops tvp5150_pad_ops = { + .init_cfg = tvp5150_init_cfg, .enum_mbus_code = tvp5150_enum_mbus_code, .enum_frame_size = tvp5150_enum_frame_size, .set_fmt = tvp5150_fill_fmt, @@ -1282,16 +1443,87 @@ static const struct v4l2_subdev_internal_ops tvp5150_internal_ops = { .registered = tvp5150_registered, }; - /**************************************************************************** I2C Client & Driver ****************************************************************************/ +static const struct regmap_range tvp5150_readable_ranges[] = { + { + .range_min = TVP5150_VD_IN_SRC_SEL_1, + .range_max = TVP5150_AUTOSW_MSK, + }, { + .range_min = TVP5150_COLOR_KIL_THSH_CTL, + .range_max = TVP5150_CONF_SHARED_PIN, + }, { + .range_min = TVP5150_ACT_VD_CROP_ST_MSB, + .range_max = TVP5150_HORIZ_SYNC_START, + }, { + .range_min = TVP5150_VERT_BLANKING_START, + .range_max = TVP5150_INTT_CONFIG_REG_B, + }, { + .range_min = TVP5150_VIDEO_STD, + .range_max = TVP5150_VIDEO_STD, + }, { + .range_min = TVP5150_CB_GAIN_FACT, + .range_max = TVP5150_REV_SELECT, + }, { + .range_min = TVP5150_MSB_DEV_ID, + .range_max = TVP5150_STATUS_REG_5, + }, { + .range_min = TVP5150_CC_DATA_INI, + .range_max = TVP5150_TELETEXT_FIL_ENA, + }, { + .range_min = TVP5150_INT_STATUS_REG_A, + .range_max = TVP5150_FIFO_OUT_CTRL, + }, { + .range_min = TVP5150_FULL_FIELD_ENA, + .range_max = TVP5150_FULL_FIELD_MODE_REG, + }, +}; + +static bool tvp5150_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TVP5150_VERT_LN_COUNT_MSB: + case TVP5150_VERT_LN_COUNT_LSB: + case TVP5150_INT_STATUS_REG_A: + case TVP5150_INT_STATUS_REG_B: + case TVP5150_INT_ACTIVE_REG_B: + case TVP5150_STATUS_REG_1: + case TVP5150_STATUS_REG_2: + case TVP5150_STATUS_REG_3: + case TVP5150_STATUS_REG_4: + case TVP5150_STATUS_REG_5: + /* CC, WSS, VPS, VITC data? */ + case TVP5150_VBI_FIFO_READ_DATA: + case TVP5150_VDP_STATUS_REG: + case TVP5150_FIFO_WORD_COUNT: + return true; + default: + return false; + } +} + +static const struct regmap_access_table tvp5150_readable_table = { + .yes_ranges = tvp5150_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(tvp5150_readable_ranges), +}; + +static struct regmap_config tvp5150_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xff, + + .cache_type = REGCACHE_RBTREE, + + .rd_table = &tvp5150_readable_table, + .volatile_reg = tvp5150_volatile_reg, +}; + static int tvp5150_detect_version(struct tvp5150 *core) { struct v4l2_subdev *sd = &core->sd; struct i2c_client *c = v4l2_get_subdevdata(sd); - unsigned int i; u8 regs[4]; int res; @@ -1299,11 +1531,10 @@ static int tvp5150_detect_version(struct tvp5150 *core) * Read consequent registers - TVP5150_MSB_DEV_ID, TVP5150_LSB_DEV_ID, * TVP5150_ROM_MAJOR_VER, TVP5150_ROM_MINOR_VER */ - for (i = 0; i < 4; i++) { - res = tvp5150_read(sd, TVP5150_MSB_DEV_ID + i); - if (res < 0) - return res; - regs[i] = res; + res = regmap_bulk_read(core->regmap, TVP5150_MSB_DEV_ID, regs, 4); + if (res < 0) { + dev_err(&c->dev, "reading ID registers failed: %d\n", res); + return res; } core->dev_id = (regs[0] << 8) | regs[1]; @@ -1319,7 +1550,7 @@ static int tvp5150_detect_version(struct tvp5150 *core) dev_info(sd->dev, "tvp5150am1 detected.\n"); /* ITU-T BT.656.4 timing */ - tvp5150_write(sd, TVP5150_REV_SELECT, 0); + regmap_write(core->regmap, TVP5150_REV_SELECT, 0); } else if (core->dev_id == 0x5151 && core->rom_ver == 0x0100) { dev_info(sd->dev, "tvp5151 detected.\n"); } else { @@ -1362,7 +1593,7 @@ static int tvp5150_init(struct i2c_client *c) static int tvp5150_parse_dt(struct tvp5150 *decoder, struct device_node *np) { - struct v4l2_fwnode_endpoint bus_cfg; + struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 }; struct device_node *ep; #ifdef CONFIG_MEDIA_CONTROLLER struct device_node *connectors, *child; @@ -1403,8 +1634,8 @@ static int tvp5150_parse_dt(struct tvp5150 *decoder, struct device_node *np) ret = of_property_read_u32(child, "input", &input_type); if (ret) { dev_err(decoder->sd.dev, - "missing type property in node %s\n", - child->name); + "missing type property in node %pOFn\n", + child); goto err_connector; } @@ -1439,8 +1670,8 @@ static int tvp5150_parse_dt(struct tvp5150 *decoder, struct device_node *np) ret = of_property_read_string(child, "label", &name); if (ret < 0) { dev_err(decoder->sd.dev, - "missing label property in node %s\n", - child->name); + "missing label property in node %pOFn\n", + child); goto err_connector; } @@ -1466,6 +1697,7 @@ static int tvp5150_probe(struct i2c_client *c, struct tvp5150 *core; struct v4l2_subdev *sd; struct device_node *np = c->dev.of_node; + struct regmap *map; int res; /* Check if the adapter supports the needed features */ @@ -1481,6 +1713,11 @@ static int tvp5150_probe(struct i2c_client *c, if (!core) return -ENOMEM; + map = devm_regmap_init_i2c(c, &tvp5150_config); + if (IS_ERR(map)) + return PTR_ERR(map); + + core->regmap = map; sd = &core->sd; if (IS_ENABLED(CONFIG_OF) && np) { @@ -1499,13 +1736,14 @@ static int tvp5150_probe(struct i2c_client *c, sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; #if defined(CONFIG_MEDIA_CONTROLLER) - core->pads[DEMOD_PAD_IF_INPUT].flags = MEDIA_PAD_FL_SINK; - core->pads[DEMOD_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE; - core->pads[DEMOD_PAD_VBI_OUT].flags = MEDIA_PAD_FL_SOURCE; + core->pads[TVP5150_PAD_IF_INPUT].flags = MEDIA_PAD_FL_SINK; + core->pads[TVP5150_PAD_IF_INPUT].sig_type = PAD_SIGNAL_ANALOG; + core->pads[TVP5150_PAD_VID_OUT].flags = MEDIA_PAD_FL_SOURCE; + core->pads[TVP5150_PAD_VID_OUT].sig_type = PAD_SIGNAL_DV; sd->entity.function = MEDIA_ENT_F_ATV_DECODER; - res = media_entity_pads_init(&sd->entity, DEMOD_NUM_PADS, core->pads); + res = media_entity_pads_init(&sd->entity, TVP5150_NUM_PADS, core->pads); if (res < 0) return res; @@ -1517,6 +1755,7 @@ static int tvp5150_probe(struct i2c_client *c, return res; core->norm = V4L2_STD_ALL; /* Default is autodetect */ + core->detected_norm = V4L2_STD_UNKNOWN; core->input = TVP5150_COMPOSITE1; core->enable = true; @@ -1534,7 +1773,7 @@ static int tvp5150_probe(struct i2c_client *c, 27000000, 1, 27000000); v4l2_ctrl_new_std_menu_items(&core->hdl, &tvp5150_ctrl_ops, V4L2_CID_TEST_PATTERN, - ARRAY_SIZE(tvp5150_test_patterns), + ARRAY_SIZE(tvp5150_test_patterns) - 1, 0, 0, tvp5150_test_patterns); sd->ctrl_handler = &core->hdl; if (core->hdl.error) { @@ -1542,16 +1781,17 @@ static int tvp5150_probe(struct i2c_client *c, goto err; } - /* Default is no cropping */ - core->rect.top = 0; - if (tvp5150_read_std(sd) & V4L2_STD_525_60) - core->rect.height = TVP5150_V_MAX_525_60; - else - core->rect.height = TVP5150_V_MAX_OTHERS; - core->rect.left = 0; - core->rect.width = TVP5150_H_MAX; + tvp5150_set_default(tvp5150_read_std(sd), &core->rect); + core->irq = c->irq; tvp5150_reset(sd, 0); /* Calls v4l2_ctrl_handler_setup() */ + if (c->irq) { + res = devm_request_threaded_irq(&c->dev, c->irq, NULL, + tvp5150_isr, IRQF_TRIGGER_HIGH | + IRQF_ONESHOT, "tvp5150", core); + if (res) + return res; + } res = v4l2_async_register_subdev(sd); if (res < 0) diff --git a/drivers/media/i2c/tvp5150_reg.h b/drivers/media/i2c/tvp5150_reg.h index d3a764cae1a0..9088186c24d1 100644 --- a/drivers/media/i2c/tvp5150_reg.h +++ b/drivers/media/i2c/tvp5150_reg.h @@ -125,8 +125,11 @@ #define TVP5150_TELETEXT_FIL_ENA 0xbb /* Teletext filter enable */ /* Reserved BCh-BFh */ #define TVP5150_INT_STATUS_REG_A 0xc0 /* Interrupt status register A */ +#define TVP5150_INT_A_LOCK_STATUS BIT(7) +#define TVP5150_INT_A_LOCK BIT(6) #define TVP5150_INT_ENABLE_REG_A 0xc1 /* Interrupt enable register A */ #define TVP5150_INT_CONF 0xc2 /* Interrupt configuration */ +#define TVP5150_VDPOE BIT(2) #define TVP5150_VDP_CONF_RAM_DATA 0xc3 /* VDP configuration RAM data */ #define TVP5150_CONF_RAM_ADDR_LOW 0xc4 /* Configuration RAM address low byte */ #define TVP5150_CONF_RAM_ADDR_HIGH 0xc5 /* Configuration RAM address high byte */ diff --git a/drivers/media/i2c/tvp7002.c b/drivers/media/i2c/tvp7002.c index 4f5c627579c7..cab2f2bd0aa9 100644 --- a/drivers/media/i2c/tvp7002.c +++ b/drivers/media/i2c/tvp7002.c @@ -889,7 +889,7 @@ static const struct v4l2_subdev_ops tvp7002_ops = { static struct tvp7002_config * tvp7002_get_pdata(struct i2c_client *client) { - struct v4l2_fwnode_endpoint bus_cfg; + struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 }; struct tvp7002_config *pdata = NULL; struct device_node *endpoint; unsigned int flags; diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c index 06d29d8f6be8..4d49af86c15e 100644 --- a/drivers/media/i2c/video-i2c.c +++ b/drivers/media/i2c/video-i2c.c @@ -352,8 +352,8 @@ static int video_i2c_querycap(struct file *file, void *priv, struct video_i2c_data *data = video_drvdata(file); struct i2c_client *client = data->client; - strlcpy(vcap->driver, data->v4l2_dev.name, sizeof(vcap->driver)); - strlcpy(vcap->card, data->vdev.name, sizeof(vcap->card)); + strscpy(vcap->driver, data->v4l2_dev.name, sizeof(vcap->driver)); + strscpy(vcap->card, data->vdev.name, sizeof(vcap->card)); sprintf(vcap->bus_info, "I2C:%d-%d", client->adapter->nr, client->addr); @@ -378,7 +378,7 @@ static int video_i2c_enum_input(struct file *file, void *fh, if (vin->index > 0) return -EINVAL; - strlcpy(vin->name, "Camera", sizeof(vin->name)); + strscpy(vin->name, "Camera", sizeof(vin->name)); vin->type = V4L2_INPUT_TYPE_CAMERA; @@ -534,7 +534,7 @@ static int video_i2c_probe(struct i2c_client *client, data->client = client; v4l2_dev = &data->v4l2_dev; - strlcpy(v4l2_dev->name, VIDEO_I2C_DRIVER, sizeof(v4l2_dev->name)); + strscpy(v4l2_dev->name, VIDEO_I2C_DRIVER, sizeof(v4l2_dev->name)); ret = v4l2_device_register(&client->dev, v4l2_dev); if (ret < 0) |