diff options
Diffstat (limited to 'drivers/media/i2c')
36 files changed, 1318 insertions, 10550 deletions
diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig index c3d5952ca27e..256d55bb2b1d 100644 --- a/drivers/media/i2c/Kconfig +++ b/drivers/media/i2c/Kconfig @@ -267,16 +267,6 @@ config VIDEO_MT9M001 This driver supports MT9M001 cameras from Micron, monochrome and colour models. -config VIDEO_MT9M032 - tristate "MT9M032 camera sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - select VIDEO_APTINA_PLL - help - This driver supports MT9M032 camera sensors from Aptina, monochrome - models only. - config VIDEO_MT9M111 tristate "mt9m111, mt9m112 and mt9m131 support" depends on I2C && VIDEO_DEV @@ -296,15 +286,6 @@ config VIDEO_MT9P031 This is a Video4Linux2 sensor driver for the Aptina (Micron) mt9p031 5 Mpixel camera. -config VIDEO_MT9T001 - tristate "Aptina MT9T001 support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - help - This is a Video4Linux2 sensor driver for the Aptina - (Micron) mt0t001 3 Mpixel camera. - config VIDEO_MT9T112 tristate "Aptina MT9T111/MT9T112 support" depends on I2C && VIDEO_DEV @@ -344,14 +325,6 @@ config VIDEO_MT9V111 To compile this driver as a module, choose M here: the module will be called mt9v111. -config VIDEO_NOON010PC30 - tristate "Siliconfile NOON010PC30 sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - help - This driver supports NOON010PC30 CIF camera from Siliconfile - config VIDEO_OG01A1B tristate "OmniVision OG01A1B sensor support" depends on I2C && VIDEO_DEV @@ -462,6 +435,7 @@ config VIDEO_OV2685 tristate "OmniVision OV2685 sensor support" depends on VIDEO_DEV && I2C select MEDIA_CONTROLLER + select VIDEO_V4L2_SUBDEV_API select V4L2_FWNODE help This is a Video4Linux2 sensor driver for the OmniVision @@ -810,21 +784,6 @@ config VIDEO_S5K6A3 This is a V4L2 sensor driver for Samsung S5K6A3 raw camera sensor. -config VIDEO_S5K6AA - tristate "Samsung S5K6AAFX sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - help - This is a V4L2 sensor driver for Samsung S5K6AA(FX) 1.3M - camera sensor with an embedded SoC image signal processor. - -config VIDEO_SR030PC30 - tristate "Siliconfile SR030PC30 sensor support" - depends on I2C && VIDEO_DEV - help - This driver supports SR030PC30 VGA camera from Siliconfile - config VIDEO_ST_VGXY61 tristate "ST VGXY61 sensor support" depends on OF && GPIOLIB && VIDEO_DEV && I2C @@ -835,19 +794,8 @@ config VIDEO_ST_VGXY61 This is a Video4Linux2 sensor driver for the ST VGXY61 camera sensor. -config VIDEO_VS6624 - tristate "ST VS6624 sensor support" - depends on VIDEO_DEV && I2C - help - This is a Video4Linux2 sensor driver for the ST VS6624 - camera. - - To compile this driver as a module, choose M here: the - module will be called vs6624. - source "drivers/media/i2c/ccs/Kconfig" source "drivers/media/i2c/et8ek8/Kconfig" -source "drivers/media/i2c/m5mols/Kconfig" endmenu @@ -1453,20 +1401,6 @@ endmenu menu "Video encoders" visible if !MEDIA_HIDE_ANCILLARY_SUBDRV -config VIDEO_AD9389B - tristate "Analog Devices AD9389B encoder" - depends on VIDEO_DEV && I2C - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - - help - Support for the Analog Devices AD9389B video encoder. - - This is a Analog Devices HDMI transmitter. - - To compile this driver as a module, choose M here: the - module will be called ad9389b. - config VIDEO_ADV7170 tristate "Analog Devices ADV7170 video encoder" depends on VIDEO_DEV && I2C diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile index 4f5e9d9cee85..b44dacf935f4 100644 --- a/drivers/media/i2c/Makefile +++ b/drivers/media/i2c/Makefile @@ -4,7 +4,6 @@ msp3400-objs := msp3400-driver.o msp3400-kthreads.o obj-$(CONFIG_SDR_MAX2175) += max2175.o obj-$(CONFIG_VIDEO_AD5820) += ad5820.o -obj-$(CONFIG_VIDEO_AD9389B) += ad9389b.o obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o @@ -56,21 +55,17 @@ obj-$(CONFIG_VIDEO_KS0127) += ks0127.o obj-$(CONFIG_VIDEO_LM3560) += lm3560.o obj-$(CONFIG_VIDEO_LM3646) += lm3646.o obj-$(CONFIG_VIDEO_M52790) += m52790.o -obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/ obj-$(CONFIG_VIDEO_MAX9271_LIB) += max9271.o obj-$(CONFIG_VIDEO_MAX9286) += max9286.o obj-$(CONFIG_VIDEO_ML86V7667) += ml86v7667.o obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o obj-$(CONFIG_VIDEO_MT9M001) += mt9m001.o -obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o obj-$(CONFIG_VIDEO_MT9M111) += mt9m111.o obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o -obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o obj-$(CONFIG_VIDEO_MT9T112) += mt9t112.o obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o obj-$(CONFIG_VIDEO_MT9V111) += mt9v111.o -obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o obj-$(CONFIG_VIDEO_OG01A1B) += og01a1b.o obj-$(CONFIG_VIDEO_OV02A10) += ov02a10.o obj-$(CONFIG_VIDEO_OV08D10) += ov08d10.o @@ -110,7 +105,6 @@ obj-$(CONFIG_VIDEO_RJ54N1) += rj54n1cb0c.o obj-$(CONFIG_VIDEO_S5C73M3) += s5c73m3/ obj-$(CONFIG_VIDEO_S5K5BAF) += s5k5baf.o obj-$(CONFIG_VIDEO_S5K6A3) += s5k6a3.o -obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o obj-$(CONFIG_VIDEO_SAA6752HS) += saa6752hs.o obj-$(CONFIG_VIDEO_SAA7110) += saa7110.o @@ -119,7 +113,6 @@ obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o obj-$(CONFIG_VIDEO_SONY_BTF_MPX) += sony-btf-mpx.o -obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o obj-$(CONFIG_VIDEO_ST_MIPID02) += st-mipid02.o obj-$(CONFIG_VIDEO_ST_VGXY61) += st-vgxy61.o obj-$(CONFIG_VIDEO_TC358743) += tc358743.o @@ -145,6 +138,5 @@ obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o -obj-$(CONFIG_VIDEO_VS6624) += vs6624.o obj-$(CONFIG_VIDEO_WM8739) += wm8739.o obj-$(CONFIG_VIDEO_WM8775) += wm8775.o diff --git a/drivers/media/i2c/ad9389b.c b/drivers/media/i2c/ad9389b.c deleted file mode 100644 index ad17097a2d25..000000000000 --- a/drivers/media/i2c/ad9389b.c +++ /dev/null @@ -1,1215 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Analog Devices AD9389B/AD9889B video encoder driver - * - * Copyright 2012 Cisco Systems, Inc. and/or its affiliates. All rights reserved. - */ - -/* - * References (c = chapter, p = page): - * REF_01 - Analog Devices, Programming Guide, AD9889B/AD9389B, - * HDMI Transitter, Rev. A, October 2010 - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/i2c.h> -#include <linux/delay.h> -#include <linux/videodev2.h> -#include <linux/workqueue.h> -#include <linux/v4l2-dv-timings.h> -#include <media/v4l2-device.h> -#include <media/v4l2-common.h> -#include <media/v4l2-dv-timings.h> -#include <media/v4l2-ctrls.h> -#include <media/i2c/ad9389b.h> - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "debug level (0-2)"); - -MODULE_DESCRIPTION("Analog Devices AD9389B/AD9889B video encoder driver"); -MODULE_AUTHOR("Hans Verkuil <hans.verkuil@cisco.com>"); -MODULE_AUTHOR("Martin Bugge <marbugge@cisco.com>"); -MODULE_LICENSE("GPL"); - -#define MASK_AD9389B_EDID_RDY_INT 0x04 -#define MASK_AD9389B_MSEN_INT 0x40 -#define MASK_AD9389B_HPD_INT 0x80 - -#define MASK_AD9389B_HPD_DETECT 0x40 -#define MASK_AD9389B_MSEN_DETECT 0x20 -#define MASK_AD9389B_EDID_RDY 0x10 - -#define EDID_MAX_RETRIES (8) -#define EDID_DELAY 250 -#define EDID_MAX_SEGM 8 - -/* -********************************************************************** -* -* Arrays with configuration parameters for the AD9389B -* -********************************************************************** -*/ - -struct ad9389b_state_edid { - /* total number of blocks */ - u32 blocks; - /* Number of segments read */ - u32 segments; - u8 data[EDID_MAX_SEGM * 256]; - /* Number of EDID read retries left */ - unsigned read_retries; -}; - -struct ad9389b_state { - struct ad9389b_platform_data pdata; - struct v4l2_subdev sd; - struct media_pad pad; - struct v4l2_ctrl_handler hdl; - int chip_revision; - /* Is the ad9389b powered on? */ - bool power_on; - /* Did we receive hotplug and rx-sense signals? */ - bool have_monitor; - /* timings from s_dv_timings */ - struct v4l2_dv_timings dv_timings; - /* controls */ - struct v4l2_ctrl *hdmi_mode_ctrl; - struct v4l2_ctrl *hotplug_ctrl; - struct v4l2_ctrl *rx_sense_ctrl; - struct v4l2_ctrl *have_edid0_ctrl; - struct v4l2_ctrl *rgb_quantization_range_ctrl; - struct i2c_client *edid_i2c_client; - struct ad9389b_state_edid edid; - /* Running counter of the number of detected EDIDs (for debugging) */ - unsigned edid_detect_counter; - struct delayed_work edid_handler; /* work entry */ -}; - -static void ad9389b_check_monitor_present_status(struct v4l2_subdev *sd); -static bool ad9389b_check_edid_status(struct v4l2_subdev *sd); -static void ad9389b_setup(struct v4l2_subdev *sd); -static int ad9389b_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq); -static int ad9389b_s_clock_freq(struct v4l2_subdev *sd, u32 freq); - -static inline struct ad9389b_state *get_ad9389b_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct ad9389b_state, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct ad9389b_state, hdl)->sd; -} - -/* ------------------------ I2C ----------------------------------------------- */ - -static int ad9389b_rd(struct v4l2_subdev *sd, u8 reg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return i2c_smbus_read_byte_data(client, reg); -} - -static int ad9389b_wr(struct v4l2_subdev *sd, u8 reg, u8 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - int i; - - for (i = 0; i < 3; i++) { - ret = i2c_smbus_write_byte_data(client, reg, val); - if (ret == 0) - return 0; - } - v4l2_err(sd, "%s: failed reg 0x%x, val 0x%x\n", __func__, reg, val); - return ret; -} - -/* To set specific bits in the register, a clear-mask is given (to be AND-ed), - and then the value-mask (to be OR-ed). */ -static inline void ad9389b_wr_and_or(struct v4l2_subdev *sd, u8 reg, - u8 clr_mask, u8 val_mask) -{ - ad9389b_wr(sd, reg, (ad9389b_rd(sd, reg) & clr_mask) | val_mask); -} - -static void ad9389b_edid_rd(struct v4l2_subdev *sd, u16 len, u8 *buf) -{ - struct ad9389b_state *state = get_ad9389b_state(sd); - int i; - - v4l2_dbg(1, debug, sd, "%s:\n", __func__); - - for (i = 0; i < len; i++) - buf[i] = i2c_smbus_read_byte_data(state->edid_i2c_client, i); -} - -static inline bool ad9389b_have_hotplug(struct v4l2_subdev *sd) -{ - return ad9389b_rd(sd, 0x42) & MASK_AD9389B_HPD_DETECT; -} - -static inline bool ad9389b_have_rx_sense(struct v4l2_subdev *sd) -{ - return ad9389b_rd(sd, 0x42) & MASK_AD9389B_MSEN_DETECT; -} - -static void ad9389b_csc_conversion_mode(struct v4l2_subdev *sd, u8 mode) -{ - ad9389b_wr_and_or(sd, 0x17, 0xe7, (mode & 0x3)<<3); - ad9389b_wr_and_or(sd, 0x18, 0x9f, (mode & 0x3)<<5); -} - -static void ad9389b_csc_coeff(struct v4l2_subdev *sd, - u16 A1, u16 A2, u16 A3, u16 A4, - u16 B1, u16 B2, u16 B3, u16 B4, - u16 C1, u16 C2, u16 C3, u16 C4) -{ - /* A */ - ad9389b_wr_and_or(sd, 0x18, 0xe0, A1>>8); - ad9389b_wr(sd, 0x19, A1); - ad9389b_wr_and_or(sd, 0x1A, 0xe0, A2>>8); - ad9389b_wr(sd, 0x1B, A2); - ad9389b_wr_and_or(sd, 0x1c, 0xe0, A3>>8); - ad9389b_wr(sd, 0x1d, A3); - ad9389b_wr_and_or(sd, 0x1e, 0xe0, A4>>8); - ad9389b_wr(sd, 0x1f, A4); - - /* B */ - ad9389b_wr_and_or(sd, 0x20, 0xe0, B1>>8); - ad9389b_wr(sd, 0x21, B1); - ad9389b_wr_and_or(sd, 0x22, 0xe0, B2>>8); - ad9389b_wr(sd, 0x23, B2); - ad9389b_wr_and_or(sd, 0x24, 0xe0, B3>>8); - ad9389b_wr(sd, 0x25, B3); - ad9389b_wr_and_or(sd, 0x26, 0xe0, B4>>8); - ad9389b_wr(sd, 0x27, B4); - - /* C */ - ad9389b_wr_and_or(sd, 0x28, 0xe0, C1>>8); - ad9389b_wr(sd, 0x29, C1); - ad9389b_wr_and_or(sd, 0x2A, 0xe0, C2>>8); - ad9389b_wr(sd, 0x2B, C2); - ad9389b_wr_and_or(sd, 0x2C, 0xe0, C3>>8); - ad9389b_wr(sd, 0x2D, C3); - ad9389b_wr_and_or(sd, 0x2E, 0xe0, C4>>8); - ad9389b_wr(sd, 0x2F, C4); -} - -static void ad9389b_csc_rgb_full2limit(struct v4l2_subdev *sd, bool enable) -{ - if (enable) { - u8 csc_mode = 0; - - ad9389b_csc_conversion_mode(sd, csc_mode); - ad9389b_csc_coeff(sd, - 4096-564, 0, 0, 256, - 0, 4096-564, 0, 256, - 0, 0, 4096-564, 256); - /* enable CSC */ - ad9389b_wr_and_or(sd, 0x3b, 0xfe, 0x1); - /* AVI infoframe: Limited range RGB (16-235) */ - ad9389b_wr_and_or(sd, 0xcd, 0xf9, 0x02); - } else { - /* disable CSC */ - ad9389b_wr_and_or(sd, 0x3b, 0xfe, 0x0); - /* AVI infoframe: Full range RGB (0-255) */ - ad9389b_wr_and_or(sd, 0xcd, 0xf9, 0x04); - } -} - -static void ad9389b_set_IT_content_AVI_InfoFrame(struct v4l2_subdev *sd) -{ - struct ad9389b_state *state = get_ad9389b_state(sd); - - if (state->dv_timings.bt.flags & V4L2_DV_FL_IS_CE_VIDEO) { - /* CE format, not IT */ - ad9389b_wr_and_or(sd, 0xcd, 0xbf, 0x00); - } else { - /* IT format */ - ad9389b_wr_and_or(sd, 0xcd, 0xbf, 0x40); - } -} - -static int ad9389b_set_rgb_quantization_mode(struct v4l2_subdev *sd, struct v4l2_ctrl *ctrl) -{ - struct ad9389b_state *state = get_ad9389b_state(sd); - - switch (ctrl->val) { - case V4L2_DV_RGB_RANGE_AUTO: - /* automatic */ - if (state->dv_timings.bt.flags & V4L2_DV_FL_IS_CE_VIDEO) { - /* CE format, RGB limited range (16-235) */ - ad9389b_csc_rgb_full2limit(sd, true); - } else { - /* not CE format, RGB full range (0-255) */ - ad9389b_csc_rgb_full2limit(sd, false); - } - break; - case V4L2_DV_RGB_RANGE_LIMITED: - /* RGB limited range (16-235) */ - ad9389b_csc_rgb_full2limit(sd, true); - break; - case V4L2_DV_RGB_RANGE_FULL: - /* RGB full range (0-255) */ - ad9389b_csc_rgb_full2limit(sd, false); - break; - default: - return -EINVAL; - } - return 0; -} - -static void ad9389b_set_manual_pll_gear(struct v4l2_subdev *sd, u32 pixelclock) -{ - u8 gear; - - /* Workaround for TMDS PLL problem - * The TMDS PLL in AD9389b change gear when the chip is heated above a - * certain temperature. The output is disabled when the PLL change gear - * so the monitor has to lock on the signal again. A workaround for - * this is to use the manual PLL gears. This is a solution from Analog - * Devices that is not documented in the datasheets. - * 0x98 [7] = enable manual gearing. 0x98 [6:4] = gear - * - * The pixel frequency ranges are based on readout of the gear the - * automatic gearing selects for different pixel clocks - * (read from 0x9e [3:1]). - */ - - if (pixelclock > 140000000) - gear = 0xc0; /* 4th gear */ - else if (pixelclock > 117000000) - gear = 0xb0; /* 3rd gear */ - else if (pixelclock > 87000000) - gear = 0xa0; /* 2nd gear */ - else if (pixelclock > 60000000) - gear = 0x90; /* 1st gear */ - else - gear = 0x80; /* 0th gear */ - - ad9389b_wr_and_or(sd, 0x98, 0x0f, gear); -} - -/* ------------------------------ CTRL OPS ------------------------------ */ - -static int ad9389b_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - struct ad9389b_state *state = get_ad9389b_state(sd); - - v4l2_dbg(1, debug, sd, - "%s: ctrl id: %d, ctrl->val %d\n", __func__, ctrl->id, ctrl->val); - - if (state->hdmi_mode_ctrl == ctrl) { - /* Set HDMI or DVI-D */ - ad9389b_wr_and_or(sd, 0xaf, 0xfd, - ctrl->val == V4L2_DV_TX_MODE_HDMI ? 0x02 : 0x00); - return 0; - } - if (state->rgb_quantization_range_ctrl == ctrl) - return ad9389b_set_rgb_quantization_mode(sd, ctrl); - return -EINVAL; -} - -static const struct v4l2_ctrl_ops ad9389b_ctrl_ops = { - .s_ctrl = ad9389b_s_ctrl, -}; - -/* ---------------------------- CORE OPS ------------------------------------------- */ - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int ad9389b_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - reg->val = ad9389b_rd(sd, reg->reg & 0xff); - reg->size = 1; - return 0; -} - -static int ad9389b_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) -{ - ad9389b_wr(sd, reg->reg & 0xff, reg->val & 0xff); - return 0; -} -#endif - -static int ad9389b_log_status(struct v4l2_subdev *sd) -{ - struct ad9389b_state *state = get_ad9389b_state(sd); - struct ad9389b_state_edid *edid = &state->edid; - - static const char * const states[] = { - "in reset", - "reading EDID", - "idle", - "initializing HDCP", - "HDCP enabled", - "initializing HDCP repeater", - "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" - }; - static const char * const errors[] = { - "no error", - "bad receiver BKSV", - "Ri mismatch", - "Pj mismatch", - "i2c error", - "timed out", - "max repeater cascade exceeded", - "hash check failed", - "too many devices", - "9", "A", "B", "C", "D", "E", "F" - }; - - u8 manual_gear; - - v4l2_info(sd, "chip revision %d\n", state->chip_revision); - v4l2_info(sd, "power %s\n", state->power_on ? "on" : "off"); - v4l2_info(sd, "%s hotplug, %s Rx Sense, %s EDID (%d block(s))\n", - (ad9389b_rd(sd, 0x42) & MASK_AD9389B_HPD_DETECT) ? - "detected" : "no", - (ad9389b_rd(sd, 0x42) & MASK_AD9389B_MSEN_DETECT) ? - "detected" : "no", - edid->segments ? "found" : "no", edid->blocks); - v4l2_info(sd, "%s output %s\n", - (ad9389b_rd(sd, 0xaf) & 0x02) ? - "HDMI" : "DVI-D", - (ad9389b_rd(sd, 0xa1) & 0x3c) ? - "disabled" : "enabled"); - v4l2_info(sd, "ad9389b: %s\n", (ad9389b_rd(sd, 0xb8) & 0x40) ? - "encrypted" : "no encryption"); - v4l2_info(sd, "state: %s, error: %s, detect count: %u, msk/irq: %02x/%02x\n", - states[ad9389b_rd(sd, 0xc8) & 0xf], - errors[ad9389b_rd(sd, 0xc8) >> 4], - state->edid_detect_counter, - ad9389b_rd(sd, 0x94), ad9389b_rd(sd, 0x96)); - manual_gear = ad9389b_rd(sd, 0x98) & 0x80; - v4l2_info(sd, "ad9389b: RGB quantization: %s range\n", - ad9389b_rd(sd, 0x3b) & 0x01 ? "limited" : "full"); - v4l2_info(sd, "ad9389b: %s gear %d\n", - manual_gear ? "manual" : "automatic", - manual_gear ? ((ad9389b_rd(sd, 0x98) & 0x70) >> 4) : - ((ad9389b_rd(sd, 0x9e) & 0x0e) >> 1)); - if (ad9389b_rd(sd, 0xaf) & 0x02) { - /* HDMI only */ - u8 manual_cts = ad9389b_rd(sd, 0x0a) & 0x80; - u32 N = (ad9389b_rd(sd, 0x01) & 0xf) << 16 | - ad9389b_rd(sd, 0x02) << 8 | - ad9389b_rd(sd, 0x03); - u8 vic_detect = ad9389b_rd(sd, 0x3e) >> 2; - u8 vic_sent = ad9389b_rd(sd, 0x3d) & 0x3f; - u32 CTS; - - if (manual_cts) - CTS = (ad9389b_rd(sd, 0x07) & 0xf) << 16 | - ad9389b_rd(sd, 0x08) << 8 | - ad9389b_rd(sd, 0x09); - else - CTS = (ad9389b_rd(sd, 0x04) & 0xf) << 16 | - ad9389b_rd(sd, 0x05) << 8 | - ad9389b_rd(sd, 0x06); - N = (ad9389b_rd(sd, 0x01) & 0xf) << 16 | - ad9389b_rd(sd, 0x02) << 8 | - ad9389b_rd(sd, 0x03); - - v4l2_info(sd, "ad9389b: CTS %s mode: N %d, CTS %d\n", - manual_cts ? "manual" : "automatic", N, CTS); - - v4l2_info(sd, "ad9389b: VIC: detected %d, sent %d\n", - vic_detect, vic_sent); - } - if (state->dv_timings.type == V4L2_DV_BT_656_1120) - v4l2_print_dv_timings(sd->name, "timings: ", - &state->dv_timings, false); - else - v4l2_info(sd, "no timings set\n"); - return 0; -} - -/* Power up/down ad9389b */ -static int ad9389b_s_power(struct v4l2_subdev *sd, int on) -{ - struct ad9389b_state *state = get_ad9389b_state(sd); - struct ad9389b_platform_data *pdata = &state->pdata; - const int retries = 20; - int i; - - v4l2_dbg(1, debug, sd, "%s: power %s\n", __func__, on ? "on" : "off"); - - state->power_on = on; - - if (!on) { - /* Power down */ - ad9389b_wr_and_or(sd, 0x41, 0xbf, 0x40); - return true; - } - - /* Power up */ - /* The ad9389b does not always come up immediately. - Retry multiple times. */ - for (i = 0; i < retries; i++) { - ad9389b_wr_and_or(sd, 0x41, 0xbf, 0x0); - if ((ad9389b_rd(sd, 0x41) & 0x40) == 0) - break; - ad9389b_wr_and_or(sd, 0x41, 0xbf, 0x40); - msleep(10); - } - if (i == retries) { - v4l2_dbg(1, debug, sd, "failed to powerup the ad9389b\n"); - ad9389b_s_power(sd, 0); - return false; - } - if (i > 1) - v4l2_dbg(1, debug, sd, - "needed %d retries to powerup the ad9389b\n", i); - - /* Select chip: AD9389B */ - ad9389b_wr_and_or(sd, 0xba, 0xef, 0x10); - - /* Reserved registers that must be set according to REF_01 p. 11*/ - ad9389b_wr_and_or(sd, 0x98, 0xf0, 0x07); - ad9389b_wr(sd, 0x9c, 0x38); - ad9389b_wr_and_or(sd, 0x9d, 0xfc, 0x01); - - /* Differential output drive strength */ - if (pdata->diff_data_drive_strength > 0) - ad9389b_wr(sd, 0xa2, pdata->diff_data_drive_strength); - else - ad9389b_wr(sd, 0xa2, 0x87); - - if (pdata->diff_clk_drive_strength > 0) - ad9389b_wr(sd, 0xa3, pdata->diff_clk_drive_strength); - else - ad9389b_wr(sd, 0xa3, 0x87); - - ad9389b_wr(sd, 0x0a, 0x01); - ad9389b_wr(sd, 0xbb, 0xff); - - /* Set number of attempts to read the EDID */ - ad9389b_wr(sd, 0xc9, 0xf); - return true; -} - -/* Enable interrupts */ -static void ad9389b_set_isr(struct v4l2_subdev *sd, bool enable) -{ - u8 irqs = MASK_AD9389B_HPD_INT | MASK_AD9389B_MSEN_INT; - u8 irqs_rd; - int retries = 100; - - /* The datasheet says that the EDID ready interrupt should be - disabled if there is no hotplug. */ - if (!enable) - irqs = 0; - else if (ad9389b_have_hotplug(sd)) - irqs |= MASK_AD9389B_EDID_RDY_INT; - - /* - * This i2c write can fail (approx. 1 in 1000 writes). But it - * is essential that this register is correct, so retry it - * multiple times. - * - * Note that the i2c write does not report an error, but the readback - * clearly shows the wrong value. - */ - do { - ad9389b_wr(sd, 0x94, irqs); - irqs_rd = ad9389b_rd(sd, 0x94); - } while (retries-- && irqs_rd != irqs); - - if (irqs_rd != irqs) - v4l2_err(sd, "Could not set interrupts: hw failure?\n"); -} - -/* Interrupt handler */ -static int ad9389b_isr(struct v4l2_subdev *sd, u32 status, bool *handled) -{ - u8 irq_status; - - /* disable interrupts to prevent a race condition */ - ad9389b_set_isr(sd, false); - irq_status = ad9389b_rd(sd, 0x96); - /* clear detected interrupts */ - ad9389b_wr(sd, 0x96, irq_status); - /* enable interrupts */ - ad9389b_set_isr(sd, true); - - v4l2_dbg(1, debug, sd, "%s: irq_status 0x%x\n", __func__, irq_status); - - if (irq_status & (MASK_AD9389B_HPD_INT)) - ad9389b_check_monitor_present_status(sd); - if (irq_status & MASK_AD9389B_EDID_RDY_INT) - ad9389b_check_edid_status(sd); - - *handled = true; - return 0; -} - -static const struct v4l2_subdev_core_ops ad9389b_core_ops = { - .log_status = ad9389b_log_status, -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = ad9389b_g_register, - .s_register = ad9389b_s_register, -#endif - .s_power = ad9389b_s_power, - .interrupt_service_routine = ad9389b_isr, -}; - -/* ------------------------------ VIDEO OPS ------------------------------ */ - -/* Enable/disable ad9389b output */ -static int ad9389b_s_stream(struct v4l2_subdev *sd, int enable) -{ - v4l2_dbg(1, debug, sd, "%s: %sable\n", __func__, (enable ? "en" : "dis")); - - ad9389b_wr_and_or(sd, 0xa1, ~0x3c, (enable ? 0 : 0x3c)); - if (enable) { - ad9389b_check_monitor_present_status(sd); - } else { - ad9389b_s_power(sd, 0); - } - return 0; -} - -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(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 | - V4L2_DV_BT_CAP_CUSTOM) -}; - -static int ad9389b_s_dv_timings(struct v4l2_subdev *sd, - struct v4l2_dv_timings *timings) -{ - struct ad9389b_state *state = get_ad9389b_state(sd); - - v4l2_dbg(1, debug, sd, "%s:\n", __func__); - - /* quick sanity check */ - if (!v4l2_valid_dv_timings(timings, &ad9389b_timings_cap, NULL, NULL)) - return -EINVAL; - - /* Fill the optional fields .standards and .flags in struct v4l2_dv_timings - if the format is one of the CEA or DMT timings. */ - v4l2_find_dv_timings_cap(timings, &ad9389b_timings_cap, 0, NULL, NULL); - - timings->bt.flags &= ~V4L2_DV_FL_REDUCED_FPS; - - /* save timings */ - state->dv_timings = *timings; - - /* update quantization range based on new dv_timings */ - ad9389b_set_rgb_quantization_mode(sd, state->rgb_quantization_range_ctrl); - - /* update PLL gear based on new dv_timings */ - if (state->pdata.tmds_pll_gear == AD9389B_TMDS_PLL_GEAR_SEMI_AUTOMATIC) - ad9389b_set_manual_pll_gear(sd, (u32)timings->bt.pixelclock); - - /* update AVI infoframe */ - ad9389b_set_IT_content_AVI_InfoFrame(sd); - - return 0; -} - -static int ad9389b_g_dv_timings(struct v4l2_subdev *sd, - struct v4l2_dv_timings *timings) -{ - struct ad9389b_state *state = get_ad9389b_state(sd); - - v4l2_dbg(1, debug, sd, "%s:\n", __func__); - - if (!timings) - return -EINVAL; - - *timings = state->dv_timings; - - return 0; -} - -static int ad9389b_enum_dv_timings(struct v4l2_subdev *sd, - struct v4l2_enum_dv_timings *timings) -{ - if (timings->pad != 0) - return -EINVAL; - - return v4l2_enum_dv_timings_cap(timings, &ad9389b_timings_cap, - NULL, NULL); -} - -static int ad9389b_dv_timings_cap(struct v4l2_subdev *sd, - struct v4l2_dv_timings_cap *cap) -{ - if (cap->pad != 0) - return -EINVAL; - - *cap = ad9389b_timings_cap; - return 0; -} - -static const struct v4l2_subdev_video_ops ad9389b_video_ops = { - .s_stream = ad9389b_s_stream, - .s_dv_timings = ad9389b_s_dv_timings, - .g_dv_timings = ad9389b_g_dv_timings, -}; - -/* ------------------------------ PAD OPS ------------------------------ */ - -static int ad9389b_get_edid(struct v4l2_subdev *sd, struct v4l2_edid *edid) -{ - struct ad9389b_state *state = get_ad9389b_state(sd); - - if (edid->pad != 0) - return -EINVAL; - if (edid->blocks == 0 || edid->blocks > 256) - return -EINVAL; - if (!state->edid.segments) { - v4l2_dbg(1, debug, sd, "EDID segment 0 not found\n"); - return -ENODATA; - } - if (edid->start_block >= state->edid.segments * 2) - return -E2BIG; - if (edid->blocks + edid->start_block >= state->edid.segments * 2) - edid->blocks = state->edid.segments * 2 - edid->start_block; - memcpy(edid->edid, &state->edid.data[edid->start_block * 128], - 128 * edid->blocks); - return 0; -} - -static const struct v4l2_subdev_pad_ops ad9389b_pad_ops = { - .get_edid = ad9389b_get_edid, - .enum_dv_timings = ad9389b_enum_dv_timings, - .dv_timings_cap = ad9389b_dv_timings_cap, -}; - -/* ------------------------------ AUDIO OPS ------------------------------ */ - -static int ad9389b_s_audio_stream(struct v4l2_subdev *sd, int enable) -{ - v4l2_dbg(1, debug, sd, "%s: %sable\n", __func__, (enable ? "en" : "dis")); - - if (enable) - ad9389b_wr_and_or(sd, 0x45, 0x3f, 0x80); - else - ad9389b_wr_and_or(sd, 0x45, 0x3f, 0x40); - - return 0; -} - -static int ad9389b_s_clock_freq(struct v4l2_subdev *sd, u32 freq) -{ - u32 N; - - switch (freq) { - case 32000: N = 4096; break; - case 44100: N = 6272; break; - case 48000: N = 6144; break; - case 88200: N = 12544; break; - case 96000: N = 12288; break; - case 176400: N = 25088; break; - case 192000: N = 24576; break; - default: - return -EINVAL; - } - - /* Set N (used with CTS to regenerate the audio clock) */ - ad9389b_wr(sd, 0x01, (N >> 16) & 0xf); - ad9389b_wr(sd, 0x02, (N >> 8) & 0xff); - ad9389b_wr(sd, 0x03, N & 0xff); - - return 0; -} - -static int ad9389b_s_i2s_clock_freq(struct v4l2_subdev *sd, u32 freq) -{ - u32 i2s_sf; - - switch (freq) { - case 32000: i2s_sf = 0x30; break; - case 44100: i2s_sf = 0x00; break; - case 48000: i2s_sf = 0x20; break; - case 88200: i2s_sf = 0x80; break; - case 96000: i2s_sf = 0xa0; break; - case 176400: i2s_sf = 0xc0; break; - case 192000: i2s_sf = 0xe0; break; - default: - return -EINVAL; - } - - /* Set sampling frequency for I2S audio to 48 kHz */ - ad9389b_wr_and_or(sd, 0x15, 0xf, i2s_sf); - - return 0; -} - -static int ad9389b_s_routing(struct v4l2_subdev *sd, u32 input, u32 output, u32 config) -{ - /* TODO based on input/output/config */ - /* TODO See datasheet "Programmers guide" p. 39-40 */ - - /* Only 2 channels in use for application */ - ad9389b_wr_and_or(sd, 0x50, 0x1f, 0x20); - /* Speaker mapping */ - ad9389b_wr(sd, 0x51, 0x00); - - /* TODO Where should this be placed? */ - /* 16 bit audio word length */ - ad9389b_wr_and_or(sd, 0x14, 0xf0, 0x02); - - return 0; -} - -static const struct v4l2_subdev_audio_ops ad9389b_audio_ops = { - .s_stream = ad9389b_s_audio_stream, - .s_clock_freq = ad9389b_s_clock_freq, - .s_i2s_clock_freq = ad9389b_s_i2s_clock_freq, - .s_routing = ad9389b_s_routing, -}; - -/* --------------------- SUBDEV OPS --------------------------------------- */ - -static const struct v4l2_subdev_ops ad9389b_ops = { - .core = &ad9389b_core_ops, - .video = &ad9389b_video_ops, - .audio = &ad9389b_audio_ops, - .pad = &ad9389b_pad_ops, -}; - -/* ----------------------------------------------------------------------- */ -static void ad9389b_dbg_dump_edid(int lvl, int debug, struct v4l2_subdev *sd, - int segment, u8 *buf) -{ - int i, j; - - if (debug < lvl) - return; - - v4l2_dbg(lvl, debug, sd, "edid segment %d\n", segment); - for (i = 0; i < 256; i += 16) { - u8 b[128]; - u8 *bp = b; - - if (i == 128) - v4l2_dbg(lvl, debug, sd, "\n"); - for (j = i; j < i + 16; j++) { - sprintf(bp, "0x%02x, ", buf[j]); - bp += 6; - } - bp[0] = '\0'; - v4l2_dbg(lvl, debug, sd, "%s\n", b); - } -} - -static void ad9389b_edid_handler(struct work_struct *work) -{ - struct delayed_work *dwork = to_delayed_work(work); - struct ad9389b_state *state = - container_of(dwork, struct ad9389b_state, edid_handler); - struct v4l2_subdev *sd = &state->sd; - struct ad9389b_edid_detect ed; - - v4l2_dbg(1, debug, sd, "%s:\n", __func__); - - if (ad9389b_check_edid_status(sd)) { - /* Return if we received the EDID. */ - return; - } - - if (ad9389b_have_hotplug(sd)) { - /* We must retry reading the EDID several times, it is possible - * that initially the EDID couldn't be read due to i2c errors - * (DVI connectors are particularly prone to this problem). */ - if (state->edid.read_retries) { - state->edid.read_retries--; - v4l2_dbg(1, debug, sd, "%s: edid read failed\n", __func__); - ad9389b_s_power(sd, false); - ad9389b_s_power(sd, true); - schedule_delayed_work(&state->edid_handler, EDID_DELAY); - return; - } - } - - /* We failed to read the EDID, so send an event for this. */ - ed.present = false; - ed.segment = ad9389b_rd(sd, 0xc4); - v4l2_subdev_notify(sd, AD9389B_EDID_DETECT, (void *)&ed); - v4l2_dbg(1, debug, sd, "%s: no edid found\n", __func__); -} - -static void ad9389b_audio_setup(struct v4l2_subdev *sd) -{ - v4l2_dbg(1, debug, sd, "%s\n", __func__); - - ad9389b_s_i2s_clock_freq(sd, 48000); - ad9389b_s_clock_freq(sd, 48000); - ad9389b_s_routing(sd, 0, 0, 0); -} - -/* Initial setup of AD9389b */ - -/* Configure hdmi transmitter. */ -static void ad9389b_setup(struct v4l2_subdev *sd) -{ - struct ad9389b_state *state = get_ad9389b_state(sd); - - v4l2_dbg(1, debug, sd, "%s\n", __func__); - - /* Input format: RGB 4:4:4 */ - ad9389b_wr_and_or(sd, 0x15, 0xf1, 0x0); - /* Output format: RGB 4:4:4 */ - ad9389b_wr_and_or(sd, 0x16, 0x3f, 0x0); - /* 1st order interpolation 4:2:2 -> 4:4:4 up conversion, - Aspect ratio: 16:9 */ - ad9389b_wr_and_or(sd, 0x17, 0xf9, 0x06); - /* Output format: RGB 4:4:4, Active Format Information is valid. */ - ad9389b_wr_and_or(sd, 0x45, 0xc7, 0x08); - /* Underscanned */ - ad9389b_wr_and_or(sd, 0x46, 0x3f, 0x80); - /* Setup video format */ - ad9389b_wr(sd, 0x3c, 0x0); - /* Active format aspect ratio: same as picure. */ - ad9389b_wr(sd, 0x47, 0x80); - /* No encryption */ - ad9389b_wr_and_or(sd, 0xaf, 0xef, 0x0); - /* Positive clk edge capture for input video clock */ - ad9389b_wr_and_or(sd, 0xba, 0x1f, 0x60); - - ad9389b_audio_setup(sd); - - v4l2_ctrl_handler_setup(&state->hdl); - - ad9389b_set_IT_content_AVI_InfoFrame(sd); -} - -static void ad9389b_notify_monitor_detect(struct v4l2_subdev *sd) -{ - struct ad9389b_monitor_detect mdt; - struct ad9389b_state *state = get_ad9389b_state(sd); - - mdt.present = state->have_monitor; - v4l2_subdev_notify(sd, AD9389B_MONITOR_DETECT, (void *)&mdt); -} - -static void ad9389b_update_monitor_present_status(struct v4l2_subdev *sd) -{ - struct ad9389b_state *state = get_ad9389b_state(sd); - /* read hotplug and rx-sense state */ - u8 status = ad9389b_rd(sd, 0x42); - - v4l2_dbg(1, debug, sd, "%s: status: 0x%x%s%s\n", - __func__, - status, - status & MASK_AD9389B_HPD_DETECT ? ", hotplug" : "", - status & MASK_AD9389B_MSEN_DETECT ? ", rx-sense" : ""); - - if (status & MASK_AD9389B_HPD_DETECT) { - v4l2_dbg(1, debug, sd, "%s: hotplug detected\n", __func__); - state->have_monitor = true; - if (!ad9389b_s_power(sd, true)) { - v4l2_dbg(1, debug, sd, - "%s: monitor detected, powerup failed\n", __func__); - return; - } - ad9389b_setup(sd); - ad9389b_notify_monitor_detect(sd); - state->edid.read_retries = EDID_MAX_RETRIES; - schedule_delayed_work(&state->edid_handler, EDID_DELAY); - } else if (!(status & MASK_AD9389B_HPD_DETECT)) { - v4l2_dbg(1, debug, sd, "%s: hotplug not detected\n", __func__); - state->have_monitor = false; - ad9389b_notify_monitor_detect(sd); - ad9389b_s_power(sd, false); - memset(&state->edid, 0, sizeof(struct ad9389b_state_edid)); - } - - /* update read only ctrls */ - v4l2_ctrl_s_ctrl(state->hotplug_ctrl, ad9389b_have_hotplug(sd) ? 0x1 : 0x0); - v4l2_ctrl_s_ctrl(state->rx_sense_ctrl, ad9389b_have_rx_sense(sd) ? 0x1 : 0x0); - v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, state->edid.segments ? 0x1 : 0x0); - - /* update with setting from ctrls */ - ad9389b_s_ctrl(state->rgb_quantization_range_ctrl); - ad9389b_s_ctrl(state->hdmi_mode_ctrl); -} - -static void ad9389b_check_monitor_present_status(struct v4l2_subdev *sd) -{ - struct ad9389b_state *state = get_ad9389b_state(sd); - int retry = 0; - - ad9389b_update_monitor_present_status(sd); - - /* - * Rapid toggling of the hotplug may leave the chip powered off, - * even if we think it is on. In that case reset and power up again. - */ - while (state->power_on && (ad9389b_rd(sd, 0x41) & 0x40)) { - if (++retry > 5) { - v4l2_err(sd, "retried %d times, give up\n", retry); - return; - } - v4l2_dbg(1, debug, sd, "%s: reset and re-check status (%d)\n", __func__, retry); - ad9389b_notify_monitor_detect(sd); - cancel_delayed_work_sync(&state->edid_handler); - memset(&state->edid, 0, sizeof(struct ad9389b_state_edid)); - ad9389b_s_power(sd, false); - ad9389b_update_monitor_present_status(sd); - } -} - -static bool edid_block_verify_crc(u8 *edid_block) -{ - u8 sum = 0; - int i; - - for (i = 0; i < 128; i++) - sum += edid_block[i]; - return sum == 0; -} - -static bool edid_verify_crc(struct v4l2_subdev *sd, u32 segment) -{ - struct ad9389b_state *state = get_ad9389b_state(sd); - u32 blocks = state->edid.blocks; - u8 *data = state->edid.data; - - if (edid_block_verify_crc(&data[segment * 256])) { - if ((segment + 1) * 2 <= blocks) - return edid_block_verify_crc(&data[segment * 256 + 128]); - return true; - } - return false; -} - -static bool edid_verify_header(struct v4l2_subdev *sd, u32 segment) -{ - static const u8 hdmi_header[] = { - 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 - }; - struct ad9389b_state *state = get_ad9389b_state(sd); - u8 *data = state->edid.data; - int i; - - if (segment) - return true; - - for (i = 0; i < ARRAY_SIZE(hdmi_header); i++) - if (data[i] != hdmi_header[i]) - return false; - - return true; -} - -static bool ad9389b_check_edid_status(struct v4l2_subdev *sd) -{ - struct ad9389b_state *state = get_ad9389b_state(sd); - struct ad9389b_edid_detect ed; - int segment; - u8 edidRdy = ad9389b_rd(sd, 0xc5); - - v4l2_dbg(1, debug, sd, "%s: edid ready (retries: %d)\n", - __func__, EDID_MAX_RETRIES - state->edid.read_retries); - - if (!(edidRdy & MASK_AD9389B_EDID_RDY)) - return false; - - segment = ad9389b_rd(sd, 0xc4); - if (segment >= EDID_MAX_SEGM) { - v4l2_err(sd, "edid segment number too big\n"); - return false; - } - v4l2_dbg(1, debug, sd, "%s: got segment %d\n", __func__, segment); - ad9389b_edid_rd(sd, 256, &state->edid.data[segment * 256]); - ad9389b_dbg_dump_edid(2, debug, sd, segment, - &state->edid.data[segment * 256]); - if (segment == 0) { - state->edid.blocks = state->edid.data[0x7e] + 1; - v4l2_dbg(1, debug, sd, "%s: %d blocks in total\n", - __func__, state->edid.blocks); - } - if (!edid_verify_crc(sd, segment) || - !edid_verify_header(sd, segment)) { - /* edid crc error, force reread of edid segment */ - v4l2_err(sd, "%s: edid crc or header error\n", __func__); - ad9389b_s_power(sd, false); - ad9389b_s_power(sd, true); - return false; - } - /* one more segment read ok */ - state->edid.segments = segment + 1; - if (((state->edid.data[0x7e] >> 1) + 1) > state->edid.segments) { - /* Request next EDID segment */ - v4l2_dbg(1, debug, sd, "%s: request segment %d\n", - __func__, state->edid.segments); - ad9389b_wr(sd, 0xc9, 0xf); - ad9389b_wr(sd, 0xc4, state->edid.segments); - state->edid.read_retries = EDID_MAX_RETRIES; - schedule_delayed_work(&state->edid_handler, EDID_DELAY); - return false; - } - - /* report when we have all segments but report only for segment 0 */ - ed.present = true; - ed.segment = 0; - v4l2_subdev_notify(sd, AD9389B_EDID_DETECT, (void *)&ed); - state->edid_detect_counter++; - v4l2_ctrl_s_ctrl(state->have_edid0_ctrl, state->edid.segments ? 0x1 : 0x0); - return ed.present; -} - -/* ----------------------------------------------------------------------- */ - -static void ad9389b_init_setup(struct v4l2_subdev *sd) -{ - struct ad9389b_state *state = get_ad9389b_state(sd); - struct ad9389b_state_edid *edid = &state->edid; - - v4l2_dbg(1, debug, sd, "%s\n", __func__); - - /* clear all interrupts */ - ad9389b_wr(sd, 0x96, 0xff); - - memset(edid, 0, sizeof(struct ad9389b_state_edid)); - state->have_monitor = false; - ad9389b_set_isr(sd, false); -} - -static int ad9389b_probe(struct i2c_client *client) -{ - const struct v4l2_dv_timings dv1080p60 = V4L2_DV_BT_CEA_1920X1080P60; - struct ad9389b_state *state; - struct ad9389b_platform_data *pdata = client->dev.platform_data; - struct v4l2_ctrl_handler *hdl; - struct v4l2_subdev *sd; - int err = -EIO; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EIO; - - v4l_dbg(1, debug, client, "detecting ad9389b client on address 0x%x\n", - client->addr << 1); - - state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL); - if (!state) - return -ENOMEM; - - /* Platform data */ - if (pdata == NULL) { - v4l_err(client, "No platform data!\n"); - return -ENODEV; - } - memcpy(&state->pdata, pdata, sizeof(state->pdata)); - - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &ad9389b_ops); - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - hdl = &state->hdl; - v4l2_ctrl_handler_init(hdl, 5); - - state->hdmi_mode_ctrl = v4l2_ctrl_new_std_menu(hdl, &ad9389b_ctrl_ops, - V4L2_CID_DV_TX_MODE, V4L2_DV_TX_MODE_HDMI, - 0, V4L2_DV_TX_MODE_DVI_D); - state->hotplug_ctrl = v4l2_ctrl_new_std(hdl, NULL, - V4L2_CID_DV_TX_HOTPLUG, 0, 1, 0, 0); - state->rx_sense_ctrl = v4l2_ctrl_new_std(hdl, NULL, - V4L2_CID_DV_TX_RXSENSE, 0, 1, 0, 0); - state->have_edid0_ctrl = v4l2_ctrl_new_std(hdl, NULL, - V4L2_CID_DV_TX_EDID_PRESENT, 0, 1, 0, 0); - state->rgb_quantization_range_ctrl = - v4l2_ctrl_new_std_menu(hdl, &ad9389b_ctrl_ops, - V4L2_CID_DV_TX_RGB_RANGE, V4L2_DV_RGB_RANGE_FULL, - 0, V4L2_DV_RGB_RANGE_AUTO); - sd->ctrl_handler = hdl; - if (hdl->error) { - err = hdl->error; - - goto err_hdl; - } - state->pad.flags = MEDIA_PAD_FL_SINK; - sd->entity.function = MEDIA_ENT_F_DV_ENCODER; - err = media_entity_pads_init(&sd->entity, 1, &state->pad); - if (err) - goto err_hdl; - - state->chip_revision = ad9389b_rd(sd, 0x0); - if (state->chip_revision != 2) { - v4l2_err(sd, "chip_revision %d != 2\n", state->chip_revision); - err = -EIO; - goto err_entity; - } - v4l2_dbg(1, debug, sd, "reg 0x41 0x%x, chip version (reg 0x00) 0x%x\n", - ad9389b_rd(sd, 0x41), state->chip_revision); - - state->edid_i2c_client = i2c_new_dummy_device(client->adapter, (0x7e >> 1)); - if (IS_ERR(state->edid_i2c_client)) { - v4l2_err(sd, "failed to register edid i2c client\n"); - err = PTR_ERR(state->edid_i2c_client); - goto err_entity; - } - - INIT_DELAYED_WORK(&state->edid_handler, ad9389b_edid_handler); - state->dv_timings = dv1080p60; - - ad9389b_init_setup(sd); - ad9389b_set_isr(sd, true); - - v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name, - client->addr << 1, client->adapter->name); - return 0; - -err_entity: - media_entity_cleanup(&sd->entity); -err_hdl: - v4l2_ctrl_handler_free(&state->hdl); - return err; -} - -/* ----------------------------------------------------------------------- */ - -static void ad9389b_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct ad9389b_state *state = get_ad9389b_state(sd); - - state->chip_revision = -1; - - v4l2_dbg(1, debug, sd, "%s removed @ 0x%x (%s)\n", client->name, - client->addr << 1, client->adapter->name); - - ad9389b_s_stream(sd, false); - ad9389b_s_audio_stream(sd, false); - ad9389b_init_setup(sd); - cancel_delayed_work_sync(&state->edid_handler); - i2c_unregister_device(state->edid_i2c_client); - v4l2_device_unregister_subdev(sd); - media_entity_cleanup(&sd->entity); - v4l2_ctrl_handler_free(sd->ctrl_handler); -} - -/* ----------------------------------------------------------------------- */ - -static const struct i2c_device_id ad9389b_id[] = { - { "ad9389b", 0 }, - { "ad9889b", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, ad9389b_id); - -static struct i2c_driver ad9389b_driver = { - .driver = { - .name = "ad9389b", - }, - .probe_new = ad9389b_probe, - .remove = ad9389b_remove, - .id_table = ad9389b_id, -}; - -module_i2c_driver(ad9389b_driver); diff --git a/drivers/media/i2c/adv748x/adv748x-hdmi.c b/drivers/media/i2c/adv748x/adv748x-hdmi.c index 52fa7bd75660..400d71c2745c 100644 --- a/drivers/media/i2c/adv748x/adv748x-hdmi.c +++ b/drivers/media/i2c/adv748x/adv748x-hdmi.c @@ -176,9 +176,9 @@ static int adv748x_hdmi_set_video_timings(struct adv748x_state *state, unsigned int i; for (i = 0; i < ARRAY_SIZE(adv748x_hdmi_video_standards); i++) { - if (!v4l2_match_dv_timings(timings, &stds[i].timings, 250000, - false)) - continue; + if (v4l2_match_dv_timings(timings, &stds[i].timings, 250000, + false)) + break; } if (i >= ARRAY_SIZE(adv748x_hdmi_video_standards)) @@ -283,6 +283,16 @@ static int adv748x_hdmi_query_dv_timings(struct v4l2_subdev *sd, memset(timings, 0, sizeof(struct v4l2_dv_timings)); + /* + * If the pattern generator is enabled the device shall not be queried + * for timings. Instead the timings programmed shall be reported as they + * are the ones being used to generate the pattern. + */ + if (cp_read(state, ADV748X_CP_PAT_GEN) & ADV748X_CP_PAT_GEN_EN) { + *timings = hdmi->timings; + return 0; + } + if (!adv748x_hdmi_has_signal(state)) return -ENOLINK; @@ -721,11 +731,10 @@ static int adv748x_hdmi_init_controls(struct adv748x_hdmi *hdmi) int adv748x_hdmi_init(struct adv748x_hdmi *hdmi) { struct adv748x_state *state = adv748x_hdmi_to_state(hdmi); - static const struct v4l2_dv_timings cea1280x720 = - V4L2_DV_BT_CEA_1280X720P30; + struct v4l2_dv_timings cea1280x720 = V4L2_DV_BT_CEA_1280X720P30; int ret; - hdmi->timings = cea1280x720; + adv748x_hdmi_s_dv_timings(&hdmi->sd, &cea1280x720); /* Initialise a default 16:9 aspect ratio */ hdmi->aspect_ratio.numerator = 16; diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 9d218962d7c8..3d0898c4175e 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -1805,6 +1805,9 @@ static void select_input(struct v4l2_subdev *sd) v4l2_dbg(2, debug, sd, "%s: Unknown port %d selected\n", __func__, state->selected_input); } + + /* Enable video adjustment (contrast, saturation, brightness and hue) */ + cp_write_clr_set(sd, 0x3e, 0x80, 0x80); } static int adv76xx_s_routing(struct v4l2_subdev *sd, @@ -3545,7 +3548,7 @@ static int adv76xx_probe(struct i2c_client *client) v4l2_ctrl_new_std(hdl, &adv76xx_ctrl_ops, V4L2_CID_SATURATION, 0, 255, 1, 128); v4l2_ctrl_new_std(hdl, &adv76xx_ctrl_ops, - V4L2_CID_HUE, 0, 128, 1, 0); + V4L2_CID_HUE, 0, 255, 1, 0); ctrl = v4l2_ctrl_new_std_menu(hdl, &adv76xx_ctrl_ops, V4L2_CID_DV_RX_IT_CONTENT_TYPE, V4L2_DV_IT_CONTENT_TYPE_NO_ITC, 0, V4L2_DV_IT_CONTENT_TYPE_NO_ITC); diff --git a/drivers/media/i2c/ccs/ccs-core.c b/drivers/media/i2c/ccs/ccs-core.c index 4a14d7e5d9f2..559a415fd827 100644 --- a/drivers/media/i2c/ccs/ccs-core.c +++ b/drivers/media/i2c/ccs/ccs-core.c @@ -569,8 +569,6 @@ static u32 ccs_pixel_order(struct ccs_sensor *sensor) flip |= CCS_IMAGE_ORIENTATION_VERTICAL_FLIP; } - flip ^= sensor->hvflip_inv_mask; - dev_dbg(&client->dev, "flip %u\n", flip); return sensor->default_pixel_order ^ flip; } @@ -632,8 +630,6 @@ static int ccs_set_ctrl(struct v4l2_ctrl *ctrl) if (sensor->vflip->val) orient |= CCS_IMAGE_ORIENTATION_VERTICAL_FLIP; - orient ^= sensor->hvflip_inv_mask; - ccs_update_mbus_formats(sensor); break; @@ -800,14 +796,24 @@ static const struct v4l2_ctrl_ops ccs_ctrl_ops = { static int ccs_init_controls(struct ccs_sensor *sensor) { struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct v4l2_fwnode_device_properties props; int rval; - rval = v4l2_ctrl_handler_init(&sensor->pixel_array->ctrl_handler, 17); + rval = v4l2_ctrl_handler_init(&sensor->pixel_array->ctrl_handler, 19); if (rval) return rval; sensor->pixel_array->ctrl_handler.lock = &sensor->mutex; + rval = v4l2_fwnode_device_parse(&client->dev, &props); + if (rval) + return rval; + + rval = v4l2_ctrl_new_fwnode_properties(&sensor->pixel_array->ctrl_handler, + &ccs_ctrl_ops, &props); + if (rval) + return rval; + switch (CCS_LIM(sensor, ANALOG_GAIN_CAPABILITY)) { case CCS_ANALOG_GAIN_CAPABILITY_GLOBAL: { struct { @@ -2828,6 +2834,10 @@ static int ccs_identify_module(struct ccs_sensor *sensor) rval = ccs_read_addr_8only(sensor, CCS_R_SENSOR_REVISION_NUMBER, &minfo->sensor_revision_number); + if (!rval && !minfo->sensor_revision_number) + rval = ccs_read_addr_8only(sensor, + CCS_R_SENSOR_REVISION_NUMBER_16, + &minfo->sensor_revision_number); if (!rval) rval = ccs_read_addr_8only(sensor, CCS_R_SENSOR_FIRMWARE_VERSION, @@ -2870,7 +2880,7 @@ static int ccs_identify_module(struct ccs_sensor *sensor) minfo->sensor_model_id); dev_dbg(&client->dev, - "sensor revision 0x%2.2x firmware version 0x%2.2x\n", + "sensor revision 0x%4.4x firmware version 0x%2.2x\n", minfo->sensor_revision_number, minfo->sensor_firmware_version); if (minfo->ccs_version) { @@ -2884,19 +2894,18 @@ static int ccs_identify_module(struct ccs_sensor *sensor) "smia version %2.2d smiapp version %2.2d\n", minfo->smia_version, minfo->smiapp_version); minfo->name = SMIAPP_NAME; - } - - /* - * Some modules have bad data in the lvalues below. Hope the - * rvalues have better stuff. The lvalues are module - * parameters whereas the rvalues are sensor parameters. - */ - if (minfo->sensor_smia_manufacturer_id && - !minfo->smia_manufacturer_id && !minfo->model_id) { - minfo->smia_manufacturer_id = - minfo->sensor_smia_manufacturer_id; - minfo->model_id = minfo->sensor_model_id; - minfo->revision_number = minfo->sensor_revision_number; + /* + * Some modules have bad data in the lvalues below. Hope the + * rvalues have better stuff. The lvalues are module + * parameters whereas the rvalues are sensor parameters. + */ + if (minfo->sensor_smia_manufacturer_id && + !minfo->smia_manufacturer_id && !minfo->model_id) { + minfo->smia_manufacturer_id = + minfo->sensor_smia_manufacturer_id; + minfo->model_id = minfo->sensor_model_id; + minfo->revision_number = minfo->sensor_revision_number; + } } for (i = 0; i < ARRAY_SIZE(ccs_module_idents); i++) { @@ -3185,7 +3194,6 @@ static int ccs_get_hwconfig(struct ccs_sensor *sensor, struct device *dev) struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = V4L2_MBUS_UNKNOWN }; struct fwnode_handle *ep; struct fwnode_handle *fwnode = dev_fwnode(dev); - u32 rotation; unsigned int i; int rval; @@ -3224,22 +3232,6 @@ static int ccs_get_hwconfig(struct ccs_sensor *sensor, struct device *dev) goto out_err; } - rval = fwnode_property_read_u32(fwnode, "rotation", &rotation); - if (!rval) { - switch (rotation) { - case 180: - hwcfg->module_board_orient = - CCS_MODULE_BOARD_ORIENT_180; - fallthrough; - case 0: - break; - default: - dev_err(dev, "invalid rotation %u\n", rotation); - rval = -EINVAL; - goto out_err; - } - } - rval = fwnode_property_read_u32(dev_fwnode(dev), "clock-frequency", &hwcfg->ext_clk); if (rval) @@ -3279,8 +3271,47 @@ out_err: return rval; } +static int ccs_firmware_name(struct i2c_client *client, + struct ccs_sensor *sensor, char *filename, + size_t filename_size, bool is_module) +{ + const struct ccs_device *ccsdev = device_get_match_data(&client->dev); + bool is_ccs = !(ccsdev->flags & CCS_DEVICE_FLAG_IS_SMIA); + bool is_smiapp = sensor->minfo.smiapp_version; + u16 manufacturer_id; + u16 model_id; + u16 revision_number; + + /* + * Old SMIA is module-agnostic. Its sensor identification is based on + * what now are those of the module. + */ + if (is_module || (!is_ccs && !is_smiapp)) { + manufacturer_id = is_ccs ? + sensor->minfo.mipi_manufacturer_id : + sensor->minfo.smia_manufacturer_id; + model_id = sensor->minfo.model_id; + revision_number = sensor->minfo.revision_number; + } else { + manufacturer_id = is_ccs ? + sensor->minfo.sensor_mipi_manufacturer_id : + sensor->minfo.sensor_smia_manufacturer_id; + model_id = sensor->minfo.sensor_model_id; + revision_number = sensor->minfo.sensor_revision_number; + } + + return snprintf(filename, filename_size, + "ccs/%s-%s-%0*x-%4.4x-%0*x.fw", + is_ccs ? "ccs" : is_smiapp ? "smiapp" : "smia", + is_module || (!is_ccs && !is_smiapp) ? + "module" : "sensor", + is_ccs ? 4 : 2, manufacturer_id, model_id, + !is_ccs && !is_module ? 2 : 4, revision_number); +} + static int ccs_probe(struct i2c_client *client) { + const struct ccs_device *ccsdev = device_get_match_data(&client->dev); struct ccs_sensor *sensor; const struct firmware *fw; char filename[40]; @@ -3389,11 +3420,8 @@ static int ccs_probe(struct i2c_client *client) goto out_power_off; } - rval = snprintf(filename, sizeof(filename), - "ccs/ccs-sensor-%4.4x-%4.4x-%4.4x.fw", - sensor->minfo.sensor_mipi_manufacturer_id, - sensor->minfo.sensor_model_id, - sensor->minfo.sensor_revision_number); + rval = ccs_firmware_name(client, sensor, filename, sizeof(filename), + false); if (rval >= sizeof(filename)) { rval = -ENOMEM; goto out_power_off; @@ -3406,21 +3434,21 @@ static int ccs_probe(struct i2c_client *client) release_firmware(fw); } - rval = snprintf(filename, sizeof(filename), - "ccs/ccs-module-%4.4x-%4.4x-%4.4x.fw", - sensor->minfo.mipi_manufacturer_id, - sensor->minfo.model_id, - sensor->minfo.revision_number); - if (rval >= sizeof(filename)) { - rval = -ENOMEM; - goto out_release_sdata; - } + if (!(ccsdev->flags & CCS_DEVICE_FLAG_IS_SMIA) || + sensor->minfo.smiapp_version) { + rval = ccs_firmware_name(client, sensor, filename, + sizeof(filename), true); + if (rval >= sizeof(filename)) { + rval = -ENOMEM; + goto out_release_sdata; + } - rval = request_firmware(&fw, filename, &client->dev); - if (!rval) { - ccs_data_parse(&sensor->mdata, fw->data, fw->size, &client->dev, - true); - release_firmware(fw); + rval = request_firmware(&fw, filename, &client->dev); + if (!rval) { + ccs_data_parse(&sensor->mdata, fw->data, fw->size, + &client->dev, true); + release_firmware(fw); + } } rval = ccs_read_all_limits(sensor); @@ -3437,25 +3465,6 @@ static int ccs_probe(struct i2c_client *client) if (rval < 0) goto out_free_ccs_limits; - /* - * Handle Sensor Module orientation on the board. - * - * The application of H-FLIP and V-FLIP on the sensor is modified by - * the sensor orientation on the board. - * - * For CCS_BOARD_SENSOR_ORIENT_180 the default behaviour is to set - * both H-FLIP and V-FLIP for normal operation which also implies - * that a set/unset operation for user space HFLIP and VFLIP v4l2 - * controls will need to be internally inverted. - * - * Rotation also changes the bayer pattern. - */ - if (sensor->hwcfg.module_board_orient == - CCS_MODULE_BOARD_ORIENT_180) - sensor->hvflip_inv_mask = - CCS_IMAGE_ORIENTATION_HORIZONTAL_MIRROR | - CCS_IMAGE_ORIENTATION_VERTICAL_FLIP; - rval = ccs_call_quirk(sensor, limits); if (rval) { dev_err(&client->dev, "limits quirks failed\n"); diff --git a/drivers/media/i2c/ccs/ccs.h b/drivers/media/i2c/ccs/ccs.h index 6beac375cc48..a94c796cea48 100644 --- a/drivers/media/i2c/ccs/ccs.h +++ b/drivers/media/i2c/ccs/ccs.h @@ -57,17 +57,6 @@ #define CCS_LIM_AT(sensor, limit, offset) \ ccs_get_limit(sensor, CCS_L_##limit, CCS_L_##limit##_OFFSET(offset)) -/* - * Sometimes due to board layout considerations the camera module can be - * mounted rotated. The typical rotation used is 180 degrees which can be - * corrected by giving a default H-FLIP and V-FLIP in the sensor readout. - * FIXME: rotation also changes the bayer pattern. - */ -enum ccs_module_board_orient { - CCS_MODULE_BOARD_ORIENT_0 = 0, - CCS_MODULE_BOARD_ORIENT_180, -}; - struct ccs_flash_strobe_parms { u8 mode; u32 strobe_width_high_us; @@ -90,8 +79,6 @@ struct ccs_hwconfig { u32 csi_signalling_mode; /* CCS_CSI_SIGNALLING_MODE_* */ u64 *op_sys_clock; - enum ccs_module_board_orient module_board_orient; - struct ccs_flash_strobe_parms *strobe_setup; }; @@ -243,7 +230,6 @@ struct ccs_sensor { u8 scale_m; u8 scaling_mode; - u8 hvflip_inv_mask; /* H/VFLIP inversion due to sensor orientation */ u8 frame_skip; u16 embedded_start; /* embedded data start line */ u16 embedded_end; diff --git a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c index e422ac7609b5..7daefab35cf0 100644 --- a/drivers/media/i2c/hi556.c +++ b/drivers/media/i2c/hi556.c @@ -63,6 +63,14 @@ #define HI556_REG_ISP_TPG_EN 0x01 #define HI556_REG_TEST_PATTERN 0x0201 +/* HI556 native and active pixel array size. */ +#define HI556_NATIVE_WIDTH 2592U +#define HI556_NATIVE_HEIGHT 1944U +#define HI556_PIXEL_ARRAY_LEFT 0U +#define HI556_PIXEL_ARRAY_TOP 0U +#define HI556_PIXEL_ARRAY_WIDTH 2592U +#define HI556_PIXEL_ARRAY_HEIGHT 1944U + enum { HI556_LINK_FREQ_437MHZ_INDEX, }; @@ -88,6 +96,9 @@ struct hi556_mode { /* Frame height in pixels */ u32 height; + /* Analog crop rectangle. */ + struct v4l2_rect crop; + /* Horizontal timining size */ u32 llp; @@ -378,6 +389,49 @@ static const struct hi556_reg mode_2592x1944_regs[] = { {0x0958, 0xbb80}, }; +static const struct hi556_reg mode_2592x1444_regs[] = { + {0x0a00, 0x0000}, + {0x0b0a, 0x8252}, + {0x0f30, 0xe545}, + {0x0f32, 0x7067}, + {0x004a, 0x0100}, + {0x004c, 0x0000}, + {0x000c, 0x0022}, + {0x0008, 0x0b00}, + {0x005a, 0x0202}, + {0x0012, 0x000e}, + {0x0018, 0x0a33}, + {0x0022, 0x0008}, + {0x0028, 0x0017}, + {0x0024, 0x0122}, + {0x002a, 0x0127}, + {0x0026, 0x012a}, + {0x002c, 0x06cf}, + {0x002e, 0x1111}, + {0x0030, 0x1111}, + {0x0032, 0x1111}, + {0x0006, 0x0821}, + {0x0a22, 0x0000}, + {0x0a12, 0x0a20}, + {0x0a14, 0x05a4}, + {0x003e, 0x0000}, + {0x0074, 0x081f}, + {0x0070, 0x040f}, + {0x0804, 0x0300}, + {0x0806, 0x0100}, + {0x0a04, 0x014a}, + {0x090c, 0x0fdc}, + {0x090e, 0x002d}, + {0x0902, 0x4319}, + {0x0914, 0xc10a}, + {0x0916, 0x071f}, + {0x0918, 0x0408}, + {0x091a, 0x0c0d}, + {0x091c, 0x0f09}, + {0x091e, 0x0a00}, + {0x0958, 0xbb80}, +}; + static const struct hi556_reg mode_1296x972_regs[] = { {0x0a00, 0x0000}, {0x0b0a, 0x8259}, @@ -450,8 +504,14 @@ static const struct hi556_link_freq_config link_freq_configs[] = { static const struct hi556_mode supported_modes[] = { { - .width = 2592, - .height = 1944, + .width = HI556_PIXEL_ARRAY_WIDTH, + .height = HI556_PIXEL_ARRAY_HEIGHT, + .crop = { + .left = HI556_PIXEL_ARRAY_LEFT, + .top = HI556_PIXEL_ARRAY_TOP, + .width = HI556_PIXEL_ARRAY_WIDTH, + .height = HI556_PIXEL_ARRAY_HEIGHT + }, .fll_def = HI556_FLL_30FPS, .fll_min = HI556_FLL_30FPS_MIN, .llp = 0x0b00, @@ -462,8 +522,32 @@ static const struct hi556_mode supported_modes[] = { .link_freq_index = HI556_LINK_FREQ_437MHZ_INDEX, }, { + .width = HI556_PIXEL_ARRAY_WIDTH, + .height = 1444, + .crop = { + .left = HI556_PIXEL_ARRAY_LEFT, + .top = 250, + .width = HI556_PIXEL_ARRAY_WIDTH, + .height = 1444 + }, + .fll_def = 0x821, + .fll_min = 0x821, + .llp = 0x0b00, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_2592x1444_regs), + .regs = mode_2592x1444_regs, + }, + .link_freq_index = HI556_LINK_FREQ_437MHZ_INDEX, + }, + { .width = 1296, .height = 972, + .crop = { + .left = HI556_PIXEL_ARRAY_LEFT, + .top = HI556_PIXEL_ARRAY_TOP, + .width = HI556_PIXEL_ARRAY_WIDTH, + .height = HI556_PIXEL_ARRAY_HEIGHT + }, .fll_def = HI556_FLL_30FPS, .fll_min = HI556_FLL_30FPS_MIN, .llp = 0x0b00, @@ -785,6 +869,58 @@ static int hi556_identify_module(struct hi556 *hi556) return 0; } +static const struct v4l2_rect * +__hi556_get_pad_crop(struct hi556 *hi556, + struct v4l2_subdev_state *sd_state, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(&hi556->sd, sd_state, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return &hi556->cur_mode->crop; + } + + return NULL; +} + +static int hi556_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + switch (sel->target) { + case V4L2_SEL_TGT_CROP: { + struct hi556 *hi556 = to_hi556(sd); + + mutex_lock(&hi556->mutex); + sel->r = *__hi556_get_pad_crop(hi556, sd_state, sel->pad, + sel->which); + mutex_unlock(&hi556->mutex); + + return 0; + } + + case V4L2_SEL_TGT_NATIVE_SIZE: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = HI556_NATIVE_WIDTH; + sel->r.height = HI556_NATIVE_HEIGHT; + + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = HI556_PIXEL_ARRAY_TOP; + sel->r.left = HI556_PIXEL_ARRAY_LEFT; + sel->r.width = HI556_PIXEL_ARRAY_WIDTH; + sel->r.height = HI556_PIXEL_ARRAY_HEIGHT; + + return 0; + } + + return -EINVAL; +} + static int hi556_start_streaming(struct hi556 *hi556) { struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); @@ -1000,10 +1136,19 @@ static int hi556_enum_frame_size(struct v4l2_subdev *sd, static int hi556_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct hi556 *hi556 = to_hi556(sd); + struct v4l2_rect *try_crop; mutex_lock(&hi556->mutex); hi556_assign_pad_format(&supported_modes[0], v4l2_subdev_get_try_format(sd, fh->state, 0)); + + /* Initialize try_crop rectangle. */ + try_crop = v4l2_subdev_get_try_crop(sd, fh->state, 0); + try_crop->top = HI556_PIXEL_ARRAY_TOP; + try_crop->left = HI556_PIXEL_ARRAY_LEFT; + try_crop->width = HI556_PIXEL_ARRAY_WIDTH; + try_crop->height = HI556_PIXEL_ARRAY_HEIGHT; + mutex_unlock(&hi556->mutex); return 0; @@ -1016,6 +1161,7 @@ static const struct v4l2_subdev_video_ops hi556_video_ops = { static const struct v4l2_subdev_pad_ops hi556_pad_ops = { .set_fmt = hi556_set_format, .get_fmt = hi556_get_format, + .get_selection = hi556_get_selection, .enum_mbus_code = hi556_enum_mbus_code, .enum_frame_size = hi556_enum_frame_size, }; diff --git a/drivers/media/i2c/hi846.c b/drivers/media/i2c/hi846.c index 7c61873b7198..306dc35e925f 100644 --- a/drivers/media/i2c/hi846.c +++ b/drivers/media/i2c/hi846.c @@ -1472,21 +1472,26 @@ static int hi846_init_controls(struct hi846 *hi846) if (ctrl_hdlr->error) { dev_err(&client->dev, "v4l ctrl handler error: %d\n", ctrl_hdlr->error); - return ctrl_hdlr->error; + ret = ctrl_hdlr->error; + goto error; } ret = v4l2_fwnode_device_parse(&client->dev, &props); if (ret) - return ret; + goto error; ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &hi846_ctrl_ops, &props); if (ret) - return ret; + goto error; hi846->sd.ctrl_handler = ctrl_hdlr; return 0; + +error: + v4l2_ctrl_handler_free(ctrl_hdlr); + return ret; } static int hi846_set_video_mode(struct hi846 *hi846, int fps) diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c index eab5fc1ee2f7..85d73b186111 100644 --- a/drivers/media/i2c/imx258.c +++ b/drivers/media/i2c/imx258.c @@ -9,6 +9,7 @@ #include <linux/pm_runtime.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> +#include <media/v4l2-fwnode.h> #include <asm/unaligned.h> #define IMX258_REG_VALUE_08BIT 1 @@ -1148,7 +1149,9 @@ static const struct v4l2_subdev_internal_ops imx258_internal_ops = { static int imx258_init_controls(struct imx258 *imx258) { struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd); + struct v4l2_fwnode_device_properties props; struct v4l2_ctrl_handler *ctrl_hdlr; + struct v4l2_ctrl *vflip, *hflip; s64 vblank_def; s64 vblank_min; s64 pixel_rate_min; @@ -1156,7 +1159,7 @@ static int imx258_init_controls(struct imx258 *imx258) int ret; ctrl_hdlr = &imx258->ctrl_handler; - ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); + ret = v4l2_ctrl_handler_init(ctrl_hdlr, 13); if (ret) return ret; @@ -1172,6 +1175,17 @@ static int imx258_init_controls(struct imx258 *imx258) if (imx258->link_freq) imx258->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + /* The driver only supports one bayer order and flips by default. */ + hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, + V4L2_CID_HFLIP, 1, 1, 1, 1); + if (hflip) + hflip->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops, + V4L2_CID_VFLIP, 1, 1, 1, 1); + if (vflip) + vflip->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]); /* By default, PIXEL_RATE is read only */ @@ -1232,6 +1246,15 @@ static int imx258_init_controls(struct imx258 *imx258) goto error; } + ret = v4l2_fwnode_device_parse(&client->dev, &props); + if (ret) + goto error; + + ret = v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &imx258_ctrl_ops, + &props); + if (ret) + goto error; + imx258->sd.ctrl_handler = ctrl_hdlr; return 0; @@ -1276,14 +1299,6 @@ static int imx258_probe(struct i2c_client *client) return -EINVAL; } - /* - * Check that the device is mounted upside down. The driver only - * supports a single pixel order right now. - */ - ret = device_property_read_u32(&client->dev, "rotation", &val); - if (ret || val != 180) - return -EINVAL; - /* Initialize subdev */ v4l2_i2c_subdev_init(&imx258->sd, client, &imx258_subdev_ops); diff --git a/drivers/media/i2c/imx290.c b/drivers/media/i2c/imx290.c index 48ae2e0adf9e..5ea25b7acc55 100644 --- a/drivers/media/i2c/imx290.c +++ b/drivers/media/i2c/imx290.c @@ -13,12 +13,17 @@ #include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/module.h> +#include <linux/of_device.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> + +#include <asm/unaligned.h> + #include <media/media-entity.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> @@ -44,7 +49,9 @@ #define IMX290_BLKLEVEL IMX290_REG_16BIT(0x300a) #define IMX290_GAIN IMX290_REG_8BIT(0x3014) #define IMX290_VMAX IMX290_REG_24BIT(0x3018) +#define IMX290_VMAX_MAX 0x3ffff #define IMX290_HMAX IMX290_REG_16BIT(0x301c) +#define IMX290_HMAX_MAX 0xffff #define IMX290_SHS1 IMX290_REG_24BIT(0x3020) #define IMX290_WINWV_OB IMX290_REG_8BIT(0x303a) #define IMX290_WINPV IMX290_REG_16BIT(0x303c) @@ -98,13 +105,16 @@ #define IMX290_TCLKPREPARE IMX290_REG_16BIT(0x3452) #define IMX290_TLPX IMX290_REG_16BIT(0x3454) #define IMX290_X_OUT_SIZE IMX290_REG_16BIT(0x3472) +#define IMX290_INCKSEL7 IMX290_REG_8BIT(0x3480) #define IMX290_PGCTRL_REGEN BIT(0) #define IMX290_PGCTRL_THRU BIT(1) #define IMX290_PGCTRL_MODE(n) ((n) << 4) -#define IMX290_VMAX_DEFAULT 1125 +/* Number of lines by which exposure must be less than VMAX */ +#define IMX290_EXPOSURE_OFFSET 2 +#define IMX290_PIXEL_RATE 148500000 /* * The IMX290 pixel array is organized as follows: @@ -157,26 +167,81 @@ #define IMX290_NUM_SUPPLIES 3 +enum imx290_colour_variant { + IMX290_VARIANT_COLOUR, + IMX290_VARIANT_MONO, + IMX290_VARIANT_MAX +}; + +enum imx290_model { + IMX290_MODEL_IMX290LQR, + IMX290_MODEL_IMX290LLR, + IMX290_MODEL_IMX327LQR, +}; + +struct imx290_model_info { + enum imx290_colour_variant colour_variant; + const struct imx290_regval *init_regs; + size_t init_regs_num; + const char *name; +}; + +enum imx290_clk_freq { + IMX290_CLK_37_125, + IMX290_CLK_74_25, + IMX290_NUM_CLK +}; + struct imx290_regval { u32 reg; u32 val; }; +/* + * Clock configuration for registers INCKSEL1 to INCKSEL6. + */ +struct imx290_clk_cfg { + u8 incksel1; + u8 incksel2; + u8 incksel3; + u8 incksel4; + u8 incksel5; + u8 incksel6; +}; + struct imx290_mode { u32 width; u32 height; - u32 hmax; + u32 hmax_min; + u32 vmax_min; u8 link_freq_index; + u8 ctrl_07; const struct imx290_regval *data; u32 data_size; + + const struct imx290_clk_cfg *clk_cfg; +}; + +struct imx290_csi_cfg { + u16 repetition; + u16 tclkpost; + u16 thszero; + u16 thsprepare; + u16 tclktrail; + u16 thstrail; + u16 tclkzero; + u16 tclkprepare; + u16 tlpx; }; struct imx290 { struct device *dev; struct clk *xclk; struct regmap *regmap; + enum imx290_clk_freq xclk_idx; u8 nlanes; + const struct imx290_model_info *model; struct v4l2_subdev sd; struct media_pad pad; @@ -188,9 +253,13 @@ struct imx290 { struct v4l2_ctrl_handler ctrls; struct v4l2_ctrl *link_freq; - struct v4l2_ctrl *pixel_rate; struct v4l2_ctrl *hblank; struct v4l2_ctrl *vblank; + struct v4l2_ctrl *exposure; + struct { + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + }; }; static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd) @@ -203,9 +272,6 @@ static inline struct imx290 *to_imx290(struct v4l2_subdev *_sd) */ static const struct imx290_regval imx290_global_init_settings[] = { - { IMX290_CTRL_07, IMX290_WINMODE_1080P }, - { IMX290_VMAX, IMX290_VMAX_DEFAULT }, - { IMX290_EXTCK_FREQ, 0x2520 }, { IMX290_WINWV_OB, 12 }, { IMX290_WINPH, 0 }, { IMX290_WINPV, 0 }, @@ -213,10 +279,14 @@ static const struct imx290_regval imx290_global_init_settings[] = { { IMX290_WINWV, 1097 }, { IMX290_XSOUTSEL, IMX290_XSOUTSEL_XVSOUTSEL_VSYNC | IMX290_XSOUTSEL_XHSOUTSEL_HSYNC }, - { IMX290_REG_8BIT(0x300f), 0x00 }, - { IMX290_REG_8BIT(0x3010), 0x21 }, + { IMX290_REG_8BIT(0x3011), 0x02 }, { IMX290_REG_8BIT(0x3012), 0x64 }, { IMX290_REG_8BIT(0x3013), 0x00 }, +}; + +static const struct imx290_regval imx290_global_init_settings_290[] = { + { IMX290_REG_8BIT(0x300f), 0x00 }, + { IMX290_REG_8BIT(0x3010), 0x21 }, { IMX290_REG_8BIT(0x3016), 0x09 }, { IMX290_REG_8BIT(0x3070), 0x02 }, { IMX290_REG_8BIT(0x3071), 0x11 }, @@ -255,57 +325,40 @@ static const struct imx290_regval imx290_global_init_settings[] = { { IMX290_REG_8BIT(0x33b0), 0x50 }, { IMX290_REG_8BIT(0x33b2), 0x1a }, { IMX290_REG_8BIT(0x33b3), 0x04 }, - { IMX290_REG_8BIT(0x3480), 0x49 }, +}; + +#define IMX290_NUM_CLK_REGS 2 +static const struct imx290_regval xclk_regs[][IMX290_NUM_CLK_REGS] = { + [IMX290_CLK_37_125] = { + { IMX290_EXTCK_FREQ, (37125 * 256) / 1000 }, + { IMX290_INCKSEL7, 0x49 }, + }, + [IMX290_CLK_74_25] = { + { IMX290_EXTCK_FREQ, (74250 * 256) / 1000 }, + { IMX290_INCKSEL7, 0x92 }, + }, +}; + +static const struct imx290_regval imx290_global_init_settings_327[] = { + { IMX290_REG_8BIT(0x309e), 0x4A }, + { IMX290_REG_8BIT(0x309f), 0x4A }, + { IMX290_REG_8BIT(0x313b), 0x61 }, }; static const struct imx290_regval imx290_1080p_settings[] = { /* mode settings */ - { IMX290_CTRL_07, IMX290_WINMODE_1080P }, { IMX290_WINWV_OB, 12 }, { IMX290_OPB_SIZE_V, 10 }, { IMX290_X_OUT_SIZE, 1920 }, { IMX290_Y_OUT_SIZE, 1080 }, - { IMX290_INCKSEL1, 0x18 }, - { IMX290_INCKSEL2, 0x03 }, - { IMX290_INCKSEL3, 0x20 }, - { IMX290_INCKSEL4, 0x01 }, - { IMX290_INCKSEL5, 0x1a }, - { IMX290_INCKSEL6, 0x1a }, - /* data rate settings */ - { IMX290_REPETITION, 0x10 }, - { IMX290_TCLKPOST, 87 }, - { IMX290_THSZERO, 55 }, - { IMX290_THSPREPARE, 31 }, - { IMX290_TCLKTRAIL, 31 }, - { IMX290_THSTRAIL, 31 }, - { IMX290_TCLKZERO, 119 }, - { IMX290_TCLKPREPARE, 31 }, - { IMX290_TLPX, 23 }, }; static const struct imx290_regval imx290_720p_settings[] = { /* mode settings */ - { IMX290_CTRL_07, IMX290_WINMODE_720P }, { IMX290_WINWV_OB, 6 }, { IMX290_OPB_SIZE_V, 4 }, { IMX290_X_OUT_SIZE, 1280 }, { IMX290_Y_OUT_SIZE, 720 }, - { IMX290_INCKSEL1, 0x20 }, - { IMX290_INCKSEL2, 0x00 }, - { IMX290_INCKSEL3, 0x20 }, - { IMX290_INCKSEL4, 0x01 }, - { IMX290_INCKSEL5, 0x1a }, - { IMX290_INCKSEL6, 0x1a }, - /* data rate settings */ - { IMX290_REPETITION, 0x10 }, - { IMX290_TCLKPOST, 79 }, - { IMX290_THSZERO, 47 }, - { IMX290_THSPREPARE, 23 }, - { IMX290_TCLKTRAIL, 23 }, - { IMX290_THSTRAIL, 23 }, - { IMX290_TCLKZERO, 87 }, - { IMX290_TCLKPREPARE, 23 }, - { IMX290_TLPX, 23 }, }; static const struct imx290_regval imx290_10bit_settings[] = { @@ -326,6 +379,58 @@ static const struct imx290_regval imx290_12bit_settings[] = { { IMX290_CSI_DT_FMT, IMX290_CSI_DT_FMT_RAW12 }, }; +static const struct imx290_csi_cfg imx290_csi_222_75mhz = { + /* 222.75MHz or 445.5Mbit/s per lane */ + .repetition = 0x10, + .tclkpost = 87, + .thszero = 55, + .thsprepare = 31, + .tclktrail = 31, + .thstrail = 31, + .tclkzero = 119, + .tclkprepare = 31, + .tlpx = 23, +}; + +static const struct imx290_csi_cfg imx290_csi_445_5mhz = { + /* 445.5MHz or 891Mbit/s per lane */ + .repetition = 0x00, + .tclkpost = 119, + .thszero = 103, + .thsprepare = 71, + .tclktrail = 55, + .thstrail = 63, + .tclkzero = 255, + .tclkprepare = 63, + .tlpx = 55, +}; + +static const struct imx290_csi_cfg imx290_csi_148_5mhz = { + /* 148.5MHz or 297Mbit/s per lane */ + .repetition = 0x10, + .tclkpost = 79, + .thszero = 47, + .thsprepare = 23, + .tclktrail = 23, + .thstrail = 23, + .tclkzero = 87, + .tclkprepare = 23, + .tlpx = 23, +}; + +static const struct imx290_csi_cfg imx290_csi_297mhz = { + /* 297MHz or 594Mbit/s per lane */ + .repetition = 0x00, + .tclkpost = 103, + .thszero = 87, + .thsprepare = 47, + .tclktrail = 39, + .thstrail = 47, + .tclkzero = 191, + .tclkprepare = 47, + .tlpx = 39, +}; + /* supported link frequencies */ #define FREQ_INDEX_1080P 0 #define FREQ_INDEX_720P 1 @@ -333,6 +438,7 @@ static const s64 imx290_link_freq_2lanes[] = { [FREQ_INDEX_1080P] = 445500000, [FREQ_INDEX_720P] = 297000000, }; + static const s64 imx290_link_freq_4lanes[] = { [FREQ_INDEX_1080P] = 222750000, [FREQ_INDEX_720P] = 148500000, @@ -358,23 +464,71 @@ static inline int imx290_link_freqs_num(const struct imx290 *imx290) return ARRAY_SIZE(imx290_link_freq_4lanes); } +static const struct imx290_clk_cfg imx290_1080p_clock_config[] = { + [IMX290_CLK_37_125] = { + /* 37.125MHz clock config */ + .incksel1 = 0x18, + .incksel2 = 0x03, + .incksel3 = 0x20, + .incksel4 = 0x01, + .incksel5 = 0x1a, + .incksel6 = 0x1a, + }, + [IMX290_CLK_74_25] = { + /* 74.25MHz clock config */ + .incksel1 = 0x0c, + .incksel2 = 0x03, + .incksel3 = 0x10, + .incksel4 = 0x01, + .incksel5 = 0x1b, + .incksel6 = 0x1b, + }, +}; + +static const struct imx290_clk_cfg imx290_720p_clock_config[] = { + [IMX290_CLK_37_125] = { + /* 37.125MHz clock config */ + .incksel1 = 0x20, + .incksel2 = 0x00, + .incksel3 = 0x20, + .incksel4 = 0x01, + .incksel5 = 0x1a, + .incksel6 = 0x1a, + }, + [IMX290_CLK_74_25] = { + /* 74.25MHz clock config */ + .incksel1 = 0x10, + .incksel2 = 0x00, + .incksel3 = 0x10, + .incksel4 = 0x01, + .incksel5 = 0x1b, + .incksel6 = 0x1b, + }, +}; + /* Mode configs */ static const struct imx290_mode imx290_modes_2lanes[] = { { .width = 1920, .height = 1080, - .hmax = 4400, + .hmax_min = 2200, + .vmax_min = 1125, .link_freq_index = FREQ_INDEX_1080P, + .ctrl_07 = IMX290_WINMODE_1080P, .data = imx290_1080p_settings, .data_size = ARRAY_SIZE(imx290_1080p_settings), + .clk_cfg = imx290_1080p_clock_config, }, { .width = 1280, .height = 720, - .hmax = 6600, + .hmax_min = 3300, + .vmax_min = 750, .link_freq_index = FREQ_INDEX_720P, + .ctrl_07 = IMX290_WINMODE_720P, .data = imx290_720p_settings, .data_size = ARRAY_SIZE(imx290_720p_settings), + .clk_cfg = imx290_720p_clock_config, }, }; @@ -382,18 +536,24 @@ static const struct imx290_mode imx290_modes_4lanes[] = { { .width = 1920, .height = 1080, - .hmax = 2200, + .hmax_min = 2200, + .vmax_min = 1125, .link_freq_index = FREQ_INDEX_1080P, + .ctrl_07 = IMX290_WINMODE_1080P, .data = imx290_1080p_settings, .data_size = ARRAY_SIZE(imx290_1080p_settings), + .clk_cfg = imx290_1080p_clock_config, }, { .width = 1280, .height = 720, - .hmax = 3300, + .hmax_min = 3300, + .vmax_min = 750, .link_freq_index = FREQ_INDEX_720P, + .ctrl_07 = IMX290_WINMODE_720P, .data = imx290_720p_settings, .data_size = ARRAY_SIZE(imx290_720p_settings), + .clk_cfg = imx290_720p_clock_config, }, }; @@ -414,7 +574,7 @@ static inline int imx290_modes_num(const struct imx290 *imx290) } struct imx290_format_info { - u32 code; + u32 code[IMX290_VARIANT_MAX]; u8 bpp; const struct imx290_regval *regs; unsigned int num_regs; @@ -422,26 +582,33 @@ struct imx290_format_info { static const struct imx290_format_info imx290_formats[] = { { - .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .code = { + [IMX290_VARIANT_COLOUR] = MEDIA_BUS_FMT_SRGGB10_1X10, + [IMX290_VARIANT_MONO] = MEDIA_BUS_FMT_Y10_1X10 + }, .bpp = 10, .regs = imx290_10bit_settings, .num_regs = ARRAY_SIZE(imx290_10bit_settings), }, { - .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .code = { + [IMX290_VARIANT_COLOUR] = MEDIA_BUS_FMT_SRGGB12_1X12, + [IMX290_VARIANT_MONO] = MEDIA_BUS_FMT_Y12_1X12 + }, .bpp = 12, .regs = imx290_12bit_settings, .num_regs = ARRAY_SIZE(imx290_12bit_settings), } }; -static const struct imx290_format_info *imx290_format_info(u32 code) +static const struct imx290_format_info * +imx290_format_info(const struct imx290 *imx290, u32 code) { unsigned int i; for (i = 0; i < ARRAY_SIZE(imx290_formats); ++i) { const struct imx290_format_info *info = &imx290_formats[i]; - if (info->code == code) + if (info->code[imx290->model->colour_variant] == code) return info; } @@ -461,28 +628,30 @@ static int __always_unused imx290_read(struct imx290 *imx290, u32 addr, u32 *val data, (addr >> IMX290_REG_SIZE_SHIFT) & 3); if (ret < 0) { dev_err(imx290->dev, "%u-bit read from 0x%04x failed: %d\n", - ((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8, + ((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8, addr & IMX290_REG_ADDR_MASK, ret); return ret; } - *value = (data[2] << 16) | (data[1] << 8) | data[0]; + *value = get_unaligned_le24(data); return 0; } static int imx290_write(struct imx290 *imx290, u32 addr, u32 value, int *err) { - u8 data[3] = { value & 0xff, (value >> 8) & 0xff, value >> 16 }; + u8 data[3]; int ret; if (err && *err) return *err; + put_unaligned_le24(value, data); + ret = regmap_raw_write(imx290->regmap, addr & IMX290_REG_ADDR_MASK, data, (addr >> IMX290_REG_SIZE_SHIFT) & 3); if (ret < 0) { dev_err(imx290->dev, "%u-bit write to 0x%04x failed: %d\n", - ((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8, + ((addr >> IMX290_REG_SIZE_SHIFT) & 3) * 8, addr & IMX290_REG_ADDR_MASK, ret); if (err) *err = ret; @@ -510,24 +679,33 @@ static int imx290_set_register_array(struct imx290 *imx290, return 0; } +static int imx290_set_clock(struct imx290 *imx290) +{ + const struct imx290_mode *mode = imx290->current_mode; + enum imx290_clk_freq clk_idx = imx290->xclk_idx; + const struct imx290_clk_cfg *clk_cfg = &mode->clk_cfg[clk_idx]; + int ret; + + ret = imx290_set_register_array(imx290, xclk_regs[clk_idx], + IMX290_NUM_CLK_REGS); + + imx290_write(imx290, IMX290_INCKSEL1, clk_cfg->incksel1, &ret); + imx290_write(imx290, IMX290_INCKSEL2, clk_cfg->incksel2, &ret); + imx290_write(imx290, IMX290_INCKSEL3, clk_cfg->incksel3, &ret); + imx290_write(imx290, IMX290_INCKSEL4, clk_cfg->incksel4, &ret); + imx290_write(imx290, IMX290_INCKSEL5, clk_cfg->incksel5, &ret); + imx290_write(imx290, IMX290_INCKSEL6, clk_cfg->incksel6, &ret); + + return ret; +} + static int imx290_set_data_lanes(struct imx290 *imx290) { int ret = 0; - u32 frsel; - - switch (imx290->nlanes) { - case 2: - default: - frsel = 0x02; - break; - case 4: - frsel = 0x01; - break; - } imx290_write(imx290, IMX290_PHY_LANE_NUM, imx290->nlanes - 1, &ret); imx290_write(imx290, IMX290_CSI_LANE_MODE, imx290->nlanes - 1, &ret); - imx290_write(imx290, IMX290_FR_FDG_SEL, frsel, &ret); + imx290_write(imx290, IMX290_FR_FDG_SEL, 0x01, &ret); return ret; } @@ -536,19 +714,55 @@ static int imx290_set_black_level(struct imx290 *imx290, const struct v4l2_mbus_framefmt *format, unsigned int black_level, int *err) { - unsigned int bpp = imx290_format_info(format->code)->bpp; + unsigned int bpp = imx290_format_info(imx290, format->code)->bpp; return imx290_write(imx290, IMX290_BLKLEVEL, black_level >> (16 - bpp), err); } +static int imx290_set_csi_config(struct imx290 *imx290) +{ + const s64 *link_freqs = imx290_link_freqs_ptr(imx290); + const struct imx290_csi_cfg *csi_cfg; + int ret = 0; + + switch (link_freqs[imx290->current_mode->link_freq_index]) { + case 445500000: + csi_cfg = &imx290_csi_445_5mhz; + break; + case 297000000: + csi_cfg = &imx290_csi_297mhz; + break; + case 222750000: + csi_cfg = &imx290_csi_222_75mhz; + break; + case 148500000: + csi_cfg = &imx290_csi_148_5mhz; + break; + default: + return -EINVAL; + } + + imx290_write(imx290, IMX290_REPETITION, csi_cfg->repetition, &ret); + imx290_write(imx290, IMX290_TCLKPOST, csi_cfg->tclkpost, &ret); + imx290_write(imx290, IMX290_THSZERO, csi_cfg->thszero, &ret); + imx290_write(imx290, IMX290_THSPREPARE, csi_cfg->thsprepare, &ret); + imx290_write(imx290, IMX290_TCLKTRAIL, csi_cfg->tclktrail, &ret); + imx290_write(imx290, IMX290_THSTRAIL, csi_cfg->thstrail, &ret); + imx290_write(imx290, IMX290_TCLKZERO, csi_cfg->tclkzero, &ret); + imx290_write(imx290, IMX290_TCLKPREPARE, csi_cfg->tclkprepare, &ret); + imx290_write(imx290, IMX290_TLPX, csi_cfg->tlpx, &ret); + + return ret; +} + static int imx290_setup_format(struct imx290 *imx290, const struct v4l2_mbus_framefmt *format) { const struct imx290_format_info *info; int ret; - info = imx290_format_info(format->code); + info = imx290_format_info(imx290, format->code); ret = imx290_set_register_array(imx290, info->regs, info->num_regs); if (ret < 0) { @@ -563,6 +777,16 @@ static int imx290_setup_format(struct imx290 *imx290, /* ---------------------------------------------------------------------------- * Controls */ +static void imx290_exposure_update(struct imx290 *imx290, + const struct imx290_mode *mode) +{ + unsigned int exposure_max; + + exposure_max = imx290->vblank->val + mode->height - + IMX290_EXPOSURE_OFFSET; + __v4l2_ctrl_modify_range(imx290->exposure, 1, exposure_max, 1, + exposure_max); +} static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) { @@ -570,7 +794,7 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) struct imx290, ctrls); const struct v4l2_mbus_framefmt *format; struct v4l2_subdev_state *state; - int ret = 0; + int ret = 0, vmax; /* * Return immediately for controls that don't need to be applied to the @@ -579,6 +803,11 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY) return 0; + if (ctrl->id == V4L2_CID_VBLANK) { + /* Changing vblank changes the allowed range for exposure. */ + imx290_exposure_update(imx290, imx290->current_mode); + } + /* V4L2 controls values will be applied only when power is already up */ if (!pm_runtime_get_if_in_use(imx290->dev)) return 0; @@ -591,9 +820,23 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) ret = imx290_write(imx290, IMX290_GAIN, ctrl->val, NULL); break; + case V4L2_CID_VBLANK: + ret = imx290_write(imx290, IMX290_VMAX, + ctrl->val + imx290->current_mode->height, + NULL); + /* + * Due to the way that exposure is programmed in this sensor in + * relation to VMAX, we have to reprogramme it whenever VMAX is + * changed. + * Update ctrl so that the V4L2_CID_EXPOSURE case can refer to + * it. + */ + ctrl = imx290->exposure; + fallthrough; case V4L2_CID_EXPOSURE: + vmax = imx290->vblank->val + imx290->current_mode->height; ret = imx290_write(imx290, IMX290_SHS1, - IMX290_VMAX_DEFAULT - ctrl->val - 1, NULL); + vmax - ctrl->val - 1, NULL); break; case V4L2_CID_TEST_PATTERN: @@ -612,6 +855,26 @@ static int imx290_set_ctrl(struct v4l2_ctrl *ctrl) } break; + case V4L2_CID_HBLANK: + ret = imx290_write(imx290, IMX290_HMAX, + ctrl->val + imx290->current_mode->width, + NULL); + break; + + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + { + u32 reg; + + reg = imx290->current_mode->ctrl_07; + if (imx290->hflip->val) + reg |= IMX290_HREVERSE; + if (imx290->vflip->val) + reg |= IMX290_VREVERSE; + ret = imx290_write(imx290, IMX290_CTRL_07, reg, NULL); + break; + } + default: ret = -EINVAL; break; @@ -642,20 +905,17 @@ static void imx290_ctrl_update(struct imx290 *imx290, const struct v4l2_mbus_framefmt *format, const struct imx290_mode *mode) { - unsigned int hblank = mode->hmax - mode->width; - unsigned int vblank = IMX290_VMAX_DEFAULT - mode->height; - s64 link_freq = imx290_link_freqs_ptr(imx290)[mode->link_freq_index]; - u64 pixel_rate; - - /* pixel rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ - pixel_rate = link_freq * 2 * imx290->nlanes; - do_div(pixel_rate, imx290_format_info(format->code)->bpp); + unsigned int hblank_min = mode->hmax_min - mode->width; + unsigned int hblank_max = IMX290_HMAX_MAX - mode->width; + unsigned int vblank_min = mode->vmax_min - mode->height; + unsigned int vblank_max = IMX290_VMAX_MAX - mode->height; __v4l2_ctrl_s_ctrl(imx290->link_freq, mode->link_freq_index); - __v4l2_ctrl_s_ctrl_int64(imx290->pixel_rate, pixel_rate); - __v4l2_ctrl_modify_range(imx290->hblank, hblank, hblank, 1, hblank); - __v4l2_ctrl_modify_range(imx290->vblank, vblank, vblank, 1, vblank); + __v4l2_ctrl_modify_range(imx290->hblank, hblank_min, hblank_max, 1, + hblank_min); + __v4l2_ctrl_modify_range(imx290->vblank, vblank_min, vblank_max, 1, + vblank_min); } static int imx290_ctrl_init(struct imx290 *imx290) @@ -667,7 +927,7 @@ static int imx290_ctrl_init(struct imx290 *imx290) if (ret < 0) return ret; - v4l2_ctrl_handler_init(&imx290->ctrls, 9); + v4l2_ctrl_handler_init(&imx290->ctrls, 11); /* * The sensor has an analog gain and a digital gain, both controlled @@ -685,9 +945,13 @@ static int imx290_ctrl_init(struct imx290 *imx290) v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, 0, 100, 1, 0); - v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, - V4L2_CID_EXPOSURE, 1, IMX290_VMAX_DEFAULT - 2, 1, - IMX290_VMAX_DEFAULT - 2); + /* + * Correct range will be determined through imx290_ctrl_update setting + * V4L2_CID_VBLANK. + */ + imx290->exposure = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, + V4L2_CID_EXPOSURE, 1, 65535, 1, + 65535); /* * Set the link frequency, pixel rate, horizontal blanking and vertical @@ -702,24 +966,29 @@ static int imx290_ctrl_init(struct imx290 *imx290) if (imx290->link_freq) imx290->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; - imx290->pixel_rate = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, - V4L2_CID_PIXEL_RATE, - 1, INT_MAX, 1, 1); + v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, V4L2_CID_PIXEL_RATE, + IMX290_PIXEL_RATE, IMX290_PIXEL_RATE, 1, + IMX290_PIXEL_RATE); v4l2_ctrl_new_std_menu_items(&imx290->ctrls, &imx290_ctrl_ops, V4L2_CID_TEST_PATTERN, ARRAY_SIZE(imx290_test_pattern_menu) - 1, 0, 0, imx290_test_pattern_menu); + /* + * Actual range will be set from imx290_ctrl_update later in the probe. + */ imx290->hblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, V4L2_CID_HBLANK, 1, 1, 1, 1); - if (imx290->hblank) - imx290->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; imx290->vblank = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, V4L2_CID_VBLANK, 1, 1, 1, 1); - if (imx290->vblank) - imx290->vblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; + + imx290->hflip = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + imx290->vflip = v4l2_ctrl_new_std(&imx290->ctrls, &imx290_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_cluster(2, &imx290->hflip); v4l2_ctrl_new_fwnode_properties(&imx290->ctrls, &imx290_ctrl_ops, &props); @@ -748,17 +1017,37 @@ static int imx290_start_streaming(struct imx290 *imx290, /* Set init register settings */ ret = imx290_set_register_array(imx290, imx290_global_init_settings, - ARRAY_SIZE( - imx290_global_init_settings)); + ARRAY_SIZE(imx290_global_init_settings)); if (ret < 0) { dev_err(imx290->dev, "Could not set init registers\n"); return ret; } + /* Set mdel specific init register settings */ + ret = imx290_set_register_array(imx290, imx290->model->init_regs, + imx290->model->init_regs_num); + if (ret < 0) { + dev_err(imx290->dev, "Could not set model specific init registers\n"); + return ret; + } + + /* Set clock parameters based on mode and xclk */ + ret = imx290_set_clock(imx290); + if (ret < 0) { + dev_err(imx290->dev, "Could not set clocks - %d\n", ret); + return ret; + } + /* Set data lane count */ ret = imx290_set_data_lanes(imx290); if (ret < 0) { - dev_err(imx290->dev, "Could not set data lanes\n"); + dev_err(imx290->dev, "Could not set data lanes - %d\n", ret); + return ret; + } + + ret = imx290_set_csi_config(imx290); + if (ret < 0) { + dev_err(imx290->dev, "Could not set csi cfg - %d\n", ret); return ret; } @@ -766,7 +1055,7 @@ static int imx290_start_streaming(struct imx290 *imx290, format = v4l2_subdev_get_pad_format(&imx290->sd, state, 0); ret = imx290_setup_format(imx290, format); if (ret < 0) { - dev_err(imx290->dev, "Could not set frame format\n"); + dev_err(imx290->dev, "Could not set frame format - %d\n", ret); return ret; } @@ -774,19 +1063,14 @@ static int imx290_start_streaming(struct imx290 *imx290, ret = imx290_set_register_array(imx290, imx290->current_mode->data, imx290->current_mode->data_size); if (ret < 0) { - dev_err(imx290->dev, "Could not set current mode\n"); + dev_err(imx290->dev, "Could not set current mode - %d\n", ret); return ret; } - ret = imx290_write(imx290, IMX290_HMAX, imx290->current_mode->hmax, - NULL); - if (ret) - return ret; - /* Apply customized values from user */ ret = __v4l2_ctrl_handler_setup(imx290->sd.ctrl_handler); if (ret) { - dev_err(imx290->dev, "Could not sync v4l2 controls\n"); + dev_err(imx290->dev, "Could not sync v4l2 controls - %d\n", ret); return ret; } @@ -835,6 +1119,13 @@ static int imx290_set_stream(struct v4l2_subdev *sd, int enable) pm_runtime_put_autosuspend(imx290->dev); } + /* + * vflip and hflip should not be changed during streaming as the sensor + * will produce an invalid frame. + */ + __v4l2_ctrl_grab(imx290->vflip, enable); + __v4l2_ctrl_grab(imx290->hflip, enable); + unlock: v4l2_subdev_unlock_state(state); return ret; @@ -844,10 +1135,12 @@ static int imx290_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { + const struct imx290 *imx290 = to_imx290(sd); + if (code->index >= ARRAY_SIZE(imx290_formats)) return -EINVAL; - code->code = imx290_formats[code->index].code; + code->code = imx290_formats[code->index].code[imx290->model->colour_variant]; return 0; } @@ -859,7 +1152,7 @@ static int imx290_enum_frame_size(struct v4l2_subdev *sd, const struct imx290 *imx290 = to_imx290(sd); const struct imx290_mode *imx290_modes = imx290_modes_ptr(imx290); - if (!imx290_format_info(fse->code)) + if (!imx290_format_info(imx290, fse->code)) return -EINVAL; if (fse->index >= imx290_modes_num(imx290)) @@ -888,10 +1181,14 @@ static int imx290_set_fmt(struct v4l2_subdev *sd, fmt->format.width = mode->width; fmt->format.height = mode->height; - if (!imx290_format_info(fmt->format.code)) - fmt->format.code = imx290_formats[0].code; + if (!imx290_format_info(imx290, fmt->format.code)) + fmt->format.code = imx290_formats[0].code[imx290->model->colour_variant]; fmt->format.field = V4L2_FIELD_NONE; + fmt->format.colorspace = V4L2_COLORSPACE_RAW; + fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_601; + fmt->format.quantization = V4L2_QUANTIZATION_FULL_RANGE; + fmt->format.xfer_func = V4L2_XFER_FUNC_NONE; format = v4l2_subdev_get_pad_format(sd, sd_state, 0); @@ -899,6 +1196,7 @@ static int imx290_set_fmt(struct v4l2_subdev *sd, imx290->current_mode = mode; imx290_ctrl_update(imx290, &fmt->format, mode); + imx290_exposure_update(imx290, mode); } *format = fmt->format; @@ -910,16 +1208,23 @@ static int imx290_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_selection *sel) { + struct imx290 *imx290 = to_imx290(sd); struct v4l2_mbus_framefmt *format; switch (sel->target) { case V4L2_SEL_TGT_CROP: { format = v4l2_subdev_get_pad_format(sd, sd_state, 0); + /* + * The sensor moves the readout by 1 pixel based on flips to + * keep the Bayer order the same. + */ sel->r.top = IMX920_PIXEL_ARRAY_MARGIN_TOP - + (IMX290_PIXEL_ARRAY_RECORDING_HEIGHT - format->height) / 2; + + (IMX290_PIXEL_ARRAY_RECORDING_HEIGHT - format->height) / 2 + + imx290->vflip->val; sel->r.left = IMX920_PIXEL_ARRAY_MARGIN_LEFT - + (IMX290_PIXEL_ARRAY_RECORDING_WIDTH - format->width) / 2; + + (IMX290_PIXEL_ARRAY_RECORDING_WIDTH - format->width) / 2 + + imx290->hflip->val; sel->r.width = format->width; sel->r.height = format->height; @@ -964,6 +1269,11 @@ static int imx290_entity_init_cfg(struct v4l2_subdev *subdev, return 0; } +static const struct v4l2_subdev_core_ops imx290_core_ops = { + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, + .unsubscribe_event = v4l2_event_subdev_unsubscribe, +}; + static const struct v4l2_subdev_video_ops imx290_video_ops = { .s_stream = imx290_set_stream, }; @@ -978,6 +1288,7 @@ static const struct v4l2_subdev_pad_ops imx290_pad_ops = { }; static const struct v4l2_subdev_ops imx290_subdev_ops = { + .core = &imx290_core_ops, .video = &imx290_video_ops, .pad = &imx290_pad_ops, }; @@ -996,7 +1307,8 @@ static int imx290_subdev_init(struct imx290 *imx290) imx290->current_mode = &imx290_modes_ptr(imx290)[0]; v4l2_i2c_subdev_init(&imx290->sd, client, &imx290_subdev_ops); - imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + imx290->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; imx290->sd.dev = imx290->dev; imx290->sd.entity.ops = &imx290_subdev_entity_ops; imx290->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; @@ -1132,15 +1444,22 @@ static int imx290_init_clk(struct imx290 *imx290) u32 xclk_freq; int ret; - ret = fwnode_property_read_u32(dev_fwnode(imx290->dev), - "clock-frequency", &xclk_freq); + ret = device_property_read_u32(imx290->dev, "clock-frequency", + &xclk_freq); if (ret) { dev_err(imx290->dev, "Could not get xclk frequency\n"); return ret; } - /* external clock must be 37.125 MHz */ - if (xclk_freq != 37125000) { + /* external clock must be 37.125 MHz or 74.25MHz */ + switch (xclk_freq) { + case 37125000: + imx290->xclk_idx = IMX290_CLK_37_125; + break; + case 74250000: + imx290->xclk_idx = IMX290_CLK_74_25; + break; + default: dev_err(imx290->dev, "External clock frequency %u is not supported\n", xclk_freq); return -EINVAL; @@ -1177,6 +1496,27 @@ static s64 imx290_check_link_freqs(const struct imx290 *imx290, return 0; } +static const struct imx290_model_info imx290_models[] = { + [IMX290_MODEL_IMX290LQR] = { + .colour_variant = IMX290_VARIANT_COLOUR, + .init_regs = imx290_global_init_settings_290, + .init_regs_num = ARRAY_SIZE(imx290_global_init_settings_290), + .name = "imx290", + }, + [IMX290_MODEL_IMX290LLR] = { + .colour_variant = IMX290_VARIANT_MONO, + .init_regs = imx290_global_init_settings_290, + .init_regs_num = ARRAY_SIZE(imx290_global_init_settings_290), + .name = "imx290", + }, + [IMX290_MODEL_IMX327LQR] = { + .colour_variant = IMX290_VARIANT_COLOUR, + .init_regs = imx290_global_init_settings_327, + .init_regs_num = ARRAY_SIZE(imx290_global_init_settings_327), + .name = "imx327", + }, +}; + static int imx290_parse_dt(struct imx290 *imx290) { /* Only CSI2 is supported for now: */ @@ -1187,6 +1527,8 @@ static int imx290_parse_dt(struct imx290 *imx290) int ret; s64 fq; + imx290->model = of_device_get_match_data(imx290->dev); + endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(imx290->dev), NULL); if (!endpoint) { dev_err(imx290->dev, "Endpoint node not found\n"); @@ -1260,7 +1602,7 @@ static int imx290_probe(struct i2c_client *client) imx290->xclk = devm_clk_get(dev, "xclk"); if (IS_ERR(imx290->xclk)) return dev_err_probe(dev, PTR_ERR(imx290->xclk), - "Could not get xclk"); + "Could not get xclk\n"); ret = imx290_get_regulators(dev, imx290); if (ret < 0) @@ -1304,6 +1646,9 @@ static int imx290_probe(struct i2c_client *client) if (ret) goto err_pm; + v4l2_i2c_subdev_set_name(&imx290->sd, client, + imx290->model->name, NULL); + /* * Finally, register the V4L2 subdev. This must be done after * initializing everything as the subdev can be used immediately after @@ -1352,8 +1697,21 @@ static void imx290_remove(struct i2c_client *client) } static const struct of_device_id imx290_of_match[] = { - { .compatible = "sony,imx290" }, - { /* sentinel */ } + { + /* Deprecated - synonym for "sony,imx290lqr" */ + .compatible = "sony,imx290", + .data = &imx290_models[IMX290_MODEL_IMX290LQR], + }, { + .compatible = "sony,imx290lqr", + .data = &imx290_models[IMX290_MODEL_IMX290LQR], + }, { + .compatible = "sony,imx290llr", + .data = &imx290_models[IMX290_MODEL_IMX290LLR], + }, { + .compatible = "sony,imx327lqr", + .data = &imx290_models[IMX290_MODEL_IMX327LQR], + }, + { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, imx290_of_match); diff --git a/drivers/media/i2c/imx296.c b/drivers/media/i2c/imx296.c index 3c12b6edeac9..4f22c0515ef8 100644 --- a/drivers/media/i2c/imx296.c +++ b/drivers/media/i2c/imx296.c @@ -685,15 +685,6 @@ static int imx296_enum_frame_size(struct v4l2_subdev *sd, return 0; } -static int imx296_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_state *state, - struct v4l2_subdev_format *fmt) -{ - fmt->format = *v4l2_subdev_get_pad_format(sd, state, fmt->pad); - - return 0; -} - static int imx296_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *state, struct v4l2_subdev_format *fmt) @@ -845,7 +836,7 @@ static const struct v4l2_subdev_video_ops imx296_subdev_video_ops = { static const struct v4l2_subdev_pad_ops imx296_subdev_pad_ops = { .enum_mbus_code = imx296_enum_mbus_code, .enum_frame_size = imx296_enum_frame_size, - .get_fmt = imx296_get_format, + .get_fmt = v4l2_subdev_get_fmt, .set_fmt = imx296_set_format, .get_selection = imx296_get_selection, .set_selection = imx296_set_selection, diff --git a/drivers/media/i2c/imx334.c b/drivers/media/i2c/imx334.c index 7b0a9086447d..309c706114d2 100644 --- a/drivers/media/i2c/imx334.c +++ b/drivers/media/i2c/imx334.c @@ -79,7 +79,6 @@ struct imx334_reg_list { * struct imx334_mode - imx334 sensor mode structure * @width: Frame width * @height: Frame height - * @code: Format code * @hblank: Horizontal blanking in lines * @vblank: Vertical blanking in lines * @vblank_min: Minimal vertical blanking in lines @@ -91,7 +90,6 @@ struct imx334_reg_list { struct imx334_mode { u32 width; u32 height; - u32 code; u32 hblank; u32 vblank; u32 vblank_min; @@ -119,6 +117,7 @@ struct imx334_mode { * @vblank: Vertical blanking in lines * @cur_mode: Pointer to current selected sensor mode * @mutex: Mutex for serializing sensor controls + * @cur_code: current selected format code * @streaming: Flag indicating streaming state */ struct imx334 { @@ -140,6 +139,7 @@ struct imx334 { u32 vblank; const struct imx334_mode *cur_mode; struct mutex mutex; + u32 cur_code; bool streaming; }; @@ -147,7 +147,170 @@ static const s64 link_freq[] = { IMX334_LINK_FREQ, }; -/* Sensor mode registers */ +/* Sensor mode registers for 1920x1080@30fps */ +static const struct imx334_reg mode_1920x1080_regs[] = { + {0x3000, 0x01}, + {0x3018, 0x04}, + {0x3030, 0xca}, + {0x3031, 0x08}, + {0x3032, 0x00}, + {0x3034, 0x4c}, + {0x3035, 0x04}, + {0x302c, 0xf0}, + {0x302d, 0x03}, + {0x302e, 0x80}, + {0x302f, 0x07}, + {0x3074, 0xcc}, + {0x3075, 0x02}, + {0x308e, 0xcd}, + {0x308f, 0x02}, + {0x3076, 0x38}, + {0x3077, 0x04}, + {0x3090, 0x38}, + {0x3091, 0x04}, + {0x3308, 0x38}, + {0x3309, 0x04}, + {0x30C6, 0x00}, + {0x30c7, 0x00}, + {0x30ce, 0x00}, + {0x30cf, 0x00}, + {0x30d8, 0x18}, + {0x30d9, 0x0a}, + {0x304c, 0x00}, + {0x304e, 0x00}, + {0x304f, 0x00}, + {0x3050, 0x00}, + {0x30b6, 0x00}, + {0x30b7, 0x00}, + {0x3116, 0x08}, + {0x3117, 0x00}, + {0x31a0, 0x20}, + {0x31a1, 0x0f}, + {0x300c, 0x3b}, + {0x300d, 0x29}, + {0x314c, 0x29}, + {0x314d, 0x01}, + {0x315a, 0x06}, + {0x3168, 0xa0}, + {0x316a, 0x7e}, + {0x319e, 0x02}, + {0x3199, 0x00}, + {0x319d, 0x00}, + {0x31dd, 0x03}, + {0x3300, 0x00}, + {0x341c, 0xff}, + {0x341d, 0x01}, + {0x3a01, 0x03}, + {0x3a18, 0x7f}, + {0x3a19, 0x00}, + {0x3a1a, 0x37}, + {0x3a1b, 0x00}, + {0x3a1c, 0x37}, + {0x3a1d, 0x00}, + {0x3a1e, 0xf7}, + {0x3a1f, 0x00}, + {0x3a20, 0x3f}, + {0x3a21, 0x00}, + {0x3a20, 0x6f}, + {0x3a21, 0x00}, + {0x3a20, 0x3f}, + {0x3a21, 0x00}, + {0x3a20, 0x5f}, + {0x3a21, 0x00}, + {0x3a20, 0x2f}, + {0x3a21, 0x00}, + {0x3078, 0x02}, + {0x3079, 0x00}, + {0x307a, 0x00}, + {0x307b, 0x00}, + {0x3080, 0x02}, + {0x3081, 0x00}, + {0x3082, 0x00}, + {0x3083, 0x00}, + {0x3088, 0x02}, + {0x3094, 0x00}, + {0x3095, 0x00}, + {0x3096, 0x00}, + {0x309b, 0x02}, + {0x309c, 0x00}, + {0x309d, 0x00}, + {0x309e, 0x00}, + {0x30a4, 0x00}, + {0x30a5, 0x00}, + {0x3288, 0x21}, + {0x328a, 0x02}, + {0x3414, 0x05}, + {0x3416, 0x18}, + {0x35Ac, 0x0e}, + {0x3648, 0x01}, + {0x364a, 0x04}, + {0x364c, 0x04}, + {0x3678, 0x01}, + {0x367c, 0x31}, + {0x367e, 0x31}, + {0x3708, 0x02}, + {0x3714, 0x01}, + {0x3715, 0x02}, + {0x3716, 0x02}, + {0x3717, 0x02}, + {0x371c, 0x3d}, + {0x371d, 0x3f}, + {0x372c, 0x00}, + {0x372d, 0x00}, + {0x372e, 0x46}, + {0x372f, 0x00}, + {0x3730, 0x89}, + {0x3731, 0x00}, + {0x3732, 0x08}, + {0x3733, 0x01}, + {0x3734, 0xfe}, + {0x3735, 0x05}, + {0x375d, 0x00}, + {0x375e, 0x00}, + {0x375f, 0x61}, + {0x3760, 0x06}, + {0x3768, 0x1b}, + {0x3769, 0x1b}, + {0x376a, 0x1a}, + {0x376b, 0x19}, + {0x376c, 0x18}, + {0x376d, 0x14}, + {0x376e, 0x0f}, + {0x3776, 0x00}, + {0x3777, 0x00}, + {0x3778, 0x46}, + {0x3779, 0x00}, + {0x377a, 0x08}, + {0x377b, 0x01}, + {0x377c, 0x45}, + {0x377d, 0x01}, + {0x377e, 0x23}, + {0x377f, 0x02}, + {0x3780, 0xd9}, + {0x3781, 0x03}, + {0x3782, 0xf5}, + {0x3783, 0x06}, + {0x3784, 0xa5}, + {0x3788, 0x0f}, + {0x378a, 0xd9}, + {0x378b, 0x03}, + {0x378c, 0xeb}, + {0x378d, 0x05}, + {0x378e, 0x87}, + {0x378f, 0x06}, + {0x3790, 0xf5}, + {0x3792, 0x43}, + {0x3794, 0x7a}, + {0x3796, 0xa1}, + {0x37b0, 0x37}, + {0x3e04, 0x0e}, + {0x30e8, 0x50}, + {0x30e9, 0x00}, + {0x3e04, 0x0e}, + {0x3002, 0x00}, +}; + +/* Sensor mode registers for 3840x2160@30fps */ static const struct imx334_reg mode_3840x2160_regs[] = { {0x3000, 0x01}, {0x3002, 0x00}, @@ -166,6 +329,7 @@ static const struct imx334_reg mode_3840x2160_regs[] = { {0x3288, 0x21}, {0x328a, 0x02}, {0x302c, 0x3c}, + {0x302d, 0x00}, {0x302e, 0x00}, {0x302f, 0x0f}, {0x3076, 0x70}, @@ -240,23 +404,75 @@ static const struct imx334_reg mode_3840x2160_regs[] = { {0x3794, 0x7a}, {0x3796, 0xa1}, {0x3e04, 0x0e}, + {0x319e, 0x00}, {0x3a00, 0x01}, + {0x3a18, 0xbf}, + {0x3a19, 0x00}, + {0x3a1a, 0x67}, + {0x3a1b, 0x00}, + {0x3a1c, 0x6f}, + {0x3a1d, 0x00}, + {0x3a1e, 0xd7}, + {0x3a1f, 0x01}, + {0x3a20, 0x6f}, + {0x3a21, 0x00}, + {0x3a22, 0xcf}, + {0x3a23, 0x00}, + {0x3a24, 0x6f}, + {0x3a25, 0x00}, + {0x3a26, 0xb7}, + {0x3a27, 0x00}, + {0x3a28, 0x5f}, + {0x3a29, 0x00}, +}; + +static const struct imx334_reg raw10_framefmt_regs[] = { + {0x3050, 0x00}, + {0x319d, 0x00}, + {0x341c, 0xff}, + {0x341d, 0x01}, +}; + +static const struct imx334_reg raw12_framefmt_regs[] = { + {0x3050, 0x01}, + {0x319d, 0x01}, + {0x341c, 0x47}, + {0x341d, 0x00}, +}; + +static const u32 imx334_mbus_codes[] = { + MEDIA_BUS_FMT_SRGGB12_1X12, + MEDIA_BUS_FMT_SRGGB10_1X10, }; /* Supported sensor mode configurations */ -static const struct imx334_mode supported_mode = { - .width = 3840, - .height = 2160, - .hblank = 560, - .vblank = 2340, - .vblank_min = 90, - .vblank_max = 132840, - .pclk = 594000000, - .link_freq_idx = 0, - .code = MEDIA_BUS_FMT_SRGGB12_1X12, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_3840x2160_regs), - .regs = mode_3840x2160_regs, +static const struct imx334_mode supported_modes[] = { + { + .width = 3840, + .height = 2160, + .hblank = 560, + .vblank = 2340, + .vblank_min = 90, + .vblank_max = 132840, + .pclk = 594000000, + .link_freq_idx = 0, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_3840x2160_regs), + .regs = mode_3840x2160_regs, + }, + }, { + .width = 1920, + .height = 1080, + .hblank = 2480, + .vblank = 1170, + .vblank_min = 45, + .vblank_max = 132840, + .pclk = 297000000, + .link_freq_idx = 0, + .reg_list = { + .num_of_regs = ARRAY_SIZE(mode_1920x1080_regs), + .regs = mode_1920x1080_regs, + }, }, }; @@ -382,7 +598,8 @@ static int imx334_update_controls(struct imx334 *imx334, if (ret) return ret; - ret = __v4l2_ctrl_s_ctrl(imx334->hblank_ctrl, mode->hblank); + ret = __v4l2_ctrl_modify_range(imx334->hblank_ctrl, mode->hblank, + mode->hblank, 1, mode->hblank); if (ret) return ret; @@ -481,6 +698,9 @@ static int imx334_set_ctrl(struct v4l2_ctrl *ctrl) pm_runtime_put(imx334->dev); break; + case V4L2_CID_HBLANK: + ret = 0; + break; default: dev_err(imx334->dev, "Invalid control %d", ctrl->id); ret = -EINVAL; @@ -494,6 +714,18 @@ static const struct v4l2_ctrl_ops imx334_ctrl_ops = { .s_ctrl = imx334_set_ctrl, }; +static int imx334_get_format_code(struct imx334 *imx334, u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(imx334_mbus_codes); i++) { + if (imx334_mbus_codes[i] == code) + return imx334_mbus_codes[i]; + } + + return imx334_mbus_codes[0]; +} + /** * imx334_enum_mbus_code() - Enumerate V4L2 sub-device mbus codes * @sd: pointer to imx334 V4L2 sub-device structure @@ -506,10 +738,10 @@ static int imx334_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { - if (code->index > 0) + if (code->index >= ARRAY_SIZE(imx334_mbus_codes)) return -EINVAL; - code->code = supported_mode.code; + code->code = imx334_mbus_codes[code->index]; return 0; } @@ -526,15 +758,20 @@ static int imx334_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fsize) { - if (fsize->index > 0) + struct imx334 *imx334 = to_imx334(sd); + u32 code; + + if (fsize->index >= ARRAY_SIZE(supported_modes)) return -EINVAL; - if (fsize->code != supported_mode.code) + code = imx334_get_format_code(imx334, fsize->code); + + if (fsize->code != code) return -EINVAL; - fsize->min_width = supported_mode.width; + fsize->min_width = supported_modes[fsize->index].width; fsize->max_width = fsize->min_width; - fsize->min_height = supported_mode.height; + fsize->min_height = supported_modes[fsize->index].height; fsize->max_height = fsize->min_height; return 0; @@ -553,7 +790,6 @@ static void imx334_fill_pad_format(struct imx334 *imx334, { fmt->format.width = mode->width; fmt->format.height = mode->height; - fmt->format.code = mode->code; fmt->format.field = V4L2_FIELD_NONE; fmt->format.colorspace = V4L2_COLORSPACE_RAW; fmt->format.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; @@ -583,6 +819,7 @@ static int imx334_get_pad_format(struct v4l2_subdev *sd, framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); fmt->format = *framefmt; } else { + fmt->format.code = imx334->cur_code; imx334_fill_pad_format(imx334, imx334->cur_mode, fmt); } @@ -609,15 +846,21 @@ static int imx334_set_pad_format(struct v4l2_subdev *sd, mutex_lock(&imx334->mutex); - mode = &supported_mode; + mode = v4l2_find_nearest_size(supported_modes, + ARRAY_SIZE(supported_modes), + width, height, + fmt->format.width, fmt->format.height); + imx334_fill_pad_format(imx334, mode, fmt); + fmt->format.code = imx334_get_format_code(imx334, fmt->format.code); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { struct v4l2_mbus_framefmt *framefmt; framefmt = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); *framefmt = fmt->format; - } else { + } else if (imx334->cur_mode != mode || imx334->cur_code != fmt->format.code) { + imx334->cur_code = fmt->format.code; ret = imx334_update_controls(imx334, mode); if (!ret) imx334->cur_mode = mode; @@ -642,11 +885,26 @@ static int imx334_init_pad_cfg(struct v4l2_subdev *sd, struct v4l2_subdev_format fmt = { 0 }; fmt.which = sd_state ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; - imx334_fill_pad_format(imx334, &supported_mode, &fmt); + imx334_fill_pad_format(imx334, &supported_modes[0], &fmt); return imx334_set_pad_format(sd, sd_state, &fmt); } +static int imx334_set_framefmt(struct imx334 *imx334) +{ + switch (imx334->cur_code) { + case MEDIA_BUS_FMT_SRGGB10_1X10: + return imx334_write_regs(imx334, raw10_framefmt_regs, + ARRAY_SIZE(raw10_framefmt_regs)); + + case MEDIA_BUS_FMT_SRGGB12_1X12: + return imx334_write_regs(imx334, raw12_framefmt_regs, + ARRAY_SIZE(raw12_framefmt_regs)); + } + + return -EINVAL; +} + /** * imx334_start_streaming() - Start sensor stream * @imx334: pointer to imx334 device @@ -667,6 +925,13 @@ static int imx334_start_streaming(struct imx334 *imx334) return ret; } + ret = imx334_set_framefmt(imx334); + if (ret) { + dev_err(imx334->dev, "%s failed to set frame format: %d\n", + __func__, ret); + return ret; + } + /* Setup handler will write actual exposure and gain */ ret = __v4l2_ctrl_handler_setup(imx334->sd.ctrl_handler); if (ret) { @@ -1037,7 +1302,8 @@ static int imx334_probe(struct i2c_client *client) } /* Set default mode to max resolution */ - imx334->cur_mode = &supported_mode; + imx334->cur_mode = &supported_modes[0]; + imx334->cur_code = imx334_mbus_codes[0]; imx334->vblank = imx334->cur_mode->vblank; ret = imx334_init_controls(imx334); diff --git a/drivers/media/i2c/m5mols/Kconfig b/drivers/media/i2c/m5mols/Kconfig deleted file mode 100644 index 7f0af32f4376..000000000000 --- a/drivers/media/i2c/m5mols/Kconfig +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -config VIDEO_M5MOLS - tristate "Fujitsu M-5MOLS 8MP sensor support" - depends on I2C && VIDEO_DEV - select MEDIA_CONTROLLER - select VIDEO_V4L2_SUBDEV_API - help - This driver supports Fujitsu M-5MOLS camera sensor with ISP diff --git a/drivers/media/i2c/m5mols/Makefile b/drivers/media/i2c/m5mols/Makefile deleted file mode 100644 index 13fa8ec29ac0..000000000000 --- a/drivers/media/i2c/m5mols/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -m5mols-objs := m5mols_core.o m5mols_controls.o m5mols_capture.o - -obj-$(CONFIG_VIDEO_M5MOLS) += m5mols.o diff --git a/drivers/media/i2c/m5mols/m5mols.h b/drivers/media/i2c/m5mols/m5mols.h deleted file mode 100644 index d8545d2280af..000000000000 --- a/drivers/media/i2c/m5mols/m5mols.h +++ /dev/null @@ -1,349 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Header for M-5MOLS 8M Pixel camera sensor with ISP - * - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - * Author: HeungJun Kim <riverful.kim@samsung.com> - * - * Copyright (C) 2009 Samsung Electronics Co., Ltd. - * Author: Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com> - */ - -#ifndef M5MOLS_H -#define M5MOLS_H - -#include <linux/sizes.h> -#include <linux/gpio/consumer.h> -#include <media/v4l2-subdev.h> -#include "m5mols_reg.h" - - -/* An amount of data transmitted in addition to the value - * determined by CAPP_JPEG_SIZE_MAX register. - */ -#define M5MOLS_JPEG_TAGS_SIZE 0x20000 -#define M5MOLS_MAIN_JPEG_SIZE_MAX (5 * SZ_1M) - -extern int m5mols_debug; - -enum m5mols_restype { - M5MOLS_RESTYPE_MONITOR, - M5MOLS_RESTYPE_CAPTURE, - M5MOLS_RESTYPE_MAX, -}; - -/** - * struct m5mols_resolution - structure for the resolution - * @type: resolution type according to the pixel code - * @width: width of the resolution - * @height: height of the resolution - * @reg: resolution preset register value - */ -struct m5mols_resolution { - u8 reg; - enum m5mols_restype type; - u16 width; - u16 height; -}; - -/** - * struct m5mols_exif - structure for the EXIF information of M-5MOLS - * @exposure_time: exposure time register value - * @shutter_speed: speed of the shutter register value - * @aperture: aperture register value - * @brightness: brightness register value - * @exposure_bias: it calls also EV bias - * @iso_speed: ISO register value - * @flash: status register value of the flash - * @sdr: status register value of the Subject Distance Range - * @qval: not written exact meaning in document - */ -struct m5mols_exif { - u32 exposure_time; - u32 shutter_speed; - u32 aperture; - u32 brightness; - u32 exposure_bias; - u16 iso_speed; - u16 flash; - u16 sdr; - u16 qval; -}; - -/** - * struct m5mols_capture - Structure for the capture capability - * @exif: EXIF information - * @buf_size: internal JPEG frame buffer size, in bytes - * @main: size in bytes of the main image - * @thumb: size in bytes of the thumb image, if it was accompanied - * @total: total size in bytes of the produced image - */ -struct m5mols_capture { - struct m5mols_exif exif; - unsigned int buf_size; - u32 main; - u32 thumb; - u32 total; -}; - -/** - * struct m5mols_scenemode - structure for the scenemode capability - * @metering: metering light register value - * @ev_bias: EV bias register value - * @wb_mode: mode which means the WhiteBalance is Auto or Manual - * @wb_preset: whitebalance preset register value in the Manual mode - * @chroma_en: register value whether the Chroma capability is enabled or not - * @chroma_lvl: chroma's level register value - * @edge_en: register value Whether the Edge capability is enabled or not - * @edge_lvl: edge's level register value - * @af_range: Auto Focus's range - * @fd_mode: Face Detection mode - * @mcc: Multi-axis Color Conversion which means emotion color - * @light: status of the Light - * @flash: status of the Flash - * @tone: Tone color which means Contrast - * @iso: ISO register value - * @capt_mode: Mode of the Image Stabilization while the camera capturing - * @wdr: Wide Dynamic Range register value - * - * The each value according to each scenemode is recommended in the documents. - */ -struct m5mols_scenemode { - u8 metering; - u8 ev_bias; - u8 wb_mode; - u8 wb_preset; - u8 chroma_en; - u8 chroma_lvl; - u8 edge_en; - u8 edge_lvl; - u8 af_range; - u8 fd_mode; - u8 mcc; - u8 light; - u8 flash; - u8 tone; - u8 iso; - u8 capt_mode; - u8 wdr; -}; - -#define VERSION_STRING_SIZE 22 - -/** - * struct m5mols_version - firmware version information - * @customer: customer information - * @project: version of project information according to customer - * @fw: firmware revision - * @hw: hardware revision - * @param: version of the parameter - * @awb: Auto WhiteBalance algorithm version - * @str: information about manufacturer and packaging vendor - * @af: Auto Focus version - * - * The register offset starts the customer version at 0x0, and it ends - * the awb version at 0x09. The customer, project information occupies 1 bytes - * each. And also the fw, hw, param, awb each requires 2 bytes. The str is - * unique string associated with firmware's version. It includes information - * about manufacturer and the vendor of the sensor's packaging. The least - * significant 2 bytes of the string indicate packaging manufacturer. - */ -struct m5mols_version { - u8 customer; - u8 project; - u16 fw; - u16 hw; - u16 param; - u16 awb; - u8 str[VERSION_STRING_SIZE]; - u8 af; -}; - -/** - * struct m5mols_info - M-5MOLS driver data structure - * @pdata: platform data - * @sd: v4l-subdev instance - * @pad: media pad - * @irq_waitq: waitqueue for the capture - * @irq_done: set to 1 in the interrupt handler - * @handle: control handler - * @auto_exposure: auto/manual exposure control - * @exposure_bias: exposure compensation control - * @exposure: manual exposure control - * @metering: exposure metering control - * @auto_iso: auto/manual ISO sensitivity control - * @iso: manual ISO sensitivity control - * @auto_wb: auto white balance control - * @lock_3a: 3A lock control - * @colorfx: color effect control - * @saturation: saturation control - * @zoom: zoom control - * @wdr: wide dynamic range control - * @stabilization: image stabilization control - * @jpeg_quality: JPEG compression quality control - * @set_power: optional power callback to the board code - * @reset: GPIO driving the reset pin of M-5MOLS - * @lock: mutex protecting the structure fields below - * @ffmt: current fmt according to resolution type - * @res_type: current resolution type - * @ver: information of the version - * @cap: the capture mode attributes - * @isp_ready: 1 when the ISP controller has completed booting - * @power: current sensor's power status - * @ctrl_sync: 1 when the control handler state is restored in H/W - * @resolution: register value for current resolution - * @mode: register value for current operation mode - */ -struct m5mols_info { - const struct m5mols_platform_data *pdata; - struct v4l2_subdev sd; - struct media_pad pad; - - wait_queue_head_t irq_waitq; - atomic_t irq_done; - - struct v4l2_ctrl_handler handle; - struct { - /* exposure/exposure bias/auto exposure cluster */ - struct v4l2_ctrl *auto_exposure; - struct v4l2_ctrl *exposure_bias; - struct v4l2_ctrl *exposure; - struct v4l2_ctrl *metering; - }; - struct { - /* iso/auto iso cluster */ - struct v4l2_ctrl *auto_iso; - struct v4l2_ctrl *iso; - }; - struct v4l2_ctrl *auto_wb; - - struct v4l2_ctrl *lock_3a; - struct v4l2_ctrl *colorfx; - struct v4l2_ctrl *saturation; - struct v4l2_ctrl *zoom; - struct v4l2_ctrl *wdr; - struct v4l2_ctrl *stabilization; - struct v4l2_ctrl *jpeg_quality; - - int (*set_power)(struct device *dev, int on); - struct gpio_desc *reset; - - struct mutex lock; - - struct v4l2_mbus_framefmt ffmt[M5MOLS_RESTYPE_MAX]; - int res_type; - - struct m5mols_version ver; - struct m5mols_capture cap; - - unsigned int isp_ready:1; - unsigned int power:1; - unsigned int ctrl_sync:1; - - u8 resolution; - u8 mode; -}; - -#define is_available_af(__info) (__info->ver.af) -#define is_code(__code, __type) (__code == m5mols_default_ffmt[__type].code) -#define is_manufacturer(__info, __manufacturer) \ - (__info->ver.str[0] == __manufacturer[0] && \ - __info->ver.str[1] == __manufacturer[1]) -/* - * I2C operation of the M-5MOLS - * - * The I2C read operation of the M-5MOLS requires 2 messages. The first - * message sends the information about the command, command category, and total - * message size. The second message is used to retrieve the data specified in - * the first message - * - * 1st message 2nd message - * +-------+---+----------+-----+-------+ +------+------+------+------+ - * | size1 | R | category | cmd | size2 | | d[0] | d[1] | d[2] | d[3] | - * +-------+---+----------+-----+-------+ +------+------+------+------+ - * - size1: message data size(5 in this case) - * - size2: desired buffer size of the 2nd message - * - d[0..3]: according to size2 - * - * The I2C write operation needs just one message. The message includes - * category, command, total size, and desired data. - * - * 1st message - * +-------+---+----------+-----+------+------+------+------+ - * | size1 | W | category | cmd | d[0] | d[1] | d[2] | d[3] | - * +-------+---+----------+-----+------+------+------+------+ - * - d[0..3]: according to size1 - */ -int m5mols_read_u8(struct v4l2_subdev *sd, u32 reg_comb, u8 *val); -int m5mols_read_u16(struct v4l2_subdev *sd, u32 reg_comb, u16 *val); -int m5mols_read_u32(struct v4l2_subdev *sd, u32 reg_comb, u32 *val); -int m5mols_write(struct v4l2_subdev *sd, u32 reg_comb, u32 val); - -int m5mols_busy_wait(struct v4l2_subdev *sd, u32 reg, u32 value, u32 mask, - int timeout); - -/* Mask value for busy waiting until M-5MOLS I2C interface is initialized */ -#define M5MOLS_I2C_RDY_WAIT_FL (1 << 16) -/* ISP state transition timeout, in ms */ -#define M5MOLS_MODE_CHANGE_TIMEOUT 200 -#define M5MOLS_BUSY_WAIT_DEF_TIMEOUT 250 - -/* - * Mode operation of the M-5MOLS - * - * Changing the mode of the M-5MOLS is needed right executing order. - * There are three modes(PARAMETER, MONITOR, CAPTURE) which can be changed - * by user. There are various categories associated with each mode. - * - * +============================================================+ - * | mode | category | - * +============================================================+ - * | FLASH | FLASH(only after Stand-by or Power-on) | - * | SYSTEM | SYSTEM(only after sensor arm-booting) | - * | PARAMETER | PARAMETER | - * | MONITOR | MONITOR(preview), Auto Focus, Face Detection | - * | CAPTURE | Single CAPTURE, Preview(recording) | - * +============================================================+ - * - * The available executing order between each modes are as follows: - * PARAMETER <---> MONITOR <---> CAPTURE - */ -int m5mols_set_mode(struct m5mols_info *info, u8 mode); - -int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg); -int m5mols_wait_interrupt(struct v4l2_subdev *sd, u8 condition, u32 timeout); -int m5mols_restore_controls(struct m5mols_info *info); -int m5mols_start_capture(struct m5mols_info *info); -int m5mols_do_scenemode(struct m5mols_info *info, u8 mode); -int m5mols_lock_3a(struct m5mols_info *info, bool lock); -int m5mols_set_ctrl(struct v4l2_ctrl *ctrl); -int m5mols_init_controls(struct v4l2_subdev *sd); - -/* The firmware function */ -int m5mols_update_fw(struct v4l2_subdev *sd, - int (*set_power)(struct m5mols_info *, bool)); - -static inline struct m5mols_info *to_m5mols(struct v4l2_subdev *subdev) -{ - return container_of(subdev, struct m5mols_info, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - struct m5mols_info *info = container_of(ctrl->handler, - struct m5mols_info, handle); - return &info->sd; -} - -static inline void m5mols_set_ctrl_mode(struct v4l2_ctrl *ctrl, - unsigned int mode) -{ - ctrl->priv = (void *)(uintptr_t)mode; -} - -static inline unsigned int m5mols_get_ctrl_mode(struct v4l2_ctrl *ctrl) -{ - return (unsigned int)(uintptr_t)ctrl->priv; -} - -#endif /* M5MOLS_H */ diff --git a/drivers/media/i2c/m5mols/m5mols_capture.c b/drivers/media/i2c/m5mols/m5mols_capture.c deleted file mode 100644 index 275c5b2539fd..000000000000 --- a/drivers/media/i2c/m5mols/m5mols_capture.c +++ /dev/null @@ -1,158 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -/* - * The Capture code for Fujitsu M-5MOLS ISP - * - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - * Author: HeungJun Kim <riverful.kim@samsung.com> - * - * Copyright (C) 2009 Samsung Electronics Co., Ltd. - * Author: Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com> - */ - -#include <linux/i2c.h> -#include <linux/slab.h> -#include <linux/irq.h> -#include <linux/interrupt.h> -#include <linux/delay.h> -#include <linux/regulator/consumer.h> -#include <linux/videodev2.h> -#include <media/v4l2-ctrls.h> -#include <media/v4l2-device.h> -#include <media/v4l2-subdev.h> -#include <media/i2c/m5mols.h> -#include <media/drv-intf/exynos-fimc.h> - -#include "m5mols.h" -#include "m5mols_reg.h" - -/** - * m5mols_read_rational - I2C read of a rational number - * @sd: sub-device, as pointed by struct v4l2_subdev - * @addr_num: numerator register - * @addr_den: denominator register - * @val: place to store the division result - * - * Read numerator and denominator from registers @addr_num and @addr_den - * respectively and return the division result in @val. - */ -static int m5mols_read_rational(struct v4l2_subdev *sd, u32 addr_num, - u32 addr_den, u32 *val) -{ - u32 num, den; - - int ret = m5mols_read_u32(sd, addr_num, &num); - if (!ret) - ret = m5mols_read_u32(sd, addr_den, &den); - if (ret) - return ret; - *val = den == 0 ? 0 : num / den; - return ret; -} - -/** - * m5mols_capture_info - Gather captured image information - * @info: M-5MOLS driver data structure - * - * For now it gathers only EXIF information and file size. - */ -static int m5mols_capture_info(struct m5mols_info *info) -{ - struct m5mols_exif *exif = &info->cap.exif; - struct v4l2_subdev *sd = &info->sd; - int ret; - - ret = m5mols_read_rational(sd, EXIF_INFO_EXPTIME_NU, - EXIF_INFO_EXPTIME_DE, &exif->exposure_time); - if (ret) - return ret; - ret = m5mols_read_rational(sd, EXIF_INFO_TV_NU, EXIF_INFO_TV_DE, - &exif->shutter_speed); - if (ret) - return ret; - ret = m5mols_read_rational(sd, EXIF_INFO_AV_NU, EXIF_INFO_AV_DE, - &exif->aperture); - if (ret) - return ret; - ret = m5mols_read_rational(sd, EXIF_INFO_BV_NU, EXIF_INFO_BV_DE, - &exif->brightness); - if (ret) - return ret; - ret = m5mols_read_rational(sd, EXIF_INFO_EBV_NU, EXIF_INFO_EBV_DE, - &exif->exposure_bias); - if (ret) - return ret; - - ret = m5mols_read_u16(sd, EXIF_INFO_ISO, &exif->iso_speed); - if (!ret) - ret = m5mols_read_u16(sd, EXIF_INFO_FLASH, &exif->flash); - if (!ret) - ret = m5mols_read_u16(sd, EXIF_INFO_SDR, &exif->sdr); - if (!ret) - ret = m5mols_read_u16(sd, EXIF_INFO_QVAL, &exif->qval); - if (ret) - return ret; - - if (!ret) - ret = m5mols_read_u32(sd, CAPC_IMAGE_SIZE, &info->cap.main); - if (!ret) - ret = m5mols_read_u32(sd, CAPC_THUMB_SIZE, &info->cap.thumb); - if (!ret) - info->cap.total = info->cap.main + info->cap.thumb; - - return ret; -} - -int m5mols_start_capture(struct m5mols_info *info) -{ - unsigned int framesize = info->cap.buf_size - M5MOLS_JPEG_TAGS_SIZE; - struct v4l2_subdev *sd = &info->sd; - int ret; - - /* - * Synchronize the controls, set the capture frame resolution and color - * format. The frame capture is initiated during switching from Monitor - * to Capture mode. - */ - ret = m5mols_set_mode(info, REG_MONITOR); - if (!ret) - ret = m5mols_restore_controls(info); - if (!ret) - ret = m5mols_write(sd, CAPP_YUVOUT_MAIN, REG_JPEG); - if (!ret) - ret = m5mols_write(sd, CAPP_MAIN_IMAGE_SIZE, info->resolution); - if (!ret) - ret = m5mols_write(sd, CAPP_JPEG_SIZE_MAX, framesize); - if (!ret) - ret = m5mols_set_mode(info, REG_CAPTURE); - if (!ret) - /* Wait until a frame is captured to ISP internal memory */ - ret = m5mols_wait_interrupt(sd, REG_INT_CAPTURE, 2000); - if (ret) - return ret; - - /* - * Initiate the captured data transfer to a MIPI-CSI receiver. - */ - ret = m5mols_write(sd, CAPC_SEL_FRAME, 1); - if (!ret) - ret = m5mols_write(sd, CAPC_START, REG_CAP_START_MAIN); - if (!ret) { - bool captured = false; - unsigned int size; - - /* Wait for the capture completion interrupt */ - ret = m5mols_wait_interrupt(sd, REG_INT_CAPTURE, 2000); - if (!ret) { - captured = true; - ret = m5mols_capture_info(info); - } - size = captured ? info->cap.main : 0; - v4l2_dbg(1, m5mols_debug, sd, "%s: size: %d, thumb.: %d B\n", - __func__, size, info->cap.thumb); - - v4l2_subdev_notify(sd, S5P_FIMC_TX_END_NOTIFY, &size); - } - - return ret; -} diff --git a/drivers/media/i2c/m5mols/m5mols_controls.c b/drivers/media/i2c/m5mols/m5mols_controls.c deleted file mode 100644 index b45e0e08b6c8..000000000000 --- a/drivers/media/i2c/m5mols/m5mols_controls.c +++ /dev/null @@ -1,625 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Controls for M-5MOLS 8M Pixel camera sensor with ISP - * - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - * Author: HeungJun Kim <riverful.kim@samsung.com> - * - * Copyright (C) 2009 Samsung Electronics Co., Ltd. - * Author: Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com> - */ - -#include <linux/i2c.h> -#include <linux/delay.h> -#include <linux/videodev2.h> -#include <media/v4l2-ctrls.h> - -#include "m5mols.h" -#include "m5mols_reg.h" - -static struct m5mols_scenemode m5mols_default_scenemode[] = { - [REG_SCENE_NORMAL] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 3, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_NORMAL, REG_LIGHT_OFF, REG_FLASH_OFF, - 5, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_PORTRAIT] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 3, REG_EDGE_ON, 4, - REG_AF_NORMAL, BIT_FD_EN | BIT_FD_DRAW_FACE_FRAME, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_LANDSCAPE] = { - REG_AE_ALL, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 4, REG_EDGE_ON, 6, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_SPORTS] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 3, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_PARTY_INDOOR] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 4, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_200, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_BEACH_SNOW] = { - REG_AE_CENTER, REG_AE_INDEX_10_POS, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 4, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_SUNSET] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET, - REG_AWB_DAYLIGHT, - REG_CHROMA_ON, 3, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_DAWN_DUSK] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_PRESET, - REG_AWB_FLUORESCENT_1, - REG_CHROMA_ON, 3, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_FALL] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 5, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_NIGHT] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 3, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_AGAINST_LIGHT] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 3, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_FIRE] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 3, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_50, REG_CAP_NONE, REG_WDR_OFF, - }, - [REG_SCENE_TEXT] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 3, REG_EDGE_ON, 7, - REG_AF_MACRO, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_AUTO, REG_CAP_ANTI_SHAKE, REG_WDR_ON, - }, - [REG_SCENE_CANDLE] = { - REG_AE_CENTER, REG_AE_INDEX_00, REG_AWB_AUTO, 0, - REG_CHROMA_ON, 3, REG_EDGE_ON, 5, - REG_AF_NORMAL, REG_FD_OFF, - REG_MCC_OFF, REG_LIGHT_OFF, REG_FLASH_OFF, - 6, REG_ISO_AUTO, REG_CAP_NONE, REG_WDR_OFF, - }, -}; - -/** - * m5mols_do_scenemode() - Change current scenemode - * @info: M-5MOLS driver data structure - * @mode: Desired mode of the scenemode - * - * WARNING: The execution order is important. Do not change the order. - */ -int m5mols_do_scenemode(struct m5mols_info *info, u8 mode) -{ - struct v4l2_subdev *sd = &info->sd; - struct m5mols_scenemode scenemode = m5mols_default_scenemode[mode]; - int ret; - - if (mode > REG_SCENE_CANDLE) - return -EINVAL; - - ret = v4l2_ctrl_s_ctrl(info->lock_3a, 0); - if (!ret) - ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, mode); - if (!ret) - ret = m5mols_write(sd, AE_EV_PRESET_CAPTURE, mode); - if (!ret) - ret = m5mols_write(sd, AE_MODE, scenemode.metering); - if (!ret) - ret = m5mols_write(sd, AE_INDEX, scenemode.ev_bias); - if (!ret) - ret = m5mols_write(sd, AWB_MODE, scenemode.wb_mode); - if (!ret) - ret = m5mols_write(sd, AWB_MANUAL, scenemode.wb_preset); - if (!ret) - ret = m5mols_write(sd, MON_CHROMA_EN, scenemode.chroma_en); - if (!ret) - ret = m5mols_write(sd, MON_CHROMA_LVL, scenemode.chroma_lvl); - if (!ret) - ret = m5mols_write(sd, MON_EDGE_EN, scenemode.edge_en); - if (!ret) - ret = m5mols_write(sd, MON_EDGE_LVL, scenemode.edge_lvl); - if (!ret && is_available_af(info)) - ret = m5mols_write(sd, AF_MODE, scenemode.af_range); - if (!ret && is_available_af(info)) - ret = m5mols_write(sd, FD_CTL, scenemode.fd_mode); - if (!ret) - ret = m5mols_write(sd, MON_TONE_CTL, scenemode.tone); - if (!ret) - ret = m5mols_write(sd, AE_ISO, scenemode.iso); - if (!ret) - ret = m5mols_set_mode(info, REG_CAPTURE); - if (!ret) - ret = m5mols_write(sd, CAPP_WDR_EN, scenemode.wdr); - if (!ret) - ret = m5mols_write(sd, CAPP_MCC_MODE, scenemode.mcc); - if (!ret) - ret = m5mols_write(sd, CAPP_LIGHT_CTRL, scenemode.light); - if (!ret) - ret = m5mols_write(sd, CAPP_FLASH_CTRL, scenemode.flash); - if (!ret) - ret = m5mols_write(sd, CAPC_MODE, scenemode.capt_mode); - if (!ret) - ret = m5mols_set_mode(info, REG_MONITOR); - - return ret; -} - -static int m5mols_3a_lock(struct m5mols_info *info, struct v4l2_ctrl *ctrl) -{ - bool af_lock = ctrl->val & V4L2_LOCK_FOCUS; - int ret = 0; - - if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_EXPOSURE) { - bool ae_lock = ctrl->val & V4L2_LOCK_EXPOSURE; - - ret = m5mols_write(&info->sd, AE_LOCK, ae_lock ? - REG_AE_LOCK : REG_AE_UNLOCK); - if (ret) - return ret; - } - - if (((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_WHITE_BALANCE) - && info->auto_wb->val) { - bool awb_lock = ctrl->val & V4L2_LOCK_WHITE_BALANCE; - - ret = m5mols_write(&info->sd, AWB_LOCK, awb_lock ? - REG_AWB_LOCK : REG_AWB_UNLOCK); - if (ret) - return ret; - } - - if (!info->ver.af || !af_lock) - return ret; - - if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_FOCUS) - ret = m5mols_write(&info->sd, AF_EXECUTE, REG_AF_STOP); - - return ret; -} - -static int m5mols_set_metering_mode(struct m5mols_info *info, int mode) -{ - unsigned int metering; - - switch (mode) { - case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED: - metering = REG_AE_CENTER; - break; - case V4L2_EXPOSURE_METERING_SPOT: - metering = REG_AE_SPOT; - break; - default: - metering = REG_AE_ALL; - break; - } - - return m5mols_write(&info->sd, AE_MODE, metering); -} - -static int m5mols_set_exposure(struct m5mols_info *info, int exposure) -{ - struct v4l2_subdev *sd = &info->sd; - int ret = 0; - - if (exposure == V4L2_EXPOSURE_AUTO) { - /* Unlock auto exposure */ - info->lock_3a->val &= ~V4L2_LOCK_EXPOSURE; - m5mols_3a_lock(info, info->lock_3a); - - ret = m5mols_set_metering_mode(info, info->metering->val); - if (ret < 0) - return ret; - - v4l2_dbg(1, m5mols_debug, sd, - "%s: exposure bias: %#x, metering: %#x\n", - __func__, info->exposure_bias->val, - info->metering->val); - - return m5mols_write(sd, AE_INDEX, info->exposure_bias->val); - } - - if (exposure == V4L2_EXPOSURE_MANUAL) { - ret = m5mols_write(sd, AE_MODE, REG_AE_OFF); - if (ret == 0) - ret = m5mols_write(sd, AE_MAN_GAIN_MON, - info->exposure->val); - if (ret == 0) - ret = m5mols_write(sd, AE_MAN_GAIN_CAP, - info->exposure->val); - - v4l2_dbg(1, m5mols_debug, sd, "%s: exposure: %#x\n", - __func__, info->exposure->val); - } - - return ret; -} - -static int m5mols_set_white_balance(struct m5mols_info *info, int val) -{ - static const unsigned short wb[][2] = { - { V4L2_WHITE_BALANCE_INCANDESCENT, REG_AWB_INCANDESCENT }, - { V4L2_WHITE_BALANCE_FLUORESCENT, REG_AWB_FLUORESCENT_1 }, - { V4L2_WHITE_BALANCE_FLUORESCENT_H, REG_AWB_FLUORESCENT_2 }, - { V4L2_WHITE_BALANCE_HORIZON, REG_AWB_HORIZON }, - { V4L2_WHITE_BALANCE_DAYLIGHT, REG_AWB_DAYLIGHT }, - { V4L2_WHITE_BALANCE_FLASH, REG_AWB_LEDLIGHT }, - { V4L2_WHITE_BALANCE_CLOUDY, REG_AWB_CLOUDY }, - { V4L2_WHITE_BALANCE_SHADE, REG_AWB_SHADE }, - { V4L2_WHITE_BALANCE_AUTO, REG_AWB_AUTO }, - }; - int i; - struct v4l2_subdev *sd = &info->sd; - int ret = -EINVAL; - - for (i = 0; i < ARRAY_SIZE(wb); i++) { - int awb; - if (wb[i][0] != val) - continue; - - v4l2_dbg(1, m5mols_debug, sd, - "Setting white balance to: %#x\n", wb[i][0]); - - awb = wb[i][0] == V4L2_WHITE_BALANCE_AUTO; - ret = m5mols_write(sd, AWB_MODE, awb ? REG_AWB_AUTO : - REG_AWB_PRESET); - if (ret < 0) - return ret; - - if (!awb) - ret = m5mols_write(sd, AWB_MANUAL, wb[i][1]); - } - - return ret; -} - -static int m5mols_set_saturation(struct m5mols_info *info, int val) -{ - int ret = m5mols_write(&info->sd, MON_CHROMA_LVL, val); - if (ret < 0) - return ret; - - return m5mols_write(&info->sd, MON_CHROMA_EN, REG_CHROMA_ON); -} - -static int m5mols_set_color_effect(struct m5mols_info *info, int val) -{ - unsigned int m_effect = REG_COLOR_EFFECT_OFF; - unsigned int p_effect = REG_EFFECT_OFF; - unsigned int cfix_r = 0, cfix_b = 0; - struct v4l2_subdev *sd = &info->sd; - int ret = 0; - - switch (val) { - case V4L2_COLORFX_BW: - m_effect = REG_COLOR_EFFECT_ON; - break; - case V4L2_COLORFX_NEGATIVE: - p_effect = REG_EFFECT_NEGA; - break; - case V4L2_COLORFX_EMBOSS: - p_effect = REG_EFFECT_EMBOSS; - break; - case V4L2_COLORFX_SEPIA: - m_effect = REG_COLOR_EFFECT_ON; - cfix_r = REG_CFIXR_SEPIA; - cfix_b = REG_CFIXB_SEPIA; - break; - } - - ret = m5mols_write(sd, PARM_EFFECT, p_effect); - if (!ret) - ret = m5mols_write(sd, MON_EFFECT, m_effect); - - if (ret == 0 && m_effect == REG_COLOR_EFFECT_ON) { - ret = m5mols_write(sd, MON_CFIXR, cfix_r); - if (!ret) - ret = m5mols_write(sd, MON_CFIXB, cfix_b); - } - - v4l2_dbg(1, m5mols_debug, sd, - "p_effect: %#x, m_effect: %#x, r: %#x, b: %#x (%d)\n", - p_effect, m_effect, cfix_r, cfix_b, ret); - - return ret; -} - -static int m5mols_set_iso(struct m5mols_info *info, int auto_iso) -{ - u32 iso = auto_iso ? 0 : info->iso->val + 1; - - return m5mols_write(&info->sd, AE_ISO, iso); -} - -static int m5mols_set_wdr(struct m5mols_info *info, int wdr) -{ - int ret; - - ret = m5mols_write(&info->sd, MON_TONE_CTL, wdr ? 9 : 5); - if (ret < 0) - return ret; - - ret = m5mols_set_mode(info, REG_CAPTURE); - if (ret < 0) - return ret; - - return m5mols_write(&info->sd, CAPP_WDR_EN, wdr); -} - -static int m5mols_set_stabilization(struct m5mols_info *info, int val) -{ - struct v4l2_subdev *sd = &info->sd; - unsigned int evp = val ? 0xe : 0x0; - int ret; - - ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, evp); - if (ret < 0) - return ret; - - return m5mols_write(sd, AE_EV_PRESET_CAPTURE, evp); -} - -static int m5mols_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - struct m5mols_info *info = to_m5mols(sd); - int ret = 0; - u8 status = REG_ISO_AUTO; - - v4l2_dbg(1, m5mols_debug, sd, "%s: ctrl: %s (%d)\n", - __func__, ctrl->name, info->isp_ready); - - if (!info->isp_ready) - return -EBUSY; - - switch (ctrl->id) { - case V4L2_CID_ISO_SENSITIVITY_AUTO: - ret = m5mols_read_u8(sd, AE_ISO, &status); - if (ret == 0) - ctrl->val = !status; - if (status != REG_ISO_AUTO) - info->iso->val = status - 1; - break; - - case V4L2_CID_3A_LOCK: - ctrl->val &= ~0x7; - - ret = m5mols_read_u8(sd, AE_LOCK, &status); - if (ret) - return ret; - if (status) - info->lock_3a->val |= V4L2_LOCK_EXPOSURE; - - ret = m5mols_read_u8(sd, AWB_LOCK, &status); - if (ret) - return ret; - if (status) - info->lock_3a->val |= V4L2_LOCK_EXPOSURE; - - ret = m5mols_read_u8(sd, AF_EXECUTE, &status); - if (!status) - info->lock_3a->val |= V4L2_LOCK_EXPOSURE; - break; - } - - return ret; -} - -static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl) -{ - unsigned int ctrl_mode = m5mols_get_ctrl_mode(ctrl); - struct v4l2_subdev *sd = to_sd(ctrl); - struct m5mols_info *info = to_m5mols(sd); - int last_mode = info->mode; - int ret = 0; - - /* - * If needed, defer restoring the controls until - * the device is fully initialized. - */ - if (!info->isp_ready) { - info->ctrl_sync = 0; - return 0; - } - - v4l2_dbg(1, m5mols_debug, sd, "%s: %s, val: %d, priv: %p\n", - __func__, ctrl->name, ctrl->val, ctrl->priv); - - if (ctrl_mode && ctrl_mode != info->mode) { - ret = m5mols_set_mode(info, ctrl_mode); - if (ret < 0) - return ret; - } - - switch (ctrl->id) { - case V4L2_CID_3A_LOCK: - ret = m5mols_3a_lock(info, ctrl); - break; - - case V4L2_CID_ZOOM_ABSOLUTE: - ret = m5mols_write(sd, MON_ZOOM, ctrl->val); - break; - - case V4L2_CID_EXPOSURE_AUTO: - ret = m5mols_set_exposure(info, ctrl->val); - break; - - case V4L2_CID_ISO_SENSITIVITY: - ret = m5mols_set_iso(info, ctrl->val); - break; - - case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: - ret = m5mols_set_white_balance(info, ctrl->val); - break; - - case V4L2_CID_SATURATION: - ret = m5mols_set_saturation(info, ctrl->val); - break; - - case V4L2_CID_COLORFX: - ret = m5mols_set_color_effect(info, ctrl->val); - break; - - case V4L2_CID_WIDE_DYNAMIC_RANGE: - ret = m5mols_set_wdr(info, ctrl->val); - break; - - case V4L2_CID_IMAGE_STABILIZATION: - ret = m5mols_set_stabilization(info, ctrl->val); - break; - - case V4L2_CID_JPEG_COMPRESSION_QUALITY: - ret = m5mols_write(sd, CAPP_JPEG_RATIO, ctrl->val); - break; - } - - if (ret == 0 && info->mode != last_mode) - ret = m5mols_set_mode(info, last_mode); - - return ret; -} - -static const struct v4l2_ctrl_ops m5mols_ctrl_ops = { - .g_volatile_ctrl = m5mols_g_volatile_ctrl, - .s_ctrl = m5mols_s_ctrl, -}; - -/* Supported manual ISO values */ -static const s64 iso_qmenu[] = { - /* AE_ISO: 0x01...0x07 (ISO: 50...3200) */ - 50000, 100000, 200000, 400000, 800000, 1600000, 3200000 -}; - -/* Supported Exposure Bias values, -2.0EV...+2.0EV */ -static const s64 ev_bias_qmenu[] = { - /* AE_INDEX: 0x00...0x08 */ - -2000, -1500, -1000, -500, 0, 500, 1000, 1500, 2000 -}; - -int m5mols_init_controls(struct v4l2_subdev *sd) -{ - struct m5mols_info *info = to_m5mols(sd); - u16 exposure_max; - u16 zoom_step; - int ret; - - /* Determine the firmware dependent control range and step values */ - ret = m5mols_read_u16(sd, AE_MAX_GAIN_MON, &exposure_max); - if (ret < 0) - return ret; - - zoom_step = is_manufacturer(info, REG_SAMSUNG_OPTICS) ? 31 : 1; - v4l2_ctrl_handler_init(&info->handle, 20); - - info->auto_wb = v4l2_ctrl_new_std_menu(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, - 9, ~0x3fe, V4L2_WHITE_BALANCE_AUTO); - - /* Exposure control cluster */ - info->auto_exposure = v4l2_ctrl_new_std_menu(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, - 1, ~0x03, V4L2_EXPOSURE_AUTO); - - info->exposure = v4l2_ctrl_new_std(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_EXPOSURE, - 0, exposure_max, 1, exposure_max / 2); - - info->exposure_bias = v4l2_ctrl_new_int_menu(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_AUTO_EXPOSURE_BIAS, - ARRAY_SIZE(ev_bias_qmenu) - 1, - ARRAY_SIZE(ev_bias_qmenu)/2 - 1, - ev_bias_qmenu); - - info->metering = v4l2_ctrl_new_std_menu(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_METERING, - 2, ~0x7, V4L2_EXPOSURE_METERING_AVERAGE); - - /* ISO control cluster */ - info->auto_iso = v4l2_ctrl_new_std_menu(&info->handle, &m5mols_ctrl_ops, - V4L2_CID_ISO_SENSITIVITY_AUTO, 1, ~0x03, 1); - - info->iso = v4l2_ctrl_new_int_menu(&info->handle, &m5mols_ctrl_ops, - V4L2_CID_ISO_SENSITIVITY, ARRAY_SIZE(iso_qmenu) - 1, - ARRAY_SIZE(iso_qmenu)/2 - 1, iso_qmenu); - - info->saturation = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, - V4L2_CID_SATURATION, 1, 5, 1, 3); - - info->zoom = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, - V4L2_CID_ZOOM_ABSOLUTE, 1, 70, zoom_step, 1); - - info->colorfx = v4l2_ctrl_new_std_menu(&info->handle, &m5mols_ctrl_ops, - V4L2_CID_COLORFX, 4, 0, V4L2_COLORFX_NONE); - - info->wdr = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, - V4L2_CID_WIDE_DYNAMIC_RANGE, 0, 1, 1, 0); - - info->stabilization = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, - V4L2_CID_IMAGE_STABILIZATION, 0, 1, 1, 0); - - info->jpeg_quality = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, - V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, 100, 1, 80); - - info->lock_3a = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, - V4L2_CID_3A_LOCK, 0, 0x7, 0, 0); - - if (info->handle.error) { - int ret = info->handle.error; - v4l2_err(sd, "Failed to initialize controls: %d\n", ret); - v4l2_ctrl_handler_free(&info->handle); - return ret; - } - - v4l2_ctrl_auto_cluster(4, &info->auto_exposure, 1, false); - info->auto_iso->flags |= V4L2_CTRL_FLAG_VOLATILE | - V4L2_CTRL_FLAG_UPDATE; - v4l2_ctrl_auto_cluster(2, &info->auto_iso, 0, false); - - info->lock_3a->flags |= V4L2_CTRL_FLAG_VOLATILE; - - m5mols_set_ctrl_mode(info->auto_exposure, REG_PARAMETER); - m5mols_set_ctrl_mode(info->auto_wb, REG_PARAMETER); - m5mols_set_ctrl_mode(info->colorfx, REG_MONITOR); - - sd->ctrl_handler = &info->handle; - - return 0; -} diff --git a/drivers/media/i2c/m5mols/m5mols_core.c b/drivers/media/i2c/m5mols/m5mols_core.c deleted file mode 100644 index 5c2336f318d9..000000000000 --- a/drivers/media/i2c/m5mols/m5mols_core.c +++ /dev/null @@ -1,1051 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Driver for M-5MOLS 8M Pixel camera sensor with ISP - * - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - * Author: HeungJun Kim <riverful.kim@samsung.com> - * - * Copyright (C) 2009 Samsung Electronics Co., Ltd. - * Author: Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com> - */ - -#include <linux/i2c.h> -#include <linux/slab.h> -#include <linux/irq.h> -#include <linux/interrupt.h> -#include <linux/delay.h> -#include <linux/gpio/consumer.h> -#include <linux/regulator/consumer.h> -#include <linux/videodev2.h> -#include <linux/module.h> -#include <media/v4l2-ctrls.h> -#include <media/v4l2-device.h> -#include <media/v4l2-subdev.h> -#include <media/i2c/m5mols.h> - -#include "m5mols.h" -#include "m5mols_reg.h" - -int m5mols_debug; -module_param(m5mols_debug, int, 0644); - -#define MODULE_NAME "M5MOLS" -#define M5MOLS_I2C_CHECK_RETRY 500 - -/* The regulator consumer names for external voltage regulators */ -static struct regulator_bulk_data supplies[] = { - { - .supply = "core", /* ARM core power, 1.2V */ - }, { - .supply = "dig_18", /* digital power 1, 1.8V */ - }, { - .supply = "d_sensor", /* sensor power 1, 1.8V */ - }, { - .supply = "dig_28", /* digital power 2, 2.8V */ - }, { - .supply = "a_sensor", /* analog power */ - }, { - .supply = "dig_12", /* digital power 3, 1.2V */ - }, -}; - -static struct v4l2_mbus_framefmt m5mols_default_ffmt[M5MOLS_RESTYPE_MAX] = { - [M5MOLS_RESTYPE_MONITOR] = { - .width = 1920, - .height = 1080, - .code = MEDIA_BUS_FMT_VYUY8_2X8, - .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_JPEG, - }, - [M5MOLS_RESTYPE_CAPTURE] = { - .width = 1920, - .height = 1080, - .code = MEDIA_BUS_FMT_JPEG_1X8, - .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_JPEG, - }, -}; -#define SIZE_DEFAULT_FFMT ARRAY_SIZE(m5mols_default_ffmt) - -static const struct m5mols_resolution m5mols_reg_res[] = { - { 0x01, M5MOLS_RESTYPE_MONITOR, 128, 96 }, /* SUB-QCIF */ - { 0x03, M5MOLS_RESTYPE_MONITOR, 160, 120 }, /* QQVGA */ - { 0x05, M5MOLS_RESTYPE_MONITOR, 176, 144 }, /* QCIF */ - { 0x06, M5MOLS_RESTYPE_MONITOR, 176, 176 }, - { 0x08, M5MOLS_RESTYPE_MONITOR, 240, 320 }, /* QVGA */ - { 0x09, M5MOLS_RESTYPE_MONITOR, 320, 240 }, /* QVGA */ - { 0x0c, M5MOLS_RESTYPE_MONITOR, 240, 400 }, /* WQVGA */ - { 0x0d, M5MOLS_RESTYPE_MONITOR, 400, 240 }, /* WQVGA */ - { 0x0e, M5MOLS_RESTYPE_MONITOR, 352, 288 }, /* CIF */ - { 0x13, M5MOLS_RESTYPE_MONITOR, 480, 360 }, - { 0x15, M5MOLS_RESTYPE_MONITOR, 640, 360 }, /* qHD */ - { 0x17, M5MOLS_RESTYPE_MONITOR, 640, 480 }, /* VGA */ - { 0x18, M5MOLS_RESTYPE_MONITOR, 720, 480 }, - { 0x1a, M5MOLS_RESTYPE_MONITOR, 800, 480 }, /* WVGA */ - { 0x1f, M5MOLS_RESTYPE_MONITOR, 800, 600 }, /* SVGA */ - { 0x21, M5MOLS_RESTYPE_MONITOR, 1280, 720 }, /* HD */ - { 0x25, M5MOLS_RESTYPE_MONITOR, 1920, 1080 }, /* 1080p */ - { 0x29, M5MOLS_RESTYPE_MONITOR, 3264, 2448 }, /* 2.63fps 8M */ - { 0x39, M5MOLS_RESTYPE_MONITOR, 800, 602 }, /* AHS_MON debug */ - - { 0x02, M5MOLS_RESTYPE_CAPTURE, 320, 240 }, /* QVGA */ - { 0x04, M5MOLS_RESTYPE_CAPTURE, 400, 240 }, /* WQVGA */ - { 0x07, M5MOLS_RESTYPE_CAPTURE, 480, 360 }, - { 0x08, M5MOLS_RESTYPE_CAPTURE, 640, 360 }, /* qHD */ - { 0x09, M5MOLS_RESTYPE_CAPTURE, 640, 480 }, /* VGA */ - { 0x0a, M5MOLS_RESTYPE_CAPTURE, 800, 480 }, /* WVGA */ - { 0x10, M5MOLS_RESTYPE_CAPTURE, 1280, 720 }, /* HD */ - { 0x14, M5MOLS_RESTYPE_CAPTURE, 1280, 960 }, /* 1M */ - { 0x17, M5MOLS_RESTYPE_CAPTURE, 1600, 1200 }, /* 2M */ - { 0x19, M5MOLS_RESTYPE_CAPTURE, 1920, 1080 }, /* Full-HD */ - { 0x1a, M5MOLS_RESTYPE_CAPTURE, 2048, 1152 }, /* 3Mega */ - { 0x1b, M5MOLS_RESTYPE_CAPTURE, 2048, 1536 }, - { 0x1c, M5MOLS_RESTYPE_CAPTURE, 2560, 1440 }, /* 4Mega */ - { 0x1d, M5MOLS_RESTYPE_CAPTURE, 2560, 1536 }, - { 0x1f, M5MOLS_RESTYPE_CAPTURE, 2560, 1920 }, /* 5Mega */ - { 0x21, M5MOLS_RESTYPE_CAPTURE, 3264, 1836 }, /* 6Mega */ - { 0x22, M5MOLS_RESTYPE_CAPTURE, 3264, 1960 }, - { 0x25, M5MOLS_RESTYPE_CAPTURE, 3264, 2448 }, /* 8Mega */ -}; - -/** - * m5mols_swap_byte - an byte array to integer conversion function - * @data: byte array - * @length: size in bytes of I2C packet defined in the M-5MOLS datasheet - * - * Convert I2C data byte array with performing any required byte - * reordering to assure proper values for each data type, regardless - * of the architecture endianness. - */ -static u32 m5mols_swap_byte(u8 *data, u8 length) -{ - if (length == 1) - return *data; - else if (length == 2) - return be16_to_cpu(*((__be16 *)data)); - else - return be32_to_cpu(*((__be32 *)data)); -} - -/** - * m5mols_read - I2C read function - * @sd: sub-device, as pointed by struct v4l2_subdev - * @size: desired size of I2C packet - * @reg: combination of size, category and command for the I2C packet - * @val: read value - * - * Returns 0 on success, or else negative errno. - */ -static int m5mols_read(struct v4l2_subdev *sd, u32 size, u32 reg, u32 *val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct m5mols_info *info = to_m5mols(sd); - u8 rbuf[M5MOLS_I2C_MAX_SIZE + 1]; - u8 category = I2C_CATEGORY(reg); - u8 cmd = I2C_COMMAND(reg); - struct i2c_msg msg[2]; - u8 wbuf[5]; - int ret; - - if (!client->adapter) - return -ENODEV; - - msg[0].addr = client->addr; - msg[0].flags = 0; - msg[0].len = 5; - msg[0].buf = wbuf; - wbuf[0] = 5; - wbuf[1] = M5MOLS_BYTE_READ; - wbuf[2] = category; - wbuf[3] = cmd; - wbuf[4] = size; - - msg[1].addr = client->addr; - msg[1].flags = I2C_M_RD; - msg[1].len = size + 1; - msg[1].buf = rbuf; - - /* minimum stabilization time */ - usleep_range(200, 300); - - ret = i2c_transfer(client->adapter, msg, 2); - - if (ret == 2) { - *val = m5mols_swap_byte(&rbuf[1], size); - return 0; - } - - if (info->isp_ready) - v4l2_err(sd, "read failed: size:%d cat:%02x cmd:%02x. %d\n", - size, category, cmd, ret); - - return ret < 0 ? ret : -EIO; -} - -int m5mols_read_u8(struct v4l2_subdev *sd, u32 reg, u8 *val) -{ - u32 val_32; - int ret; - - if (I2C_SIZE(reg) != 1) { - v4l2_err(sd, "Wrong data size\n"); - return -EINVAL; - } - - ret = m5mols_read(sd, I2C_SIZE(reg), reg, &val_32); - if (ret) - return ret; - - *val = (u8)val_32; - return ret; -} - -int m5mols_read_u16(struct v4l2_subdev *sd, u32 reg, u16 *val) -{ - u32 val_32; - int ret; - - if (I2C_SIZE(reg) != 2) { - v4l2_err(sd, "Wrong data size\n"); - return -EINVAL; - } - - ret = m5mols_read(sd, I2C_SIZE(reg), reg, &val_32); - if (ret) - return ret; - - *val = (u16)val_32; - return ret; -} - -int m5mols_read_u32(struct v4l2_subdev *sd, u32 reg, u32 *val) -{ - if (I2C_SIZE(reg) != 4) { - v4l2_err(sd, "Wrong data size\n"); - return -EINVAL; - } - - return m5mols_read(sd, I2C_SIZE(reg), reg, val); -} - -/** - * m5mols_write - I2C command write function - * @sd: sub-device, as pointed by struct v4l2_subdev - * @reg: combination of size, category and command for the I2C packet - * @val: value to write - * - * Returns 0 on success, or else negative errno. - */ -int m5mols_write(struct v4l2_subdev *sd, u32 reg, u32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct m5mols_info *info = to_m5mols(sd); - u8 wbuf[M5MOLS_I2C_MAX_SIZE + 4]; - u8 category = I2C_CATEGORY(reg); - u8 cmd = I2C_COMMAND(reg); - u8 size = I2C_SIZE(reg); - u32 *buf = (u32 *)&wbuf[4]; - struct i2c_msg msg[1]; - int ret; - - if (!client->adapter) - return -ENODEV; - - if (size != 1 && size != 2 && size != 4) { - v4l2_err(sd, "Wrong data size\n"); - return -EINVAL; - } - - msg->addr = client->addr; - msg->flags = 0; - msg->len = (u16)size + 4; - msg->buf = wbuf; - wbuf[0] = size + 4; - wbuf[1] = M5MOLS_BYTE_WRITE; - wbuf[2] = category; - wbuf[3] = cmd; - - *buf = m5mols_swap_byte((u8 *)&val, size); - - /* minimum stabilization time */ - usleep_range(200, 300); - - ret = i2c_transfer(client->adapter, msg, 1); - if (ret == 1) - return 0; - - if (info->isp_ready) - v4l2_err(sd, "write failed: cat:%02x cmd:%02x ret:%d\n", - category, cmd, ret); - - return ret < 0 ? ret : -EIO; -} - -/** - * m5mols_busy_wait - Busy waiting with I2C register polling - * @sd: sub-device, as pointed by struct v4l2_subdev - * @reg: the I2C_REG() address of an 8-bit status register to check - * @value: expected status register value - * @mask: bit mask for the read status register value - * @timeout: timeout in milliseconds, or -1 for default timeout - * - * The @reg register value is ORed with @mask before comparing with @value. - * - * Return: 0 if the requested condition became true within less than - * @timeout ms, or else negative errno. - */ -int m5mols_busy_wait(struct v4l2_subdev *sd, u32 reg, u32 value, u32 mask, - int timeout) -{ - int ms = timeout < 0 ? M5MOLS_BUSY_WAIT_DEF_TIMEOUT : timeout; - unsigned long end = jiffies + msecs_to_jiffies(ms); - u8 status; - - do { - int ret = m5mols_read_u8(sd, reg, &status); - - if (ret < 0 && !(mask & M5MOLS_I2C_RDY_WAIT_FL)) - return ret; - if (!ret && (status & mask & 0xff) == (value & 0xff)) - return 0; - usleep_range(100, 250); - } while (ms > 0 && time_is_after_jiffies(end)); - - return -EBUSY; -} - -/** - * m5mols_enable_interrupt - Clear interrupt pending bits and unmask interrupts - * @sd: sub-device, as pointed by struct v4l2_subdev - * @reg: combination of size, category and command for the I2C packet - * - * Before writing desired interrupt value the INT_FACTOR register should - * be read to clear pending interrupts. - */ -int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg) -{ - struct m5mols_info *info = to_m5mols(sd); - u8 mask = is_available_af(info) ? REG_INT_AF : 0; - u8 dummy; - int ret; - - ret = m5mols_read_u8(sd, SYSTEM_INT_FACTOR, &dummy); - if (!ret) - ret = m5mols_write(sd, SYSTEM_INT_ENABLE, reg & ~mask); - return ret; -} - -int m5mols_wait_interrupt(struct v4l2_subdev *sd, u8 irq_mask, u32 timeout) -{ - struct m5mols_info *info = to_m5mols(sd); - - int ret = wait_event_interruptible_timeout(info->irq_waitq, - atomic_add_unless(&info->irq_done, -1, 0), - msecs_to_jiffies(timeout)); - if (ret <= 0) - return ret ? ret : -ETIMEDOUT; - - return m5mols_busy_wait(sd, SYSTEM_INT_FACTOR, irq_mask, - M5MOLS_I2C_RDY_WAIT_FL | irq_mask, -1); -} - -/** - * m5mols_reg_mode - Write the mode and check busy status - * @sd: sub-device, as pointed by struct v4l2_subdev - * @mode: the required operation mode - * - * It always accompanies a little delay changing the M-5MOLS mode, so it is - * needed checking current busy status to guarantee right mode. - */ -static int m5mols_reg_mode(struct v4l2_subdev *sd, u8 mode) -{ - int ret = m5mols_write(sd, SYSTEM_SYSMODE, mode); - if (ret < 0) - return ret; - return m5mols_busy_wait(sd, SYSTEM_SYSMODE, mode, 0xff, - M5MOLS_MODE_CHANGE_TIMEOUT); -} - -/** - * m5mols_set_mode - set the M-5MOLS controller mode - * @info: M-5MOLS driver data structure - * @mode: the required operation mode - * - * The commands of M-5MOLS are grouped into specific modes. Each functionality - * can be guaranteed only when the sensor is operating in mode which a command - * belongs to. - */ -int m5mols_set_mode(struct m5mols_info *info, u8 mode) -{ - struct v4l2_subdev *sd = &info->sd; - int ret = -EINVAL; - u8 reg; - - if (mode < REG_PARAMETER || mode > REG_CAPTURE) - return ret; - - ret = m5mols_read_u8(sd, SYSTEM_SYSMODE, ®); - if (ret || reg == mode) - return ret; - - switch (reg) { - case REG_PARAMETER: - ret = m5mols_reg_mode(sd, REG_MONITOR); - if (mode == REG_MONITOR) - break; - if (!ret) - ret = m5mols_reg_mode(sd, REG_CAPTURE); - break; - - case REG_MONITOR: - if (mode == REG_PARAMETER) { - ret = m5mols_reg_mode(sd, REG_PARAMETER); - break; - } - - ret = m5mols_reg_mode(sd, REG_CAPTURE); - break; - - case REG_CAPTURE: - ret = m5mols_reg_mode(sd, REG_MONITOR); - if (mode == REG_MONITOR) - break; - if (!ret) - ret = m5mols_reg_mode(sd, REG_PARAMETER); - break; - - default: - v4l2_warn(sd, "Wrong mode: %d\n", mode); - } - - if (!ret) - info->mode = mode; - - return ret; -} - -/** - * m5mols_get_version - retrieve full revisions information of M-5MOLS - * @sd: sub-device, as pointed by struct v4l2_subdev - * - * The version information includes revisions of hardware and firmware, - * AutoFocus alghorithm version and the version string. - */ -static int m5mols_get_version(struct v4l2_subdev *sd) -{ - struct m5mols_info *info = to_m5mols(sd); - struct m5mols_version *ver = &info->ver; - u8 *str = ver->str; - int i; - int ret; - - ret = m5mols_read_u8(sd, SYSTEM_VER_CUSTOMER, &ver->customer); - if (!ret) - ret = m5mols_read_u8(sd, SYSTEM_VER_PROJECT, &ver->project); - if (!ret) - ret = m5mols_read_u16(sd, SYSTEM_VER_FIRMWARE, &ver->fw); - if (!ret) - ret = m5mols_read_u16(sd, SYSTEM_VER_HARDWARE, &ver->hw); - if (!ret) - ret = m5mols_read_u16(sd, SYSTEM_VER_PARAMETER, &ver->param); - if (!ret) - ret = m5mols_read_u16(sd, SYSTEM_VER_AWB, &ver->awb); - if (!ret) - ret = m5mols_read_u8(sd, AF_VERSION, &ver->af); - if (ret) - return ret; - - for (i = 0; i < VERSION_STRING_SIZE; i++) { - ret = m5mols_read_u8(sd, SYSTEM_VER_STRING, &str[i]); - if (ret) - return ret; - } - - v4l2_info(sd, "Manufacturer\t[%s]\n", - is_manufacturer(info, REG_SAMSUNG_ELECTRO) ? - "Samsung Electro-Mechanics" : - is_manufacturer(info, REG_SAMSUNG_OPTICS) ? - "Samsung Fiber-Optics" : - is_manufacturer(info, REG_SAMSUNG_TECHWIN) ? - "Samsung Techwin" : "None"); - v4l2_info(sd, "Customer/Project\t[0x%02x/0x%02x]\n", - info->ver.customer, info->ver.project); - - if (!is_available_af(info)) - v4l2_info(sd, "No support Auto Focus on this firmware\n"); - - return ret; -} - -/** - * __find_restype - Lookup M-5MOLS resolution type according to pixel code - * @code: pixel code - */ -static enum m5mols_restype __find_restype(u32 code) -{ - enum m5mols_restype type = M5MOLS_RESTYPE_MONITOR; - - do { - if (code == m5mols_default_ffmt[type].code) - return type; - } while (++type != SIZE_DEFAULT_FFMT); - - return 0; -} - -/** - * __find_resolution - Lookup preset and type of M-5MOLS's resolution - * @sd: sub-device, as pointed by struct v4l2_subdev - * @mf: pixel format to find/negotiate the resolution preset for - * @type: M-5MOLS resolution type - * @resolution: M-5MOLS resolution preset register value - * - * Find nearest resolution matching resolution preset and adjust mf - * to supported values. - */ -static int __find_resolution(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf, - enum m5mols_restype *type, - u32 *resolution) -{ - const struct m5mols_resolution *fsize = &m5mols_reg_res[0]; - const struct m5mols_resolution *match = NULL; - enum m5mols_restype stype = __find_restype(mf->code); - int i = ARRAY_SIZE(m5mols_reg_res); - unsigned int min_err = ~0; - - while (i--) { - int err; - if (stype == fsize->type) { - err = abs(fsize->width - mf->width) - + abs(fsize->height - mf->height); - - if (err < min_err) { - min_err = err; - match = fsize; - } - } - fsize++; - } - if (match) { - mf->width = match->width; - mf->height = match->height; - *resolution = match->reg; - *type = stype; - return 0; - } - - return -EINVAL; -} - -static struct v4l2_mbus_framefmt *__find_format(struct m5mols_info *info, - struct v4l2_subdev_state *sd_state, - enum v4l2_subdev_format_whence which, - enum m5mols_restype type) -{ - if (which == V4L2_SUBDEV_FORMAT_TRY) - return sd_state ? v4l2_subdev_get_try_format(&info->sd, - sd_state, 0) : NULL; - - return &info->ffmt[type]; -} - -static int m5mols_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct m5mols_info *info = to_m5mols(sd); - struct v4l2_mbus_framefmt *format; - int ret = 0; - - mutex_lock(&info->lock); - - format = __find_format(info, sd_state, fmt->which, info->res_type); - if (format) - fmt->format = *format; - else - ret = -EINVAL; - - mutex_unlock(&info->lock); - return ret; -} - -static int m5mols_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct m5mols_info *info = to_m5mols(sd); - struct v4l2_mbus_framefmt *format = &fmt->format; - struct v4l2_mbus_framefmt *sfmt; - enum m5mols_restype type; - u32 resolution = 0; - int ret; - - ret = __find_resolution(sd, format, &type, &resolution); - if (ret < 0) - return ret; - - sfmt = __find_format(info, sd_state, fmt->which, type); - if (!sfmt) - return 0; - - mutex_lock(&info->lock); - - format->code = m5mols_default_ffmt[type].code; - format->colorspace = V4L2_COLORSPACE_JPEG; - format->field = V4L2_FIELD_NONE; - - if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - *sfmt = *format; - info->resolution = resolution; - info->res_type = type; - } - - mutex_unlock(&info->lock); - return ret; -} - -static int m5mols_get_frame_desc(struct v4l2_subdev *sd, unsigned int pad, - struct v4l2_mbus_frame_desc *fd) -{ - struct m5mols_info *info = to_m5mols(sd); - - if (pad != 0 || fd == NULL) - return -EINVAL; - - mutex_lock(&info->lock); - /* - * .get_frame_desc is only used for compressed formats, - * thus we always return the capture frame parameters here. - */ - fd->entry[0].length = info->cap.buf_size; - fd->entry[0].pixelcode = info->ffmt[M5MOLS_RESTYPE_CAPTURE].code; - mutex_unlock(&info->lock); - - fd->entry[0].flags = V4L2_MBUS_FRAME_DESC_FL_LEN_MAX; - fd->num_entries = 1; - - return 0; -} - -static int m5mols_set_frame_desc(struct v4l2_subdev *sd, unsigned int pad, - struct v4l2_mbus_frame_desc *fd) -{ - struct m5mols_info *info = to_m5mols(sd); - struct v4l2_mbus_framefmt *mf = &info->ffmt[M5MOLS_RESTYPE_CAPTURE]; - - if (pad != 0 || fd == NULL) - return -EINVAL; - - fd->entry[0].flags = V4L2_MBUS_FRAME_DESC_FL_LEN_MAX; - fd->num_entries = 1; - fd->entry[0].length = clamp_t(u32, fd->entry[0].length, - mf->width * mf->height, - M5MOLS_MAIN_JPEG_SIZE_MAX); - mutex_lock(&info->lock); - info->cap.buf_size = fd->entry[0].length; - mutex_unlock(&info->lock); - - return 0; -} - - -static int m5mols_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (!code || code->index >= SIZE_DEFAULT_FFMT) - return -EINVAL; - - code->code = m5mols_default_ffmt[code->index].code; - - return 0; -} - -static const struct v4l2_subdev_pad_ops m5mols_pad_ops = { - .enum_mbus_code = m5mols_enum_mbus_code, - .get_fmt = m5mols_get_fmt, - .set_fmt = m5mols_set_fmt, - .get_frame_desc = m5mols_get_frame_desc, - .set_frame_desc = m5mols_set_frame_desc, -}; - -/** - * m5mols_restore_controls - Apply current control values to the registers - * @info: M-5MOLS driver data structure - * - * m5mols_do_scenemode() handles all parameters for which there is yet no - * individual control. It should be replaced at some point by setting each - * control individually, in required register set up order. - */ -int m5mols_restore_controls(struct m5mols_info *info) -{ - int ret; - - if (info->ctrl_sync) - return 0; - - ret = m5mols_do_scenemode(info, REG_SCENE_NORMAL); - if (ret) - return ret; - - ret = v4l2_ctrl_handler_setup(&info->handle); - info->ctrl_sync = !ret; - - return ret; -} - -/** - * m5mols_start_monitor - Start the monitor mode - * @info: M-5MOLS driver data structure - * - * Before applying the controls setup the resolution and frame rate - * in PARAMETER mode, and then switch over to MONITOR mode. - */ -static int m5mols_start_monitor(struct m5mols_info *info) -{ - struct v4l2_subdev *sd = &info->sd; - int ret; - - ret = m5mols_set_mode(info, REG_PARAMETER); - if (!ret) - ret = m5mols_write(sd, PARM_MON_SIZE, info->resolution); - if (!ret) - ret = m5mols_write(sd, PARM_MON_FPS, REG_FPS_30); - if (!ret) - ret = m5mols_set_mode(info, REG_MONITOR); - if (!ret) - ret = m5mols_restore_controls(info); - - return ret; -} - -static int m5mols_s_stream(struct v4l2_subdev *sd, int enable) -{ - struct m5mols_info *info = to_m5mols(sd); - u32 code; - int ret; - - mutex_lock(&info->lock); - code = info->ffmt[info->res_type].code; - - if (enable) { - if (is_code(code, M5MOLS_RESTYPE_MONITOR)) - ret = m5mols_start_monitor(info); - else if (is_code(code, M5MOLS_RESTYPE_CAPTURE)) - ret = m5mols_start_capture(info); - else - ret = -EINVAL; - } else { - ret = m5mols_set_mode(info, REG_PARAMETER); - } - - mutex_unlock(&info->lock); - return ret; -} - -static const struct v4l2_subdev_video_ops m5mols_video_ops = { - .s_stream = m5mols_s_stream, -}; - -static int m5mols_sensor_power(struct m5mols_info *info, bool enable) -{ - struct v4l2_subdev *sd = &info->sd; - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret; - - if (info->power == enable) - return 0; - - if (enable) { - if (info->set_power) { - ret = info->set_power(&client->dev, 1); - if (ret) - return ret; - } - - ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies); - if (ret) { - if (info->set_power) - info->set_power(&client->dev, 0); - return ret; - } - - gpiod_set_value(info->reset, 0); - info->power = 1; - - return ret; - } - - ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies); - if (ret) - return ret; - - if (info->set_power) - info->set_power(&client->dev, 0); - - gpiod_set_value(info->reset, 1); - - info->isp_ready = 0; - info->power = 0; - - return ret; -} - -/* m5mols_update_fw - optional firmware update routine */ -int __attribute__ ((weak)) m5mols_update_fw(struct v4l2_subdev *sd, - int (*set_power)(struct m5mols_info *, bool)) -{ - return 0; -} - -/** - * m5mols_fw_start - M-5MOLS internal ARM controller initialization - * @sd: sub-device, as pointed by struct v4l2_subdev - * - * Execute the M-5MOLS internal ARM controller initialization sequence. - * This function should be called after the supply voltage has been - * applied and before any requests to the device are made. - */ -static int m5mols_fw_start(struct v4l2_subdev *sd) -{ - struct m5mols_info *info = to_m5mols(sd); - int ret; - - atomic_set(&info->irq_done, 0); - /* Wait until I2C slave is initialized in Flash Writer mode */ - ret = m5mols_busy_wait(sd, FLASH_CAM_START, REG_IN_FLASH_MODE, - M5MOLS_I2C_RDY_WAIT_FL | 0xff, -1); - if (!ret) - ret = m5mols_write(sd, FLASH_CAM_START, REG_START_ARM_BOOT); - if (!ret) - ret = m5mols_wait_interrupt(sd, REG_INT_MODE, 2000); - if (ret < 0) - return ret; - - info->isp_ready = 1; - - ret = m5mols_get_version(sd); - if (!ret) - ret = m5mols_update_fw(sd, m5mols_sensor_power); - if (ret) - return ret; - - v4l2_dbg(1, m5mols_debug, sd, "Success ARM Booting\n"); - - ret = m5mols_write(sd, PARM_INTERFACE, REG_INTERFACE_MIPI); - if (!ret) - ret = m5mols_enable_interrupt(sd, - REG_INT_AF | REG_INT_CAPTURE); - - return ret; -} - -/* Execute the lens soft-landing algorithm */ -static int m5mols_auto_focus_stop(struct m5mols_info *info) -{ - int ret; - - ret = m5mols_write(&info->sd, AF_EXECUTE, REG_AF_STOP); - if (!ret) - ret = m5mols_write(&info->sd, AF_MODE, REG_AF_POWEROFF); - if (!ret) - ret = m5mols_busy_wait(&info->sd, SYSTEM_STATUS, REG_AF_IDLE, - 0xff, -1); - return ret; -} - -/** - * m5mols_s_power - Main sensor power control function - * @sd: sub-device, as pointed by struct v4l2_subdev - * @on: if true, powers on the device; powers off otherwise. - * - * To prevent breaking the lens when the sensor is powered off the Soft-Landing - * algorithm is called where available. The Soft-Landing algorithm availability - * dependends on the firmware provider. - */ -static int m5mols_s_power(struct v4l2_subdev *sd, int on) -{ - struct m5mols_info *info = to_m5mols(sd); - int ret; - - mutex_lock(&info->lock); - - if (on) { - ret = m5mols_sensor_power(info, true); - if (!ret) - ret = m5mols_fw_start(sd); - } else { - if (is_manufacturer(info, REG_SAMSUNG_TECHWIN)) { - ret = m5mols_set_mode(info, REG_MONITOR); - if (!ret) - ret = m5mols_auto_focus_stop(info); - if (ret < 0) - v4l2_warn(sd, "Soft landing lens failed\n"); - } - ret = m5mols_sensor_power(info, false); - - info->ctrl_sync = 0; - } - - mutex_unlock(&info->lock); - return ret; -} - -static int m5mols_log_status(struct v4l2_subdev *sd) -{ - struct m5mols_info *info = to_m5mols(sd); - - v4l2_ctrl_handler_log_status(&info->handle, sd->name); - - return 0; -} - -static const struct v4l2_subdev_core_ops m5mols_core_ops = { - .s_power = m5mols_s_power, - .log_status = m5mols_log_status, -}; - -/* - * V4L2 subdev internal operations - */ -static int m5mols_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(sd, - fh->state, - 0); - - *format = m5mols_default_ffmt[0]; - return 0; -} - -static const struct v4l2_subdev_internal_ops m5mols_subdev_internal_ops = { - .open = m5mols_open, -}; - -static const struct v4l2_subdev_ops m5mols_ops = { - .core = &m5mols_core_ops, - .pad = &m5mols_pad_ops, - .video = &m5mols_video_ops, -}; - -static irqreturn_t m5mols_irq_handler(int irq, void *data) -{ - struct m5mols_info *info = to_m5mols(data); - - atomic_set(&info->irq_done, 1); - wake_up_interruptible(&info->irq_waitq); - - return IRQ_HANDLED; -} - -static int m5mols_probe(struct i2c_client *client) -{ - const struct m5mols_platform_data *pdata = client->dev.platform_data; - struct m5mols_info *info; - struct v4l2_subdev *sd; - int ret; - - if (pdata == NULL) { - dev_err(&client->dev, "No platform data\n"); - return -EINVAL; - } - - if (!client->irq) { - dev_err(&client->dev, "Interrupt not assigned\n"); - return -EINVAL; - } - - info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - /* This asserts reset, descriptor shall have polarity specified */ - info->reset = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_HIGH); - if (IS_ERR(info->reset)) - return PTR_ERR(info->reset); - /* Notice: the "N" in M5MOLS_NRST implies active low */ - gpiod_set_consumer_name(info->reset, "M5MOLS_NRST"); - - info->pdata = pdata; - info->set_power = pdata->set_power; - - ret = devm_regulator_bulk_get(&client->dev, ARRAY_SIZE(supplies), - supplies); - if (ret) { - dev_err(&client->dev, "Failed to get regulators: %d\n", ret); - return ret; - } - - sd = &info->sd; - v4l2_i2c_subdev_init(sd, client, &m5mols_ops); - /* Static name; NEVER use in new drivers! */ - strscpy(sd->name, MODULE_NAME, sizeof(sd->name)); - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - sd->internal_ops = &m5mols_subdev_internal_ops; - info->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_pads_init(&sd->entity, 1, &info->pad); - if (ret < 0) - return ret; - sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; - - init_waitqueue_head(&info->irq_waitq); - mutex_init(&info->lock); - - ret = devm_request_irq(&client->dev, client->irq, m5mols_irq_handler, - IRQF_TRIGGER_RISING, MODULE_NAME, sd); - if (ret) { - dev_err(&client->dev, "Interrupt request failed: %d\n", ret); - goto error; - } - info->res_type = M5MOLS_RESTYPE_MONITOR; - info->ffmt[0] = m5mols_default_ffmt[0]; - info->ffmt[1] = m5mols_default_ffmt[1]; - - ret = m5mols_sensor_power(info, true); - if (ret) - goto error; - - ret = m5mols_fw_start(sd); - if (!ret) - ret = m5mols_init_controls(sd); - - ret = m5mols_sensor_power(info, false); - if (!ret) - return 0; -error: - media_entity_cleanup(&sd->entity); - return ret; -} - -static void m5mols_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(sd->ctrl_handler); - media_entity_cleanup(&sd->entity); -} - -static const struct i2c_device_id m5mols_id[] = { - { MODULE_NAME, 0 }, - { }, -}; -MODULE_DEVICE_TABLE(i2c, m5mols_id); - -static struct i2c_driver m5mols_i2c_driver = { - .driver = { - .name = MODULE_NAME, - }, - .probe_new = m5mols_probe, - .remove = m5mols_remove, - .id_table = m5mols_id, -}; - -module_i2c_driver(m5mols_i2c_driver); - -MODULE_AUTHOR("HeungJun Kim <riverful.kim@samsung.com>"); -MODULE_AUTHOR("Dongsoo Kim <dongsoo45.kim@samsung.com>"); -MODULE_DESCRIPTION("Fujitsu M-5MOLS 8M Pixel camera driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/m5mols/m5mols_reg.h b/drivers/media/i2c/m5mols/m5mols_reg.h deleted file mode 100644 index 947ee33812d3..000000000000 --- a/drivers/media/i2c/m5mols/m5mols_reg.h +++ /dev/null @@ -1,359 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Register map for M-5MOLS 8M Pixel camera sensor with ISP - * - * Copyright (C) 2011 Samsung Electronics Co., Ltd. - * Author: HeungJun Kim <riverful.kim@samsung.com> - * - * Copyright (C) 2009 Samsung Electronics Co., Ltd. - * Author: Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com> - */ - -#ifndef M5MOLS_REG_H -#define M5MOLS_REG_H - -#define M5MOLS_I2C_MAX_SIZE 4 -#define M5MOLS_BYTE_READ 0x01 -#define M5MOLS_BYTE_WRITE 0x02 - -#define I2C_CATEGORY(__cat) ((__cat >> 16) & 0xff) -#define I2C_COMMAND(__comm) ((__comm >> 8) & 0xff) -#define I2C_SIZE(__reg_s) ((__reg_s) & 0xff) -#define I2C_REG(__cat, __cmd, __reg_s) ((__cat << 16) | (__cmd << 8) | __reg_s) - -/* - * Category section register - * - * The category means set including relevant command of M-5MOLS. - */ -#define CAT_SYSTEM 0x00 -#define CAT_PARAM 0x01 -#define CAT_MONITOR 0x02 -#define CAT_AE 0x03 -#define CAT_WB 0x06 -#define CAT_EXIF 0x07 -#define CAT_FD 0x09 -#define CAT_LENS 0x0a -#define CAT_CAPT_PARM 0x0b -#define CAT_CAPT_CTRL 0x0c -#define CAT_FLASH 0x0f /* related to FW, revisions, booting */ - -/* - * Category 0 - SYSTEM mode - * - * The SYSTEM mode in the M-5MOLS means area available to handle with the whole - * & all-round system of sensor. It deals with version/interrupt/setting mode & - * even sensor's status. Especially, the M-5MOLS sensor with ISP varies by - * packaging & manufacturer, even the customer and project code. And the - * function details may vary among them. The version information helps to - * determine what methods shall be used in the driver. - * - * There is many registers between customer version address and awb one. For - * more specific contents, see definition if file m5mols.h. - */ -#define SYSTEM_VER_CUSTOMER I2C_REG(CAT_SYSTEM, 0x00, 1) -#define SYSTEM_VER_PROJECT I2C_REG(CAT_SYSTEM, 0x01, 1) -#define SYSTEM_VER_FIRMWARE I2C_REG(CAT_SYSTEM, 0x02, 2) -#define SYSTEM_VER_HARDWARE I2C_REG(CAT_SYSTEM, 0x04, 2) -#define SYSTEM_VER_PARAMETER I2C_REG(CAT_SYSTEM, 0x06, 2) -#define SYSTEM_VER_AWB I2C_REG(CAT_SYSTEM, 0x08, 2) - -#define SYSTEM_SYSMODE I2C_REG(CAT_SYSTEM, 0x0b, 1) -#define REG_SYSINIT 0x00 /* SYSTEM mode */ -#define REG_PARAMETER 0x01 /* PARAMETER mode */ -#define REG_MONITOR 0x02 /* MONITOR mode */ -#define REG_CAPTURE 0x03 /* CAPTURE mode */ - -#define SYSTEM_CMD(__cmd) I2C_REG(CAT_SYSTEM, cmd, 1) -#define SYSTEM_VER_STRING I2C_REG(CAT_SYSTEM, 0x0a, 1) -#define REG_SAMSUNG_ELECTRO "SE" /* Samsung Electro-Mechanics */ -#define REG_SAMSUNG_OPTICS "OP" /* Samsung Fiber-Optics */ -#define REG_SAMSUNG_TECHWIN "TB" /* Samsung Techwin */ -/* SYSTEM mode status */ -#define SYSTEM_STATUS I2C_REG(CAT_SYSTEM, 0x0c, 1) - -/* Interrupt pending register */ -#define SYSTEM_INT_FACTOR I2C_REG(CAT_SYSTEM, 0x10, 1) -/* interrupt enable register */ -#define SYSTEM_INT_ENABLE I2C_REG(CAT_SYSTEM, 0x11, 1) -#define REG_INT_MODE (1 << 0) -#define REG_INT_AF (1 << 1) -#define REG_INT_ZOOM (1 << 2) -#define REG_INT_CAPTURE (1 << 3) -#define REG_INT_FRAMESYNC (1 << 4) -#define REG_INT_FD (1 << 5) -#define REG_INT_LENS_INIT (1 << 6) -#define REG_INT_SOUND (1 << 7) -#define REG_INT_MASK 0x0f - -/* - * category 1 - PARAMETER mode - * - * This category supports function of camera features of M-5MOLS. It means we - * can handle with preview(MONITOR) resolution size/frame per second/interface - * between the sensor and the Application Processor/even the image effect. - */ - -/* Resolution in the MONITOR mode */ -#define PARM_MON_SIZE I2C_REG(CAT_PARAM, 0x01, 1) - -/* Frame rate */ -#define PARM_MON_FPS I2C_REG(CAT_PARAM, 0x02, 1) -#define REG_FPS_30 0x02 - -/* Video bus between the sensor and a host processor */ -#define PARM_INTERFACE I2C_REG(CAT_PARAM, 0x00, 1) -#define REG_INTERFACE_MIPI 0x02 - -/* Image effects */ -#define PARM_EFFECT I2C_REG(CAT_PARAM, 0x0b, 1) -#define REG_EFFECT_OFF 0x00 -#define REG_EFFECT_NEGA 0x01 -#define REG_EFFECT_EMBOSS 0x06 -#define REG_EFFECT_OUTLINE 0x07 -#define REG_EFFECT_WATERCOLOR 0x08 - -/* - * Category 2 - MONITOR mode - * - * The MONITOR mode is same as preview mode as we said. The M-5MOLS has another - * mode named "Preview", but this preview mode is used at the case specific - * vider-recording mode. This mmode supports only YUYV format. On the other - * hand, the JPEG & RAW formats is supports by CAPTURE mode. And, there are - * another options like zoom/color effect(different with effect in PARAMETER - * mode)/anti hand shaking algorithm. - */ - -/* Target digital zoom position */ -#define MON_ZOOM I2C_REG(CAT_MONITOR, 0x01, 1) - -/* CR value for color effect */ -#define MON_CFIXR I2C_REG(CAT_MONITOR, 0x0a, 1) -/* CB value for color effect */ -#define MON_CFIXB I2C_REG(CAT_MONITOR, 0x09, 1) -#define REG_CFIXB_SEPIA 0xd8 -#define REG_CFIXR_SEPIA 0x18 - -#define MON_EFFECT I2C_REG(CAT_MONITOR, 0x0b, 1) -#define REG_COLOR_EFFECT_OFF 0x00 -#define REG_COLOR_EFFECT_ON 0x01 - -/* Chroma enable */ -#define MON_CHROMA_EN I2C_REG(CAT_MONITOR, 0x10, 1) -/* Chroma level */ -#define MON_CHROMA_LVL I2C_REG(CAT_MONITOR, 0x0f, 1) -#define REG_CHROMA_OFF 0x00 -#define REG_CHROMA_ON 0x01 - -/* Sharpness on/off */ -#define MON_EDGE_EN I2C_REG(CAT_MONITOR, 0x12, 1) -/* Sharpness level */ -#define MON_EDGE_LVL I2C_REG(CAT_MONITOR, 0x11, 1) -#define REG_EDGE_OFF 0x00 -#define REG_EDGE_ON 0x01 - -/* Set color tone (contrast) */ -#define MON_TONE_CTL I2C_REG(CAT_MONITOR, 0x25, 1) - -/* - * Category 3 - Auto Exposure - * - * The M-5MOLS exposure capbility is detailed as which is similar to digital - * camera. This category supports AE locking/various AE mode(range of exposure) - * /ISO/flickering/EV bias/shutter/meteoring, and anything else. And the - * maximum/minimum exposure gain value depending on M-5MOLS firmware, may be - * different. So, this category also provide getting the max/min values. And, - * each MONITOR and CAPTURE mode has each gain/shutter/max exposure values. - */ - -/* Auto Exposure locking */ -#define AE_LOCK I2C_REG(CAT_AE, 0x00, 1) -#define REG_AE_UNLOCK 0x00 -#define REG_AE_LOCK 0x01 - -/* Auto Exposure algorithm mode */ -#define AE_MODE I2C_REG(CAT_AE, 0x01, 1) -#define REG_AE_OFF 0x00 /* AE off */ -#define REG_AE_ALL 0x01 /* calc AE in all block integral */ -#define REG_AE_CENTER 0x03 /* calc AE in center weighted */ -#define REG_AE_SPOT 0x06 /* calc AE in specific spot */ - -#define AE_ISO I2C_REG(CAT_AE, 0x05, 1) -#define REG_ISO_AUTO 0x00 -#define REG_ISO_50 0x01 -#define REG_ISO_100 0x02 -#define REG_ISO_200 0x03 -#define REG_ISO_400 0x04 -#define REG_ISO_800 0x05 - -/* EV (scenemode) preset for MONITOR */ -#define AE_EV_PRESET_MONITOR I2C_REG(CAT_AE, 0x0a, 1) -/* EV (scenemode) preset for CAPTURE */ -#define AE_EV_PRESET_CAPTURE I2C_REG(CAT_AE, 0x0b, 1) -#define REG_SCENE_NORMAL 0x00 -#define REG_SCENE_PORTRAIT 0x01 -#define REG_SCENE_LANDSCAPE 0x02 -#define REG_SCENE_SPORTS 0x03 -#define REG_SCENE_PARTY_INDOOR 0x04 -#define REG_SCENE_BEACH_SNOW 0x05 -#define REG_SCENE_SUNSET 0x06 -#define REG_SCENE_DAWN_DUSK 0x07 -#define REG_SCENE_FALL 0x08 -#define REG_SCENE_NIGHT 0x09 -#define REG_SCENE_AGAINST_LIGHT 0x0a -#define REG_SCENE_FIRE 0x0b -#define REG_SCENE_TEXT 0x0c -#define REG_SCENE_CANDLE 0x0d - -/* Manual gain in MONITOR mode */ -#define AE_MAN_GAIN_MON I2C_REG(CAT_AE, 0x12, 2) -/* Maximum gain in MONITOR mode */ -#define AE_MAX_GAIN_MON I2C_REG(CAT_AE, 0x1a, 2) -/* Manual gain in CAPTURE mode */ -#define AE_MAN_GAIN_CAP I2C_REG(CAT_AE, 0x26, 2) - -#define AE_INDEX I2C_REG(CAT_AE, 0x38, 1) -#define REG_AE_INDEX_20_NEG 0x00 -#define REG_AE_INDEX_15_NEG 0x01 -#define REG_AE_INDEX_10_NEG 0x02 -#define REG_AE_INDEX_05_NEG 0x03 -#define REG_AE_INDEX_00 0x04 -#define REG_AE_INDEX_05_POS 0x05 -#define REG_AE_INDEX_10_POS 0x06 -#define REG_AE_INDEX_15_POS 0x07 -#define REG_AE_INDEX_20_POS 0x08 - -/* - * Category 6 - White Balance - */ - -/* Auto Whitebalance locking */ -#define AWB_LOCK I2C_REG(CAT_WB, 0x00, 1) -#define REG_AWB_UNLOCK 0x00 -#define REG_AWB_LOCK 0x01 - -#define AWB_MODE I2C_REG(CAT_WB, 0x02, 1) -#define REG_AWB_AUTO 0x01 /* AWB off */ -#define REG_AWB_PRESET 0x02 /* AWB preset */ - -/* Manual WB (preset) */ -#define AWB_MANUAL I2C_REG(CAT_WB, 0x03, 1) -#define REG_AWB_INCANDESCENT 0x01 -#define REG_AWB_FLUORESCENT_1 0x02 -#define REG_AWB_FLUORESCENT_2 0x03 -#define REG_AWB_DAYLIGHT 0x04 -#define REG_AWB_CLOUDY 0x05 -#define REG_AWB_SHADE 0x06 -#define REG_AWB_HORIZON 0x07 -#define REG_AWB_LEDLIGHT 0x09 - -/* - * Category 7 - EXIF information - */ -#define EXIF_INFO_EXPTIME_NU I2C_REG(CAT_EXIF, 0x00, 4) -#define EXIF_INFO_EXPTIME_DE I2C_REG(CAT_EXIF, 0x04, 4) -#define EXIF_INFO_TV_NU I2C_REG(CAT_EXIF, 0x08, 4) -#define EXIF_INFO_TV_DE I2C_REG(CAT_EXIF, 0x0c, 4) -#define EXIF_INFO_AV_NU I2C_REG(CAT_EXIF, 0x10, 4) -#define EXIF_INFO_AV_DE I2C_REG(CAT_EXIF, 0x14, 4) -#define EXIF_INFO_BV_NU I2C_REG(CAT_EXIF, 0x18, 4) -#define EXIF_INFO_BV_DE I2C_REG(CAT_EXIF, 0x1c, 4) -#define EXIF_INFO_EBV_NU I2C_REG(CAT_EXIF, 0x20, 4) -#define EXIF_INFO_EBV_DE I2C_REG(CAT_EXIF, 0x24, 4) -#define EXIF_INFO_ISO I2C_REG(CAT_EXIF, 0x28, 2) -#define EXIF_INFO_FLASH I2C_REG(CAT_EXIF, 0x2a, 2) -#define EXIF_INFO_SDR I2C_REG(CAT_EXIF, 0x2c, 2) -#define EXIF_INFO_QVAL I2C_REG(CAT_EXIF, 0x2e, 2) - -/* - * Category 9 - Face Detection - */ -#define FD_CTL I2C_REG(CAT_FD, 0x00, 1) -#define BIT_FD_EN 0 -#define BIT_FD_DRAW_FACE_FRAME 4 -#define BIT_FD_DRAW_SMILE_LVL 6 -#define REG_FD(shift) (1 << shift) -#define REG_FD_OFF 0x0 - -/* - * Category A - Lens Parameter - */ -#define AF_MODE I2C_REG(CAT_LENS, 0x01, 1) -#define REG_AF_NORMAL 0x00 /* Normal AF, one time */ -#define REG_AF_MACRO 0x01 /* Macro AF, one time */ -#define REG_AF_POWEROFF 0x07 - -#define AF_EXECUTE I2C_REG(CAT_LENS, 0x02, 1) -#define REG_AF_STOP 0x00 -#define REG_AF_EXE_AUTO 0x01 -#define REG_AF_EXE_CAF 0x02 - -#define AF_STATUS I2C_REG(CAT_LENS, 0x03, 1) -#define REG_AF_FAIL 0x00 -#define REG_AF_SUCCESS 0x02 -#define REG_AF_IDLE 0x04 -#define REG_AF_BUSY 0x05 - -#define AF_VERSION I2C_REG(CAT_LENS, 0x0a, 1) - -/* - * Category B - CAPTURE Parameter - */ -#define CAPP_YUVOUT_MAIN I2C_REG(CAT_CAPT_PARM, 0x00, 1) -#define REG_YUV422 0x00 -#define REG_BAYER10 0x05 -#define REG_BAYER8 0x06 -#define REG_JPEG 0x10 - -#define CAPP_MAIN_IMAGE_SIZE I2C_REG(CAT_CAPT_PARM, 0x01, 1) -#define CAPP_JPEG_SIZE_MAX I2C_REG(CAT_CAPT_PARM, 0x0f, 4) -#define CAPP_JPEG_RATIO I2C_REG(CAT_CAPT_PARM, 0x17, 1) - -#define CAPP_MCC_MODE I2C_REG(CAT_CAPT_PARM, 0x1d, 1) -#define REG_MCC_OFF 0x00 -#define REG_MCC_NORMAL 0x01 - -#define CAPP_WDR_EN I2C_REG(CAT_CAPT_PARM, 0x2c, 1) -#define REG_WDR_OFF 0x00 -#define REG_WDR_ON 0x01 -#define REG_WDR_AUTO 0x02 - -#define CAPP_LIGHT_CTRL I2C_REG(CAT_CAPT_PARM, 0x40, 1) -#define REG_LIGHT_OFF 0x00 -#define REG_LIGHT_ON 0x01 -#define REG_LIGHT_AUTO 0x02 - -#define CAPP_FLASH_CTRL I2C_REG(CAT_CAPT_PARM, 0x41, 1) -#define REG_FLASH_OFF 0x00 -#define REG_FLASH_ON 0x01 -#define REG_FLASH_AUTO 0x02 - -/* - * Category C - CAPTURE Control - */ -#define CAPC_MODE I2C_REG(CAT_CAPT_CTRL, 0x00, 1) -#define REG_CAP_NONE 0x00 -#define REG_CAP_ANTI_SHAKE 0x02 - -/* Select single- or multi-shot capture */ -#define CAPC_SEL_FRAME I2C_REG(CAT_CAPT_CTRL, 0x06, 1) - -#define CAPC_START I2C_REG(CAT_CAPT_CTRL, 0x09, 1) -#define REG_CAP_START_MAIN 0x01 -#define REG_CAP_START_THUMB 0x03 - -#define CAPC_IMAGE_SIZE I2C_REG(CAT_CAPT_CTRL, 0x0d, 4) -#define CAPC_THUMB_SIZE I2C_REG(CAT_CAPT_CTRL, 0x11, 4) - -/* - * Category F - Flash - * - * This mode provides functions about internal flash stuff and system startup. - */ - -/* Starts internal ARM core booting after power-up */ -#define FLASH_CAM_START I2C_REG(CAT_FLASH, 0x12, 1) -#define REG_START_ARM_BOOT 0x01 /* write value */ -#define REG_IN_FLASH_MODE 0x00 /* read value */ - -#endif /* M5MOLS_REG_H */ diff --git a/drivers/media/i2c/max9286.c b/drivers/media/i2c/max9286.c index 701038d6d19b..13a986b88588 100644 --- a/drivers/media/i2c/max9286.c +++ b/drivers/media/i2c/max9286.c @@ -1122,6 +1122,7 @@ err_async: static void max9286_v4l2_unregister(struct max9286_priv *priv) { fwnode_handle_put(priv->sd.fwnode); + v4l2_ctrl_handler_free(&priv->ctrls); v4l2_async_unregister_subdev(&priv->sd); max9286_v4l2_notifier_unregister(priv); } diff --git a/drivers/media/i2c/mt9m032.c b/drivers/media/i2c/mt9m032.c deleted file mode 100644 index 958cfdd73d57..000000000000 --- a/drivers/media/i2c/mt9m032.c +++ /dev/null @@ -1,891 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Driver for MT9M032 CMOS Image Sensor from Micron - * - * Copyright (C) 2010-2011 Lund Engineering - * Contact: Gil Lund <gwlund@lundeng.com> - * Author: Martin Hostettler <martin@neutronstar.dyndns.org> - */ - -#include <linux/delay.h> -#include <linux/i2c.h> -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/math64.h> -#include <linux/module.h> -#include <linux/mutex.h> -#include <linux/slab.h> -#include <linux/v4l2-mediabus.h> - -#include <media/media-entity.h> -#include <media/i2c/mt9m032.h> -#include <media/v4l2-ctrls.h> -#include <media/v4l2-device.h> -#include <media/v4l2-subdev.h> - -#include "aptina-pll.h" - -/* - * width and height include active boundary and black parts - * - * column 0- 15 active boundary - * column 16-1455 image - * column 1456-1471 active boundary - * column 1472-1599 black - * - * row 0- 51 black - * row 53- 59 active boundary - * row 60-1139 image - * row 1140-1147 active boundary - * row 1148-1151 black - */ - -#define MT9M032_PIXEL_ARRAY_WIDTH 1600 -#define MT9M032_PIXEL_ARRAY_HEIGHT 1152 - -#define MT9M032_CHIP_VERSION 0x00 -#define MT9M032_CHIP_VERSION_VALUE 0x1402 -#define MT9M032_ROW_START 0x01 -#define MT9M032_ROW_START_MIN 0 -#define MT9M032_ROW_START_MAX 1152 -#define MT9M032_ROW_START_DEF 60 -#define MT9M032_COLUMN_START 0x02 -#define MT9M032_COLUMN_START_MIN 0 -#define MT9M032_COLUMN_START_MAX 1600 -#define MT9M032_COLUMN_START_DEF 16 -#define MT9M032_ROW_SIZE 0x03 -#define MT9M032_ROW_SIZE_MIN 32 -#define MT9M032_ROW_SIZE_MAX 1152 -#define MT9M032_ROW_SIZE_DEF 1080 -#define MT9M032_COLUMN_SIZE 0x04 -#define MT9M032_COLUMN_SIZE_MIN 32 -#define MT9M032_COLUMN_SIZE_MAX 1600 -#define MT9M032_COLUMN_SIZE_DEF 1440 -#define MT9M032_HBLANK 0x05 -#define MT9M032_VBLANK 0x06 -#define MT9M032_VBLANK_MAX 0x7ff -#define MT9M032_SHUTTER_WIDTH_HIGH 0x08 -#define MT9M032_SHUTTER_WIDTH_LOW 0x09 -#define MT9M032_SHUTTER_WIDTH_MIN 1 -#define MT9M032_SHUTTER_WIDTH_MAX 1048575 -#define MT9M032_SHUTTER_WIDTH_DEF 1943 -#define MT9M032_PIX_CLK_CTRL 0x0a -#define MT9M032_PIX_CLK_CTRL_INV_PIXCLK 0x8000 -#define MT9M032_RESTART 0x0b -#define MT9M032_RESET 0x0d -#define MT9M032_PLL_CONFIG1 0x11 -#define MT9M032_PLL_CONFIG1_PREDIV_MASK 0x3f -#define MT9M032_PLL_CONFIG1_MUL_SHIFT 8 -#define MT9M032_READ_MODE1 0x1e -#define MT9M032_READ_MODE1_OUTPUT_BAD_FRAMES (1 << 13) -#define MT9M032_READ_MODE1_MAINTAIN_FRAME_RATE (1 << 12) -#define MT9M032_READ_MODE1_XOR_LINE_VALID (1 << 11) -#define MT9M032_READ_MODE1_CONT_LINE_VALID (1 << 10) -#define MT9M032_READ_MODE1_INVERT_TRIGGER (1 << 9) -#define MT9M032_READ_MODE1_SNAPSHOT (1 << 8) -#define MT9M032_READ_MODE1_GLOBAL_RESET (1 << 7) -#define MT9M032_READ_MODE1_BULB_EXPOSURE (1 << 6) -#define MT9M032_READ_MODE1_INVERT_STROBE (1 << 5) -#define MT9M032_READ_MODE1_STROBE_ENABLE (1 << 4) -#define MT9M032_READ_MODE1_STROBE_START_TRIG1 (0 << 2) -#define MT9M032_READ_MODE1_STROBE_START_EXP (1 << 2) -#define MT9M032_READ_MODE1_STROBE_START_SHUTTER (2 << 2) -#define MT9M032_READ_MODE1_STROBE_START_TRIG2 (3 << 2) -#define MT9M032_READ_MODE1_STROBE_END_TRIG1 (0 << 0) -#define MT9M032_READ_MODE1_STROBE_END_EXP (1 << 0) -#define MT9M032_READ_MODE1_STROBE_END_SHUTTER (2 << 0) -#define MT9M032_READ_MODE1_STROBE_END_TRIG2 (3 << 0) -#define MT9M032_READ_MODE2 0x20 -#define MT9M032_READ_MODE2_VFLIP_SHIFT 15 -#define MT9M032_READ_MODE2_HFLIP_SHIFT 14 -#define MT9M032_READ_MODE2_ROW_BLC 0x40 -#define MT9M032_GAIN_GREEN1 0x2b -#define MT9M032_GAIN_BLUE 0x2c -#define MT9M032_GAIN_RED 0x2d -#define MT9M032_GAIN_GREEN2 0x2e - -/* write only */ -#define MT9M032_GAIN_ALL 0x35 -#define MT9M032_GAIN_DIGITAL_MASK 0x7f -#define MT9M032_GAIN_DIGITAL_SHIFT 8 -#define MT9M032_GAIN_AMUL_SHIFT 6 -#define MT9M032_GAIN_ANALOG_MASK 0x3f -#define MT9M032_FORMATTER1 0x9e -#define MT9M032_FORMATTER1_PLL_P1_6 (1 << 8) -#define MT9M032_FORMATTER1_PARALLEL (1 << 12) -#define MT9M032_FORMATTER2 0x9f -#define MT9M032_FORMATTER2_DOUT_EN 0x1000 -#define MT9M032_FORMATTER2_PIXCLK_EN 0x2000 - -/* - * The available MT9M032 datasheet is missing documentation for register 0x10 - * MT9P031 seems to be close enough, so use constants from that datasheet for - * now. - * But keep the name MT9P031 to remind us, that this isn't really confirmed - * for this sensor. - */ -#define MT9P031_PLL_CONTROL 0x10 -#define MT9P031_PLL_CONTROL_PWROFF 0x0050 -#define MT9P031_PLL_CONTROL_PWRON 0x0051 -#define MT9P031_PLL_CONTROL_USEPLL 0x0052 - -struct mt9m032 { - struct v4l2_subdev subdev; - struct media_pad pad; - struct mt9m032_platform_data *pdata; - - unsigned int pix_clock; - - struct v4l2_ctrl_handler ctrls; - struct { - struct v4l2_ctrl *hflip; - struct v4l2_ctrl *vflip; - }; - - struct mutex lock; /* Protects streaming, format, interval and crop */ - - bool streaming; - - struct v4l2_mbus_framefmt format; - struct v4l2_rect crop; - struct v4l2_fract frame_interval; -}; - -#define to_mt9m032(sd) container_of(sd, struct mt9m032, subdev) -#define to_dev(sensor) \ - (&((struct i2c_client *)v4l2_get_subdevdata(&(sensor)->subdev))->dev) - -static int mt9m032_read(struct i2c_client *client, u8 reg) -{ - return i2c_smbus_read_word_swapped(client, reg); -} - -static int mt9m032_write(struct i2c_client *client, u8 reg, const u16 data) -{ - return i2c_smbus_write_word_swapped(client, reg, data); -} - -static u32 mt9m032_row_time(struct mt9m032 *sensor, unsigned int width) -{ - unsigned int effective_width; - u32 ns; - - effective_width = width + 716; /* empirical value */ - ns = div_u64(1000000000ULL * effective_width, sensor->pix_clock); - dev_dbg(to_dev(sensor), "MT9M032 line time: %u ns\n", ns); - return ns; -} - -static int mt9m032_update_timing(struct mt9m032 *sensor, - struct v4l2_fract *interval) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); - struct v4l2_rect *crop = &sensor->crop; - unsigned int min_vblank; - unsigned int vblank; - u32 row_time; - - if (!interval) - interval = &sensor->frame_interval; - - row_time = mt9m032_row_time(sensor, crop->width); - - vblank = div_u64(1000000000ULL * interval->numerator, - (u64)row_time * interval->denominator) - - crop->height; - - if (vblank > MT9M032_VBLANK_MAX) { - /* hardware limits to 11 bit values */ - interval->denominator = 1000; - interval->numerator = - div_u64((crop->height + MT9M032_VBLANK_MAX) * - (u64)row_time * interval->denominator, - 1000000000ULL); - vblank = div_u64(1000000000ULL * interval->numerator, - (u64)row_time * interval->denominator) - - crop->height; - } - /* enforce minimal 1.6ms blanking time. */ - min_vblank = 1600000 / row_time; - vblank = clamp_t(unsigned int, vblank, min_vblank, MT9M032_VBLANK_MAX); - - return mt9m032_write(client, MT9M032_VBLANK, vblank); -} - -static int mt9m032_update_geom_timing(struct mt9m032 *sensor) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); - int ret; - - ret = mt9m032_write(client, MT9M032_COLUMN_SIZE, - sensor->crop.width - 1); - if (!ret) - ret = mt9m032_write(client, MT9M032_ROW_SIZE, - sensor->crop.height - 1); - if (!ret) - ret = mt9m032_write(client, MT9M032_COLUMN_START, - sensor->crop.left); - if (!ret) - ret = mt9m032_write(client, MT9M032_ROW_START, - sensor->crop.top); - if (!ret) - ret = mt9m032_update_timing(sensor, NULL); - return ret; -} - -static int update_formatter2(struct mt9m032 *sensor, bool streaming) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); - u16 reg_val = MT9M032_FORMATTER2_DOUT_EN - | 0x0070; /* parts reserved! */ - /* possibly for changing to 14-bit mode */ - - if (streaming) - reg_val |= MT9M032_FORMATTER2_PIXCLK_EN; /* pixclock enable */ - - return mt9m032_write(client, MT9M032_FORMATTER2, reg_val); -} - -static int mt9m032_setup_pll(struct mt9m032 *sensor) -{ - static const struct aptina_pll_limits limits = { - .ext_clock_min = 8000000, - .ext_clock_max = 16500000, - .int_clock_min = 2000000, - .int_clock_max = 24000000, - .out_clock_min = 322000000, - .out_clock_max = 693000000, - .pix_clock_max = 99000000, - .n_min = 1, - .n_max = 64, - .m_min = 16, - .m_max = 255, - .p1_min = 6, - .p1_max = 7, - }; - - struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); - struct mt9m032_platform_data *pdata = sensor->pdata; - struct aptina_pll pll; - u16 reg_val; - int ret; - - pll.ext_clock = pdata->ext_clock; - pll.pix_clock = pdata->pix_clock; - - ret = aptina_pll_calculate(&client->dev, &limits, &pll); - if (ret < 0) - return ret; - - sensor->pix_clock = pdata->pix_clock; - - ret = mt9m032_write(client, MT9M032_PLL_CONFIG1, - (pll.m << MT9M032_PLL_CONFIG1_MUL_SHIFT) | - ((pll.n - 1) & MT9M032_PLL_CONFIG1_PREDIV_MASK)); - if (!ret) - ret = mt9m032_write(client, MT9P031_PLL_CONTROL, - MT9P031_PLL_CONTROL_PWRON | - MT9P031_PLL_CONTROL_USEPLL); - if (!ret) /* more reserved, Continuous, Master Mode */ - ret = mt9m032_write(client, MT9M032_READ_MODE1, 0x8000 | - MT9M032_READ_MODE1_STROBE_START_EXP | - MT9M032_READ_MODE1_STROBE_END_SHUTTER); - if (!ret) { - reg_val = (pll.p1 == 6 ? MT9M032_FORMATTER1_PLL_P1_6 : 0) - | MT9M032_FORMATTER1_PARALLEL | 0x001e; /* 14-bit */ - ret = mt9m032_write(client, MT9M032_FORMATTER1, reg_val); - } - - return ret; -} - -/* ----------------------------------------------------------------------------- - * Subdev pad operations - */ - -static int mt9m032_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->index != 0) - return -EINVAL; - - code->code = MEDIA_BUS_FMT_Y8_1X8; - return 0; -} - -static int mt9m032_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_size_enum *fse) -{ - if (fse->index != 0 || fse->code != MEDIA_BUS_FMT_Y8_1X8) - return -EINVAL; - - fse->min_width = MT9M032_COLUMN_SIZE_DEF; - fse->max_width = MT9M032_COLUMN_SIZE_DEF; - fse->min_height = MT9M032_ROW_SIZE_DEF; - fse->max_height = MT9M032_ROW_SIZE_DEF; - - return 0; -} - -/** - * __mt9m032_get_pad_crop() - get crop rect - * @sensor: pointer to the sensor struct - * @sd_state: v4l2_subdev_state for getting the try crop rect from - * @which: select try or active crop rect - * - * Returns a pointer the current active or fh relative try crop rect - */ -static struct v4l2_rect * -__mt9m032_get_pad_crop(struct mt9m032 *sensor, - struct v4l2_subdev_state *sd_state, - enum v4l2_subdev_format_whence which) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&sensor->subdev, sd_state, 0); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &sensor->crop; - default: - return NULL; - } -} - -/** - * __mt9m032_get_pad_format() - get format - * @sensor: pointer to the sensor struct - * @sd_state: v4l2_subdev_state for getting the try format from - * @which: select try or active format - * - * Returns a pointer the current active or fh relative try format - */ -static struct v4l2_mbus_framefmt * -__mt9m032_get_pad_format(struct mt9m032 *sensor, - struct v4l2_subdev_state *sd_state, - enum v4l2_subdev_format_whence which) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&sensor->subdev, sd_state, - 0); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &sensor->format; - default: - return NULL; - } -} - -static int mt9m032_get_pad_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct mt9m032 *sensor = to_mt9m032(subdev); - - mutex_lock(&sensor->lock); - fmt->format = *__mt9m032_get_pad_format(sensor, sd_state, fmt->which); - mutex_unlock(&sensor->lock); - - return 0; -} - -static int mt9m032_set_pad_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct mt9m032 *sensor = to_mt9m032(subdev); - int ret; - - mutex_lock(&sensor->lock); - - if (sensor->streaming && fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - ret = -EBUSY; - goto done; - } - - /* Scaling is not supported, the format is thus fixed. */ - fmt->format = *__mt9m032_get_pad_format(sensor, sd_state, fmt->which); - ret = 0; - -done: - mutex_unlock(&sensor->lock); - return ret; -} - -static int mt9m032_get_pad_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_selection *sel) -{ - struct mt9m032 *sensor = to_mt9m032(subdev); - - if (sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - mutex_lock(&sensor->lock); - sel->r = *__mt9m032_get_pad_crop(sensor, sd_state, sel->which); - mutex_unlock(&sensor->lock); - - return 0; -} - -static int mt9m032_set_pad_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_selection *sel) -{ - struct mt9m032 *sensor = to_mt9m032(subdev); - struct v4l2_mbus_framefmt *format; - struct v4l2_rect *__crop; - struct v4l2_rect rect; - int ret = 0; - - if (sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - mutex_lock(&sensor->lock); - - if (sensor->streaming && sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - ret = -EBUSY; - goto done; - } - - /* Clamp the crop rectangle boundaries and align them to a multiple of 2 - * pixels to ensure a GRBG Bayer pattern. - */ - rect.left = clamp(ALIGN(sel->r.left, 2), MT9M032_COLUMN_START_MIN, - MT9M032_COLUMN_START_MAX); - rect.top = clamp(ALIGN(sel->r.top, 2), MT9M032_ROW_START_MIN, - MT9M032_ROW_START_MAX); - rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 2), - MT9M032_COLUMN_SIZE_MIN, MT9M032_COLUMN_SIZE_MAX); - rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2), - MT9M032_ROW_SIZE_MIN, MT9M032_ROW_SIZE_MAX); - - rect.width = min_t(unsigned int, rect.width, - MT9M032_PIXEL_ARRAY_WIDTH - rect.left); - rect.height = min_t(unsigned int, rect.height, - MT9M032_PIXEL_ARRAY_HEIGHT - rect.top); - - __crop = __mt9m032_get_pad_crop(sensor, sd_state, sel->which); - - if (rect.width != __crop->width || rect.height != __crop->height) { - /* Reset the output image size if the crop rectangle size has - * been modified. - */ - format = __mt9m032_get_pad_format(sensor, sd_state, - sel->which); - format->width = rect.width; - format->height = rect.height; - } - - *__crop = rect; - sel->r = rect; - - if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) - ret = mt9m032_update_geom_timing(sensor); - -done: - mutex_unlock(&sensor->lock); - return ret; -} - -static int mt9m032_get_frame_interval(struct v4l2_subdev *subdev, - struct v4l2_subdev_frame_interval *fi) -{ - struct mt9m032 *sensor = to_mt9m032(subdev); - - mutex_lock(&sensor->lock); - memset(fi, 0, sizeof(*fi)); - fi->interval = sensor->frame_interval; - mutex_unlock(&sensor->lock); - - return 0; -} - -static int mt9m032_set_frame_interval(struct v4l2_subdev *subdev, - struct v4l2_subdev_frame_interval *fi) -{ - struct mt9m032 *sensor = to_mt9m032(subdev); - int ret; - - mutex_lock(&sensor->lock); - - if (sensor->streaming) { - ret = -EBUSY; - goto done; - } - - /* Avoid divisions by 0. */ - if (fi->interval.denominator == 0) - fi->interval.denominator = 1; - - ret = mt9m032_update_timing(sensor, &fi->interval); - if (!ret) - sensor->frame_interval = fi->interval; - -done: - mutex_unlock(&sensor->lock); - return ret; -} - -static int mt9m032_s_stream(struct v4l2_subdev *subdev, int streaming) -{ - struct mt9m032 *sensor = to_mt9m032(subdev); - int ret; - - mutex_lock(&sensor->lock); - ret = update_formatter2(sensor, streaming); - if (!ret) - sensor->streaming = streaming; - mutex_unlock(&sensor->lock); - - return ret; -} - -/* ----------------------------------------------------------------------------- - * V4L2 subdev core operations - */ - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int mt9m032_g_register(struct v4l2_subdev *sd, - struct v4l2_dbg_register *reg) -{ - struct mt9m032 *sensor = to_mt9m032(sd); - struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); - int val; - - if (reg->reg > 0xff) - return -EINVAL; - - val = mt9m032_read(client, reg->reg); - if (val < 0) - return -EIO; - - reg->size = 2; - reg->val = val; - - return 0; -} - -static int mt9m032_s_register(struct v4l2_subdev *sd, - const struct v4l2_dbg_register *reg) -{ - struct mt9m032 *sensor = to_mt9m032(sd); - struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); - - if (reg->reg > 0xff) - return -EINVAL; - - return mt9m032_write(client, reg->reg, reg->val); -} -#endif - -/* ----------------------------------------------------------------------------- - * V4L2 subdev control operations - */ - -static int update_read_mode2(struct mt9m032 *sensor, bool vflip, bool hflip) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); - int reg_val = (vflip << MT9M032_READ_MODE2_VFLIP_SHIFT) - | (hflip << MT9M032_READ_MODE2_HFLIP_SHIFT) - | MT9M032_READ_MODE2_ROW_BLC - | 0x0007; - - return mt9m032_write(client, MT9M032_READ_MODE2, reg_val); -} - -static int mt9m032_set_gain(struct mt9m032 *sensor, s32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); - int digital_gain_val; /* in 1/8th (0..127) */ - int analog_mul; /* 0 or 1 */ - int analog_gain_val; /* in 1/16th. (0..63) */ - u16 reg_val; - - digital_gain_val = 51; /* from setup example */ - - if (val < 63) { - analog_mul = 0; - analog_gain_val = val; - } else { - analog_mul = 1; - analog_gain_val = val / 2; - } - - /* a_gain = (1 + analog_mul) + (analog_gain_val + 1) / 16 */ - /* overall_gain = a_gain * (1 + digital_gain_val / 8) */ - - reg_val = ((digital_gain_val & MT9M032_GAIN_DIGITAL_MASK) - << MT9M032_GAIN_DIGITAL_SHIFT) - | ((analog_mul & 1) << MT9M032_GAIN_AMUL_SHIFT) - | (analog_gain_val & MT9M032_GAIN_ANALOG_MASK); - - return mt9m032_write(client, MT9M032_GAIN_ALL, reg_val); -} - -static int mt9m032_try_ctrl(struct v4l2_ctrl *ctrl) -{ - if (ctrl->id == V4L2_CID_GAIN && ctrl->val >= 63) { - /* round because of multiplier used for values >= 63 */ - ctrl->val &= ~1; - } - - return 0; -} - -static int mt9m032_set_ctrl(struct v4l2_ctrl *ctrl) -{ - struct mt9m032 *sensor = - container_of(ctrl->handler, struct mt9m032, ctrls); - struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev); - int ret; - - switch (ctrl->id) { - case V4L2_CID_GAIN: - return mt9m032_set_gain(sensor, ctrl->val); - - case V4L2_CID_HFLIP: - /* case V4L2_CID_VFLIP: -- In the same cluster */ - return update_read_mode2(sensor, sensor->vflip->val, - sensor->hflip->val); - - case V4L2_CID_EXPOSURE: - ret = mt9m032_write(client, MT9M032_SHUTTER_WIDTH_HIGH, - (ctrl->val >> 16) & 0xffff); - if (ret < 0) - return ret; - - return mt9m032_write(client, MT9M032_SHUTTER_WIDTH_LOW, - ctrl->val & 0xffff); - } - - return 0; -} - -static const struct v4l2_ctrl_ops mt9m032_ctrl_ops = { - .s_ctrl = mt9m032_set_ctrl, - .try_ctrl = mt9m032_try_ctrl, -}; - -/* -------------------------------------------------------------------------- */ - -static const struct v4l2_subdev_core_ops mt9m032_core_ops = { -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = mt9m032_g_register, - .s_register = mt9m032_s_register, -#endif -}; - -static const struct v4l2_subdev_video_ops mt9m032_video_ops = { - .s_stream = mt9m032_s_stream, - .g_frame_interval = mt9m032_get_frame_interval, - .s_frame_interval = mt9m032_set_frame_interval, -}; - -static const struct v4l2_subdev_pad_ops mt9m032_pad_ops = { - .enum_mbus_code = mt9m032_enum_mbus_code, - .enum_frame_size = mt9m032_enum_frame_size, - .get_fmt = mt9m032_get_pad_format, - .set_fmt = mt9m032_set_pad_format, - .set_selection = mt9m032_set_pad_selection, - .get_selection = mt9m032_get_pad_selection, -}; - -static const struct v4l2_subdev_ops mt9m032_ops = { - .core = &mt9m032_core_ops, - .video = &mt9m032_video_ops, - .pad = &mt9m032_pad_ops, -}; - -/* ----------------------------------------------------------------------------- - * Driver initialization and probing - */ - -static int mt9m032_probe(struct i2c_client *client) -{ - struct mt9m032_platform_data *pdata = client->dev.platform_data; - struct i2c_adapter *adapter = client->adapter; - struct mt9m032 *sensor; - int chip_version; - int ret; - - if (pdata == NULL) { - dev_err(&client->dev, "No platform data\n"); - return -EINVAL; - } - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { - dev_warn(&client->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); - return -EIO; - } - - if (!client->dev.platform_data) - return -ENODEV; - - sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL); - if (sensor == NULL) - return -ENOMEM; - - mutex_init(&sensor->lock); - - sensor->pdata = pdata; - - v4l2_i2c_subdev_init(&sensor->subdev, client, &mt9m032_ops); - sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - chip_version = mt9m032_read(client, MT9M032_CHIP_VERSION); - if (chip_version != MT9M032_CHIP_VERSION_VALUE) { - dev_err(&client->dev, "MT9M032 not detected, wrong version " - "0x%04x\n", chip_version); - ret = -ENODEV; - goto error_sensor; - } - - dev_info(&client->dev, "MT9M032 detected at address 0x%02x\n", - client->addr); - - sensor->frame_interval.numerator = 1; - sensor->frame_interval.denominator = 30; - - sensor->crop.left = MT9M032_COLUMN_START_DEF; - sensor->crop.top = MT9M032_ROW_START_DEF; - sensor->crop.width = MT9M032_COLUMN_SIZE_DEF; - sensor->crop.height = MT9M032_ROW_SIZE_DEF; - - sensor->format.width = sensor->crop.width; - sensor->format.height = sensor->crop.height; - sensor->format.code = MEDIA_BUS_FMT_Y8_1X8; - sensor->format.field = V4L2_FIELD_NONE; - sensor->format.colorspace = V4L2_COLORSPACE_SRGB; - - v4l2_ctrl_handler_init(&sensor->ctrls, 5); - - v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops, - V4L2_CID_GAIN, 0, 127, 1, 64); - - sensor->hflip = v4l2_ctrl_new_std(&sensor->ctrls, - &mt9m032_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - sensor->vflip = v4l2_ctrl_new_std(&sensor->ctrls, - &mt9m032_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - - v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops, - V4L2_CID_EXPOSURE, MT9M032_SHUTTER_WIDTH_MIN, - MT9M032_SHUTTER_WIDTH_MAX, 1, - MT9M032_SHUTTER_WIDTH_DEF); - v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops, - V4L2_CID_PIXEL_RATE, pdata->pix_clock, - pdata->pix_clock, 1, pdata->pix_clock); - - if (sensor->ctrls.error) { - ret = sensor->ctrls.error; - dev_err(&client->dev, "control initialization error %d\n", ret); - goto error_ctrl; - } - - v4l2_ctrl_cluster(2, &sensor->hflip); - - sensor->subdev.ctrl_handler = &sensor->ctrls; - sensor->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; - sensor->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_pads_init(&sensor->subdev.entity, 1, &sensor->pad); - if (ret < 0) - goto error_ctrl; - - ret = mt9m032_write(client, MT9M032_RESET, 1); /* reset on */ - if (ret < 0) - goto error_entity; - ret = mt9m032_write(client, MT9M032_RESET, 0); /* reset off */ - if (ret < 0) - goto error_entity; - - ret = mt9m032_setup_pll(sensor); - if (ret < 0) - goto error_entity; - usleep_range(10000, 11000); - - ret = v4l2_ctrl_handler_setup(&sensor->ctrls); - if (ret < 0) - goto error_entity; - - /* SIZE */ - ret = mt9m032_update_geom_timing(sensor); - if (ret < 0) - goto error_entity; - - ret = mt9m032_write(client, 0x41, 0x0000); /* reserved !!! */ - if (ret < 0) - goto error_entity; - ret = mt9m032_write(client, 0x42, 0x0003); /* reserved !!! */ - if (ret < 0) - goto error_entity; - ret = mt9m032_write(client, 0x43, 0x0003); /* reserved !!! */ - if (ret < 0) - goto error_entity; - ret = mt9m032_write(client, 0x7f, 0x0000); /* reserved !!! */ - if (ret < 0) - goto error_entity; - if (sensor->pdata->invert_pixclock) { - ret = mt9m032_write(client, MT9M032_PIX_CLK_CTRL, - MT9M032_PIX_CLK_CTRL_INV_PIXCLK); - if (ret < 0) - goto error_entity; - } - - ret = mt9m032_write(client, MT9M032_RESTART, 1); /* Restart on */ - if (ret < 0) - goto error_entity; - msleep(100); - ret = mt9m032_write(client, MT9M032_RESTART, 0); /* Restart off */ - if (ret < 0) - goto error_entity; - msleep(100); - ret = update_formatter2(sensor, false); - if (ret < 0) - goto error_entity; - - return ret; - -error_entity: - media_entity_cleanup(&sensor->subdev.entity); -error_ctrl: - v4l2_ctrl_handler_free(&sensor->ctrls); -error_sensor: - mutex_destroy(&sensor->lock); - return ret; -} - -static void mt9m032_remove(struct i2c_client *client) -{ - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct mt9m032 *sensor = to_mt9m032(subdev); - - v4l2_device_unregister_subdev(subdev); - v4l2_ctrl_handler_free(&sensor->ctrls); - media_entity_cleanup(&subdev->entity); - mutex_destroy(&sensor->lock); -} - -static const struct i2c_device_id mt9m032_id_table[] = { - { MT9M032_NAME, 0 }, - { } -}; - -MODULE_DEVICE_TABLE(i2c, mt9m032_id_table); - -static struct i2c_driver mt9m032_i2c_driver = { - .driver = { - .name = MT9M032_NAME, - }, - .probe_new = mt9m032_probe, - .remove = mt9m032_remove, - .id_table = mt9m032_id_table, -}; - -module_i2c_driver(mt9m032_i2c_driver); - -MODULE_AUTHOR("Martin Hostettler <martin@neutronstar.dyndns.org>"); -MODULE_DESCRIPTION("MT9M032 camera sensor driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/mt9t001.c b/drivers/media/i2c/mt9t001.c deleted file mode 100644 index c635ed11388a..000000000000 --- a/drivers/media/i2c/mt9t001.c +++ /dev/null @@ -1,992 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Driver for MT9T001 CMOS Image Sensor from Aptina (Micron) - * - * Copyright (C) 2010-2011, Laurent Pinchart <laurent.pinchart@ideasonboard.com> - * - * Based on the MT9M001 driver, - * - * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> - */ - -#include <linux/clk.h> -#include <linux/i2c.h> -#include <linux/log2.h> -#include <linux/module.h> -#include <linux/regulator/consumer.h> -#include <linux/slab.h> -#include <linux/videodev2.h> -#include <linux/v4l2-mediabus.h> - -#include <media/i2c/mt9t001.h> -#include <media/v4l2-ctrls.h> -#include <media/v4l2-device.h> -#include <media/v4l2-subdev.h> - -#define MT9T001_PIXEL_ARRAY_HEIGHT 1568 -#define MT9T001_PIXEL_ARRAY_WIDTH 2112 - -#define MT9T001_CHIP_VERSION 0x00 -#define MT9T001_CHIP_ID 0x1621 -#define MT9T001_ROW_START 0x01 -#define MT9T001_ROW_START_MIN 0 -#define MT9T001_ROW_START_DEF 20 -#define MT9T001_ROW_START_MAX 1534 -#define MT9T001_COLUMN_START 0x02 -#define MT9T001_COLUMN_START_MIN 0 -#define MT9T001_COLUMN_START_DEF 32 -#define MT9T001_COLUMN_START_MAX 2046 -#define MT9T001_WINDOW_HEIGHT 0x03 -#define MT9T001_WINDOW_HEIGHT_MIN 1 -#define MT9T001_WINDOW_HEIGHT_DEF 1535 -#define MT9T001_WINDOW_HEIGHT_MAX 1567 -#define MT9T001_WINDOW_WIDTH 0x04 -#define MT9T001_WINDOW_WIDTH_MIN 1 -#define MT9T001_WINDOW_WIDTH_DEF 2047 -#define MT9T001_WINDOW_WIDTH_MAX 2111 -#define MT9T001_HORIZONTAL_BLANKING 0x05 -#define MT9T001_HORIZONTAL_BLANKING_MIN 21 -#define MT9T001_HORIZONTAL_BLANKING_MAX 1023 -#define MT9T001_VERTICAL_BLANKING 0x06 -#define MT9T001_VERTICAL_BLANKING_MIN 3 -#define MT9T001_VERTICAL_BLANKING_MAX 1023 -#define MT9T001_OUTPUT_CONTROL 0x07 -#define MT9T001_OUTPUT_CONTROL_SYNC (1 << 0) -#define MT9T001_OUTPUT_CONTROL_CHIP_ENABLE (1 << 1) -#define MT9T001_OUTPUT_CONTROL_TEST_DATA (1 << 6) -#define MT9T001_OUTPUT_CONTROL_DEF 0x0002 -#define MT9T001_SHUTTER_WIDTH_HIGH 0x08 -#define MT9T001_SHUTTER_WIDTH_LOW 0x09 -#define MT9T001_SHUTTER_WIDTH_MIN 1 -#define MT9T001_SHUTTER_WIDTH_DEF 1561 -#define MT9T001_SHUTTER_WIDTH_MAX (1024 * 1024) -#define MT9T001_PIXEL_CLOCK 0x0a -#define MT9T001_PIXEL_CLOCK_INVERT (1 << 15) -#define MT9T001_PIXEL_CLOCK_SHIFT_MASK (7 << 8) -#define MT9T001_PIXEL_CLOCK_SHIFT_SHIFT 8 -#define MT9T001_PIXEL_CLOCK_DIVIDE_MASK (0x7f << 0) -#define MT9T001_FRAME_RESTART 0x0b -#define MT9T001_SHUTTER_DELAY 0x0c -#define MT9T001_SHUTTER_DELAY_MAX 2047 -#define MT9T001_RESET 0x0d -#define MT9T001_READ_MODE1 0x1e -#define MT9T001_READ_MODE_SNAPSHOT (1 << 8) -#define MT9T001_READ_MODE_STROBE_ENABLE (1 << 9) -#define MT9T001_READ_MODE_STROBE_WIDTH (1 << 10) -#define MT9T001_READ_MODE_STROBE_OVERRIDE (1 << 11) -#define MT9T001_READ_MODE2 0x20 -#define MT9T001_READ_MODE_BAD_FRAMES (1 << 0) -#define MT9T001_READ_MODE_LINE_VALID_CONTINUOUS (1 << 9) -#define MT9T001_READ_MODE_LINE_VALID_FRAME (1 << 10) -#define MT9T001_READ_MODE3 0x21 -#define MT9T001_READ_MODE_GLOBAL_RESET (1 << 0) -#define MT9T001_READ_MODE_GHST_CTL (1 << 1) -#define MT9T001_ROW_ADDRESS_MODE 0x22 -#define MT9T001_ROW_SKIP_MASK (7 << 0) -#define MT9T001_ROW_BIN_MASK (3 << 3) -#define MT9T001_ROW_BIN_SHIFT 3 -#define MT9T001_COLUMN_ADDRESS_MODE 0x23 -#define MT9T001_COLUMN_SKIP_MASK (7 << 0) -#define MT9T001_COLUMN_BIN_MASK (3 << 3) -#define MT9T001_COLUMN_BIN_SHIFT 3 -#define MT9T001_GREEN1_GAIN 0x2b -#define MT9T001_BLUE_GAIN 0x2c -#define MT9T001_RED_GAIN 0x2d -#define MT9T001_GREEN2_GAIN 0x2e -#define MT9T001_TEST_DATA 0x32 -#define MT9T001_GLOBAL_GAIN 0x35 -#define MT9T001_GLOBAL_GAIN_MIN 8 -#define MT9T001_GLOBAL_GAIN_MAX 1024 -#define MT9T001_BLACK_LEVEL 0x49 -#define MT9T001_ROW_BLACK_DEFAULT_OFFSET 0x4b -#define MT9T001_BLC_DELTA_THRESHOLDS 0x5d -#define MT9T001_CAL_THRESHOLDS 0x5f -#define MT9T001_GREEN1_OFFSET 0x60 -#define MT9T001_GREEN2_OFFSET 0x61 -#define MT9T001_BLACK_LEVEL_CALIBRATION 0x62 -#define MT9T001_BLACK_LEVEL_OVERRIDE (1 << 0) -#define MT9T001_BLACK_LEVEL_DISABLE_OFFSET (1 << 1) -#define MT9T001_BLACK_LEVEL_RECALCULATE (1 << 12) -#define MT9T001_BLACK_LEVEL_LOCK_RED_BLUE (1 << 13) -#define MT9T001_BLACK_LEVEL_LOCK_GREEN (1 << 14) -#define MT9T001_RED_OFFSET 0x63 -#define MT9T001_BLUE_OFFSET 0x64 - -struct mt9t001 { - struct v4l2_subdev subdev; - struct media_pad pad; - - struct clk *clk; - struct regulator_bulk_data regulators[2]; - - struct mutex power_lock; /* lock to protect power_count */ - int power_count; - - struct v4l2_mbus_framefmt format; - struct v4l2_rect crop; - - struct v4l2_ctrl_handler ctrls; - struct v4l2_ctrl *gains[4]; - - u16 output_control; - u16 black_level; -}; - -static inline struct mt9t001 *to_mt9t001(struct v4l2_subdev *sd) -{ - return container_of(sd, struct mt9t001, subdev); -} - -static int mt9t001_read(struct i2c_client *client, u8 reg) -{ - return i2c_smbus_read_word_swapped(client, reg); -} - -static int mt9t001_write(struct i2c_client *client, u8 reg, u16 data) -{ - return i2c_smbus_write_word_swapped(client, reg, data); -} - -static int mt9t001_set_output_control(struct mt9t001 *mt9t001, u16 clear, - u16 set) -{ - struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev); - u16 value = (mt9t001->output_control & ~clear) | set; - int ret; - - if (value == mt9t001->output_control) - return 0; - - ret = mt9t001_write(client, MT9T001_OUTPUT_CONTROL, value); - if (ret < 0) - return ret; - - mt9t001->output_control = value; - return 0; -} - -static int mt9t001_reset(struct mt9t001 *mt9t001) -{ - struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev); - int ret; - - /* Reset the chip and stop data read out */ - ret = mt9t001_write(client, MT9T001_RESET, 1); - if (ret < 0) - return ret; - - ret = mt9t001_write(client, MT9T001_RESET, 0); - if (ret < 0) - return ret; - - mt9t001->output_control = MT9T001_OUTPUT_CONTROL_DEF; - - return mt9t001_set_output_control(mt9t001, - MT9T001_OUTPUT_CONTROL_CHIP_ENABLE, - 0); -} - -static int mt9t001_power_on(struct mt9t001 *mt9t001) -{ - int ret; - - /* Bring up the supplies */ - ret = regulator_bulk_enable(ARRAY_SIZE(mt9t001->regulators), - mt9t001->regulators); - if (ret < 0) - return ret; - - /* Enable clock */ - ret = clk_prepare_enable(mt9t001->clk); - if (ret < 0) - regulator_bulk_disable(ARRAY_SIZE(mt9t001->regulators), - mt9t001->regulators); - - return ret; -} - -static void mt9t001_power_off(struct mt9t001 *mt9t001) -{ - regulator_bulk_disable(ARRAY_SIZE(mt9t001->regulators), - mt9t001->regulators); - - clk_disable_unprepare(mt9t001->clk); -} - -static int __mt9t001_set_power(struct mt9t001 *mt9t001, bool on) -{ - struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev); - int ret; - - if (!on) { - mt9t001_power_off(mt9t001); - return 0; - } - - ret = mt9t001_power_on(mt9t001); - if (ret < 0) - return ret; - - ret = mt9t001_reset(mt9t001); - if (ret < 0) { - dev_err(&client->dev, "Failed to reset the camera\n"); - goto e_power; - } - - ret = v4l2_ctrl_handler_setup(&mt9t001->ctrls); - if (ret < 0) { - dev_err(&client->dev, "Failed to set up control handlers\n"); - goto e_power; - } - - return 0; - -e_power: - mt9t001_power_off(mt9t001); - - return ret; -} - -/* ----------------------------------------------------------------------------- - * V4L2 subdev video operations - */ - -static struct v4l2_mbus_framefmt * -__mt9t001_get_pad_format(struct mt9t001 *mt9t001, - struct v4l2_subdev_state *sd_state, - unsigned int pad, enum v4l2_subdev_format_whence which) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(&mt9t001->subdev, sd_state, - pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &mt9t001->format; - default: - return NULL; - } -} - -static struct v4l2_rect * -__mt9t001_get_pad_crop(struct mt9t001 *mt9t001, - struct v4l2_subdev_state *sd_state, - unsigned int pad, enum v4l2_subdev_format_whence which) -{ - switch (which) { - case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(&mt9t001->subdev, sd_state, - pad); - case V4L2_SUBDEV_FORMAT_ACTIVE: - return &mt9t001->crop; - default: - return NULL; - } -} - -static int mt9t001_s_stream(struct v4l2_subdev *subdev, int enable) -{ - const u16 mode = MT9T001_OUTPUT_CONTROL_CHIP_ENABLE; - struct i2c_client *client = v4l2_get_subdevdata(subdev); - struct mt9t001_platform_data *pdata = client->dev.platform_data; - struct mt9t001 *mt9t001 = to_mt9t001(subdev); - struct v4l2_mbus_framefmt *format = &mt9t001->format; - struct v4l2_rect *crop = &mt9t001->crop; - unsigned int hratio; - unsigned int vratio; - int ret; - - if (!enable) - return mt9t001_set_output_control(mt9t001, mode, 0); - - /* Configure the pixel clock polarity */ - if (pdata->clk_pol) { - ret = mt9t001_write(client, MT9T001_PIXEL_CLOCK, - MT9T001_PIXEL_CLOCK_INVERT); - if (ret < 0) - return ret; - } - - /* Configure the window size and row/column bin */ - hratio = DIV_ROUND_CLOSEST(crop->width, format->width); - vratio = DIV_ROUND_CLOSEST(crop->height, format->height); - - ret = mt9t001_write(client, MT9T001_ROW_ADDRESS_MODE, hratio - 1); - if (ret < 0) - return ret; - - ret = mt9t001_write(client, MT9T001_COLUMN_ADDRESS_MODE, vratio - 1); - if (ret < 0) - return ret; - - ret = mt9t001_write(client, MT9T001_COLUMN_START, crop->left); - if (ret < 0) - return ret; - - ret = mt9t001_write(client, MT9T001_ROW_START, crop->top); - if (ret < 0) - return ret; - - ret = mt9t001_write(client, MT9T001_WINDOW_WIDTH, crop->width - 1); - if (ret < 0) - return ret; - - ret = mt9t001_write(client, MT9T001_WINDOW_HEIGHT, crop->height - 1); - if (ret < 0) - return ret; - - /* Switch to master "normal" mode */ - return mt9t001_set_output_control(mt9t001, 0, mode); -} - -static int mt9t001_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->index > 0) - return -EINVAL; - - code->code = MEDIA_BUS_FMT_SGRBG10_1X10; - return 0; -} - -static int mt9t001_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_size_enum *fse) -{ - if (fse->index >= 8 || fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) - return -EINVAL; - - fse->min_width = (MT9T001_WINDOW_WIDTH_DEF + 1) / fse->index; - fse->max_width = fse->min_width; - fse->min_height = (MT9T001_WINDOW_HEIGHT_DEF + 1) / fse->index; - fse->max_height = fse->min_height; - - return 0; -} - -static int mt9t001_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct mt9t001 *mt9t001 = to_mt9t001(subdev); - - format->format = *__mt9t001_get_pad_format(mt9t001, sd_state, - format->pad, - format->which); - return 0; -} - -static int mt9t001_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct mt9t001 *mt9t001 = to_mt9t001(subdev); - struct v4l2_mbus_framefmt *__format; - struct v4l2_rect *__crop; - unsigned int width; - unsigned int height; - unsigned int hratio; - unsigned int vratio; - - __crop = __mt9t001_get_pad_crop(mt9t001, sd_state, format->pad, - format->which); - - /* Clamp the width and height to avoid dividing by zero. */ - width = clamp_t(unsigned int, ALIGN(format->format.width, 2), - max_t(unsigned int, __crop->width / 8, - MT9T001_WINDOW_HEIGHT_MIN + 1), - __crop->width); - height = clamp_t(unsigned int, ALIGN(format->format.height, 2), - max_t(unsigned int, __crop->height / 8, - MT9T001_WINDOW_HEIGHT_MIN + 1), - __crop->height); - - hratio = DIV_ROUND_CLOSEST(__crop->width, width); - vratio = DIV_ROUND_CLOSEST(__crop->height, height); - - __format = __mt9t001_get_pad_format(mt9t001, sd_state, format->pad, - format->which); - __format->width = __crop->width / hratio; - __format->height = __crop->height / vratio; - - format->format = *__format; - - return 0; -} - -static int mt9t001_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_selection *sel) -{ - struct mt9t001 *mt9t001 = to_mt9t001(subdev); - - if (sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - sel->r = *__mt9t001_get_pad_crop(mt9t001, sd_state, sel->pad, - sel->which); - return 0; -} - -static int mt9t001_set_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_selection *sel) -{ - struct mt9t001 *mt9t001 = to_mt9t001(subdev); - struct v4l2_mbus_framefmt *__format; - struct v4l2_rect *__crop; - struct v4l2_rect rect; - - if (sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - /* Clamp the crop rectangle boundaries and align them to a multiple of 2 - * pixels. - */ - rect.left = clamp(ALIGN(sel->r.left, 2), - MT9T001_COLUMN_START_MIN, - MT9T001_COLUMN_START_MAX); - rect.top = clamp(ALIGN(sel->r.top, 2), - MT9T001_ROW_START_MIN, - MT9T001_ROW_START_MAX); - rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 2), - MT9T001_WINDOW_WIDTH_MIN + 1, - MT9T001_WINDOW_WIDTH_MAX + 1); - rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2), - MT9T001_WINDOW_HEIGHT_MIN + 1, - MT9T001_WINDOW_HEIGHT_MAX + 1); - - rect.width = min_t(unsigned int, rect.width, - MT9T001_PIXEL_ARRAY_WIDTH - rect.left); - rect.height = min_t(unsigned int, rect.height, - MT9T001_PIXEL_ARRAY_HEIGHT - rect.top); - - __crop = __mt9t001_get_pad_crop(mt9t001, sd_state, sel->pad, - sel->which); - - if (rect.width != __crop->width || rect.height != __crop->height) { - /* Reset the output image size if the crop rectangle size has - * been modified. - */ - __format = __mt9t001_get_pad_format(mt9t001, sd_state, - sel->pad, - sel->which); - __format->width = rect.width; - __format->height = rect.height; - } - - *__crop = rect; - sel->r = rect; - - return 0; -} - -/* ----------------------------------------------------------------------------- - * V4L2 subdev control operations - */ - -#define V4L2_CID_TEST_PATTERN_COLOR (V4L2_CID_USER_BASE | 0x1001) -#define V4L2_CID_BLACK_LEVEL_AUTO (V4L2_CID_USER_BASE | 0x1002) -#define V4L2_CID_BLACK_LEVEL_OFFSET (V4L2_CID_USER_BASE | 0x1003) -#define V4L2_CID_BLACK_LEVEL_CALIBRATE (V4L2_CID_USER_BASE | 0x1004) - -#define V4L2_CID_GAIN_RED (V4L2_CTRL_CLASS_CAMERA | 0x1001) -#define V4L2_CID_GAIN_GREEN_RED (V4L2_CTRL_CLASS_CAMERA | 0x1002) -#define V4L2_CID_GAIN_GREEN_BLUE (V4L2_CTRL_CLASS_CAMERA | 0x1003) -#define V4L2_CID_GAIN_BLUE (V4L2_CTRL_CLASS_CAMERA | 0x1004) - -static u16 mt9t001_gain_value(s32 *gain) -{ - /* Gain is controlled by 2 analog stages and a digital stage. Valid - * values for the 3 stages are - * - * Stage Min Max Step - * ------------------------------------------ - * First analog stage x1 x2 1 - * Second analog stage x1 x4 0.125 - * Digital stage x1 x16 0.125 - * - * To minimize noise, the gain stages should be used in the second - * analog stage, first analog stage, digital stage order. Gain from a - * previous stage should be pushed to its maximum value before the next - * stage is used. - */ - if (*gain <= 32) - return *gain; - - if (*gain <= 64) { - *gain &= ~1; - return (1 << 6) | (*gain >> 1); - } - - *gain &= ~7; - return ((*gain - 64) << 5) | (1 << 6) | 32; -} - -static int mt9t001_ctrl_freeze(struct mt9t001 *mt9t001, bool freeze) -{ - return mt9t001_set_output_control(mt9t001, - freeze ? 0 : MT9T001_OUTPUT_CONTROL_SYNC, - freeze ? MT9T001_OUTPUT_CONTROL_SYNC : 0); -} - -static int mt9t001_s_ctrl(struct v4l2_ctrl *ctrl) -{ - static const u8 gains[4] = { - MT9T001_RED_GAIN, MT9T001_GREEN1_GAIN, - MT9T001_GREEN2_GAIN, MT9T001_BLUE_GAIN - }; - - struct mt9t001 *mt9t001 = - container_of(ctrl->handler, struct mt9t001, ctrls); - struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->subdev); - unsigned int count; - unsigned int i; - u16 value; - int ret; - - switch (ctrl->id) { - case V4L2_CID_GAIN_RED: - case V4L2_CID_GAIN_GREEN_RED: - case V4L2_CID_GAIN_GREEN_BLUE: - case V4L2_CID_GAIN_BLUE: - - /* Disable control updates if more than one control has changed - * in the cluster. - */ - for (i = 0, count = 0; i < 4; ++i) { - struct v4l2_ctrl *gain = mt9t001->gains[i]; - - if (gain->val != gain->cur.val) - count++; - } - - if (count > 1) { - ret = mt9t001_ctrl_freeze(mt9t001, true); - if (ret < 0) - return ret; - } - - /* Update the gain controls. */ - for (i = 0; i < 4; ++i) { - struct v4l2_ctrl *gain = mt9t001->gains[i]; - - if (gain->val == gain->cur.val) - continue; - - value = mt9t001_gain_value(&gain->val); - ret = mt9t001_write(client, gains[i], value); - if (ret < 0) { - mt9t001_ctrl_freeze(mt9t001, false); - return ret; - } - } - - /* Enable control updates. */ - if (count > 1) { - ret = mt9t001_ctrl_freeze(mt9t001, false); - if (ret < 0) - return ret; - } - - break; - - case V4L2_CID_EXPOSURE: - ret = mt9t001_write(client, MT9T001_SHUTTER_WIDTH_LOW, - ctrl->val & 0xffff); - if (ret < 0) - return ret; - - return mt9t001_write(client, MT9T001_SHUTTER_WIDTH_HIGH, - ctrl->val >> 16); - - case V4L2_CID_TEST_PATTERN: - return mt9t001_set_output_control(mt9t001, - ctrl->val ? 0 : MT9T001_OUTPUT_CONTROL_TEST_DATA, - ctrl->val ? MT9T001_OUTPUT_CONTROL_TEST_DATA : 0); - - case V4L2_CID_TEST_PATTERN_COLOR: - return mt9t001_write(client, MT9T001_TEST_DATA, ctrl->val << 2); - - case V4L2_CID_BLACK_LEVEL_AUTO: - value = ctrl->val ? 0 : MT9T001_BLACK_LEVEL_OVERRIDE; - ret = mt9t001_write(client, MT9T001_BLACK_LEVEL_CALIBRATION, - value); - if (ret < 0) - return ret; - - mt9t001->black_level = value; - break; - - case V4L2_CID_BLACK_LEVEL_OFFSET: - ret = mt9t001_write(client, MT9T001_GREEN1_OFFSET, ctrl->val); - if (ret < 0) - return ret; - - ret = mt9t001_write(client, MT9T001_GREEN2_OFFSET, ctrl->val); - if (ret < 0) - return ret; - - ret = mt9t001_write(client, MT9T001_RED_OFFSET, ctrl->val); - if (ret < 0) - return ret; - - return mt9t001_write(client, MT9T001_BLUE_OFFSET, ctrl->val); - - case V4L2_CID_BLACK_LEVEL_CALIBRATE: - return mt9t001_write(client, MT9T001_BLACK_LEVEL_CALIBRATION, - MT9T001_BLACK_LEVEL_RECALCULATE | - mt9t001->black_level); - } - - return 0; -} - -static const struct v4l2_ctrl_ops mt9t001_ctrl_ops = { - .s_ctrl = mt9t001_s_ctrl, -}; - -static const char * const mt9t001_test_pattern_menu[] = { - "Disabled", - "Enabled", -}; - -static const struct v4l2_ctrl_config mt9t001_ctrls[] = { - { - .ops = &mt9t001_ctrl_ops, - .id = V4L2_CID_TEST_PATTERN_COLOR, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Test Pattern Color", - .min = 0, - .max = 1023, - .step = 1, - .def = 0, - .flags = 0, - }, { - .ops = &mt9t001_ctrl_ops, - .id = V4L2_CID_BLACK_LEVEL_AUTO, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Black Level, Auto", - .min = 0, - .max = 1, - .step = 1, - .def = 1, - .flags = 0, - }, { - .ops = &mt9t001_ctrl_ops, - .id = V4L2_CID_BLACK_LEVEL_OFFSET, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Black Level, Offset", - .min = -256, - .max = 255, - .step = 1, - .def = 32, - .flags = 0, - }, { - .ops = &mt9t001_ctrl_ops, - .id = V4L2_CID_BLACK_LEVEL_CALIBRATE, - .type = V4L2_CTRL_TYPE_BUTTON, - .name = "Black Level, Calibrate", - .min = 0, - .max = 0, - .step = 0, - .def = 0, - .flags = V4L2_CTRL_FLAG_WRITE_ONLY, - }, -}; - -static const struct v4l2_ctrl_config mt9t001_gains[] = { - { - .ops = &mt9t001_ctrl_ops, - .id = V4L2_CID_GAIN_RED, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain, Red", - .min = MT9T001_GLOBAL_GAIN_MIN, - .max = MT9T001_GLOBAL_GAIN_MAX, - .step = 1, - .def = MT9T001_GLOBAL_GAIN_MIN, - .flags = 0, - }, { - .ops = &mt9t001_ctrl_ops, - .id = V4L2_CID_GAIN_GREEN_RED, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain, Green (R)", - .min = MT9T001_GLOBAL_GAIN_MIN, - .max = MT9T001_GLOBAL_GAIN_MAX, - .step = 1, - .def = MT9T001_GLOBAL_GAIN_MIN, - .flags = 0, - }, { - .ops = &mt9t001_ctrl_ops, - .id = V4L2_CID_GAIN_GREEN_BLUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain, Green (B)", - .min = MT9T001_GLOBAL_GAIN_MIN, - .max = MT9T001_GLOBAL_GAIN_MAX, - .step = 1, - .def = MT9T001_GLOBAL_GAIN_MIN, - .flags = 0, - }, { - .ops = &mt9t001_ctrl_ops, - .id = V4L2_CID_GAIN_BLUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain, Blue", - .min = MT9T001_GLOBAL_GAIN_MIN, - .max = MT9T001_GLOBAL_GAIN_MAX, - .step = 1, - .def = MT9T001_GLOBAL_GAIN_MIN, - .flags = 0, - }, -}; - -/* ----------------------------------------------------------------------------- - * V4L2 subdev core operations - */ - -static int mt9t001_set_power(struct v4l2_subdev *subdev, int on) -{ - struct mt9t001 *mt9t001 = to_mt9t001(subdev); - int ret = 0; - - mutex_lock(&mt9t001->power_lock); - - /* If the power count is modified from 0 to != 0 or from != 0 to 0, - * update the power state. - */ - if (mt9t001->power_count == !on) { - ret = __mt9t001_set_power(mt9t001, !!on); - if (ret < 0) - goto out; - } - - /* Update the power count. */ - mt9t001->power_count += on ? 1 : -1; - WARN_ON(mt9t001->power_count < 0); - -out: - mutex_unlock(&mt9t001->power_lock); - return ret; -} - -/* ----------------------------------------------------------------------------- - * V4L2 subdev internal operations - */ - -static int mt9t001_registered(struct v4l2_subdev *subdev) -{ - struct i2c_client *client = v4l2_get_subdevdata(subdev); - struct mt9t001 *mt9t001 = to_mt9t001(subdev); - s32 data; - int ret; - - ret = mt9t001_power_on(mt9t001); - if (ret < 0) { - dev_err(&client->dev, "MT9T001 power up failed\n"); - return ret; - } - - /* Read out the chip version register */ - data = mt9t001_read(client, MT9T001_CHIP_VERSION); - mt9t001_power_off(mt9t001); - - if (data != MT9T001_CHIP_ID) { - dev_err(&client->dev, - "MT9T001 not detected, wrong version 0x%04x\n", data); - return -ENODEV; - } - - dev_info(&client->dev, "MT9T001 detected at address 0x%02x\n", - client->addr); - - return 0; -} - -static int mt9t001_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) -{ - struct v4l2_mbus_framefmt *format; - struct v4l2_rect *crop; - - crop = v4l2_subdev_get_try_crop(subdev, fh->state, 0); - crop->left = MT9T001_COLUMN_START_DEF; - crop->top = MT9T001_ROW_START_DEF; - crop->width = MT9T001_WINDOW_WIDTH_DEF + 1; - crop->height = MT9T001_WINDOW_HEIGHT_DEF + 1; - - format = v4l2_subdev_get_try_format(subdev, fh->state, 0); - format->code = MEDIA_BUS_FMT_SGRBG10_1X10; - format->width = MT9T001_WINDOW_WIDTH_DEF + 1; - format->height = MT9T001_WINDOW_HEIGHT_DEF + 1; - format->field = V4L2_FIELD_NONE; - format->colorspace = V4L2_COLORSPACE_SRGB; - - return mt9t001_set_power(subdev, 1); -} - -static int mt9t001_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) -{ - return mt9t001_set_power(subdev, 0); -} - -static const struct v4l2_subdev_core_ops mt9t001_subdev_core_ops = { - .s_power = mt9t001_set_power, -}; - -static const struct v4l2_subdev_video_ops mt9t001_subdev_video_ops = { - .s_stream = mt9t001_s_stream, -}; - -static const struct v4l2_subdev_pad_ops mt9t001_subdev_pad_ops = { - .enum_mbus_code = mt9t001_enum_mbus_code, - .enum_frame_size = mt9t001_enum_frame_size, - .get_fmt = mt9t001_get_format, - .set_fmt = mt9t001_set_format, - .get_selection = mt9t001_get_selection, - .set_selection = mt9t001_set_selection, -}; - -static const struct v4l2_subdev_ops mt9t001_subdev_ops = { - .core = &mt9t001_subdev_core_ops, - .video = &mt9t001_subdev_video_ops, - .pad = &mt9t001_subdev_pad_ops, -}; - -static const struct v4l2_subdev_internal_ops mt9t001_subdev_internal_ops = { - .registered = mt9t001_registered, - .open = mt9t001_open, - .close = mt9t001_close, -}; - -static int mt9t001_probe(struct i2c_client *client) -{ - struct mt9t001_platform_data *pdata = client->dev.platform_data; - struct mt9t001 *mt9t001; - unsigned int i; - int ret; - - if (pdata == NULL) { - dev_err(&client->dev, "No platform data\n"); - return -EINVAL; - } - - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_WORD_DATA)) { - dev_warn(&client->adapter->dev, - "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); - return -EIO; - } - - mt9t001 = devm_kzalloc(&client->dev, sizeof(*mt9t001), GFP_KERNEL); - if (!mt9t001) - return -ENOMEM; - - mutex_init(&mt9t001->power_lock); - mt9t001->output_control = MT9T001_OUTPUT_CONTROL_DEF; - - mt9t001->regulators[0].supply = "vdd"; - mt9t001->regulators[1].supply = "vaa"; - - ret = devm_regulator_bulk_get(&client->dev, 2, mt9t001->regulators); - if (ret < 0) { - dev_err(&client->dev, "Unable to get regulators\n"); - return ret; - } - - mt9t001->clk = devm_clk_get(&client->dev, NULL); - if (IS_ERR(mt9t001->clk)) { - dev_err(&client->dev, "Unable to get clock\n"); - return PTR_ERR(mt9t001->clk); - } - - v4l2_ctrl_handler_init(&mt9t001->ctrls, ARRAY_SIZE(mt9t001_ctrls) + - ARRAY_SIZE(mt9t001_gains) + 4); - - v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops, - V4L2_CID_EXPOSURE, MT9T001_SHUTTER_WIDTH_MIN, - MT9T001_SHUTTER_WIDTH_MAX, 1, - MT9T001_SHUTTER_WIDTH_DEF); - v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops, - V4L2_CID_BLACK_LEVEL, 1, 1, 1, 1); - v4l2_ctrl_new_std(&mt9t001->ctrls, &mt9t001_ctrl_ops, - V4L2_CID_PIXEL_RATE, pdata->ext_clk, pdata->ext_clk, - 1, pdata->ext_clk); - v4l2_ctrl_new_std_menu_items(&mt9t001->ctrls, &mt9t001_ctrl_ops, - V4L2_CID_TEST_PATTERN, - ARRAY_SIZE(mt9t001_test_pattern_menu) - 1, 0, - 0, mt9t001_test_pattern_menu); - - for (i = 0; i < ARRAY_SIZE(mt9t001_ctrls); ++i) - v4l2_ctrl_new_custom(&mt9t001->ctrls, &mt9t001_ctrls[i], NULL); - - for (i = 0; i < ARRAY_SIZE(mt9t001_gains); ++i) - mt9t001->gains[i] = v4l2_ctrl_new_custom(&mt9t001->ctrls, - &mt9t001_gains[i], NULL); - - v4l2_ctrl_cluster(ARRAY_SIZE(mt9t001_gains), mt9t001->gains); - - mt9t001->subdev.ctrl_handler = &mt9t001->ctrls; - - if (mt9t001->ctrls.error) { - printk(KERN_INFO "%s: control initialization error %d\n", - __func__, mt9t001->ctrls.error); - ret = -EINVAL; - goto done; - } - - mt9t001->crop.left = MT9T001_COLUMN_START_DEF; - mt9t001->crop.top = MT9T001_ROW_START_DEF; - mt9t001->crop.width = MT9T001_WINDOW_WIDTH_DEF + 1; - mt9t001->crop.height = MT9T001_WINDOW_HEIGHT_DEF + 1; - - mt9t001->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; - mt9t001->format.width = MT9T001_WINDOW_WIDTH_DEF + 1; - mt9t001->format.height = MT9T001_WINDOW_HEIGHT_DEF + 1; - mt9t001->format.field = V4L2_FIELD_NONE; - mt9t001->format.colorspace = V4L2_COLORSPACE_SRGB; - - v4l2_i2c_subdev_init(&mt9t001->subdev, client, &mt9t001_subdev_ops); - mt9t001->subdev.internal_ops = &mt9t001_subdev_internal_ops; - mt9t001->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - mt9t001->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; - mt9t001->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_pads_init(&mt9t001->subdev.entity, 1, &mt9t001->pad); - -done: - if (ret < 0) { - v4l2_ctrl_handler_free(&mt9t001->ctrls); - media_entity_cleanup(&mt9t001->subdev.entity); - } - - return ret; -} - -static void mt9t001_remove(struct i2c_client *client) -{ - struct v4l2_subdev *subdev = i2c_get_clientdata(client); - struct mt9t001 *mt9t001 = to_mt9t001(subdev); - - v4l2_ctrl_handler_free(&mt9t001->ctrls); - v4l2_device_unregister_subdev(subdev); - media_entity_cleanup(&subdev->entity); -} - -static const struct i2c_device_id mt9t001_id[] = { - { "mt9t001", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, mt9t001_id); - -static struct i2c_driver mt9t001_driver = { - .driver = { - .name = "mt9t001", - }, - .probe_new = mt9t001_probe, - .remove = mt9t001_remove, - .id_table = mt9t001_id, -}; - -module_i2c_driver(mt9t001_driver); - -MODULE_DESCRIPTION("Aptina (Micron) MT9T001 Camera driver"); -MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/noon010pc30.c b/drivers/media/i2c/noon010pc30.c deleted file mode 100644 index 144bef2835f7..000000000000 --- a/drivers/media/i2c/noon010pc30.c +++ /dev/null @@ -1,821 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Driver for SiliconFile NOON010PC30 CIF (1/11") Image Sensor with ISP - * - * Copyright (C) 2010 - 2011 Samsung Electronics Co., Ltd. - * Contact: Sylwester Nawrocki, <s.nawrocki@samsung.com> - * - * Initial register configuration based on a driver authored by - * HeungJun Kim <riverful.kim@samsung.com>. - */ - -#include <linux/delay.h> -#include <linux/gpio/consumer.h> -#include <linux/i2c.h> -#include <linux/slab.h> -#include <linux/regulator/consumer.h> -#include <media/i2c/noon010pc30.h> -#include <linux/videodev2.h> -#include <linux/module.h> -#include <media/v4l2-ctrls.h> -#include <media/v4l2-device.h> -#include <media/v4l2-mediabus.h> -#include <media/v4l2-subdev.h> - -static int debug; -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Enable module debug trace. Set to 1 to enable."); - -#define MODULE_NAME "NOON010PC30" - -/* - * Register offsets within a page - * b15..b8 - page id, b7..b0 - register address - */ -#define POWER_CTRL_REG 0x0001 -#define PAGEMODE_REG 0x03 -#define DEVICE_ID_REG 0x0004 -#define NOON010PC30_ID 0x86 -#define VDO_CTL_REG(n) (0x0010 + (n)) -#define SYNC_CTL_REG 0x0012 -/* Window size and position */ -#define WIN_ROWH_REG 0x0013 -#define WIN_ROWL_REG 0x0014 -#define WIN_COLH_REG 0x0015 -#define WIN_COLL_REG 0x0016 -#define WIN_HEIGHTH_REG 0x0017 -#define WIN_HEIGHTL_REG 0x0018 -#define WIN_WIDTHH_REG 0x0019 -#define WIN_WIDTHL_REG 0x001A -#define HBLANKH_REG 0x001B -#define HBLANKL_REG 0x001C -#define VSYNCH_REG 0x001D -#define VSYNCL_REG 0x001E -/* VSYNC control */ -#define VS_CTL_REG(n) (0x00A1 + (n)) -/* page 1 */ -#define ISP_CTL_REG(n) (0x0110 + (n)) -#define YOFS_REG 0x0119 -#define DARK_YOFS_REG 0x011A -#define SAT_CTL_REG 0x0120 -#define BSAT_REG 0x0121 -#define RSAT_REG 0x0122 -/* Color correction */ -#define CMC_CTL_REG 0x0130 -#define CMC_OFSGH_REG 0x0133 -#define CMC_OFSGL_REG 0x0135 -#define CMC_SIGN_REG 0x0136 -#define CMC_GOFS_REG 0x0137 -#define CMC_COEF_REG(n) (0x0138 + (n)) -#define CMC_OFS_REG(n) (0x0141 + (n)) -/* Gamma correction */ -#define GMA_CTL_REG 0x0160 -#define GMA_COEF_REG(n) (0x0161 + (n)) -/* Lens Shading */ -#define LENS_CTRL_REG 0x01D0 -#define LENS_XCEN_REG 0x01D1 -#define LENS_YCEN_REG 0x01D2 -#define LENS_RC_REG 0x01D3 -#define LENS_GC_REG 0x01D4 -#define LENS_BC_REG 0x01D5 -#define L_AGON_REG 0x01D6 -#define L_AGOFF_REG 0x01D7 -/* Page 3 - Auto Exposure */ -#define AE_CTL_REG(n) (0x0310 + (n)) -#define AE_CTL9_REG 0x032C -#define AE_CTL10_REG 0x032D -#define AE_YLVL_REG 0x031C -#define AE_YTH_REG(n) (0x031D + (n)) -#define AE_WGT_REG 0x0326 -#define EXP_TIMEH_REG 0x0333 -#define EXP_TIMEM_REG 0x0334 -#define EXP_TIMEL_REG 0x0335 -#define EXP_MMINH_REG 0x0336 -#define EXP_MMINL_REG 0x0337 -#define EXP_MMAXH_REG 0x0338 -#define EXP_MMAXM_REG 0x0339 -#define EXP_MMAXL_REG 0x033A -/* Page 4 - Auto White Balance */ -#define AWB_CTL_REG(n) (0x0410 + (n)) -#define AWB_ENABE 0x80 -#define AWB_WGHT_REG 0x0419 -#define BGAIN_PAR_REG(n) (0x044F + (n)) -/* Manual white balance, when AWB_CTL2[0]=1 */ -#define MWB_RGAIN_REG 0x0466 -#define MWB_BGAIN_REG 0x0467 - -/* The token to mark an array end */ -#define REG_TERM 0xFFFF - -struct noon010_format { - u32 code; - enum v4l2_colorspace colorspace; - u16 ispctl1_reg; -}; - -struct noon010_frmsize { - u16 width; - u16 height; - int vid_ctl1; -}; - -static const char * const noon010_supply_name[] = { - "vdd_core", "vddio", "vdda" -}; - -#define NOON010_NUM_SUPPLIES ARRAY_SIZE(noon010_supply_name) - -struct noon010_info { - struct v4l2_subdev sd; - struct media_pad pad; - struct v4l2_ctrl_handler hdl; - struct regulator_bulk_data supply[NOON010_NUM_SUPPLIES]; - struct gpio_desc *reset; - struct gpio_desc *stby; - - /* Protects the struct members below */ - struct mutex lock; - - const struct noon010_format *curr_fmt; - const struct noon010_frmsize *curr_win; - unsigned int apply_new_cfg:1; - unsigned int streaming:1; - unsigned int hflip:1; - unsigned int vflip:1; - unsigned int power:1; - u8 i2c_reg_page; -}; - -struct i2c_regval { - u16 addr; - u16 val; -}; - -/* Supported resolutions. */ -static const struct noon010_frmsize noon010_sizes[] = { - { - .width = 352, - .height = 288, - .vid_ctl1 = 0, - }, { - .width = 176, - .height = 144, - .vid_ctl1 = 0x10, - }, { - .width = 88, - .height = 72, - .vid_ctl1 = 0x20, - }, -}; - -/* Supported pixel formats. */ -static const struct noon010_format noon010_formats[] = { - { - .code = MEDIA_BUS_FMT_YUYV8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .ispctl1_reg = 0x03, - }, { - .code = MEDIA_BUS_FMT_YVYU8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .ispctl1_reg = 0x02, - }, { - .code = MEDIA_BUS_FMT_VYUY8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .ispctl1_reg = 0, - }, { - .code = MEDIA_BUS_FMT_UYVY8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .ispctl1_reg = 0x01, - }, { - .code = MEDIA_BUS_FMT_RGB565_2X8_BE, - .colorspace = V4L2_COLORSPACE_JPEG, - .ispctl1_reg = 0x40, - }, -}; - -static const struct i2c_regval noon010_base_regs[] = { - { WIN_COLL_REG, 0x06 }, { HBLANKL_REG, 0x7C }, - /* Color corection and saturation */ - { ISP_CTL_REG(0), 0x30 }, { ISP_CTL_REG(2), 0x30 }, - { YOFS_REG, 0x80 }, { DARK_YOFS_REG, 0x04 }, - { SAT_CTL_REG, 0x1F }, { BSAT_REG, 0x90 }, - { CMC_CTL_REG, 0x0F }, { CMC_OFSGH_REG, 0x3C }, - { CMC_OFSGL_REG, 0x2C }, { CMC_SIGN_REG, 0x3F }, - { CMC_COEF_REG(0), 0x79 }, { CMC_OFS_REG(0), 0x00 }, - { CMC_COEF_REG(1), 0x39 }, { CMC_OFS_REG(1), 0x00 }, - { CMC_COEF_REG(2), 0x00 }, { CMC_OFS_REG(2), 0x00 }, - { CMC_COEF_REG(3), 0x11 }, { CMC_OFS_REG(3), 0x8B }, - { CMC_COEF_REG(4), 0x65 }, { CMC_OFS_REG(4), 0x07 }, - { CMC_COEF_REG(5), 0x14 }, { CMC_OFS_REG(5), 0x04 }, - { CMC_COEF_REG(6), 0x01 }, { CMC_OFS_REG(6), 0x9C }, - { CMC_COEF_REG(7), 0x33 }, { CMC_OFS_REG(7), 0x89 }, - { CMC_COEF_REG(8), 0x74 }, { CMC_OFS_REG(8), 0x25 }, - /* Automatic white balance */ - { AWB_CTL_REG(0), 0x78 }, { AWB_CTL_REG(1), 0x2E }, - { AWB_CTL_REG(2), 0x20 }, { AWB_CTL_REG(3), 0x85 }, - /* Auto exposure */ - { AE_CTL_REG(0), 0xDC }, { AE_CTL_REG(1), 0x81 }, - { AE_CTL_REG(2), 0x30 }, { AE_CTL_REG(3), 0xA5 }, - { AE_CTL_REG(4), 0x40 }, { AE_CTL_REG(5), 0x51 }, - { AE_CTL_REG(6), 0x33 }, { AE_CTL_REG(7), 0x7E }, - { AE_CTL9_REG, 0x00 }, { AE_CTL10_REG, 0x02 }, - { AE_YLVL_REG, 0x44 }, { AE_YTH_REG(0), 0x34 }, - { AE_YTH_REG(1), 0x30 }, { AE_WGT_REG, 0xD5 }, - /* Lens shading compensation */ - { LENS_CTRL_REG, 0x01 }, { LENS_XCEN_REG, 0x80 }, - { LENS_YCEN_REG, 0x70 }, { LENS_RC_REG, 0x53 }, - { LENS_GC_REG, 0x40 }, { LENS_BC_REG, 0x3E }, - { REG_TERM, 0 }, -}; - -static inline struct noon010_info *to_noon010(struct v4l2_subdev *sd) -{ - return container_of(sd, struct noon010_info, sd); -} - -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct noon010_info, hdl)->sd; -} - -static inline int set_i2c_page(struct noon010_info *info, - struct i2c_client *client, unsigned int reg) -{ - u32 page = reg >> 8 & 0xFF; - int ret = 0; - - if (info->i2c_reg_page != page && (reg & 0xFF) != 0x03) { - ret = i2c_smbus_write_byte_data(client, PAGEMODE_REG, page); - if (!ret) - info->i2c_reg_page = page; - } - return ret; -} - -static int cam_i2c_read(struct v4l2_subdev *sd, u32 reg_addr) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct noon010_info *info = to_noon010(sd); - int ret = set_i2c_page(info, client, reg_addr); - - if (ret) - return ret; - return i2c_smbus_read_byte_data(client, reg_addr & 0xFF); -} - -static int cam_i2c_write(struct v4l2_subdev *sd, u32 reg_addr, u32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct noon010_info *info = to_noon010(sd); - int ret = set_i2c_page(info, client, reg_addr); - - if (ret) - return ret; - return i2c_smbus_write_byte_data(client, reg_addr & 0xFF, val); -} - -static inline int noon010_bulk_write_reg(struct v4l2_subdev *sd, - const struct i2c_regval *msg) -{ - while (msg->addr != REG_TERM) { - int ret = cam_i2c_write(sd, msg->addr, msg->val); - - if (ret) - return ret; - msg++; - } - return 0; -} - -/* Device reset and sleep mode control */ -static int noon010_power_ctrl(struct v4l2_subdev *sd, bool reset, bool sleep) -{ - struct noon010_info *info = to_noon010(sd); - u8 reg = sleep ? 0xF1 : 0xF0; - int ret = 0; - - if (reset) { - ret = cam_i2c_write(sd, POWER_CTRL_REG, reg | 0x02); - udelay(20); - } - if (!ret) { - ret = cam_i2c_write(sd, POWER_CTRL_REG, reg); - if (reset && !ret) - info->i2c_reg_page = -1; - } - return ret; -} - -/* Automatic white balance control */ -static int noon010_enable_autowhitebalance(struct v4l2_subdev *sd, int on) -{ - int ret; - - ret = cam_i2c_write(sd, AWB_CTL_REG(1), on ? 0x2E : 0x2F); - if (!ret) - ret = cam_i2c_write(sd, AWB_CTL_REG(0), on ? 0xFB : 0x7B); - return ret; -} - -/* Called with struct noon010_info.lock mutex held */ -static int noon010_set_flip(struct v4l2_subdev *sd, int hflip, int vflip) -{ - struct noon010_info *info = to_noon010(sd); - int reg, ret; - - reg = cam_i2c_read(sd, VDO_CTL_REG(1)); - if (reg < 0) - return reg; - - reg &= 0x7C; - if (hflip) - reg |= 0x01; - if (vflip) - reg |= 0x02; - - ret = cam_i2c_write(sd, VDO_CTL_REG(1), reg | 0x80); - if (!ret) { - info->hflip = hflip; - info->vflip = vflip; - } - return ret; -} - -/* Configure resolution and color format */ -static int noon010_set_params(struct v4l2_subdev *sd) -{ - struct noon010_info *info = to_noon010(sd); - - int ret = cam_i2c_write(sd, VDO_CTL_REG(0), - info->curr_win->vid_ctl1); - if (ret) - return ret; - return cam_i2c_write(sd, ISP_CTL_REG(0), - info->curr_fmt->ispctl1_reg); -} - -/* Find nearest matching image pixel size. */ -static int noon010_try_frame_size(struct v4l2_mbus_framefmt *mf, - const struct noon010_frmsize **size) -{ - unsigned int min_err = ~0; - int i = ARRAY_SIZE(noon010_sizes); - const struct noon010_frmsize *fsize = &noon010_sizes[0], - *match = NULL; - - while (i--) { - int err = abs(fsize->width - mf->width) - + abs(fsize->height - mf->height); - - if (err < min_err) { - min_err = err; - match = fsize; - } - fsize++; - } - if (match) { - mf->width = match->width; - mf->height = match->height; - if (size) - *size = match; - return 0; - } - return -EINVAL; -} - -/* Called with info.lock mutex held */ -static int power_enable(struct noon010_info *info) -{ - int ret; - - if (info->power) { - v4l2_info(&info->sd, "%s: sensor is already on\n", __func__); - return 0; - } - - /* Assert standby: line should be flagged active low in descriptor */ - if (info->stby) - gpiod_set_value(info->stby, 1); - - /* Assert reset: line should be flagged active low in descriptor */ - if (info->reset) - gpiod_set_value(info->reset, 1); - - ret = regulator_bulk_enable(NOON010_NUM_SUPPLIES, info->supply); - if (ret) - return ret; - - /* De-assert reset and standby */ - if (info->reset) { - msleep(50); - gpiod_set_value(info->reset, 0); - } - if (info->stby) { - udelay(1000); - gpiod_set_value(info->stby, 0); - } - /* Cycle reset: assert and deassert */ - if (info->reset) { - udelay(1000); - gpiod_set_value(info->reset, 1); - msleep(100); - gpiod_set_value(info->reset, 0); - msleep(20); - } - info->power = 1; - - v4l2_dbg(1, debug, &info->sd, "%s: sensor is on\n", __func__); - return 0; -} - -/* Called with info.lock mutex held */ -static int power_disable(struct noon010_info *info) -{ - int ret; - - if (!info->power) { - v4l2_info(&info->sd, "%s: sensor is already off\n", __func__); - return 0; - } - - ret = regulator_bulk_disable(NOON010_NUM_SUPPLIES, info->supply); - if (ret) - return ret; - - /* Assert standby and reset */ - if (info->stby) - gpiod_set_value(info->stby, 1); - - if (info->reset) - gpiod_set_value(info->reset, 1); - - info->power = 0; - - v4l2_dbg(1, debug, &info->sd, "%s: sensor is off\n", __func__); - - return 0; -} - -static int noon010_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - struct noon010_info *info = to_noon010(sd); - int ret = 0; - - v4l2_dbg(1, debug, sd, "%s: ctrl_id: %d, value: %d\n", - __func__, ctrl->id, ctrl->val); - - mutex_lock(&info->lock); - /* - * If the device is not powered up by the host driver do - * not apply any controls to H/W at this time. Instead - * the controls will be restored right after power-up. - */ - if (!info->power) - goto unlock; - - switch (ctrl->id) { - case V4L2_CID_AUTO_WHITE_BALANCE: - ret = noon010_enable_autowhitebalance(sd, ctrl->val); - break; - case V4L2_CID_BLUE_BALANCE: - ret = cam_i2c_write(sd, MWB_BGAIN_REG, ctrl->val); - break; - case V4L2_CID_RED_BALANCE: - ret = cam_i2c_write(sd, MWB_RGAIN_REG, ctrl->val); - break; - default: - ret = -EINVAL; - } -unlock: - mutex_unlock(&info->lock); - return ret; -} - -static int noon010_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->index >= ARRAY_SIZE(noon010_formats)) - return -EINVAL; - - code->code = noon010_formats[code->index].code; - return 0; -} - -static int noon010_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct noon010_info *info = to_noon010(sd); - struct v4l2_mbus_framefmt *mf; - - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - if (sd_state) { - mf = v4l2_subdev_get_try_format(sd, sd_state, 0); - fmt->format = *mf; - } - return 0; - } - mf = &fmt->format; - - mutex_lock(&info->lock); - mf->width = info->curr_win->width; - mf->height = info->curr_win->height; - mf->code = info->curr_fmt->code; - mf->colorspace = info->curr_fmt->colorspace; - mf->field = V4L2_FIELD_NONE; - - mutex_unlock(&info->lock); - return 0; -} - -/* Return nearest media bus frame format. */ -static const struct noon010_format *noon010_try_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - int i = ARRAY_SIZE(noon010_formats); - - while (--i) - if (mf->code == noon010_formats[i].code) - break; - mf->code = noon010_formats[i].code; - - return &noon010_formats[i]; -} - -static int noon010_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct noon010_info *info = to_noon010(sd); - const struct noon010_frmsize *size = NULL; - const struct noon010_format *nf; - struct v4l2_mbus_framefmt *mf; - int ret = 0; - - nf = noon010_try_fmt(sd, &fmt->format); - noon010_try_frame_size(&fmt->format, &size); - fmt->format.colorspace = V4L2_COLORSPACE_JPEG; - fmt->format.field = V4L2_FIELD_NONE; - - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - if (sd_state) { - mf = v4l2_subdev_get_try_format(sd, sd_state, 0); - *mf = fmt->format; - } - return 0; - } - mutex_lock(&info->lock); - if (!info->streaming) { - info->apply_new_cfg = 1; - info->curr_fmt = nf; - info->curr_win = size; - } else { - ret = -EBUSY; - } - mutex_unlock(&info->lock); - return ret; -} - -/* Called with struct noon010_info.lock mutex held */ -static int noon010_base_config(struct v4l2_subdev *sd) -{ - int ret = noon010_bulk_write_reg(sd, noon010_base_regs); - if (!ret) - ret = noon010_set_params(sd); - if (!ret) - ret = noon010_set_flip(sd, 1, 0); - - return ret; -} - -static int noon010_s_power(struct v4l2_subdev *sd, int on) -{ - struct noon010_info *info = to_noon010(sd); - int ret; - - mutex_lock(&info->lock); - if (on) { - ret = power_enable(info); - if (!ret) - ret = noon010_base_config(sd); - } else { - noon010_power_ctrl(sd, false, true); - ret = power_disable(info); - } - mutex_unlock(&info->lock); - - /* Restore the controls state */ - if (!ret && on) - ret = v4l2_ctrl_handler_setup(&info->hdl); - - return ret; -} - -static int noon010_s_stream(struct v4l2_subdev *sd, int on) -{ - struct noon010_info *info = to_noon010(sd); - int ret = 0; - - mutex_lock(&info->lock); - if (!info->streaming != !on) { - ret = noon010_power_ctrl(sd, false, !on); - if (!ret) - info->streaming = on; - } - if (!ret && on && info->apply_new_cfg) { - ret = noon010_set_params(sd); - if (!ret) - info->apply_new_cfg = 0; - } - mutex_unlock(&info->lock); - return ret; -} - -static int noon010_log_status(struct v4l2_subdev *sd) -{ - struct noon010_info *info = to_noon010(sd); - - v4l2_ctrl_handler_log_status(&info->hdl, sd->name); - return 0; -} - -static int noon010_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - struct v4l2_mbus_framefmt *mf = v4l2_subdev_get_try_format(sd, - fh->state, - 0); - - mf->width = noon010_sizes[0].width; - mf->height = noon010_sizes[0].height; - mf->code = noon010_formats[0].code; - mf->colorspace = V4L2_COLORSPACE_JPEG; - mf->field = V4L2_FIELD_NONE; - return 0; -} - -static const struct v4l2_subdev_internal_ops noon010_subdev_internal_ops = { - .open = noon010_open, -}; - -static const struct v4l2_ctrl_ops noon010_ctrl_ops = { - .s_ctrl = noon010_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops noon010_core_ops = { - .s_power = noon010_s_power, - .log_status = noon010_log_status, -}; - -static const struct v4l2_subdev_pad_ops noon010_pad_ops = { - .enum_mbus_code = noon010_enum_mbus_code, - .get_fmt = noon010_get_fmt, - .set_fmt = noon010_set_fmt, -}; - -static const struct v4l2_subdev_video_ops noon010_video_ops = { - .s_stream = noon010_s_stream, -}; - -static const struct v4l2_subdev_ops noon010_ops = { - .core = &noon010_core_ops, - .pad = &noon010_pad_ops, - .video = &noon010_video_ops, -}; - -/* Return 0 if NOON010PC30L sensor type was detected or -ENODEV otherwise. */ -static int noon010_detect(struct i2c_client *client, struct noon010_info *info) -{ - int ret; - - ret = power_enable(info); - if (ret) - return ret; - - ret = i2c_smbus_read_byte_data(client, DEVICE_ID_REG); - if (ret < 0) - dev_err(&client->dev, "I2C read failed: 0x%X\n", ret); - - power_disable(info); - - return ret == NOON010PC30_ID ? 0 : -ENODEV; -} - -static int noon010_probe(struct i2c_client *client) -{ - struct noon010_info *info; - struct v4l2_subdev *sd; - const struct noon010pc30_platform_data *pdata - = client->dev.platform_data; - int ret; - int i; - - if (!pdata) { - dev_err(&client->dev, "No platform data!\n"); - return -EIO; - } - - info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - mutex_init(&info->lock); - sd = &info->sd; - v4l2_i2c_subdev_init(sd, client, &noon010_ops); - /* Static name; NEVER use in new drivers! */ - strscpy(sd->name, MODULE_NAME, sizeof(sd->name)); - - sd->internal_ops = &noon010_subdev_internal_ops; - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - v4l2_ctrl_handler_init(&info->hdl, 3); - - v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, - V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); - v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, - V4L2_CID_RED_BALANCE, 0, 127, 1, 64); - v4l2_ctrl_new_std(&info->hdl, &noon010_ctrl_ops, - V4L2_CID_BLUE_BALANCE, 0, 127, 1, 64); - - sd->ctrl_handler = &info->hdl; - - ret = info->hdl.error; - if (ret) - goto np_err; - - info->i2c_reg_page = -1; - info->curr_fmt = &noon010_formats[0]; - info->curr_win = &noon010_sizes[0]; - - /* Request reset asserted so we get put into reset */ - info->reset = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_HIGH); - if (IS_ERR(info->reset)) { - ret = PTR_ERR(info->reset); - goto np_err; - } - gpiod_set_consumer_name(info->reset, "NOON010PC30 NRST"); - - /* Request standby asserted so we get put into standby */ - info->stby = devm_gpiod_get(&client->dev, "standby", GPIOD_OUT_HIGH); - if (IS_ERR(info->stby)) { - ret = PTR_ERR(info->stby); - goto np_err; - } - gpiod_set_consumer_name(info->reset, "NOON010PC30 STBY"); - - for (i = 0; i < NOON010_NUM_SUPPLIES; i++) - info->supply[i].supply = noon010_supply_name[i]; - - ret = devm_regulator_bulk_get(&client->dev, NOON010_NUM_SUPPLIES, - info->supply); - if (ret) - goto np_err; - - info->pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; - ret = media_entity_pads_init(&sd->entity, 1, &info->pad); - if (ret < 0) - goto np_err; - - ret = noon010_detect(client, info); - if (!ret) - return 0; - -np_err: - v4l2_ctrl_handler_free(&info->hdl); - v4l2_device_unregister_subdev(sd); - return ret; -} - -static void noon010_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct noon010_info *info = to_noon010(sd); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(&info->hdl); - media_entity_cleanup(&sd->entity); -} - -static const struct i2c_device_id noon010_id[] = { - { MODULE_NAME, 0 }, - { }, -}; -MODULE_DEVICE_TABLE(i2c, noon010_id); - - -static struct i2c_driver noon010_i2c_driver = { - .driver = { - .name = MODULE_NAME - }, - .probe_new = noon010_probe, - .remove = noon010_remove, - .id_table = noon010_id, -}; - -module_i2c_driver(noon010_i2c_driver); - -MODULE_DESCRIPTION("Siliconfile NOON010PC30 camera driver"); -MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/ov13b10.c b/drivers/media/i2c/ov13b10.c index 549e5d93e568..c1430044fb1e 100644 --- a/drivers/media/i2c/ov13b10.c +++ b/drivers/media/i2c/ov13b10.c @@ -243,7 +243,6 @@ static const struct ov13b10_reg mipi_data_rate_1120mbps[] = { {0x5047, 0xa4}, {0x5048, 0x20}, {0x5049, 0xa4}, - {0x0100, 0x01}, }; static const struct ov13b10_reg mode_4208x3120_regs[] = { @@ -589,6 +588,9 @@ struct ov13b10 { /* Streaming on/off */ bool streaming; + + /* True if the device has been identified */ + bool identified; }; #define to_ov13b10(_sd) container_of(_sd, struct ov13b10, sd) @@ -1023,12 +1025,42 @@ ov13b10_set_pad_format(struct v4l2_subdev *sd, return 0; } +/* Verify chip ID */ +static int ov13b10_identify_module(struct ov13b10 *ov13b) +{ + struct i2c_client *client = v4l2_get_subdevdata(&ov13b->sd); + int ret; + u32 val; + + if (ov13b->identified) + return 0; + + ret = ov13b10_read_reg(ov13b, OV13B10_REG_CHIP_ID, + OV13B10_REG_VALUE_24BIT, &val); + if (ret) + return ret; + + if (val != OV13B10_CHIP_ID) { + dev_err(&client->dev, "chip id mismatch: %x!=%x\n", + OV13B10_CHIP_ID, val); + return -EIO; + } + + ov13b->identified = true; + + return 0; +} + static int ov13b10_start_streaming(struct ov13b10 *ov13b) { struct i2c_client *client = v4l2_get_subdevdata(&ov13b->sd); const struct ov13b10_reg_list *reg_list; int ret, link_freq_index; + ret = ov13b10_identify_module(ov13b); + if (ret) + return ret; + /* Get out of from software reset */ ret = ov13b10_write_reg(ov13b, OV13B10_REG_SOFTWARE_RST, OV13B10_REG_VALUE_08BIT, OV13B10_SOFTWARE_RST); @@ -1144,27 +1176,6 @@ error: return ret; } -/* Verify chip ID */ -static int ov13b10_identify_module(struct ov13b10 *ov13b) -{ - struct i2c_client *client = v4l2_get_subdevdata(&ov13b->sd); - int ret; - u32 val; - - ret = ov13b10_read_reg(ov13b, OV13B10_REG_CHIP_ID, - OV13B10_REG_VALUE_24BIT, &val); - if (ret) - return ret; - - if (val != OV13B10_CHIP_ID) { - dev_err(&client->dev, "chip id mismatch: %x!=%x\n", - OV13B10_CHIP_ID, val); - return -EIO; - } - - return 0; -} - static const struct v4l2_subdev_video_ops ov13b10_video_ops = { .s_stream = ov13b10_set_stream, }; @@ -1379,6 +1390,7 @@ out_err: static int ov13b10_probe(struct i2c_client *client) { struct ov13b10 *ov13b; + bool full_power; int ret; /* Check HW config */ @@ -1395,11 +1407,14 @@ static int ov13b10_probe(struct i2c_client *client) /* Initialize subdev */ v4l2_i2c_subdev_init(&ov13b->sd, client, &ov13b10_subdev_ops); - /* Check module identity */ - ret = ov13b10_identify_module(ov13b); - if (ret) { - dev_err(&client->dev, "failed to find sensor: %d\n", ret); - return ret; + full_power = acpi_dev_state_d0(&client->dev); + if (full_power) { + /* Check module identity */ + ret = ov13b10_identify_module(ov13b); + if (ret) { + dev_err(&client->dev, "failed to find sensor: %d\n", ret); + return ret; + } } /* Set default mode to max resolution */ @@ -1431,7 +1446,10 @@ static int ov13b10_probe(struct i2c_client *client) * Device is already turned on by i2c-core with ACPI domain PM. * Enable runtime PM and turn off the device. */ - pm_runtime_set_active(&client->dev); + + /* Set the device's state to active if it's in D0 state. */ + if (full_power) + pm_runtime_set_active(&client->dev); pm_runtime_enable(&client->dev); pm_runtime_idle(&client->dev); @@ -1480,6 +1498,7 @@ static struct i2c_driver ov13b10_i2c_driver = { }, .probe_new = ov13b10_probe, .remove = ov13b10_remove, + .flags = I2C_DRV_ACPI_WAIVE_D0_PROBE, }; module_i2c_driver(ov13b10_i2c_driver); diff --git a/drivers/media/i2c/ov2685.c b/drivers/media/i2c/ov2685.c index 1c80b121e7d6..f119a93e7c64 100644 --- a/drivers/media/i2c/ov2685.c +++ b/drivers/media/i2c/ov2685.c @@ -17,6 +17,7 @@ #include <media/media-entity.h> #include <media/v4l2-async.h> #include <media/v4l2-ctrls.h> +#include <media/v4l2-fwnode.h> #include <media/v4l2-subdev.h> #define CHIP_ID 0x2685 @@ -55,6 +56,9 @@ #define OV2685_REG_VALUE_16BIT 2 #define OV2685_REG_VALUE_24BIT 3 +#define OV2685_NATIVE_WIDTH 1616 +#define OV2685_NATIVE_HEIGHT 1216 + #define OV2685_LANES 1 #define OV2685_BITS_PER_SAMPLE 10 @@ -77,6 +81,7 @@ struct ov2685_mode { u32 exp_def; u32 hts_def; u32 vts_def; + const struct v4l2_rect *analog_crop; const struct regval *reg_list; }; @@ -230,6 +235,13 @@ static const int ov2685_test_pattern_val[] = { OV2685_TEST_PATTERN_COLOR_SQUARE, }; +static const struct v4l2_rect ov2685_analog_crop = { + .left = 8, + .top = 8, + .width = 1600, + .height = 1200, +}; + static const struct ov2685_mode supported_modes[] = { { .width = 1600, @@ -237,6 +249,7 @@ static const struct ov2685_mode supported_modes[] = { .exp_def = 0x04ee, .hts_def = 0x06a4, .vts_def = 0x050e, + .analog_crop = &ov2685_analog_crop, .reg_list = ov2685_1600x1200_regs, }, }; @@ -383,6 +396,53 @@ static int ov2685_enum_frame_sizes(struct v4l2_subdev *sd, return 0; } +static const struct v4l2_rect * +__ov2685_get_pad_crop(struct ov2685 *ov2685, + struct v4l2_subdev_state *state, unsigned int pad, + enum v4l2_subdev_format_whence which) +{ + const struct ov2685_mode *mode = ov2685->cur_mode; + + switch (which) { + case V4L2_SUBDEV_FORMAT_TRY: + return v4l2_subdev_get_try_crop(&ov2685->subdev, state, pad); + case V4L2_SUBDEV_FORMAT_ACTIVE: + return mode->analog_crop; + } + + return NULL; +} + +static int ov2685_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + struct ov2685 *ov2685 = to_ov2685(sd); + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: + mutex_lock(&ov2685->mutex); + sel->r = *__ov2685_get_pad_crop(ov2685, sd_state, sel->pad, + sel->which); + mutex_unlock(&ov2685->mutex); + break; + case V4L2_SEL_TGT_NATIVE_SIZE: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = OV2685_NATIVE_WIDTH; + sel->r.height = OV2685_NATIVE_HEIGHT; + break; + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r = ov2685_analog_crop; + break; + default: + return -EINVAL; + } + + return 0; +} + /* Calculate the delay in us by clock rate and clock cycles */ static inline u32 ov2685_cal_delay(u32 cycles) { @@ -419,8 +479,10 @@ static int __ov2685_power_on(struct ov2685 *ov2685) * writing register before .s_stream() as a workaround */ ret = ov2685_write_array(ov2685->client, ov2685->cur_mode->reg_list); - if (ret) + if (ret) { + dev_err(dev, "Failed to set regs for power on\n"); goto disable_supplies; + } return 0; @@ -589,6 +651,8 @@ static const struct v4l2_subdev_pad_ops ov2685_pad_ops = { .enum_frame_size = ov2685_enum_frame_sizes, .get_fmt = ov2685_get_fmt, .set_fmt = ov2685_set_fmt, + .get_selection = ov2685_get_selection, + .set_selection = ov2685_get_selection, }; static const struct v4l2_subdev_ops ov2685_subdev_ops = { @@ -611,13 +675,14 @@ static int ov2685_initialize_controls(struct ov2685 *ov2685) const struct ov2685_mode *mode; struct v4l2_ctrl_handler *handler; struct v4l2_ctrl *ctrl; + struct v4l2_fwnode_device_properties props; u64 exposure_max; u32 pixel_rate, h_blank; int ret; handler = &ov2685->ctrl_handler; mode = ov2685->cur_mode; - ret = v4l2_ctrl_handler_init(handler, 8); + ret = v4l2_ctrl_handler_init(handler, 10); if (ret) return ret; handler->lock = &ov2685->mutex; @@ -659,6 +724,15 @@ static int ov2685_initialize_controls(struct ov2685 *ov2685) ARRAY_SIZE(ov2685_test_pattern_menu) - 1, 0, 0, ov2685_test_pattern_menu); + /* set properties from fwnode (e.g. rotation, orientation) */ + ret = v4l2_fwnode_device_parse(&ov2685->client->dev, &props); + if (ret) + goto err_free_handler; + + ret = v4l2_ctrl_new_fwnode_properties(handler, &ov2685_ctrl_ops, &props); + if (ret) + goto err_free_handler; + if (handler->error) { ret = handler->error; dev_err(&ov2685->client->dev, @@ -733,7 +807,7 @@ static int ov2685_probe(struct i2c_client *client) if (clk_get_rate(ov2685->xvclk) != OV2685_XVCLK_FREQ) dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); - ov2685->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + ov2685->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ov2685->reset_gpio)) { dev_err(dev, "Failed to get reset-gpios\n"); return -EINVAL; diff --git a/drivers/media/i2c/ov5647.c b/drivers/media/i2c/ov5647.c index 847a7bbb69c5..233576ee9503 100644 --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -58,6 +58,7 @@ #define OV5647_REG_MIPI_CTRL00 0x4800 #define OV5647_REG_MIPI_CTRL14 0x4814 #define OV5647_REG_AWB 0x5001 +#define OV5647_REG_ISPCTRL3D 0x503d #define REG_TERM 0xfffe #define VAL_TERM 0xfe @@ -116,6 +117,20 @@ static inline struct ov5647 *to_sensor(struct v4l2_subdev *sd) return container_of(sd, struct ov5647, sd); } +static const char * const ov5647_test_pattern_menu[] = { + "Disabled", + "Color Bars", + "Color Squares", + "Random Data", +}; + +static const u8 ov5647_test_pattern_val[] = { + 0x00, /* Disabled */ + 0x80, /* Color Bars */ + 0x82, /* Color Squares */ + 0x81, /* Random Data */ +}; + static const struct regval_list sensor_oe_disable_regs[] = { {0x3000, 0x00}, {0x3001, 0x00}, @@ -614,23 +629,29 @@ static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val) static int ov5647_read(struct v4l2_subdev *sd, u16 reg, u8 *val) { - unsigned char data_w[2] = { reg >> 8, reg & 0xff }; struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 buf[2] = { reg >> 8, reg & 0xff }; + struct i2c_msg msg[2]; int ret; - ret = i2c_master_send(client, data_w, 2); - if (ret < 0) { - dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n", - __func__, reg); - return ret; + msg[0].addr = client->addr; + msg[0].flags = client->flags; + msg[0].buf = buf; + msg[0].len = sizeof(buf); + + msg[1].addr = client->addr; + msg[1].flags = client->flags | I2C_M_RD; + msg[1].buf = buf; + msg[1].len = 1; + + ret = i2c_transfer(client->adapter, msg, 2); + if (ret != 2) { + dev_err(&client->dev, "%s: i2c read error, reg: %x = %d\n", + __func__, reg, ret); + return ret >= 0 ? -EINVAL : ret; } - ret = i2c_master_recv(client, val, 1); - if (ret < 0) { - dev_dbg(&client->dev, "%s: i2c read error, reg: %x\n", - __func__, reg); - return ret; - } + *val = buf[0]; return 0; } @@ -1242,6 +1263,10 @@ static int ov5647_s_ctrl(struct v4l2_ctrl *ctrl) ret = ov5647_write16(sd, OV5647_REG_VTS_HI, sensor->mode->format.height + ctrl->val); break; + case V4L2_CID_TEST_PATTERN: + ret = ov5647_write(sd, OV5647_REG_ISPCTRL3D, + ov5647_test_pattern_val[ctrl->val]); + break; /* Read-only, but we adjust it based on mode. */ case V4L2_CID_PIXEL_RATE: @@ -1270,7 +1295,7 @@ static int ov5647_init_controls(struct ov5647 *sensor) struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd); int hblank, exposure_max, exposure_def; - v4l2_ctrl_handler_init(&sensor->ctrls, 8); + v4l2_ctrl_handler_init(&sensor->ctrls, 9); v4l2_ctrl_new_std(&sensor->ctrls, &ov5647_ctrl_ops, V4L2_CID_AUTOGAIN, 0, 1, 1, 0); @@ -1314,6 +1339,11 @@ static int ov5647_init_controls(struct ov5647 *sensor) sensor->mode->vts - sensor->mode->format.height); + v4l2_ctrl_new_std_menu_items(&sensor->ctrls, &ov5647_ctrl_ops, + V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(ov5647_test_pattern_menu) - 1, + 0, 0, ov5647_test_pattern_menu); + if (sensor->ctrls.error) goto handler_free; diff --git a/drivers/media/i2c/ov5670.c b/drivers/media/i2c/ov5670.c index f79d908f4531..c026610d0f31 100644 --- a/drivers/media/i2c/ov5670.c +++ b/drivers/media/i2c/ov5670.c @@ -29,6 +29,12 @@ #define OV5670_REG_SOFTWARE_RST 0x0103 #define OV5670_SOFTWARE_RST 0x01 +#define OV5670_MIPI_SC_CTRL0_REG 0x3018 +#define OV5670_MIPI_SC_CTRL0_LANES(v) ((((v) - 1) << 5) & \ + GENMASK(7, 5)) +#define OV5670_MIPI_SC_CTRL0_MIPI_EN BIT(4) +#define OV5670_MIPI_SC_CTRL0_RESERVED BIT(1) + /* vertical-timings from sensor */ #define OV5670_REG_VTS 0x380e #define OV5670_VTS_30FPS 0x0808 /* default for 30 fps */ @@ -92,7 +98,6 @@ struct ov5670_reg_list { }; struct ov5670_link_freq_config { - u32 pixel_rate; const struct ov5670_reg_list reg_list; }; @@ -163,7 +168,6 @@ static const struct ov5670_reg mode_2592x1944_regs[] = { {0x3005, 0xf0}, {0x3007, 0x00}, {0x3015, 0x0f}, - {0x3018, 0x32}, {0x301a, 0xf0}, {0x301b, 0xf0}, {0x301c, 0xf0}, @@ -429,7 +433,6 @@ static const struct ov5670_reg mode_1296x972_regs[] = { {0x3005, 0xf0}, {0x3007, 0x00}, {0x3015, 0x0f}, - {0x3018, 0x32}, {0x301a, 0xf0}, {0x301b, 0xf0}, {0x301c, 0xf0}, @@ -695,7 +698,6 @@ static const struct ov5670_reg mode_648x486_regs[] = { {0x3005, 0xf0}, {0x3007, 0x00}, {0x3015, 0x0f}, - {0x3018, 0x32}, {0x301a, 0xf0}, {0x301b, 0xf0}, {0x301c, 0xf0}, @@ -961,7 +963,6 @@ static const struct ov5670_reg mode_2560x1440_regs[] = { {0x3005, 0xf0}, {0x3007, 0x00}, {0x3015, 0x0f}, - {0x3018, 0x32}, {0x301a, 0xf0}, {0x301b, 0xf0}, {0x301c, 0xf0}, @@ -1226,7 +1227,6 @@ static const struct ov5670_reg mode_1280x720_regs[] = { {0x3005, 0xf0}, {0x3007, 0x00}, {0x3015, 0x0f}, - {0x3018, 0x32}, {0x301a, 0xf0}, {0x301b, 0xf0}, {0x301c, 0xf0}, @@ -1492,7 +1492,6 @@ static const struct ov5670_reg mode_640x360_regs[] = { {0x3005, 0xf0}, {0x3007, 0x00}, {0x3015, 0x0f}, - {0x3018, 0x32}, {0x301a, 0xf0}, {0x301b, 0xf0}, {0x301c, 0xf0}, @@ -1762,8 +1761,6 @@ static const char * const ov5670_test_pattern_menu[] = { #define OV5670_LINK_FREQ_422MHZ_INDEX 0 static const struct ov5670_link_freq_config link_freq_configs[] = { { - /* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ - .pixel_rate = (OV5670_LINK_FREQ_422MHZ * 2 * 2) / 10, .reg_list = { .num_of_regs = ARRAY_SIZE(mipi_data_rate_840mbps), .regs = mipi_data_rate_840mbps, @@ -1859,6 +1856,7 @@ static const struct ov5670_mode supported_modes[] = { struct ov5670 { struct v4l2_subdev sd; struct media_pad pad; + struct v4l2_fwnode_endpoint endpoint; struct v4l2_ctrl_handler ctrl_handler; /* V4L2 Controls */ @@ -2101,9 +2099,13 @@ static const struct v4l2_ctrl_ops ov5670_ctrl_ops = { /* Initialize control handlers */ static int ov5670_init_controls(struct ov5670 *ov5670) { + struct v4l2_mbus_config_mipi_csi2 *bus_mipi_csi2 = + &ov5670->endpoint.bus.mipi_csi2; struct i2c_client *client = v4l2_get_subdevdata(&ov5670->sd); struct v4l2_fwnode_device_properties props; struct v4l2_ctrl_handler *ctrl_hdlr; + unsigned int lanes_count; + s64 mipi_pixel_rate; s64 vblank_max; s64 vblank_def; s64 vblank_min; @@ -2124,12 +2126,15 @@ static int ov5670_init_controls(struct ov5670 *ov5670) ov5670->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; /* By default, V4L2_CID_PIXEL_RATE is read only */ + lanes_count = bus_mipi_csi2->num_data_lanes; + mipi_pixel_rate = OV5670_LINK_FREQ_422MHZ * 2 * lanes_count / 10; + ov5670->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &ov5670_ctrl_ops, V4L2_CID_PIXEL_RATE, - link_freq_configs[0].pixel_rate, - link_freq_configs[0].pixel_rate, + mipi_pixel_rate, + mipi_pixel_rate, 1, - link_freq_configs[0].pixel_rate); + mipi_pixel_rate); vblank_max = OV5670_VTS_MAX - ov5670->cur_mode->height; vblank_def = ov5670->cur_mode->vts_def - ov5670->cur_mode->height; @@ -2288,8 +2293,13 @@ static int ov5670_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_format *fmt) { struct ov5670 *ov5670 = to_ov5670(sd); + struct v4l2_mbus_config_mipi_csi2 *bus_mipi_csi2 = + &ov5670->endpoint.bus.mipi_csi2; const struct ov5670_mode *mode; + unsigned int lanes_count; + s64 mipi_pixel_rate; s32 vblank_def; + s64 link_freq; s32 h_blank; mutex_lock(&ov5670->mutex); @@ -2306,9 +2316,14 @@ static int ov5670_set_pad_format(struct v4l2_subdev *sd, } else { ov5670->cur_mode = mode; __v4l2_ctrl_s_ctrl(ov5670->link_freq, mode->link_freq_index); + + lanes_count = bus_mipi_csi2->num_data_lanes; + link_freq = link_freq_menu_items[mode->link_freq_index]; + /* pixel_rate = link_freq * 2 * nr_of_lanes / bits_per_sample */ + mipi_pixel_rate = div_s64(link_freq * 2 * lanes_count, 10); __v4l2_ctrl_s_ctrl_int64( ov5670->pixel_rate, - link_freq_configs[mode->link_freq_index].pixel_rate); + mipi_pixel_rate); /* Update limits and set FPS to default */ vblank_def = ov5670->cur_mode->vts_def - ov5670->cur_mode->height; @@ -2361,6 +2376,19 @@ static int ov5670_identify_module(struct ov5670 *ov5670) return 0; } +static int ov5670_mipi_configure(struct ov5670 *ov5670) +{ + struct v4l2_mbus_config_mipi_csi2 *bus_mipi_csi2 = + &ov5670->endpoint.bus.mipi_csi2; + unsigned int lanes_count = bus_mipi_csi2->num_data_lanes; + + return ov5670_write_reg(ov5670, OV5670_MIPI_SC_CTRL0_REG, + OV5670_REG_VALUE_08BIT, + OV5670_MIPI_SC_CTRL0_LANES(lanes_count) | + OV5670_MIPI_SC_CTRL0_MIPI_EN | + OV5670_MIPI_SC_CTRL0_RESERVED); +} + /* Prepare streaming by writing default values and customized values */ static int ov5670_start_streaming(struct ov5670 *ov5670) { @@ -2399,6 +2427,12 @@ static int ov5670_start_streaming(struct ov5670 *ov5670) return ret; } + ret = ov5670_mipi_configure(ov5670); + if (ret) { + dev_err(&client->dev, "%s failed to configure MIPI\n", __func__); + return ret; + } + ret = __v4l2_ctrl_handler_setup(ov5670->sd.ctrl_handler); if (ret) return ret; @@ -2647,23 +2681,20 @@ static int ov5670_gpio_probe(struct ov5670 *ov5670) static int ov5670_probe(struct i2c_client *client) { + struct fwnode_handle *handle; struct ov5670 *ov5670; - const char *err_msg; u32 input_clk = 0; bool full_power; int ret; ov5670 = devm_kzalloc(&client->dev, sizeof(*ov5670), GFP_KERNEL); - if (!ov5670) { - ret = -ENOMEM; - err_msg = "devm_kzalloc() error"; - goto error_print; - } + if (!ov5670) + return -ENOMEM; - ov5670->xvclk = devm_clk_get(&client->dev, NULL); + ov5670->xvclk = devm_clk_get_optional(&client->dev, NULL); if (!IS_ERR_OR_NULL(ov5670->xvclk)) input_clk = clk_get_rate(ov5670->xvclk); - else if (PTR_ERR(ov5670->xvclk) == -ENOENT) + else if (!ov5670->xvclk || PTR_ERR(ov5670->xvclk) == -ENOENT) device_property_read_u32(&client->dev, "clock-frequency", &input_clk); else @@ -2680,29 +2711,38 @@ static int ov5670_probe(struct i2c_client *client) v4l2_i2c_subdev_init(&ov5670->sd, client, &ov5670_subdev_ops); ret = ov5670_regulators_probe(ov5670); - if (ret) { - err_msg = "Regulators probe failed"; - goto error_print; - } + if (ret) + return dev_err_probe(&client->dev, ret, "Regulators probe failed\n"); ret = ov5670_gpio_probe(ov5670); - if (ret) { - err_msg = "GPIO probe failed"; - goto error_print; - } + if (ret) + return dev_err_probe(&client->dev, ret, "GPIO probe failed\n"); + + /* Graph Endpoint */ + handle = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL); + if (!handle) + return dev_err_probe(&client->dev, -ENXIO, "Endpoint for node get failed\n"); + + ov5670->endpoint.bus_type = V4L2_MBUS_CSI2_DPHY; + ov5670->endpoint.bus.mipi_csi2.num_data_lanes = 2; + + ret = v4l2_fwnode_endpoint_alloc_parse(handle, &ov5670->endpoint); + fwnode_handle_put(handle); + if (ret) + return dev_err_probe(&client->dev, ret, "Endpoint parse failed\n"); full_power = acpi_dev_state_d0(&client->dev); if (full_power) { ret = ov5670_runtime_resume(&client->dev); if (ret) { - err_msg = "Power up failed"; - goto error_print; + dev_err_probe(&client->dev, ret, "Power up failed\n"); + goto error_endpoint; } /* Check module identity */ ret = ov5670_identify_module(ov5670); if (ret) { - err_msg = "ov5670_identify_module() error"; + dev_err_probe(&client->dev, ret, "ov5670_identify_module() error\n"); goto error_power_off; } } @@ -2714,7 +2754,7 @@ static int ov5670_probe(struct i2c_client *client) ret = ov5670_init_controls(ov5670); if (ret) { - err_msg = "ov5670_init_controls() error"; + dev_err_probe(&client->dev, ret, "ov5670_init_controls() error\n"); goto error_mutex_destroy; } @@ -2727,7 +2767,7 @@ static int ov5670_probe(struct i2c_client *client) ov5670->pad.flags = MEDIA_PAD_FL_SOURCE; ret = media_entity_pads_init(&ov5670->sd.entity, 1, &ov5670->pad); if (ret) { - err_msg = "media_entity_pads_init() error"; + dev_err_probe(&client->dev, ret, "media_entity_pads_init() error\n"); goto error_handler_free; } @@ -2741,7 +2781,7 @@ static int ov5670_probe(struct i2c_client *client) /* Async register for subdev */ ret = v4l2_async_register_subdev_sensor(&ov5670->sd); if (ret < 0) { - err_msg = "v4l2_async_register_subdev() error"; + dev_err_probe(&client->dev, ret, "v4l2_async_register_subdev() error\n"); goto error_pm_disable; } @@ -2764,8 +2804,8 @@ error_power_off: if (full_power) ov5670_runtime_suspend(&client->dev); -error_print: - dev_err(&client->dev, "%s: %s %d\n", __func__, err_msg, ret); +error_endpoint: + v4l2_fwnode_endpoint_free(&ov5670->endpoint); return ret; } @@ -2782,6 +2822,8 @@ static void ov5670_remove(struct i2c_client *client) pm_runtime_disable(&client->dev); ov5670_runtime_suspend(&client->dev); + + v4l2_fwnode_endpoint_free(&ov5670->endpoint); } static const struct dev_pm_ops ov5670_pm_ops = { diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index b1bb0833571e..ecbded4f0765 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c @@ -1894,14 +1894,9 @@ static int ov7670_probe(struct i2c_client *client) info->pclk_hb_disable = true; } - info->clk = devm_clk_get(&client->dev, "xclk"); /* optional */ - if (IS_ERR(info->clk)) { - ret = PTR_ERR(info->clk); - if (ret == -ENOENT) - info->clk = NULL; - else - return ret; - } + info->clk = devm_clk_get_optional(&client->dev, "xclk"); + if (IS_ERR(info->clk)) + return PTR_ERR(info->clk); ret = ov7670_init_gpio(client, info); if (ret) diff --git a/drivers/media/i2c/ov8856.c b/drivers/media/i2c/ov8856.c index cf8384e09413..b5c7881383ca 100644 --- a/drivers/media/i2c/ov8856.c +++ b/drivers/media/i2c/ov8856.c @@ -1709,46 +1709,6 @@ static int ov8856_identify_module(struct ov8856 *ov8856) return -ENXIO; } - ret = ov8856_write_reg(ov8856, OV8856_REG_MODE_SELECT, - OV8856_REG_VALUE_08BIT, OV8856_MODE_STREAMING); - if (ret) - return ret; - - ret = ov8856_write_reg(ov8856, OV8856_OTP_MODE_CTRL, - OV8856_REG_VALUE_08BIT, OV8856_OTP_MODE_AUTO); - if (ret) { - dev_err(&client->dev, "failed to set otp mode"); - return ret; - } - - ret = ov8856_write_reg(ov8856, OV8856_OTP_LOAD_CTRL, - OV8856_REG_VALUE_08BIT, - OV8856_OTP_LOAD_CTRL_ENABLE); - if (ret) { - dev_err(&client->dev, "failed to enable load control"); - return ret; - } - - ret = ov8856_read_reg(ov8856, OV8856_MODULE_REVISION, - OV8856_REG_VALUE_08BIT, &val); - if (ret) { - dev_err(&client->dev, "failed to read module revision"); - return ret; - } - - dev_info(&client->dev, "OV8856 revision %x (%s) at address 0x%02x\n", - val, - val == OV8856_2A_MODULE ? "2A" : - val == OV8856_1B_MODULE ? "1B" : "unknown revision", - client->addr); - - ret = ov8856_write_reg(ov8856, OV8856_REG_MODE_SELECT, - OV8856_REG_VALUE_08BIT, OV8856_MODE_STANDBY); - if (ret) { - dev_err(&client->dev, "failed to exit streaming mode"); - return ret; - } - ov8856->identified = true; return 0; diff --git a/drivers/media/i2c/s5k6aa.c b/drivers/media/i2c/s5k6aa.c deleted file mode 100644 index 5996153371fc..000000000000 --- a/drivers/media/i2c/s5k6aa.c +++ /dev/null @@ -1,1652 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Driver for Samsung S5K6AAFX SXGA 1/6" 1.3M CMOS Image Sensor - * with embedded SoC ISP. - * - * Copyright (C) 2011, Samsung Electronics Co., Ltd. - * Sylwester Nawrocki <s.nawrocki@samsung.com> - * - * Based on a driver authored by Dongsoo Nathaniel Kim. - * Copyright (C) 2009, Dongsoo Nathaniel Kim <dongsoo45.kim@samsung.com> - */ - -#include <linux/clk.h> -#include <linux/delay.h> -#include <linux/gpio.h> -#include <linux/i2c.h> -#include <linux/media.h> -#include <linux/module.h> -#include <linux/regulator/consumer.h> -#include <linux/slab.h> - -#include <media/media-entity.h> -#include <media/v4l2-ctrls.h> -#include <media/v4l2-device.h> -#include <media/v4l2-subdev.h> -#include <media/v4l2-mediabus.h> -#include <media/i2c/s5k6aa.h> - -static int debug; -module_param(debug, int, 0644); - -#define DRIVER_NAME "S5K6AA" - -/* The token to indicate array termination */ -#define S5K6AA_TERM 0xffff -#define S5K6AA_OUT_WIDTH_DEF 640 -#define S5K6AA_OUT_HEIGHT_DEF 480 -#define S5K6AA_WIN_WIDTH_MAX 1280 -#define S5K6AA_WIN_HEIGHT_MAX 1024 -#define S5K6AA_WIN_WIDTH_MIN 8 -#define S5K6AA_WIN_HEIGHT_MIN 8 - -/* - * H/W register Interface (0xD0000000 - 0xD0000FFF) - */ -#define AHB_MSB_ADDR_PTR 0xfcfc -#define GEN_REG_OFFSH 0xd000 -#define REG_CMDWR_ADDRH 0x0028 -#define REG_CMDWR_ADDRL 0x002a -#define REG_CMDRD_ADDRH 0x002c -#define REG_CMDRD_ADDRL 0x002e -#define REG_CMDBUF0_ADDR 0x0f12 -#define REG_CMDBUF1_ADDR 0x0f10 - -/* - * Host S/W Register interface (0x70000000 - 0x70002000) - * The value of the two most significant address bytes is 0x7000, - * (HOST_SWIF_OFFS_H). The register addresses below specify 2 LSBs. - */ -#define HOST_SWIF_OFFSH 0x7000 - -/* Initialization parameters */ -/* Master clock frequency in KHz */ -#define REG_I_INCLK_FREQ_L 0x01b8 -#define REG_I_INCLK_FREQ_H 0x01ba -#define MIN_MCLK_FREQ_KHZ 6000U -#define MAX_MCLK_FREQ_KHZ 27000U -#define REG_I_USE_NPVI_CLOCKS 0x01c6 -#define REG_I_USE_NMIPI_CLOCKS 0x01c8 - -/* Clock configurations, n = 0..2. REG_I_* frequency unit is 4 kHz. */ -#define REG_I_OPCLK_4KHZ(n) ((n) * 6 + 0x01cc) -#define REG_I_MIN_OUTRATE_4KHZ(n) ((n) * 6 + 0x01ce) -#define REG_I_MAX_OUTRATE_4KHZ(n) ((n) * 6 + 0x01d0) -#define SYS_PLL_OUT_FREQ (48000000 / 4000) -#define PCLK_FREQ_MIN (24000000 / 4000) -#define PCLK_FREQ_MAX (48000000 / 4000) -#define REG_I_INIT_PARAMS_UPDATED 0x01e0 -#define REG_I_ERROR_INFO 0x01e2 - -/* General purpose parameters */ -#define REG_USER_BRIGHTNESS 0x01e4 -#define REG_USER_CONTRAST 0x01e6 -#define REG_USER_SATURATION 0x01e8 -#define REG_USER_SHARPBLUR 0x01ea - -#define REG_G_SPEC_EFFECTS 0x01ee -#define REG_G_ENABLE_PREV 0x01f0 -#define REG_G_ENABLE_PREV_CHG 0x01f2 -#define REG_G_NEW_CFG_SYNC 0x01f8 -#define REG_G_PREVZOOM_IN_WIDTH 0x020a -#define REG_G_PREVZOOM_IN_HEIGHT 0x020c -#define REG_G_PREVZOOM_IN_XOFFS 0x020e -#define REG_G_PREVZOOM_IN_YOFFS 0x0210 -#define REG_G_INPUTS_CHANGE_REQ 0x021a -#define REG_G_ACTIVE_PREV_CFG 0x021c -#define REG_G_PREV_CFG_CHG 0x021e -#define REG_G_PREV_OPEN_AFTER_CH 0x0220 -#define REG_G_PREV_CFG_ERROR 0x0222 - -/* Preview control section. n = 0...4. */ -#define PREG(n, x) ((n) * 0x26 + x) -#define REG_P_OUT_WIDTH(n) PREG(n, 0x0242) -#define REG_P_OUT_HEIGHT(n) PREG(n, 0x0244) -#define REG_P_FMT(n) PREG(n, 0x0246) -#define REG_P_MAX_OUT_RATE(n) PREG(n, 0x0248) -#define REG_P_MIN_OUT_RATE(n) PREG(n, 0x024a) -#define REG_P_PVI_MASK(n) PREG(n, 0x024c) -#define REG_P_CLK_INDEX(n) PREG(n, 0x024e) -#define REG_P_FR_RATE_TYPE(n) PREG(n, 0x0250) -#define FR_RATE_DYNAMIC 0 -#define FR_RATE_FIXED 1 -#define FR_RATE_FIXED_ACCURATE 2 -#define REG_P_FR_RATE_Q_TYPE(n) PREG(n, 0x0252) -#define FR_RATE_Q_BEST_FRRATE 1 /* Binning enabled */ -#define FR_RATE_Q_BEST_QUALITY 2 /* Binning disabled */ -/* Frame period in 0.1 ms units */ -#define REG_P_MAX_FR_TIME(n) PREG(n, 0x0254) -#define REG_P_MIN_FR_TIME(n) PREG(n, 0x0256) -/* Conversion to REG_P_[MAX/MIN]_FR_TIME value; __t: time in us */ -#define US_TO_FR_TIME(__t) ((__t) / 100) -#define S5K6AA_MIN_FR_TIME 33300 /* us */ -#define S5K6AA_MAX_FR_TIME 650000 /* us */ -#define S5K6AA_MAX_HIGHRES_FR_TIME 666 /* x100 us */ -/* The below 5 registers are for "device correction" values */ -#define REG_P_COLORTEMP(n) PREG(n, 0x025e) -#define REG_P_PREV_MIRROR(n) PREG(n, 0x0262) - -/* Extended image property controls */ -/* Exposure time in 10 us units */ -#define REG_SF_USR_EXPOSURE_L 0x03c6 -#define REG_SF_USR_EXPOSURE_H 0x03c8 -#define REG_SF_USR_EXPOSURE_CHG 0x03ca -#define REG_SF_USR_TOT_GAIN 0x03cc -#define REG_SF_USR_TOT_GAIN_CHG 0x03ce -#define REG_SF_RGAIN 0x03d0 -#define REG_SF_RGAIN_CHG 0x03d2 -#define REG_SF_GGAIN 0x03d4 -#define REG_SF_GGAIN_CHG 0x03d6 -#define REG_SF_BGAIN 0x03d8 -#define REG_SF_BGAIN_CHG 0x03da -#define REG_SF_FLICKER_QUANT 0x03dc -#define REG_SF_FLICKER_QUANT_CHG 0x03de - -/* Output interface (parallel/MIPI) setup */ -#define REG_OIF_EN_MIPI_LANES 0x03fa -#define REG_OIF_EN_PACKETS 0x03fc -#define REG_OIF_CFG_CHG 0x03fe - -/* Auto-algorithms enable mask */ -#define REG_DBG_AUTOALG_EN 0x0400 -#define AALG_ALL_EN_MASK (1 << 0) -#define AALG_AE_EN_MASK (1 << 1) -#define AALG_DIVLEI_EN_MASK (1 << 2) -#define AALG_WB_EN_MASK (1 << 3) -#define AALG_FLICKER_EN_MASK (1 << 5) -#define AALG_FIT_EN_MASK (1 << 6) -#define AALG_WRHW_EN_MASK (1 << 7) - -/* Firmware revision information */ -#define REG_FW_APIVER 0x012e -#define S5K6AAFX_FW_APIVER 0x0001 -#define REG_FW_REVISION 0x0130 - -/* For now we use only one user configuration register set */ -#define S5K6AA_MAX_PRESETS 1 - -static const char * const s5k6aa_supply_names[] = { - "vdd_core", /* Digital core supply 1.5V (1.4V to 1.6V) */ - "vdda", /* Analog power supply 2.8V (2.6V to 3.0V) */ - "vdd_reg", /* Regulator input power 1.8V (1.7V to 1.9V) - or 2.8V (2.6V to 3.0) */ - "vddio", /* I/O supply 1.8V (1.65V to 1.95V) - or 2.8V (2.5V to 3.1V) */ -}; -#define S5K6AA_NUM_SUPPLIES ARRAY_SIZE(s5k6aa_supply_names) - -enum s5k6aa_gpio_id { - STBY, - RSET, - GPIO_NUM, -}; - -struct s5k6aa_regval { - u16 addr; - u16 val; -}; - -struct s5k6aa_pixfmt { - u32 code; - u32 colorspace; - /* REG_P_FMT(x) register value */ - u16 reg_p_fmt; -}; - -struct s5k6aa_preset { - /* output pixel format and resolution */ - struct v4l2_mbus_framefmt mbus_fmt; - u8 clk_id; - u8 index; -}; - -struct s5k6aa_ctrls { - struct v4l2_ctrl_handler handler; - /* Auto / manual white balance cluster */ - struct v4l2_ctrl *awb; - struct v4l2_ctrl *gain_red; - struct v4l2_ctrl *gain_blue; - struct v4l2_ctrl *gain_green; - /* Mirror cluster */ - struct v4l2_ctrl *hflip; - struct v4l2_ctrl *vflip; - /* Auto exposure / manual exposure and gain cluster */ - struct v4l2_ctrl *auto_exp; - struct v4l2_ctrl *exposure; - struct v4l2_ctrl *gain; -}; - -struct s5k6aa_interval { - u16 reg_fr_time; - struct v4l2_fract interval; - /* Maximum rectangle for the interval */ - struct v4l2_frmsize_discrete size; -}; - -struct s5k6aa { - struct v4l2_subdev sd; - struct media_pad pad; - - enum v4l2_mbus_type bus_type; - u8 mipi_lanes; - - int (*s_power)(int enable); - struct regulator_bulk_data supplies[S5K6AA_NUM_SUPPLIES]; - struct s5k6aa_gpio gpio[GPIO_NUM]; - - /* external master clock frequency */ - unsigned long mclk_frequency; - /* ISP internal master clock frequency */ - u16 clk_fop; - /* output pixel clock frequency range */ - u16 pclk_fmin; - u16 pclk_fmax; - - unsigned int inv_hflip:1; - unsigned int inv_vflip:1; - - /* protects the struct members below */ - struct mutex lock; - - /* sensor matrix scan window */ - struct v4l2_rect ccd_rect; - - struct s5k6aa_ctrls ctrls; - struct s5k6aa_preset presets[S5K6AA_MAX_PRESETS]; - struct s5k6aa_preset *preset; - const struct s5k6aa_interval *fiv; - - unsigned int streaming:1; - unsigned int apply_cfg:1; - unsigned int apply_crop:1; - unsigned int power; -}; - -static struct s5k6aa_regval s5k6aa_analog_config[] = { - /* Analog settings */ - { 0x112a, 0x0000 }, { 0x1132, 0x0000 }, - { 0x113e, 0x0000 }, { 0x115c, 0x0000 }, - { 0x1164, 0x0000 }, { 0x1174, 0x0000 }, - { 0x1178, 0x0000 }, { 0x077a, 0x0000 }, - { 0x077c, 0x0000 }, { 0x077e, 0x0000 }, - { 0x0780, 0x0000 }, { 0x0782, 0x0000 }, - { 0x0784, 0x0000 }, { 0x0786, 0x0000 }, - { 0x0788, 0x0000 }, { 0x07a2, 0x0000 }, - { 0x07a4, 0x0000 }, { 0x07a6, 0x0000 }, - { 0x07a8, 0x0000 }, { 0x07b6, 0x0000 }, - { 0x07b8, 0x0002 }, { 0x07ba, 0x0004 }, - { 0x07bc, 0x0004 }, { 0x07be, 0x0005 }, - { 0x07c0, 0x0005 }, { S5K6AA_TERM, 0 }, -}; - -/* TODO: Add RGB888 and Bayer format */ -static const struct s5k6aa_pixfmt s5k6aa_formats[] = { - { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG, 5 }, - /* range 16-240 */ - { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_REC709, 6 }, - { MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_JPEG, 0 }, -}; - -static const struct s5k6aa_interval s5k6aa_intervals[] = { - { 1000, {10000, 1000000}, {1280, 1024} }, /* 10 fps */ - { 666, {15000, 1000000}, {1280, 1024} }, /* 15 fps */ - { 500, {20000, 1000000}, {1280, 720} }, /* 20 fps */ - { 400, {25000, 1000000}, {640, 480} }, /* 25 fps */ - { 333, {33300, 1000000}, {640, 480} }, /* 30 fps */ -}; - -#define S5K6AA_INTERVAL_DEF_INDEX 1 /* 15 fps */ - -static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct s5k6aa, ctrls.handler)->sd; -} - -static inline struct s5k6aa *to_s5k6aa(struct v4l2_subdev *sd) -{ - return container_of(sd, struct s5k6aa, sd); -} - -/* Set initial values for all preview presets */ -static void s5k6aa_presets_data_init(struct s5k6aa *s5k6aa) -{ - struct s5k6aa_preset *preset = &s5k6aa->presets[0]; - int i; - - for (i = 0; i < S5K6AA_MAX_PRESETS; i++) { - preset->mbus_fmt.width = S5K6AA_OUT_WIDTH_DEF; - preset->mbus_fmt.height = S5K6AA_OUT_HEIGHT_DEF; - preset->mbus_fmt.code = s5k6aa_formats[0].code; - preset->index = i; - preset->clk_id = 0; - preset++; - } - - s5k6aa->fiv = &s5k6aa_intervals[S5K6AA_INTERVAL_DEF_INDEX]; - s5k6aa->preset = &s5k6aa->presets[0]; -} - -static int s5k6aa_i2c_read(struct i2c_client *client, u16 addr, u16 *val) -{ - u8 wbuf[2] = {addr >> 8, addr & 0xFF}; - struct i2c_msg msg[2]; - u8 rbuf[2]; - int ret; - - msg[0].addr = client->addr; - msg[0].flags = 0; - msg[0].len = 2; - msg[0].buf = wbuf; - - msg[1].addr = client->addr; - msg[1].flags = I2C_M_RD; - msg[1].len = 2; - msg[1].buf = rbuf; - - ret = i2c_transfer(client->adapter, msg, 2); - *val = be16_to_cpu(*((__be16 *)rbuf)); - - v4l2_dbg(3, debug, client, "i2c_read: 0x%04X : 0x%04x\n", addr, *val); - - return ret == 2 ? 0 : ret; -} - -static int s5k6aa_i2c_write(struct i2c_client *client, u16 addr, u16 val) -{ - u8 buf[4] = {addr >> 8, addr & 0xFF, val >> 8, val & 0xFF}; - - int ret = i2c_master_send(client, buf, 4); - v4l2_dbg(3, debug, client, "i2c_write: 0x%04X : 0x%04x\n", addr, val); - - return ret == 4 ? 0 : ret; -} - -/* The command register write, assumes Command_Wr_addH = 0x7000. */ -static int s5k6aa_write(struct i2c_client *c, u16 addr, u16 val) -{ - int ret = s5k6aa_i2c_write(c, REG_CMDWR_ADDRL, addr); - if (ret) - return ret; - return s5k6aa_i2c_write(c, REG_CMDBUF0_ADDR, val); -} - -/* The command register read, assumes Command_Rd_addH = 0x7000. */ -static int s5k6aa_read(struct i2c_client *client, u16 addr, u16 *val) -{ - int ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRL, addr); - if (ret) - return ret; - return s5k6aa_i2c_read(client, REG_CMDBUF0_ADDR, val); -} - -static int s5k6aa_write_array(struct v4l2_subdev *sd, - const struct s5k6aa_regval *msg) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u16 addr_incr = 0; - int ret = 0; - - while (msg->addr != S5K6AA_TERM) { - if (addr_incr != 2) - ret = s5k6aa_i2c_write(client, REG_CMDWR_ADDRL, - msg->addr); - if (ret) - break; - ret = s5k6aa_i2c_write(client, REG_CMDBUF0_ADDR, msg->val); - if (ret) - break; - /* Assume that msg->addr is always less than 0xfffc */ - addr_incr = (msg + 1)->addr - msg->addr; - msg++; - } - - return ret; -} - -/* Configure the AHB high address bytes for GTG registers access */ -static int s5k6aa_set_ahb_address(struct i2c_client *client) -{ - int ret = s5k6aa_i2c_write(client, AHB_MSB_ADDR_PTR, GEN_REG_OFFSH); - if (ret) - return ret; - ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRH, HOST_SWIF_OFFSH); - if (ret) - return ret; - return s5k6aa_i2c_write(client, REG_CMDWR_ADDRH, HOST_SWIF_OFFSH); -} - -/** - * s5k6aa_configure_pixel_clocks - apply ISP main clock/PLL configuration - * @s5k6aa: pointer to &struct s5k6aa describing the device - * - * Configure the internal ISP PLL for the required output frequency. - * Locking: called with s5k6aa.lock mutex held. - */ -static int s5k6aa_configure_pixel_clocks(struct s5k6aa *s5k6aa) -{ - struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); - unsigned long fmclk = s5k6aa->mclk_frequency / 1000; - u16 status; - int ret; - - if (WARN(fmclk < MIN_MCLK_FREQ_KHZ || fmclk > MAX_MCLK_FREQ_KHZ, - "Invalid clock frequency: %ld\n", fmclk)) - return -EINVAL; - - s5k6aa->pclk_fmin = PCLK_FREQ_MIN; - s5k6aa->pclk_fmax = PCLK_FREQ_MAX; - s5k6aa->clk_fop = SYS_PLL_OUT_FREQ; - - /* External input clock frequency in kHz */ - ret = s5k6aa_write(c, REG_I_INCLK_FREQ_H, fmclk >> 16); - if (!ret) - ret = s5k6aa_write(c, REG_I_INCLK_FREQ_L, fmclk & 0xFFFF); - if (!ret) - ret = s5k6aa_write(c, REG_I_USE_NPVI_CLOCKS, 1); - /* Internal PLL frequency */ - if (!ret) - ret = s5k6aa_write(c, REG_I_OPCLK_4KHZ(0), s5k6aa->clk_fop); - if (!ret) - ret = s5k6aa_write(c, REG_I_MIN_OUTRATE_4KHZ(0), - s5k6aa->pclk_fmin); - if (!ret) - ret = s5k6aa_write(c, REG_I_MAX_OUTRATE_4KHZ(0), - s5k6aa->pclk_fmax); - if (!ret) - ret = s5k6aa_write(c, REG_I_INIT_PARAMS_UPDATED, 1); - if (!ret) - ret = s5k6aa_read(c, REG_I_ERROR_INFO, &status); - - return ret ? ret : (status ? -EINVAL : 0); -} - -/* Set horizontal and vertical image flipping */ -static int s5k6aa_set_mirror(struct s5k6aa *s5k6aa, int horiz_flip) -{ - struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); - int index = s5k6aa->preset->index; - - unsigned int vflip = s5k6aa->ctrls.vflip->val ^ s5k6aa->inv_vflip; - unsigned int flip = (horiz_flip ^ s5k6aa->inv_hflip) | (vflip << 1); - - return s5k6aa_write(client, REG_P_PREV_MIRROR(index), flip); -} - -/* Configure auto/manual white balance and R/G/B gains */ -static int s5k6aa_set_awb(struct s5k6aa *s5k6aa, int awb) -{ - struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); - struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls; - u16 reg; - - int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, ®); - - if (!ret && !awb) { - ret = s5k6aa_write(c, REG_SF_RGAIN, ctrls->gain_red->val); - if (!ret) - ret = s5k6aa_write(c, REG_SF_RGAIN_CHG, 1); - if (ret) - return ret; - - ret = s5k6aa_write(c, REG_SF_GGAIN, ctrls->gain_green->val); - if (!ret) - ret = s5k6aa_write(c, REG_SF_GGAIN_CHG, 1); - if (ret) - return ret; - - ret = s5k6aa_write(c, REG_SF_BGAIN, ctrls->gain_blue->val); - if (!ret) - ret = s5k6aa_write(c, REG_SF_BGAIN_CHG, 1); - } - if (!ret) { - reg = awb ? reg | AALG_WB_EN_MASK : reg & ~AALG_WB_EN_MASK; - ret = s5k6aa_write(c, REG_DBG_AUTOALG_EN, reg); - } - - return ret; -} - -/* Program FW with exposure time, 'exposure' in us units */ -static int s5k6aa_set_user_exposure(struct i2c_client *client, int exposure) -{ - unsigned int time = exposure / 10; - - int ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_L, time & 0xffff); - if (!ret) - ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_H, time >> 16); - if (ret) - return ret; - return s5k6aa_write(client, REG_SF_USR_EXPOSURE_CHG, 1); -} - -static int s5k6aa_set_user_gain(struct i2c_client *client, int gain) -{ - int ret = s5k6aa_write(client, REG_SF_USR_TOT_GAIN, gain); - if (ret) - return ret; - return s5k6aa_write(client, REG_SF_USR_TOT_GAIN_CHG, 1); -} - -/* Set auto/manual exposure and total gain */ -static int s5k6aa_set_auto_exposure(struct s5k6aa *s5k6aa, int value) -{ - struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); - unsigned int exp_time = s5k6aa->ctrls.exposure->val; - u16 auto_alg; - - int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, &auto_alg); - if (ret) - return ret; - - v4l2_dbg(1, debug, c, "man_exp: %d, auto_exp: %d, a_alg: 0x%x\n", - exp_time, value, auto_alg); - - if (value == V4L2_EXPOSURE_AUTO) { - auto_alg |= AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK; - } else { - ret = s5k6aa_set_user_exposure(c, exp_time); - if (ret) - return ret; - ret = s5k6aa_set_user_gain(c, s5k6aa->ctrls.gain->val); - if (ret) - return ret; - auto_alg &= ~(AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK); - } - - return s5k6aa_write(c, REG_DBG_AUTOALG_EN, auto_alg); -} - -static int s5k6aa_set_anti_flicker(struct s5k6aa *s5k6aa, int value) -{ - struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); - u16 auto_alg; - int ret; - - ret = s5k6aa_read(client, REG_DBG_AUTOALG_EN, &auto_alg); - if (ret) - return ret; - - if (value == V4L2_CID_POWER_LINE_FREQUENCY_AUTO) { - auto_alg |= AALG_FLICKER_EN_MASK; - } else { - auto_alg &= ~AALG_FLICKER_EN_MASK; - /* The V4L2_CID_LINE_FREQUENCY control values match - * the register values */ - ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT, value); - if (ret) - return ret; - ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT_CHG, 1); - if (ret) - return ret; - } - - return s5k6aa_write(client, REG_DBG_AUTOALG_EN, auto_alg); -} - -static int s5k6aa_set_colorfx(struct s5k6aa *s5k6aa, int val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); - static const struct v4l2_control colorfx[] = { - { V4L2_COLORFX_NONE, 0 }, - { V4L2_COLORFX_BW, 1 }, - { V4L2_COLORFX_NEGATIVE, 2 }, - { V4L2_COLORFX_SEPIA, 3 }, - { V4L2_COLORFX_SKY_BLUE, 4 }, - { V4L2_COLORFX_SKETCH, 5 }, - }; - int i; - - for (i = 0; i < ARRAY_SIZE(colorfx); i++) { - if (colorfx[i].id == val) - return s5k6aa_write(client, REG_G_SPEC_EFFECTS, - colorfx[i].value); - } - return -EINVAL; -} - -static int s5k6aa_preview_config_status(struct i2c_client *client) -{ - u16 error = 0; - int ret = s5k6aa_read(client, REG_G_PREV_CFG_ERROR, &error); - - v4l2_dbg(1, debug, client, "error: 0x%x (%d)\n", error, ret); - return ret ? ret : (error ? -EINVAL : 0); -} - -static int s5k6aa_get_pixfmt_index(struct s5k6aa *s5k6aa, - struct v4l2_mbus_framefmt *mf) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(s5k6aa_formats); i++) - if (mf->colorspace == s5k6aa_formats[i].colorspace && - mf->code == s5k6aa_formats[i].code) - return i; - return 0; -} - -static int s5k6aa_set_output_framefmt(struct s5k6aa *s5k6aa, - struct s5k6aa_preset *preset) -{ - struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); - int fmt_index = s5k6aa_get_pixfmt_index(s5k6aa, &preset->mbus_fmt); - int ret; - - ret = s5k6aa_write(client, REG_P_OUT_WIDTH(preset->index), - preset->mbus_fmt.width); - if (!ret) - ret = s5k6aa_write(client, REG_P_OUT_HEIGHT(preset->index), - preset->mbus_fmt.height); - if (!ret) - ret = s5k6aa_write(client, REG_P_FMT(preset->index), - s5k6aa_formats[fmt_index].reg_p_fmt); - return ret; -} - -static int s5k6aa_set_input_params(struct s5k6aa *s5k6aa) -{ - struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); - struct v4l2_rect *r = &s5k6aa->ccd_rect; - int ret; - - ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_WIDTH, r->width); - if (!ret) - ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_HEIGHT, r->height); - if (!ret) - ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_XOFFS, r->left); - if (!ret) - ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_YOFFS, r->top); - if (!ret) - ret = s5k6aa_write(c, REG_G_INPUTS_CHANGE_REQ, 1); - if (!ret) - s5k6aa->apply_crop = 0; - - return ret; -} - -/** - * s5k6aa_configure_video_bus - configure the video output interface - * @s5k6aa: pointer to &struct s5k6aa describing the device - * @bus_type: video bus type: parallel or MIPI-CSI - * @nlanes: number of MIPI lanes to be used (MIPI-CSI only) - * - * Note: Only parallel bus operation has been tested. - */ -static int s5k6aa_configure_video_bus(struct s5k6aa *s5k6aa, - enum v4l2_mbus_type bus_type, int nlanes) -{ - struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); - u16 cfg = 0; - int ret; - - /* - * TODO: The sensor is supposed to support BT.601 and BT.656 - * but there is nothing indicating how to switch between both - * in the datasheet. For now default BT.601 interface is assumed. - */ - if (bus_type == V4L2_MBUS_CSI2_DPHY) - cfg = nlanes; - else if (bus_type != V4L2_MBUS_PARALLEL) - return -EINVAL; - - ret = s5k6aa_write(client, REG_OIF_EN_MIPI_LANES, cfg); - if (ret) - return ret; - return s5k6aa_write(client, REG_OIF_CFG_CHG, 1); -} - -/* This function should be called when switching to new user configuration set*/ -static int s5k6aa_new_config_sync(struct i2c_client *client, int timeout, - int cid) -{ - unsigned long end = jiffies + msecs_to_jiffies(timeout); - u16 reg = 1; - int ret; - - ret = s5k6aa_write(client, REG_G_ACTIVE_PREV_CFG, cid); - if (!ret) - ret = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1); - if (!ret) - ret = s5k6aa_write(client, REG_G_NEW_CFG_SYNC, 1); - if (timeout == 0) - return ret; - - while (ret >= 0 && time_is_after_jiffies(end)) { - ret = s5k6aa_read(client, REG_G_NEW_CFG_SYNC, ®); - if (!reg) - return 0; - usleep_range(1000, 5000); - } - return ret ? ret : -ETIMEDOUT; -} - -/** - * s5k6aa_set_prev_config - write user preview register set - * @s5k6aa: pointer to &struct s5k6aa describing the device - * @preset: s5kaa preset to be applied - * - * Configure output resolution and color format, pixel clock - * frequency range, device frame rate type and frame period range. - */ -static int s5k6aa_set_prev_config(struct s5k6aa *s5k6aa, - struct s5k6aa_preset *preset) -{ - struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); - int idx = preset->index; - u16 frame_rate_q; - int ret; - - if (s5k6aa->fiv->reg_fr_time >= S5K6AA_MAX_HIGHRES_FR_TIME) - frame_rate_q = FR_RATE_Q_BEST_FRRATE; - else - frame_rate_q = FR_RATE_Q_BEST_QUALITY; - - ret = s5k6aa_set_output_framefmt(s5k6aa, preset); - if (!ret) - ret = s5k6aa_write(client, REG_P_MAX_OUT_RATE(idx), - s5k6aa->pclk_fmax); - if (!ret) - ret = s5k6aa_write(client, REG_P_MIN_OUT_RATE(idx), - s5k6aa->pclk_fmin); - if (!ret) - ret = s5k6aa_write(client, REG_P_CLK_INDEX(idx), - preset->clk_id); - if (!ret) - ret = s5k6aa_write(client, REG_P_FR_RATE_TYPE(idx), - FR_RATE_DYNAMIC); - if (!ret) - ret = s5k6aa_write(client, REG_P_FR_RATE_Q_TYPE(idx), - frame_rate_q); - if (!ret) - ret = s5k6aa_write(client, REG_P_MAX_FR_TIME(idx), - s5k6aa->fiv->reg_fr_time + 33); - if (!ret) - ret = s5k6aa_write(client, REG_P_MIN_FR_TIME(idx), - s5k6aa->fiv->reg_fr_time - 33); - if (!ret) - ret = s5k6aa_new_config_sync(client, 250, idx); - if (!ret) - ret = s5k6aa_preview_config_status(client); - if (!ret) - s5k6aa->apply_cfg = 0; - - v4l2_dbg(1, debug, client, "Frame interval: %d +/- 3.3ms. (%d)\n", - s5k6aa->fiv->reg_fr_time, ret); - return ret; -} - -/** - * s5k6aa_initialize_isp - basic ISP MCU initialization - * @sd: pointer to V4L2 sub-device descriptor - * - * Configure AHB addresses for registers read/write; configure PLLs for - * required output pixel clock. The ISP power supply needs to be already - * enabled, with an optional H/W reset. - * Locking: called with s5k6aa.lock mutex held. - */ -static int s5k6aa_initialize_isp(struct v4l2_subdev *sd) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - int ret; - - s5k6aa->apply_crop = 1; - s5k6aa->apply_cfg = 1; - msleep(100); - - ret = s5k6aa_set_ahb_address(client); - if (ret) - return ret; - ret = s5k6aa_configure_video_bus(s5k6aa, s5k6aa->bus_type, - s5k6aa->mipi_lanes); - if (ret) - return ret; - ret = s5k6aa_write_array(sd, s5k6aa_analog_config); - if (ret) - return ret; - msleep(20); - - return s5k6aa_configure_pixel_clocks(s5k6aa); -} - -static int s5k6aa_gpio_set_value(struct s5k6aa *priv, int id, u32 val) -{ - if (!gpio_is_valid(priv->gpio[id].gpio)) - return 0; - gpio_set_value(priv->gpio[id].gpio, !!val); - return 1; -} - -static int s5k6aa_gpio_assert(struct s5k6aa *priv, int id) -{ - return s5k6aa_gpio_set_value(priv, id, priv->gpio[id].level); -} - -static int s5k6aa_gpio_deassert(struct s5k6aa *priv, int id) -{ - return s5k6aa_gpio_set_value(priv, id, !priv->gpio[id].level); -} - -static int __s5k6aa_power_on(struct s5k6aa *s5k6aa) -{ - int ret; - - ret = regulator_bulk_enable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); - if (ret) - return ret; - if (s5k6aa_gpio_deassert(s5k6aa, STBY)) - usleep_range(150, 200); - - if (s5k6aa->s_power) - ret = s5k6aa->s_power(1); - usleep_range(4000, 5000); - - if (s5k6aa_gpio_deassert(s5k6aa, RSET)) - msleep(20); - - return ret; -} - -static int __s5k6aa_power_off(struct s5k6aa *s5k6aa) -{ - int ret; - - if (s5k6aa_gpio_assert(s5k6aa, RSET)) - usleep_range(100, 150); - - if (s5k6aa->s_power) { - ret = s5k6aa->s_power(0); - if (ret) - return ret; - } - if (s5k6aa_gpio_assert(s5k6aa, STBY)) - usleep_range(50, 100); - s5k6aa->streaming = 0; - - return regulator_bulk_disable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); -} - -/* - * V4L2 subdev core and video operations - */ -static int s5k6aa_set_power(struct v4l2_subdev *sd, int on) -{ - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - int ret = 0; - - mutex_lock(&s5k6aa->lock); - - if (s5k6aa->power == !on) { - if (on) { - ret = __s5k6aa_power_on(s5k6aa); - if (!ret) - ret = s5k6aa_initialize_isp(sd); - } else { - ret = __s5k6aa_power_off(s5k6aa); - } - - if (!ret) - s5k6aa->power += on ? 1 : -1; - } - - mutex_unlock(&s5k6aa->lock); - - if (!on || ret || s5k6aa->power != 1) - return ret; - - return v4l2_ctrl_handler_setup(sd->ctrl_handler); -} - -static int __s5k6aa_stream(struct s5k6aa *s5k6aa, int enable) -{ - struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); - int ret = 0; - - ret = s5k6aa_write(client, REG_G_ENABLE_PREV, enable); - if (!ret) - ret = s5k6aa_write(client, REG_G_ENABLE_PREV_CHG, 1); - if (!ret) - s5k6aa->streaming = enable; - - return ret; -} - -static int s5k6aa_s_stream(struct v4l2_subdev *sd, int on) -{ - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - int ret = 0; - - mutex_lock(&s5k6aa->lock); - - if (s5k6aa->streaming == !on) { - if (!ret && s5k6aa->apply_cfg) - ret = s5k6aa_set_prev_config(s5k6aa, s5k6aa->preset); - if (s5k6aa->apply_crop) - ret = s5k6aa_set_input_params(s5k6aa); - if (!ret) - ret = __s5k6aa_stream(s5k6aa, !!on); - } - mutex_unlock(&s5k6aa->lock); - - return ret; -} - -static int s5k6aa_g_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *fi) -{ - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - - mutex_lock(&s5k6aa->lock); - fi->interval = s5k6aa->fiv->interval; - mutex_unlock(&s5k6aa->lock); - - return 0; -} - -static int __s5k6aa_set_frame_interval(struct s5k6aa *s5k6aa, - struct v4l2_subdev_frame_interval *fi) -{ - struct v4l2_mbus_framefmt *mbus_fmt = &s5k6aa->preset->mbus_fmt; - const struct s5k6aa_interval *fiv = &s5k6aa_intervals[0]; - unsigned int err, min_err = UINT_MAX; - unsigned int i, fr_time; - - if (fi->interval.denominator == 0) - return -EINVAL; - - fr_time = fi->interval.numerator * 10000 / fi->interval.denominator; - - for (i = 0; i < ARRAY_SIZE(s5k6aa_intervals); i++) { - const struct s5k6aa_interval *iv = &s5k6aa_intervals[i]; - - if (mbus_fmt->width > iv->size.width || - mbus_fmt->height > iv->size.height) - continue; - - err = abs(iv->reg_fr_time - fr_time); - if (err < min_err) { - fiv = iv; - min_err = err; - } - } - s5k6aa->fiv = fiv; - - v4l2_dbg(1, debug, &s5k6aa->sd, "Changed frame interval to %d us\n", - fiv->reg_fr_time * 100); - return 0; -} - -static int s5k6aa_s_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *fi) -{ - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - int ret; - - v4l2_dbg(1, debug, sd, "Setting %d/%d frame interval\n", - fi->interval.numerator, fi->interval.denominator); - - mutex_lock(&s5k6aa->lock); - ret = __s5k6aa_set_frame_interval(s5k6aa, fi); - s5k6aa->apply_cfg = 1; - - mutex_unlock(&s5k6aa->lock); - return ret; -} - -/* - * V4L2 subdev pad level and video operations - */ -static int s5k6aa_enum_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_interval_enum *fie) -{ - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - const struct s5k6aa_interval *fi; - int ret = 0; - - if (fie->index >= ARRAY_SIZE(s5k6aa_intervals)) - return -EINVAL; - - v4l_bound_align_image(&fie->width, S5K6AA_WIN_WIDTH_MIN, - S5K6AA_WIN_WIDTH_MAX, 1, - &fie->height, S5K6AA_WIN_HEIGHT_MIN, - S5K6AA_WIN_HEIGHT_MAX, 1, 0); - - mutex_lock(&s5k6aa->lock); - fi = &s5k6aa_intervals[fie->index]; - if (fie->width > fi->size.width || fie->height > fi->size.height) - ret = -EINVAL; - else - fie->interval = fi->interval; - mutex_unlock(&s5k6aa->lock); - - return ret; -} - -static int s5k6aa_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->index >= ARRAY_SIZE(s5k6aa_formats)) - return -EINVAL; - - code->code = s5k6aa_formats[code->index].code; - return 0; -} - -static int s5k6aa_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_frame_size_enum *fse) -{ - int i = ARRAY_SIZE(s5k6aa_formats); - - if (fse->index > 0) - return -EINVAL; - - while (--i) - if (fse->code == s5k6aa_formats[i].code) - break; - - fse->code = s5k6aa_formats[i].code; - fse->min_width = S5K6AA_WIN_WIDTH_MIN; - fse->max_width = S5K6AA_WIN_WIDTH_MAX; - fse->max_height = S5K6AA_WIN_HEIGHT_MIN; - fse->min_height = S5K6AA_WIN_HEIGHT_MAX; - - return 0; -} - -static struct v4l2_rect * -__s5k6aa_get_crop_rect(struct s5k6aa *s5k6aa, - struct v4l2_subdev_state *sd_state, - enum v4l2_subdev_format_whence which) -{ - if (which == V4L2_SUBDEV_FORMAT_ACTIVE) - return &s5k6aa->ccd_rect; - - WARN_ON(which != V4L2_SUBDEV_FORMAT_TRY); - return v4l2_subdev_get_try_crop(&s5k6aa->sd, sd_state, 0); -} - -static void s5k6aa_try_format(struct s5k6aa *s5k6aa, - struct v4l2_mbus_framefmt *mf) -{ - unsigned int index; - - v4l_bound_align_image(&mf->width, S5K6AA_WIN_WIDTH_MIN, - S5K6AA_WIN_WIDTH_MAX, 1, - &mf->height, S5K6AA_WIN_HEIGHT_MIN, - S5K6AA_WIN_HEIGHT_MAX, 1, 0); - - if (mf->colorspace != V4L2_COLORSPACE_JPEG && - mf->colorspace != V4L2_COLORSPACE_REC709) - mf->colorspace = V4L2_COLORSPACE_JPEG; - - index = s5k6aa_get_pixfmt_index(s5k6aa, mf); - - mf->colorspace = s5k6aa_formats[index].colorspace; - mf->code = s5k6aa_formats[index].code; - mf->field = V4L2_FIELD_NONE; -} - -static int s5k6aa_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - struct v4l2_mbus_framefmt *mf; - - memset(fmt->reserved, 0, sizeof(fmt->reserved)); - - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, 0); - fmt->format = *mf; - return 0; - } - - mutex_lock(&s5k6aa->lock); - fmt->format = s5k6aa->preset->mbus_fmt; - mutex_unlock(&s5k6aa->lock); - - return 0; -} - -static int s5k6aa_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *fmt) -{ - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - struct s5k6aa_preset *preset = s5k6aa->preset; - struct v4l2_mbus_framefmt *mf; - struct v4l2_rect *crop; - int ret = 0; - - mutex_lock(&s5k6aa->lock); - s5k6aa_try_format(s5k6aa, &fmt->format); - - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - mf = v4l2_subdev_get_try_format(sd, sd_state, fmt->pad); - crop = v4l2_subdev_get_try_crop(sd, sd_state, 0); - } else { - if (s5k6aa->streaming) { - ret = -EBUSY; - } else { - mf = &preset->mbus_fmt; - crop = &s5k6aa->ccd_rect; - s5k6aa->apply_cfg = 1; - } - } - - if (ret == 0) { - struct v4l2_subdev_frame_interval fiv = { - .interval = {0, 1} - }; - - *mf = fmt->format; - /* - * Make sure the crop window is valid, i.e. its size is - * greater than the output window, as the ISP supports - * only down-scaling. - */ - crop->width = clamp_t(unsigned int, crop->width, mf->width, - S5K6AA_WIN_WIDTH_MAX); - crop->height = clamp_t(unsigned int, crop->height, mf->height, - S5K6AA_WIN_HEIGHT_MAX); - crop->left = clamp_t(unsigned int, crop->left, 0, - S5K6AA_WIN_WIDTH_MAX - crop->width); - crop->top = clamp_t(unsigned int, crop->top, 0, - S5K6AA_WIN_HEIGHT_MAX - crop->height); - - /* Reset to minimum possible frame interval */ - ret = __s5k6aa_set_frame_interval(s5k6aa, &fiv); - } - mutex_unlock(&s5k6aa->lock); - - return ret; -} - -static int s5k6aa_get_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_selection *sel) -{ - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - struct v4l2_rect *rect; - - if (sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - memset(sel->reserved, 0, sizeof(sel->reserved)); - - mutex_lock(&s5k6aa->lock); - rect = __s5k6aa_get_crop_rect(s5k6aa, sd_state, sel->which); - sel->r = *rect; - mutex_unlock(&s5k6aa->lock); - - v4l2_dbg(1, debug, sd, "Current crop rectangle: (%d,%d)/%dx%d\n", - rect->left, rect->top, rect->width, rect->height); - - return 0; -} - -static int s5k6aa_set_selection(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_selection *sel) -{ - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - struct v4l2_mbus_framefmt *mf; - unsigned int max_x, max_y; - struct v4l2_rect *crop_r; - - if (sel->target != V4L2_SEL_TGT_CROP) - return -EINVAL; - - mutex_lock(&s5k6aa->lock); - crop_r = __s5k6aa_get_crop_rect(s5k6aa, sd_state, sel->which); - - if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { - mf = &s5k6aa->preset->mbus_fmt; - s5k6aa->apply_crop = 1; - } else { - mf = v4l2_subdev_get_try_format(sd, sd_state, 0); - } - v4l_bound_align_image(&sel->r.width, mf->width, - S5K6AA_WIN_WIDTH_MAX, 1, - &sel->r.height, mf->height, - S5K6AA_WIN_HEIGHT_MAX, 1, 0); - - max_x = (S5K6AA_WIN_WIDTH_MAX - sel->r.width) & ~1; - max_y = (S5K6AA_WIN_HEIGHT_MAX - sel->r.height) & ~1; - - sel->r.left = clamp_t(unsigned int, sel->r.left, 0, max_x); - sel->r.top = clamp_t(unsigned int, sel->r.top, 0, max_y); - - *crop_r = sel->r; - - mutex_unlock(&s5k6aa->lock); - - v4l2_dbg(1, debug, sd, "Set crop rectangle: (%d,%d)/%dx%d\n", - crop_r->left, crop_r->top, crop_r->width, crop_r->height); - - return 0; -} - -static const struct v4l2_subdev_pad_ops s5k6aa_pad_ops = { - .enum_mbus_code = s5k6aa_enum_mbus_code, - .enum_frame_size = s5k6aa_enum_frame_size, - .enum_frame_interval = s5k6aa_enum_frame_interval, - .get_fmt = s5k6aa_get_fmt, - .set_fmt = s5k6aa_set_fmt, - .get_selection = s5k6aa_get_selection, - .set_selection = s5k6aa_set_selection, -}; - -static const struct v4l2_subdev_video_ops s5k6aa_video_ops = { - .g_frame_interval = s5k6aa_g_frame_interval, - .s_frame_interval = s5k6aa_s_frame_interval, - .s_stream = s5k6aa_s_stream, -}; - -/* - * V4L2 subdev controls - */ - -static int s5k6aa_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = ctrl_to_sd(ctrl); - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - int idx, err = 0; - - v4l2_dbg(1, debug, sd, "ctrl: 0x%x, value: %d\n", ctrl->id, ctrl->val); - - mutex_lock(&s5k6aa->lock); - /* - * If the device is not powered up by the host driver do - * not apply any controls to H/W at this time. Instead - * the controls will be restored right after power-up. - */ - if (s5k6aa->power == 0) - goto unlock; - idx = s5k6aa->preset->index; - - switch (ctrl->id) { - case V4L2_CID_AUTO_WHITE_BALANCE: - err = s5k6aa_set_awb(s5k6aa, ctrl->val); - break; - - case V4L2_CID_BRIGHTNESS: - err = s5k6aa_write(client, REG_USER_BRIGHTNESS, ctrl->val); - break; - - case V4L2_CID_COLORFX: - err = s5k6aa_set_colorfx(s5k6aa, ctrl->val); - break; - - case V4L2_CID_CONTRAST: - err = s5k6aa_write(client, REG_USER_CONTRAST, ctrl->val); - break; - - case V4L2_CID_EXPOSURE_AUTO: - err = s5k6aa_set_auto_exposure(s5k6aa, ctrl->val); - break; - - case V4L2_CID_HFLIP: - err = s5k6aa_set_mirror(s5k6aa, ctrl->val); - if (err) - break; - err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1); - break; - - case V4L2_CID_POWER_LINE_FREQUENCY: - err = s5k6aa_set_anti_flicker(s5k6aa, ctrl->val); - break; - - case V4L2_CID_SATURATION: - err = s5k6aa_write(client, REG_USER_SATURATION, ctrl->val); - break; - - case V4L2_CID_SHARPNESS: - err = s5k6aa_write(client, REG_USER_SHARPBLUR, ctrl->val); - break; - - case V4L2_CID_WHITE_BALANCE_TEMPERATURE: - err = s5k6aa_write(client, REG_P_COLORTEMP(idx), ctrl->val); - if (err) - break; - err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1); - break; - } -unlock: - mutex_unlock(&s5k6aa->lock); - return err; -} - -static const struct v4l2_ctrl_ops s5k6aa_ctrl_ops = { - .s_ctrl = s5k6aa_s_ctrl, -}; - -static int s5k6aa_log_status(struct v4l2_subdev *sd) -{ - v4l2_ctrl_handler_log_status(sd->ctrl_handler, sd->name); - return 0; -} - -#define V4L2_CID_RED_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1001) -#define V4L2_CID_GREEN_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1002) -#define V4L2_CID_BLUE_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1003) - -static const struct v4l2_ctrl_config s5k6aa_ctrls[] = { - { - .ops = &s5k6aa_ctrl_ops, - .id = V4L2_CID_RED_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain, Red", - .min = 0, - .max = 256, - .def = 127, - .step = 1, - }, { - .ops = &s5k6aa_ctrl_ops, - .id = V4L2_CID_GREEN_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain, Green", - .min = 0, - .max = 256, - .def = 127, - .step = 1, - }, { - .ops = &s5k6aa_ctrl_ops, - .id = V4L2_CID_BLUE_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain, Blue", - .min = 0, - .max = 256, - .def = 127, - .step = 1, - }, -}; - -static int s5k6aa_initialize_ctrls(struct s5k6aa *s5k6aa) -{ - const struct v4l2_ctrl_ops *ops = &s5k6aa_ctrl_ops; - struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls; - struct v4l2_ctrl_handler *hdl = &ctrls->handler; - - int ret = v4l2_ctrl_handler_init(hdl, 16); - if (ret) - return ret; - /* Auto white balance cluster */ - ctrls->awb = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, - 0, 1, 1, 1); - ctrls->gain_red = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[0], NULL); - ctrls->gain_green = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[1], NULL); - ctrls->gain_blue = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[2], NULL); - v4l2_ctrl_auto_cluster(4, &ctrls->awb, 0, false); - - ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0); - ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0); - v4l2_ctrl_cluster(2, &ctrls->hflip); - - ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops, - V4L2_CID_EXPOSURE_AUTO, - V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO); - /* Exposure time: x 1 us */ - ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, - 0, 6000000U, 1, 100000U); - /* Total gain: 256 <=> 1x */ - ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, - 0, 256, 1, 256); - v4l2_ctrl_auto_cluster(3, &ctrls->auto_exp, 0, false); - - v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_POWER_LINE_FREQUENCY, - V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, - V4L2_CID_POWER_LINE_FREQUENCY_AUTO); - - v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_COLORFX, - V4L2_COLORFX_SKY_BLUE, ~0x6f, V4L2_COLORFX_NONE); - - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_WHITE_BALANCE_TEMPERATURE, - 0, 256, 1, 0); - - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, -127, 127, 1, 0); - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -127, 127, 1, 0); - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -127, 127, 1, 0); - v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS, -127, 127, 1, 0); - - if (hdl->error) { - ret = hdl->error; - v4l2_ctrl_handler_free(hdl); - return ret; - } - - s5k6aa->sd.ctrl_handler = hdl; - return 0; -} - -/* - * V4L2 subdev internal operations - */ -static int s5k6aa_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(sd, - fh->state, - 0); - struct v4l2_rect *crop = v4l2_subdev_get_try_crop(sd, fh->state, 0); - - format->colorspace = s5k6aa_formats[0].colorspace; - format->code = s5k6aa_formats[0].code; - format->width = S5K6AA_OUT_WIDTH_DEF; - format->height = S5K6AA_OUT_HEIGHT_DEF; - format->field = V4L2_FIELD_NONE; - - crop->width = S5K6AA_WIN_WIDTH_MAX; - crop->height = S5K6AA_WIN_HEIGHT_MAX; - crop->left = 0; - crop->top = 0; - - return 0; -} - -static int s5k6aa_check_fw_revision(struct s5k6aa *s5k6aa) -{ - struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); - u16 api_ver = 0, fw_rev = 0; - - int ret = s5k6aa_set_ahb_address(client); - - if (!ret) - ret = s5k6aa_read(client, REG_FW_APIVER, &api_ver); - if (!ret) - ret = s5k6aa_read(client, REG_FW_REVISION, &fw_rev); - if (ret) { - v4l2_err(&s5k6aa->sd, "FW revision check failed!\n"); - return ret; - } - - v4l2_info(&s5k6aa->sd, "FW API ver.: 0x%X, FW rev.: 0x%X\n", - api_ver, fw_rev); - - return api_ver == S5K6AAFX_FW_APIVER ? 0 : -ENODEV; -} - -static int s5k6aa_registered(struct v4l2_subdev *sd) -{ - struct s5k6aa *s5k6aa = to_s5k6aa(sd); - int ret; - - mutex_lock(&s5k6aa->lock); - ret = __s5k6aa_power_on(s5k6aa); - if (!ret) { - msleep(100); - ret = s5k6aa_check_fw_revision(s5k6aa); - __s5k6aa_power_off(s5k6aa); - } - mutex_unlock(&s5k6aa->lock); - - return ret; -} - -static const struct v4l2_subdev_internal_ops s5k6aa_subdev_internal_ops = { - .registered = s5k6aa_registered, - .open = s5k6aa_open, -}; - -static const struct v4l2_subdev_core_ops s5k6aa_core_ops = { - .s_power = s5k6aa_set_power, - .log_status = s5k6aa_log_status, -}; - -static const struct v4l2_subdev_ops s5k6aa_subdev_ops = { - .core = &s5k6aa_core_ops, - .pad = &s5k6aa_pad_ops, - .video = &s5k6aa_video_ops, -}; - -/* - * GPIO setup - */ - -static int s5k6aa_configure_gpios(struct s5k6aa *s5k6aa, - const struct s5k6aa_platform_data *pdata) -{ - struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); - const struct s5k6aa_gpio *gpio; - unsigned long flags; - int ret; - - s5k6aa->gpio[STBY].gpio = -EINVAL; - s5k6aa->gpio[RSET].gpio = -EINVAL; - - gpio = &pdata->gpio_stby; - if (gpio_is_valid(gpio->gpio)) { - flags = (gpio->level ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW) - | GPIOF_EXPORT; - ret = devm_gpio_request_one(&client->dev, gpio->gpio, flags, - "S5K6AA_STBY"); - if (ret < 0) - return ret; - - s5k6aa->gpio[STBY] = *gpio; - } - - gpio = &pdata->gpio_reset; - if (gpio_is_valid(gpio->gpio)) { - flags = (gpio->level ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW) - | GPIOF_EXPORT; - ret = devm_gpio_request_one(&client->dev, gpio->gpio, flags, - "S5K6AA_RST"); - if (ret < 0) - return ret; - - s5k6aa->gpio[RSET] = *gpio; - } - - return 0; -} - -static int s5k6aa_probe(struct i2c_client *client) -{ - const struct s5k6aa_platform_data *pdata = client->dev.platform_data; - struct v4l2_subdev *sd; - struct s5k6aa *s5k6aa; - int i, ret; - - if (pdata == NULL) { - dev_err(&client->dev, "Platform data not specified\n"); - return -EINVAL; - } - - if (pdata->mclk_frequency == 0) { - dev_err(&client->dev, "MCLK frequency not specified\n"); - return -EINVAL; - } - - s5k6aa = devm_kzalloc(&client->dev, sizeof(*s5k6aa), GFP_KERNEL); - if (!s5k6aa) - return -ENOMEM; - - mutex_init(&s5k6aa->lock); - - s5k6aa->mclk_frequency = pdata->mclk_frequency; - s5k6aa->bus_type = pdata->bus_type; - s5k6aa->mipi_lanes = pdata->nlanes; - s5k6aa->s_power = pdata->set_power; - s5k6aa->inv_hflip = pdata->horiz_flip; - s5k6aa->inv_vflip = pdata->vert_flip; - - sd = &s5k6aa->sd; - v4l2_i2c_subdev_init(sd, client, &s5k6aa_subdev_ops); - /* Static name; NEVER use in new drivers! */ - strscpy(sd->name, DRIVER_NAME, sizeof(sd->name)); - - sd->internal_ops = &s5k6aa_subdev_internal_ops; - sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - - s5k6aa->pad.flags = MEDIA_PAD_FL_SOURCE; - sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; - ret = media_entity_pads_init(&sd->entity, 1, &s5k6aa->pad); - if (ret) - return ret; - - ret = s5k6aa_configure_gpios(s5k6aa, pdata); - if (ret) - goto out_err; - - for (i = 0; i < S5K6AA_NUM_SUPPLIES; i++) - s5k6aa->supplies[i].supply = s5k6aa_supply_names[i]; - - ret = devm_regulator_bulk_get(&client->dev, S5K6AA_NUM_SUPPLIES, - s5k6aa->supplies); - if (ret) { - dev_err(&client->dev, "Failed to get regulators\n"); - goto out_err; - } - - ret = s5k6aa_initialize_ctrls(s5k6aa); - if (ret) - goto out_err; - - s5k6aa_presets_data_init(s5k6aa); - - s5k6aa->ccd_rect.width = S5K6AA_WIN_WIDTH_MAX; - s5k6aa->ccd_rect.height = S5K6AA_WIN_HEIGHT_MAX; - s5k6aa->ccd_rect.left = 0; - s5k6aa->ccd_rect.top = 0; - - return 0; - -out_err: - media_entity_cleanup(&s5k6aa->sd.entity); - return ret; -} - -static void s5k6aa_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(sd->ctrl_handler); - media_entity_cleanup(&sd->entity); -} - -static const struct i2c_device_id s5k6aa_id[] = { - { DRIVER_NAME, 0 }, - { }, -}; -MODULE_DEVICE_TABLE(i2c, s5k6aa_id); - - -static struct i2c_driver s5k6aa_i2c_driver = { - .driver = { - .name = DRIVER_NAME - }, - .probe_new = s5k6aa_probe, - .remove = s5k6aa_remove, - .id_table = s5k6aa_id, -}; - -module_i2c_driver(s5k6aa_i2c_driver); - -MODULE_DESCRIPTION("Samsung S5K6AA(FX) SXGA camera driver"); -MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/sr030pc30.c b/drivers/media/i2c/sr030pc30.c deleted file mode 100644 index a83c8bf1c5dd..000000000000 --- a/drivers/media/i2c/sr030pc30.c +++ /dev/null @@ -1,762 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Driver for SiliconFile SR030PC30 VGA (1/10-Inch) Image Sensor with ISP - * - * Copyright (C) 2010 Samsung Electronics Co., Ltd - * Author: Sylwester Nawrocki, s.nawrocki@samsung.com - * - * Based on original driver authored by Dongsoo Nathaniel Kim - * and HeungJun Kim <riverful.kim@samsung.com>. - * - * Based on mt9v011 Micron Digital Image Sensor driver - * Copyright (c) 2009 Mauro Carvalho Chehab - */ - -#include <linux/i2c.h> -#include <linux/delay.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <media/v4l2-device.h> -#include <media/v4l2-subdev.h> -#include <media/v4l2-mediabus.h> -#include <media/v4l2-ctrls.h> -#include <media/i2c/sr030pc30.h> - -static int debug; -module_param(debug, int, 0644); - -#define MODULE_NAME "SR030PC30" - -/* - * Register offsets within a page - * b15..b8 - page id, b7..b0 - register address - */ -#define POWER_CTRL_REG 0x0001 -#define PAGEMODE_REG 0x03 -#define DEVICE_ID_REG 0x0004 -#define NOON010PC30_ID 0x86 -#define SR030PC30_ID 0x8C -#define VDO_CTL1_REG 0x0010 -#define SUBSAMPL_NONE_VGA 0 -#define SUBSAMPL_QVGA 0x10 -#define SUBSAMPL_QQVGA 0x20 -#define VDO_CTL2_REG 0x0011 -#define SYNC_CTL_REG 0x0012 -#define WIN_ROWH_REG 0x0020 -#define WIN_ROWL_REG 0x0021 -#define WIN_COLH_REG 0x0022 -#define WIN_COLL_REG 0x0023 -#define WIN_HEIGHTH_REG 0x0024 -#define WIN_HEIGHTL_REG 0x0025 -#define WIN_WIDTHH_REG 0x0026 -#define WIN_WIDTHL_REG 0x0027 -#define HBLANKH_REG 0x0040 -#define HBLANKL_REG 0x0041 -#define VSYNCH_REG 0x0042 -#define VSYNCL_REG 0x0043 -/* page 10 */ -#define ISP_CTL_REG(n) (0x1010 + (n)) -#define YOFS_REG 0x1040 -#define DARK_YOFS_REG 0x1041 -#define AG_ABRTH_REG 0x1050 -#define SAT_CTL_REG 0x1060 -#define BSAT_REG 0x1061 -#define RSAT_REG 0x1062 -#define AG_SAT_TH_REG 0x1063 -/* page 11 */ -#define ZLPF_CTRL_REG 0x1110 -#define ZLPF_CTRL2_REG 0x1112 -#define ZLPF_AGH_THR_REG 0x1121 -#define ZLPF_THR_REG 0x1160 -#define ZLPF_DYN_THR_REG 0x1160 -/* page 12 */ -#define YCLPF_CTL1_REG 0x1240 -#define YCLPF_CTL2_REG 0x1241 -#define YCLPF_THR_REG 0x1250 -#define BLPF_CTL_REG 0x1270 -#define BLPF_THR1_REG 0x1274 -#define BLPF_THR2_REG 0x1275 -/* page 14 - Lens Shading Compensation */ -#define LENS_CTRL_REG 0x1410 -#define LENS_XCEN_REG 0x1420 -#define LENS_YCEN_REG 0x1421 -#define LENS_R_COMP_REG 0x1422 -#define LENS_G_COMP_REG 0x1423 -#define LENS_B_COMP_REG 0x1424 -/* page 15 - Color correction */ -#define CMC_CTL_REG 0x1510 -#define CMC_OFSGH_REG 0x1514 -#define CMC_OFSGL_REG 0x1516 -#define CMC_SIGN_REG 0x1517 -/* Color correction coefficients */ -#define CMC_COEF_REG(n) (0x1530 + (n)) -/* Color correction offset coefficients */ -#define CMC_OFS_REG(n) (0x1540 + (n)) -/* page 16 - Gamma correction */ -#define GMA_CTL_REG 0x1610 -/* Gamma correction coefficients 0.14 */ -#define GMA_COEF_REG(n) (0x1630 + (n)) -/* page 20 - Auto Exposure */ -#define AE_CTL1_REG 0x2010 -#define AE_CTL2_REG 0x2011 -#define AE_FRM_CTL_REG 0x2020 -#define AE_FINE_CTL_REG(n) (0x2028 + (n)) -#define EXP_TIMEH_REG 0x2083 -#define EXP_TIMEM_REG 0x2084 -#define EXP_TIMEL_REG 0x2085 -#define EXP_MMINH_REG 0x2086 -#define EXP_MMINL_REG 0x2087 -#define EXP_MMAXH_REG 0x2088 -#define EXP_MMAXM_REG 0x2089 -#define EXP_MMAXL_REG 0x208A -/* page 22 - Auto White Balance */ -#define AWB_CTL1_REG 0x2210 -#define AWB_ENABLE 0x80 -#define AWB_CTL2_REG 0x2211 -#define MWB_ENABLE 0x01 -/* RGB gain control (manual WB) when AWB_CTL1[7]=0 */ -#define AWB_RGAIN_REG 0x2280 -#define AWB_GGAIN_REG 0x2281 -#define AWB_BGAIN_REG 0x2282 -#define AWB_RMAX_REG 0x2283 -#define AWB_RMIN_REG 0x2284 -#define AWB_BMAX_REG 0x2285 -#define AWB_BMIN_REG 0x2286 -/* R, B gain range in bright light conditions */ -#define AWB_RMAXB_REG 0x2287 -#define AWB_RMINB_REG 0x2288 -#define AWB_BMAXB_REG 0x2289 -#define AWB_BMINB_REG 0x228A -/* manual white balance, when AWB_CTL2[0]=1 */ -#define MWB_RGAIN_REG 0x22B2 -#define MWB_BGAIN_REG 0x22B3 -/* the token to mark an array end */ -#define REG_TERM 0xFFFF - -/* Minimum and maximum exposure time in ms */ -#define EXPOS_MIN_MS 1 -#define EXPOS_MAX_MS 125 - -struct sr030pc30_info { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - const struct sr030pc30_platform_data *pdata; - const struct sr030pc30_format *curr_fmt; - const struct sr030pc30_frmsize *curr_win; - unsigned int hflip:1; - unsigned int vflip:1; - unsigned int sleep:1; - struct { - /* auto whitebalance control cluster */ - struct v4l2_ctrl *awb; - struct v4l2_ctrl *red; - struct v4l2_ctrl *blue; - }; - struct { - /* auto exposure control cluster */ - struct v4l2_ctrl *autoexp; - struct v4l2_ctrl *exp; - }; - u8 i2c_reg_page; -}; - -struct sr030pc30_format { - u32 code; - enum v4l2_colorspace colorspace; - u16 ispctl1_reg; -}; - -struct sr030pc30_frmsize { - u16 width; - u16 height; - int vid_ctl1; -}; - -struct i2c_regval { - u16 addr; - u16 val; -}; - -/* supported resolutions */ -static const struct sr030pc30_frmsize sr030pc30_sizes[] = { - { - .width = 640, - .height = 480, - .vid_ctl1 = SUBSAMPL_NONE_VGA, - }, { - .width = 320, - .height = 240, - .vid_ctl1 = SUBSAMPL_QVGA, - }, { - .width = 160, - .height = 120, - .vid_ctl1 = SUBSAMPL_QQVGA, - }, -}; - -/* supported pixel formats */ -static const struct sr030pc30_format sr030pc30_formats[] = { - { - .code = MEDIA_BUS_FMT_YUYV8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .ispctl1_reg = 0x03, - }, { - .code = MEDIA_BUS_FMT_YVYU8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .ispctl1_reg = 0x02, - }, { - .code = MEDIA_BUS_FMT_VYUY8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .ispctl1_reg = 0, - }, { - .code = MEDIA_BUS_FMT_UYVY8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - .ispctl1_reg = 0x01, - }, { - .code = MEDIA_BUS_FMT_RGB565_2X8_BE, - .colorspace = V4L2_COLORSPACE_JPEG, - .ispctl1_reg = 0x40, - }, -}; - -static const struct i2c_regval sr030pc30_base_regs[] = { - /* Window size and position within pixel matrix */ - { WIN_ROWH_REG, 0x00 }, { WIN_ROWL_REG, 0x06 }, - { WIN_COLH_REG, 0x00 }, { WIN_COLL_REG, 0x06 }, - { WIN_HEIGHTH_REG, 0x01 }, { WIN_HEIGHTL_REG, 0xE0 }, - { WIN_WIDTHH_REG, 0x02 }, { WIN_WIDTHL_REG, 0x80 }, - { HBLANKH_REG, 0x01 }, { HBLANKL_REG, 0x50 }, - { VSYNCH_REG, 0x00 }, { VSYNCL_REG, 0x14 }, - { SYNC_CTL_REG, 0 }, - /* Color corection and saturation */ - { ISP_CTL_REG(0), 0x30 }, { YOFS_REG, 0x80 }, - { DARK_YOFS_REG, 0x04 }, { AG_ABRTH_REG, 0x78 }, - { SAT_CTL_REG, 0x1F }, { BSAT_REG, 0x90 }, - { AG_SAT_TH_REG, 0xF0 }, { 0x1064, 0x80 }, - { CMC_CTL_REG, 0x03 }, { CMC_OFSGH_REG, 0x3C }, - { CMC_OFSGL_REG, 0x2C }, { CMC_SIGN_REG, 0x2F }, - { CMC_COEF_REG(0), 0xCB }, { CMC_OFS_REG(0), 0x87 }, - { CMC_COEF_REG(1), 0x61 }, { CMC_OFS_REG(1), 0x18 }, - { CMC_COEF_REG(2), 0x16 }, { CMC_OFS_REG(2), 0x91 }, - { CMC_COEF_REG(3), 0x23 }, { CMC_OFS_REG(3), 0x94 }, - { CMC_COEF_REG(4), 0xCE }, { CMC_OFS_REG(4), 0x9f }, - { CMC_COEF_REG(5), 0x2B }, { CMC_OFS_REG(5), 0x33 }, - { CMC_COEF_REG(6), 0x01 }, { CMC_OFS_REG(6), 0x00 }, - { CMC_COEF_REG(7), 0x34 }, { CMC_OFS_REG(7), 0x94 }, - { CMC_COEF_REG(8), 0x75 }, { CMC_OFS_REG(8), 0x14 }, - /* Color corection coefficients */ - { GMA_CTL_REG, 0x03 }, { GMA_COEF_REG(0), 0x00 }, - { GMA_COEF_REG(1), 0x19 }, { GMA_COEF_REG(2), 0x26 }, - { GMA_COEF_REG(3), 0x3B }, { GMA_COEF_REG(4), 0x5D }, - { GMA_COEF_REG(5), 0x79 }, { GMA_COEF_REG(6), 0x8E }, - { GMA_COEF_REG(7), 0x9F }, { GMA_COEF_REG(8), 0xAF }, - { GMA_COEF_REG(9), 0xBD }, { GMA_COEF_REG(10), 0xCA }, - { GMA_COEF_REG(11), 0xDD }, { GMA_COEF_REG(12), 0xEC }, - { GMA_COEF_REG(13), 0xF7 }, { GMA_COEF_REG(14), 0xFF }, - /* Noise reduction, Z-LPF, YC-LPF and BLPF filters setup */ - { ZLPF_CTRL_REG, 0x99 }, { ZLPF_CTRL2_REG, 0x0E }, - { ZLPF_AGH_THR_REG, 0x29 }, { ZLPF_THR_REG, 0x0F }, - { ZLPF_DYN_THR_REG, 0x63 }, { YCLPF_CTL1_REG, 0x23 }, - { YCLPF_CTL2_REG, 0x3B }, { YCLPF_THR_REG, 0x05 }, - { BLPF_CTL_REG, 0x1D }, { BLPF_THR1_REG, 0x05 }, - { BLPF_THR2_REG, 0x04 }, - /* Automatic white balance */ - { AWB_CTL1_REG, 0xFB }, { AWB_CTL2_REG, 0x26 }, - { AWB_RMAX_REG, 0x54 }, { AWB_RMIN_REG, 0x2B }, - { AWB_BMAX_REG, 0x57 }, { AWB_BMIN_REG, 0x29 }, - { AWB_RMAXB_REG, 0x50 }, { AWB_RMINB_REG, 0x43 }, - { AWB_BMAXB_REG, 0x30 }, { AWB_BMINB_REG, 0x22 }, - /* Auto exposure */ - { AE_CTL1_REG, 0x8C }, { AE_CTL2_REG, 0x04 }, - { AE_FRM_CTL_REG, 0x01 }, { AE_FINE_CTL_REG(0), 0x3F }, - { AE_FINE_CTL_REG(1), 0xA3 }, { AE_FINE_CTL_REG(3), 0x34 }, - /* Lens shading compensation */ - { LENS_CTRL_REG, 0x01 }, { LENS_XCEN_REG, 0x80 }, - { LENS_YCEN_REG, 0x70 }, { LENS_R_COMP_REG, 0x53 }, - { LENS_G_COMP_REG, 0x40 }, { LENS_B_COMP_REG, 0x3e }, - { REG_TERM, 0 }, -}; - -static inline struct sr030pc30_info *to_sr030pc30(struct v4l2_subdev *sd) -{ - return container_of(sd, struct sr030pc30_info, sd); -} - -static inline int set_i2c_page(struct sr030pc30_info *info, - struct i2c_client *client, unsigned int reg) -{ - int ret = 0; - u32 page = reg >> 8 & 0xFF; - - if (info->i2c_reg_page != page && (reg & 0xFF) != 0x03) { - ret = i2c_smbus_write_byte_data(client, PAGEMODE_REG, page); - if (!ret) - info->i2c_reg_page = page; - } - return ret; -} - -static int cam_i2c_read(struct v4l2_subdev *sd, u32 reg_addr) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct sr030pc30_info *info = to_sr030pc30(sd); - - int ret = set_i2c_page(info, client, reg_addr); - if (!ret) - ret = i2c_smbus_read_byte_data(client, reg_addr & 0xFF); - return ret; -} - -static int cam_i2c_write(struct v4l2_subdev *sd, u32 reg_addr, u32 val) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct sr030pc30_info *info = to_sr030pc30(sd); - - int ret = set_i2c_page(info, client, reg_addr); - if (!ret) - ret = i2c_smbus_write_byte_data( - client, reg_addr & 0xFF, val); - return ret; -} - -static inline int sr030pc30_bulk_write_reg(struct v4l2_subdev *sd, - const struct i2c_regval *msg) -{ - while (msg->addr != REG_TERM) { - int ret = cam_i2c_write(sd, msg->addr, msg->val); - if (ret) - return ret; - msg++; - } - return 0; -} - -/* Device reset and sleep mode control */ -static int sr030pc30_pwr_ctrl(struct v4l2_subdev *sd, - bool reset, bool sleep) -{ - struct sr030pc30_info *info = to_sr030pc30(sd); - u8 reg = sleep ? 0xF1 : 0xF0; - int ret = 0; - - if (reset) - ret = cam_i2c_write(sd, POWER_CTRL_REG, reg | 0x02); - if (!ret) { - ret = cam_i2c_write(sd, POWER_CTRL_REG, reg); - if (!ret) { - info->sleep = sleep; - if (reset) - info->i2c_reg_page = -1; - } - } - return ret; -} - -static int sr030pc30_set_flip(struct v4l2_subdev *sd) -{ - struct sr030pc30_info *info = to_sr030pc30(sd); - - s32 reg = cam_i2c_read(sd, VDO_CTL2_REG); - if (reg < 0) - return reg; - - reg &= 0x7C; - if (info->hflip) - reg |= 0x01; - if (info->vflip) - reg |= 0x02; - return cam_i2c_write(sd, VDO_CTL2_REG, reg | 0x80); -} - -/* Configure resolution, color format and image flip */ -static int sr030pc30_set_params(struct v4l2_subdev *sd) -{ - struct sr030pc30_info *info = to_sr030pc30(sd); - int ret; - - if (!info->curr_win) - return -EINVAL; - - /* Configure the resolution through subsampling */ - ret = cam_i2c_write(sd, VDO_CTL1_REG, - info->curr_win->vid_ctl1); - - if (!ret && info->curr_fmt) - ret = cam_i2c_write(sd, ISP_CTL_REG(0), - info->curr_fmt->ispctl1_reg); - if (!ret) - ret = sr030pc30_set_flip(sd); - - return ret; -} - -/* Find nearest matching image pixel size. */ -static int sr030pc30_try_frame_size(struct v4l2_mbus_framefmt *mf) -{ - unsigned int min_err = ~0; - int i = ARRAY_SIZE(sr030pc30_sizes); - const struct sr030pc30_frmsize *fsize = &sr030pc30_sizes[0], - *match = NULL; - while (i--) { - int err = abs(fsize->width - mf->width) - + abs(fsize->height - mf->height); - if (err < min_err) { - min_err = err; - match = fsize; - } - fsize++; - } - if (match) { - mf->width = match->width; - mf->height = match->height; - return 0; - } - return -EINVAL; -} - -static int sr030pc30_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct sr030pc30_info *info = - container_of(ctrl->handler, struct sr030pc30_info, hdl); - struct v4l2_subdev *sd = &info->sd; - int ret = 0; - - v4l2_dbg(1, debug, sd, "%s: ctrl_id: %d, value: %d\n", - __func__, ctrl->id, ctrl->val); - - switch (ctrl->id) { - case V4L2_CID_AUTO_WHITE_BALANCE: - if (ctrl->is_new) { - ret = cam_i2c_write(sd, AWB_CTL2_REG, - ctrl->val ? 0x2E : 0x2F); - if (!ret) - ret = cam_i2c_write(sd, AWB_CTL1_REG, - ctrl->val ? 0xFB : 0x7B); - } - if (!ret && info->blue->is_new) - ret = cam_i2c_write(sd, MWB_BGAIN_REG, info->blue->val); - if (!ret && info->red->is_new) - ret = cam_i2c_write(sd, MWB_RGAIN_REG, info->red->val); - return ret; - - case V4L2_CID_EXPOSURE_AUTO: - /* auto anti-flicker is also enabled here */ - if (ctrl->is_new) - ret = cam_i2c_write(sd, AE_CTL1_REG, - ctrl->val == V4L2_EXPOSURE_AUTO ? 0xDC : 0x0C); - if (info->exp->is_new) { - unsigned long expos = info->exp->val; - - expos = expos * info->pdata->clk_rate / (8 * 1000); - - if (!ret) - ret = cam_i2c_write(sd, EXP_TIMEH_REG, - expos >> 16 & 0xFF); - if (!ret) - ret = cam_i2c_write(sd, EXP_TIMEM_REG, - expos >> 8 & 0xFF); - if (!ret) - ret = cam_i2c_write(sd, EXP_TIMEL_REG, - expos & 0xFF); - } - return ret; - default: - return -EINVAL; - } - - return 0; -} - -static int sr030pc30_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (!code || code->pad || - code->index >= ARRAY_SIZE(sr030pc30_formats)) - return -EINVAL; - - code->code = sr030pc30_formats[code->index].code; - return 0; -} - -static int sr030pc30_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *mf; - struct sr030pc30_info *info = to_sr030pc30(sd); - - if (!format || format->pad) - return -EINVAL; - - mf = &format->format; - - if (!info->curr_win || !info->curr_fmt) - return -EINVAL; - - mf->width = info->curr_win->width; - mf->height = info->curr_win->height; - mf->code = info->curr_fmt->code; - mf->colorspace = info->curr_fmt->colorspace; - mf->field = V4L2_FIELD_NONE; - - return 0; -} - -/* Return nearest media bus frame format. */ -static const struct sr030pc30_format *try_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *mf) -{ - int i; - - sr030pc30_try_frame_size(mf); - - for (i = 0; i < ARRAY_SIZE(sr030pc30_formats); i++) { - if (mf->code == sr030pc30_formats[i].code) - break; - } - if (i == ARRAY_SIZE(sr030pc30_formats)) - i = 0; - - mf->code = sr030pc30_formats[i].code; - - return &sr030pc30_formats[i]; -} - -/* Return nearest media bus frame format. */ -static int sr030pc30_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct sr030pc30_info *info = sd ? to_sr030pc30(sd) : NULL; - const struct sr030pc30_format *fmt; - struct v4l2_mbus_framefmt *mf; - - if (!sd || !format) - return -EINVAL; - - mf = &format->format; - if (format->pad) - return -EINVAL; - - fmt = try_fmt(sd, mf); - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - sd_state->pads->try_fmt = *mf; - return 0; - } - - info->curr_fmt = fmt; - - return sr030pc30_set_params(sd); -} - -static int sr030pc30_base_config(struct v4l2_subdev *sd) -{ - struct sr030pc30_info *info = to_sr030pc30(sd); - int ret; - unsigned long expmin, expmax; - - ret = sr030pc30_bulk_write_reg(sd, sr030pc30_base_regs); - if (!ret) { - info->curr_fmt = &sr030pc30_formats[0]; - info->curr_win = &sr030pc30_sizes[0]; - ret = sr030pc30_set_params(sd); - } - if (!ret) - ret = sr030pc30_pwr_ctrl(sd, false, false); - - if (ret) - return ret; - - expmin = EXPOS_MIN_MS * info->pdata->clk_rate / (8 * 1000); - expmax = EXPOS_MAX_MS * info->pdata->clk_rate / (8 * 1000); - - v4l2_dbg(1, debug, sd, "%s: expmin= %lx, expmax= %lx", __func__, - expmin, expmax); - - /* Setting up manual exposure time range */ - ret = cam_i2c_write(sd, EXP_MMINH_REG, expmin >> 8 & 0xFF); - if (!ret) - ret = cam_i2c_write(sd, EXP_MMINL_REG, expmin & 0xFF); - if (!ret) - ret = cam_i2c_write(sd, EXP_MMAXH_REG, expmax >> 16 & 0xFF); - if (!ret) - ret = cam_i2c_write(sd, EXP_MMAXM_REG, expmax >> 8 & 0xFF); - if (!ret) - ret = cam_i2c_write(sd, EXP_MMAXL_REG, expmax & 0xFF); - - return ret; -} - -static int sr030pc30_s_power(struct v4l2_subdev *sd, int on) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct sr030pc30_info *info = to_sr030pc30(sd); - const struct sr030pc30_platform_data *pdata = info->pdata; - int ret; - - if (pdata == NULL) { - WARN(1, "No platform data!\n"); - return -EINVAL; - } - - /* - * Put sensor into power sleep mode before switching off - * power and disabling MCLK. - */ - if (!on) - sr030pc30_pwr_ctrl(sd, false, true); - - /* set_power controls sensor's power and clock */ - if (pdata->set_power) { - ret = pdata->set_power(&client->dev, on); - if (ret) - return ret; - } - - if (on) { - ret = sr030pc30_base_config(sd); - } else { - ret = 0; - info->curr_win = NULL; - info->curr_fmt = NULL; - } - - return ret; -} - -static const struct v4l2_ctrl_ops sr030pc30_ctrl_ops = { - .s_ctrl = sr030pc30_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops sr030pc30_core_ops = { - .s_power = sr030pc30_s_power, -}; - -static const struct v4l2_subdev_pad_ops sr030pc30_pad_ops = { - .enum_mbus_code = sr030pc30_enum_mbus_code, - .get_fmt = sr030pc30_get_fmt, - .set_fmt = sr030pc30_set_fmt, -}; - -static const struct v4l2_subdev_ops sr030pc30_ops = { - .core = &sr030pc30_core_ops, - .pad = &sr030pc30_pad_ops, -}; - -/* - * Detect sensor type. Return 0 if SR030PC30 was detected - * or -ENODEV otherwise. - */ -static int sr030pc30_detect(struct i2c_client *client) -{ - const struct sr030pc30_platform_data *pdata - = client->dev.platform_data; - int ret; - - /* Enable sensor's power and clock */ - if (pdata->set_power) { - ret = pdata->set_power(&client->dev, 1); - if (ret) - return ret; - } - - ret = i2c_smbus_read_byte_data(client, DEVICE_ID_REG); - - if (pdata->set_power) - pdata->set_power(&client->dev, 0); - - if (ret < 0) { - dev_err(&client->dev, "%s: I2C read failed\n", __func__); - return ret; - } - - return ret == SR030PC30_ID ? 0 : -ENODEV; -} - - -static int sr030pc30_probe(struct i2c_client *client) -{ - struct sr030pc30_info *info; - struct v4l2_subdev *sd; - struct v4l2_ctrl_handler *hdl; - const struct sr030pc30_platform_data *pdata - = client->dev.platform_data; - int ret; - - if (!pdata) { - dev_err(&client->dev, "No platform data!"); - return -EIO; - } - - ret = sr030pc30_detect(client); - if (ret) - return ret; - - info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - sd = &info->sd; - info->pdata = client->dev.platform_data; - - v4l2_i2c_subdev_init(sd, client, &sr030pc30_ops); - - hdl = &info->hdl; - v4l2_ctrl_handler_init(hdl, 6); - info->awb = v4l2_ctrl_new_std(hdl, &sr030pc30_ctrl_ops, - V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1); - info->red = v4l2_ctrl_new_std(hdl, &sr030pc30_ctrl_ops, - V4L2_CID_RED_BALANCE, 0, 127, 1, 64); - info->blue = v4l2_ctrl_new_std(hdl, &sr030pc30_ctrl_ops, - V4L2_CID_BLUE_BALANCE, 0, 127, 1, 64); - info->autoexp = v4l2_ctrl_new_std(hdl, &sr030pc30_ctrl_ops, - V4L2_CID_EXPOSURE_AUTO, 0, 1, 1, 1); - info->exp = v4l2_ctrl_new_std(hdl, &sr030pc30_ctrl_ops, - V4L2_CID_EXPOSURE, EXPOS_MIN_MS, EXPOS_MAX_MS, 1, 30); - sd->ctrl_handler = hdl; - if (hdl->error) { - int err = hdl->error; - - v4l2_ctrl_handler_free(hdl); - return err; - } - v4l2_ctrl_auto_cluster(3, &info->awb, 0, false); - v4l2_ctrl_auto_cluster(2, &info->autoexp, V4L2_EXPOSURE_MANUAL, false); - v4l2_ctrl_handler_setup(hdl); - - info->i2c_reg_page = -1; - info->hflip = 1; - - return 0; -} - -static void sr030pc30_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(sd->ctrl_handler); -} - -static const struct i2c_device_id sr030pc30_id[] = { - { MODULE_NAME, 0 }, - { }, -}; -MODULE_DEVICE_TABLE(i2c, sr030pc30_id); - - -static struct i2c_driver sr030pc30_i2c_driver = { - .driver = { - .name = MODULE_NAME - }, - .probe_new = sr030pc30_probe, - .remove = sr030pc30_remove, - .id_table = sr030pc30_id, -}; - -module_i2c_driver(sr030pc30_i2c_driver); - -MODULE_DESCRIPTION("Siliconfile SR030PC30 camera driver"); -MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>"); -MODULE_LICENSE("GPL"); diff --git a/drivers/media/i2c/st-vgxy61.c b/drivers/media/i2c/st-vgxy61.c index 5dcabee6677d..adbd093ad190 100644 --- a/drivers/media/i2c/st-vgxy61.c +++ b/drivers/media/i2c/st-vgxy61.c @@ -1334,7 +1334,6 @@ static int vgxy61_init_cfg(struct v4l2_subdev *sd, struct vgxy61_dev *sensor = to_vgxy61_dev(sd); struct v4l2_subdev_format fmt = { 0 }; - sensor->current_mode = sensor->default_mode; vgxy61_fill_framefmt(sensor, sensor->current_mode, &fmt.format, VGXY61_MEDIA_BUS_FMT_DEF); @@ -1549,7 +1548,7 @@ static int vgxy61_tx_from_ep(struct vgxy61_dev *sensor, sensor->nb_of_lane = l_nb; dev_dbg(&client->dev, "tx uses %d lanes", l_nb); - for (i = 0; i < 5; i++) { + for (i = 0; i < VGXY61_NB_POLARITIES; i++) { dev_dbg(&client->dev, "log2phy[%d] = %d\n", i, log2phy[i]); dev_dbg(&client->dev, "phy2log[%d] = %d\n", i, phy2log[i]); dev_dbg(&client->dev, "polarity[%d] = %d\n", i, polarities[i]); @@ -1735,6 +1734,12 @@ static int vgxy61_power_on(struct device *dev) } } + ret = vgxy61_detect(sensor); + if (ret) { + dev_err(&client->dev, "sensor detect failed %d\n", ret); + goto disable_clock; + } + ret = vgxy61_patch(sensor); if (ret) { dev_err(&client->dev, "sensor patch failed %d\n", ret); @@ -1861,21 +1866,15 @@ static int vgxy61_probe(struct i2c_client *client) if (ret) return ret; - ret = vgxy61_detect(sensor); - if (ret) { - dev_err(&client->dev, "sensor detect failed %d\n", ret); - return ret; - } - vgxy61_fill_sensor_param(sensor); vgxy61_fill_framefmt(sensor, sensor->current_mode, &sensor->fmt, VGXY61_MEDIA_BUS_FMT_DEF); + mutex_init(&sensor->lock); + ret = vgxy61_update_hdr(sensor, sensor->hdr); if (ret) - return ret; - - mutex_init(&sensor->lock); + goto error_power_off; ret = vgxy61_init_controls(sensor); if (ret) { @@ -1914,8 +1913,8 @@ error_pm_runtime: media_entity_cleanup(&sensor->sd.entity); error_handler_free: v4l2_ctrl_handler_free(sensor->sd.ctrl_handler); - mutex_destroy(&sensor->lock); error_power_off: + mutex_destroy(&sensor->lock); vgxy61_power_off(dev); return ret; diff --git a/drivers/media/i2c/tc358746.c b/drivers/media/i2c/tc358746.c index 4063754a6732..ec1a193ba161 100644 --- a/drivers/media/i2c/tc358746.c +++ b/drivers/media/i2c/tc358746.c @@ -854,11 +854,11 @@ static unsigned long tc358746_find_pll_settings(struct tc358746 *tc358746, m_best = mul; min_delta = delta; best_freq = tmp; - }; + } if (delta == 0) break; - }; + } if (!best_freq) { dev_err(dev, "Failed find PLL frequency\n"); diff --git a/drivers/media/i2c/vs6624.c b/drivers/media/i2c/vs6624.c deleted file mode 100644 index d35c5ec148f4..000000000000 --- a/drivers/media/i2c/vs6624.c +++ /dev/null @@ -1,854 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * vs6624.c ST VS6624 CMOS image sensor driver - * - * Copyright (c) 2011 Analog Devices Inc. - */ - -#include <linux/delay.h> -#include <linux/errno.h> -#include <linux/gpio.h> -#include <linux/i2c.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/types.h> -#include <linux/videodev2.h> - -#include <media/v4l2-ctrls.h> -#include <media/v4l2-device.h> -#include <media/v4l2-mediabus.h> -#include <media/v4l2-image-sizes.h> - -#include "vs6624_regs.h" - -#define MAX_FRAME_RATE 30 - -struct vs6624 { - struct v4l2_subdev sd; - struct v4l2_ctrl_handler hdl; - struct v4l2_fract frame_rate; - struct v4l2_mbus_framefmt fmt; - unsigned ce_pin; -}; - -static const struct vs6624_format { - u32 mbus_code; - enum v4l2_colorspace colorspace; -} vs6624_formats[] = { - { - .mbus_code = MEDIA_BUS_FMT_UYVY8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - }, - { - .mbus_code = MEDIA_BUS_FMT_YUYV8_2X8, - .colorspace = V4L2_COLORSPACE_JPEG, - }, - { - .mbus_code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .colorspace = V4L2_COLORSPACE_SRGB, - }, -}; - -static const struct v4l2_mbus_framefmt vs6624_default_fmt = { - .width = VGA_WIDTH, - .height = VGA_HEIGHT, - .code = MEDIA_BUS_FMT_UYVY8_2X8, - .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_JPEG, -}; - -static const u16 vs6624_p1[] = { - 0x8104, 0x03, - 0x8105, 0x01, - 0xc900, 0x03, - 0xc904, 0x47, - 0xc905, 0x10, - 0xc906, 0x80, - 0xc907, 0x3a, - 0x903a, 0x02, - 0x903b, 0x47, - 0x903c, 0x15, - 0xc908, 0x31, - 0xc909, 0xdc, - 0xc90a, 0x80, - 0xc90b, 0x44, - 0x9044, 0x02, - 0x9045, 0x31, - 0x9046, 0xe2, - 0xc90c, 0x07, - 0xc90d, 0xe0, - 0xc90e, 0x80, - 0xc90f, 0x47, - 0x9047, 0x90, - 0x9048, 0x83, - 0x9049, 0x81, - 0x904a, 0xe0, - 0x904b, 0x60, - 0x904c, 0x08, - 0x904d, 0x90, - 0x904e, 0xc0, - 0x904f, 0x43, - 0x9050, 0x74, - 0x9051, 0x01, - 0x9052, 0xf0, - 0x9053, 0x80, - 0x9054, 0x05, - 0x9055, 0xE4, - 0x9056, 0x90, - 0x9057, 0xc0, - 0x9058, 0x43, - 0x9059, 0xf0, - 0x905a, 0x02, - 0x905b, 0x07, - 0x905c, 0xec, - 0xc910, 0x5d, - 0xc911, 0xca, - 0xc912, 0x80, - 0xc913, 0x5d, - 0x905d, 0xa3, - 0x905e, 0x04, - 0x905f, 0xf0, - 0x9060, 0xa3, - 0x9061, 0x04, - 0x9062, 0xf0, - 0x9063, 0x22, - 0xc914, 0x72, - 0xc915, 0x92, - 0xc916, 0x80, - 0xc917, 0x64, - 0x9064, 0x74, - 0x9065, 0x01, - 0x9066, 0x02, - 0x9067, 0x72, - 0x9068, 0x95, - 0xc918, 0x47, - 0xc919, 0xf2, - 0xc91a, 0x81, - 0xc91b, 0x69, - 0x9169, 0x74, - 0x916a, 0x02, - 0x916b, 0xf0, - 0x916c, 0xec, - 0x916d, 0xb4, - 0x916e, 0x10, - 0x916f, 0x0a, - 0x9170, 0x90, - 0x9171, 0x80, - 0x9172, 0x16, - 0x9173, 0xe0, - 0x9174, 0x70, - 0x9175, 0x04, - 0x9176, 0x90, - 0x9177, 0xd3, - 0x9178, 0xc4, - 0x9179, 0xf0, - 0x917a, 0x22, - 0xc91c, 0x0a, - 0xc91d, 0xbe, - 0xc91e, 0x80, - 0xc91f, 0x73, - 0x9073, 0xfc, - 0x9074, 0xa3, - 0x9075, 0xe0, - 0x9076, 0xf5, - 0x9077, 0x82, - 0x9078, 0x8c, - 0x9079, 0x83, - 0x907a, 0xa3, - 0x907b, 0xa3, - 0x907c, 0xe0, - 0x907d, 0xfc, - 0x907e, 0xa3, - 0x907f, 0xe0, - 0x9080, 0xc3, - 0x9081, 0x9f, - 0x9082, 0xff, - 0x9083, 0xec, - 0x9084, 0x9e, - 0x9085, 0xfe, - 0x9086, 0x02, - 0x9087, 0x0a, - 0x9088, 0xea, - 0xc920, 0x47, - 0xc921, 0x38, - 0xc922, 0x80, - 0xc923, 0x89, - 0x9089, 0xec, - 0x908a, 0xd3, - 0x908b, 0x94, - 0x908c, 0x20, - 0x908d, 0x40, - 0x908e, 0x01, - 0x908f, 0x1c, - 0x9090, 0x90, - 0x9091, 0xd3, - 0x9092, 0xd4, - 0x9093, 0xec, - 0x9094, 0xf0, - 0x9095, 0x02, - 0x9096, 0x47, - 0x9097, 0x3d, - 0xc924, 0x45, - 0xc925, 0xca, - 0xc926, 0x80, - 0xc927, 0x98, - 0x9098, 0x12, - 0x9099, 0x77, - 0x909a, 0xd6, - 0x909b, 0x02, - 0x909c, 0x45, - 0x909d, 0xcd, - 0xc928, 0x20, - 0xc929, 0xd5, - 0xc92a, 0x80, - 0xc92b, 0x9e, - 0x909e, 0x90, - 0x909f, 0x82, - 0x90a0, 0x18, - 0x90a1, 0xe0, - 0x90a2, 0xb4, - 0x90a3, 0x03, - 0x90a4, 0x0e, - 0x90a5, 0x90, - 0x90a6, 0x83, - 0x90a7, 0xbf, - 0x90a8, 0xe0, - 0x90a9, 0x60, - 0x90aa, 0x08, - 0x90ab, 0x90, - 0x90ac, 0x81, - 0x90ad, 0xfc, - 0x90ae, 0xe0, - 0x90af, 0xff, - 0x90b0, 0xc3, - 0x90b1, 0x13, - 0x90b2, 0xf0, - 0x90b3, 0x90, - 0x90b4, 0x81, - 0x90b5, 0xfc, - 0x90b6, 0xe0, - 0x90b7, 0xff, - 0x90b8, 0x02, - 0x90b9, 0x20, - 0x90ba, 0xda, - 0xc92c, 0x70, - 0xc92d, 0xbc, - 0xc92e, 0x80, - 0xc92f, 0xbb, - 0x90bb, 0x90, - 0x90bc, 0x82, - 0x90bd, 0x18, - 0x90be, 0xe0, - 0x90bf, 0xb4, - 0x90c0, 0x03, - 0x90c1, 0x06, - 0x90c2, 0x90, - 0x90c3, 0xc1, - 0x90c4, 0x06, - 0x90c5, 0x74, - 0x90c6, 0x05, - 0x90c7, 0xf0, - 0x90c8, 0x90, - 0x90c9, 0xd3, - 0x90ca, 0xa0, - 0x90cb, 0x02, - 0x90cc, 0x70, - 0x90cd, 0xbf, - 0xc930, 0x72, - 0xc931, 0x21, - 0xc932, 0x81, - 0xc933, 0x3b, - 0x913b, 0x7d, - 0x913c, 0x02, - 0x913d, 0x7f, - 0x913e, 0x7b, - 0x913f, 0x02, - 0x9140, 0x72, - 0x9141, 0x25, - 0xc934, 0x28, - 0xc935, 0xae, - 0xc936, 0x80, - 0xc937, 0xd2, - 0x90d2, 0xf0, - 0x90d3, 0x90, - 0x90d4, 0xd2, - 0x90d5, 0x0a, - 0x90d6, 0x02, - 0x90d7, 0x28, - 0x90d8, 0xb4, - 0xc938, 0x28, - 0xc939, 0xb1, - 0xc93a, 0x80, - 0xc93b, 0xd9, - 0x90d9, 0x90, - 0x90da, 0x83, - 0x90db, 0xba, - 0x90dc, 0xe0, - 0x90dd, 0xff, - 0x90de, 0x90, - 0x90df, 0xd2, - 0x90e0, 0x08, - 0x90e1, 0xe0, - 0x90e2, 0xe4, - 0x90e3, 0xef, - 0x90e4, 0xf0, - 0x90e5, 0xa3, - 0x90e6, 0xe0, - 0x90e7, 0x74, - 0x90e8, 0xff, - 0x90e9, 0xf0, - 0x90ea, 0x90, - 0x90eb, 0xd2, - 0x90ec, 0x0a, - 0x90ed, 0x02, - 0x90ee, 0x28, - 0x90ef, 0xb4, - 0xc93c, 0x29, - 0xc93d, 0x79, - 0xc93e, 0x80, - 0xc93f, 0xf0, - 0x90f0, 0xf0, - 0x90f1, 0x90, - 0x90f2, 0xd2, - 0x90f3, 0x0e, - 0x90f4, 0x02, - 0x90f5, 0x29, - 0x90f6, 0x7f, - 0xc940, 0x29, - 0xc941, 0x7c, - 0xc942, 0x80, - 0xc943, 0xf7, - 0x90f7, 0x90, - 0x90f8, 0x83, - 0x90f9, 0xba, - 0x90fa, 0xe0, - 0x90fb, 0xff, - 0x90fc, 0x90, - 0x90fd, 0xd2, - 0x90fe, 0x0c, - 0x90ff, 0xe0, - 0x9100, 0xe4, - 0x9101, 0xef, - 0x9102, 0xf0, - 0x9103, 0xa3, - 0x9104, 0xe0, - 0x9105, 0x74, - 0x9106, 0xff, - 0x9107, 0xf0, - 0x9108, 0x90, - 0x9109, 0xd2, - 0x910a, 0x0e, - 0x910b, 0x02, - 0x910c, 0x29, - 0x910d, 0x7f, - 0xc944, 0x2a, - 0xc945, 0x42, - 0xc946, 0x81, - 0xc947, 0x0e, - 0x910e, 0xf0, - 0x910f, 0x90, - 0x9110, 0xd2, - 0x9111, 0x12, - 0x9112, 0x02, - 0x9113, 0x2a, - 0x9114, 0x48, - 0xc948, 0x2a, - 0xc949, 0x45, - 0xc94a, 0x81, - 0xc94b, 0x15, - 0x9115, 0x90, - 0x9116, 0x83, - 0x9117, 0xba, - 0x9118, 0xe0, - 0x9119, 0xff, - 0x911a, 0x90, - 0x911b, 0xd2, - 0x911c, 0x10, - 0x911d, 0xe0, - 0x911e, 0xe4, - 0x911f, 0xef, - 0x9120, 0xf0, - 0x9121, 0xa3, - 0x9122, 0xe0, - 0x9123, 0x74, - 0x9124, 0xff, - 0x9125, 0xf0, - 0x9126, 0x90, - 0x9127, 0xd2, - 0x9128, 0x12, - 0x9129, 0x02, - 0x912a, 0x2a, - 0x912b, 0x48, - 0xc900, 0x01, - 0x0000, 0x00, -}; - -static const u16 vs6624_p2[] = { - 0x806f, 0x01, - 0x058c, 0x01, - 0x0000, 0x00, -}; - -static const u16 vs6624_run_setup[] = { - 0x1d18, 0x00, /* Enableconstrainedwhitebalance */ - VS6624_PEAK_MIN_OUT_G_MSB, 0x3c, /* Damper PeakGain Output MSB */ - VS6624_PEAK_MIN_OUT_G_LSB, 0x66, /* Damper PeakGain Output LSB */ - VS6624_CM_LOW_THR_MSB, 0x65, /* Damper Low MSB */ - VS6624_CM_LOW_THR_LSB, 0xd1, /* Damper Low LSB */ - VS6624_CM_HIGH_THR_MSB, 0x66, /* Damper High MSB */ - VS6624_CM_HIGH_THR_LSB, 0x62, /* Damper High LSB */ - VS6624_CM_MIN_OUT_MSB, 0x00, /* Damper Min output MSB */ - VS6624_CM_MIN_OUT_LSB, 0x00, /* Damper Min output LSB */ - VS6624_NORA_DISABLE, 0x00, /* Nora fDisable */ - VS6624_NORA_USAGE, 0x04, /* Nora usage */ - VS6624_NORA_LOW_THR_MSB, 0x63, /* Damper Low MSB Changed 0x63 to 0x65 */ - VS6624_NORA_LOW_THR_LSB, 0xd1, /* Damper Low LSB */ - VS6624_NORA_HIGH_THR_MSB, 0x68, /* Damper High MSB */ - VS6624_NORA_HIGH_THR_LSB, 0xdd, /* Damper High LSB */ - VS6624_NORA_MIN_OUT_MSB, 0x3a, /* Damper Min output MSB */ - VS6624_NORA_MIN_OUT_LSB, 0x00, /* Damper Min output LSB */ - VS6624_F2B_DISABLE, 0x00, /* Disable */ - 0x1d8a, 0x30, /* MAXWeightHigh */ - 0x1d91, 0x62, /* fpDamperLowThresholdHigh MSB */ - 0x1d92, 0x4a, /* fpDamperLowThresholdHigh LSB */ - 0x1d95, 0x65, /* fpDamperHighThresholdHigh MSB */ - 0x1d96, 0x0e, /* fpDamperHighThresholdHigh LSB */ - 0x1da1, 0x3a, /* fpMinimumDamperOutputLow MSB */ - 0x1da2, 0xb8, /* fpMinimumDamperOutputLow LSB */ - 0x1e08, 0x06, /* MAXWeightLow */ - 0x1e0a, 0x0a, /* MAXWeightHigh */ - 0x1601, 0x3a, /* Red A MSB */ - 0x1602, 0x14, /* Red A LSB */ - 0x1605, 0x3b, /* Blue A MSB */ - 0x1606, 0x85, /* BLue A LSB */ - 0x1609, 0x3b, /* RED B MSB */ - 0x160a, 0x85, /* RED B LSB */ - 0x160d, 0x3a, /* Blue B MSB */ - 0x160e, 0x14, /* Blue B LSB */ - 0x1611, 0x30, /* Max Distance from Locus MSB */ - 0x1612, 0x8f, /* Max Distance from Locus MSB */ - 0x1614, 0x01, /* Enable constrainer */ - 0x0000, 0x00, -}; - -static const u16 vs6624_default[] = { - VS6624_CONTRAST0, 0x84, - VS6624_SATURATION0, 0x75, - VS6624_GAMMA0, 0x11, - VS6624_CONTRAST1, 0x84, - VS6624_SATURATION1, 0x75, - VS6624_GAMMA1, 0x11, - VS6624_MAN_RG, 0x80, - VS6624_MAN_GG, 0x80, - VS6624_MAN_BG, 0x80, - VS6624_WB_MODE, 0x1, - VS6624_EXPO_COMPENSATION, 0xfe, - VS6624_EXPO_METER, 0x0, - VS6624_LIGHT_FREQ, 0x64, - VS6624_PEAK_GAIN, 0xe, - VS6624_PEAK_LOW_THR, 0x28, - VS6624_HMIRROR0, 0x0, - VS6624_VFLIP0, 0x0, - VS6624_ZOOM_HSTEP0_MSB, 0x0, - VS6624_ZOOM_HSTEP0_LSB, 0x1, - VS6624_ZOOM_VSTEP0_MSB, 0x0, - VS6624_ZOOM_VSTEP0_LSB, 0x1, - VS6624_PAN_HSTEP0_MSB, 0x0, - VS6624_PAN_HSTEP0_LSB, 0xf, - VS6624_PAN_VSTEP0_MSB, 0x0, - VS6624_PAN_VSTEP0_LSB, 0xf, - VS6624_SENSOR_MODE, 0x1, - VS6624_SYNC_CODE_SETUP, 0x21, - VS6624_DISABLE_FR_DAMPER, 0x0, - VS6624_FR_DEN, 0x1, - VS6624_FR_NUM_LSB, 0xf, - VS6624_INIT_PIPE_SETUP, 0x0, - VS6624_IMG_FMT0, 0x0, - VS6624_YUV_SETUP, 0x1, - VS6624_IMAGE_SIZE0, 0x2, - 0x0000, 0x00, -}; - -static inline struct vs6624 *to_vs6624(struct v4l2_subdev *sd) -{ - return container_of(sd, struct vs6624, sd); -} -static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) -{ - return &container_of(ctrl->handler, struct vs6624, hdl)->sd; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int vs6624_read(struct v4l2_subdev *sd, u16 index) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 buf[2]; - - buf[0] = index >> 8; - buf[1] = index; - i2c_master_send(client, buf, 2); - i2c_master_recv(client, buf, 1); - - return buf[0]; -} -#endif - -static int vs6624_write(struct v4l2_subdev *sd, u16 index, - u8 value) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - u8 buf[3]; - - buf[0] = index >> 8; - buf[1] = index; - buf[2] = value; - - return i2c_master_send(client, buf, 3); -} - -static int vs6624_writeregs(struct v4l2_subdev *sd, const u16 *regs) -{ - u16 reg; - u8 data; - - while (*regs != 0x00) { - reg = *regs++; - data = *regs++; - - vs6624_write(sd, reg, data); - } - return 0; -} - -static int vs6624_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - - switch (ctrl->id) { - case V4L2_CID_CONTRAST: - vs6624_write(sd, VS6624_CONTRAST0, ctrl->val); - break; - case V4L2_CID_SATURATION: - vs6624_write(sd, VS6624_SATURATION0, ctrl->val); - break; - case V4L2_CID_HFLIP: - vs6624_write(sd, VS6624_HMIRROR0, ctrl->val); - break; - case V4L2_CID_VFLIP: - vs6624_write(sd, VS6624_VFLIP0, ctrl->val); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int vs6624_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->pad || code->index >= ARRAY_SIZE(vs6624_formats)) - return -EINVAL; - - code->code = vs6624_formats[code->index].mbus_code; - return 0; -} - -static int vs6624_set_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct v4l2_mbus_framefmt *fmt = &format->format; - struct vs6624 *sensor = to_vs6624(sd); - int index; - - if (format->pad) - return -EINVAL; - - for (index = 0; index < ARRAY_SIZE(vs6624_formats); index++) - if (vs6624_formats[index].mbus_code == fmt->code) - break; - if (index >= ARRAY_SIZE(vs6624_formats)) { - /* default to first format */ - index = 0; - fmt->code = vs6624_formats[0].mbus_code; - } - - /* sensor mode is VGA */ - if (fmt->width > VGA_WIDTH) - fmt->width = VGA_WIDTH; - if (fmt->height > VGA_HEIGHT) - fmt->height = VGA_HEIGHT; - fmt->width = fmt->width & (~3); - fmt->height = fmt->height & (~3); - fmt->field = V4L2_FIELD_NONE; - fmt->colorspace = vs6624_formats[index].colorspace; - - if (format->which == V4L2_SUBDEV_FORMAT_TRY) { - sd_state->pads->try_fmt = *fmt; - return 0; - } - - /* set image format */ - switch (fmt->code) { - case MEDIA_BUS_FMT_UYVY8_2X8: - vs6624_write(sd, VS6624_IMG_FMT0, 0x0); - vs6624_write(sd, VS6624_YUV_SETUP, 0x1); - break; - case MEDIA_BUS_FMT_YUYV8_2X8: - vs6624_write(sd, VS6624_IMG_FMT0, 0x0); - vs6624_write(sd, VS6624_YUV_SETUP, 0x3); - break; - case MEDIA_BUS_FMT_RGB565_2X8_LE: - vs6624_write(sd, VS6624_IMG_FMT0, 0x4); - vs6624_write(sd, VS6624_RGB_SETUP, 0x0); - break; - default: - return -EINVAL; - } - - /* set image size */ - if ((fmt->width == VGA_WIDTH) && (fmt->height == VGA_HEIGHT)) - vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x2); - else if ((fmt->width == QVGA_WIDTH) && (fmt->height == QVGA_HEIGHT)) - vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x4); - else if ((fmt->width == QQVGA_WIDTH) && (fmt->height == QQVGA_HEIGHT)) - vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x6); - else if ((fmt->width == CIF_WIDTH) && (fmt->height == CIF_HEIGHT)) - vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x3); - else if ((fmt->width == QCIF_WIDTH) && (fmt->height == QCIF_HEIGHT)) - vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x5); - else if ((fmt->width == QQCIF_WIDTH) && (fmt->height == QQCIF_HEIGHT)) - vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x7); - else { - vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x8); - vs6624_write(sd, VS6624_MAN_HSIZE0_MSB, fmt->width >> 8); - vs6624_write(sd, VS6624_MAN_HSIZE0_LSB, fmt->width & 0xFF); - vs6624_write(sd, VS6624_MAN_VSIZE0_MSB, fmt->height >> 8); - vs6624_write(sd, VS6624_MAN_VSIZE0_LSB, fmt->height & 0xFF); - vs6624_write(sd, VS6624_CROP_CTRL0, 0x1); - } - - sensor->fmt = *fmt; - - return 0; -} - -static int vs6624_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_state *sd_state, - struct v4l2_subdev_format *format) -{ - struct vs6624 *sensor = to_vs6624(sd); - - if (format->pad) - return -EINVAL; - - format->format = sensor->fmt; - return 0; -} - -static int vs6624_g_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *ival) -{ - struct vs6624 *sensor = to_vs6624(sd); - - ival->interval.numerator = sensor->frame_rate.denominator; - ival->interval.denominator = sensor->frame_rate.numerator; - return 0; -} - -static int vs6624_s_frame_interval(struct v4l2_subdev *sd, - struct v4l2_subdev_frame_interval *ival) -{ - struct vs6624 *sensor = to_vs6624(sd); - struct v4l2_fract *tpf = &ival->interval; - - - if (tpf->numerator == 0 || tpf->denominator == 0 - || (tpf->denominator > tpf->numerator * MAX_FRAME_RATE)) { - /* reset to max frame rate */ - tpf->numerator = 1; - tpf->denominator = MAX_FRAME_RATE; - } - sensor->frame_rate.numerator = tpf->denominator; - sensor->frame_rate.denominator = tpf->numerator; - vs6624_write(sd, VS6624_DISABLE_FR_DAMPER, 0x0); - vs6624_write(sd, VS6624_FR_NUM_MSB, - sensor->frame_rate.numerator >> 8); - vs6624_write(sd, VS6624_FR_NUM_LSB, - sensor->frame_rate.numerator & 0xFF); - vs6624_write(sd, VS6624_FR_DEN, - sensor->frame_rate.denominator & 0xFF); - return 0; -} - -static int vs6624_s_stream(struct v4l2_subdev *sd, int enable) -{ - if (enable) - vs6624_write(sd, VS6624_USER_CMD, 0x2); - else - vs6624_write(sd, VS6624_USER_CMD, 0x4); - udelay(100); - return 0; -} - -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int vs6624_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) -{ - reg->val = vs6624_read(sd, reg->reg & 0xffff); - reg->size = 1; - return 0; -} - -static int vs6624_s_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg) -{ - vs6624_write(sd, reg->reg & 0xffff, reg->val & 0xff); - return 0; -} -#endif - -static const struct v4l2_ctrl_ops vs6624_ctrl_ops = { - .s_ctrl = vs6624_s_ctrl, -}; - -static const struct v4l2_subdev_core_ops vs6624_core_ops = { -#ifdef CONFIG_VIDEO_ADV_DEBUG - .g_register = vs6624_g_register, - .s_register = vs6624_s_register, -#endif -}; - -static const struct v4l2_subdev_video_ops vs6624_video_ops = { - .s_frame_interval = vs6624_s_frame_interval, - .g_frame_interval = vs6624_g_frame_interval, - .s_stream = vs6624_s_stream, -}; - -static const struct v4l2_subdev_pad_ops vs6624_pad_ops = { - .enum_mbus_code = vs6624_enum_mbus_code, - .get_fmt = vs6624_get_fmt, - .set_fmt = vs6624_set_fmt, -}; - -static const struct v4l2_subdev_ops vs6624_ops = { - .core = &vs6624_core_ops, - .video = &vs6624_video_ops, - .pad = &vs6624_pad_ops, -}; - -static int vs6624_probe(struct i2c_client *client) -{ - struct vs6624 *sensor; - struct v4l2_subdev *sd; - struct v4l2_ctrl_handler *hdl; - const unsigned *ce; - int ret; - - /* Check if the adapter supports the needed features */ - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) - return -EIO; - - ce = client->dev.platform_data; - if (ce == NULL) - return -EINVAL; - - ret = devm_gpio_request_one(&client->dev, *ce, GPIOF_OUT_INIT_HIGH, - "VS6624 Chip Enable"); - if (ret) { - v4l_err(client, "failed to request GPIO %d\n", *ce); - return ret; - } - /* wait 100ms before any further i2c writes are performed */ - msleep(100); - - sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL); - if (sensor == NULL) - return -ENOMEM; - - sd = &sensor->sd; - v4l2_i2c_subdev_init(sd, client, &vs6624_ops); - - vs6624_writeregs(sd, vs6624_p1); - vs6624_write(sd, VS6624_MICRO_EN, 0x2); - vs6624_write(sd, VS6624_DIO_EN, 0x1); - usleep_range(10000, 11000); - vs6624_writeregs(sd, vs6624_p2); - - vs6624_writeregs(sd, vs6624_default); - vs6624_write(sd, VS6624_HSYNC_SETUP, 0xF); - vs6624_writeregs(sd, vs6624_run_setup); - - /* set frame rate */ - sensor->frame_rate.numerator = MAX_FRAME_RATE; - sensor->frame_rate.denominator = 1; - vs6624_write(sd, VS6624_DISABLE_FR_DAMPER, 0x0); - vs6624_write(sd, VS6624_FR_NUM_MSB, - sensor->frame_rate.numerator >> 8); - vs6624_write(sd, VS6624_FR_NUM_LSB, - sensor->frame_rate.numerator & 0xFF); - vs6624_write(sd, VS6624_FR_DEN, - sensor->frame_rate.denominator & 0xFF); - - sensor->fmt = vs6624_default_fmt; - sensor->ce_pin = *ce; - - v4l_info(client, "chip found @ 0x%02x (%s)\n", - client->addr << 1, client->adapter->name); - - hdl = &sensor->hdl; - v4l2_ctrl_handler_init(hdl, 4); - v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, - V4L2_CID_CONTRAST, 0, 0xFF, 1, 0x87); - v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, - V4L2_CID_SATURATION, 0, 0xFF, 1, 0x78); - v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, - V4L2_CID_HFLIP, 0, 1, 1, 0); - v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops, - V4L2_CID_VFLIP, 0, 1, 1, 0); - /* hook the control handler into the driver */ - sd->ctrl_handler = hdl; - if (hdl->error) { - int err = hdl->error; - - v4l2_ctrl_handler_free(hdl); - return err; - } - - /* initialize the hardware to the default control values */ - ret = v4l2_ctrl_handler_setup(hdl); - if (ret) - v4l2_ctrl_handler_free(hdl); - return ret; -} - -static void vs6624_remove(struct i2c_client *client) -{ - struct v4l2_subdev *sd = i2c_get_clientdata(client); - - v4l2_device_unregister_subdev(sd); - v4l2_ctrl_handler_free(sd->ctrl_handler); -} - -static const struct i2c_device_id vs6624_id[] = { - {"vs6624", 0}, - {}, -}; - -MODULE_DEVICE_TABLE(i2c, vs6624_id); - -static struct i2c_driver vs6624_driver = { - .driver = { - .name = "vs6624", - }, - .probe_new = vs6624_probe, - .remove = vs6624_remove, - .id_table = vs6624_id, -}; - -module_i2c_driver(vs6624_driver); - -MODULE_DESCRIPTION("VS6624 sensor driver"); -MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/i2c/vs6624_regs.h b/drivers/media/i2c/vs6624_regs.h deleted file mode 100644 index 76c9ed0f2c89..000000000000 --- a/drivers/media/i2c/vs6624_regs.h +++ /dev/null @@ -1,325 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * vs6624 - ST VS6624 CMOS image sensor registers - * - * Copyright (c) 2011 Analog Devices Inc. - */ - -#ifndef _VS6624_REGS_H_ -#define _VS6624_REGS_H_ - -/* low level control registers */ -#define VS6624_MICRO_EN 0xC003 /* power enable for all MCU clock */ -#define VS6624_DIO_EN 0xC044 /* enable digital I/O */ -/* device parameters */ -#define VS6624_DEV_ID_MSB 0x0001 /* device id MSB */ -#define VS6624_DEV_ID_LSB 0x0002 /* device id LSB */ -#define VS6624_FW_VSN_MAJOR 0x0004 /* firmware version major */ -#define VS6624_FW_VSN_MINOR 0x0006 /* firmware version minor */ -#define VS6624_PATCH_VSN_MAJOR 0x0008 /* patch version major */ -#define VS6624_PATCH_VSN_MINOR 0x000A /* patch version minor */ -/* host interface manager control */ -#define VS6624_USER_CMD 0x0180 /* user level control of operating states */ -/* host interface manager status */ -#define VS6624_STATE 0x0202 /* current state of the mode manager */ -/* run mode control */ -#define VS6624_METER_ON 0x0280 /* if false AE and AWB are disabled */ -/* mode setup */ -#define VS6624_ACTIVE_PIPE_SETUP 0x0302 /* select the active bank for non view live mode */ -#define VS6624_SENSOR_MODE 0x0308 /* select the different sensor mode */ -/* pipe setup bank0 */ -#define VS6624_IMAGE_SIZE0 0x0380 /* required output dimension */ -#define VS6624_MAN_HSIZE0_MSB 0x0383 /* input required manual H size MSB */ -#define VS6624_MAN_HSIZE0_LSB 0x0384 /* input required manual H size LSB */ -#define VS6624_MAN_VSIZE0_MSB 0x0387 /* input required manual V size MSB */ -#define VS6624_MAN_VSIZE0_LSB 0x0388 /* input required manual V size LSB */ -#define VS6624_ZOOM_HSTEP0_MSB 0x038B /* set the zoom H step MSB */ -#define VS6624_ZOOM_HSTEP0_LSB 0x038C /* set the zoom H step LSB */ -#define VS6624_ZOOM_VSTEP0_MSB 0x038F /* set the zoom V step MSB */ -#define VS6624_ZOOM_VSTEP0_LSB 0x0390 /* set the zoom V step LSB */ -#define VS6624_ZOOM_CTRL0 0x0392 /* control zoon in, out and stop */ -#define VS6624_PAN_HSTEP0_MSB 0x0395 /* set the pan H step MSB */ -#define VS6624_PAN_HSTEP0_LSB 0x0396 /* set the pan H step LSB */ -#define VS6624_PAN_VSTEP0_MSB 0x0399 /* set the pan V step MSB */ -#define VS6624_PAN_VSTEP0_LSB 0x039A /* set the pan V step LSB */ -#define VS6624_PAN_CTRL0 0x039C /* control pan operation */ -#define VS6624_CROP_CTRL0 0x039E /* select cropping mode */ -#define VS6624_CROP_HSTART0_MSB 0x03A1 /* set the cropping H start address MSB */ -#define VS6624_CROP_HSTART0_LSB 0x03A2 /* set the cropping H start address LSB */ -#define VS6624_CROP_HSIZE0_MSB 0x03A5 /* set the cropping H size MSB */ -#define VS6624_CROP_HSIZE0_LSB 0x03A6 /* set the cropping H size LSB */ -#define VS6624_CROP_VSTART0_MSB 0x03A9 /* set the cropping V start address MSB */ -#define VS6624_CROP_VSTART0_LSB 0x03AA /* set the cropping V start address LSB */ -#define VS6624_CROP_VSIZE0_MSB 0x03AD /* set the cropping V size MSB */ -#define VS6624_CROP_VSIZE0_LSB 0x03AE /* set the cropping V size LSB */ -#define VS6624_IMG_FMT0 0x03B0 /* select required output image format */ -#define VS6624_BAYER_OUT_ALIGN0 0x03B2 /* set bayer output alignment */ -#define VS6624_CONTRAST0 0x03B4 /* contrast control for output */ -#define VS6624_SATURATION0 0x03B6 /* saturation control for output */ -#define VS6624_GAMMA0 0x03B8 /* gamma settings */ -#define VS6624_HMIRROR0 0x03BA /* horizontal image orientation flip */ -#define VS6624_VFLIP0 0x03BC /* vertical image orientation flip */ -#define VS6624_CHANNEL_ID0 0x03BE /* logical DMA channel number */ -/* pipe setup bank1 */ -#define VS6624_IMAGE_SIZE1 0x0400 /* required output dimension */ -#define VS6624_MAN_HSIZE1_MSB 0x0403 /* input required manual H size MSB */ -#define VS6624_MAN_HSIZE1_LSB 0x0404 /* input required manual H size LSB */ -#define VS6624_MAN_VSIZE1_MSB 0x0407 /* input required manual V size MSB */ -#define VS6624_MAN_VSIZE1_LSB 0x0408 /* input required manual V size LSB */ -#define VS6624_ZOOM_HSTEP1_MSB 0x040B /* set the zoom H step MSB */ -#define VS6624_ZOOM_HSTEP1_LSB 0x040C /* set the zoom H step LSB */ -#define VS6624_ZOOM_VSTEP1_MSB 0x040F /* set the zoom V step MSB */ -#define VS6624_ZOOM_VSTEP1_LSB 0x0410 /* set the zoom V step LSB */ -#define VS6624_ZOOM_CTRL1 0x0412 /* control zoon in, out and stop */ -#define VS6624_PAN_HSTEP1_MSB 0x0415 /* set the pan H step MSB */ -#define VS6624_PAN_HSTEP1_LSB 0x0416 /* set the pan H step LSB */ -#define VS6624_PAN_VSTEP1_MSB 0x0419 /* set the pan V step MSB */ -#define VS6624_PAN_VSTEP1_LSB 0x041A /* set the pan V step LSB */ -#define VS6624_PAN_CTRL1 0x041C /* control pan operation */ -#define VS6624_CROP_CTRL1 0x041E /* select cropping mode */ -#define VS6624_CROP_HSTART1_MSB 0x0421 /* set the cropping H start address MSB */ -#define VS6624_CROP_HSTART1_LSB 0x0422 /* set the cropping H start address LSB */ -#define VS6624_CROP_HSIZE1_MSB 0x0425 /* set the cropping H size MSB */ -#define VS6624_CROP_HSIZE1_LSB 0x0426 /* set the cropping H size LSB */ -#define VS6624_CROP_VSTART1_MSB 0x0429 /* set the cropping V start address MSB */ -#define VS6624_CROP_VSTART1_LSB 0x042A /* set the cropping V start address LSB */ -#define VS6624_CROP_VSIZE1_MSB 0x042D /* set the cropping V size MSB */ -#define VS6624_CROP_VSIZE1_LSB 0x042E /* set the cropping V size LSB */ -#define VS6624_IMG_FMT1 0x0430 /* select required output image format */ -#define VS6624_BAYER_OUT_ALIGN1 0x0432 /* set bayer output alignment */ -#define VS6624_CONTRAST1 0x0434 /* contrast control for output */ -#define VS6624_SATURATION1 0x0436 /* saturation control for output */ -#define VS6624_GAMMA1 0x0438 /* gamma settings */ -#define VS6624_HMIRROR1 0x043A /* horizontal image orientation flip */ -#define VS6624_VFLIP1 0x043C /* vertical image orientation flip */ -#define VS6624_CHANNEL_ID1 0x043E /* logical DMA channel number */ -/* view live control */ -#define VS6624_VIEW_LIVE_EN 0x0480 /* enable view live mode */ -#define VS6624_INIT_PIPE_SETUP 0x0482 /* select initial pipe setup bank */ -/* view live status */ -#define VS6624_CUR_PIPE_SETUP 0x0500 /* indicates most recently applied setup bank */ -/* power management */ -#define VS6624_TIME_TO_POWER_DOWN 0x0580 /* automatically transition time to stop mode */ -/* video timing parameter host inputs */ -#define VS6624_EXT_CLK_FREQ_NUM_MSB 0x0605 /* external clock frequency numerator MSB */ -#define VS6624_EXT_CLK_FREQ_NUM_LSB 0x0606 /* external clock frequency numerator LSB */ -#define VS6624_EXT_CLK_FREQ_DEN 0x0608 /* external clock frequency denominator */ -/* video timing control */ -#define VS6624_SYS_CLK_MODE 0x0880 /* decides system clock frequency */ -/* frame dimension parameter host inputs */ -#define VS6624_LIGHT_FREQ 0x0C80 /* AC frequency used for flicker free time */ -#define VS6624_FLICKER_COMPAT 0x0C82 /* flicker compatible frame length */ -/* static frame rate control */ -#define VS6624_FR_NUM_MSB 0x0D81 /* desired frame rate numerator MSB */ -#define VS6624_FR_NUM_LSB 0x0D82 /* desired frame rate numerator LSB */ -#define VS6624_FR_DEN 0x0D84 /* desired frame rate denominator */ -/* automatic frame rate control */ -#define VS6624_DISABLE_FR_DAMPER 0x0E80 /* defines frame rate mode */ -#define VS6624_MIN_DAMPER_OUT_MSB 0x0E8C /* minimum frame rate MSB */ -#define VS6624_MIN_DAMPER_OUT_LSB 0x0E8A /* minimum frame rate LSB */ -/* exposure controls */ -#define VS6624_EXPO_MODE 0x1180 /* exposure mode */ -#define VS6624_EXPO_METER 0x1182 /* weights to be associated with the zones */ -#define VS6624_EXPO_TIME_NUM 0x1184 /* exposure time numerator */ -#define VS6624_EXPO_TIME_DEN 0x1186 /* exposure time denominator */ -#define VS6624_EXPO_TIME_MSB 0x1189 /* exposure time for the Manual Mode MSB */ -#define VS6624_EXPO_TIME_LSB 0x118A /* exposure time for the Manual Mode LSB */ -#define VS6624_EXPO_COMPENSATION 0x1190 /* exposure compensation */ -#define VS6624_DIRECT_COARSE_MSB 0x1195 /* coarse integration lines for Direct Mode MSB */ -#define VS6624_DIRECT_COARSE_LSB 0x1196 /* coarse integration lines for Direct Mode LSB */ -#define VS6624_DIRECT_FINE_MSB 0x1199 /* fine integration pixels for Direct Mode MSB */ -#define VS6624_DIRECT_FINE_LSB 0x119A /* fine integration pixels for Direct Mode LSB */ -#define VS6624_DIRECT_ANAL_GAIN_MSB 0x119D /* analog gain for Direct Mode MSB */ -#define VS6624_DIRECT_ANAL_GAIN_LSB 0x119E /* analog gain for Direct Mode LSB */ -#define VS6624_DIRECT_DIGI_GAIN_MSB 0x11A1 /* digital gain for Direct Mode MSB */ -#define VS6624_DIRECT_DIGI_GAIN_LSB 0x11A2 /* digital gain for Direct Mode LSB */ -#define VS6624_FLASH_COARSE_MSB 0x11A5 /* coarse integration lines for Flash Gun Mode MSB */ -#define VS6624_FLASH_COARSE_LSB 0x11A6 /* coarse integration lines for Flash Gun Mode LSB */ -#define VS6624_FLASH_FINE_MSB 0x11A9 /* fine integration pixels for Flash Gun Mode MSB */ -#define VS6624_FLASH_FINE_LSB 0x11AA /* fine integration pixels for Flash Gun Mode LSB */ -#define VS6624_FLASH_ANAL_GAIN_MSB 0x11AD /* analog gain for Flash Gun Mode MSB */ -#define VS6624_FLASH_ANAL_GAIN_LSB 0x11AE /* analog gain for Flash Gun Mode LSB */ -#define VS6624_FLASH_DIGI_GAIN_MSB 0x11B1 /* digital gain for Flash Gun Mode MSB */ -#define VS6624_FLASH_DIGI_GAIN_LSB 0x11B2 /* digital gain for Flash Gun Mode LSB */ -#define VS6624_FREEZE_AE 0x11B4 /* freeze auto exposure */ -#define VS6624_MAX_INT_TIME_MSB 0x11B7 /* user maximum integration time MSB */ -#define VS6624_MAX_INT_TIME_LSB 0x11B8 /* user maximum integration time LSB */ -#define VS6624_FLASH_AG_THR_MSB 0x11BB /* recommend flash gun analog gain threshold MSB */ -#define VS6624_FLASH_AG_THR_LSB 0x11BC /* recommend flash gun analog gain threshold LSB */ -#define VS6624_ANTI_FLICKER_MODE 0x11C0 /* anti flicker mode */ -/* white balance control */ -#define VS6624_WB_MODE 0x1480 /* set white balance mode */ -#define VS6624_MAN_RG 0x1482 /* user setting for red channel gain */ -#define VS6624_MAN_GG 0x1484 /* user setting for green channel gain */ -#define VS6624_MAN_BG 0x1486 /* user setting for blue channel gain */ -#define VS6624_FLASH_RG_MSB 0x148B /* red gain for Flash Gun MSB */ -#define VS6624_FLASH_RG_LSB 0x148C /* red gain for Flash Gun LSB */ -#define VS6624_FLASH_GG_MSB 0x148F /* green gain for Flash Gun MSB */ -#define VS6624_FLASH_GG_LSB 0x1490 /* green gain for Flash Gun LSB */ -#define VS6624_FLASH_BG_MSB 0x1493 /* blue gain for Flash Gun MSB */ -#define VS6624_FLASH_BG_LSB 0x1494 /* blue gain for Flash Gun LSB */ -/* sensor setup */ -#define VS6624_BC_OFFSET 0x1990 /* Black Correction Offset */ -/* image stability */ -#define VS6624_STABLE_WB 0x1900 /* white balance stable */ -#define VS6624_STABLE_EXPO 0x1902 /* exposure stable */ -#define VS6624_STABLE 0x1906 /* system stable */ -/* flash control */ -#define VS6624_FLASH_MODE 0x1A80 /* flash mode */ -#define VS6624_FLASH_OFF_LINE_MSB 0x1A83 /* off line at flash pulse mode MSB */ -#define VS6624_FLASH_OFF_LINE_LSB 0x1A84 /* off line at flash pulse mode LSB */ -/* flash status */ -#define VS6624_FLASH_RECOM 0x1B00 /* flash gun is recommended */ -#define VS6624_FLASH_GRAB_COMPLETE 0x1B02 /* flash gun image has been grabbed */ -/* scythe filter controls */ -#define VS6624_SCYTHE_FILTER 0x1D80 /* disable scythe defect correction */ -/* jack filter controls */ -#define VS6624_JACK_FILTER 0x1E00 /* disable jack defect correction */ -/* demosaic control */ -#define VS6624_ANTI_ALIAS_FILTER 0x1E80 /* anti alias filter suppress */ -/* color matrix dampers */ -#define VS6624_CM_DISABLE 0x1F00 /* disable color matrix damper */ -#define VS6624_CM_LOW_THR_MSB 0x1F03 /* low threshold for exposure MSB */ -#define VS6624_CM_LOW_THR_LSB 0x1F04 /* low threshold for exposure LSB */ -#define VS6624_CM_HIGH_THR_MSB 0x1F07 /* high threshold for exposure MSB */ -#define VS6624_CM_HIGH_THR_LSB 0x1F08 /* high threshold for exposure LSB */ -#define VS6624_CM_MIN_OUT_MSB 0x1F0B /* minimum possible damper output MSB */ -#define VS6624_CM_MIN_OUT_LSB 0x1F0C /* minimum possible damper output LSB */ -/* peaking control */ -#define VS6624_PEAK_GAIN 0x2000 /* controls peaking gain */ -#define VS6624_PEAK_G_DISABLE 0x2002 /* disable peak gain damping */ -#define VS6624_PEAK_LOW_THR_G_MSB 0x2005 /* low threshold for exposure for gain MSB */ -#define VS6624_PEAK_LOW_THR_G_LSB 0x2006 /* low threshold for exposure for gain LSB */ -#define VS6624_PEAK_HIGH_THR_G_MSB 0x2009 /* high threshold for exposure for gain MSB */ -#define VS6624_PEAK_HIGH_THR_G_LSB 0x200A /* high threshold for exposure for gain LSB */ -#define VS6624_PEAK_MIN_OUT_G_MSB 0x200D /* minimum damper output for gain MSB */ -#define VS6624_PEAK_MIN_OUT_G_LSB 0x200E /* minimum damper output for gain LSB */ -#define VS6624_PEAK_LOW_THR 0x2010 /* adjust degree of coring */ -#define VS6624_PEAK_C_DISABLE 0x2012 /* disable coring damping */ -#define VS6624_PEAK_HIGH_THR 0x2014 /* adjust maximum gain */ -#define VS6624_PEAK_LOW_THR_C_MSB 0x2017 /* low threshold for exposure for coring MSB */ -#define VS6624_PEAK_LOW_THR_C_LSB 0x2018 /* low threshold for exposure for coring LSB */ -#define VS6624_PEAK_HIGH_THR_C_MSB 0x201B /* high threshold for exposure for coring MSB */ -#define VS6624_PEAK_HIGH_THR_C_LSB 0x201C /* high threshold for exposure for coring LSB */ -#define VS6624_PEAK_MIN_OUT_C_MSB 0x201F /* minimum damper output for coring MSB */ -#define VS6624_PEAK_MIN_OUT_C_LSB 0x2020 /* minimum damper output for coring LSB */ -/* pipe 0 RGB to YUV matrix manual control */ -#define VS6624_RYM0_MAN_CTRL 0x2180 /* enable manual RGB to YUV matrix */ -#define VS6624_RYM0_W00_MSB 0x2183 /* row 0 column 0 of YUV matrix MSB */ -#define VS6624_RYM0_W00_LSB 0x2184 /* row 0 column 0 of YUV matrix LSB */ -#define VS6624_RYM0_W01_MSB 0x2187 /* row 0 column 1 of YUV matrix MSB */ -#define VS6624_RYM0_W01_LSB 0x2188 /* row 0 column 1 of YUV matrix LSB */ -#define VS6624_RYM0_W02_MSB 0x218C /* row 0 column 2 of YUV matrix MSB */ -#define VS6624_RYM0_W02_LSB 0x218D /* row 0 column 2 of YUV matrix LSB */ -#define VS6624_RYM0_W10_MSB 0x2190 /* row 1 column 0 of YUV matrix MSB */ -#define VS6624_RYM0_W10_LSB 0x218F /* row 1 column 0 of YUV matrix LSB */ -#define VS6624_RYM0_W11_MSB 0x2193 /* row 1 column 1 of YUV matrix MSB */ -#define VS6624_RYM0_W11_LSB 0x2194 /* row 1 column 1 of YUV matrix LSB */ -#define VS6624_RYM0_W12_MSB 0x2197 /* row 1 column 2 of YUV matrix MSB */ -#define VS6624_RYM0_W12_LSB 0x2198 /* row 1 column 2 of YUV matrix LSB */ -#define VS6624_RYM0_W20_MSB 0x219B /* row 2 column 0 of YUV matrix MSB */ -#define VS6624_RYM0_W20_LSB 0x219C /* row 2 column 0 of YUV matrix LSB */ -#define VS6624_RYM0_W21_MSB 0x21A0 /* row 2 column 1 of YUV matrix MSB */ -#define VS6624_RYM0_W21_LSB 0x219F /* row 2 column 1 of YUV matrix LSB */ -#define VS6624_RYM0_W22_MSB 0x21A3 /* row 2 column 2 of YUV matrix MSB */ -#define VS6624_RYM0_W22_LSB 0x21A4 /* row 2 column 2 of YUV matrix LSB */ -#define VS6624_RYM0_YINY_MSB 0x21A7 /* Y in Y MSB */ -#define VS6624_RYM0_YINY_LSB 0x21A8 /* Y in Y LSB */ -#define VS6624_RYM0_YINCB_MSB 0x21AB /* Y in Cb MSB */ -#define VS6624_RYM0_YINCB_LSB 0x21AC /* Y in Cb LSB */ -#define VS6624_RYM0_YINCR_MSB 0x21B0 /* Y in Cr MSB */ -#define VS6624_RYM0_YINCR_LSB 0x21AF /* Y in Cr LSB */ -/* pipe 1 RGB to YUV matrix manual control */ -#define VS6624_RYM1_MAN_CTRL 0x2200 /* enable manual RGB to YUV matrix */ -#define VS6624_RYM1_W00_MSB 0x2203 /* row 0 column 0 of YUV matrix MSB */ -#define VS6624_RYM1_W00_LSB 0x2204 /* row 0 column 0 of YUV matrix LSB */ -#define VS6624_RYM1_W01_MSB 0x2207 /* row 0 column 1 of YUV matrix MSB */ -#define VS6624_RYM1_W01_LSB 0x2208 /* row 0 column 1 of YUV matrix LSB */ -#define VS6624_RYM1_W02_MSB 0x220C /* row 0 column 2 of YUV matrix MSB */ -#define VS6624_RYM1_W02_LSB 0x220D /* row 0 column 2 of YUV matrix LSB */ -#define VS6624_RYM1_W10_MSB 0x2210 /* row 1 column 0 of YUV matrix MSB */ -#define VS6624_RYM1_W10_LSB 0x220F /* row 1 column 0 of YUV matrix LSB */ -#define VS6624_RYM1_W11_MSB 0x2213 /* row 1 column 1 of YUV matrix MSB */ -#define VS6624_RYM1_W11_LSB 0x2214 /* row 1 column 1 of YUV matrix LSB */ -#define VS6624_RYM1_W12_MSB 0x2217 /* row 1 column 2 of YUV matrix MSB */ -#define VS6624_RYM1_W12_LSB 0x2218 /* row 1 column 2 of YUV matrix LSB */ -#define VS6624_RYM1_W20_MSB 0x221B /* row 2 column 0 of YUV matrix MSB */ -#define VS6624_RYM1_W20_LSB 0x221C /* row 2 column 0 of YUV matrix LSB */ -#define VS6624_RYM1_W21_MSB 0x2220 /* row 2 column 1 of YUV matrix MSB */ -#define VS6624_RYM1_W21_LSB 0x221F /* row 2 column 1 of YUV matrix LSB */ -#define VS6624_RYM1_W22_MSB 0x2223 /* row 2 column 2 of YUV matrix MSB */ -#define VS6624_RYM1_W22_LSB 0x2224 /* row 2 column 2 of YUV matrix LSB */ -#define VS6624_RYM1_YINY_MSB 0x2227 /* Y in Y MSB */ -#define VS6624_RYM1_YINY_LSB 0x2228 /* Y in Y LSB */ -#define VS6624_RYM1_YINCB_MSB 0x222B /* Y in Cb MSB */ -#define VS6624_RYM1_YINCB_LSB 0x222C /* Y in Cb LSB */ -#define VS6624_RYM1_YINCR_MSB 0x2220 /* Y in Cr MSB */ -#define VS6624_RYM1_YINCR_LSB 0x222F /* Y in Cr LSB */ -/* pipe 0 gamma manual control */ -#define VS6624_GAMMA_MAN_CTRL0 0x2280 /* enable manual gamma setup */ -#define VS6624_GAMMA_PEAK_R0 0x2282 /* peaked red channel gamma value */ -#define VS6624_GAMMA_PEAK_G0 0x2284 /* peaked green channel gamma value */ -#define VS6624_GAMMA_PEAK_B0 0x2286 /* peaked blue channel gamma value */ -#define VS6624_GAMMA_UNPEAK_R0 0x2288 /* unpeaked red channel gamma value */ -#define VS6624_GAMMA_UNPEAK_G0 0x228A /* unpeaked green channel gamma value */ -#define VS6624_GAMMA_UNPEAK_B0 0x228C /* unpeaked blue channel gamma value */ -/* pipe 1 gamma manual control */ -#define VS6624_GAMMA_MAN_CTRL1 0x2300 /* enable manual gamma setup */ -#define VS6624_GAMMA_PEAK_R1 0x2302 /* peaked red channel gamma value */ -#define VS6624_GAMMA_PEAK_G1 0x2304 /* peaked green channel gamma value */ -#define VS6624_GAMMA_PEAK_B1 0x2306 /* peaked blue channel gamma value */ -#define VS6624_GAMMA_UNPEAK_R1 0x2308 /* unpeaked red channel gamma value */ -#define VS6624_GAMMA_UNPEAK_G1 0x230A /* unpeaked green channel gamma value */ -#define VS6624_GAMMA_UNPEAK_B1 0x230C /* unpeaked blue channel gamma value */ -/* fade to black */ -#define VS6624_F2B_DISABLE 0x2480 /* disable fade to black */ -#define VS6624_F2B_BLACK_VAL_MSB 0x2483 /* black value MSB */ -#define VS6624_F2B_BLACK_VAL_LSB 0x2484 /* black value LSB */ -#define VS6624_F2B_LOW_THR_MSB 0x2487 /* low threshold for exposure MSB */ -#define VS6624_F2B_LOW_THR_LSB 0x2488 /* low threshold for exposure LSB */ -#define VS6624_F2B_HIGH_THR_MSB 0x248B /* high threshold for exposure MSB */ -#define VS6624_F2B_HIGH_THR_LSB 0x248C /* high threshold for exposure LSB */ -#define VS6624_F2B_MIN_OUT_MSB 0x248F /* minimum damper output MSB */ -#define VS6624_F2B_MIN_OUT_LSB 0x2490 /* minimum damper output LSB */ -/* output formatter control */ -#define VS6624_CODE_CK_EN 0x2580 /* code check enable */ -#define VS6624_BLANK_FMT 0x2582 /* blank format */ -#define VS6624_SYNC_CODE_SETUP 0x2584 /* sync code setup */ -#define VS6624_HSYNC_SETUP 0x2586 /* H sync setup */ -#define VS6624_VSYNC_SETUP 0x2588 /* V sync setup */ -#define VS6624_PCLK_SETUP 0x258A /* PCLK setup */ -#define VS6624_PCLK_EN 0x258C /* PCLK enable */ -#define VS6624_OPF_SP_SETUP 0x258E /* output formatter sp setup */ -#define VS6624_BLANK_DATA_MSB 0x2590 /* blank data MSB */ -#define VS6624_BLANK_DATA_LSB 0x2592 /* blank data LSB */ -#define VS6624_RGB_SETUP 0x2594 /* RGB setup */ -#define VS6624_YUV_SETUP 0x2596 /* YUV setup */ -#define VS6624_VSYNC_RIS_COARSE_H 0x2598 /* V sync rising coarse high */ -#define VS6624_VSYNC_RIS_COARSE_L 0x259A /* V sync rising coarse low */ -#define VS6624_VSYNC_RIS_FINE_H 0x259C /* V sync rising fine high */ -#define VS6624_VSYNC_RIS_FINE_L 0x259E /* V sync rising fine low */ -#define VS6624_VSYNC_FALL_COARSE_H 0x25A0 /* V sync falling coarse high */ -#define VS6624_VSYNC_FALL_COARSE_L 0x25A2 /* V sync falling coarse low */ -#define VS6624_VSYNC_FALL_FINE_H 0x25A4 /* V sync falling fine high */ -#define VS6624_VSYNC_FALL_FINE_L 0x25A6 /* V sync falling fine low */ -#define VS6624_HSYNC_RIS_H 0x25A8 /* H sync rising high */ -#define VS6624_HSYNC_RIS_L 0x25AA /* H sync rising low */ -#define VS6624_HSYNC_FALL_H 0x25AC /* H sync falling high */ -#define VS6624_HSYNC_FALL_L 0x25AE /* H sync falling low */ -#define VS6624_OUT_IF 0x25B0 /* output interface */ -#define VS6624_CCP_EXT_DATA 0x25B2 /* CCP extra data */ -/* NoRA controls */ -#define VS6624_NORA_DISABLE 0x2600 /* NoRA control mode */ -#define VS6624_NORA_USAGE 0x2602 /* usage */ -#define VS6624_NORA_SPLIT_KN 0x2604 /* split kn */ -#define VS6624_NORA_SPLIT_NI 0x2606 /* split ni */ -#define VS6624_NORA_TIGHT_G 0x2608 /* tight green */ -#define VS6624_NORA_DISABLE_NP 0x260A /* disable noro promoting */ -#define VS6624_NORA_LOW_THR_MSB 0x260D /* low threshold for exposure MSB */ -#define VS6624_NORA_LOW_THR_LSB 0x260E /* low threshold for exposure LSB */ -#define VS6624_NORA_HIGH_THR_MSB 0x2611 /* high threshold for exposure MSB */ -#define VS6624_NORA_HIGH_THR_LSB 0x2612 /* high threshold for exposure LSB */ -#define VS6624_NORA_MIN_OUT_MSB 0x2615 /* minimum damper output MSB */ -#define VS6624_NORA_MIN_OUT_LSB 0x2616 /* minimum damper output LSB */ - -#endif |