1cb7a01acSMauro Carvalho Chehab /* 2d8dde6c8SPhilipp Zabel * Driver for MT9V022, MT9V024, MT9V032, and MT9V034 CMOS Image Sensors 3cb7a01acSMauro Carvalho Chehab * 4cb7a01acSMauro Carvalho Chehab * Copyright (C) 2010, Laurent Pinchart <laurent.pinchart@ideasonboard.com> 5cb7a01acSMauro Carvalho Chehab * 6cb7a01acSMauro Carvalho Chehab * Based on the MT9M001 driver, 7cb7a01acSMauro Carvalho Chehab * 8cb7a01acSMauro Carvalho Chehab * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> 9cb7a01acSMauro Carvalho Chehab * 10cb7a01acSMauro Carvalho Chehab * This program is free software; you can redistribute it and/or modify 11cb7a01acSMauro Carvalho Chehab * it under the terms of the GNU General Public License version 2 as 12cb7a01acSMauro Carvalho Chehab * published by the Free Software Foundation. 13cb7a01acSMauro Carvalho Chehab */ 14cb7a01acSMauro Carvalho Chehab 153300a8fdSLaurent Pinchart #include <linux/clk.h> 16cb7a01acSMauro Carvalho Chehab #include <linux/delay.h> 1728d5bdbeSMarkus Pargmann #include <linux/gpio/consumer.h> 18cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h> 19cb7a01acSMauro Carvalho Chehab #include <linux/log2.h> 20cb7a01acSMauro Carvalho Chehab #include <linux/mutex.h> 21f2272e13SLaurent Pinchart #include <linux/of.h> 22859969b3SSakari Ailus #include <linux/of_graph.h> 2380b44ef2SPhilipp Zabel #include <linux/regmap.h> 24cb7a01acSMauro Carvalho Chehab #include <linux/slab.h> 25cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h> 26cb7a01acSMauro Carvalho Chehab #include <linux/v4l2-mediabus.h> 27cb7a01acSMauro Carvalho Chehab #include <linux/module.h> 28cb7a01acSMauro Carvalho Chehab 29b5dcee22SMauro Carvalho Chehab #include <media/i2c/mt9v032.h> 30cb7a01acSMauro Carvalho Chehab #include <media/v4l2-ctrls.h> 31cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h> 32859969b3SSakari Ailus #include <media/v4l2-fwnode.h> 33cb7a01acSMauro Carvalho Chehab #include <media/v4l2-subdev.h> 34cb7a01acSMauro Carvalho Chehab 352b9e9f77SLaurent Pinchart /* The first four rows are black rows. The active area spans 753x481 pixels. */ 362b9e9f77SLaurent Pinchart #define MT9V032_PIXEL_ARRAY_HEIGHT 485 372b9e9f77SLaurent Pinchart #define MT9V032_PIXEL_ARRAY_WIDTH 753 38cb7a01acSMauro Carvalho Chehab 39e9a50e4cSLaurent Pinchart #define MT9V032_SYSCLK_FREQ_DEF 26600000 40e9a50e4cSLaurent Pinchart 41cb7a01acSMauro Carvalho Chehab #define MT9V032_CHIP_VERSION 0x00 42cb7a01acSMauro Carvalho Chehab #define MT9V032_CHIP_ID_REV1 0x1311 43cb7a01acSMauro Carvalho Chehab #define MT9V032_CHIP_ID_REV3 0x1313 44daecfebcSLaurent Pinchart #define MT9V034_CHIP_ID_REV1 0X1324 45cb7a01acSMauro Carvalho Chehab #define MT9V032_COLUMN_START 0x01 46cb7a01acSMauro Carvalho Chehab #define MT9V032_COLUMN_START_MIN 1 47cb7a01acSMauro Carvalho Chehab #define MT9V032_COLUMN_START_DEF 1 48cb7a01acSMauro Carvalho Chehab #define MT9V032_COLUMN_START_MAX 752 49cb7a01acSMauro Carvalho Chehab #define MT9V032_ROW_START 0x02 50cb7a01acSMauro Carvalho Chehab #define MT9V032_ROW_START_MIN 4 51cb7a01acSMauro Carvalho Chehab #define MT9V032_ROW_START_DEF 5 52cb7a01acSMauro Carvalho Chehab #define MT9V032_ROW_START_MAX 482 53cb7a01acSMauro Carvalho Chehab #define MT9V032_WINDOW_HEIGHT 0x03 54cb7a01acSMauro Carvalho Chehab #define MT9V032_WINDOW_HEIGHT_MIN 1 55cb7a01acSMauro Carvalho Chehab #define MT9V032_WINDOW_HEIGHT_DEF 480 56cb7a01acSMauro Carvalho Chehab #define MT9V032_WINDOW_HEIGHT_MAX 480 57cb7a01acSMauro Carvalho Chehab #define MT9V032_WINDOW_WIDTH 0x04 58cb7a01acSMauro Carvalho Chehab #define MT9V032_WINDOW_WIDTH_MIN 1 59cb7a01acSMauro Carvalho Chehab #define MT9V032_WINDOW_WIDTH_DEF 752 60cb7a01acSMauro Carvalho Chehab #define MT9V032_WINDOW_WIDTH_MAX 752 61cb7a01acSMauro Carvalho Chehab #define MT9V032_HORIZONTAL_BLANKING 0x05 62cb7a01acSMauro Carvalho Chehab #define MT9V032_HORIZONTAL_BLANKING_MIN 43 63daecfebcSLaurent Pinchart #define MT9V034_HORIZONTAL_BLANKING_MIN 61 649ec670e2SLaurent Pinchart #define MT9V032_HORIZONTAL_BLANKING_DEF 94 65cb7a01acSMauro Carvalho Chehab #define MT9V032_HORIZONTAL_BLANKING_MAX 1023 66cb7a01acSMauro Carvalho Chehab #define MT9V032_VERTICAL_BLANKING 0x06 67cb7a01acSMauro Carvalho Chehab #define MT9V032_VERTICAL_BLANKING_MIN 4 68daecfebcSLaurent Pinchart #define MT9V034_VERTICAL_BLANKING_MIN 2 699ec670e2SLaurent Pinchart #define MT9V032_VERTICAL_BLANKING_DEF 45 70cb7a01acSMauro Carvalho Chehab #define MT9V032_VERTICAL_BLANKING_MAX 3000 71daecfebcSLaurent Pinchart #define MT9V034_VERTICAL_BLANKING_MAX 32288 72cb7a01acSMauro Carvalho Chehab #define MT9V032_CHIP_CONTROL 0x07 73cb7a01acSMauro Carvalho Chehab #define MT9V032_CHIP_CONTROL_MASTER_MODE (1 << 3) 74cb7a01acSMauro Carvalho Chehab #define MT9V032_CHIP_CONTROL_DOUT_ENABLE (1 << 7) 75cb7a01acSMauro Carvalho Chehab #define MT9V032_CHIP_CONTROL_SEQUENTIAL (1 << 8) 76cb7a01acSMauro Carvalho Chehab #define MT9V032_SHUTTER_WIDTH1 0x08 77cb7a01acSMauro Carvalho Chehab #define MT9V032_SHUTTER_WIDTH2 0x09 78cb7a01acSMauro Carvalho Chehab #define MT9V032_SHUTTER_WIDTH_CONTROL 0x0a 79cb7a01acSMauro Carvalho Chehab #define MT9V032_TOTAL_SHUTTER_WIDTH 0x0b 80cb7a01acSMauro Carvalho Chehab #define MT9V032_TOTAL_SHUTTER_WIDTH_MIN 1 81daecfebcSLaurent Pinchart #define MT9V034_TOTAL_SHUTTER_WIDTH_MIN 0 82cb7a01acSMauro Carvalho Chehab #define MT9V032_TOTAL_SHUTTER_WIDTH_DEF 480 83cb7a01acSMauro Carvalho Chehab #define MT9V032_TOTAL_SHUTTER_WIDTH_MAX 32767 84daecfebcSLaurent Pinchart #define MT9V034_TOTAL_SHUTTER_WIDTH_MAX 32765 85cb7a01acSMauro Carvalho Chehab #define MT9V032_RESET 0x0c 86cb7a01acSMauro Carvalho Chehab #define MT9V032_READ_MODE 0x0d 87cb7a01acSMauro Carvalho Chehab #define MT9V032_READ_MODE_ROW_BIN_MASK (3 << 0) 88cb7a01acSMauro Carvalho Chehab #define MT9V032_READ_MODE_ROW_BIN_SHIFT 0 89cb7a01acSMauro Carvalho Chehab #define MT9V032_READ_MODE_COLUMN_BIN_MASK (3 << 2) 90cb7a01acSMauro Carvalho Chehab #define MT9V032_READ_MODE_COLUMN_BIN_SHIFT 2 91cb7a01acSMauro Carvalho Chehab #define MT9V032_READ_MODE_ROW_FLIP (1 << 4) 92cb7a01acSMauro Carvalho Chehab #define MT9V032_READ_MODE_COLUMN_FLIP (1 << 5) 93cb7a01acSMauro Carvalho Chehab #define MT9V032_READ_MODE_DARK_COLUMNS (1 << 6) 94cb7a01acSMauro Carvalho Chehab #define MT9V032_READ_MODE_DARK_ROWS (1 << 7) 95d131e54bSPhilipp Zabel #define MT9V032_READ_MODE_RESERVED 0x0300 96cb7a01acSMauro Carvalho Chehab #define MT9V032_PIXEL_OPERATION_MODE 0x0f 97daecfebcSLaurent Pinchart #define MT9V034_PIXEL_OPERATION_MODE_HDR (1 << 0) 98daecfebcSLaurent Pinchart #define MT9V034_PIXEL_OPERATION_MODE_COLOR (1 << 1) 99cb7a01acSMauro Carvalho Chehab #define MT9V032_PIXEL_OPERATION_MODE_COLOR (1 << 2) 100cb7a01acSMauro Carvalho Chehab #define MT9V032_PIXEL_OPERATION_MODE_HDR (1 << 6) 101cb7a01acSMauro Carvalho Chehab #define MT9V032_ANALOG_GAIN 0x35 102cb7a01acSMauro Carvalho Chehab #define MT9V032_ANALOG_GAIN_MIN 16 103cb7a01acSMauro Carvalho Chehab #define MT9V032_ANALOG_GAIN_DEF 16 104cb7a01acSMauro Carvalho Chehab #define MT9V032_ANALOG_GAIN_MAX 64 105cb7a01acSMauro Carvalho Chehab #define MT9V032_MAX_ANALOG_GAIN 0x36 106cb7a01acSMauro Carvalho Chehab #define MT9V032_MAX_ANALOG_GAIN_MAX 127 107cb7a01acSMauro Carvalho Chehab #define MT9V032_FRAME_DARK_AVERAGE 0x42 108cb7a01acSMauro Carvalho Chehab #define MT9V032_DARK_AVG_THRESH 0x46 109cb7a01acSMauro Carvalho Chehab #define MT9V032_DARK_AVG_LOW_THRESH_MASK (255 << 0) 110cb7a01acSMauro Carvalho Chehab #define MT9V032_DARK_AVG_LOW_THRESH_SHIFT 0 111cb7a01acSMauro Carvalho Chehab #define MT9V032_DARK_AVG_HIGH_THRESH_MASK (255 << 8) 112cb7a01acSMauro Carvalho Chehab #define MT9V032_DARK_AVG_HIGH_THRESH_SHIFT 8 113cb7a01acSMauro Carvalho Chehab #define MT9V032_ROW_NOISE_CORR_CONTROL 0x70 114daecfebcSLaurent Pinchart #define MT9V034_ROW_NOISE_CORR_ENABLE (1 << 0) 115daecfebcSLaurent Pinchart #define MT9V034_ROW_NOISE_CORR_USE_BLK_AVG (1 << 1) 116cb7a01acSMauro Carvalho Chehab #define MT9V032_ROW_NOISE_CORR_ENABLE (1 << 5) 117cb7a01acSMauro Carvalho Chehab #define MT9V032_ROW_NOISE_CORR_USE_BLK_AVG (1 << 7) 118cb7a01acSMauro Carvalho Chehab #define MT9V032_PIXEL_CLOCK 0x74 119daecfebcSLaurent Pinchart #define MT9V034_PIXEL_CLOCK 0x72 120cb7a01acSMauro Carvalho Chehab #define MT9V032_PIXEL_CLOCK_INV_LINE (1 << 0) 121cb7a01acSMauro Carvalho Chehab #define MT9V032_PIXEL_CLOCK_INV_FRAME (1 << 1) 122cb7a01acSMauro Carvalho Chehab #define MT9V032_PIXEL_CLOCK_XOR_LINE (1 << 2) 123cb7a01acSMauro Carvalho Chehab #define MT9V032_PIXEL_CLOCK_CONT_LINE (1 << 3) 124cb7a01acSMauro Carvalho Chehab #define MT9V032_PIXEL_CLOCK_INV_PXL_CLK (1 << 4) 125cb7a01acSMauro Carvalho Chehab #define MT9V032_TEST_PATTERN 0x7f 126cb7a01acSMauro Carvalho Chehab #define MT9V032_TEST_PATTERN_DATA_MASK (1023 << 0) 127cb7a01acSMauro Carvalho Chehab #define MT9V032_TEST_PATTERN_DATA_SHIFT 0 128cb7a01acSMauro Carvalho Chehab #define MT9V032_TEST_PATTERN_USE_DATA (1 << 10) 129cb7a01acSMauro Carvalho Chehab #define MT9V032_TEST_PATTERN_GRAY_MASK (3 << 11) 130cb7a01acSMauro Carvalho Chehab #define MT9V032_TEST_PATTERN_GRAY_NONE (0 << 11) 131cb7a01acSMauro Carvalho Chehab #define MT9V032_TEST_PATTERN_GRAY_VERTICAL (1 << 11) 132cb7a01acSMauro Carvalho Chehab #define MT9V032_TEST_PATTERN_GRAY_HORIZONTAL (2 << 11) 133cb7a01acSMauro Carvalho Chehab #define MT9V032_TEST_PATTERN_GRAY_DIAGONAL (3 << 11) 134cb7a01acSMauro Carvalho Chehab #define MT9V032_TEST_PATTERN_ENABLE (1 << 13) 135cb7a01acSMauro Carvalho Chehab #define MT9V032_TEST_PATTERN_FLIP (1 << 14) 13681ea4829SMarkus Pargmann #define MT9V032_AEGC_DESIRED_BIN 0xa5 13781ea4829SMarkus Pargmann #define MT9V032_AEC_UPDATE_FREQUENCY 0xa6 13881ea4829SMarkus Pargmann #define MT9V032_AEC_LPF 0xa8 13981ea4829SMarkus Pargmann #define MT9V032_AGC_UPDATE_FREQUENCY 0xa9 14081ea4829SMarkus Pargmann #define MT9V032_AGC_LPF 0xaa 141cb7a01acSMauro Carvalho Chehab #define MT9V032_AEC_AGC_ENABLE 0xaf 142cb7a01acSMauro Carvalho Chehab #define MT9V032_AEC_ENABLE (1 << 0) 143cb7a01acSMauro Carvalho Chehab #define MT9V032_AGC_ENABLE (1 << 1) 14481ea4829SMarkus Pargmann #define MT9V034_AEC_MAX_SHUTTER_WIDTH 0xad 14581ea4829SMarkus Pargmann #define MT9V032_AEC_MAX_SHUTTER_WIDTH 0xbd 146cb7a01acSMauro Carvalho Chehab #define MT9V032_THERMAL_INFO 0xc1 147cb7a01acSMauro Carvalho Chehab 148220ddc7fSLaurent Pinchart enum mt9v032_model { 149d8dde6c8SPhilipp Zabel MT9V032_MODEL_V022_COLOR, /* MT9V022IX7ATC */ 150d8dde6c8SPhilipp Zabel MT9V032_MODEL_V022_MONO, /* MT9V022IX7ATM */ 151d8dde6c8SPhilipp Zabel MT9V032_MODEL_V024_COLOR, /* MT9V024IA7XTC */ 152d8dde6c8SPhilipp Zabel MT9V032_MODEL_V024_MONO, /* MT9V024IA7XTM */ 153d8dde6c8SPhilipp Zabel MT9V032_MODEL_V032_COLOR, /* MT9V032C12STM */ 154d8dde6c8SPhilipp Zabel MT9V032_MODEL_V032_MONO, /* MT9V032C12STC */ 155daecfebcSLaurent Pinchart MT9V032_MODEL_V034_COLOR, 156daecfebcSLaurent Pinchart MT9V032_MODEL_V034_MONO, 157220ddc7fSLaurent Pinchart }; 158220ddc7fSLaurent Pinchart 1590a466b60SLaurent Pinchart struct mt9v032_model_version { 1600a466b60SLaurent Pinchart unsigned int version; 1610a466b60SLaurent Pinchart const char *name; 1620a466b60SLaurent Pinchart }; 1630a466b60SLaurent Pinchart 1640a466b60SLaurent Pinchart struct mt9v032_model_data { 1650a466b60SLaurent Pinchart unsigned int min_row_time; 1660a466b60SLaurent Pinchart unsigned int min_hblank; 1670a466b60SLaurent Pinchart unsigned int min_vblank; 1680a466b60SLaurent Pinchart unsigned int max_vblank; 1690a466b60SLaurent Pinchart unsigned int min_shutter; 1700a466b60SLaurent Pinchart unsigned int max_shutter; 1710a466b60SLaurent Pinchart unsigned int pclk_reg; 17281ea4829SMarkus Pargmann unsigned int aec_max_shutter_reg; 17381ea4829SMarkus Pargmann const struct v4l2_ctrl_config * const aec_max_shutter_v4l2_ctrl; 1740a466b60SLaurent Pinchart }; 1750a466b60SLaurent Pinchart 176220ddc7fSLaurent Pinchart struct mt9v032_model_info { 1770a466b60SLaurent Pinchart const struct mt9v032_model_data *data; 178220ddc7fSLaurent Pinchart bool color; 179220ddc7fSLaurent Pinchart }; 180220ddc7fSLaurent Pinchart 1810a466b60SLaurent Pinchart static const struct mt9v032_model_version mt9v032_versions[] = { 182d8dde6c8SPhilipp Zabel { MT9V032_CHIP_ID_REV1, "MT9V022/MT9V032 rev1/2" }, 183d8dde6c8SPhilipp Zabel { MT9V032_CHIP_ID_REV3, "MT9V022/MT9V032 rev3" }, 184d8dde6c8SPhilipp Zabel { MT9V034_CHIP_ID_REV1, "MT9V024/MT9V034 rev1" }, 1850a466b60SLaurent Pinchart }; 1860a466b60SLaurent Pinchart 187cb7a01acSMauro Carvalho Chehab struct mt9v032 { 188cb7a01acSMauro Carvalho Chehab struct v4l2_subdev subdev; 189cb7a01acSMauro Carvalho Chehab struct media_pad pad; 190cb7a01acSMauro Carvalho Chehab 191cb7a01acSMauro Carvalho Chehab struct v4l2_mbus_framefmt format; 192cb7a01acSMauro Carvalho Chehab struct v4l2_rect crop; 193637f005eSLaurent Pinchart unsigned int hratio; 194637f005eSLaurent Pinchart unsigned int vratio; 195cb7a01acSMauro Carvalho Chehab 196cb7a01acSMauro Carvalho Chehab struct v4l2_ctrl_handler ctrls; 197e9a50e4cSLaurent Pinchart struct { 198e9a50e4cSLaurent Pinchart struct v4l2_ctrl *link_freq; 199e9a50e4cSLaurent Pinchart struct v4l2_ctrl *pixel_rate; 200e9a50e4cSLaurent Pinchart }; 201cb7a01acSMauro Carvalho Chehab 202cb7a01acSMauro Carvalho Chehab struct mutex power_lock; 203cb7a01acSMauro Carvalho Chehab int power_count; 204cb7a01acSMauro Carvalho Chehab 20580b44ef2SPhilipp Zabel struct regmap *regmap; 2063300a8fdSLaurent Pinchart struct clk *clk; 20728d5bdbeSMarkus Pargmann struct gpio_desc *reset_gpio; 20828d5bdbeSMarkus Pargmann struct gpio_desc *standby_gpio; 2093300a8fdSLaurent Pinchart 210cb7a01acSMauro Carvalho Chehab struct mt9v032_platform_data *pdata; 211220ddc7fSLaurent Pinchart const struct mt9v032_model_info *model; 2120a466b60SLaurent Pinchart const struct mt9v032_model_version *version; 213e9a50e4cSLaurent Pinchart 214e9a50e4cSLaurent Pinchart u32 sysclk; 215cb7a01acSMauro Carvalho Chehab u16 aec_agc; 2169ec670e2SLaurent Pinchart u16 hblank; 217b28d7017SLad, Prabhakar struct { 218b28d7017SLad, Prabhakar struct v4l2_ctrl *test_pattern; 219b28d7017SLad, Prabhakar struct v4l2_ctrl *test_pattern_color; 220b28d7017SLad, Prabhakar }; 221cb7a01acSMauro Carvalho Chehab }; 222cb7a01acSMauro Carvalho Chehab 223cb7a01acSMauro Carvalho Chehab static struct mt9v032 *to_mt9v032(struct v4l2_subdev *sd) 224cb7a01acSMauro Carvalho Chehab { 225cb7a01acSMauro Carvalho Chehab return container_of(sd, struct mt9v032, subdev); 226cb7a01acSMauro Carvalho Chehab } 227cb7a01acSMauro Carvalho Chehab 228cb7a01acSMauro Carvalho Chehab static int 229cb7a01acSMauro Carvalho Chehab mt9v032_update_aec_agc(struct mt9v032 *mt9v032, u16 which, int enable) 230cb7a01acSMauro Carvalho Chehab { 23180b44ef2SPhilipp Zabel struct regmap *map = mt9v032->regmap; 232cb7a01acSMauro Carvalho Chehab u16 value = mt9v032->aec_agc; 233cb7a01acSMauro Carvalho Chehab int ret; 234cb7a01acSMauro Carvalho Chehab 235cb7a01acSMauro Carvalho Chehab if (enable) 236cb7a01acSMauro Carvalho Chehab value |= which; 237cb7a01acSMauro Carvalho Chehab else 238cb7a01acSMauro Carvalho Chehab value &= ~which; 239cb7a01acSMauro Carvalho Chehab 24080b44ef2SPhilipp Zabel ret = regmap_write(map, MT9V032_AEC_AGC_ENABLE, value); 241cb7a01acSMauro Carvalho Chehab if (ret < 0) 242cb7a01acSMauro Carvalho Chehab return ret; 243cb7a01acSMauro Carvalho Chehab 244cb7a01acSMauro Carvalho Chehab mt9v032->aec_agc = value; 245cb7a01acSMauro Carvalho Chehab return 0; 246cb7a01acSMauro Carvalho Chehab } 247cb7a01acSMauro Carvalho Chehab 2489ec670e2SLaurent Pinchart static int 2499ec670e2SLaurent Pinchart mt9v032_update_hblank(struct mt9v032 *mt9v032) 2509ec670e2SLaurent Pinchart { 2519ec670e2SLaurent Pinchart struct v4l2_rect *crop = &mt9v032->crop; 252daecfebcSLaurent Pinchart unsigned int min_hblank = mt9v032->model->data->min_hblank; 2530a466b60SLaurent Pinchart unsigned int hblank; 2549ec670e2SLaurent Pinchart 255daecfebcSLaurent Pinchart if (mt9v032->version->version == MT9V034_CHIP_ID_REV1) 256daecfebcSLaurent Pinchart min_hblank += (mt9v032->hratio - 1) * 10; 257f17bc3f4SPhilipp Zabel min_hblank = max_t(int, mt9v032->model->data->min_row_time - crop->width, 258f17bc3f4SPhilipp Zabel min_hblank); 259daecfebcSLaurent Pinchart hblank = max_t(unsigned int, mt9v032->hblank, min_hblank); 260daecfebcSLaurent Pinchart 26180b44ef2SPhilipp Zabel return regmap_write(mt9v032->regmap, MT9V032_HORIZONTAL_BLANKING, 26280b44ef2SPhilipp Zabel hblank); 2639ec670e2SLaurent Pinchart } 2649ec670e2SLaurent Pinchart 265cb7a01acSMauro Carvalho Chehab static int mt9v032_power_on(struct mt9v032 *mt9v032) 266cb7a01acSMauro Carvalho Chehab { 26780b44ef2SPhilipp Zabel struct regmap *map = mt9v032->regmap; 268cb7a01acSMauro Carvalho Chehab int ret; 269cb7a01acSMauro Carvalho Chehab 27028d5bdbeSMarkus Pargmann gpiod_set_value_cansleep(mt9v032->reset_gpio, 1); 27128d5bdbeSMarkus Pargmann 27279019190SLad, Prabhakar ret = clk_set_rate(mt9v032->clk, mt9v032->sysclk); 27379019190SLad, Prabhakar if (ret < 0) 27479019190SLad, Prabhakar return ret; 27579019190SLad, Prabhakar 27628d5bdbeSMarkus Pargmann /* System clock has to be enabled before releasing the reset */ 27779019190SLad, Prabhakar ret = clk_prepare_enable(mt9v032->clk); 27879019190SLad, Prabhakar if (ret) 27979019190SLad, Prabhakar return ret; 28079019190SLad, Prabhakar 281cb7a01acSMauro Carvalho Chehab udelay(1); 282cb7a01acSMauro Carvalho Chehab 28328d5bdbeSMarkus Pargmann if (mt9v032->reset_gpio) { 28428d5bdbeSMarkus Pargmann gpiod_set_value_cansleep(mt9v032->reset_gpio, 0); 28528d5bdbeSMarkus Pargmann 28628d5bdbeSMarkus Pargmann /* After releasing reset we need to wait 10 clock cycles 28728d5bdbeSMarkus Pargmann * before accessing the sensor over I2C. As the minimum SYSCLK 28828d5bdbeSMarkus Pargmann * frequency is 13MHz, waiting 1µs will be enough in the worst 28928d5bdbeSMarkus Pargmann * case. 29028d5bdbeSMarkus Pargmann */ 29128d5bdbeSMarkus Pargmann udelay(1); 29228d5bdbeSMarkus Pargmann } 29328d5bdbeSMarkus Pargmann 294cb7a01acSMauro Carvalho Chehab /* Reset the chip and stop data read out */ 29580b44ef2SPhilipp Zabel ret = regmap_write(map, MT9V032_RESET, 1); 296cb7a01acSMauro Carvalho Chehab if (ret < 0) 297cb7a01acSMauro Carvalho Chehab return ret; 298cb7a01acSMauro Carvalho Chehab 29980b44ef2SPhilipp Zabel ret = regmap_write(map, MT9V032_RESET, 0); 300cb7a01acSMauro Carvalho Chehab if (ret < 0) 301cb7a01acSMauro Carvalho Chehab return ret; 302cb7a01acSMauro Carvalho Chehab 30378060d51SMarkus Pargmann return regmap_write(map, MT9V032_CHIP_CONTROL, 30478060d51SMarkus Pargmann MT9V032_CHIP_CONTROL_MASTER_MODE); 305cb7a01acSMauro Carvalho Chehab } 306cb7a01acSMauro Carvalho Chehab 307cb7a01acSMauro Carvalho Chehab static void mt9v032_power_off(struct mt9v032 *mt9v032) 308cb7a01acSMauro Carvalho Chehab { 3093300a8fdSLaurent Pinchart clk_disable_unprepare(mt9v032->clk); 310cb7a01acSMauro Carvalho Chehab } 311cb7a01acSMauro Carvalho Chehab 312cb7a01acSMauro Carvalho Chehab static int __mt9v032_set_power(struct mt9v032 *mt9v032, bool on) 313cb7a01acSMauro Carvalho Chehab { 31480b44ef2SPhilipp Zabel struct regmap *map = mt9v032->regmap; 315cb7a01acSMauro Carvalho Chehab int ret; 316cb7a01acSMauro Carvalho Chehab 317cb7a01acSMauro Carvalho Chehab if (!on) { 318cb7a01acSMauro Carvalho Chehab mt9v032_power_off(mt9v032); 319cb7a01acSMauro Carvalho Chehab return 0; 320cb7a01acSMauro Carvalho Chehab } 321cb7a01acSMauro Carvalho Chehab 322cb7a01acSMauro Carvalho Chehab ret = mt9v032_power_on(mt9v032); 323cb7a01acSMauro Carvalho Chehab if (ret < 0) 324cb7a01acSMauro Carvalho Chehab return ret; 325cb7a01acSMauro Carvalho Chehab 326cb7a01acSMauro Carvalho Chehab /* Configure the pixel clock polarity */ 327cb7a01acSMauro Carvalho Chehab if (mt9v032->pdata && mt9v032->pdata->clk_pol) { 32880b44ef2SPhilipp Zabel ret = regmap_write(map, mt9v032->model->data->pclk_reg, 329cb7a01acSMauro Carvalho Chehab MT9V032_PIXEL_CLOCK_INV_PXL_CLK); 330cb7a01acSMauro Carvalho Chehab if (ret < 0) 331cb7a01acSMauro Carvalho Chehab return ret; 332cb7a01acSMauro Carvalho Chehab } 333cb7a01acSMauro Carvalho Chehab 334cb7a01acSMauro Carvalho Chehab /* Disable the noise correction algorithm and restore the controls. */ 33580b44ef2SPhilipp Zabel ret = regmap_write(map, MT9V032_ROW_NOISE_CORR_CONTROL, 0); 336cb7a01acSMauro Carvalho Chehab if (ret < 0) 337cb7a01acSMauro Carvalho Chehab return ret; 338cb7a01acSMauro Carvalho Chehab 339cb7a01acSMauro Carvalho Chehab return v4l2_ctrl_handler_setup(&mt9v032->ctrls); 340cb7a01acSMauro Carvalho Chehab } 341cb7a01acSMauro Carvalho Chehab 342cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------------- 343cb7a01acSMauro Carvalho Chehab * V4L2 subdev video operations 344cb7a01acSMauro Carvalho Chehab */ 345cb7a01acSMauro Carvalho Chehab 346cb7a01acSMauro Carvalho Chehab static struct v4l2_mbus_framefmt * 347f7234138SHans Verkuil __mt9v032_get_pad_format(struct mt9v032 *mt9v032, struct v4l2_subdev_pad_config *cfg, 348cb7a01acSMauro Carvalho Chehab unsigned int pad, enum v4l2_subdev_format_whence which) 349cb7a01acSMauro Carvalho Chehab { 350cb7a01acSMauro Carvalho Chehab switch (which) { 351cb7a01acSMauro Carvalho Chehab case V4L2_SUBDEV_FORMAT_TRY: 352f7234138SHans Verkuil return v4l2_subdev_get_try_format(&mt9v032->subdev, cfg, pad); 353cb7a01acSMauro Carvalho Chehab case V4L2_SUBDEV_FORMAT_ACTIVE: 354cb7a01acSMauro Carvalho Chehab return &mt9v032->format; 355cb7a01acSMauro Carvalho Chehab default: 356cb7a01acSMauro Carvalho Chehab return NULL; 357cb7a01acSMauro Carvalho Chehab } 358cb7a01acSMauro Carvalho Chehab } 359cb7a01acSMauro Carvalho Chehab 360cb7a01acSMauro Carvalho Chehab static struct v4l2_rect * 361f7234138SHans Verkuil __mt9v032_get_pad_crop(struct mt9v032 *mt9v032, struct v4l2_subdev_pad_config *cfg, 362cb7a01acSMauro Carvalho Chehab unsigned int pad, enum v4l2_subdev_format_whence which) 363cb7a01acSMauro Carvalho Chehab { 364cb7a01acSMauro Carvalho Chehab switch (which) { 365cb7a01acSMauro Carvalho Chehab case V4L2_SUBDEV_FORMAT_TRY: 366f7234138SHans Verkuil return v4l2_subdev_get_try_crop(&mt9v032->subdev, cfg, pad); 367cb7a01acSMauro Carvalho Chehab case V4L2_SUBDEV_FORMAT_ACTIVE: 368cb7a01acSMauro Carvalho Chehab return &mt9v032->crop; 369cb7a01acSMauro Carvalho Chehab default: 370cb7a01acSMauro Carvalho Chehab return NULL; 371cb7a01acSMauro Carvalho Chehab } 372cb7a01acSMauro Carvalho Chehab } 373cb7a01acSMauro Carvalho Chehab 374cb7a01acSMauro Carvalho Chehab static int mt9v032_s_stream(struct v4l2_subdev *subdev, int enable) 375cb7a01acSMauro Carvalho Chehab { 37678060d51SMarkus Pargmann const u16 mode = MT9V032_CHIP_CONTROL_DOUT_ENABLE 377cb7a01acSMauro Carvalho Chehab | MT9V032_CHIP_CONTROL_SEQUENTIAL; 378cb7a01acSMauro Carvalho Chehab struct mt9v032 *mt9v032 = to_mt9v032(subdev); 379cb7a01acSMauro Carvalho Chehab struct v4l2_rect *crop = &mt9v032->crop; 38080b44ef2SPhilipp Zabel struct regmap *map = mt9v032->regmap; 381637f005eSLaurent Pinchart unsigned int hbin; 382637f005eSLaurent Pinchart unsigned int vbin; 383cb7a01acSMauro Carvalho Chehab int ret; 384cb7a01acSMauro Carvalho Chehab 385cb7a01acSMauro Carvalho Chehab if (!enable) 38680b44ef2SPhilipp Zabel return regmap_update_bits(map, MT9V032_CHIP_CONTROL, mode, 0); 387cb7a01acSMauro Carvalho Chehab 388cb7a01acSMauro Carvalho Chehab /* Configure the window size and row/column bin */ 389637f005eSLaurent Pinchart hbin = fls(mt9v032->hratio) - 1; 390637f005eSLaurent Pinchart vbin = fls(mt9v032->vratio) - 1; 39180b44ef2SPhilipp Zabel ret = regmap_update_bits(map, MT9V032_READ_MODE, 39280b44ef2SPhilipp Zabel ~MT9V032_READ_MODE_RESERVED, 39380b44ef2SPhilipp Zabel hbin << MT9V032_READ_MODE_COLUMN_BIN_SHIFT | 39480b44ef2SPhilipp Zabel vbin << MT9V032_READ_MODE_ROW_BIN_SHIFT); 395cb7a01acSMauro Carvalho Chehab if (ret < 0) 396cb7a01acSMauro Carvalho Chehab return ret; 397cb7a01acSMauro Carvalho Chehab 39880b44ef2SPhilipp Zabel ret = regmap_write(map, MT9V032_COLUMN_START, crop->left); 399cb7a01acSMauro Carvalho Chehab if (ret < 0) 400cb7a01acSMauro Carvalho Chehab return ret; 401cb7a01acSMauro Carvalho Chehab 40280b44ef2SPhilipp Zabel ret = regmap_write(map, MT9V032_ROW_START, crop->top); 403cb7a01acSMauro Carvalho Chehab if (ret < 0) 404cb7a01acSMauro Carvalho Chehab return ret; 405cb7a01acSMauro Carvalho Chehab 40680b44ef2SPhilipp Zabel ret = regmap_write(map, MT9V032_WINDOW_WIDTH, crop->width); 407cb7a01acSMauro Carvalho Chehab if (ret < 0) 408cb7a01acSMauro Carvalho Chehab return ret; 409cb7a01acSMauro Carvalho Chehab 41080b44ef2SPhilipp Zabel ret = regmap_write(map, MT9V032_WINDOW_HEIGHT, crop->height); 411cb7a01acSMauro Carvalho Chehab if (ret < 0) 412cb7a01acSMauro Carvalho Chehab return ret; 413cb7a01acSMauro Carvalho Chehab 4149ec670e2SLaurent Pinchart ret = mt9v032_update_hblank(mt9v032); 415cb7a01acSMauro Carvalho Chehab if (ret < 0) 416cb7a01acSMauro Carvalho Chehab return ret; 417cb7a01acSMauro Carvalho Chehab 418cb7a01acSMauro Carvalho Chehab /* Switch to master "normal" mode */ 41980b44ef2SPhilipp Zabel return regmap_update_bits(map, MT9V032_CHIP_CONTROL, mode, mode); 420cb7a01acSMauro Carvalho Chehab } 421cb7a01acSMauro Carvalho Chehab 422cb7a01acSMauro Carvalho Chehab static int mt9v032_enum_mbus_code(struct v4l2_subdev *subdev, 423f7234138SHans Verkuil struct v4l2_subdev_pad_config *cfg, 424cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_mbus_code_enum *code) 425cb7a01acSMauro Carvalho Chehab { 426cb7a01acSMauro Carvalho Chehab if (code->index > 0) 427cb7a01acSMauro Carvalho Chehab return -EINVAL; 428cb7a01acSMauro Carvalho Chehab 429f5fe58fdSBoris BREZILLON code->code = MEDIA_BUS_FMT_SGRBG10_1X10; 430cb7a01acSMauro Carvalho Chehab return 0; 431cb7a01acSMauro Carvalho Chehab } 432cb7a01acSMauro Carvalho Chehab 433cb7a01acSMauro Carvalho Chehab static int mt9v032_enum_frame_size(struct v4l2_subdev *subdev, 434f7234138SHans Verkuil struct v4l2_subdev_pad_config *cfg, 435cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_frame_size_enum *fse) 436cb7a01acSMauro Carvalho Chehab { 437f5fe58fdSBoris BREZILLON if (fse->index >= 3 || fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) 438cb7a01acSMauro Carvalho Chehab return -EINVAL; 439cb7a01acSMauro Carvalho Chehab 440637f005eSLaurent Pinchart fse->min_width = MT9V032_WINDOW_WIDTH_DEF / (1 << fse->index); 441cb7a01acSMauro Carvalho Chehab fse->max_width = fse->min_width; 442637f005eSLaurent Pinchart fse->min_height = MT9V032_WINDOW_HEIGHT_DEF / (1 << fse->index); 443cb7a01acSMauro Carvalho Chehab fse->max_height = fse->min_height; 444cb7a01acSMauro Carvalho Chehab 445cb7a01acSMauro Carvalho Chehab return 0; 446cb7a01acSMauro Carvalho Chehab } 447cb7a01acSMauro Carvalho Chehab 448cb7a01acSMauro Carvalho Chehab static int mt9v032_get_format(struct v4l2_subdev *subdev, 449f7234138SHans Verkuil struct v4l2_subdev_pad_config *cfg, 450cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_format *format) 451cb7a01acSMauro Carvalho Chehab { 452cb7a01acSMauro Carvalho Chehab struct mt9v032 *mt9v032 = to_mt9v032(subdev); 453cb7a01acSMauro Carvalho Chehab 454f7234138SHans Verkuil format->format = *__mt9v032_get_pad_format(mt9v032, cfg, format->pad, 455cb7a01acSMauro Carvalho Chehab format->which); 456cb7a01acSMauro Carvalho Chehab return 0; 457cb7a01acSMauro Carvalho Chehab } 458cb7a01acSMauro Carvalho Chehab 459637f005eSLaurent Pinchart static void mt9v032_configure_pixel_rate(struct mt9v032 *mt9v032) 46041a33a00SSakari Ailus { 46141a33a00SSakari Ailus struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); 46241a33a00SSakari Ailus int ret; 46341a33a00SSakari Ailus 464e9a50e4cSLaurent Pinchart ret = v4l2_ctrl_s_ctrl_int64(mt9v032->pixel_rate, 465637f005eSLaurent Pinchart mt9v032->sysclk / mt9v032->hratio); 46641a33a00SSakari Ailus if (ret < 0) 46741a33a00SSakari Ailus dev_warn(&client->dev, "failed to set pixel rate (%d)\n", ret); 46841a33a00SSakari Ailus } 46941a33a00SSakari Ailus 470637f005eSLaurent Pinchart static unsigned int mt9v032_calc_ratio(unsigned int input, unsigned int output) 471637f005eSLaurent Pinchart { 472637f005eSLaurent Pinchart /* Compute the power-of-two binning factor closest to the input size to 473637f005eSLaurent Pinchart * output size ratio. Given that the output size is bounded by input/4 474637f005eSLaurent Pinchart * and input, a generic implementation would be an ineffective luxury. 475637f005eSLaurent Pinchart */ 476637f005eSLaurent Pinchart if (output * 3 > input * 2) 477637f005eSLaurent Pinchart return 1; 478637f005eSLaurent Pinchart if (output * 3 > input) 479637f005eSLaurent Pinchart return 2; 480637f005eSLaurent Pinchart return 4; 481637f005eSLaurent Pinchart } 482637f005eSLaurent Pinchart 483cb7a01acSMauro Carvalho Chehab static int mt9v032_set_format(struct v4l2_subdev *subdev, 484f7234138SHans Verkuil struct v4l2_subdev_pad_config *cfg, 485cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_format *format) 486cb7a01acSMauro Carvalho Chehab { 487cb7a01acSMauro Carvalho Chehab struct mt9v032 *mt9v032 = to_mt9v032(subdev); 488cb7a01acSMauro Carvalho Chehab struct v4l2_mbus_framefmt *__format; 489cb7a01acSMauro Carvalho Chehab struct v4l2_rect *__crop; 490cb7a01acSMauro Carvalho Chehab unsigned int width; 491cb7a01acSMauro Carvalho Chehab unsigned int height; 492cb7a01acSMauro Carvalho Chehab unsigned int hratio; 493cb7a01acSMauro Carvalho Chehab unsigned int vratio; 494cb7a01acSMauro Carvalho Chehab 495f7234138SHans Verkuil __crop = __mt9v032_get_pad_crop(mt9v032, cfg, format->pad, 496cb7a01acSMauro Carvalho Chehab format->which); 497cb7a01acSMauro Carvalho Chehab 498cb7a01acSMauro Carvalho Chehab /* Clamp the width and height to avoid dividing by zero. */ 499f90580caSRicardo Ribalda width = clamp(ALIGN(format->format.width, 2), 500f90580caSRicardo Ribalda max_t(unsigned int, __crop->width / 4, 501f90580caSRicardo Ribalda MT9V032_WINDOW_WIDTH_MIN), 502cb7a01acSMauro Carvalho Chehab __crop->width); 503f90580caSRicardo Ribalda height = clamp(ALIGN(format->format.height, 2), 504f90580caSRicardo Ribalda max_t(unsigned int, __crop->height / 4, 505f90580caSRicardo Ribalda MT9V032_WINDOW_HEIGHT_MIN), 506cb7a01acSMauro Carvalho Chehab __crop->height); 507cb7a01acSMauro Carvalho Chehab 508637f005eSLaurent Pinchart hratio = mt9v032_calc_ratio(__crop->width, width); 509637f005eSLaurent Pinchart vratio = mt9v032_calc_ratio(__crop->height, height); 510cb7a01acSMauro Carvalho Chehab 511f7234138SHans Verkuil __format = __mt9v032_get_pad_format(mt9v032, cfg, format->pad, 512cb7a01acSMauro Carvalho Chehab format->which); 513cb7a01acSMauro Carvalho Chehab __format->width = __crop->width / hratio; 514cb7a01acSMauro Carvalho Chehab __format->height = __crop->height / vratio; 515637f005eSLaurent Pinchart 516637f005eSLaurent Pinchart if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { 517637f005eSLaurent Pinchart mt9v032->hratio = hratio; 518637f005eSLaurent Pinchart mt9v032->vratio = vratio; 519637f005eSLaurent Pinchart mt9v032_configure_pixel_rate(mt9v032); 520637f005eSLaurent Pinchart } 521cb7a01acSMauro Carvalho Chehab 522cb7a01acSMauro Carvalho Chehab format->format = *__format; 523cb7a01acSMauro Carvalho Chehab 524cb7a01acSMauro Carvalho Chehab return 0; 525cb7a01acSMauro Carvalho Chehab } 526cb7a01acSMauro Carvalho Chehab 5271a023febSHans Verkuil static int mt9v032_get_selection(struct v4l2_subdev *subdev, 528f7234138SHans Verkuil struct v4l2_subdev_pad_config *cfg, 5291a023febSHans Verkuil struct v4l2_subdev_selection *sel) 530cb7a01acSMauro Carvalho Chehab { 531cb7a01acSMauro Carvalho Chehab struct mt9v032 *mt9v032 = to_mt9v032(subdev); 532cb7a01acSMauro Carvalho Chehab 5331a023febSHans Verkuil if (sel->target != V4L2_SEL_TGT_CROP) 5341a023febSHans Verkuil return -EINVAL; 5351a023febSHans Verkuil 536f7234138SHans Verkuil sel->r = *__mt9v032_get_pad_crop(mt9v032, cfg, sel->pad, sel->which); 537cb7a01acSMauro Carvalho Chehab return 0; 538cb7a01acSMauro Carvalho Chehab } 539cb7a01acSMauro Carvalho Chehab 5401a023febSHans Verkuil static int mt9v032_set_selection(struct v4l2_subdev *subdev, 541f7234138SHans Verkuil struct v4l2_subdev_pad_config *cfg, 5421a023febSHans Verkuil struct v4l2_subdev_selection *sel) 543cb7a01acSMauro Carvalho Chehab { 544cb7a01acSMauro Carvalho Chehab struct mt9v032 *mt9v032 = to_mt9v032(subdev); 545cb7a01acSMauro Carvalho Chehab struct v4l2_mbus_framefmt *__format; 546cb7a01acSMauro Carvalho Chehab struct v4l2_rect *__crop; 547cb7a01acSMauro Carvalho Chehab struct v4l2_rect rect; 548cb7a01acSMauro Carvalho Chehab 5491a023febSHans Verkuil if (sel->target != V4L2_SEL_TGT_CROP) 5501a023febSHans Verkuil return -EINVAL; 5511a023febSHans Verkuil 552cb7a01acSMauro Carvalho Chehab /* Clamp the crop rectangle boundaries and align them to a non multiple 553cb7a01acSMauro Carvalho Chehab * of 2 pixels to ensure a GRBG Bayer pattern. 554cb7a01acSMauro Carvalho Chehab */ 5551a023febSHans Verkuil rect.left = clamp(ALIGN(sel->r.left + 1, 2) - 1, 556cb7a01acSMauro Carvalho Chehab MT9V032_COLUMN_START_MIN, 557cb7a01acSMauro Carvalho Chehab MT9V032_COLUMN_START_MAX); 5581a023febSHans Verkuil rect.top = clamp(ALIGN(sel->r.top + 1, 2) - 1, 559cb7a01acSMauro Carvalho Chehab MT9V032_ROW_START_MIN, 560cb7a01acSMauro Carvalho Chehab MT9V032_ROW_START_MAX); 5611a023febSHans Verkuil rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 2), 562cb7a01acSMauro Carvalho Chehab MT9V032_WINDOW_WIDTH_MIN, 563cb7a01acSMauro Carvalho Chehab MT9V032_WINDOW_WIDTH_MAX); 5641a023febSHans Verkuil rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2), 565cb7a01acSMauro Carvalho Chehab MT9V032_WINDOW_HEIGHT_MIN, 566cb7a01acSMauro Carvalho Chehab MT9V032_WINDOW_HEIGHT_MAX); 567cb7a01acSMauro Carvalho Chehab 568f90580caSRicardo Ribalda rect.width = min_t(unsigned int, 569f90580caSRicardo Ribalda rect.width, MT9V032_PIXEL_ARRAY_WIDTH - rect.left); 570f90580caSRicardo Ribalda rect.height = min_t(unsigned int, 571f90580caSRicardo Ribalda rect.height, MT9V032_PIXEL_ARRAY_HEIGHT - rect.top); 572cb7a01acSMauro Carvalho Chehab 573f7234138SHans Verkuil __crop = __mt9v032_get_pad_crop(mt9v032, cfg, sel->pad, sel->which); 574cb7a01acSMauro Carvalho Chehab 575cb7a01acSMauro Carvalho Chehab if (rect.width != __crop->width || rect.height != __crop->height) { 576cb7a01acSMauro Carvalho Chehab /* Reset the output image size if the crop rectangle size has 577cb7a01acSMauro Carvalho Chehab * been modified. 578cb7a01acSMauro Carvalho Chehab */ 579f7234138SHans Verkuil __format = __mt9v032_get_pad_format(mt9v032, cfg, sel->pad, 5801a023febSHans Verkuil sel->which); 581cb7a01acSMauro Carvalho Chehab __format->width = rect.width; 582cb7a01acSMauro Carvalho Chehab __format->height = rect.height; 5831a023febSHans Verkuil if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { 584637f005eSLaurent Pinchart mt9v032->hratio = 1; 585637f005eSLaurent Pinchart mt9v032->vratio = 1; 586637f005eSLaurent Pinchart mt9v032_configure_pixel_rate(mt9v032); 587637f005eSLaurent Pinchart } 588cb7a01acSMauro Carvalho Chehab } 589cb7a01acSMauro Carvalho Chehab 590cb7a01acSMauro Carvalho Chehab *__crop = rect; 5911a023febSHans Verkuil sel->r = rect; 592cb7a01acSMauro Carvalho Chehab 593cb7a01acSMauro Carvalho Chehab return 0; 594cb7a01acSMauro Carvalho Chehab } 595cb7a01acSMauro Carvalho Chehab 596cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------------- 597cb7a01acSMauro Carvalho Chehab * V4L2 subdev control operations 598cb7a01acSMauro Carvalho Chehab */ 599cb7a01acSMauro Carvalho Chehab 600b28d7017SLad, Prabhakar #define V4L2_CID_TEST_PATTERN_COLOR (V4L2_CID_USER_BASE | 0x1001) 60181ea4829SMarkus Pargmann /* 60281ea4829SMarkus Pargmann * Value between 1 and 64 to set the desired bin. This is effectively a measure 60381ea4829SMarkus Pargmann * of how bright the image is supposed to be. Both AGC and AEC try to reach 60481ea4829SMarkus Pargmann * this. 60581ea4829SMarkus Pargmann */ 60681ea4829SMarkus Pargmann #define V4L2_CID_AEGC_DESIRED_BIN (V4L2_CID_USER_BASE | 0x1002) 60781ea4829SMarkus Pargmann /* 60881ea4829SMarkus Pargmann * LPF is the low pass filter capability of the chip. Both AEC and AGC have 60981ea4829SMarkus Pargmann * this setting. This limits the speed in which AGC/AEC adjust their settings. 61081ea4829SMarkus Pargmann * Possible values are 0-2. 0 means no LPF. For 1 and 2 this equation is used: 61181ea4829SMarkus Pargmann * 61281ea4829SMarkus Pargmann * if |(calculated new exp - current exp)| > (current exp / 4) 61381ea4829SMarkus Pargmann * next exp = calculated new exp 61481ea4829SMarkus Pargmann * else 61581ea4829SMarkus Pargmann * next exp = current exp + ((calculated new exp - current exp) / 2^LPF) 61681ea4829SMarkus Pargmann */ 61781ea4829SMarkus Pargmann #define V4L2_CID_AEC_LPF (V4L2_CID_USER_BASE | 0x1003) 61881ea4829SMarkus Pargmann #define V4L2_CID_AGC_LPF (V4L2_CID_USER_BASE | 0x1004) 61981ea4829SMarkus Pargmann /* 62081ea4829SMarkus Pargmann * Value between 0 and 15. This is the number of frames being skipped before 62181ea4829SMarkus Pargmann * updating the auto exposure/gain. 62281ea4829SMarkus Pargmann */ 62381ea4829SMarkus Pargmann #define V4L2_CID_AEC_UPDATE_INTERVAL (V4L2_CID_USER_BASE | 0x1005) 62481ea4829SMarkus Pargmann #define V4L2_CID_AGC_UPDATE_INTERVAL (V4L2_CID_USER_BASE | 0x1006) 62581ea4829SMarkus Pargmann /* 62681ea4829SMarkus Pargmann * Maximum shutter width used for AEC. 62781ea4829SMarkus Pargmann */ 62881ea4829SMarkus Pargmann #define V4L2_CID_AEC_MAX_SHUTTER_WIDTH (V4L2_CID_USER_BASE | 0x1007) 629cb7a01acSMauro Carvalho Chehab 630cb7a01acSMauro Carvalho Chehab static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl) 631cb7a01acSMauro Carvalho Chehab { 632cb7a01acSMauro Carvalho Chehab struct mt9v032 *mt9v032 = 633cb7a01acSMauro Carvalho Chehab container_of(ctrl->handler, struct mt9v032, ctrls); 63480b44ef2SPhilipp Zabel struct regmap *map = mt9v032->regmap; 635e9a50e4cSLaurent Pinchart u32 freq; 636cb7a01acSMauro Carvalho Chehab u16 data; 637cb7a01acSMauro Carvalho Chehab 638cb7a01acSMauro Carvalho Chehab switch (ctrl->id) { 639cb7a01acSMauro Carvalho Chehab case V4L2_CID_AUTOGAIN: 640cb7a01acSMauro Carvalho Chehab return mt9v032_update_aec_agc(mt9v032, MT9V032_AGC_ENABLE, 641cb7a01acSMauro Carvalho Chehab ctrl->val); 642cb7a01acSMauro Carvalho Chehab 643cb7a01acSMauro Carvalho Chehab case V4L2_CID_GAIN: 64480b44ef2SPhilipp Zabel return regmap_write(map, MT9V032_ANALOG_GAIN, ctrl->val); 645cb7a01acSMauro Carvalho Chehab 646cb7a01acSMauro Carvalho Chehab case V4L2_CID_EXPOSURE_AUTO: 647cb7a01acSMauro Carvalho Chehab return mt9v032_update_aec_agc(mt9v032, MT9V032_AEC_ENABLE, 648cb7a01acSMauro Carvalho Chehab !ctrl->val); 649cb7a01acSMauro Carvalho Chehab 650cb7a01acSMauro Carvalho Chehab case V4L2_CID_EXPOSURE: 65180b44ef2SPhilipp Zabel return regmap_write(map, MT9V032_TOTAL_SHUTTER_WIDTH, 652cb7a01acSMauro Carvalho Chehab ctrl->val); 653cb7a01acSMauro Carvalho Chehab 6549ec670e2SLaurent Pinchart case V4L2_CID_HBLANK: 6559ec670e2SLaurent Pinchart mt9v032->hblank = ctrl->val; 6569ec670e2SLaurent Pinchart return mt9v032_update_hblank(mt9v032); 6579ec670e2SLaurent Pinchart 6589ec670e2SLaurent Pinchart case V4L2_CID_VBLANK: 65980b44ef2SPhilipp Zabel return regmap_write(map, MT9V032_VERTICAL_BLANKING, 6609ec670e2SLaurent Pinchart ctrl->val); 6619ec670e2SLaurent Pinchart 662e9a50e4cSLaurent Pinchart case V4L2_CID_PIXEL_RATE: 663e9a50e4cSLaurent Pinchart case V4L2_CID_LINK_FREQ: 664e9a50e4cSLaurent Pinchart if (mt9v032->link_freq == NULL) 665e9a50e4cSLaurent Pinchart break; 666e9a50e4cSLaurent Pinchart 667e9a50e4cSLaurent Pinchart freq = mt9v032->pdata->link_freqs[mt9v032->link_freq->val]; 6682a9ec373SHans Verkuil *mt9v032->pixel_rate->p_new.p_s64 = freq; 669e9a50e4cSLaurent Pinchart mt9v032->sysclk = freq; 670e9a50e4cSLaurent Pinchart break; 671e9a50e4cSLaurent Pinchart 672cb7a01acSMauro Carvalho Chehab case V4L2_CID_TEST_PATTERN: 673b28d7017SLad, Prabhakar switch (mt9v032->test_pattern->val) { 674cb7a01acSMauro Carvalho Chehab case 0: 675cb7a01acSMauro Carvalho Chehab data = 0; 676cb7a01acSMauro Carvalho Chehab break; 677cb7a01acSMauro Carvalho Chehab case 1: 678cb7a01acSMauro Carvalho Chehab data = MT9V032_TEST_PATTERN_GRAY_VERTICAL 679cb7a01acSMauro Carvalho Chehab | MT9V032_TEST_PATTERN_ENABLE; 680cb7a01acSMauro Carvalho Chehab break; 681cb7a01acSMauro Carvalho Chehab case 2: 682cb7a01acSMauro Carvalho Chehab data = MT9V032_TEST_PATTERN_GRAY_HORIZONTAL 683cb7a01acSMauro Carvalho Chehab | MT9V032_TEST_PATTERN_ENABLE; 684cb7a01acSMauro Carvalho Chehab break; 685cb7a01acSMauro Carvalho Chehab case 3: 686cb7a01acSMauro Carvalho Chehab data = MT9V032_TEST_PATTERN_GRAY_DIAGONAL 687cb7a01acSMauro Carvalho Chehab | MT9V032_TEST_PATTERN_ENABLE; 688cb7a01acSMauro Carvalho Chehab break; 689cb7a01acSMauro Carvalho Chehab default: 690b28d7017SLad, Prabhakar data = (mt9v032->test_pattern_color->val << 691b28d7017SLad, Prabhakar MT9V032_TEST_PATTERN_DATA_SHIFT) 692cb7a01acSMauro Carvalho Chehab | MT9V032_TEST_PATTERN_USE_DATA 693cb7a01acSMauro Carvalho Chehab | MT9V032_TEST_PATTERN_ENABLE 694cb7a01acSMauro Carvalho Chehab | MT9V032_TEST_PATTERN_FLIP; 695cb7a01acSMauro Carvalho Chehab break; 696cb7a01acSMauro Carvalho Chehab } 69780b44ef2SPhilipp Zabel return regmap_write(map, MT9V032_TEST_PATTERN, data); 69881ea4829SMarkus Pargmann 69981ea4829SMarkus Pargmann case V4L2_CID_AEGC_DESIRED_BIN: 70081ea4829SMarkus Pargmann return regmap_write(map, MT9V032_AEGC_DESIRED_BIN, ctrl->val); 70181ea4829SMarkus Pargmann 70281ea4829SMarkus Pargmann case V4L2_CID_AEC_LPF: 70381ea4829SMarkus Pargmann return regmap_write(map, MT9V032_AEC_LPF, ctrl->val); 70481ea4829SMarkus Pargmann 70581ea4829SMarkus Pargmann case V4L2_CID_AGC_LPF: 70681ea4829SMarkus Pargmann return regmap_write(map, MT9V032_AGC_LPF, ctrl->val); 70781ea4829SMarkus Pargmann 70881ea4829SMarkus Pargmann case V4L2_CID_AEC_UPDATE_INTERVAL: 70981ea4829SMarkus Pargmann return regmap_write(map, MT9V032_AEC_UPDATE_FREQUENCY, 71081ea4829SMarkus Pargmann ctrl->val); 71181ea4829SMarkus Pargmann 71281ea4829SMarkus Pargmann case V4L2_CID_AGC_UPDATE_INTERVAL: 71381ea4829SMarkus Pargmann return regmap_write(map, MT9V032_AGC_UPDATE_FREQUENCY, 71481ea4829SMarkus Pargmann ctrl->val); 71581ea4829SMarkus Pargmann 71681ea4829SMarkus Pargmann case V4L2_CID_AEC_MAX_SHUTTER_WIDTH: 71781ea4829SMarkus Pargmann return regmap_write(map, 71881ea4829SMarkus Pargmann mt9v032->model->data->aec_max_shutter_reg, 71981ea4829SMarkus Pargmann ctrl->val); 720cb7a01acSMauro Carvalho Chehab } 721cb7a01acSMauro Carvalho Chehab 722cb7a01acSMauro Carvalho Chehab return 0; 723cb7a01acSMauro Carvalho Chehab } 724cb7a01acSMauro Carvalho Chehab 725217bdb07SJulia Lawall static const struct v4l2_ctrl_ops mt9v032_ctrl_ops = { 726cb7a01acSMauro Carvalho Chehab .s_ctrl = mt9v032_s_ctrl, 727cb7a01acSMauro Carvalho Chehab }; 728cb7a01acSMauro Carvalho Chehab 729b28d7017SLad, Prabhakar static const char * const mt9v032_test_pattern_menu[] = { 730b28d7017SLad, Prabhakar "Disabled", 731b28d7017SLad, Prabhakar "Gray Vertical Shade", 732b28d7017SLad, Prabhakar "Gray Horizontal Shade", 733b28d7017SLad, Prabhakar "Gray Diagonal Shade", 734b28d7017SLad, Prabhakar "Plain", 735b28d7017SLad, Prabhakar }; 736b28d7017SLad, Prabhakar 737b28d7017SLad, Prabhakar static const struct v4l2_ctrl_config mt9v032_test_pattern_color = { 738cb7a01acSMauro Carvalho Chehab .ops = &mt9v032_ctrl_ops, 739b28d7017SLad, Prabhakar .id = V4L2_CID_TEST_PATTERN_COLOR, 740cb7a01acSMauro Carvalho Chehab .type = V4L2_CTRL_TYPE_INTEGER, 741b28d7017SLad, Prabhakar .name = "Test Pattern Color", 742cb7a01acSMauro Carvalho Chehab .min = 0, 743cb7a01acSMauro Carvalho Chehab .max = 1023, 744cb7a01acSMauro Carvalho Chehab .step = 1, 745cb7a01acSMauro Carvalho Chehab .def = 0, 746cb7a01acSMauro Carvalho Chehab .flags = 0, 747cb7a01acSMauro Carvalho Chehab }; 748cb7a01acSMauro Carvalho Chehab 74981ea4829SMarkus Pargmann static const struct v4l2_ctrl_config mt9v032_aegc_controls[] = { 75081ea4829SMarkus Pargmann { 75181ea4829SMarkus Pargmann .ops = &mt9v032_ctrl_ops, 75281ea4829SMarkus Pargmann .id = V4L2_CID_AEGC_DESIRED_BIN, 75381ea4829SMarkus Pargmann .type = V4L2_CTRL_TYPE_INTEGER, 75481ea4829SMarkus Pargmann .name = "AEC/AGC Desired Bin", 75581ea4829SMarkus Pargmann .min = 1, 75681ea4829SMarkus Pargmann .max = 64, 75781ea4829SMarkus Pargmann .step = 1, 75881ea4829SMarkus Pargmann .def = 58, 75981ea4829SMarkus Pargmann .flags = 0, 76081ea4829SMarkus Pargmann }, { 76181ea4829SMarkus Pargmann .ops = &mt9v032_ctrl_ops, 76281ea4829SMarkus Pargmann .id = V4L2_CID_AEC_LPF, 76381ea4829SMarkus Pargmann .type = V4L2_CTRL_TYPE_INTEGER, 76481ea4829SMarkus Pargmann .name = "AEC Low Pass Filter", 76581ea4829SMarkus Pargmann .min = 0, 76681ea4829SMarkus Pargmann .max = 2, 76781ea4829SMarkus Pargmann .step = 1, 76881ea4829SMarkus Pargmann .def = 0, 76981ea4829SMarkus Pargmann .flags = 0, 77081ea4829SMarkus Pargmann }, { 77181ea4829SMarkus Pargmann .ops = &mt9v032_ctrl_ops, 77281ea4829SMarkus Pargmann .id = V4L2_CID_AGC_LPF, 77381ea4829SMarkus Pargmann .type = V4L2_CTRL_TYPE_INTEGER, 77481ea4829SMarkus Pargmann .name = "AGC Low Pass Filter", 77581ea4829SMarkus Pargmann .min = 0, 77681ea4829SMarkus Pargmann .max = 2, 77781ea4829SMarkus Pargmann .step = 1, 77881ea4829SMarkus Pargmann .def = 2, 77981ea4829SMarkus Pargmann .flags = 0, 78081ea4829SMarkus Pargmann }, { 78181ea4829SMarkus Pargmann .ops = &mt9v032_ctrl_ops, 78281ea4829SMarkus Pargmann .id = V4L2_CID_AEC_UPDATE_INTERVAL, 78381ea4829SMarkus Pargmann .type = V4L2_CTRL_TYPE_INTEGER, 78481ea4829SMarkus Pargmann .name = "AEC Update Interval", 78581ea4829SMarkus Pargmann .min = 0, 78681ea4829SMarkus Pargmann .max = 16, 78781ea4829SMarkus Pargmann .step = 1, 78881ea4829SMarkus Pargmann .def = 2, 78981ea4829SMarkus Pargmann .flags = 0, 79081ea4829SMarkus Pargmann }, { 79181ea4829SMarkus Pargmann .ops = &mt9v032_ctrl_ops, 79281ea4829SMarkus Pargmann .id = V4L2_CID_AGC_UPDATE_INTERVAL, 79381ea4829SMarkus Pargmann .type = V4L2_CTRL_TYPE_INTEGER, 79481ea4829SMarkus Pargmann .name = "AGC Update Interval", 79581ea4829SMarkus Pargmann .min = 0, 79681ea4829SMarkus Pargmann .max = 16, 79781ea4829SMarkus Pargmann .step = 1, 79881ea4829SMarkus Pargmann .def = 2, 79981ea4829SMarkus Pargmann .flags = 0, 80081ea4829SMarkus Pargmann } 80181ea4829SMarkus Pargmann }; 80281ea4829SMarkus Pargmann 80381ea4829SMarkus Pargmann static const struct v4l2_ctrl_config mt9v032_aec_max_shutter_width = { 80481ea4829SMarkus Pargmann .ops = &mt9v032_ctrl_ops, 80581ea4829SMarkus Pargmann .id = V4L2_CID_AEC_MAX_SHUTTER_WIDTH, 80681ea4829SMarkus Pargmann .type = V4L2_CTRL_TYPE_INTEGER, 80781ea4829SMarkus Pargmann .name = "AEC Max Shutter Width", 80881ea4829SMarkus Pargmann .min = 1, 80981ea4829SMarkus Pargmann .max = 2047, 81081ea4829SMarkus Pargmann .step = 1, 81181ea4829SMarkus Pargmann .def = 480, 81281ea4829SMarkus Pargmann .flags = 0, 81381ea4829SMarkus Pargmann }; 81481ea4829SMarkus Pargmann 81581ea4829SMarkus Pargmann static const struct v4l2_ctrl_config mt9v034_aec_max_shutter_width = { 81681ea4829SMarkus Pargmann .ops = &mt9v032_ctrl_ops, 81781ea4829SMarkus Pargmann .id = V4L2_CID_AEC_MAX_SHUTTER_WIDTH, 81881ea4829SMarkus Pargmann .type = V4L2_CTRL_TYPE_INTEGER, 81981ea4829SMarkus Pargmann .name = "AEC Max Shutter Width", 82081ea4829SMarkus Pargmann .min = 1, 82181ea4829SMarkus Pargmann .max = 32765, 82281ea4829SMarkus Pargmann .step = 1, 82381ea4829SMarkus Pargmann .def = 480, 82481ea4829SMarkus Pargmann .flags = 0, 82581ea4829SMarkus Pargmann }; 82681ea4829SMarkus Pargmann 827cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------------- 828cb7a01acSMauro Carvalho Chehab * V4L2 subdev core operations 829cb7a01acSMauro Carvalho Chehab */ 830cb7a01acSMauro Carvalho Chehab 831cb7a01acSMauro Carvalho Chehab static int mt9v032_set_power(struct v4l2_subdev *subdev, int on) 832cb7a01acSMauro Carvalho Chehab { 833cb7a01acSMauro Carvalho Chehab struct mt9v032 *mt9v032 = to_mt9v032(subdev); 834cb7a01acSMauro Carvalho Chehab int ret = 0; 835cb7a01acSMauro Carvalho Chehab 836cb7a01acSMauro Carvalho Chehab mutex_lock(&mt9v032->power_lock); 837cb7a01acSMauro Carvalho Chehab 838cb7a01acSMauro Carvalho Chehab /* If the power count is modified from 0 to != 0 or from != 0 to 0, 839cb7a01acSMauro Carvalho Chehab * update the power state. 840cb7a01acSMauro Carvalho Chehab */ 841cb7a01acSMauro Carvalho Chehab if (mt9v032->power_count == !on) { 842cb7a01acSMauro Carvalho Chehab ret = __mt9v032_set_power(mt9v032, !!on); 843cb7a01acSMauro Carvalho Chehab if (ret < 0) 844cb7a01acSMauro Carvalho Chehab goto done; 845cb7a01acSMauro Carvalho Chehab } 846cb7a01acSMauro Carvalho Chehab 847cb7a01acSMauro Carvalho Chehab /* Update the power count. */ 848cb7a01acSMauro Carvalho Chehab mt9v032->power_count += on ? 1 : -1; 849cb7a01acSMauro Carvalho Chehab WARN_ON(mt9v032->power_count < 0); 850cb7a01acSMauro Carvalho Chehab 851cb7a01acSMauro Carvalho Chehab done: 852cb7a01acSMauro Carvalho Chehab mutex_unlock(&mt9v032->power_lock); 853cb7a01acSMauro Carvalho Chehab return ret; 854cb7a01acSMauro Carvalho Chehab } 855cb7a01acSMauro Carvalho Chehab 856cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------------- 857cb7a01acSMauro Carvalho Chehab * V4L2 subdev internal operations 858cb7a01acSMauro Carvalho Chehab */ 859cb7a01acSMauro Carvalho Chehab 860cb7a01acSMauro Carvalho Chehab static int mt9v032_registered(struct v4l2_subdev *subdev) 861cb7a01acSMauro Carvalho Chehab { 862cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(subdev); 863cb7a01acSMauro Carvalho Chehab struct mt9v032 *mt9v032 = to_mt9v032(subdev); 8640a466b60SLaurent Pinchart unsigned int i; 86580b44ef2SPhilipp Zabel u32 version; 866cb7a01acSMauro Carvalho Chehab int ret; 867cb7a01acSMauro Carvalho Chehab 868cb7a01acSMauro Carvalho Chehab dev_info(&client->dev, "Probing MT9V032 at address 0x%02x\n", 869cb7a01acSMauro Carvalho Chehab client->addr); 870cb7a01acSMauro Carvalho Chehab 871cb7a01acSMauro Carvalho Chehab ret = mt9v032_power_on(mt9v032); 872cb7a01acSMauro Carvalho Chehab if (ret < 0) { 873cb7a01acSMauro Carvalho Chehab dev_err(&client->dev, "MT9V032 power up failed\n"); 874cb7a01acSMauro Carvalho Chehab return ret; 875cb7a01acSMauro Carvalho Chehab } 876cb7a01acSMauro Carvalho Chehab 877cb7a01acSMauro Carvalho Chehab /* Read and check the sensor version */ 87880b44ef2SPhilipp Zabel ret = regmap_read(mt9v032->regmap, MT9V032_CHIP_VERSION, &version); 87980b44ef2SPhilipp Zabel if (ret < 0) { 8800a466b60SLaurent Pinchart dev_err(&client->dev, "Failed reading chip version\n"); 88180b44ef2SPhilipp Zabel return ret; 8820a466b60SLaurent Pinchart } 8830a466b60SLaurent Pinchart 8840a466b60SLaurent Pinchart for (i = 0; i < ARRAY_SIZE(mt9v032_versions); ++i) { 8850a466b60SLaurent Pinchart if (mt9v032_versions[i].version == version) { 8860a466b60SLaurent Pinchart mt9v032->version = &mt9v032_versions[i]; 8870a466b60SLaurent Pinchart break; 8880a466b60SLaurent Pinchart } 8890a466b60SLaurent Pinchart } 8900a466b60SLaurent Pinchart 8910a466b60SLaurent Pinchart if (mt9v032->version == NULL) { 8920a466b60SLaurent Pinchart dev_err(&client->dev, "Unsupported chip version 0x%04x\n", 8930a466b60SLaurent Pinchart version); 894cb7a01acSMauro Carvalho Chehab return -ENODEV; 895cb7a01acSMauro Carvalho Chehab } 896cb7a01acSMauro Carvalho Chehab 897cb7a01acSMauro Carvalho Chehab mt9v032_power_off(mt9v032); 898cb7a01acSMauro Carvalho Chehab 8990a466b60SLaurent Pinchart dev_info(&client->dev, "%s detected at address 0x%02x\n", 9000a466b60SLaurent Pinchart mt9v032->version->name, client->addr); 901cb7a01acSMauro Carvalho Chehab 902637f005eSLaurent Pinchart mt9v032_configure_pixel_rate(mt9v032); 90341a33a00SSakari Ailus 904cb7a01acSMauro Carvalho Chehab return ret; 905cb7a01acSMauro Carvalho Chehab } 906cb7a01acSMauro Carvalho Chehab 907cb7a01acSMauro Carvalho Chehab static int mt9v032_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) 908cb7a01acSMauro Carvalho Chehab { 909220ddc7fSLaurent Pinchart struct mt9v032 *mt9v032 = to_mt9v032(subdev); 910cb7a01acSMauro Carvalho Chehab struct v4l2_mbus_framefmt *format; 911cb7a01acSMauro Carvalho Chehab struct v4l2_rect *crop; 912cb7a01acSMauro Carvalho Chehab 913f7234138SHans Verkuil crop = v4l2_subdev_get_try_crop(subdev, fh->pad, 0); 914cb7a01acSMauro Carvalho Chehab crop->left = MT9V032_COLUMN_START_DEF; 915cb7a01acSMauro Carvalho Chehab crop->top = MT9V032_ROW_START_DEF; 916cb7a01acSMauro Carvalho Chehab crop->width = MT9V032_WINDOW_WIDTH_DEF; 917cb7a01acSMauro Carvalho Chehab crop->height = MT9V032_WINDOW_HEIGHT_DEF; 918cb7a01acSMauro Carvalho Chehab 919f7234138SHans Verkuil format = v4l2_subdev_get_try_format(subdev, fh->pad, 0); 920220ddc7fSLaurent Pinchart 921220ddc7fSLaurent Pinchart if (mt9v032->model->color) 922f5fe58fdSBoris BREZILLON format->code = MEDIA_BUS_FMT_SGRBG10_1X10; 923220ddc7fSLaurent Pinchart else 924f5fe58fdSBoris BREZILLON format->code = MEDIA_BUS_FMT_Y10_1X10; 925220ddc7fSLaurent Pinchart 926cb7a01acSMauro Carvalho Chehab format->width = MT9V032_WINDOW_WIDTH_DEF; 927cb7a01acSMauro Carvalho Chehab format->height = MT9V032_WINDOW_HEIGHT_DEF; 928cb7a01acSMauro Carvalho Chehab format->field = V4L2_FIELD_NONE; 929cb7a01acSMauro Carvalho Chehab format->colorspace = V4L2_COLORSPACE_SRGB; 930cb7a01acSMauro Carvalho Chehab 931cb7a01acSMauro Carvalho Chehab return mt9v032_set_power(subdev, 1); 932cb7a01acSMauro Carvalho Chehab } 933cb7a01acSMauro Carvalho Chehab 934cb7a01acSMauro Carvalho Chehab static int mt9v032_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) 935cb7a01acSMauro Carvalho Chehab { 936cb7a01acSMauro Carvalho Chehab return mt9v032_set_power(subdev, 0); 937cb7a01acSMauro Carvalho Chehab } 938cb7a01acSMauro Carvalho Chehab 9397c137c60SBhumika Goyal static const struct v4l2_subdev_core_ops mt9v032_subdev_core_ops = { 940cb7a01acSMauro Carvalho Chehab .s_power = mt9v032_set_power, 941cb7a01acSMauro Carvalho Chehab }; 942cb7a01acSMauro Carvalho Chehab 9437c137c60SBhumika Goyal static const struct v4l2_subdev_video_ops mt9v032_subdev_video_ops = { 944cb7a01acSMauro Carvalho Chehab .s_stream = mt9v032_s_stream, 945cb7a01acSMauro Carvalho Chehab }; 946cb7a01acSMauro Carvalho Chehab 9477c137c60SBhumika Goyal static const struct v4l2_subdev_pad_ops mt9v032_subdev_pad_ops = { 948cb7a01acSMauro Carvalho Chehab .enum_mbus_code = mt9v032_enum_mbus_code, 949cb7a01acSMauro Carvalho Chehab .enum_frame_size = mt9v032_enum_frame_size, 950cb7a01acSMauro Carvalho Chehab .get_fmt = mt9v032_get_format, 951cb7a01acSMauro Carvalho Chehab .set_fmt = mt9v032_set_format, 9521a023febSHans Verkuil .get_selection = mt9v032_get_selection, 9531a023febSHans Verkuil .set_selection = mt9v032_set_selection, 954cb7a01acSMauro Carvalho Chehab }; 955cb7a01acSMauro Carvalho Chehab 9567c137c60SBhumika Goyal static const struct v4l2_subdev_ops mt9v032_subdev_ops = { 957cb7a01acSMauro Carvalho Chehab .core = &mt9v032_subdev_core_ops, 958cb7a01acSMauro Carvalho Chehab .video = &mt9v032_subdev_video_ops, 959cb7a01acSMauro Carvalho Chehab .pad = &mt9v032_subdev_pad_ops, 960cb7a01acSMauro Carvalho Chehab }; 961cb7a01acSMauro Carvalho Chehab 962cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_internal_ops mt9v032_subdev_internal_ops = { 963cb7a01acSMauro Carvalho Chehab .registered = mt9v032_registered, 964cb7a01acSMauro Carvalho Chehab .open = mt9v032_open, 965cb7a01acSMauro Carvalho Chehab .close = mt9v032_close, 966cb7a01acSMauro Carvalho Chehab }; 967cb7a01acSMauro Carvalho Chehab 96880b44ef2SPhilipp Zabel static const struct regmap_config mt9v032_regmap_config = { 96980b44ef2SPhilipp Zabel .reg_bits = 8, 97080b44ef2SPhilipp Zabel .val_bits = 16, 97180b44ef2SPhilipp Zabel .max_register = 0xff, 97280b44ef2SPhilipp Zabel .cache_type = REGCACHE_RBTREE, 97380b44ef2SPhilipp Zabel }; 97480b44ef2SPhilipp Zabel 975cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------------- 976cb7a01acSMauro Carvalho Chehab * Driver initialization and probing 977cb7a01acSMauro Carvalho Chehab */ 978cb7a01acSMauro Carvalho Chehab 979f2272e13SLaurent Pinchart static struct mt9v032_platform_data * 980f2272e13SLaurent Pinchart mt9v032_get_pdata(struct i2c_client *client) 981f2272e13SLaurent Pinchart { 9828e8a6b23SHans Verkuil struct mt9v032_platform_data *pdata = NULL; 983859969b3SSakari Ailus struct v4l2_fwnode_endpoint endpoint; 984f2272e13SLaurent Pinchart struct device_node *np; 985f2272e13SLaurent Pinchart struct property *prop; 986f2272e13SLaurent Pinchart 987f2272e13SLaurent Pinchart if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node) 988f2272e13SLaurent Pinchart return client->dev.platform_data; 989f2272e13SLaurent Pinchart 990f2272e13SLaurent Pinchart np = of_graph_get_next_endpoint(client->dev.of_node, NULL); 991f2272e13SLaurent Pinchart if (!np) 992f2272e13SLaurent Pinchart return NULL; 993f2272e13SLaurent Pinchart 994859969b3SSakari Ailus if (v4l2_fwnode_endpoint_parse(of_fwnode_handle(np), &endpoint) < 0) 995f2272e13SLaurent Pinchart goto done; 996f2272e13SLaurent Pinchart 997f2272e13SLaurent Pinchart pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); 998f2272e13SLaurent Pinchart if (!pdata) 999f2272e13SLaurent Pinchart goto done; 1000f2272e13SLaurent Pinchart 1001f2272e13SLaurent Pinchart prop = of_find_property(np, "link-frequencies", NULL); 1002f2272e13SLaurent Pinchart if (prop) { 1003f2272e13SLaurent Pinchart u64 *link_freqs; 1004f2272e13SLaurent Pinchart size_t size = prop->length / sizeof(*link_freqs); 1005f2272e13SLaurent Pinchart 1006f2272e13SLaurent Pinchart link_freqs = devm_kcalloc(&client->dev, size, 1007f2272e13SLaurent Pinchart sizeof(*link_freqs), GFP_KERNEL); 1008f2272e13SLaurent Pinchart if (!link_freqs) 1009f2272e13SLaurent Pinchart goto done; 1010f2272e13SLaurent Pinchart 1011f2272e13SLaurent Pinchart if (of_property_read_u64_array(np, "link-frequencies", 1012f2272e13SLaurent Pinchart link_freqs, size) < 0) 1013f2272e13SLaurent Pinchart goto done; 1014f2272e13SLaurent Pinchart 1015f2272e13SLaurent Pinchart pdata->link_freqs = link_freqs; 1016f2272e13SLaurent Pinchart pdata->link_def_freq = link_freqs[0]; 1017f2272e13SLaurent Pinchart } 1018f2272e13SLaurent Pinchart 1019f2272e13SLaurent Pinchart pdata->clk_pol = !!(endpoint.bus.parallel.flags & 1020f2272e13SLaurent Pinchart V4L2_MBUS_PCLK_SAMPLE_RISING); 1021f2272e13SLaurent Pinchart 1022f2272e13SLaurent Pinchart done: 1023f2272e13SLaurent Pinchart of_node_put(np); 1024f2272e13SLaurent Pinchart return pdata; 1025f2272e13SLaurent Pinchart } 1026f2272e13SLaurent Pinchart 1027cb7a01acSMauro Carvalho Chehab static int mt9v032_probe(struct i2c_client *client, 1028cb7a01acSMauro Carvalho Chehab const struct i2c_device_id *did) 1029cb7a01acSMauro Carvalho Chehab { 1030f2272e13SLaurent Pinchart struct mt9v032_platform_data *pdata = mt9v032_get_pdata(client); 1031cb7a01acSMauro Carvalho Chehab struct mt9v032 *mt9v032; 1032cb7a01acSMauro Carvalho Chehab unsigned int i; 1033cb7a01acSMauro Carvalho Chehab int ret; 1034cb7a01acSMauro Carvalho Chehab 1035c02b211dSLaurent Pinchart mt9v032 = devm_kzalloc(&client->dev, sizeof(*mt9v032), GFP_KERNEL); 1036cb7a01acSMauro Carvalho Chehab if (!mt9v032) 1037cb7a01acSMauro Carvalho Chehab return -ENOMEM; 1038cb7a01acSMauro Carvalho Chehab 103980b44ef2SPhilipp Zabel mt9v032->regmap = devm_regmap_init_i2c(client, &mt9v032_regmap_config); 104080b44ef2SPhilipp Zabel if (IS_ERR(mt9v032->regmap)) 104180b44ef2SPhilipp Zabel return PTR_ERR(mt9v032->regmap); 104280b44ef2SPhilipp Zabel 10433300a8fdSLaurent Pinchart mt9v032->clk = devm_clk_get(&client->dev, NULL); 10443300a8fdSLaurent Pinchart if (IS_ERR(mt9v032->clk)) 10453300a8fdSLaurent Pinchart return PTR_ERR(mt9v032->clk); 10463300a8fdSLaurent Pinchart 104728d5bdbeSMarkus Pargmann mt9v032->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", 104828d5bdbeSMarkus Pargmann GPIOD_OUT_HIGH); 104928d5bdbeSMarkus Pargmann if (IS_ERR(mt9v032->reset_gpio)) 105028d5bdbeSMarkus Pargmann return PTR_ERR(mt9v032->reset_gpio); 105128d5bdbeSMarkus Pargmann 105228d5bdbeSMarkus Pargmann mt9v032->standby_gpio = devm_gpiod_get_optional(&client->dev, "standby", 105328d5bdbeSMarkus Pargmann GPIOD_OUT_LOW); 105428d5bdbeSMarkus Pargmann if (IS_ERR(mt9v032->standby_gpio)) 105528d5bdbeSMarkus Pargmann return PTR_ERR(mt9v032->standby_gpio); 105628d5bdbeSMarkus Pargmann 1057cb7a01acSMauro Carvalho Chehab mutex_init(&mt9v032->power_lock); 1058e9a50e4cSLaurent Pinchart mt9v032->pdata = pdata; 1059220ddc7fSLaurent Pinchart mt9v032->model = (const void *)did->driver_data; 1060cb7a01acSMauro Carvalho Chehab 106181ea4829SMarkus Pargmann v4l2_ctrl_handler_init(&mt9v032->ctrls, 11 + 106281ea4829SMarkus Pargmann ARRAY_SIZE(mt9v032_aegc_controls)); 1063cb7a01acSMauro Carvalho Chehab 1064cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, 1065cb7a01acSMauro Carvalho Chehab V4L2_CID_AUTOGAIN, 0, 1, 1, 1); 1066cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, 1067cb7a01acSMauro Carvalho Chehab V4L2_CID_GAIN, MT9V032_ANALOG_GAIN_MIN, 1068cb7a01acSMauro Carvalho Chehab MT9V032_ANALOG_GAIN_MAX, 1, MT9V032_ANALOG_GAIN_DEF); 1069cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std_menu(&mt9v032->ctrls, &mt9v032_ctrl_ops, 1070cb7a01acSMauro Carvalho Chehab V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0, 1071cb7a01acSMauro Carvalho Chehab V4L2_EXPOSURE_AUTO); 1072cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, 10730a466b60SLaurent Pinchart V4L2_CID_EXPOSURE, mt9v032->model->data->min_shutter, 10740a466b60SLaurent Pinchart mt9v032->model->data->max_shutter, 1, 1075cb7a01acSMauro Carvalho Chehab MT9V032_TOTAL_SHUTTER_WIDTH_DEF); 10769ec670e2SLaurent Pinchart v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, 10770a466b60SLaurent Pinchart V4L2_CID_HBLANK, mt9v032->model->data->min_hblank, 10789ec670e2SLaurent Pinchart MT9V032_HORIZONTAL_BLANKING_MAX, 1, 10799ec670e2SLaurent Pinchart MT9V032_HORIZONTAL_BLANKING_DEF); 10809ec670e2SLaurent Pinchart v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, 10810a466b60SLaurent Pinchart V4L2_CID_VBLANK, mt9v032->model->data->min_vblank, 10820a466b60SLaurent Pinchart mt9v032->model->data->max_vblank, 1, 10839ec670e2SLaurent Pinchart MT9V032_VERTICAL_BLANKING_DEF); 1084b28d7017SLad, Prabhakar mt9v032->test_pattern = v4l2_ctrl_new_std_menu_items(&mt9v032->ctrls, 1085b28d7017SLad, Prabhakar &mt9v032_ctrl_ops, V4L2_CID_TEST_PATTERN, 1086b28d7017SLad, Prabhakar ARRAY_SIZE(mt9v032_test_pattern_menu) - 1, 0, 0, 1087b28d7017SLad, Prabhakar mt9v032_test_pattern_menu); 1088b28d7017SLad, Prabhakar mt9v032->test_pattern_color = v4l2_ctrl_new_custom(&mt9v032->ctrls, 1089b28d7017SLad, Prabhakar &mt9v032_test_pattern_color, NULL); 1090b28d7017SLad, Prabhakar 109181ea4829SMarkus Pargmann v4l2_ctrl_new_custom(&mt9v032->ctrls, 109281ea4829SMarkus Pargmann mt9v032->model->data->aec_max_shutter_v4l2_ctrl, 109381ea4829SMarkus Pargmann NULL); 109481ea4829SMarkus Pargmann for (i = 0; i < ARRAY_SIZE(mt9v032_aegc_controls); ++i) 109581ea4829SMarkus Pargmann v4l2_ctrl_new_custom(&mt9v032->ctrls, &mt9v032_aegc_controls[i], 109681ea4829SMarkus Pargmann NULL); 109781ea4829SMarkus Pargmann 1098b28d7017SLad, Prabhakar v4l2_ctrl_cluster(2, &mt9v032->test_pattern); 1099e9a50e4cSLaurent Pinchart 110041a33a00SSakari Ailus mt9v032->pixel_rate = 110141a33a00SSakari Ailus v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, 11020ba2aeb6SHans Verkuil V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, 1); 1103cb7a01acSMauro Carvalho Chehab 1104e9a50e4cSLaurent Pinchart if (pdata && pdata->link_freqs) { 1105e9a50e4cSLaurent Pinchart unsigned int def = 0; 1106e9a50e4cSLaurent Pinchart 1107e9a50e4cSLaurent Pinchart for (i = 0; pdata->link_freqs[i]; ++i) { 1108e9a50e4cSLaurent Pinchart if (pdata->link_freqs[i] == pdata->link_def_freq) 1109e9a50e4cSLaurent Pinchart def = i; 1110e9a50e4cSLaurent Pinchart } 1111e9a50e4cSLaurent Pinchart 1112e9a50e4cSLaurent Pinchart mt9v032->link_freq = 1113e9a50e4cSLaurent Pinchart v4l2_ctrl_new_int_menu(&mt9v032->ctrls, 1114e9a50e4cSLaurent Pinchart &mt9v032_ctrl_ops, 1115e9a50e4cSLaurent Pinchart V4L2_CID_LINK_FREQ, i - 1, def, 1116e9a50e4cSLaurent Pinchart pdata->link_freqs); 1117e9a50e4cSLaurent Pinchart v4l2_ctrl_cluster(2, &mt9v032->link_freq); 1118e9a50e4cSLaurent Pinchart } 1119e9a50e4cSLaurent Pinchart 1120cb7a01acSMauro Carvalho Chehab 1121cb7a01acSMauro Carvalho Chehab mt9v032->subdev.ctrl_handler = &mt9v032->ctrls; 1122cb7a01acSMauro Carvalho Chehab 11232d01209fSLaurent Pinchart if (mt9v032->ctrls.error) { 11242d01209fSLaurent Pinchart dev_err(&client->dev, "control initialization error %d\n", 11252d01209fSLaurent Pinchart mt9v032->ctrls.error); 11262d01209fSLaurent Pinchart ret = mt9v032->ctrls.error; 11272d01209fSLaurent Pinchart goto err; 11282d01209fSLaurent Pinchart } 1129cb7a01acSMauro Carvalho Chehab 1130cb7a01acSMauro Carvalho Chehab mt9v032->crop.left = MT9V032_COLUMN_START_DEF; 1131cb7a01acSMauro Carvalho Chehab mt9v032->crop.top = MT9V032_ROW_START_DEF; 1132cb7a01acSMauro Carvalho Chehab mt9v032->crop.width = MT9V032_WINDOW_WIDTH_DEF; 1133cb7a01acSMauro Carvalho Chehab mt9v032->crop.height = MT9V032_WINDOW_HEIGHT_DEF; 1134cb7a01acSMauro Carvalho Chehab 1135220ddc7fSLaurent Pinchart if (mt9v032->model->color) 1136f5fe58fdSBoris BREZILLON mt9v032->format.code = MEDIA_BUS_FMT_SGRBG10_1X10; 1137220ddc7fSLaurent Pinchart else 1138f5fe58fdSBoris BREZILLON mt9v032->format.code = MEDIA_BUS_FMT_Y10_1X10; 1139220ddc7fSLaurent Pinchart 1140cb7a01acSMauro Carvalho Chehab mt9v032->format.width = MT9V032_WINDOW_WIDTH_DEF; 1141cb7a01acSMauro Carvalho Chehab mt9v032->format.height = MT9V032_WINDOW_HEIGHT_DEF; 1142cb7a01acSMauro Carvalho Chehab mt9v032->format.field = V4L2_FIELD_NONE; 1143cb7a01acSMauro Carvalho Chehab mt9v032->format.colorspace = V4L2_COLORSPACE_SRGB; 1144cb7a01acSMauro Carvalho Chehab 1145637f005eSLaurent Pinchart mt9v032->hratio = 1; 1146637f005eSLaurent Pinchart mt9v032->vratio = 1; 1147637f005eSLaurent Pinchart 1148cb7a01acSMauro Carvalho Chehab mt9v032->aec_agc = MT9V032_AEC_ENABLE | MT9V032_AGC_ENABLE; 11499ec670e2SLaurent Pinchart mt9v032->hblank = MT9V032_HORIZONTAL_BLANKING_DEF; 1150e9a50e4cSLaurent Pinchart mt9v032->sysclk = MT9V032_SYSCLK_FREQ_DEF; 1151cb7a01acSMauro Carvalho Chehab 1152cb7a01acSMauro Carvalho Chehab v4l2_i2c_subdev_init(&mt9v032->subdev, client, &mt9v032_subdev_ops); 1153cb7a01acSMauro Carvalho Chehab mt9v032->subdev.internal_ops = &mt9v032_subdev_internal_ops; 1154cb7a01acSMauro Carvalho Chehab mt9v032->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 1155cb7a01acSMauro Carvalho Chehab 1156cb7a01acSMauro Carvalho Chehab mt9v032->pad.flags = MEDIA_PAD_FL_SOURCE; 1157ab22e77cSMauro Carvalho Chehab ret = media_entity_pads_init(&mt9v032->subdev.entity, 1, &mt9v032->pad); 1158c02b211dSLaurent Pinchart if (ret < 0) 115994b76ce8SPhilipp Zabel goto err; 1160cb7a01acSMauro Carvalho Chehab 116194b76ce8SPhilipp Zabel mt9v032->subdev.dev = &client->dev; 116294b76ce8SPhilipp Zabel ret = v4l2_async_register_subdev(&mt9v032->subdev); 116394b76ce8SPhilipp Zabel if (ret < 0) 116494b76ce8SPhilipp Zabel goto err; 116594b76ce8SPhilipp Zabel 116694b76ce8SPhilipp Zabel return 0; 116794b76ce8SPhilipp Zabel 116894b76ce8SPhilipp Zabel err: 116994b76ce8SPhilipp Zabel media_entity_cleanup(&mt9v032->subdev.entity); 117094b76ce8SPhilipp Zabel v4l2_ctrl_handler_free(&mt9v032->ctrls); 1171cb7a01acSMauro Carvalho Chehab return ret; 1172cb7a01acSMauro Carvalho Chehab } 1173cb7a01acSMauro Carvalho Chehab 1174cb7a01acSMauro Carvalho Chehab static int mt9v032_remove(struct i2c_client *client) 1175cb7a01acSMauro Carvalho Chehab { 1176cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *subdev = i2c_get_clientdata(client); 1177cb7a01acSMauro Carvalho Chehab struct mt9v032 *mt9v032 = to_mt9v032(subdev); 1178cb7a01acSMauro Carvalho Chehab 117994b76ce8SPhilipp Zabel v4l2_async_unregister_subdev(subdev); 11809462550fSLaurent Pinchart v4l2_ctrl_handler_free(&mt9v032->ctrls); 1181cb7a01acSMauro Carvalho Chehab media_entity_cleanup(&subdev->entity); 11829462550fSLaurent Pinchart 1183cb7a01acSMauro Carvalho Chehab return 0; 1184cb7a01acSMauro Carvalho Chehab } 1185cb7a01acSMauro Carvalho Chehab 118681ea4829SMarkus Pargmann static const struct mt9v032_model_data mt9v032_model_data[] = { 118781ea4829SMarkus Pargmann { 118881ea4829SMarkus Pargmann /* MT9V022, MT9V032 revisions 1/2/3 */ 118981ea4829SMarkus Pargmann .min_row_time = 660, 119081ea4829SMarkus Pargmann .min_hblank = MT9V032_HORIZONTAL_BLANKING_MIN, 119181ea4829SMarkus Pargmann .min_vblank = MT9V032_VERTICAL_BLANKING_MIN, 119281ea4829SMarkus Pargmann .max_vblank = MT9V032_VERTICAL_BLANKING_MAX, 119381ea4829SMarkus Pargmann .min_shutter = MT9V032_TOTAL_SHUTTER_WIDTH_MIN, 119481ea4829SMarkus Pargmann .max_shutter = MT9V032_TOTAL_SHUTTER_WIDTH_MAX, 119581ea4829SMarkus Pargmann .pclk_reg = MT9V032_PIXEL_CLOCK, 119681ea4829SMarkus Pargmann .aec_max_shutter_reg = MT9V032_AEC_MAX_SHUTTER_WIDTH, 119781ea4829SMarkus Pargmann .aec_max_shutter_v4l2_ctrl = &mt9v032_aec_max_shutter_width, 119881ea4829SMarkus Pargmann }, { 119981ea4829SMarkus Pargmann /* MT9V024, MT9V034 */ 120081ea4829SMarkus Pargmann .min_row_time = 690, 120181ea4829SMarkus Pargmann .min_hblank = MT9V034_HORIZONTAL_BLANKING_MIN, 120281ea4829SMarkus Pargmann .min_vblank = MT9V034_VERTICAL_BLANKING_MIN, 120381ea4829SMarkus Pargmann .max_vblank = MT9V034_VERTICAL_BLANKING_MAX, 120481ea4829SMarkus Pargmann .min_shutter = MT9V034_TOTAL_SHUTTER_WIDTH_MIN, 120581ea4829SMarkus Pargmann .max_shutter = MT9V034_TOTAL_SHUTTER_WIDTH_MAX, 120681ea4829SMarkus Pargmann .pclk_reg = MT9V034_PIXEL_CLOCK, 120781ea4829SMarkus Pargmann .aec_max_shutter_reg = MT9V034_AEC_MAX_SHUTTER_WIDTH, 120881ea4829SMarkus Pargmann .aec_max_shutter_v4l2_ctrl = &mt9v034_aec_max_shutter_width, 120981ea4829SMarkus Pargmann }, 121081ea4829SMarkus Pargmann }; 121181ea4829SMarkus Pargmann 121281ea4829SMarkus Pargmann static const struct mt9v032_model_info mt9v032_models[] = { 121381ea4829SMarkus Pargmann [MT9V032_MODEL_V022_COLOR] = { 121481ea4829SMarkus Pargmann .data = &mt9v032_model_data[0], 121581ea4829SMarkus Pargmann .color = true, 121681ea4829SMarkus Pargmann }, 121781ea4829SMarkus Pargmann [MT9V032_MODEL_V022_MONO] = { 121881ea4829SMarkus Pargmann .data = &mt9v032_model_data[0], 121981ea4829SMarkus Pargmann .color = false, 122081ea4829SMarkus Pargmann }, 122181ea4829SMarkus Pargmann [MT9V032_MODEL_V024_COLOR] = { 122281ea4829SMarkus Pargmann .data = &mt9v032_model_data[1], 122381ea4829SMarkus Pargmann .color = true, 122481ea4829SMarkus Pargmann }, 122581ea4829SMarkus Pargmann [MT9V032_MODEL_V024_MONO] = { 122681ea4829SMarkus Pargmann .data = &mt9v032_model_data[1], 122781ea4829SMarkus Pargmann .color = false, 122881ea4829SMarkus Pargmann }, 122981ea4829SMarkus Pargmann [MT9V032_MODEL_V032_COLOR] = { 123081ea4829SMarkus Pargmann .data = &mt9v032_model_data[0], 123181ea4829SMarkus Pargmann .color = true, 123281ea4829SMarkus Pargmann }, 123381ea4829SMarkus Pargmann [MT9V032_MODEL_V032_MONO] = { 123481ea4829SMarkus Pargmann .data = &mt9v032_model_data[0], 123581ea4829SMarkus Pargmann .color = false, 123681ea4829SMarkus Pargmann }, 123781ea4829SMarkus Pargmann [MT9V032_MODEL_V034_COLOR] = { 123881ea4829SMarkus Pargmann .data = &mt9v032_model_data[1], 123981ea4829SMarkus Pargmann .color = true, 124081ea4829SMarkus Pargmann }, 124181ea4829SMarkus Pargmann [MT9V032_MODEL_V034_MONO] = { 124281ea4829SMarkus Pargmann .data = &mt9v032_model_data[1], 124381ea4829SMarkus Pargmann .color = false, 124481ea4829SMarkus Pargmann }, 124581ea4829SMarkus Pargmann }; 124681ea4829SMarkus Pargmann 1247cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id mt9v032_id[] = { 1248d8dde6c8SPhilipp Zabel { "mt9v022", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V022_COLOR] }, 1249d8dde6c8SPhilipp Zabel { "mt9v022m", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V022_MONO] }, 1250d8dde6c8SPhilipp Zabel { "mt9v024", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V024_COLOR] }, 1251d8dde6c8SPhilipp Zabel { "mt9v024m", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V024_MONO] }, 1252daecfebcSLaurent Pinchart { "mt9v032", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V032_COLOR] }, 1253daecfebcSLaurent Pinchart { "mt9v032m", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V032_MONO] }, 1254daecfebcSLaurent Pinchart { "mt9v034", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V034_COLOR] }, 1255daecfebcSLaurent Pinchart { "mt9v034m", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V034_MONO] }, 1256cb7a01acSMauro Carvalho Chehab { } 1257cb7a01acSMauro Carvalho Chehab }; 1258cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, mt9v032_id); 1259cb7a01acSMauro Carvalho Chehab 1260f2272e13SLaurent Pinchart #if IS_ENABLED(CONFIG_OF) 1261f2272e13SLaurent Pinchart static const struct of_device_id mt9v032_of_match[] = { 1262f2272e13SLaurent Pinchart { .compatible = "aptina,mt9v022" }, 1263f2272e13SLaurent Pinchart { .compatible = "aptina,mt9v022m" }, 1264f2272e13SLaurent Pinchart { .compatible = "aptina,mt9v024" }, 1265f2272e13SLaurent Pinchart { .compatible = "aptina,mt9v024m" }, 1266f2272e13SLaurent Pinchart { .compatible = "aptina,mt9v032" }, 1267f2272e13SLaurent Pinchart { .compatible = "aptina,mt9v032m" }, 1268f2272e13SLaurent Pinchart { .compatible = "aptina,mt9v034" }, 1269f2272e13SLaurent Pinchart { .compatible = "aptina,mt9v034m" }, 1270f2272e13SLaurent Pinchart { /* Sentinel */ } 1271f2272e13SLaurent Pinchart }; 1272f2272e13SLaurent Pinchart MODULE_DEVICE_TABLE(of, mt9v032_of_match); 1273f2272e13SLaurent Pinchart #endif 1274f2272e13SLaurent Pinchart 1275cb7a01acSMauro Carvalho Chehab static struct i2c_driver mt9v032_driver = { 1276cb7a01acSMauro Carvalho Chehab .driver = { 1277cb7a01acSMauro Carvalho Chehab .name = "mt9v032", 1278f2272e13SLaurent Pinchart .of_match_table = of_match_ptr(mt9v032_of_match), 1279cb7a01acSMauro Carvalho Chehab }, 1280cb7a01acSMauro Carvalho Chehab .probe = mt9v032_probe, 1281cb7a01acSMauro Carvalho Chehab .remove = mt9v032_remove, 1282cb7a01acSMauro Carvalho Chehab .id_table = mt9v032_id, 1283cb7a01acSMauro Carvalho Chehab }; 1284cb7a01acSMauro Carvalho Chehab 1285cb7a01acSMauro Carvalho Chehab module_i2c_driver(mt9v032_driver); 1286cb7a01acSMauro Carvalho Chehab 1287cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("Aptina MT9V032 Camera driver"); 1288cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); 1289cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL"); 1290