diff options
Diffstat (limited to 'drivers/media/i2c')
27 files changed, 2259 insertions, 436 deletions
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index 704af210e270..4c936e129500 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -61,6 +61,7 @@ config VIDEO_TDA1997X depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API depends on SND_SOC select SND_PCM + select HDMI ---help--- V4L2 subdevice driver for the NXP TDA1997x HDMI receivers. @@ -595,6 +596,18 @@ config VIDEO_APTINA_PLL config VIDEO_SMIAPP_PLL tristate +config VIDEO_IMX214 + tristate "Sony IMX214 sensor support" + depends on GPIOLIB && I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + depends on MEDIA_CAMERA_SUPPORT + depends on V4L2_FWNODE + help + This is a Video4Linux2 sensor driver for the Sony + IMX214 camera. + + To compile this driver as a module, choose M here: the + module will be called imx214. + config VIDEO_IMX258 tristate "Sony IMX258 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API @@ -610,6 +623,7 @@ config VIDEO_IMX274 tristate "Sony IMX274 sensor support" depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API depends on MEDIA_CAMERA_SUPPORT + select REGMAP_I2C ---help--- This is a V4L2 sensor driver for the Sony IMX274 CMOS image sensor. @@ -846,6 +860,7 @@ config VIDEO_MT9M032 config VIDEO_MT9M111 tristate "mt9m111, mt9m112 and mt9m131 support" depends on I2C && VIDEO_V4L2 + select V4L2_FWNODE help This driver supports MT9M111, MT9M112 and MT9M131 cameras from Micron/Aptina diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 260d4d9ec2a1..65fae7732de0 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -106,6 +106,7 @@ obj-$(CONFIG_VIDEO_I2C) += video-i2c.o obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o obj-$(CONFIG_VIDEO_OV2659) += ov2659.o obj-$(CONFIG_VIDEO_TC358743) += tc358743.o +obj-$(CONFIG_VIDEO_IMX214) += imx214.o obj-$(CONFIG_VIDEO_IMX258) += imx258.o obj-$(CONFIG_VIDEO_IMX274) += imx274.o obj-$(CONFIG_VIDEO_IMX319) += imx319.o diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c index 5b008b0002c0..aa8b04cfed0f 100644 --- a/drivers/media/i2c/ad9389b.c +++ b/drivers/media/i2c/ad9389b.c @@ -578,7 +578,7 @@ static const struct v4l2_dv_timings_cap ad9389b_timings_cap = { .type = V4L2_DV_BT_656_1120, /* keep this initialization for compatibility with GCC < 4.4.6 */ .reserved = { 0 }, - V4L2_INIT_BT_TIMINGS(0, 1920, 0, 1200, 25000000, 170000000, + V4L2_INIT_BT_TIMINGS(640, 1920, 350, 1200, 25000000, 170000000, V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_REDUCED_BLANKING | diff --git a/drivers/media/i2c/adv7180.c b/drivers/media/i2c/adv7180.c index 99697baad2ea..6f3dc8862622 100644 --- a/drivers/media/i2c/adv7180.c +++ b/drivers/media/i2c/adv7180.c @@ -180,6 +180,9 @@ #define V4L2_CID_ADV_FAST_SWITCH (V4L2_CID_USER_ADV7180_BASE + 0x00) +/* Initial number of frames to skip to avoid possible garbage */ +#define ADV7180_NUM_OF_SKIP_FRAMES 2 + struct adv7180_state; #define ADV7180_FLAG_RESET_POWERED BIT(0) @@ -769,6 +772,13 @@ static int adv7180_g_mbus_config(struct v4l2_subdev *sd, return 0; } +static int adv7180_get_skip_frames(struct v4l2_subdev *sd, u32 *frames) +{ + *frames = ADV7180_NUM_OF_SKIP_FRAMES; + + return 0; +} + static int adv7180_g_pixelaspect(struct v4l2_subdev *sd, struct v4l2_fract *aspect) { struct adv7180_state *state = to_state(sd); @@ -849,10 +859,15 @@ static const struct v4l2_subdev_pad_ops adv7180_pad_ops = { .get_fmt = adv7180_get_pad_format, }; +static const struct v4l2_subdev_sensor_ops adv7180_sensor_ops = { + .g_skip_frames = adv7180_get_skip_frames, +}; + static const struct v4l2_subdev_ops adv7180_ops = { .core = &adv7180_core_ops, .video = &adv7180_video_ops, .pad = &adv7180_pad_ops, + .sensor = &adv7180_sensor_ops, }; static irqreturn_t adv7180_irq(int irq, void *devid) diff --git a/drivers/media/i2c/adv7511.c b/drivers/media/i2c/adv7511.c index f3899cc84e27..cec5ebb1c9e6 100644 --- a/drivers/media/i2c/adv7511.c +++ b/drivers/media/i2c/adv7511.c @@ -130,7 +130,7 @@ static const struct v4l2_dv_timings_cap adv7511_timings_cap = { .type = V4L2_DV_BT_656_1120, /* keep this initialization for compatibility with GCC < 4.4.6 */ .reserved = { 0 }, - V4L2_INIT_BT_TIMINGS(0, ADV7511_MAX_WIDTH, 0, ADV7511_MAX_HEIGHT, + V4L2_INIT_BT_TIMINGS(640, ADV7511_MAX_WIDTH, 350, ADV7511_MAX_HEIGHT, ADV7511_MIN_PIXELCLOCK, ADV7511_MAX_PIXELCLOCK, V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, @@ -550,7 +550,7 @@ static void log_infoframe(struct v4l2_subdev *sd, const struct adv7511_cfg_read_ buffer[3] = 0; buffer[3] = hdmi_infoframe_checksum(buffer, len + 4); - if (hdmi_infoframe_unpack(&frame, buffer) < 0) { + if (hdmi_infoframe_unpack(&frame, buffer, sizeof(buffer)) < 0) { v4l2_err(sd, "%s: unpack of %s infoframe failed\n", __func__, cri->desc); return; } diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 9eb7c70a7712..28a84bf9f8a9 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -27,6 +27,7 @@ #include <linux/videodev2.h> #include <linux/workqueue.h> #include <linux/regmap.h> +#include <linux/interrupt.h> #include <media/i2c/adv7604.h> #include <media/cec.h> @@ -114,6 +115,11 @@ struct adv76xx_chip_info { unsigned int fmt_change_digital_mask; unsigned int cp_csc; + unsigned int cec_irq_status; + unsigned int cec_rx_enable; + unsigned int cec_rx_enable_mask; + bool cec_irq_swap; + const struct adv76xx_format_info *formats; unsigned int nformats; @@ -766,7 +772,7 @@ static const struct v4l2_dv_timings_cap adv7604_timings_cap_analog = { .type = V4L2_DV_BT_656_1120, /* keep this initialization for compatibility with GCC < 4.4.6 */ .reserved = { 0 }, - V4L2_INIT_BT_TIMINGS(0, 1920, 0, 1200, 25000000, 170000000, + V4L2_INIT_BT_TIMINGS(640, 1920, 350, 1200, 25000000, 170000000, V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_REDUCED_BLANKING | @@ -777,7 +783,7 @@ static const struct v4l2_dv_timings_cap adv76xx_timings_cap_digital = { .type = V4L2_DV_BT_656_1120, /* keep this initialization for compatibility with GCC < 4.4.6 */ .reserved = { 0 }, - V4L2_INIT_BT_TIMINGS(0, 1920, 0, 1200, 25000000, 225000000, + V4L2_INIT_BT_TIMINGS(640, 1920, 350, 1200, 25000000, 225000000, V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_REDUCED_BLANKING | @@ -2003,10 +2009,11 @@ static void adv76xx_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status) static void adv76xx_cec_isr(struct v4l2_subdev *sd, bool *handled) { struct adv76xx_state *state = to_state(sd); + const struct adv76xx_chip_info *info = state->info; u8 cec_irq; /* cec controller */ - cec_irq = io_read(sd, 0x4d) & 0x0f; + cec_irq = io_read(sd, info->cec_irq_status) & 0x0f; if (!cec_irq) return; @@ -2024,15 +2031,21 @@ static void adv76xx_cec_isr(struct v4l2_subdev *sd, bool *handled) for (i = 0; i < msg.len; i++) msg.msg[i] = cec_read(sd, i + 0x15); - cec_write(sd, 0x26, 0x01); /* re-enable rx */ + cec_write(sd, info->cec_rx_enable, + info->cec_rx_enable_mask); /* re-enable rx */ cec_received_msg(state->cec_adap, &msg); } } - /* note: the bit order is swapped between 0x4d and 0x4e */ - cec_irq = ((cec_irq & 0x08) >> 3) | ((cec_irq & 0x04) >> 1) | - ((cec_irq & 0x02) << 1) | ((cec_irq & 0x01) << 3); - io_write(sd, 0x4e, cec_irq); + if (info->cec_irq_swap) { + /* + * Note: the bit order is swapped between 0x4d and 0x4e + * on adv7604 + */ + cec_irq = ((cec_irq & 0x08) >> 3) | ((cec_irq & 0x04) >> 1) | + ((cec_irq & 0x02) << 1) | ((cec_irq & 0x01) << 3); + } + io_write(sd, info->cec_irq_status + 1, cec_irq); if (handled) *handled = true; @@ -2041,6 +2054,7 @@ static void adv76xx_cec_isr(struct v4l2_subdev *sd, bool *handled) static int adv76xx_cec_adap_enable(struct cec_adapter *adap, bool enable) { struct adv76xx_state *state = cec_get_drvdata(adap); + const struct adv76xx_chip_info *info = state->info; struct v4l2_subdev *sd = &state->sd; if (!state->cec_enabled_adap && enable) { @@ -2052,11 +2066,11 @@ static int adv76xx_cec_adap_enable(struct cec_adapter *adap, bool enable) /* tx: arbitration lost */ /* tx: retry timeout */ /* rx: ready */ - io_write_clr_set(sd, 0x50, 0x0f, 0x0f); - cec_write(sd, 0x26, 0x01); /* enable rx */ + io_write_clr_set(sd, info->cec_irq_status + 3, 0x0f, 0x0f); + cec_write(sd, info->cec_rx_enable, info->cec_rx_enable_mask); } else if (state->cec_enabled_adap && !enable) { /* disable cec interrupts */ - io_write_clr_set(sd, 0x50, 0x0f, 0x00); + io_write_clr_set(sd, info->cec_irq_status + 3, 0x0f, 0x00); /* disable address mask 1-3 */ cec_write_clr_set(sd, 0x27, 0x70, 0x00); /* power down cec section */ @@ -2221,6 +2235,16 @@ static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled) return 0; } +static irqreturn_t adv76xx_irq_handler(int irq, void *dev_id) +{ + struct adv76xx_state *state = dev_id; + bool handled = false; + + adv76xx_isr(&state->sd, 0, &handled); + + return handled ? IRQ_HANDLED : IRQ_NONE; +} + static int adv76xx_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) { struct adv76xx_state *state = to_state(sd); @@ -2420,7 +2444,7 @@ static int adv76xx_read_infoframe(struct v4l2_subdev *sd, int index, buffer[i + 3] = infoframe_read(sd, adv76xx_cri[index].payload_addr + i); - if (hdmi_infoframe_unpack(frame, buffer) < 0) { + if (hdmi_infoframe_unpack(frame, buffer, sizeof(buffer)) < 0) { v4l2_err(sd, "%s: unpack of %s infoframe failed\n", __func__, adv76xx_cri[index].desc); return -ENOENT; @@ -2960,6 +2984,10 @@ static const struct adv76xx_chip_info adv76xx_chip_info[] = { .cable_det_mask = 0x1e, .fmt_change_digital_mask = 0xc1, .cp_csc = 0xfc, + .cec_irq_status = 0x4d, + .cec_rx_enable = 0x26, + .cec_rx_enable_mask = 0x01, + .cec_irq_swap = true, .formats = adv7604_formats, .nformats = ARRAY_SIZE(adv7604_formats), .set_termination = adv7604_set_termination, @@ -3006,6 +3034,9 @@ static const struct adv76xx_chip_info adv76xx_chip_info[] = { .cable_det_mask = 0x01, .fmt_change_digital_mask = 0x03, .cp_csc = 0xf4, + .cec_irq_status = 0x93, + .cec_rx_enable = 0x2c, + .cec_rx_enable_mask = 0x02, .formats = adv7611_formats, .nformats = ARRAY_SIZE(adv7611_formats), .set_termination = adv7611_set_termination, @@ -3047,6 +3078,9 @@ static const struct adv76xx_chip_info adv76xx_chip_info[] = { .cable_det_mask = 0x01, .fmt_change_digital_mask = 0x03, .cp_csc = 0xf4, + .cec_irq_status = 0x93, + .cec_rx_enable = 0x2c, + .cec_rx_enable_mask = 0x02, .formats = adv7612_formats, .nformats = ARRAY_SIZE(adv7612_formats), .set_termination = adv7611_set_termination, @@ -3134,7 +3168,7 @@ static int adv76xx_parse_dt(struct adv76xx_state *state) state->pdata.insert_av_codes = 1; /* Disable the interrupt for now as no DT-based board uses it. */ - state->pdata.int1_config = ADV76XX_INT1_CONFIG_DISABLED; + state->pdata.int1_config = ADV76XX_INT1_CONFIG_ACTIVE_HIGH; /* Hardcode the remaining platform data fields. */ state->pdata.disable_pwrdnb = 0; @@ -3517,6 +3551,16 @@ static int adv76xx_probe(struct i2c_client *client, if (err) goto err_entity; + if (client->irq) { + err = devm_request_threaded_irq(&client->dev, + client->irq, + NULL, adv76xx_irq_handler, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + client->name, state); + if (err) + goto err_entity; + } + #if IS_ENABLED(CONFIG_VIDEO_ADV7604_CEC) state->cec_adap = cec_allocate_adapter(&adv76xx_cec_adap_ops, state, dev_name(&client->dev), diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index 4721d49dcf0f..989259488e3d 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -663,7 +663,7 @@ static const struct v4l2_dv_timings_cap adv7842_timings_cap_analog = { .type = V4L2_DV_BT_656_1120, /* keep this initialization for compatibility with GCC < 4.4.6 */ .reserved = { 0 }, - V4L2_INIT_BT_TIMINGS(0, 1920, 0, 1200, 25000000, 170000000, + V4L2_INIT_BT_TIMINGS(640, 1920, 350, 1200, 25000000, 170000000, V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_REDUCED_BLANKING | @@ -674,7 +674,7 @@ static const struct v4l2_dv_timings_cap adv7842_timings_cap_digital = { .type = V4L2_DV_BT_656_1120, /* keep this initialization for compatibility with GCC < 4.4.6 */ .reserved = { 0 }, - V4L2_INIT_BT_TIMINGS(0, 1920, 0, 1200, 25000000, 225000000, + V4L2_INIT_BT_TIMINGS(640, 1920, 350, 1200, 25000000, 225000000, V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_REDUCED_BLANKING | @@ -2574,7 +2574,7 @@ static void log_infoframe(struct v4l2_subdev *sd, struct adv7842_cfg_read_infofr for (i = 0; i < len; i++) buffer[i + 3] = infoframe_read(sd, cri->payload_addr + i); - if (hdmi_infoframe_unpack(&frame, buffer) < 0) { + if (hdmi_infoframe_unpack(&frame, buffer, sizeof(buffer)) < 0) { v4l2_err(sd, "%s: unpack of %s infoframe failed\n", __func__, cri->desc); return; } diff --git a/drivers/media/i2c/imx214.c b/drivers/media/i2c/imx214.c new file mode 100644 index 000000000000..ec3d1b855f62 --- /dev/null +++ b/drivers/media/i2c/imx214.c @@ -0,0 +1,1118 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * imx214.c - imx214 sensor driver + * + * Copyright 2018 Qtechnology A/S + * + * Ricardo Ribalda <ricardo.ribalda@gmail.com> + */ +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <media/media-entity.h> +#include <media/v4l2-ctrls.h> +#include <media/v4l2-fwnode.h> +#include <media/v4l2-subdev.h> + +#define IMX214_DEFAULT_CLK_FREQ 24000000 +#define IMX214_DEFAULT_LINK_FREQ 480000000 +#define IMX214_DEFAULT_PIXEL_RATE ((IMX214_DEFAULT_LINK_FREQ * 8LL) / 10) +#define IMX214_FPS 30 +#define IMX214_MBUS_CODE MEDIA_BUS_FMT_SRGGB10_1X10 + +static const char * const imx214_supply_name[] = { + "vdda", + "vddd", + "vdddo", +}; + +#define IMX214_NUM_SUPPLIES ARRAY_SIZE(imx214_supply_name) + +struct imx214 { + struct device *dev; + struct clk *xclk; + struct regmap *regmap; + + struct v4l2_subdev sd; + struct media_pad pad; + struct v4l2_mbus_framefmt fmt; + struct v4l2_rect crop; + + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *exposure; + + struct regulator_bulk_data supplies[IMX214_NUM_SUPPLIES]; + + struct gpio_desc *enable_gpio; + + /* + * Serialize control access, get/set format, get selection + * and start streaming. + */ + struct mutex mutex; + + bool streaming; +}; + +struct reg_8 { + u16 addr; + u8 val; +}; + +enum { + IMX214_TABLE_WAIT_MS = 0, + IMX214_TABLE_END, + IMX214_MAX_RETRIES, + IMX214_WAIT_MS +}; + +/*From imx214_mode_tbls.h*/ +static const struct reg_8 mode_4096x2304[] = { + {0x0114, 0x03}, + {0x0220, 0x00}, + {0x0221, 0x11}, + {0x0222, 0x01}, + {0x0340, 0x0C}, + {0x0341, 0x7A}, + {0x0342, 0x13}, + {0x0343, 0x90}, + {0x0344, 0x00}, + {0x0345, 0x38}, + {0x0346, 0x01}, + {0x0347, 0x98}, + {0x0348, 0x10}, + {0x0349, 0x37}, + {0x034A, 0x0A}, + {0x034B, 0x97}, + {0x0381, 0x01}, + {0x0383, 0x01}, + {0x0385, 0x01}, + {0x0387, 0x01}, + {0x0900, 0x00}, + {0x0901, 0x00}, + {0x0902, 0x00}, + {0x3000, 0x35}, + {0x3054, 0x01}, + {0x305C, 0x11}, + + {0x0112, 0x0A}, + {0x0113, 0x0A}, + {0x034C, 0x10}, + {0x034D, 0x00}, + {0x034E, 0x09}, + {0x034F, 0x00}, + {0x0401, 0x00}, + {0x0404, 0x00}, + {0x0405, 0x10}, + {0x0408, 0x00}, + {0x0409, 0x00}, + {0x040A, 0x00}, + {0x040B, 0x00}, + {0x040C, 0x10}, + {0x040D, 0x00}, + {0x040E, 0x09}, + {0x040F, 0x00}, + + {0x0301, 0x05}, + {0x0303, 0x02}, + {0x0305, 0x03}, + {0x0306, 0x00}, + {0x0307, 0x96}, + {0x0309, 0x0A}, + {0x030B, 0x01}, + {0x0310, 0x00}, + + {0x0820, 0x12}, + {0x0821, 0xC0}, + {0x0822, 0x00}, + {0x0823, 0x00}, + + {0x3A03, 0x09}, + {0x3A04, 0x50}, + {0x3A05, 0x01}, + + {0x0B06, 0x01}, + {0x30A2, 0x00}, + + {0x30B4, 0x00}, + + {0x3A02, 0xFF}, + + {0x3011, 0x00}, + {0x3013, 0x01}, + + {0x0202, 0x0C}, + {0x0203, 0x70}, + {0x0224, 0x01}, + {0x0225, 0xF4}, + + {0x0204, 0x00}, + {0x0205, 0x00}, + {0x020E, 0x01}, + {0x020F, 0x00}, + {0x0210, 0x01}, + {0x0211, 0x00}, + {0x0212, 0x01}, + {0x0213, 0x00}, + {0x0214, 0x01}, + {0x0215, 0x00}, + {0x0216, 0x00}, + {0x0217, 0x00}, + + {0x4170, 0x00}, + {0x4171, 0x10}, + {0x4176, 0x00}, + {0x4177, 0x3C}, + {0xAE20, 0x04}, + {0xAE21, 0x5C}, + + {IMX214_TABLE_WAIT_MS, 10}, + {0x0138, 0x01}, + {IMX214_TABLE_END, 0x00} +}; + +static const struct reg_8 mode_1920x1080[] = { + {0x0114, 0x03}, + {0x0220, 0x00}, + {0x0221, 0x11}, + {0x0222, 0x01}, + {0x0340, 0x0C}, + {0x0341, 0x7A}, + {0x0342, 0x13}, + {0x0343, 0x90}, + {0x0344, 0x04}, + {0x0345, 0x78}, + {0x0346, 0x03}, + {0x0347, 0xFC}, + {0x0348, 0x0B}, + {0x0349, 0xF7}, + {0x034A, 0x08}, + {0x034B, 0x33}, + {0x0381, 0x01}, + {0x0383, 0x01}, + {0x0385, 0x01}, + {0x0387, 0x01}, + {0x0900, 0x00}, + {0x0901, 0x00}, + {0x0902, 0x00}, + {0x3000, 0x35}, + {0x3054, 0x01}, + {0x305C, 0x11}, + + {0x0112, 0x0A}, + {0x0113, 0x0A}, + {0x034C, 0x07}, + {0x034D, 0x80}, + {0x034E, 0x04}, + {0x034F, 0x38}, + {0x0401, 0x00}, + {0x0404, 0x00}, + {0x0405, 0x10}, + {0x0408, 0x00}, + {0x0409, 0x00}, + {0x040A, 0x00}, + {0x040B, 0x00}, + {0x040C, 0x07}, + {0x040D, 0x80}, + {0x040E, 0x04}, + {0x040F, 0x38}, + + {0x0301, 0x05}, + {0x0303, 0x02}, + {0x0305, 0x03}, + {0x0306, 0x00}, + {0x0307, 0x96}, + {0x0309, 0x0A}, + {0x030B, 0x01}, + {0x0310, 0x00}, + + {0x0820, 0x12}, + {0x0821, 0xC0}, + {0x0822, 0x00}, + {0x0823, 0x00}, + + {0x3A03, 0x04}, + {0x3A04, 0xF8}, + {0x3A05, 0x02}, + + {0x0B06, 0x01}, + {0x30A2, 0x00}, + + {0x30B4, 0x00}, + + {0x3A02, 0xFF}, + + {0x3011, 0x00}, + {0x3013, 0x01}, + + {0x0202, 0x0C}, + {0x0203, 0x70}, + {0x0224, 0x01}, + {0x0225, 0xF4}, + + {0x0204, 0x00}, + {0x0205, 0x00}, + {0x020E, 0x01}, + {0x020F, 0x00}, + {0x0210, 0x01}, + {0x0211, 0x00}, + {0x0212, 0x01}, + {0x0213, 0x00}, + {0x0214, 0x01}, + {0x0215, 0x00}, + {0x0216, 0x00}, + {0x0217, 0x00}, + + {0x4170, 0x00}, + {0x4171, 0x10}, + {0x4176, 0x00}, + {0x4177, 0x3C}, + {0xAE20, 0x04}, + {0xAE21, 0x5C}, + + {IMX214_TABLE_WAIT_MS, 10}, + {0x0138, 0x01}, + {IMX214_TABLE_END, 0x00} +}; + +static const struct reg_8 mode_table_common[] = { + /* software reset */ + + /* software standby settings */ + {0x0100, 0x00}, + + /* ATR setting */ + {0x9300, 0x02}, + + /* external clock setting */ + {0x0136, 0x18}, + {0x0137, 0x00}, + + /* global setting */ + /* basic config */ + {0x0101, 0x00}, + {0x0105, 0x01}, + {0x0106, 0x01}, + {0x4550, 0x02}, + {0x4601, 0x00}, + {0x4642, 0x05}, + {0x6227, 0x11}, + {0x6276, 0x00}, + {0x900E, 0x06}, + {0xA802, 0x90}, + {0xA803, 0x11}, + {0xA804, 0x62}, + {0xA805, 0x77}, + {0xA806, 0xAE}, + {0xA807, 0x34}, + {0xA808, 0xAE}, + {0xA809, 0x35}, + {0xA80A, 0x62}, + {0xA80B, 0x83}, + {0xAE33, 0x00}, + + /* analog setting */ + {0x4174, 0x00}, + {0x4175, 0x11}, + {0x4612, 0x29}, + {0x461B, 0x12}, + {0x461F, 0x06}, + {0x4635, 0x07}, + {0x4637, 0x30}, + {0x463F, 0x18}, + {0x4641, 0x0D}, + {0x465B, 0x12}, + {0x465F, 0x11}, + {0x4663, 0x11}, + {0x4667, 0x0F}, + {0x466F, 0x0F}, + {0x470E, 0x09}, + {0x4909, 0xAB}, + {0x490B, 0x95}, + {0x4915, 0x5D}, + {0x4A5F, 0xFF}, + {0x4A61, 0xFF}, + {0x4A73, 0x62}, + {0x4A85, 0x00}, + {0x4A87, 0xFF}, + + /* embedded data */ + {0x5041, 0x04}, + {0x583C, 0x04}, + {0x620E, 0x04}, + {0x6EB2, 0x01}, + {0x6EB3, 0x00}, + {0x9300, 0x02}, + + /* imagequality */ + /* HDR setting */ + {0x3001, 0x07}, + {0x6D12, 0x3F}, + {0x6D13, 0xFF}, + {0x9344, 0x03}, + {0x9706, 0x10}, + {0x9707, 0x03}, + {0x9708, 0x03}, + {0x9E04, 0x01}, + {0x9E05, 0x00}, + {0x9E0C, 0x01}, + {0x9E0D, 0x02}, + {0x9E24, 0x00}, + {0x9E25, 0x8C}, + {0x9E26, 0x00}, + {0x9E27, 0x94}, + {0x9E28, 0x00}, + {0x9E29, 0x96}, + + /* CNR parameter setting */ + {0x69DB, 0x01}, + + /* Moire reduction */ + {0x6957, 0x01}, + + /* image enhancment */ + {0x6987, 0x17}, + {0x698A, 0x03}, + {0x698B, 0x03}, + + /* white balanace */ + {0x0B8E, 0x01}, + {0x0B8F, 0x00}, + {0x0B90, 0x01}, + {0x0B91, 0x00}, + {0x0B92, 0x01}, + {0x0B93, 0x00}, + {0x0B94, 0x01}, + {0x0B95, 0x00}, + + /* ATR setting */ + {0x6E50, 0x00}, + {0x6E51, 0x32}, + {0x9340, 0x00}, + {0x9341, 0x3C}, + {0x9342, 0x03}, + {0x9343, 0xFF}, + {IMX214_TABLE_END, 0x00} +}; + +/* + * Declare modes in order, from biggest + * to smallest height. + */ +static const struct imx214_mode { + u32 width; + u32 height; + const struct reg_8 *reg_table; +} imx214_modes[] = { + { + .width = 4096, + .height = 2304, + .reg_table = mode_4096x2304, + }, + { + .width = 1920, + .height = 1080, + .reg_table = mode_1920x1080, + }, +}; + +static inline struct imx214 *to_imx214(struct v4l2_subdev *sd) +{ + return container_of(sd, struct imx214, sd); +} + +static int __maybe_unused imx214_power_on(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx214 *imx214 = to_imx214(sd); + int ret; + + ret = regulator_bulk_enable(IMX214_NUM_SUPPLIES, imx214->supplies); + if (ret < 0) { + dev_err(imx214->dev, "failed to enable regulators: %d\n", ret); + return ret; + } + + usleep_range(2000, 3000); + + ret = clk_prepare_enable(imx214->xclk); + if (ret < 0) { + regulator_bulk_disable(IMX214_NUM_SUPPLIES, imx214->supplies); + dev_err(imx214->dev, "clk prepare enable failed\n"); + return ret; + } + + gpiod_set_value_cansleep(imx214->enable_gpio, 1); + usleep_range(12000, 15000); + + return 0; +} + +static int __maybe_unused imx214_power_off(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx214 *imx214 = to_imx214(sd); + + gpiod_set_value_cansleep(imx214->enable_gpio, 0); + + clk_disable_unprepare(imx214->xclk); + + regulator_bulk_disable(IMX214_NUM_SUPPLIES, imx214->supplies); + usleep_range(10, 20); + + return 0; +} + +static int imx214_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index > 0) + return -EINVAL; + + code->code = IMX214_MBUS_CODE; + + return 0; +} + +static int imx214_enum_frame_size(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + if (fse->code != IMX214_MBUS_CODE) + return -EINVAL; + + if (fse->index >= ARRAY_SIZE(imx214_modes)) + return -EINVAL; + + fse->min_width = fse->max_width = imx214_modes[fse->index].width; + fse->min_height = fse->max_height = imx214_modes[fse->index].height; + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int imx214_s_register(struct v4l2_subdev *subdev, + const struct v4l2_dbg_register *reg) +{ + struct imx214 *imx214 = container_of(subdev, struct imx214, sd); + + return regmap_write(imx214->regmap, reg->reg, reg->val); +} + +static int imx214_g_register(struct v4l2_subdev *subdev, + struct v4l2_dbg_register *reg) +{ + struct imx214 *imx214 = container_of(subdev, struct imx214, sd); + unsigned int aux; + int ret; + + reg->size = 1; + ret = regmap_read(imx214->regmap, reg->reg, &aux); + reg->val = aux; + + return ret; +} +#endif + +static const struct v4l2_subdev_core_ops imx214_core_ops = { +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = imx214_g_register, + .s_register = imx214_s_register, +#endif +}; + +static struct v4l2_mbus_framefmt * +__imx214_get_pad_format(struct imx214 *imx214, + struct v4l2_subdev_pad_config *cfg, + unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_format(&imx214->sd, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &imx214->fmt; + default: + return NULL; + } +} + +static int imx214_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct imx214 *imx214 = to_imx214(sd); + + mutex_lock(&imx214->mutex); + format->format = *__imx214_get_pad_format(imx214, cfg, format->pad, + format->which); + mutex_unlock(&imx214->mutex); + + return 0; +} + +static struct v4l2_rect * +__imx214_get_pad_crop(struct imx214 *imx214, struct v4l2_subdev_pad_config *cfg, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(&imx214->sd, cfg, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &imx214->crop; + default: + return NULL; + } +} + +static int imx214_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct imx214 *imx214 = to_imx214(sd); + struct v4l2_mbus_framefmt *__format; + struct v4l2_rect *__crop; + const struct imx214_mode *mode; + + mutex_lock(&imx214->mutex); + + __crop = __imx214_get_pad_crop(imx214, cfg, format->pad, format->which); + + if (format) + mode = v4l2_find_nearest_size(imx214_modes, + ARRAY_SIZE(imx214_modes), width, height, + format->format.width, format->format.height); + else + mode = &imx214_modes[0]; + + __crop->width = mode->width; + __crop->height = mode->height; + + __format = __imx214_get_pad_format(imx214, cfg, format->pad, + format->which); + __format->width = __crop->width; + __format->height = __crop->height; + __format->code = IMX214_MBUS_CODE; + __format->field = V4L2_FIELD_NONE; + __format->colorspace = V4L2_COLORSPACE_SRGB; + __format->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(__format->colorspace); + __format->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(true, + __format->colorspace, __format->ycbcr_enc); + __format->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(__format->colorspace); + + format->format = *__format; + + mutex_unlock(&imx214->mutex); + + return 0; +} + +static int imx214_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_selection *sel) +{ + struct imx214 *imx214 = to_imx214(sd); + + if (sel->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + mutex_lock(&imx214->mutex); + sel->r = *__imx214_get_pad_crop(imx214, cfg, sel->pad, + sel->which); + mutex_unlock(&imx214->mutex); + return 0; +} + +static int imx214_entity_init_cfg(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg) +{ + struct v4l2_subdev_format fmt = { }; + + fmt.which = cfg ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + fmt.format.width = imx214_modes[0].width; + fmt.format.height = imx214_modes[0].height; + + imx214_set_format(subdev, cfg, &fmt); + + return 0; +} + +static int imx214_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct imx214 *imx214 = container_of(ctrl->handler, + struct imx214, ctrls); + u8 vals[2]; + int ret; + + /* + * Applying V4L2 control value only happens + * when power is up for streaming + */ + if (!pm_runtime_get_if_in_use(imx214->dev)) + return 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + vals[1] = ctrl->val; + vals[0] = ctrl->val >> 8; + ret = regmap_bulk_write(imx214->regmap, 0x202, vals, 2); + if (ret < 0) + dev_err(imx214->dev, "Error %d\n", ret); + ret = 0; + break; + + default: + ret = -EINVAL; + } + + pm_runtime_put(imx214->dev); + + return ret; +} + +static const struct v4l2_ctrl_ops imx214_ctrl_ops = { + .s_ctrl = imx214_set_ctrl, +}; + +#define MAX_CMD 4 +static int imx214_write_table(struct imx214 *imx214, + const struct reg_8 table[]) +{ + u8 vals[MAX_CMD]; + int i; + int ret; + + for (; table->addr != IMX214_TABLE_END ; table++) { + if (table->addr == IMX214_TABLE_WAIT_MS) { + usleep_range(table->val * 1000, + table->val * 1000 + 500); + continue; + } + + for (i = 0; i < MAX_CMD; i++) { + if (table[i].addr != (table[0].addr + i)) + break; + vals[i] = table[i].val; + } + + ret = regmap_bulk_write(imx214->regmap, table->addr, vals, i); + + if (ret) { + dev_err(imx214->dev, "write_table error: %d\n", ret); + return ret; + } + + table += i - 1; + } + + return 0; +} + +static int imx214_start_streaming(struct imx214 *imx214) +{ + const struct imx214_mode *mode; + int ret; + + mutex_lock(&imx214->mutex); + ret = imx214_write_table(imx214, mode_table_common); + if (ret < 0) { + dev_err(imx214->dev, "could not sent common table %d\n", ret); + goto error; + } + + mode = v4l2_find_nearest_size(imx214_modes, + ARRAY_SIZE(imx214_modes), width, height, + imx214->fmt.width, imx214->fmt.height); + ret = imx214_write_table(imx214, mode->reg_table); + if (ret < 0) { + dev_err(imx214->dev, "could not sent mode table %d\n", ret); + goto error; + } + ret = __v4l2_ctrl_handler_setup(&imx214->ctrls); + if (ret < 0) { + dev_err(imx214->dev, "could not sync v4l2 controls\n"); + goto error; + } + ret = regmap_write(imx214->regmap, 0x100, 1); + if (ret < 0) { + dev_err(imx214->dev, "could not sent start table %d\n", ret); + goto error; + } + + mutex_unlock(&imx214->mutex); + return 0; + +error: + mutex_unlock(&imx214->mutex); + return ret; +} + +static int imx214_stop_streaming(struct imx214 *imx214) +{ + int ret; + + ret = regmap_write(imx214->regmap, 0x100, 0); + if (ret < 0) + dev_err(imx214->dev, "could not sent stop table %d\n", ret); + + return ret; +} + +static int imx214_s_stream(struct v4l2_subdev *subdev, int enable) +{ + struct imx214 *imx214 = to_imx214(subdev); + int ret; + + if (imx214->streaming == enable) + return 0; + + if (enable) { + ret = pm_runtime_get_sync(imx214->dev); + if (ret < 0) { + pm_runtime_put_noidle(imx214->dev); + return ret; + } + + ret = imx214_start_streaming(imx214); + if (ret < 0) + goto err_rpm_put; + } else { + ret = imx214_start_streaming(imx214); + if (ret < 0) + goto err_rpm_put; + pm_runtime_put(imx214->dev); + } + + imx214->streaming = enable; + return 0; + +err_rpm_put: + pm_runtime_put(imx214->dev); + return ret; +} + +static int imx214_g_frame_interval(struct v4l2_subdev *subdev, + struct v4l2_subdev_frame_interval *fival) +{ + fival->pad = 0; + fival->interval.numerator = 1; + fival->interval.denominator = IMX214_FPS; + + return 0; +} + +static int imx214_enum_frame_interval(struct v4l2_subdev *subdev, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + const struct imx214_mode *mode; + + if (fie->index != 0) + return -EINVAL; + + mode = v4l2_find_nearest_size(imx214_modes, + ARRAY_SIZE(imx214_modes), width, height, + fie->width, fie->height); + + fie->code = IMX214_MBUS_CODE; + fie->width = mode->width; + fie->height = mode->height; + fie->interval.numerator = 1; + fie->interval.denominator = IMX214_FPS; + + return 0; +} + +static const struct v4l2_subdev_video_ops imx214_video_ops = { + .s_stream = imx214_s_stream, + .g_frame_interval = imx214_g_frame_interval, + .s_frame_interval = imx214_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops imx214_subdev_pad_ops = { + .enum_mbus_code = imx214_enum_mbus_code, + .enum_frame_size = imx214_enum_frame_size, + .enum_frame_interval = imx214_enum_frame_interval, + .get_fmt = imx214_get_format, + .set_fmt = imx214_set_format, + .get_selection = imx214_get_selection, + .init_cfg = imx214_entity_init_cfg, +}; + +static const struct v4l2_subdev_ops imx214_subdev_ops = { + .core = &imx214_core_ops, + .video = &imx214_video_ops, + .pad = &imx214_subdev_pad_ops, +}; + +static const struct regmap_config sensor_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, +}; + +static int imx214_get_regulators(struct device *dev, struct imx214 *imx214) +{ + unsigned int i; + + for (i = 0; i < IMX214_NUM_SUPPLIES; i++) + imx214->supplies[i].supply = imx214_supply_name[i]; + + return devm_regulator_bulk_get(dev, IMX214_NUM_SUPPLIES, + imx214->supplies); +} + +static int imx214_parse_fwnode(struct device *dev) +{ + struct fwnode_handle *endpoint; + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_CSI2_DPHY, + }; + unsigned int i; + int ret; + + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(dev), NULL); + if (!endpoint) { + dev_err(dev, "endpoint node not found\n"); + return -EINVAL; + } + + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg); + if (ret) { + dev_err(dev, "parsing endpoint node failed\n"); + goto done; + } + + for (i = 0; i < bus_cfg.nr_of_link_frequencies; i++) + if (bus_cfg.link_frequencies[i] == IMX214_DEFAULT_LINK_FREQ) + break; + + if (i == bus_cfg.nr_of_link_frequencies) { + dev_err(dev, "link-frequencies %d not supported, Please review your DT\n", + IMX214_DEFAULT_LINK_FREQ); + ret = -EINVAL; + goto done; + } + +done: + v4l2_fwnode_endpoint_free(&bus_cfg); + fwnode_handle_put(endpoint); + return ret; +} + +static int __maybe_unused imx214_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx214 *imx214 = to_imx214(sd); + + if (imx214->streaming) + imx214_stop_streaming(imx214); + + return 0; +} + +static int __maybe_unused imx214_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx214 *imx214 = to_imx214(sd); + int ret; + + if (imx214->streaming) { + ret = imx214_start_streaming(imx214); + if (ret) + goto error; + } + + return 0; + +error: + imx214_stop_streaming(imx214); + imx214->streaming = 0; + return ret; +} + +static int imx214_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct imx214 *imx214; + static const s64 link_freq[] = { + IMX214_DEFAULT_LINK_FREQ, + }; + int ret; + + ret = imx214_parse_fwnode(dev); + if (ret) + return ret; + + imx214 = devm_kzalloc(dev, sizeof(*imx214), GFP_KERNEL); + if (!imx214) + return -ENOMEM; + + imx214->dev = dev; + + imx214->xclk = devm_clk_get(dev, NULL); + if (IS_ERR(imx214->xclk)) { + dev_err(dev, "could not get xclk"); + return PTR_ERR(imx214->xclk); + } + + ret = clk_set_rate(imx214->xclk, IMX214_DEFAULT_CLK_FREQ); + if (ret) { + dev_err(dev, "could not set xclk frequency\n"); + return ret; + } + + ret = imx214_get_regulators(dev, imx214); + if (ret < 0) { + dev_err(dev, "cannot get regulators\n"); + return ret; + } + + imx214->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(imx214->enable_gpio)) { + dev_err(dev, "cannot get enable gpio\n"); + return PTR_ERR(imx214->enable_gpio); + } + + imx214->regmap = devm_regmap_init_i2c(client, &sensor_regmap_config); + if (IS_ERR(imx214->regmap)) { + dev_err(dev, "regmap init failed\n"); + return PTR_ERR(imx214->regmap); + } + + v4l2_i2c_subdev_init(&imx214->sd, client, &imx214_subdev_ops); + + /* + * Enable power initially, to avoid warnings + * from clk_disable on power_off + */ + imx214_power_on(imx214->dev); + + pm_runtime_set_active(imx214->dev); + pm_runtime_enable(imx214->dev); + pm_runtime_idle(imx214->dev); + + v4l2_ctrl_handler_init(&imx214->ctrls, 3); + + imx214->pixel_rate = v4l2_ctrl_new_std(&imx214->ctrls, NULL, + V4L2_CID_PIXEL_RATE, 0, + IMX214_DEFAULT_PIXEL_RATE, 1, + IMX214_DEFAULT_PIXEL_RATE); + imx214->link_freq = v4l2_ctrl_new_int_menu(&imx214->ctrls, NULL, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(link_freq) - 1, + 0, link_freq); + if (imx214->link_freq) + imx214->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + /* + * WARNING! + * Values obtained reverse engineering blobs and/or devices. + * Ranges and functionality might be wrong. + * + * Sony, please release some register set documentation for the + * device. + * + * Yours sincerely, Ricardo. + */ + imx214->exposure = v4l2_ctrl_new_std(&imx214->ctrls, &imx214_ctrl_ops, + V4L2_CID_EXPOSURE, + 0, 3184, 1, 0x0c70); + + ret = imx214->ctrls.error; + if (ret) { + dev_err(&client->dev, "%s control init failed (%d)\n", + __func__, ret); + goto free_ctrl; + } + + imx214->sd.ctrl_handler = &imx214->ctrls; + mutex_init(&imx214->mutex); + imx214->ctrls.lock = &imx214->mutex; + + imx214->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + imx214->pad.flags = MEDIA_PAD_FL_SOURCE; + imx214->sd.dev = &client->dev; + imx214->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; + + ret = media_entity_pads_init(&imx214->sd.entity, 1, &imx214->pad); + if (ret < 0) { + dev_err(dev, "could not register media entity\n"); + goto free_ctrl; + } + + imx214_entity_init_cfg(&imx214->sd, NULL); + + ret = v4l2_async_register_subdev_sensor_common(&imx214->sd); + if (ret < 0) { + dev_err(dev, "could not register v4l2 device\n"); + goto free_entity; + } + + return 0; + +free_entity: + media_entity_cleanup(&imx214->sd.entity); +free_ctrl: + mutex_destroy(&imx214->mutex); + v4l2_ctrl_handler_free(&imx214->ctrls); + pm_runtime_disable(imx214->dev); + + return ret; +} + +static int imx214_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct imx214 *imx214 = to_imx214(sd); + + v4l2_async_unregister_subdev(&imx214->sd); + media_entity_cleanup(&imx214->sd.entity); + v4l2_ctrl_handler_free(&imx214->ctrls); + + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + + mutex_destroy(&imx214->mutex); + + return 0; +} + +static const struct of_device_id imx214_of_match[] = { + { .compatible = "sony,imx214" }, + { } +}; +MODULE_DEVICE_TABLE(of, imx214_of_match); + +static const struct dev_pm_ops imx214_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(imx214_suspend, imx214_resume) + SET_RUNTIME_PM_OPS(imx214_power_off, imx214_power_on, NULL) +}; + +static struct i2c_driver imx214_i2c_driver = { + .driver = { + .of_match_table = imx214_of_match, + .pm = &imx214_pm_ops, + .name = "imx214", + }, + .probe_new = imx214_probe, + .remove = imx214_remove, +}; + +module_i2c_driver(imx214_i2c_driver); + +MODULE_DESCRIPTION("Sony IMX214 Camera drier"); +MODULE_AUTHOR("Ricardo Ribalda <ricardo.ribalda@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index 31a1e2294843..f86ae18bc104 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -62,11 +62,6 @@ /* Test Pattern Control */ #define IMX258_REG_TEST_PATTERN 0x0600 -#define IMX258_TEST_PATTERN_DISABLE 0 -#define IMX258_TEST_PATTERN_SOLID_COLOR 1 -#define IMX258_TEST_PATTERN_COLOR_BARS 2 -#define IMX258_TEST_PATTERN_GREY_COLOR 3 -#define IMX258_TEST_PATTERN_PN9 4 /* Orientation */ #define REG_MIRROR_FLIP_CONTROL 0x0101 @@ -504,18 +499,10 @@ static const struct imx258_reg mode_1048_780_regs[] = { static const char * const imx258_test_pattern_menu[] = { "Disabled", - "Color Bars", - "Solid Color", - "Grey Color Bars", - "PN9" -}; - -static const int imx258_test_pattern_val[] = { - IMX258_TEST_PATTERN_DISABLE, - IMX258_TEST_PATTERN_COLOR_BARS, - IMX258_TEST_PATTERN_SOLID_COLOR, - IMX258_TEST_PATTERN_GREY_COLOR, - IMX258_TEST_PATTERN_PN9, + "Solid Colour", + "Eight Vertical Colour Bars", + "Colour Bars With Fade to Grey", + "Pseudorandom Sequence (PN9)", }; /* Configurations for supported link frequencies */ @@ -778,13 +765,10 @@ static int imx258_set_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_TEST_PATTERN: ret = imx258_write_reg(imx258, IMX258_REG_TEST_PATTERN, IMX258_REG_VALUE_16BIT, - imx258_test_pattern_val[ctrl->val]); - + ctrl->val); ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL, IMX258_REG_VALUE_08BIT, - ctrl->val == imx258_test_pattern_val - [IMX258_TEST_PATTERN_DISABLE] ? - REG_CONFIG_MIRROR_FLIP : + !ctrl->val ? REG_CONFIG_MIRROR_FLIP : REG_CONFIG_FLIP_TEST_PATTERN); break; default: diff --git a/drivers/media/i2c/imx274.c b/drivers/media/i2c/imx274.c index 11c69281692e..5fac7fd32634 100644 --- a/drivers/media/i2c/imx274.c +++ b/drivers/media/i2c/imx274.c @@ -619,16 +619,19 @@ static int imx274_write_table(struct stimx274 *priv, const struct reg_8 table[]) static inline int imx274_read_reg(struct stimx274 *priv, u16 addr, u8 *val) { + unsigned int uint_val; int err; - err = regmap_read(priv->regmap, addr, (unsigned int *)val); + err = regmap_read(priv->regmap, addr, &uint_val); if (err) dev_err(&priv->client->dev, "%s : i2c read failed, addr = %x\n", __func__, addr); else dev_dbg(&priv->client->dev, "%s : addr 0x%x, val=0x%x\n", __func__, - addr, *val); + addr, uint_val); + + *val = uint_val; return err; } @@ -1901,7 +1904,7 @@ static int imx274_probe(struct i2c_client *client, imx274_reset(imx274, 1); /* initialize controls */ - ret = v4l2_ctrl_handler_init(&imx274->ctrls.handler, 2); + ret = v4l2_ctrl_handler_init(&imx274->ctrls.handler, 4); if (ret < 0) { dev_err(&client->dev, "%s : ctrl handler init Failed\n", __func__); diff --git a/drivers/media/i2c/imx319.c b/drivers/media/i2c/imx319.c index 0d3e27812b93..17c2e4b41221 100644 --- a/drivers/media/i2c/imx319.c +++ b/drivers/media/i2c/imx319.c @@ -1648,10 +1648,10 @@ static const struct imx319_reg mode_1280x720_regs[] = { static const char * const imx319_test_pattern_menu[] = { "Disabled", - "100% color bars", - "Solid color", - "Fade to gray color bars", - "PN9" + "Solid Colour", + "Eight Vertical Colour Bars", + "Colour Bars With Fade to Grey", + "Pseudorandom Sequence (PN9)", }; /* supported link frequencies */ diff --git a/drivers/media/i2c/imx355.c b/drivers/media/i2c/imx355.c index 20c8eea5db4b..bed293b60e50 100644 --- a/drivers/media/i2c/imx355.c +++ b/drivers/media/i2c/imx355.c @@ -876,10 +876,10 @@ static const struct imx355_reg mode_820x616_regs[] = { static const char * const imx355_test_pattern_menu[] = { "Disabled", - "100% color bars", - "Solid color", - "Fade to gray color bars", - "PN9" + "Solid Colour", + "Eight Vertical Colour Bars", + "Colour Bars With Fade to Grey", + "Pseudorandom Sequence (PN9)", }; /* supported link frequencies */ diff --git a/drivers/media/i2c/mt9m111.c b/drivers/media/i2c/mt9m111.c index 1395986a07bb..d639b9bcf64a 100644 --- a/drivers/media/i2c/mt9m111.c +++ b/drivers/media/i2c/mt9m111.c @@ -15,12 +15,15 @@ #include <linux/delay.h> #include <linux/v4l2-mediabus.h> #include <linux/module.h> +#include <linux/property.h> #include <media/v4l2-async.h> #include <media/v4l2-clk.h> #include <media/v4l2-common.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> +#include <media/v4l2-event.h> +#include <media/v4l2-fwnode.h> /* * MT9M111, MT9M112 and MT9M131: @@ -101,6 +104,7 @@ #define MT9M111_REDUCER_XSIZE_A 0x1a7 #define MT9M111_REDUCER_YZOOM_A 0x1a9 #define MT9M111_REDUCER_YSIZE_A 0x1aa +#define MT9M111_EFFECTS_MODE 0x1e2 #define MT9M111_OUTPUT_FORMAT_CTRL2_A 0x13a #define MT9M111_OUTPUT_FORMAT_CTRL2_B 0x19b @@ -126,6 +130,9 @@ #define MT9M111_OUTFMT_SWAP_YCbCr_C_Y_RGB_EVEN (1 << 1) #define MT9M111_OUTFMT_SWAP_YCbCr_Cb_Cr_RGB_R_B (1 << 0) #define MT9M111_TPG_SEL_MASK GENMASK(2, 0) +#define MT9M111_EFFECTS_MODE_MASK GENMASK(2, 0) +#define MT9M111_RM_PWR_MASK BIT(10) +#define MT9M111_RM_SKIP2_MASK GENMASK(3, 2) /* * Camera control register addresses (0x200..0x2ff not implemented) @@ -204,6 +211,23 @@ static const struct mt9m111_datafmt mt9m111_colour_fmts[] = { {MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_COLORSPACE_SRGB}, }; +enum mt9m111_mode_id { + MT9M111_MODE_SXGA_8FPS, + MT9M111_MODE_SXGA_15FPS, + MT9M111_MODE_QSXGA_30FPS, + MT9M111_NUM_MODES, +}; + +struct mt9m111_mode_info { + unsigned int sensor_w; + unsigned int sensor_h; + unsigned int max_image_w; + unsigned int max_image_h; + unsigned int max_fps; + unsigned int reg_val; + unsigned int reg_mask; +}; + struct mt9m111 { struct v4l2_subdev subdev; struct v4l2_ctrl_handler hdl; @@ -213,15 +237,51 @@ struct mt9m111 { struct v4l2_clk *clk; unsigned int width; /* output */ unsigned int height; /* sizes */ + struct v4l2_fract frame_interval; + const struct mt9m111_mode_info *current_mode; struct mutex power_lock; /* lock to protect power_count */ int power_count; const struct mt9m111_datafmt *fmt; int lastpage; /* PageMap cache value */ + bool is_streaming; + /* user point of view - 0: falling 1: rising edge */ + unsigned int pclk_sample:1; #ifdef CONFIG_MEDIA_CONTROLLER struct media_pad pad; #endif }; +static const struct mt9m111_mode_info mt9m111_mode_data[MT9M111_NUM_MODES] = { + [MT9M111_MODE_SXGA_8FPS] = { + .sensor_w = 1280, + .sensor_h = 1024, + .max_image_w = 1280, + .max_image_h = 1024, + .max_fps = 8, + .reg_val = MT9M111_RM_LOW_POWER_RD, + .reg_mask = MT9M111_RM_PWR_MASK | MT9M111_RM_SKIP2_MASK, + }, + [MT9M111_MODE_SXGA_15FPS] = { + .sensor_w = 1280, + .sensor_h = 1024, + .max_image_w = 1280, + .max_image_h = 1024, + .max_fps = 15, + .reg_val = MT9M111_RM_FULL_POWER_RD, + .reg_mask = MT9M111_RM_PWR_MASK | MT9M111_RM_SKIP2_MASK, + }, + [MT9M111_MODE_QSXGA_30FPS] = { + .sensor_w = 1280, + .sensor_h = 1024, + .max_image_w = 640, + .max_image_h = 512, + .max_fps = 30, + .reg_val = MT9M111_RM_LOW_POWER_RD | MT9M111_RM_COL_SKIP_2X | + MT9M111_RM_ROW_SKIP_2X, + .reg_mask = MT9M111_RM_PWR_MASK | MT9M111_RM_SKIP2_MASK, + }, +}; + /* Find a data format by a pixel code */ static const struct mt9m111_datafmt *mt9m111_find_datafmt(struct mt9m111 *mt9m111, u32 code) @@ -538,6 +598,10 @@ static int mt9m111_set_pixfmt(struct mt9m111 *mt9m111, return -EINVAL; } + /* receiver samples on falling edge, chip-hw default is rising */ + if (mt9m111->pclk_sample == 0) + mask_outfmt2 |= MT9M111_OUTFMT_INV_PIX_CLOCK; + ret = mt9m111_reg_mask(client, context_a.output_fmt_ctrl2, data_outfmt2, mask_outfmt2); if (!ret) @@ -559,6 +623,9 @@ static int mt9m111_set_fmt(struct v4l2_subdev *sd, bool bayer; int ret; + if (mt9m111->is_streaming) + return -EBUSY; + if (format->pad) return -EINVAL; @@ -611,6 +678,61 @@ static int mt9m111_set_fmt(struct v4l2_subdev *sd, return ret; } +static const struct mt9m111_mode_info * +mt9m111_find_mode(struct mt9m111 *mt9m111, unsigned int req_fps, + unsigned int width, unsigned int height) +{ + const struct mt9m111_mode_info *mode; + struct v4l2_rect *sensor_rect = &mt9m111->rect; + unsigned int gap, gap_best = (unsigned int) -1; + int i, best_gap_idx = MT9M111_MODE_SXGA_15FPS; + bool skip_30fps = false; + + /* + * The fps selection is based on the row, column skipping mechanism. + * So ensure that the sensor window is set to default else the fps + * aren't calculated correctly within the sensor hw. + */ + if (sensor_rect->width != MT9M111_MAX_WIDTH || + sensor_rect->height != MT9M111_MAX_HEIGHT) { + dev_info(mt9m111->subdev.dev, + "Framerate selection is not supported for cropped " + "images\n"); + return NULL; + } + + /* 30fps only supported for images not exceeding 640x512 */ + if (width > MT9M111_MAX_WIDTH / 2 || height > MT9M111_MAX_HEIGHT / 2) { + dev_dbg(mt9m111->subdev.dev, + "Framerates > 15fps are supported only for images " + "not exceeding 640x512\n"); + skip_30fps = true; + } + + /* find best matched fps */ + for (i = 0; i < MT9M111_NUM_MODES; i++) { + unsigned int fps = mt9m111_mode_data[i].max_fps; + + if (fps == 30 && skip_30fps) + continue; + + gap = abs(fps - req_fps); + if (gap < gap_best) { + best_gap_idx = i; + gap_best = gap; + } + } + + /* + * Use context a/b default timing values instead of calculate blanking + * timing values. + */ + mode = &mt9m111_mode_data[best_gap_idx]; + mt9m111->ctx = (best_gap_idx == MT9M111_MODE_QSXGA_30FPS) ? &context_a : + &context_b; + return mode; +} + #ifdef CONFIG_VIDEO_ADV_DEBUG static int mt9m111_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) @@ -726,6 +848,29 @@ static int mt9m111_set_test_pattern(struct mt9m111 *mt9m111, int val) MT9M111_TPG_SEL_MASK); } +static int mt9m111_set_colorfx(struct mt9m111 *mt9m111, int val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); + static const struct v4l2_control colorfx[] = { + { V4L2_COLORFX_NONE, 0 }, + { V4L2_COLORFX_BW, 1 }, + { V4L2_COLORFX_SEPIA, 2 }, + { V4L2_COLORFX_NEGATIVE, 3 }, + { V4L2_COLORFX_SOLARIZATION, 4 }, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(colorfx); i++) { + if (colorfx[i].id == val) { + return mt9m111_reg_mask(client, MT9M111_EFFECTS_MODE, + colorfx[i].value, + MT9M111_EFFECTS_MODE_MASK); + } + } + + return -EINVAL; +} + static int mt9m111_s_ctrl(struct v4l2_ctrl *ctrl) { struct mt9m111 *mt9m111 = container_of(ctrl->handler, @@ -746,6 +891,8 @@ static int mt9m111_s_ctrl(struct v4l2_ctrl *ctrl) return mt9m111_set_autowhitebalance(mt9m111, ctrl->val); case V4L2_CID_TEST_PATTERN: return mt9m111_set_test_pattern(mt9m111, ctrl->val); + case V4L2_CID_COLORFX: + return mt9m111_set_colorfx(mt9m111, ctrl->val); } return -EINVAL; @@ -771,11 +918,16 @@ static int mt9m111_suspend(struct mt9m111 *mt9m111) static void mt9m111_restore_state(struct mt9m111 *mt9m111) { + struct i2c_client *client = v4l2_get_subdevdata(&mt9m111->subdev); + mt9m111_set_context(mt9m111, mt9m111->ctx); mt9m111_set_pixfmt(mt9m111, mt9m111->fmt->code); mt9m111_setup_geometry(mt9m111, &mt9m111->rect, mt9m111->width, mt9m111->height, mt9m111->fmt->code); v4l2_ctrl_handler_setup(&mt9m111->hdl); + mt9m111_reg_mask(client, mt9m111->ctx->read_mode, + mt9m111->current_mode->reg_val, + mt9m111->current_mode->reg_mask); } static int mt9m111_resume(struct mt9m111 *mt9m111) @@ -862,12 +1014,62 @@ static const struct v4l2_ctrl_ops mt9m111_ctrl_ops = { static const struct v4l2_subdev_core_ops mt9m111_subdev_core_ops = { .s_power = mt9m111_s_power, + .log_status = v4l2_ctrl_subdev_log_status, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = mt9m111_g_register, .s_register = mt9m111_s_register, #endif }; +static int mt9m111_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); + + fi->interval = mt9m111->frame_interval; + + return 0; +} + +static int mt9m111_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); + const struct mt9m111_mode_info *mode; + struct v4l2_fract *fract = &fi->interval; + int fps; + + if (mt9m111->is_streaming) + return -EBUSY; + + if (fi->pad != 0) + return -EINVAL; + + if (fract->numerator == 0) { + fract->denominator = 30; + fract->numerator = 1; + } + + fps = DIV_ROUND_CLOSEST(fract->denominator, fract->numerator); + + /* Find best fitting mode. Do not update the mode if no one was found. */ + mode = mt9m111_find_mode(mt9m111, fps, mt9m111->width, mt9m111->height); + if (!mode) + return 0; + + if (mode->max_fps != fps) { + fract->denominator = mode->max_fps; + fract->numerator = 1; + } + + mt9m111->current_mode = mode; + mt9m111->frame_interval = fi->interval; + + return 0; +} + static int mt9m111_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) @@ -879,12 +1081,26 @@ static int mt9m111_enum_mbus_code(struct v4l2_subdev *sd, return 0; } +static int mt9m111_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); + + mt9m111->is_streaming = !!enable; + return 0; +} + static int mt9m111_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { - cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | + struct mt9m111 *mt9m111 = container_of(sd, struct mt9m111, subdev); + + cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH; + + cfg->flags |= mt9m111->pclk_sample ? V4L2_MBUS_PCLK_SAMPLE_RISING : + V4L2_MBUS_PCLK_SAMPLE_FALLING; + cfg->type = V4L2_MBUS_PARALLEL; return 0; @@ -892,6 +1108,9 @@ static int mt9m111_g_mbus_config(struct v4l2_subdev *sd, static const struct v4l2_subdev_video_ops mt9m111_subdev_video_ops = { .g_mbus_config = mt9m111_g_mbus_config, + .s_stream = mt9m111_s_stream, + .g_frame_interval = mt9m111_g_frame_interval, + .s_frame_interval = mt9m111_s_frame_interval, }; static const struct v4l2_subdev_pad_ops mt9m111_subdev_pad_ops = { @@ -951,6 +1170,30 @@ done: return ret; } +static int mt9m111_probe_fw(struct i2c_client *client, struct mt9m111 *mt9m111) +{ + struct v4l2_fwnode_endpoint bus_cfg = { + .bus_type = V4L2_MBUS_PARALLEL + }; + struct fwnode_handle *np; + int ret; + + np = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL); + if (!np) + return -EINVAL; + + ret = v4l2_fwnode_endpoint_parse(np, &bus_cfg); + if (ret) + goto out_put_fw; + + mt9m111->pclk_sample = !!(bus_cfg.bus.parallel.flags & + V4L2_MBUS_PCLK_SAMPLE_RISING); + +out_put_fw: + fwnode_handle_put(np); + return ret; +} + static int mt9m111_probe(struct i2c_client *client, const struct i2c_device_id *did) { @@ -968,6 +1211,10 @@ static int mt9m111_probe(struct i2c_client *client, if (!mt9m111) return -ENOMEM; + ret = mt9m111_probe_fw(client, mt9m111); + if (ret) + return ret; + mt9m111->clk = v4l2_clk_get(&client->dev, "mclk"); if (IS_ERR(mt9m111->clk)) return PTR_ERR(mt9m111->clk); @@ -976,9 +1223,10 @@ static int mt9m111_probe(struct i2c_client *client, mt9m111->ctx = &context_b; v4l2_i2c_subdev_init(&mt9m111->subdev, client, &mt9m111_subdev_ops); - mt9m111->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + mt9m111->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; - v4l2_ctrl_handler_init(&mt9m111->hdl, 5); + v4l2_ctrl_handler_init(&mt9m111->hdl, 7); v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); v4l2_ctrl_new_std(&mt9m111->hdl, &mt9m111_ctrl_ops, @@ -994,6 +1242,14 @@ static int mt9m111_probe(struct i2c_client *client, &mt9m111_ctrl_ops, V4L2_CID_TEST_PATTERN, ARRAY_SIZE(mt9m111_test_pattern_menu) - 1, 0, 0, mt9m111_test_pattern_menu); + v4l2_ctrl_new_std_menu(&mt9m111->hdl, &mt9m111_ctrl_ops, + V4L2_CID_COLORFX, V4L2_COLORFX_SOLARIZATION, + ~(BIT(V4L2_COLORFX_NONE) | + BIT(V4L2_COLORFX_BW) | + BIT(V4L2_COLORFX_SEPIA) | + BIT(V4L2_COLORFX_NEGATIVE) | + BIT(V4L2_COLORFX_SOLARIZATION)), + V4L2_COLORFX_NONE); mt9m111->subdev.ctrl_handler = &mt9m111->hdl; if (mt9m111->hdl.error) { ret = mt9m111->hdl.error; @@ -1008,6 +1264,10 @@ static int mt9m111_probe(struct i2c_client *client, goto out_hdlfree; #endif + mt9m111->current_mode = &mt9m111_mode_data[MT9M111_MODE_SXGA_15FPS]; + mt9m111->frame_interval.numerator = 1; + mt9m111->frame_interval.denominator = mt9m111->current_mode->max_fps; + /* Second stage probe - when a capture adapter is there */ mt9m111->rect.left = MT9M111_MIN_DARK_COLS; mt9m111->rect.top = MT9M111_MIN_DARK_ROWS; diff --git a/drivers/media/i2c/ov13858.c b/drivers/media/i2c/ov13858.c index c8bbc1f52261..45bb872db3c5 100644 --- a/drivers/media/i2c/ov13858.c +++ b/drivers/media/i2c/ov13858.c @@ -1612,7 +1612,8 @@ static int ov13858_init_controls(struct ov13858 *ov13858) OV13858_NUM_OF_LINK_FREQS - 1, 0, link_freq_menu_items); - ov13858->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + if (ov13858->link_freq) + ov13858->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; pixel_rate_max = link_freq_to_pixel_rate(link_freq_menu_items[0]); pixel_rate_min = link_freq_to_pixel_rate(link_freq_menu_items[1]); @@ -1635,7 +1636,8 @@ static int ov13858_init_controls(struct ov13858 *ov13858) ov13858->hblank = v4l2_ctrl_new_std( ctrl_hdlr, &ov13858_ctrl_ops, V4L2_CID_HBLANK, hblank, hblank, 1, hblank); - ov13858->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + if (ov13858->hblank) + ov13858->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; exposure_max = mode->vts_def - 8; ov13858->exposure = v4l2_ctrl_new_std( diff --git a/drivers/media/i2c/ov2640.c b/drivers/media/i2c/ov2640.c index 20a8853ba1e2..5d2d6735cc78 100644 --- a/drivers/media/i2c/ov2640.c +++ b/drivers/media/i2c/ov2640.c @@ -26,6 +26,7 @@ #include <linux/videodev2.h> #include <media/v4l2-device.h> +#include <media/v4l2-event.h> #include <media/v4l2-subdev.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-image-sizes.h> @@ -705,6 +706,11 @@ err: return ret; } +static const char * const ov2640_test_pattern_menu[] = { + "Disabled", + "Eight Vertical Colour Bars", +}; + /* * functions */ @@ -740,6 +746,9 @@ static int ov2640_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_HFLIP: val = ctrl->val ? REG04_HFLIP_IMG : 0x00; return ov2640_mask_set(client, REG04, REG04_HFLIP_IMG, val); + case V4L2_CID_TEST_PATTERN: + val = ctrl->val ? COM7_COLOR_BAR_TEST : 0x00; + return ov2640_mask_set(client, COM7, COM7_COLOR_BAR_TEST, val); } return -EINVAL; @@ -1088,6 +1097,9 @@ static const struct v4l2_ctrl_ops ov2640_ctrl_ops = { }; static const struct v4l2_subdev_core_ops ov2640_subdev_core_ops = { + .log_status = v4l2_ctrl_subdev_log_status, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = ov2640_g_register, .s_register = ov2640_s_register, @@ -1182,14 +1194,19 @@ static int ov2640_probe(struct i2c_client *client, goto err_clk; v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops); - priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; mutex_init(&priv->lock); - v4l2_ctrl_handler_init(&priv->hdl, 2); + v4l2_ctrl_handler_init(&priv->hdl, 3); priv->hdl.lock = &priv->lock; v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std_menu_items(&priv->hdl, &ov2640_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov2640_test_pattern_menu) - 1, 0, 0, + ov2640_test_pattern_menu); priv->subdev.ctrl_handler = &priv->hdl; if (priv->hdl.error) { ret = priv->hdl.error; diff --git a/drivers/media/i2c/ov2680.c b/drivers/media/i2c/ov2680.c index 0e34e15b67b3..b10bcfabaeeb 100644 --- a/drivers/media/i2c/ov2680.c +++ b/drivers/media/i2c/ov2680.c @@ -568,10 +568,6 @@ static int ov2680_power_on(struct ov2680_dev *sensor) if (ret < 0) return ret; - ret = ov2680_mode_restore(sensor); - if (ret < 0) - goto disable; - sensor->is_enabled = true; /* Set clock lane into LP-11 state */ @@ -580,12 +576,6 @@ static int ov2680_power_on(struct ov2680_dev *sensor) ov2680_stream_disable(sensor); return 0; - -disable: - dev_err(dev, "failed to enable sensor: %d\n", ret); - ov2680_power_off(sensor); - - return ret; } static int ov2680_s_power(struct v4l2_subdev *sd, int on) @@ -606,6 +596,8 @@ static int ov2680_s_power(struct v4l2_subdev *sd, int on) ret = v4l2_ctrl_handler_setup(&sensor->ctrls.handler); if (ret < 0) return ret; + + ret = ov2680_mode_restore(sensor); } return ret; diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index eaefdb58653b..bef3f3aae0ed 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -25,6 +25,7 @@ #include <media/v4l2-async.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> +#include <media/v4l2-event.h> #include <media/v4l2-fwnode.h> #include <media/v4l2-subdev.h> @@ -94,9 +95,6 @@ #define OV5640_REG_SDE_CTRL5 0x5585 #define OV5640_REG_AVG_READOUT 0x56a1 -#define OV5640_SCLK2X_ROOT_DIVIDER_DEFAULT 1 -#define OV5640_SCLK_ROOT_DIVIDER_DEFAULT 2 - enum ov5640_mode_id { OV5640_MODE_QCIF_176_144 = 0, OV5640_MODE_QVGA_320_240, @@ -113,6 +111,7 @@ enum ov5640_mode_id { enum ov5640_frame_rate { OV5640_15_FPS = 0, OV5640_30_FPS, + OV5640_60_FPS, OV5640_NUM_FRAMERATES, }; @@ -141,6 +140,7 @@ MODULE_PARM_DESC(virtual_channel, static const int ov5640_framerates[] = { [OV5640_15_FPS] = 15, [OV5640_30_FPS] = 30, + [OV5640_60_FPS] = 60, }; /* regulator supplies */ @@ -261,8 +261,7 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) 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}, - {0x3034, 0x18, 0, 0}, {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, - {0x3037, 0x13, 0, 0}, {0x3630, 0x36, 0, 0}, + {0x3630, 0x36, 0, 0}, {0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x12, 0, 0}, {0x3621, 0xe0, 0, 0}, {0x3704, 0xa0, 0, 0}, {0x3703, 0x5a, 0, 0}, {0x3715, 0x78, 0, 0}, {0x3717, 0x01, 0, 0}, {0x370b, 0x60, 0, 0}, @@ -344,27 +343,8 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = { {0x3a1f, 0x14, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3c00, 0x04, 0, 300}, }; -static const struct reg_value ov5640_setting_30fps_VGA_640_480[] = { - {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, - {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, - {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, - {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x0e, 0, 0}, - {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, - {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3503, 0x00, 0, 0}, -}; - -static const struct reg_value ov5640_setting_15fps_VGA_640_480[] = { - {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, +static const struct reg_value ov5640_setting_VGA_640_480[] = { + {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, @@ -382,28 +362,8 @@ static const struct reg_value ov5640_setting_15fps_VGA_640_480[] = { {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, }; -static const struct reg_value ov5640_setting_30fps_XGA_1024_768[] = { - {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, - {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, - {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, - {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x0e, 0, 0}, - {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, - {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, {0x3503, 0x00, 0, 0}, - {0x3035, 0x12, 0, 0}, -}; - -static const struct reg_value ov5640_setting_15fps_XGA_1024_768[] = { - {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, +static const struct reg_value ov5640_setting_XGA_1024_768[] = { + {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, @@ -421,8 +381,8 @@ static const struct reg_value ov5640_setting_15fps_XGA_1024_768[] = { {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, }; -static const struct reg_value ov5640_setting_30fps_QVGA_320_240[] = { - {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, +static const struct reg_value ov5640_setting_QVGA_320_240[] = { + {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, @@ -440,8 +400,8 @@ static const struct reg_value ov5640_setting_30fps_QVGA_320_240[] = { {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, }; -static const struct reg_value ov5640_setting_15fps_QVGA_320_240[] = { - {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, +static const struct reg_value ov5640_setting_QCIF_176_144[] = { + {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, @@ -459,46 +419,8 @@ static const struct reg_value ov5640_setting_15fps_QVGA_320_240[] = { {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, }; -static const struct reg_value ov5640_setting_30fps_QCIF_176_144[] = { - {0x3035, 0x14, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, - {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, - {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, - {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, - {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, - {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, -}; - -static const struct reg_value ov5640_setting_15fps_QCIF_176_144[] = { - {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, - {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, - {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, - {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, - {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, - {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, -}; - -static const struct reg_value ov5640_setting_30fps_NTSC_720_480[] = { - {0x3035, 0x12, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, +static const struct reg_value ov5640_setting_NTSC_720_480[] = { + {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, @@ -516,27 +438,8 @@ static const struct reg_value ov5640_setting_30fps_NTSC_720_480[] = { {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, }; -static const struct reg_value ov5640_setting_15fps_NTSC_720_480[] = { - {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0}, - {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, - {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, - {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, - {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, - {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, -}; - -static const struct reg_value ov5640_setting_30fps_PAL_720_576[] = { - {0x3035, 0x12, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, +static const struct reg_value ov5640_setting_PAL_720_576[] = { + {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, @@ -554,48 +457,8 @@ static const struct reg_value ov5640_setting_30fps_PAL_720_576[] = { {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, }; -static const struct reg_value ov5640_setting_15fps_PAL_720_576[] = { - {0x3035, 0x22, 0, 0}, {0x3036, 0x38, 0, 0}, {0x3c07, 0x08, 0, 0}, - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, - {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, - {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, - {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, - {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, - {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, {0x4713, 0x03, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, -}; - -static const struct reg_value ov5640_setting_30fps_720P_1280_720[] = { - {0x3008, 0x42, 0, 0}, - {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0}, - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, - {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, - {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0}, - {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}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, - {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0}, {0x4005, 0x1a, 0, 0}, - {0x3008, 0x02, 0, 0}, {0x3503, 0, 0, 0}, -}; - -static const struct reg_value ov5640_setting_15fps_720P_1280_720[] = { - {0x3035, 0x41, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0}, +static const struct reg_value ov5640_setting_720P_1280_720[] = { + {0x3c07, 0x07, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3814, 0x31, 0, 0}, {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, @@ -613,9 +476,9 @@ static const struct reg_value ov5640_setting_15fps_720P_1280_720[] = { {0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0}, }; -static const struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = { +static const struct reg_value ov5640_setting_1080P_1920_1080[] = { {0x3008, 0x42, 0, 0}, - {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0}, + {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3814, 0x11, 0, 0}, {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, @@ -630,8 +493,8 @@ static const struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = { {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0}, {0x3035, 0x11, 0, 0}, - {0x3036, 0x54, 0, 0}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0}, + {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0}, + {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0}, {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0}, @@ -643,43 +506,10 @@ static const struct reg_value ov5640_setting_30fps_1080P_1920_1080[] = { {0x3a15, 0x60, 0, 0}, {0x4713, 0x02, 0, 0}, {0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0}, {0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0}, - {0x3503, 0, 0, 0}, -}; - -static const struct reg_value ov5640_setting_15fps_1080P_1920_1080[] = { - {0x3008, 0x42, 0, 0}, - {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0}, - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3814, 0x11, 0, 0}, - {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, - {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0}, - {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0}, - {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, - {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, - {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x06, 0, 0}, {0x4713, 0x03, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0}, {0x3035, 0x21, 0, 0}, - {0x3036, 0x54, 0, 1}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0}, - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0}, - {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0}, - {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, - {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0}, - {0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0}, - {0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0}, - {0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0}, - {0x3a15, 0x60, 0, 0}, {0x4713, 0x02, 0, 0}, {0x4407, 0x04, 0, 0}, - {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0}, - {0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3503, 0, 0, 0}, }; -static const struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = { - {0x3035, 0x21, 0, 0}, {0x3036, 0x54, 0, 0}, {0x3c07, 0x08, 0, 0}, +static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = { + {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3814, 0x11, 0, 0}, {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, @@ -705,79 +535,43 @@ static const struct ov5640_mode_info ov5640_mode_init_data = { }; static const struct ov5640_mode_info -ov5640_mode_data[OV5640_NUM_FRAMERATES][OV5640_NUM_MODES] = { - { - {OV5640_MODE_QCIF_176_144, SUBSAMPLING, - 176, 1896, 144, 984, - ov5640_setting_15fps_QCIF_176_144, - ARRAY_SIZE(ov5640_setting_15fps_QCIF_176_144)}, - {OV5640_MODE_QVGA_320_240, SUBSAMPLING, - 320, 1896, 240, 984, - ov5640_setting_15fps_QVGA_320_240, - ARRAY_SIZE(ov5640_setting_15fps_QVGA_320_240)}, - {OV5640_MODE_VGA_640_480, SUBSAMPLING, - 640, 1896, 480, 1080, - ov5640_setting_15fps_VGA_640_480, - ARRAY_SIZE(ov5640_setting_15fps_VGA_640_480)}, - {OV5640_MODE_NTSC_720_480, SUBSAMPLING, - 720, 1896, 480, 984, - ov5640_setting_15fps_NTSC_720_480, - ARRAY_SIZE(ov5640_setting_15fps_NTSC_720_480)}, - {OV5640_MODE_PAL_720_576, SUBSAMPLING, - 720, 1896, 576, 984, - ov5640_setting_15fps_PAL_720_576, - ARRAY_SIZE(ov5640_setting_15fps_PAL_720_576)}, - {OV5640_MODE_XGA_1024_768, SUBSAMPLING, - 1024, 1896, 768, 1080, - ov5640_setting_15fps_XGA_1024_768, - ARRAY_SIZE(ov5640_setting_15fps_XGA_1024_768)}, - {OV5640_MODE_720P_1280_720, SUBSAMPLING, - 1280, 1892, 720, 740, - ov5640_setting_15fps_720P_1280_720, - ARRAY_SIZE(ov5640_setting_15fps_720P_1280_720)}, - {OV5640_MODE_1080P_1920_1080, SCALING, - 1920, 2500, 1080, 1120, - ov5640_setting_15fps_1080P_1920_1080, - ARRAY_SIZE(ov5640_setting_15fps_1080P_1920_1080)}, - {OV5640_MODE_QSXGA_2592_1944, SCALING, - 2592, 2844, 1944, 1968, - ov5640_setting_15fps_QSXGA_2592_1944, - ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)}, - }, { - {OV5640_MODE_QCIF_176_144, SUBSAMPLING, - 176, 1896, 144, 984, - ov5640_setting_30fps_QCIF_176_144, - ARRAY_SIZE(ov5640_setting_30fps_QCIF_176_144)}, - {OV5640_MODE_QVGA_320_240, SUBSAMPLING, - 320, 1896, 240, 984, - ov5640_setting_30fps_QVGA_320_240, - ARRAY_SIZE(ov5640_setting_30fps_QVGA_320_240)}, - {OV5640_MODE_VGA_640_480, SUBSAMPLING, - 640, 1896, 480, 1080, - ov5640_setting_30fps_VGA_640_480, - ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)}, - {OV5640_MODE_NTSC_720_480, SUBSAMPLING, - 720, 1896, 480, 984, - ov5640_setting_30fps_NTSC_720_480, - ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)}, - {OV5640_MODE_PAL_720_576, SUBSAMPLING, - 720, 1896, 576, 984, - ov5640_setting_30fps_PAL_720_576, - ARRAY_SIZE(ov5640_setting_30fps_PAL_720_576)}, - {OV5640_MODE_XGA_1024_768, SUBSAMPLING, - 1024, 1896, 768, 1080, - ov5640_setting_30fps_XGA_1024_768, - ARRAY_SIZE(ov5640_setting_30fps_XGA_1024_768)}, - {OV5640_MODE_720P_1280_720, SUBSAMPLING, - 1280, 1892, 720, 740, - ov5640_setting_30fps_720P_1280_720, - ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)}, - {OV5640_MODE_1080P_1920_1080, SCALING, - 1920, 2500, 1080, 1120, - ov5640_setting_30fps_1080P_1920_1080, - ARRAY_SIZE(ov5640_setting_30fps_1080P_1920_1080)}, - {OV5640_MODE_QSXGA_2592_1944, -1, 0, 0, 0, 0, NULL, 0}, - }, +ov5640_mode_data[OV5640_NUM_MODES] = { + {OV5640_MODE_QCIF_176_144, SUBSAMPLING, + 176, 1896, 144, 984, + ov5640_setting_QCIF_176_144, + ARRAY_SIZE(ov5640_setting_QCIF_176_144)}, + {OV5640_MODE_QVGA_320_240, SUBSAMPLING, + 320, 1896, 240, 984, + ov5640_setting_QVGA_320_240, + ARRAY_SIZE(ov5640_setting_QVGA_320_240)}, + {OV5640_MODE_VGA_640_480, SUBSAMPLING, + 640, 1896, 480, 1080, + ov5640_setting_VGA_640_480, + ARRAY_SIZE(ov5640_setting_VGA_640_480)}, + {OV5640_MODE_NTSC_720_480, SUBSAMPLING, + 720, 1896, 480, 984, + ov5640_setting_NTSC_720_480, + ARRAY_SIZE(ov5640_setting_NTSC_720_480)}, + {OV5640_MODE_PAL_720_576, SUBSAMPLING, + 720, 1896, 576, 984, + ov5640_setting_PAL_720_576, + ARRAY_SIZE(ov5640_setting_PAL_720_576)}, + {OV5640_MODE_XGA_1024_768, SUBSAMPLING, + 1024, 1896, 768, 1080, + ov5640_setting_XGA_1024_768, + ARRAY_SIZE(ov5640_setting_XGA_1024_768)}, + {OV5640_MODE_720P_1280_720, SUBSAMPLING, + 1280, 1892, 720, 740, + ov5640_setting_720P_1280_720, + ARRAY_SIZE(ov5640_setting_720P_1280_720)}, + {OV5640_MODE_1080P_1920_1080, SCALING, + 1920, 2500, 1080, 1120, + ov5640_setting_1080P_1920_1080, + ARRAY_SIZE(ov5640_setting_1080P_1920_1080)}, + {OV5640_MODE_QSXGA_2592_1944, SCALING, + 2592, 2844, 1944, 1968, + ov5640_setting_QSXGA_2592_1944, + ARRAY_SIZE(ov5640_setting_QSXGA_2592_1944)}, }; static int ov5640_init_slave_id(struct ov5640_dev *sensor) @@ -909,6 +703,333 @@ static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg, return ov5640_write_reg(sensor, reg, val); } +/* + * After trying the various combinations, reading various + * documentations spreaded around the net, and from the various + * feedback, the clock tree is probably as follows: + * + * +--------------+ + * | Ext. Clock | + * +-+------------+ + * | +----------+ + * +->| PLL1 | - reg 0x3036, for the multiplier + * +-+--------+ - reg 0x3037, bits 0-3 for the pre-divider + * | +--------------+ + * +->| System Clock | - reg 0x3035, bits 4-7 + * +-+------------+ + * | +--------------+ + * +->| MIPI Divider | - reg 0x3035, bits 0-3 + * | +-+------------+ + * | +----------------> MIPI SCLK + * | + +-----+ + * | +->| / 2 |-------> MIPI BIT CLK + * | +-----+ + * | +--------------+ + * +->| PLL Root Div | - reg 0x3037, bit 4 + * +-+------------+ + * | +---------+ + * +->| Bit Div | - reg 0x3035, bits 0-3 + * +-+-------+ + * | +-------------+ + * +->| SCLK Div | - reg 0x3108, bits 0-1 + * | +-+-----------+ + * | +---------------> SCLK + * | +-------------+ + * +->| SCLK 2X Div | - reg 0x3108, bits 2-3 + * | +-+-----------+ + * | +---------------> SCLK 2X + * | +-------------+ + * +->| PCLK Div | - reg 0x3108, bits 4-5 + * ++------------+ + * + +-----------+ + * +->| P_DIV | - reg 0x3035, bits 0-3 + * +-----+-----+ + * +------------> PCLK + * + * This is deviating from the datasheet at least for the register + * 0x3108, since it's said here that the PCLK would be clocked from + * the PLL. + * + * There seems to be also (unverified) constraints: + * - the PLL pre-divider output rate should be in the 4-27MHz range + * - the PLL multiplier output rate should be in the 500-1000MHz range + * - PCLK >= SCLK * 2 in YUV, >= SCLK in Raw or JPEG + * + * In the two latter cases, these constraints are met since our + * factors are hardcoded. If we were to change that, we would need to + * take this into account. The only varying parts are the PLL + * multiplier and the system clock divider, which are shared between + * all these clocks so won't cause any issue. + */ + +/* + * This is supposed to be ranging from 1 to 8, but the value is always + * set to 3 in the vendor kernels. + */ +#define OV5640_PLL_PREDIV 3 + +#define OV5640_PLL_MULT_MIN 4 +#define OV5640_PLL_MULT_MAX 252 + +/* + * This is supposed to be ranging from 1 to 16, but the value is + * always set to either 1 or 2 in the vendor kernels. + */ +#define OV5640_SYSDIV_MIN 1 +#define OV5640_SYSDIV_MAX 16 + +/* + * Hardcode these values for scaler and non-scaler modes. + * FIXME: to be re-calcualted for 1 data lanes setups + */ +#define OV5640_MIPI_DIV_PCLK 2 +#define OV5640_MIPI_DIV_SCLK 1 + +/* + * This is supposed to be ranging from 1 to 2, but the value is always + * set to 2 in the vendor kernels. + */ +#define OV5640_PLL_ROOT_DIV 2 +#define OV5640_PLL_CTRL3_PLL_ROOT_DIV_2 BIT(4) + +/* + * We only supports 8-bit formats at the moment + */ +#define OV5640_BIT_DIV 2 +#define OV5640_PLL_CTRL0_MIPI_MODE_8BIT 0x08 + +/* + * This is supposed to be ranging from 1 to 8, but the value is always + * set to 2 in the vendor kernels. + */ +#define OV5640_SCLK_ROOT_DIV 2 + +/* + * This is hardcoded so that the consistency is maintained between SCLK and + * SCLK 2x. + */ +#define OV5640_SCLK2X_ROOT_DIV (OV5640_SCLK_ROOT_DIV / 2) + +/* + * This is supposed to be ranging from 1 to 8, but the value is always + * set to 1 in the vendor kernels. + */ +#define OV5640_PCLK_ROOT_DIV 1 +#define OV5640_PLL_SYS_ROOT_DIVIDER_BYPASS 0x00 + +static unsigned long ov5640_compute_sys_clk(struct ov5640_dev *sensor, + u8 pll_prediv, u8 pll_mult, + u8 sysdiv) +{ + unsigned long sysclk = sensor->xclk_freq / pll_prediv * pll_mult; + + /* PLL1 output cannot exceed 1GHz. */ + if (sysclk / 1000000 > 1000) + return 0; + + return sysclk / sysdiv; +} + +static unsigned long ov5640_calc_sys_clk(struct ov5640_dev *sensor, + unsigned long rate, + u8 *pll_prediv, u8 *pll_mult, + u8 *sysdiv) +{ + unsigned long best = ~0; + u8 best_sysdiv = 1, best_mult = 1; + u8 _sysdiv, _pll_mult; + + for (_sysdiv = OV5640_SYSDIV_MIN; + _sysdiv <= OV5640_SYSDIV_MAX; + _sysdiv++) { + for (_pll_mult = OV5640_PLL_MULT_MIN; + _pll_mult <= OV5640_PLL_MULT_MAX; + _pll_mult++) { + unsigned long _rate; + + /* + * The PLL multiplier cannot be odd if above + * 127. + */ + if (_pll_mult > 127 && (_pll_mult % 2)) + continue; + + _rate = ov5640_compute_sys_clk(sensor, + OV5640_PLL_PREDIV, + _pll_mult, _sysdiv); + + /* + * We have reached the maximum allowed PLL1 output, + * increase sysdiv. + */ + if (!rate) + break; + + /* + * Prefer rates above the expected clock rate than + * below, even if that means being less precise. + */ + if (_rate < rate) + continue; + + if (abs(rate - _rate) < abs(rate - best)) { + best = _rate; + best_sysdiv = _sysdiv; + best_mult = _pll_mult; + } + + if (_rate == rate) + goto out; + } + } + +out: + *sysdiv = best_sysdiv; + *pll_prediv = OV5640_PLL_PREDIV; + *pll_mult = best_mult; + + return best; +} + +/* + * ov5640_set_mipi_pclk() - Calculate the clock tree configuration values + * for the MIPI CSI-2 output. + * + * @rate: The requested bandwidth per lane in bytes per second. + * 'Bandwidth Per Lane' is calculated as: + * bpl = HTOT * VTOT * FPS * bpp / num_lanes; + * + * This function use the requested bandwidth to calculate: + * - sample_rate = bpl / (bpp / num_lanes); + * = bpl / (PLL_RDIV * BIT_DIV * PCLK_DIV * MIPI_DIV / num_lanes); + * + * - mipi_sclk = bpl / MIPI_DIV / 2; ( / 2 is for CSI-2 DDR) + * + * with these fixed parameters: + * PLL_RDIV = 2; + * BIT_DIVIDER = 2; (MIPI_BIT_MODE == 8 ? 2 : 2,5); + * PCLK_DIV = 1; + * + * The MIPI clock generation differs for modes that use the scaler and modes + * that do not. In case the scaler is in use, the MIPI_SCLK generates the MIPI + * BIT CLk, and thus: + * + * - mipi_sclk = bpl / MIPI_DIV / 2; + * MIPI_DIV = 1; + * + * For modes that do not go through the scaler, the MIPI BIT CLOCK is generated + * from the pixel clock, and thus: + * + * - sample_rate = bpl / (bpp / num_lanes); + * = bpl / (2 * 2 * 1 * MIPI_DIV / num_lanes); + * = bpl / (4 * MIPI_DIV / num_lanes); + * - MIPI_DIV = bpp / (4 * num_lanes); + * + * FIXME: this have been tested with 16bpp and 2 lanes setup only. + * MIPI_DIV is fixed to value 2, but it -might- be changed according to the + * above formula for setups with 1 lane or image formats with different bpp. + * + * FIXME: this deviates from the sensor manual documentation which is quite + * thin on the MIPI clock tree generation part. + */ +static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor, + unsigned long rate) +{ + const struct ov5640_mode_info *mode = sensor->current_mode; + u8 prediv, mult, sysdiv; + u8 mipi_div; + int ret; + + /* + * 1280x720 is reported to use 'SUBSAMPLING' only, + * but according to the sensor manual it goes through the + * scaler before subsampling. + */ + if (mode->dn_mode == SCALING || + (mode->id == OV5640_MODE_720P_1280_720)) + mipi_div = OV5640_MIPI_DIV_SCLK; + else + mipi_div = OV5640_MIPI_DIV_PCLK; + + ov5640_calc_sys_clk(sensor, rate, &prediv, &mult, &sysdiv); + + ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL0, + 0x0f, OV5640_PLL_CTRL0_MIPI_MODE_8BIT); + + ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1, + 0xff, sysdiv << 4 | mipi_div); + if (ret) + return ret; + + ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL2, 0xff, mult); + if (ret) + return ret; + + ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3, + 0x1f, OV5640_PLL_CTRL3_PLL_ROOT_DIV_2 | prediv); + if (ret) + return ret; + + return ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, + 0x30, OV5640_PLL_SYS_ROOT_DIVIDER_BYPASS); +} + +static unsigned long ov5640_calc_pclk(struct ov5640_dev *sensor, + unsigned long rate, + u8 *pll_prediv, u8 *pll_mult, u8 *sysdiv, + u8 *pll_rdiv, u8 *bit_div, u8 *pclk_div) +{ + unsigned long _rate = rate * OV5640_PLL_ROOT_DIV * OV5640_BIT_DIV * + OV5640_PCLK_ROOT_DIV; + + _rate = ov5640_calc_sys_clk(sensor, _rate, pll_prediv, pll_mult, + sysdiv); + *pll_rdiv = OV5640_PLL_ROOT_DIV; + *bit_div = OV5640_BIT_DIV; + *pclk_div = OV5640_PCLK_ROOT_DIV; + + return _rate / *pll_rdiv / *bit_div / *pclk_div; +} + +static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor, unsigned long rate) +{ + u8 prediv, mult, sysdiv, pll_rdiv, bit_div, pclk_div; + int ret; + + ov5640_calc_pclk(sensor, rate, &prediv, &mult, &sysdiv, &pll_rdiv, + &bit_div, &pclk_div); + + if (bit_div == 2) + bit_div = 8; + + ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL0, + 0x0f, bit_div); + if (ret) + return ret; + + /* + * We need to set sysdiv according to the clock, and to clear + * the MIPI divider. + */ + ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1, + 0xff, sysdiv << 4); + if (ret) + return ret; + + ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL2, + 0xff, mult); + if (ret) + return ret; + + ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3, + 0x1f, prediv | ((pll_rdiv - 1) << 4)); + if (ret) + return ret; + + return ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x30, + (ilog2(pclk_div) << 4)); +} + /* download ov5640 settings to sensor through i2c */ static int ov5640_set_timings(struct ov5640_dev *sensor, const struct ov5640_mode_info *mode) @@ -1062,16 +1183,6 @@ static int ov5640_set_stream_dvp(struct ov5640_dev *sensor, bool on) if (on) { /* - * reset MIPI PCLK/SERCLK divider - * - * SC PLL CONTRL1 0 - * - [3..0]: MIPI PCLK/SERCLK divider - */ - ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1, 0x0f, 0); - if (ret) - return ret; - - /* * configure parallel port control lines polarity * * POLARITY CTRL0 @@ -1444,8 +1555,8 @@ ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr, { const struct ov5640_mode_info *mode; - mode = v4l2_find_nearest_size(ov5640_mode_data[fr], - ARRAY_SIZE(ov5640_mode_data[fr]), + mode = v4l2_find_nearest_size(ov5640_mode_data, + ARRAY_SIZE(ov5640_mode_data), hact, vact, width, height); @@ -1453,6 +1564,11 @@ ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr, (!nearest && (mode->hact != width || mode->vact != height))) return NULL; + /* Only 640x480 can operate at 60fps (for now) */ + if (fr == OV5640_60_FPS && + !(mode->hact == 640 && mode->vact == 480)) + return NULL; + return mode; } @@ -1637,6 +1753,7 @@ static int ov5640_set_mode(struct ov5640_dev *sensor) enum ov5640_downsize_mode dn_mode, orig_dn_mode; bool auto_gain = sensor->ctrls.auto_gain->val == 1; bool auto_exp = sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO; + unsigned long rate; int ret; dn_mode = mode->dn_mode; @@ -1655,6 +1772,23 @@ static int ov5640_set_mode(struct ov5640_dev *sensor) goto restore_auto_gain; } + /* + * All the formats we support have 16 bits per pixel, seems to require + * the same rate than YUV, so we can just use 16 bpp all the time. + */ + rate = mode->vtot * mode->htot * 16; + rate *= ov5640_framerates[sensor->current_fr]; + if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY) { + rate = rate / sensor->ep.bus.mipi_csi2.num_data_lanes; + ret = ov5640_set_mipi_pclk(sensor, rate); + } else { + rate = rate / sensor->ep.bus.parallel.bus_width; + ret = ov5640_set_dvp_pclk(sensor, rate); + } + + if (ret < 0) + return 0; + if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) || (dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) { /* @@ -1724,8 +1858,8 @@ static int ov5640_restore_mode(struct ov5640_dev *sensor) 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) | - ilog2(OV5640_SCLK_ROOT_DIVIDER_DEFAULT)); + (ilog2(OV5640_SCLK2X_ROOT_DIV) << 2) | + ilog2(OV5640_SCLK_ROOT_DIV)); if (ret) return ret; @@ -1925,34 +2059,39 @@ static int ov5640_try_frame_interval(struct ov5640_dev *sensor, u32 width, u32 height) { const struct ov5640_mode_info *mode; - u32 minfps, maxfps, fps; - int ret; + enum ov5640_frame_rate rate = OV5640_30_FPS; + int minfps, maxfps, best_fps, fps; + int i; minfps = ov5640_framerates[OV5640_15_FPS]; - maxfps = ov5640_framerates[OV5640_30_FPS]; + maxfps = ov5640_framerates[OV5640_60_FPS]; if (fi->numerator == 0) { fi->denominator = maxfps; fi->numerator = 1; - return OV5640_30_FPS; + rate = OV5640_60_FPS; + goto find_mode; } - fps = DIV_ROUND_CLOSEST(fi->denominator, fi->numerator); + fps = clamp_val(DIV_ROUND_CLOSEST(fi->denominator, fi->numerator), + minfps, maxfps); - fi->numerator = 1; - if (fps > maxfps) - fi->denominator = maxfps; - else if (fps < minfps) - fi->denominator = minfps; - else if (2 * fps >= 2 * minfps + (maxfps - minfps)) - fi->denominator = maxfps; - else - fi->denominator = minfps; + best_fps = minfps; + for (i = 0; i < ARRAY_SIZE(ov5640_framerates); i++) { + int curr_fps = ov5640_framerates[i]; - ret = (fi->denominator == minfps) ? OV5640_15_FPS : OV5640_30_FPS; + if (abs(curr_fps - fps) < abs(best_fps - fps)) { + best_fps = curr_fps; + rate = i; + } + } - mode = ov5640_find_mode(sensor, ret, width, height, false); - return mode ? ret : -EINVAL; + fi->numerator = 1; + fi->denominator = best_fps; + +find_mode: + mode = ov5640_find_mode(sensor, rate, width, height, false); + return mode ? rate : -EINVAL; } static int ov5640_get_fmt(struct v4l2_subdev *sd, @@ -2020,6 +2159,7 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd, struct ov5640_dev *sensor = to_ov5640_dev(sd); const struct ov5640_mode_info *new_mode; struct v4l2_mbus_framefmt *mbus_fmt = &format->format; + struct v4l2_mbus_framefmt *fmt; int ret; if (format->pad != 0) @@ -2037,22 +2177,20 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd, if (ret) goto out; - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - struct v4l2_mbus_framefmt *fmt = - v4l2_subdev_get_try_format(sd, cfg, 0); + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + fmt = v4l2_subdev_get_try_format(sd, cfg, 0); + else + fmt = &sensor->fmt; - *fmt = *mbus_fmt; - goto out; - } + *fmt = *mbus_fmt; if (new_mode != sensor->current_mode) { sensor->current_mode = new_mode; sensor->pending_mode_change = true; } - if (mbus_fmt->code != sensor->fmt.code) { - sensor->fmt = *mbus_fmt; + if (mbus_fmt->code != sensor->fmt.code) sensor->pending_fmt_change = true; - } + out: mutex_unlock(&sensor->lock); return ret; @@ -2502,10 +2640,10 @@ static int ov5640_enum_frame_size(struct v4l2_subdev *sd, return -EINVAL; fse->min_width = - ov5640_mode_data[0][fse->index].hact; + ov5640_mode_data[fse->index].hact; fse->max_width = fse->min_width; fse->min_height = - ov5640_mode_data[0][fse->index].vact; + ov5640_mode_data[fse->index].vact; fse->max_height = fse->min_height; return 0; @@ -2570,8 +2708,11 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd, frame_rate = ov5640_try_frame_interval(sensor, &fi->interval, mode->hact, mode->vact); - if (frame_rate < 0) - frame_rate = OV5640_15_FPS; + if (frame_rate < 0) { + /* Always return a valid frame interval value */ + fi->interval = sensor->frame_interval; + goto out; + } mode = ov5640_find_mode(sensor, frame_rate, mode->hact, mode->vact, true); @@ -2641,6 +2782,9 @@ out: static const struct v4l2_subdev_core_ops ov5640_core_ops = { .s_power = ov5640_s_power, + .log_status = v4l2_ctrl_subdev_log_status, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, }; static const struct v4l2_subdev_video_ops ov5640_video_ops = { @@ -2736,7 +2880,7 @@ static int ov5640_probe(struct i2c_client *client, sensor->frame_interval.denominator = ov5640_framerates[OV5640_30_FPS]; sensor->current_fr = OV5640_30_FPS; sensor->current_mode = - &ov5640_mode_data[OV5640_30_FPS][OV5640_MODE_VGA_640_480]; + &ov5640_mode_data[OV5640_MODE_VGA_640_480]; sensor->last_mode = sensor->current_mode; sensor->ae_target = 52; @@ -2795,7 +2939,8 @@ static int ov5640_probe(struct i2c_client *client, v4l2_i2c_subdev_init(&sensor->sd, client, &ov5640_subdev_ops); - sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; sensor->pad.flags = MEDIA_PAD_FL_SOURCE; sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad); diff --git a/drivers/media/i2c/ov5645.c b/drivers/media/i2c/ov5645.c index 5eba8dd7222b..785f326ac519 100644 --- a/drivers/media/i2c/ov5645.c +++ b/drivers/media/i2c/ov5645.c @@ -886,7 +886,7 @@ static int ov5645_s_ctrl(struct v4l2_ctrl *ctrl) return ret; } -static struct v4l2_ctrl_ops ov5645_ctrl_ops = { +static const struct v4l2_ctrl_ops ov5645_ctrl_ops = { .s_ctrl = ov5645_s_ctrl, }; diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index bc68a3a5b4ec..a70a6ff7b36e 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -20,6 +20,7 @@ #include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <media/v4l2-device.h> +#include <media/v4l2-event.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-fwnode.h> #include <media/v4l2-mediabus.h> @@ -1651,6 +1652,9 @@ static int ov7670_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) static const struct v4l2_subdev_core_ops ov7670_core_ops = { .reset = ov7670_reset, .init = ov7670_init, + .log_status = v4l2_ctrl_subdev_log_status, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = ov7670_g_register, .s_register = ov7670_s_register, @@ -1773,7 +1777,7 @@ static int ov7670_probe(struct i2c_client *client, #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API sd->internal_ops = &ov7670_subdev_internal_ops; - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; #endif info->clock_speed = 30; /* default: a guess */ diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c index fefff7fd7d68..2e9a758736a1 100644 --- a/drivers/media/i2c/ov772x.c +++ b/drivers/media/i2c/ov772x.c @@ -30,6 +30,7 @@ #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> +#include <media/v4l2-event.h> #include <media/v4l2-image-sizes.h> #include <media/v4l2-subdev.h> @@ -1287,6 +1288,9 @@ static const struct v4l2_ctrl_ops ov772x_ctrl_ops = { }; static const struct v4l2_subdev_core_ops ov772x_subdev_core_ops = { + .log_status = v4l2_ctrl_subdev_log_status, + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = ov772x_g_register, .s_register = ov772x_s_register, @@ -1379,7 +1383,8 @@ static int ov772x_probe(struct i2c_client *client, mutex_init(&priv->lock); v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops); - priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; v4l2_ctrl_handler_init(&priv->hdl, 3); /* Use our mutex for the controls */ priv->hdl.lock = &priv->lock; diff --git a/drivers/media/i2c/ov7740.c b/drivers/media/i2c/ov7740.c index 6e9c233cfbe3..177688afd9a6 100644 --- a/drivers/media/i2c/ov7740.c +++ b/drivers/media/i2c/ov7740.c @@ -322,7 +322,7 @@ static int ov7740_set_power(struct ov7740 *ov7740, int on) return 0; } -static struct v4l2_subdev_core_ops ov7740_subdev_core_ops = { +static const struct v4l2_subdev_core_ops ov7740_subdev_core_ops = { .log_status = v4l2_ctrl_subdev_log_status, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = ov7740_get_register, @@ -648,7 +648,7 @@ static int ov7740_s_frame_interval(struct v4l2_subdev *sd, return 0; } -static struct v4l2_subdev_video_ops ov7740_subdev_video_ops = { +static const struct v4l2_subdev_video_ops ov7740_subdev_video_ops = { .s_stream = ov7740_set_stream, .s_frame_interval = ov7740_s_frame_interval, .g_frame_interval = ov7740_g_frame_interval, diff --git a/drivers/media/i2c/tc358743.c b/drivers/media/i2c/tc358743.c index 41d470d9ca94..bc2e35e5ce61 100644 --- a/drivers/media/i2c/tc358743.c +++ b/drivers/media/i2c/tc358743.c @@ -59,7 +59,7 @@ static const struct v4l2_dv_timings_cap tc358743_timings_cap = { /* keep this initialization for compatibility with GCC < 4.4.6 */ .reserved = { 0 }, /* Pixel clock from REF_01 p. 20. Min/max height/width are unknown */ - V4L2_INIT_BT_TIMINGS(1, 10000, 1, 10000, 0, 165000000, + V4L2_INIT_BT_TIMINGS(640, 1920, 350, 1200, 13000000, 165000000, V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT, V4L2_DV_BT_CAP_PROGRESSIVE | @@ -444,7 +444,7 @@ static void print_avi_infoframe(struct v4l2_subdev *sd) i2c_rd(sd, PK_AVI_0HEAD, buffer, HDMI_INFOFRAME_SIZE(AVI)); - if (hdmi_infoframe_unpack(&frame, buffer) < 0) { + if (hdmi_infoframe_unpack(&frame, buffer, sizeof(buffer)) < 0) { v4l2_err(sd, "%s: unpack of AVI infoframe failed\n", __func__); return; } diff --git a/drivers/media/i2c/tda1997x.c b/drivers/media/i2c/tda1997x.c index c4c2a6134e1e..e8613e364403 100644 --- a/drivers/media/i2c/tda1997x.c +++ b/drivers/media/i2c/tda1997x.c @@ -1253,7 +1253,7 @@ tda1997x_parse_infoframe(struct tda1997x_state *state, u16 addr) /* read data */ len = io_readn(sd, addr, sizeof(buffer), buffer); - err = hdmi_infoframe_unpack(&frame, buffer); + err = hdmi_infoframe_unpack(&frame, buffer, sizeof(buffer)); if (err) { v4l_err(state->client, "failed parsing %d byte infoframe: 0x%04x/0x%02x\n", @@ -1928,7 +1928,7 @@ static int tda1997x_log_infoframe(struct v4l2_subdev *sd, int addr) /* read data */ len = io_readn(sd, addr, sizeof(buffer), buffer); v4l2_dbg(1, debug, sd, "infoframe: addr=%d len=%d\n", addr, len); - err = hdmi_infoframe_unpack(&frame, buffer); + err = hdmi_infoframe_unpack(&frame, buffer, sizeof(buffer)); if (err) { v4l_err(state->client, "failed parsing %d byte infoframe: 0x%04x/0x%02x\n", diff --git a/drivers/media/i2c/tda7432.c b/drivers/media/i2c/tda7432.c index 9b4f21237810..06a78c2cdaab 100644 --- a/drivers/media/i2c/tda7432.c +++ b/drivers/media/i2c/tda7432.c @@ -19,7 +19,7 @@ * * loudness - set between 0 and 15 for varying degrees of loudness effect * - * maxvol - set maximium volume to +20db (1), default is 0db(0) + * maxvol - set maximum volume to +20db (1), default is 0db(0) */ #include <linux/module.h> @@ -53,7 +53,7 @@ MODULE_PARM_DESC(debug, "Set debugging level from 0 to 3. Default is off(0)."); module_param(loudness, int, S_IRUGO); MODULE_PARM_DESC(loudness, "Turn loudness on(1) else off(0). Default is off(0)."); module_param(maxvol, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(maxvol, "Set maximium volume to +20dB(0) else +0dB(1). Default is +20dB(0)."); +MODULE_PARM_DESC(maxvol, "Set maximum volume to +20dB(0) else +0dB(1). Default is +20dB(0)."); /* Structure of address and subaddresses for the tda7432 */ diff --git a/drivers/media/i2c/ths8200.c b/drivers/media/i2c/ths8200.c index 498ad2368cbc..f5ee28058ea2 100644 --- a/drivers/media/i2c/ths8200.c +++ b/drivers/media/i2c/ths8200.c @@ -49,7 +49,7 @@ static const struct v4l2_dv_timings_cap ths8200_timings_cap = { .type = V4L2_DV_BT_656_1120, /* keep this initialization for compatibility with GCC < 4.4.6 */ .reserved = { 0 }, - V4L2_INIT_BT_TIMINGS(0, 1920, 0, 1080, 25000000, 148500000, + V4L2_INIT_BT_TIMINGS(640, 1920, 350, 1080, 25000000, 148500000, V4L2_DV_BT_STD_CEA861, V4L2_DV_BT_CAP_PROGRESSIVE) }; diff --git a/drivers/media/i2c/tvp5150.c b/drivers/media/i2c/tvp5150.c index 0e91b9949c3a..eaddd977ba40 100644 --- a/drivers/media/i2c/tvp5150.c +++ b/drivers/media/i2c/tvp5150.c @@ -1790,7 +1790,7 @@ static int tvp5150_probe(struct i2c_client *c, tvp5150_isr, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "tvp5150", core); if (res) - return res; + goto err; } res = v4l2_async_register_subdev(sd); diff --git a/drivers/media/i2c/video-i2c.c b/drivers/media/i2c/video-i2c.c index 4d49af86c15e..01dcf179f203 100644 --- a/drivers/media/i2c/video-i2c.c +++ b/drivers/media/i2c/video-i2c.c @@ -17,6 +17,8 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/of_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> #include <linux/sched.h> #include <linux/slab.h> #include <linux/videodev2.h> @@ -38,7 +40,7 @@ struct video_i2c_buffer { }; struct video_i2c_data { - struct i2c_client *client; + struct regmap *regmap; const struct video_i2c_chip *chip; struct mutex lock; spinlock_t slock; @@ -51,6 +53,8 @@ struct video_i2c_data { struct task_struct *kthread_vid_cap; struct list_head vid_cap_active; + + struct v4l2_fract frame_interval; }; static const struct v4l2_fmtdesc amg88xx_format = { @@ -62,13 +66,20 @@ static const struct v4l2_frmsize_discrete amg88xx_size = { .height = 8, }; +static const struct regmap_config amg88xx_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xff +}; + struct video_i2c_chip { /* video dimensions */ const struct v4l2_fmtdesc *format; const struct v4l2_frmsize_discrete *size; - /* max frames per second */ - unsigned int max_fps; + /* available frame intervals */ + const struct v4l2_fract *frame_intervals; + unsigned int num_frame_intervals; /* pixel buffer size */ unsigned int buffer_size; @@ -76,33 +87,111 @@ struct video_i2c_chip { /* pixel size in bits */ unsigned int bpp; + const struct regmap_config *regmap_config; + + /* setup function */ + int (*setup)(struct video_i2c_data *data); + /* xfer function */ int (*xfer)(struct video_i2c_data *data, char *buf); + /* power control function */ + int (*set_power)(struct video_i2c_data *data, bool on); + /* hwmon init function */ int (*hwmon_init)(struct video_i2c_data *data); }; +/* Power control register */ +#define AMG88XX_REG_PCTL 0x00 +#define AMG88XX_PCTL_NORMAL 0x00 +#define AMG88XX_PCTL_SLEEP 0x10 + +/* Reset register */ +#define AMG88XX_REG_RST 0x01 +#define AMG88XX_RST_FLAG 0x30 +#define AMG88XX_RST_INIT 0x3f + +/* Frame rate register */ +#define AMG88XX_REG_FPSC 0x02 +#define AMG88XX_FPSC_1FPS BIT(0) + +/* Thermistor register */ +#define AMG88XX_REG_TTHL 0x0e + +/* Temperature register */ +#define AMG88XX_REG_T01L 0x80 + static int amg88xx_xfer(struct video_i2c_data *data, char *buf) { - struct i2c_client *client = data->client; - struct i2c_msg msg[2]; - u8 reg = 0x80; + return regmap_bulk_read(data->regmap, AMG88XX_REG_T01L, buf, + data->chip->buffer_size); +} + +static int amg88xx_setup(struct video_i2c_data *data) +{ + unsigned int mask = AMG88XX_FPSC_1FPS; + unsigned int val; + + if (data->frame_interval.numerator == data->frame_interval.denominator) + val = mask; + else + val = 0; + + return regmap_update_bits(data->regmap, AMG88XX_REG_FPSC, mask, val); +} + +static int amg88xx_set_power_on(struct video_i2c_data *data) +{ + int ret; + + ret = regmap_write(data->regmap, AMG88XX_REG_PCTL, AMG88XX_PCTL_NORMAL); + if (ret) + return ret; + + msleep(50); + + ret = regmap_write(data->regmap, AMG88XX_REG_RST, AMG88XX_RST_INIT); + if (ret) + return ret; + + usleep_range(2000, 3000); + + ret = regmap_write(data->regmap, AMG88XX_REG_RST, AMG88XX_RST_FLAG); + if (ret) + return ret; + + /* + * Wait two frames before reading thermistor and temperature registers + */ + msleep(200); + + return 0; +} + +static int amg88xx_set_power_off(struct video_i2c_data *data) +{ int ret; - msg[0].addr = client->addr; - msg[0].flags = 0; - msg[0].len = 1; - msg[0].buf = (char *)® + ret = regmap_write(data->regmap, AMG88XX_REG_PCTL, AMG88XX_PCTL_SLEEP); + if (ret) + return ret; + /* + * Wait for a while to avoid resuming normal mode immediately after + * entering sleep mode, otherwise the device occasionally goes wrong + * (thermistor and temperature registers are not updated at all) + */ + msleep(100); - msg[1].addr = client->addr; - msg[1].flags = I2C_M_RD; - msg[1].len = data->chip->buffer_size; - msg[1].buf = (char *)buf; + return 0; +} - ret = i2c_transfer(client->adapter, msg, 2); +static int amg88xx_set_power(struct video_i2c_data *data, bool on) +{ + if (on) + return amg88xx_set_power_on(data); - return (ret == 2) ? 0 : -EIO; + return amg88xx_set_power_off(data); } #if IS_ENABLED(CONFIG_HWMON) @@ -133,12 +222,23 @@ static int amg88xx_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { struct video_i2c_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - int tmp = i2c_smbus_read_word_data(client, 0x0e); + __le16 buf; + int tmp; + + tmp = pm_runtime_get_sync(regmap_get_device(data->regmap)); + if (tmp < 0) { + pm_runtime_put_noidle(regmap_get_device(data->regmap)); + return tmp; + } - if (tmp < 0) + tmp = regmap_bulk_read(data->regmap, AMG88XX_REG_TTHL, &buf, 2); + pm_runtime_mark_last_busy(regmap_get_device(data->regmap)); + pm_runtime_put_autosuspend(regmap_get_device(data->regmap)); + if (tmp) return tmp; + tmp = le16_to_cpu(buf); + /* * Check for sign bit, this isn't a two's complement value but an * absolute temperature that needs to be inverted in the case of being @@ -164,8 +264,9 @@ static const struct hwmon_chip_info amg88xx_chip_info = { static int amg88xx_hwmon_init(struct video_i2c_data *data) { - void *hwmon = devm_hwmon_device_register_with_info(&data->client->dev, - "amg88xx", data, &amg88xx_chip_info, NULL); + struct device *dev = regmap_get_device(data->regmap); + void *hwmon = devm_hwmon_device_register_with_info(dev, "amg88xx", data, + &amg88xx_chip_info, NULL); return PTR_ERR_OR_ZERO(hwmon); } @@ -175,14 +276,23 @@ static int amg88xx_hwmon_init(struct video_i2c_data *data) #define AMG88XX 0 +static const struct v4l2_fract amg88xx_frame_intervals[] = { + { 1, 10 }, + { 1, 1 }, +}; + static const struct video_i2c_chip video_i2c_chip[] = { [AMG88XX] = { .size = &amg88xx_size, .format = &amg88xx_format, - .max_fps = 10, + .frame_intervals = amg88xx_frame_intervals, + .num_frame_intervals = ARRAY_SIZE(amg88xx_frame_intervals), .buffer_size = 128, .bpp = 16, + .regmap_config = &amg88xx_regmap_config, + .setup = &amg88xx_setup, .xfer = &amg88xx_xfer, + .set_power = amg88xx_set_power, .hwmon_init = amg88xx_hwmon_init, }, }; @@ -246,7 +356,8 @@ static void buffer_queue(struct vb2_buffer *vb) static int video_i2c_thread_vid_cap(void *priv) { struct video_i2c_data *data = priv; - unsigned int delay = msecs_to_jiffies(1000 / data->chip->max_fps); + unsigned int delay = mult_frac(HZ, data->frame_interval.numerator, + data->frame_interval.denominator); set_freezable(); @@ -308,19 +419,36 @@ static void video_i2c_del_list(struct vb2_queue *vq, enum vb2_buffer_state state static int start_streaming(struct vb2_queue *vq, unsigned int count) { struct video_i2c_data *data = vb2_get_drv_priv(vq); + struct device *dev = regmap_get_device(data->regmap); + int ret; if (data->kthread_vid_cap) return 0; + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + pm_runtime_put_noidle(dev); + goto error_del_list; + } + + ret = data->chip->setup(data); + if (ret) + goto error_rpm_put; + data->sequence = 0; data->kthread_vid_cap = kthread_run(video_i2c_thread_vid_cap, data, "%s-vid-cap", data->v4l2_dev.name); - if (!IS_ERR(data->kthread_vid_cap)) + ret = PTR_ERR_OR_ZERO(data->kthread_vid_cap); + if (!ret) return 0; +error_rpm_put: + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); +error_del_list: video_i2c_del_list(vq, VB2_BUF_STATE_QUEUED); - return PTR_ERR(data->kthread_vid_cap); + return ret; } static void stop_streaming(struct vb2_queue *vq) @@ -332,11 +460,13 @@ static void stop_streaming(struct vb2_queue *vq) kthread_stop(data->kthread_vid_cap); data->kthread_vid_cap = NULL; + pm_runtime_mark_last_busy(regmap_get_device(data->regmap)); + pm_runtime_put_autosuspend(regmap_get_device(data->regmap)); video_i2c_del_list(vq, VB2_BUF_STATE_ERROR); } -static struct vb2_ops video_i2c_video_qops = { +static const struct vb2_ops video_i2c_video_qops = { .queue_setup = queue_setup, .buf_prepare = buffer_prepare, .buf_queue = buffer_queue, @@ -350,7 +480,8 @@ static int video_i2c_querycap(struct file *file, void *priv, struct v4l2_capability *vcap) { struct video_i2c_data *data = video_drvdata(file); - struct i2c_client *client = data->client; + struct device *dev = regmap_get_device(data->regmap); + struct i2c_client *client = to_i2c_client(dev); strscpy(vcap->driver, data->v4l2_dev.name, sizeof(vcap->driver)); strscpy(vcap->card, data->vdev.name, sizeof(vcap->card)); @@ -426,15 +557,14 @@ static int video_i2c_enum_frameintervals(struct file *file, void *priv, const struct video_i2c_data *data = video_drvdata(file); const struct v4l2_frmsize_discrete *size = data->chip->size; - if (fe->index > 0) + if (fe->index >= data->chip->num_frame_intervals) return -EINVAL; if (fe->width != size->width || fe->height != size->height) return -EINVAL; fe->type = V4L2_FRMIVAL_TYPE_DISCRETE; - fe->discrete.numerator = 1; - fe->discrete.denominator = data->chip->max_fps; + fe->discrete = data->chip->frame_intervals[fe->index]; return 0; } @@ -479,12 +609,27 @@ static int video_i2c_g_parm(struct file *filp, void *priv, parm->parm.capture.readbuffers = 1; parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; - parm->parm.capture.timeperframe.numerator = 1; - parm->parm.capture.timeperframe.denominator = data->chip->max_fps; + parm->parm.capture.timeperframe = data->frame_interval; return 0; } +static int video_i2c_s_parm(struct file *filp, void *priv, + struct v4l2_streamparm *parm) +{ + struct video_i2c_data *data = video_drvdata(filp); + int i; + + for (i = 0; i < data->chip->num_frame_intervals - 1; i++) { + if (V4L2_FRACT_COMPARE(parm->parm.capture.timeperframe, <=, + data->chip->frame_intervals[i])) + break; + } + data->frame_interval = data->chip->frame_intervals[i]; + + return video_i2c_g_parm(filp, priv, parm); +} + static const struct v4l2_ioctl_ops video_i2c_ioctl_ops = { .vidioc_querycap = video_i2c_querycap, .vidioc_g_input = video_i2c_g_input, @@ -496,7 +641,7 @@ static const struct v4l2_ioctl_ops video_i2c_ioctl_ops = { .vidioc_g_fmt_vid_cap = video_i2c_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = video_i2c_s_fmt_vid_cap, .vidioc_g_parm = video_i2c_g_parm, - .vidioc_s_parm = video_i2c_g_parm, + .vidioc_s_parm = video_i2c_s_parm, .vidioc_try_fmt_vid_cap = video_i2c_try_fmt_vid_cap, .vidioc_reqbufs = vb2_ioctl_reqbufs, .vidioc_create_bufs = vb2_ioctl_create_bufs, @@ -510,7 +655,13 @@ static const struct v4l2_ioctl_ops video_i2c_ioctl_ops = { static void video_i2c_release(struct video_device *vdev) { - kfree(video_get_drvdata(vdev)); + struct video_i2c_data *data = video_get_drvdata(vdev); + + v4l2_device_unregister(&data->v4l2_dev); + mutex_destroy(&data->lock); + mutex_destroy(&data->queue_lock); + regmap_exit(data->regmap); + kfree(data); } static int video_i2c_probe(struct i2c_client *client, @@ -532,13 +683,18 @@ static int video_i2c_probe(struct i2c_client *client, else goto error_free_device; - data->client = client; + data->regmap = regmap_init_i2c(client, data->chip->regmap_config); + if (IS_ERR(data->regmap)) { + ret = PTR_ERR(data->regmap); + goto error_free_device; + } + v4l2_dev = &data->v4l2_dev; strscpy(v4l2_dev->name, VIDEO_I2C_DRIVER, sizeof(v4l2_dev->name)); ret = v4l2_device_register(&client->dev, v4l2_dev); if (ret < 0) - goto error_free_device; + goto error_regmap_exit; mutex_init(&data->lock); mutex_init(&data->queue_lock); @@ -575,9 +731,23 @@ static int video_i2c_probe(struct i2c_client *client, spin_lock_init(&data->slock); INIT_LIST_HEAD(&data->vid_cap_active); + data->frame_interval = data->chip->frame_intervals[0]; + video_set_drvdata(&data->vdev, data); i2c_set_clientdata(client, data); + if (data->chip->set_power) { + ret = data->chip->set_power(data, true); + if (ret) + goto error_unregister_device; + } + + pm_runtime_get_noresume(&client->dev); + pm_runtime_set_active(&client->dev); + pm_runtime_enable(&client->dev); + pm_runtime_set_autosuspend_delay(&client->dev, 2000); + pm_runtime_use_autosuspend(&client->dev); + if (data->chip->hwmon_init) { ret = data->chip->hwmon_init(data); if (ret < 0) { @@ -588,15 +758,29 @@ static int video_i2c_probe(struct i2c_client *client, ret = video_register_device(&data->vdev, VFL_TYPE_GRABBER, -1); if (ret < 0) - goto error_unregister_device; + goto error_pm_disable; + + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_autosuspend(&client->dev); return 0; +error_pm_disable: + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + pm_runtime_put_noidle(&client->dev); + + if (data->chip->set_power) + data->chip->set_power(data, false); + error_unregister_device: v4l2_device_unregister(v4l2_dev); mutex_destroy(&data->lock); mutex_destroy(&data->queue_lock); +error_regmap_exit: + regmap_exit(data->regmap); + error_free_device: kfree(data); @@ -607,15 +791,48 @@ static int video_i2c_remove(struct i2c_client *client) { struct video_i2c_data *data = i2c_get_clientdata(client); - video_unregister_device(&data->vdev); - v4l2_device_unregister(&data->v4l2_dev); + pm_runtime_get_sync(&client->dev); + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + pm_runtime_put_noidle(&client->dev); - mutex_destroy(&data->lock); - mutex_destroy(&data->queue_lock); + if (data->chip->set_power) + data->chip->set_power(data, false); + + video_unregister_device(&data->vdev); return 0; } +#ifdef CONFIG_PM + +static int video_i2c_pm_runtime_suspend(struct device *dev) +{ + struct video_i2c_data *data = i2c_get_clientdata(to_i2c_client(dev)); + + if (!data->chip->set_power) + return 0; + + return data->chip->set_power(data, false); +} + +static int video_i2c_pm_runtime_resume(struct device *dev) +{ + struct video_i2c_data *data = i2c_get_clientdata(to_i2c_client(dev)); + + if (!data->chip->set_power) + return 0; + + return data->chip->set_power(data, true); +} + +#endif + +static const struct dev_pm_ops video_i2c_pm_ops = { + SET_RUNTIME_PM_OPS(video_i2c_pm_runtime_suspend, + video_i2c_pm_runtime_resume, NULL) +}; + static const struct i2c_device_id video_i2c_id_table[] = { { "amg88xx", AMG88XX }, {} @@ -632,6 +849,7 @@ static struct i2c_driver video_i2c_driver = { .driver = { .name = VIDEO_I2C_DRIVER, .of_match_table = video_i2c_of_match, + .pm = &video_i2c_pm_ops, }, .probe = video_i2c_probe, .remove = video_i2c_remove, |