1cb7a01acSMauro Carvalho Chehab /* 2cb7a01acSMauro Carvalho Chehab * Driver for MT9V032 CMOS Image Sensor from Micron 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> 17cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h> 18cb7a01acSMauro Carvalho Chehab #include <linux/log2.h> 19cb7a01acSMauro Carvalho Chehab #include <linux/mutex.h> 20cb7a01acSMauro Carvalho Chehab #include <linux/slab.h> 21cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h> 22cb7a01acSMauro Carvalho Chehab #include <linux/v4l2-mediabus.h> 23cb7a01acSMauro Carvalho Chehab #include <linux/module.h> 24cb7a01acSMauro Carvalho Chehab 25cb7a01acSMauro Carvalho Chehab #include <media/mt9v032.h> 26cb7a01acSMauro Carvalho Chehab #include <media/v4l2-ctrls.h> 27cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h> 28cb7a01acSMauro Carvalho Chehab #include <media/v4l2-subdev.h> 29cb7a01acSMauro Carvalho Chehab 302b9e9f77SLaurent Pinchart /* The first four rows are black rows. The active area spans 753x481 pixels. */ 312b9e9f77SLaurent Pinchart #define MT9V032_PIXEL_ARRAY_HEIGHT 485 322b9e9f77SLaurent Pinchart #define MT9V032_PIXEL_ARRAY_WIDTH 753 33cb7a01acSMauro Carvalho Chehab 34e9a50e4cSLaurent Pinchart #define MT9V032_SYSCLK_FREQ_DEF 26600000 35e9a50e4cSLaurent Pinchart 36cb7a01acSMauro Carvalho Chehab #define MT9V032_CHIP_VERSION 0x00 37cb7a01acSMauro Carvalho Chehab #define MT9V032_CHIP_ID_REV1 0x1311 38cb7a01acSMauro Carvalho Chehab #define MT9V032_CHIP_ID_REV3 0x1313 39daecfebcSLaurent Pinchart #define MT9V034_CHIP_ID_REV1 0X1324 40cb7a01acSMauro Carvalho Chehab #define MT9V032_COLUMN_START 0x01 41cb7a01acSMauro Carvalho Chehab #define MT9V032_COLUMN_START_MIN 1 42cb7a01acSMauro Carvalho Chehab #define MT9V032_COLUMN_START_DEF 1 43cb7a01acSMauro Carvalho Chehab #define MT9V032_COLUMN_START_MAX 752 44cb7a01acSMauro Carvalho Chehab #define MT9V032_ROW_START 0x02 45cb7a01acSMauro Carvalho Chehab #define MT9V032_ROW_START_MIN 4 46cb7a01acSMauro Carvalho Chehab #define MT9V032_ROW_START_DEF 5 47cb7a01acSMauro Carvalho Chehab #define MT9V032_ROW_START_MAX 482 48cb7a01acSMauro Carvalho Chehab #define MT9V032_WINDOW_HEIGHT 0x03 49cb7a01acSMauro Carvalho Chehab #define MT9V032_WINDOW_HEIGHT_MIN 1 50cb7a01acSMauro Carvalho Chehab #define MT9V032_WINDOW_HEIGHT_DEF 480 51cb7a01acSMauro Carvalho Chehab #define MT9V032_WINDOW_HEIGHT_MAX 480 52cb7a01acSMauro Carvalho Chehab #define MT9V032_WINDOW_WIDTH 0x04 53cb7a01acSMauro Carvalho Chehab #define MT9V032_WINDOW_WIDTH_MIN 1 54cb7a01acSMauro Carvalho Chehab #define MT9V032_WINDOW_WIDTH_DEF 752 55cb7a01acSMauro Carvalho Chehab #define MT9V032_WINDOW_WIDTH_MAX 752 56cb7a01acSMauro Carvalho Chehab #define MT9V032_HORIZONTAL_BLANKING 0x05 57cb7a01acSMauro Carvalho Chehab #define MT9V032_HORIZONTAL_BLANKING_MIN 43 58daecfebcSLaurent Pinchart #define MT9V034_HORIZONTAL_BLANKING_MIN 61 599ec670e2SLaurent Pinchart #define MT9V032_HORIZONTAL_BLANKING_DEF 94 60cb7a01acSMauro Carvalho Chehab #define MT9V032_HORIZONTAL_BLANKING_MAX 1023 61cb7a01acSMauro Carvalho Chehab #define MT9V032_VERTICAL_BLANKING 0x06 62cb7a01acSMauro Carvalho Chehab #define MT9V032_VERTICAL_BLANKING_MIN 4 63daecfebcSLaurent Pinchart #define MT9V034_VERTICAL_BLANKING_MIN 2 649ec670e2SLaurent Pinchart #define MT9V032_VERTICAL_BLANKING_DEF 45 65cb7a01acSMauro Carvalho Chehab #define MT9V032_VERTICAL_BLANKING_MAX 3000 66daecfebcSLaurent Pinchart #define MT9V034_VERTICAL_BLANKING_MAX 32288 67cb7a01acSMauro Carvalho Chehab #define MT9V032_CHIP_CONTROL 0x07 68cb7a01acSMauro Carvalho Chehab #define MT9V032_CHIP_CONTROL_MASTER_MODE (1 << 3) 69cb7a01acSMauro Carvalho Chehab #define MT9V032_CHIP_CONTROL_DOUT_ENABLE (1 << 7) 70cb7a01acSMauro Carvalho Chehab #define MT9V032_CHIP_CONTROL_SEQUENTIAL (1 << 8) 71cb7a01acSMauro Carvalho Chehab #define MT9V032_SHUTTER_WIDTH1 0x08 72cb7a01acSMauro Carvalho Chehab #define MT9V032_SHUTTER_WIDTH2 0x09 73cb7a01acSMauro Carvalho Chehab #define MT9V032_SHUTTER_WIDTH_CONTROL 0x0a 74cb7a01acSMauro Carvalho Chehab #define MT9V032_TOTAL_SHUTTER_WIDTH 0x0b 75cb7a01acSMauro Carvalho Chehab #define MT9V032_TOTAL_SHUTTER_WIDTH_MIN 1 76daecfebcSLaurent Pinchart #define MT9V034_TOTAL_SHUTTER_WIDTH_MIN 0 77cb7a01acSMauro Carvalho Chehab #define MT9V032_TOTAL_SHUTTER_WIDTH_DEF 480 78cb7a01acSMauro Carvalho Chehab #define MT9V032_TOTAL_SHUTTER_WIDTH_MAX 32767 79daecfebcSLaurent Pinchart #define MT9V034_TOTAL_SHUTTER_WIDTH_MAX 32765 80cb7a01acSMauro Carvalho Chehab #define MT9V032_RESET 0x0c 81cb7a01acSMauro Carvalho Chehab #define MT9V032_READ_MODE 0x0d 82cb7a01acSMauro Carvalho Chehab #define MT9V032_READ_MODE_ROW_BIN_MASK (3 << 0) 83cb7a01acSMauro Carvalho Chehab #define MT9V032_READ_MODE_ROW_BIN_SHIFT 0 84cb7a01acSMauro Carvalho Chehab #define MT9V032_READ_MODE_COLUMN_BIN_MASK (3 << 2) 85cb7a01acSMauro Carvalho Chehab #define MT9V032_READ_MODE_COLUMN_BIN_SHIFT 2 86cb7a01acSMauro Carvalho Chehab #define MT9V032_READ_MODE_ROW_FLIP (1 << 4) 87cb7a01acSMauro Carvalho Chehab #define MT9V032_READ_MODE_COLUMN_FLIP (1 << 5) 88cb7a01acSMauro Carvalho Chehab #define MT9V032_READ_MODE_DARK_COLUMNS (1 << 6) 89cb7a01acSMauro Carvalho Chehab #define MT9V032_READ_MODE_DARK_ROWS (1 << 7) 90d131e54bSPhilipp Zabel #define MT9V032_READ_MODE_RESERVED 0x0300 91cb7a01acSMauro Carvalho Chehab #define MT9V032_PIXEL_OPERATION_MODE 0x0f 92daecfebcSLaurent Pinchart #define MT9V034_PIXEL_OPERATION_MODE_HDR (1 << 0) 93daecfebcSLaurent Pinchart #define MT9V034_PIXEL_OPERATION_MODE_COLOR (1 << 1) 94cb7a01acSMauro Carvalho Chehab #define MT9V032_PIXEL_OPERATION_MODE_COLOR (1 << 2) 95cb7a01acSMauro Carvalho Chehab #define MT9V032_PIXEL_OPERATION_MODE_HDR (1 << 6) 96cb7a01acSMauro Carvalho Chehab #define MT9V032_ANALOG_GAIN 0x35 97cb7a01acSMauro Carvalho Chehab #define MT9V032_ANALOG_GAIN_MIN 16 98cb7a01acSMauro Carvalho Chehab #define MT9V032_ANALOG_GAIN_DEF 16 99cb7a01acSMauro Carvalho Chehab #define MT9V032_ANALOG_GAIN_MAX 64 100cb7a01acSMauro Carvalho Chehab #define MT9V032_MAX_ANALOG_GAIN 0x36 101cb7a01acSMauro Carvalho Chehab #define MT9V032_MAX_ANALOG_GAIN_MAX 127 102cb7a01acSMauro Carvalho Chehab #define MT9V032_FRAME_DARK_AVERAGE 0x42 103cb7a01acSMauro Carvalho Chehab #define MT9V032_DARK_AVG_THRESH 0x46 104cb7a01acSMauro Carvalho Chehab #define MT9V032_DARK_AVG_LOW_THRESH_MASK (255 << 0) 105cb7a01acSMauro Carvalho Chehab #define MT9V032_DARK_AVG_LOW_THRESH_SHIFT 0 106cb7a01acSMauro Carvalho Chehab #define MT9V032_DARK_AVG_HIGH_THRESH_MASK (255 << 8) 107cb7a01acSMauro Carvalho Chehab #define MT9V032_DARK_AVG_HIGH_THRESH_SHIFT 8 108cb7a01acSMauro Carvalho Chehab #define MT9V032_ROW_NOISE_CORR_CONTROL 0x70 109daecfebcSLaurent Pinchart #define MT9V034_ROW_NOISE_CORR_ENABLE (1 << 0) 110daecfebcSLaurent Pinchart #define MT9V034_ROW_NOISE_CORR_USE_BLK_AVG (1 << 1) 111cb7a01acSMauro Carvalho Chehab #define MT9V032_ROW_NOISE_CORR_ENABLE (1 << 5) 112cb7a01acSMauro Carvalho Chehab #define MT9V032_ROW_NOISE_CORR_USE_BLK_AVG (1 << 7) 113cb7a01acSMauro Carvalho Chehab #define MT9V032_PIXEL_CLOCK 0x74 114daecfebcSLaurent Pinchart #define MT9V034_PIXEL_CLOCK 0x72 115cb7a01acSMauro Carvalho Chehab #define MT9V032_PIXEL_CLOCK_INV_LINE (1 << 0) 116cb7a01acSMauro Carvalho Chehab #define MT9V032_PIXEL_CLOCK_INV_FRAME (1 << 1) 117cb7a01acSMauro Carvalho Chehab #define MT9V032_PIXEL_CLOCK_XOR_LINE (1 << 2) 118cb7a01acSMauro Carvalho Chehab #define MT9V032_PIXEL_CLOCK_CONT_LINE (1 << 3) 119cb7a01acSMauro Carvalho Chehab #define MT9V032_PIXEL_CLOCK_INV_PXL_CLK (1 << 4) 120cb7a01acSMauro Carvalho Chehab #define MT9V032_TEST_PATTERN 0x7f 121cb7a01acSMauro Carvalho Chehab #define MT9V032_TEST_PATTERN_DATA_MASK (1023 << 0) 122cb7a01acSMauro Carvalho Chehab #define MT9V032_TEST_PATTERN_DATA_SHIFT 0 123cb7a01acSMauro Carvalho Chehab #define MT9V032_TEST_PATTERN_USE_DATA (1 << 10) 124cb7a01acSMauro Carvalho Chehab #define MT9V032_TEST_PATTERN_GRAY_MASK (3 << 11) 125cb7a01acSMauro Carvalho Chehab #define MT9V032_TEST_PATTERN_GRAY_NONE (0 << 11) 126cb7a01acSMauro Carvalho Chehab #define MT9V032_TEST_PATTERN_GRAY_VERTICAL (1 << 11) 127cb7a01acSMauro Carvalho Chehab #define MT9V032_TEST_PATTERN_GRAY_HORIZONTAL (2 << 11) 128cb7a01acSMauro Carvalho Chehab #define MT9V032_TEST_PATTERN_GRAY_DIAGONAL (3 << 11) 129cb7a01acSMauro Carvalho Chehab #define MT9V032_TEST_PATTERN_ENABLE (1 << 13) 130cb7a01acSMauro Carvalho Chehab #define MT9V032_TEST_PATTERN_FLIP (1 << 14) 131cb7a01acSMauro Carvalho Chehab #define MT9V032_AEC_AGC_ENABLE 0xaf 132cb7a01acSMauro Carvalho Chehab #define MT9V032_AEC_ENABLE (1 << 0) 133cb7a01acSMauro Carvalho Chehab #define MT9V032_AGC_ENABLE (1 << 1) 134cb7a01acSMauro Carvalho Chehab #define MT9V032_THERMAL_INFO 0xc1 135cb7a01acSMauro Carvalho Chehab 136220ddc7fSLaurent Pinchart enum mt9v032_model { 137daecfebcSLaurent Pinchart MT9V032_MODEL_V032_COLOR, 138daecfebcSLaurent Pinchart MT9V032_MODEL_V032_MONO, 139daecfebcSLaurent Pinchart MT9V032_MODEL_V034_COLOR, 140daecfebcSLaurent Pinchart MT9V032_MODEL_V034_MONO, 141220ddc7fSLaurent Pinchart }; 142220ddc7fSLaurent Pinchart 1430a466b60SLaurent Pinchart struct mt9v032_model_version { 1440a466b60SLaurent Pinchart unsigned int version; 1450a466b60SLaurent Pinchart const char *name; 1460a466b60SLaurent Pinchart }; 1470a466b60SLaurent Pinchart 1480a466b60SLaurent Pinchart struct mt9v032_model_data { 1490a466b60SLaurent Pinchart unsigned int min_row_time; 1500a466b60SLaurent Pinchart unsigned int min_hblank; 1510a466b60SLaurent Pinchart unsigned int min_vblank; 1520a466b60SLaurent Pinchart unsigned int max_vblank; 1530a466b60SLaurent Pinchart unsigned int min_shutter; 1540a466b60SLaurent Pinchart unsigned int max_shutter; 1550a466b60SLaurent Pinchart unsigned int pclk_reg; 1560a466b60SLaurent Pinchart }; 1570a466b60SLaurent Pinchart 158220ddc7fSLaurent Pinchart struct mt9v032_model_info { 1590a466b60SLaurent Pinchart const struct mt9v032_model_data *data; 160220ddc7fSLaurent Pinchart bool color; 161220ddc7fSLaurent Pinchart }; 162220ddc7fSLaurent Pinchart 1630a466b60SLaurent Pinchart static const struct mt9v032_model_version mt9v032_versions[] = { 1640a466b60SLaurent Pinchart { MT9V032_CHIP_ID_REV1, "MT9V032 rev1/2" }, 1650a466b60SLaurent Pinchart { MT9V032_CHIP_ID_REV3, "MT9V032 rev3" }, 166daecfebcSLaurent Pinchart { MT9V034_CHIP_ID_REV1, "MT9V034 rev1" }, 1670a466b60SLaurent Pinchart }; 1680a466b60SLaurent Pinchart 1690a466b60SLaurent Pinchart static const struct mt9v032_model_data mt9v032_model_data[] = { 1700a466b60SLaurent Pinchart { 1710a466b60SLaurent Pinchart /* MT9V032 revisions 1/2/3 */ 1720a466b60SLaurent Pinchart .min_row_time = 660, 1730a466b60SLaurent Pinchart .min_hblank = MT9V032_HORIZONTAL_BLANKING_MIN, 1740a466b60SLaurent Pinchart .min_vblank = MT9V032_VERTICAL_BLANKING_MIN, 1750a466b60SLaurent Pinchart .max_vblank = MT9V032_VERTICAL_BLANKING_MAX, 1760a466b60SLaurent Pinchart .min_shutter = MT9V032_TOTAL_SHUTTER_WIDTH_MIN, 1770a466b60SLaurent Pinchart .max_shutter = MT9V032_TOTAL_SHUTTER_WIDTH_MAX, 1780a466b60SLaurent Pinchart .pclk_reg = MT9V032_PIXEL_CLOCK, 179daecfebcSLaurent Pinchart }, { 180daecfebcSLaurent Pinchart /* MT9V034 */ 181daecfebcSLaurent Pinchart .min_row_time = 690, 182daecfebcSLaurent Pinchart .min_hblank = MT9V034_HORIZONTAL_BLANKING_MIN, 183daecfebcSLaurent Pinchart .min_vblank = MT9V034_VERTICAL_BLANKING_MIN, 184daecfebcSLaurent Pinchart .max_vblank = MT9V034_VERTICAL_BLANKING_MAX, 185daecfebcSLaurent Pinchart .min_shutter = MT9V034_TOTAL_SHUTTER_WIDTH_MIN, 186daecfebcSLaurent Pinchart .max_shutter = MT9V034_TOTAL_SHUTTER_WIDTH_MAX, 187daecfebcSLaurent Pinchart .pclk_reg = MT9V034_PIXEL_CLOCK, 1880a466b60SLaurent Pinchart }, 1890a466b60SLaurent Pinchart }; 1900a466b60SLaurent Pinchart 191220ddc7fSLaurent Pinchart static const struct mt9v032_model_info mt9v032_models[] = { 192daecfebcSLaurent Pinchart [MT9V032_MODEL_V032_COLOR] = { 1930a466b60SLaurent Pinchart .data = &mt9v032_model_data[0], 194220ddc7fSLaurent Pinchart .color = true, 195220ddc7fSLaurent Pinchart }, 196daecfebcSLaurent Pinchart [MT9V032_MODEL_V032_MONO] = { 1970a466b60SLaurent Pinchart .data = &mt9v032_model_data[0], 198220ddc7fSLaurent Pinchart .color = false, 199220ddc7fSLaurent Pinchart }, 200daecfebcSLaurent Pinchart [MT9V032_MODEL_V034_COLOR] = { 201daecfebcSLaurent Pinchart .data = &mt9v032_model_data[1], 202daecfebcSLaurent Pinchart .color = true, 203daecfebcSLaurent Pinchart }, 204daecfebcSLaurent Pinchart [MT9V032_MODEL_V034_MONO] = { 205daecfebcSLaurent Pinchart .data = &mt9v032_model_data[1], 206daecfebcSLaurent Pinchart .color = false, 207daecfebcSLaurent Pinchart }, 208220ddc7fSLaurent Pinchart }; 209220ddc7fSLaurent Pinchart 210cb7a01acSMauro Carvalho Chehab struct mt9v032 { 211cb7a01acSMauro Carvalho Chehab struct v4l2_subdev subdev; 212cb7a01acSMauro Carvalho Chehab struct media_pad pad; 213cb7a01acSMauro Carvalho Chehab 214cb7a01acSMauro Carvalho Chehab struct v4l2_mbus_framefmt format; 215cb7a01acSMauro Carvalho Chehab struct v4l2_rect crop; 216637f005eSLaurent Pinchart unsigned int hratio; 217637f005eSLaurent Pinchart unsigned int vratio; 218cb7a01acSMauro Carvalho Chehab 219cb7a01acSMauro Carvalho Chehab struct v4l2_ctrl_handler ctrls; 220e9a50e4cSLaurent Pinchart struct { 221e9a50e4cSLaurent Pinchart struct v4l2_ctrl *link_freq; 222e9a50e4cSLaurent Pinchart struct v4l2_ctrl *pixel_rate; 223e9a50e4cSLaurent Pinchart }; 224cb7a01acSMauro Carvalho Chehab 225cb7a01acSMauro Carvalho Chehab struct mutex power_lock; 226cb7a01acSMauro Carvalho Chehab int power_count; 227cb7a01acSMauro Carvalho Chehab 2283300a8fdSLaurent Pinchart struct clk *clk; 2293300a8fdSLaurent Pinchart 230cb7a01acSMauro Carvalho Chehab struct mt9v032_platform_data *pdata; 231220ddc7fSLaurent Pinchart const struct mt9v032_model_info *model; 2320a466b60SLaurent Pinchart const struct mt9v032_model_version *version; 233e9a50e4cSLaurent Pinchart 234e9a50e4cSLaurent Pinchart u32 sysclk; 235cb7a01acSMauro Carvalho Chehab u16 chip_control; 236cb7a01acSMauro Carvalho Chehab u16 aec_agc; 2379ec670e2SLaurent Pinchart u16 hblank; 238b28d7017SLad, Prabhakar struct { 239b28d7017SLad, Prabhakar struct v4l2_ctrl *test_pattern; 240b28d7017SLad, Prabhakar struct v4l2_ctrl *test_pattern_color; 241b28d7017SLad, Prabhakar }; 242cb7a01acSMauro Carvalho Chehab }; 243cb7a01acSMauro Carvalho Chehab 244cb7a01acSMauro Carvalho Chehab static struct mt9v032 *to_mt9v032(struct v4l2_subdev *sd) 245cb7a01acSMauro Carvalho Chehab { 246cb7a01acSMauro Carvalho Chehab return container_of(sd, struct mt9v032, subdev); 247cb7a01acSMauro Carvalho Chehab } 248cb7a01acSMauro Carvalho Chehab 249cb7a01acSMauro Carvalho Chehab static int mt9v032_read(struct i2c_client *client, const u8 reg) 250cb7a01acSMauro Carvalho Chehab { 251cb7a01acSMauro Carvalho Chehab s32 data = i2c_smbus_read_word_swapped(client, reg); 252cb7a01acSMauro Carvalho Chehab dev_dbg(&client->dev, "%s: read 0x%04x from 0x%02x\n", __func__, 253cb7a01acSMauro Carvalho Chehab data, reg); 254cb7a01acSMauro Carvalho Chehab return data; 255cb7a01acSMauro Carvalho Chehab } 256cb7a01acSMauro Carvalho Chehab 257cb7a01acSMauro Carvalho Chehab static int mt9v032_write(struct i2c_client *client, const u8 reg, 258cb7a01acSMauro Carvalho Chehab const u16 data) 259cb7a01acSMauro Carvalho Chehab { 260cb7a01acSMauro Carvalho Chehab dev_dbg(&client->dev, "%s: writing 0x%04x to 0x%02x\n", __func__, 261cb7a01acSMauro Carvalho Chehab data, reg); 262cb7a01acSMauro Carvalho Chehab return i2c_smbus_write_word_swapped(client, reg, data); 263cb7a01acSMauro Carvalho Chehab } 264cb7a01acSMauro Carvalho Chehab 265cb7a01acSMauro Carvalho Chehab static int mt9v032_set_chip_control(struct mt9v032 *mt9v032, u16 clear, u16 set) 266cb7a01acSMauro Carvalho Chehab { 267cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); 268cb7a01acSMauro Carvalho Chehab u16 value = (mt9v032->chip_control & ~clear) | set; 269cb7a01acSMauro Carvalho Chehab int ret; 270cb7a01acSMauro Carvalho Chehab 271cb7a01acSMauro Carvalho Chehab ret = mt9v032_write(client, MT9V032_CHIP_CONTROL, value); 272cb7a01acSMauro Carvalho Chehab if (ret < 0) 273cb7a01acSMauro Carvalho Chehab return ret; 274cb7a01acSMauro Carvalho Chehab 275cb7a01acSMauro Carvalho Chehab mt9v032->chip_control = value; 276cb7a01acSMauro Carvalho Chehab return 0; 277cb7a01acSMauro Carvalho Chehab } 278cb7a01acSMauro Carvalho Chehab 279cb7a01acSMauro Carvalho Chehab static int 280cb7a01acSMauro Carvalho Chehab mt9v032_update_aec_agc(struct mt9v032 *mt9v032, u16 which, int enable) 281cb7a01acSMauro Carvalho Chehab { 282cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); 283cb7a01acSMauro Carvalho Chehab u16 value = mt9v032->aec_agc; 284cb7a01acSMauro Carvalho Chehab int ret; 285cb7a01acSMauro Carvalho Chehab 286cb7a01acSMauro Carvalho Chehab if (enable) 287cb7a01acSMauro Carvalho Chehab value |= which; 288cb7a01acSMauro Carvalho Chehab else 289cb7a01acSMauro Carvalho Chehab value &= ~which; 290cb7a01acSMauro Carvalho Chehab 291cb7a01acSMauro Carvalho Chehab ret = mt9v032_write(client, MT9V032_AEC_AGC_ENABLE, value); 292cb7a01acSMauro Carvalho Chehab if (ret < 0) 293cb7a01acSMauro Carvalho Chehab return ret; 294cb7a01acSMauro Carvalho Chehab 295cb7a01acSMauro Carvalho Chehab mt9v032->aec_agc = value; 296cb7a01acSMauro Carvalho Chehab return 0; 297cb7a01acSMauro Carvalho Chehab } 298cb7a01acSMauro Carvalho Chehab 2999ec670e2SLaurent Pinchart static int 3009ec670e2SLaurent Pinchart mt9v032_update_hblank(struct mt9v032 *mt9v032) 3019ec670e2SLaurent Pinchart { 3029ec670e2SLaurent Pinchart struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); 3039ec670e2SLaurent Pinchart struct v4l2_rect *crop = &mt9v032->crop; 304daecfebcSLaurent Pinchart unsigned int min_hblank = mt9v032->model->data->min_hblank; 3050a466b60SLaurent Pinchart unsigned int hblank; 3069ec670e2SLaurent Pinchart 307daecfebcSLaurent Pinchart if (mt9v032->version->version == MT9V034_CHIP_ID_REV1) 308daecfebcSLaurent Pinchart min_hblank += (mt9v032->hratio - 1) * 10; 309f17bc3f4SPhilipp Zabel min_hblank = max_t(int, mt9v032->model->data->min_row_time - crop->width, 310f17bc3f4SPhilipp Zabel min_hblank); 311daecfebcSLaurent Pinchart hblank = max_t(unsigned int, mt9v032->hblank, min_hblank); 312daecfebcSLaurent Pinchart 3130a466b60SLaurent Pinchart return mt9v032_write(client, MT9V032_HORIZONTAL_BLANKING, hblank); 3149ec670e2SLaurent Pinchart } 3159ec670e2SLaurent Pinchart 316cb7a01acSMauro Carvalho Chehab static int mt9v032_power_on(struct mt9v032 *mt9v032) 317cb7a01acSMauro Carvalho Chehab { 318cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); 319cb7a01acSMauro Carvalho Chehab int ret; 320cb7a01acSMauro Carvalho Chehab 32179019190SLad, Prabhakar ret = clk_set_rate(mt9v032->clk, mt9v032->sysclk); 32279019190SLad, Prabhakar if (ret < 0) 32379019190SLad, Prabhakar return ret; 32479019190SLad, Prabhakar 32579019190SLad, Prabhakar ret = clk_prepare_enable(mt9v032->clk); 32679019190SLad, Prabhakar if (ret) 32779019190SLad, Prabhakar return ret; 32879019190SLad, Prabhakar 329cb7a01acSMauro Carvalho Chehab udelay(1); 330cb7a01acSMauro Carvalho Chehab 331cb7a01acSMauro Carvalho Chehab /* Reset the chip and stop data read out */ 332cb7a01acSMauro Carvalho Chehab ret = mt9v032_write(client, MT9V032_RESET, 1); 333cb7a01acSMauro Carvalho Chehab if (ret < 0) 334cb7a01acSMauro Carvalho Chehab return ret; 335cb7a01acSMauro Carvalho Chehab 336cb7a01acSMauro Carvalho Chehab ret = mt9v032_write(client, MT9V032_RESET, 0); 337cb7a01acSMauro Carvalho Chehab if (ret < 0) 338cb7a01acSMauro Carvalho Chehab return ret; 339cb7a01acSMauro Carvalho Chehab 340cb7a01acSMauro Carvalho Chehab return mt9v032_write(client, MT9V032_CHIP_CONTROL, 0); 341cb7a01acSMauro Carvalho Chehab } 342cb7a01acSMauro Carvalho Chehab 343cb7a01acSMauro Carvalho Chehab static void mt9v032_power_off(struct mt9v032 *mt9v032) 344cb7a01acSMauro Carvalho Chehab { 3453300a8fdSLaurent Pinchart clk_disable_unprepare(mt9v032->clk); 346cb7a01acSMauro Carvalho Chehab } 347cb7a01acSMauro Carvalho Chehab 348cb7a01acSMauro Carvalho Chehab static int __mt9v032_set_power(struct mt9v032 *mt9v032, bool on) 349cb7a01acSMauro Carvalho Chehab { 350cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); 351cb7a01acSMauro Carvalho Chehab int ret; 352cb7a01acSMauro Carvalho Chehab 353cb7a01acSMauro Carvalho Chehab if (!on) { 354cb7a01acSMauro Carvalho Chehab mt9v032_power_off(mt9v032); 355cb7a01acSMauro Carvalho Chehab return 0; 356cb7a01acSMauro Carvalho Chehab } 357cb7a01acSMauro Carvalho Chehab 358cb7a01acSMauro Carvalho Chehab ret = mt9v032_power_on(mt9v032); 359cb7a01acSMauro Carvalho Chehab if (ret < 0) 360cb7a01acSMauro Carvalho Chehab return ret; 361cb7a01acSMauro Carvalho Chehab 362cb7a01acSMauro Carvalho Chehab /* Configure the pixel clock polarity */ 363cb7a01acSMauro Carvalho Chehab if (mt9v032->pdata && mt9v032->pdata->clk_pol) { 3640a466b60SLaurent Pinchart ret = mt9v032_write(client, mt9v032->model->data->pclk_reg, 365cb7a01acSMauro Carvalho Chehab MT9V032_PIXEL_CLOCK_INV_PXL_CLK); 366cb7a01acSMauro Carvalho Chehab if (ret < 0) 367cb7a01acSMauro Carvalho Chehab return ret; 368cb7a01acSMauro Carvalho Chehab } 369cb7a01acSMauro Carvalho Chehab 370cb7a01acSMauro Carvalho Chehab /* Disable the noise correction algorithm and restore the controls. */ 371cb7a01acSMauro Carvalho Chehab ret = mt9v032_write(client, MT9V032_ROW_NOISE_CORR_CONTROL, 0); 372cb7a01acSMauro Carvalho Chehab if (ret < 0) 373cb7a01acSMauro Carvalho Chehab return ret; 374cb7a01acSMauro Carvalho Chehab 375cb7a01acSMauro Carvalho Chehab return v4l2_ctrl_handler_setup(&mt9v032->ctrls); 376cb7a01acSMauro Carvalho Chehab } 377cb7a01acSMauro Carvalho Chehab 378cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------------- 379cb7a01acSMauro Carvalho Chehab * V4L2 subdev video operations 380cb7a01acSMauro Carvalho Chehab */ 381cb7a01acSMauro Carvalho Chehab 382cb7a01acSMauro Carvalho Chehab static struct v4l2_mbus_framefmt * 383cb7a01acSMauro Carvalho Chehab __mt9v032_get_pad_format(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh, 384cb7a01acSMauro Carvalho Chehab unsigned int pad, enum v4l2_subdev_format_whence which) 385cb7a01acSMauro Carvalho Chehab { 386cb7a01acSMauro Carvalho Chehab switch (which) { 387cb7a01acSMauro Carvalho Chehab case V4L2_SUBDEV_FORMAT_TRY: 388cb7a01acSMauro Carvalho Chehab return v4l2_subdev_get_try_format(fh, pad); 389cb7a01acSMauro Carvalho Chehab case V4L2_SUBDEV_FORMAT_ACTIVE: 390cb7a01acSMauro Carvalho Chehab return &mt9v032->format; 391cb7a01acSMauro Carvalho Chehab default: 392cb7a01acSMauro Carvalho Chehab return NULL; 393cb7a01acSMauro Carvalho Chehab } 394cb7a01acSMauro Carvalho Chehab } 395cb7a01acSMauro Carvalho Chehab 396cb7a01acSMauro Carvalho Chehab static struct v4l2_rect * 397cb7a01acSMauro Carvalho Chehab __mt9v032_get_pad_crop(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh, 398cb7a01acSMauro Carvalho Chehab unsigned int pad, enum v4l2_subdev_format_whence which) 399cb7a01acSMauro Carvalho Chehab { 400cb7a01acSMauro Carvalho Chehab switch (which) { 401cb7a01acSMauro Carvalho Chehab case V4L2_SUBDEV_FORMAT_TRY: 402cb7a01acSMauro Carvalho Chehab return v4l2_subdev_get_try_crop(fh, pad); 403cb7a01acSMauro Carvalho Chehab case V4L2_SUBDEV_FORMAT_ACTIVE: 404cb7a01acSMauro Carvalho Chehab return &mt9v032->crop; 405cb7a01acSMauro Carvalho Chehab default: 406cb7a01acSMauro Carvalho Chehab return NULL; 407cb7a01acSMauro Carvalho Chehab } 408cb7a01acSMauro Carvalho Chehab } 409cb7a01acSMauro Carvalho Chehab 410cb7a01acSMauro Carvalho Chehab static int mt9v032_s_stream(struct v4l2_subdev *subdev, int enable) 411cb7a01acSMauro Carvalho Chehab { 412cb7a01acSMauro Carvalho Chehab const u16 mode = MT9V032_CHIP_CONTROL_MASTER_MODE 413cb7a01acSMauro Carvalho Chehab | MT9V032_CHIP_CONTROL_DOUT_ENABLE 414cb7a01acSMauro Carvalho Chehab | MT9V032_CHIP_CONTROL_SEQUENTIAL; 415cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(subdev); 416cb7a01acSMauro Carvalho Chehab struct mt9v032 *mt9v032 = to_mt9v032(subdev); 417cb7a01acSMauro Carvalho Chehab struct v4l2_rect *crop = &mt9v032->crop; 418d131e54bSPhilipp Zabel unsigned int read_mode; 419637f005eSLaurent Pinchart unsigned int hbin; 420637f005eSLaurent Pinchart unsigned int vbin; 421cb7a01acSMauro Carvalho Chehab int ret; 422cb7a01acSMauro Carvalho Chehab 423cb7a01acSMauro Carvalho Chehab if (!enable) 424cb7a01acSMauro Carvalho Chehab return mt9v032_set_chip_control(mt9v032, mode, 0); 425cb7a01acSMauro Carvalho Chehab 426cb7a01acSMauro Carvalho Chehab /* Configure the window size and row/column bin */ 427637f005eSLaurent Pinchart hbin = fls(mt9v032->hratio) - 1; 428637f005eSLaurent Pinchart vbin = fls(mt9v032->vratio) - 1; 429d131e54bSPhilipp Zabel read_mode = mt9v032_read(client, MT9V032_READ_MODE); 430d131e54bSPhilipp Zabel if (read_mode < 0) 431d131e54bSPhilipp Zabel return read_mode; 432d131e54bSPhilipp Zabel read_mode &= MT9V032_READ_MODE_RESERVED; 433d131e54bSPhilipp Zabel read_mode |= hbin << MT9V032_READ_MODE_COLUMN_BIN_SHIFT | 434d131e54bSPhilipp Zabel vbin << MT9V032_READ_MODE_ROW_BIN_SHIFT; 435d131e54bSPhilipp Zabel ret = mt9v032_write(client, MT9V032_READ_MODE, read_mode); 436cb7a01acSMauro Carvalho Chehab if (ret < 0) 437cb7a01acSMauro Carvalho Chehab return ret; 438cb7a01acSMauro Carvalho Chehab 439cb7a01acSMauro Carvalho Chehab ret = mt9v032_write(client, MT9V032_COLUMN_START, crop->left); 440cb7a01acSMauro Carvalho Chehab if (ret < 0) 441cb7a01acSMauro Carvalho Chehab return ret; 442cb7a01acSMauro Carvalho Chehab 443cb7a01acSMauro Carvalho Chehab ret = mt9v032_write(client, MT9V032_ROW_START, crop->top); 444cb7a01acSMauro Carvalho Chehab if (ret < 0) 445cb7a01acSMauro Carvalho Chehab return ret; 446cb7a01acSMauro Carvalho Chehab 447cb7a01acSMauro Carvalho Chehab ret = mt9v032_write(client, MT9V032_WINDOW_WIDTH, crop->width); 448cb7a01acSMauro Carvalho Chehab if (ret < 0) 449cb7a01acSMauro Carvalho Chehab return ret; 450cb7a01acSMauro Carvalho Chehab 451cb7a01acSMauro Carvalho Chehab ret = mt9v032_write(client, MT9V032_WINDOW_HEIGHT, crop->height); 452cb7a01acSMauro Carvalho Chehab if (ret < 0) 453cb7a01acSMauro Carvalho Chehab return ret; 454cb7a01acSMauro Carvalho Chehab 4559ec670e2SLaurent Pinchart ret = mt9v032_update_hblank(mt9v032); 456cb7a01acSMauro Carvalho Chehab if (ret < 0) 457cb7a01acSMauro Carvalho Chehab return ret; 458cb7a01acSMauro Carvalho Chehab 459cb7a01acSMauro Carvalho Chehab /* Switch to master "normal" mode */ 460cb7a01acSMauro Carvalho Chehab return mt9v032_set_chip_control(mt9v032, 0, mode); 461cb7a01acSMauro Carvalho Chehab } 462cb7a01acSMauro Carvalho Chehab 463cb7a01acSMauro Carvalho Chehab static int mt9v032_enum_mbus_code(struct v4l2_subdev *subdev, 464cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_fh *fh, 465cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_mbus_code_enum *code) 466cb7a01acSMauro Carvalho Chehab { 467cb7a01acSMauro Carvalho Chehab if (code->index > 0) 468cb7a01acSMauro Carvalho Chehab return -EINVAL; 469cb7a01acSMauro Carvalho Chehab 470cb7a01acSMauro Carvalho Chehab code->code = V4L2_MBUS_FMT_SGRBG10_1X10; 471cb7a01acSMauro Carvalho Chehab return 0; 472cb7a01acSMauro Carvalho Chehab } 473cb7a01acSMauro Carvalho Chehab 474cb7a01acSMauro Carvalho Chehab static int mt9v032_enum_frame_size(struct v4l2_subdev *subdev, 475cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_fh *fh, 476cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_frame_size_enum *fse) 477cb7a01acSMauro Carvalho Chehab { 478637f005eSLaurent Pinchart if (fse->index >= 3 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10) 479cb7a01acSMauro Carvalho Chehab return -EINVAL; 480cb7a01acSMauro Carvalho Chehab 481637f005eSLaurent Pinchart fse->min_width = MT9V032_WINDOW_WIDTH_DEF / (1 << fse->index); 482cb7a01acSMauro Carvalho Chehab fse->max_width = fse->min_width; 483637f005eSLaurent Pinchart fse->min_height = MT9V032_WINDOW_HEIGHT_DEF / (1 << fse->index); 484cb7a01acSMauro Carvalho Chehab fse->max_height = fse->min_height; 485cb7a01acSMauro Carvalho Chehab 486cb7a01acSMauro Carvalho Chehab return 0; 487cb7a01acSMauro Carvalho Chehab } 488cb7a01acSMauro Carvalho Chehab 489cb7a01acSMauro Carvalho Chehab static int mt9v032_get_format(struct v4l2_subdev *subdev, 490cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_fh *fh, 491cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_format *format) 492cb7a01acSMauro Carvalho Chehab { 493cb7a01acSMauro Carvalho Chehab struct mt9v032 *mt9v032 = to_mt9v032(subdev); 494cb7a01acSMauro Carvalho Chehab 495cb7a01acSMauro Carvalho Chehab format->format = *__mt9v032_get_pad_format(mt9v032, fh, format->pad, 496cb7a01acSMauro Carvalho Chehab format->which); 497cb7a01acSMauro Carvalho Chehab return 0; 498cb7a01acSMauro Carvalho Chehab } 499cb7a01acSMauro Carvalho Chehab 500637f005eSLaurent Pinchart static void mt9v032_configure_pixel_rate(struct mt9v032 *mt9v032) 50141a33a00SSakari Ailus { 50241a33a00SSakari Ailus struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); 50341a33a00SSakari Ailus int ret; 50441a33a00SSakari Ailus 505e9a50e4cSLaurent Pinchart ret = v4l2_ctrl_s_ctrl_int64(mt9v032->pixel_rate, 506637f005eSLaurent Pinchart mt9v032->sysclk / mt9v032->hratio); 50741a33a00SSakari Ailus if (ret < 0) 50841a33a00SSakari Ailus dev_warn(&client->dev, "failed to set pixel rate (%d)\n", ret); 50941a33a00SSakari Ailus } 51041a33a00SSakari Ailus 511637f005eSLaurent Pinchart static unsigned int mt9v032_calc_ratio(unsigned int input, unsigned int output) 512637f005eSLaurent Pinchart { 513637f005eSLaurent Pinchart /* Compute the power-of-two binning factor closest to the input size to 514637f005eSLaurent Pinchart * output size ratio. Given that the output size is bounded by input/4 515637f005eSLaurent Pinchart * and input, a generic implementation would be an ineffective luxury. 516637f005eSLaurent Pinchart */ 517637f005eSLaurent Pinchart if (output * 3 > input * 2) 518637f005eSLaurent Pinchart return 1; 519637f005eSLaurent Pinchart if (output * 3 > input) 520637f005eSLaurent Pinchart return 2; 521637f005eSLaurent Pinchart return 4; 522637f005eSLaurent Pinchart } 523637f005eSLaurent Pinchart 524cb7a01acSMauro Carvalho Chehab static int mt9v032_set_format(struct v4l2_subdev *subdev, 525cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_fh *fh, 526cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_format *format) 527cb7a01acSMauro Carvalho Chehab { 528cb7a01acSMauro Carvalho Chehab struct mt9v032 *mt9v032 = to_mt9v032(subdev); 529cb7a01acSMauro Carvalho Chehab struct v4l2_mbus_framefmt *__format; 530cb7a01acSMauro Carvalho Chehab struct v4l2_rect *__crop; 531cb7a01acSMauro Carvalho Chehab unsigned int width; 532cb7a01acSMauro Carvalho Chehab unsigned int height; 533cb7a01acSMauro Carvalho Chehab unsigned int hratio; 534cb7a01acSMauro Carvalho Chehab unsigned int vratio; 535cb7a01acSMauro Carvalho Chehab 536cb7a01acSMauro Carvalho Chehab __crop = __mt9v032_get_pad_crop(mt9v032, fh, format->pad, 537cb7a01acSMauro Carvalho Chehab format->which); 538cb7a01acSMauro Carvalho Chehab 539cb7a01acSMauro Carvalho Chehab /* Clamp the width and height to avoid dividing by zero. */ 540f90580caSRicardo Ribalda width = clamp(ALIGN(format->format.width, 2), 541f90580caSRicardo Ribalda max_t(unsigned int, __crop->width / 4, 542f90580caSRicardo Ribalda MT9V032_WINDOW_WIDTH_MIN), 543cb7a01acSMauro Carvalho Chehab __crop->width); 544f90580caSRicardo Ribalda height = clamp(ALIGN(format->format.height, 2), 545f90580caSRicardo Ribalda max_t(unsigned int, __crop->height / 4, 546f90580caSRicardo Ribalda MT9V032_WINDOW_HEIGHT_MIN), 547cb7a01acSMauro Carvalho Chehab __crop->height); 548cb7a01acSMauro Carvalho Chehab 549637f005eSLaurent Pinchart hratio = mt9v032_calc_ratio(__crop->width, width); 550637f005eSLaurent Pinchart vratio = mt9v032_calc_ratio(__crop->height, height); 551cb7a01acSMauro Carvalho Chehab 552cb7a01acSMauro Carvalho Chehab __format = __mt9v032_get_pad_format(mt9v032, fh, format->pad, 553cb7a01acSMauro Carvalho Chehab format->which); 554cb7a01acSMauro Carvalho Chehab __format->width = __crop->width / hratio; 555cb7a01acSMauro Carvalho Chehab __format->height = __crop->height / vratio; 556637f005eSLaurent Pinchart 557637f005eSLaurent Pinchart if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { 558637f005eSLaurent Pinchart mt9v032->hratio = hratio; 559637f005eSLaurent Pinchart mt9v032->vratio = vratio; 560637f005eSLaurent Pinchart mt9v032_configure_pixel_rate(mt9v032); 561637f005eSLaurent Pinchart } 562cb7a01acSMauro Carvalho Chehab 563cb7a01acSMauro Carvalho Chehab format->format = *__format; 564cb7a01acSMauro Carvalho Chehab 565cb7a01acSMauro Carvalho Chehab return 0; 566cb7a01acSMauro Carvalho Chehab } 567cb7a01acSMauro Carvalho Chehab 568cb7a01acSMauro Carvalho Chehab static int mt9v032_get_crop(struct v4l2_subdev *subdev, 569cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_fh *fh, 570cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_crop *crop) 571cb7a01acSMauro Carvalho Chehab { 572cb7a01acSMauro Carvalho Chehab struct mt9v032 *mt9v032 = to_mt9v032(subdev); 573cb7a01acSMauro Carvalho Chehab 574cb7a01acSMauro Carvalho Chehab crop->rect = *__mt9v032_get_pad_crop(mt9v032, fh, crop->pad, 575cb7a01acSMauro Carvalho Chehab crop->which); 576cb7a01acSMauro Carvalho Chehab return 0; 577cb7a01acSMauro Carvalho Chehab } 578cb7a01acSMauro Carvalho Chehab 579cb7a01acSMauro Carvalho Chehab static int mt9v032_set_crop(struct v4l2_subdev *subdev, 580cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_fh *fh, 581cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_crop *crop) 582cb7a01acSMauro Carvalho Chehab { 583cb7a01acSMauro Carvalho Chehab struct mt9v032 *mt9v032 = to_mt9v032(subdev); 584cb7a01acSMauro Carvalho Chehab struct v4l2_mbus_framefmt *__format; 585cb7a01acSMauro Carvalho Chehab struct v4l2_rect *__crop; 586cb7a01acSMauro Carvalho Chehab struct v4l2_rect rect; 587cb7a01acSMauro Carvalho Chehab 588cb7a01acSMauro Carvalho Chehab /* Clamp the crop rectangle boundaries and align them to a non multiple 589cb7a01acSMauro Carvalho Chehab * of 2 pixels to ensure a GRBG Bayer pattern. 590cb7a01acSMauro Carvalho Chehab */ 591cb7a01acSMauro Carvalho Chehab rect.left = clamp(ALIGN(crop->rect.left + 1, 2) - 1, 592cb7a01acSMauro Carvalho Chehab MT9V032_COLUMN_START_MIN, 593cb7a01acSMauro Carvalho Chehab MT9V032_COLUMN_START_MAX); 594cb7a01acSMauro Carvalho Chehab rect.top = clamp(ALIGN(crop->rect.top + 1, 2) - 1, 595cb7a01acSMauro Carvalho Chehab MT9V032_ROW_START_MIN, 596cb7a01acSMauro Carvalho Chehab MT9V032_ROW_START_MAX); 597f90580caSRicardo Ribalda rect.width = clamp_t(unsigned int, ALIGN(crop->rect.width, 2), 598cb7a01acSMauro Carvalho Chehab MT9V032_WINDOW_WIDTH_MIN, 599cb7a01acSMauro Carvalho Chehab MT9V032_WINDOW_WIDTH_MAX); 600f90580caSRicardo Ribalda rect.height = clamp_t(unsigned int, ALIGN(crop->rect.height, 2), 601cb7a01acSMauro Carvalho Chehab MT9V032_WINDOW_HEIGHT_MIN, 602cb7a01acSMauro Carvalho Chehab MT9V032_WINDOW_HEIGHT_MAX); 603cb7a01acSMauro Carvalho Chehab 604f90580caSRicardo Ribalda rect.width = min_t(unsigned int, 605f90580caSRicardo Ribalda rect.width, MT9V032_PIXEL_ARRAY_WIDTH - rect.left); 606f90580caSRicardo Ribalda rect.height = min_t(unsigned int, 607f90580caSRicardo Ribalda rect.height, MT9V032_PIXEL_ARRAY_HEIGHT - rect.top); 608cb7a01acSMauro Carvalho Chehab 609cb7a01acSMauro Carvalho Chehab __crop = __mt9v032_get_pad_crop(mt9v032, fh, crop->pad, crop->which); 610cb7a01acSMauro Carvalho Chehab 611cb7a01acSMauro Carvalho Chehab if (rect.width != __crop->width || rect.height != __crop->height) { 612cb7a01acSMauro Carvalho Chehab /* Reset the output image size if the crop rectangle size has 613cb7a01acSMauro Carvalho Chehab * been modified. 614cb7a01acSMauro Carvalho Chehab */ 615cb7a01acSMauro Carvalho Chehab __format = __mt9v032_get_pad_format(mt9v032, fh, crop->pad, 616cb7a01acSMauro Carvalho Chehab crop->which); 617cb7a01acSMauro Carvalho Chehab __format->width = rect.width; 618cb7a01acSMauro Carvalho Chehab __format->height = rect.height; 619637f005eSLaurent Pinchart if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { 620637f005eSLaurent Pinchart mt9v032->hratio = 1; 621637f005eSLaurent Pinchart mt9v032->vratio = 1; 622637f005eSLaurent Pinchart mt9v032_configure_pixel_rate(mt9v032); 623637f005eSLaurent Pinchart } 624cb7a01acSMauro Carvalho Chehab } 625cb7a01acSMauro Carvalho Chehab 626cb7a01acSMauro Carvalho Chehab *__crop = rect; 627cb7a01acSMauro Carvalho Chehab crop->rect = rect; 628cb7a01acSMauro Carvalho Chehab 629cb7a01acSMauro Carvalho Chehab return 0; 630cb7a01acSMauro Carvalho Chehab } 631cb7a01acSMauro Carvalho Chehab 632cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------------- 633cb7a01acSMauro Carvalho Chehab * V4L2 subdev control operations 634cb7a01acSMauro Carvalho Chehab */ 635cb7a01acSMauro Carvalho Chehab 636b28d7017SLad, Prabhakar #define V4L2_CID_TEST_PATTERN_COLOR (V4L2_CID_USER_BASE | 0x1001) 637cb7a01acSMauro Carvalho Chehab 638cb7a01acSMauro Carvalho Chehab static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl) 639cb7a01acSMauro Carvalho Chehab { 640cb7a01acSMauro Carvalho Chehab struct mt9v032 *mt9v032 = 641cb7a01acSMauro Carvalho Chehab container_of(ctrl->handler, struct mt9v032, ctrls); 642cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); 643e9a50e4cSLaurent Pinchart u32 freq; 644cb7a01acSMauro Carvalho Chehab u16 data; 645cb7a01acSMauro Carvalho Chehab 646cb7a01acSMauro Carvalho Chehab switch (ctrl->id) { 647cb7a01acSMauro Carvalho Chehab case V4L2_CID_AUTOGAIN: 648cb7a01acSMauro Carvalho Chehab return mt9v032_update_aec_agc(mt9v032, MT9V032_AGC_ENABLE, 649cb7a01acSMauro Carvalho Chehab ctrl->val); 650cb7a01acSMauro Carvalho Chehab 651cb7a01acSMauro Carvalho Chehab case V4L2_CID_GAIN: 652cb7a01acSMauro Carvalho Chehab return mt9v032_write(client, MT9V032_ANALOG_GAIN, ctrl->val); 653cb7a01acSMauro Carvalho Chehab 654cb7a01acSMauro Carvalho Chehab case V4L2_CID_EXPOSURE_AUTO: 655cb7a01acSMauro Carvalho Chehab return mt9v032_update_aec_agc(mt9v032, MT9V032_AEC_ENABLE, 656cb7a01acSMauro Carvalho Chehab !ctrl->val); 657cb7a01acSMauro Carvalho Chehab 658cb7a01acSMauro Carvalho Chehab case V4L2_CID_EXPOSURE: 659cb7a01acSMauro Carvalho Chehab return mt9v032_write(client, MT9V032_TOTAL_SHUTTER_WIDTH, 660cb7a01acSMauro Carvalho Chehab ctrl->val); 661cb7a01acSMauro Carvalho Chehab 6629ec670e2SLaurent Pinchart case V4L2_CID_HBLANK: 6639ec670e2SLaurent Pinchart mt9v032->hblank = ctrl->val; 6649ec670e2SLaurent Pinchart return mt9v032_update_hblank(mt9v032); 6659ec670e2SLaurent Pinchart 6669ec670e2SLaurent Pinchart case V4L2_CID_VBLANK: 6679ec670e2SLaurent Pinchart return mt9v032_write(client, MT9V032_VERTICAL_BLANKING, 6689ec670e2SLaurent Pinchart ctrl->val); 6699ec670e2SLaurent Pinchart 670e9a50e4cSLaurent Pinchart case V4L2_CID_PIXEL_RATE: 671e9a50e4cSLaurent Pinchart case V4L2_CID_LINK_FREQ: 672e9a50e4cSLaurent Pinchart if (mt9v032->link_freq == NULL) 673e9a50e4cSLaurent Pinchart break; 674e9a50e4cSLaurent Pinchart 675e9a50e4cSLaurent Pinchart freq = mt9v032->pdata->link_freqs[mt9v032->link_freq->val]; 6762a9ec373SHans Verkuil *mt9v032->pixel_rate->p_new.p_s64 = freq; 677e9a50e4cSLaurent Pinchart mt9v032->sysclk = freq; 678e9a50e4cSLaurent Pinchart break; 679e9a50e4cSLaurent Pinchart 680cb7a01acSMauro Carvalho Chehab case V4L2_CID_TEST_PATTERN: 681b28d7017SLad, Prabhakar switch (mt9v032->test_pattern->val) { 682cb7a01acSMauro Carvalho Chehab case 0: 683cb7a01acSMauro Carvalho Chehab data = 0; 684cb7a01acSMauro Carvalho Chehab break; 685cb7a01acSMauro Carvalho Chehab case 1: 686cb7a01acSMauro Carvalho Chehab data = MT9V032_TEST_PATTERN_GRAY_VERTICAL 687cb7a01acSMauro Carvalho Chehab | MT9V032_TEST_PATTERN_ENABLE; 688cb7a01acSMauro Carvalho Chehab break; 689cb7a01acSMauro Carvalho Chehab case 2: 690cb7a01acSMauro Carvalho Chehab data = MT9V032_TEST_PATTERN_GRAY_HORIZONTAL 691cb7a01acSMauro Carvalho Chehab | MT9V032_TEST_PATTERN_ENABLE; 692cb7a01acSMauro Carvalho Chehab break; 693cb7a01acSMauro Carvalho Chehab case 3: 694cb7a01acSMauro Carvalho Chehab data = MT9V032_TEST_PATTERN_GRAY_DIAGONAL 695cb7a01acSMauro Carvalho Chehab | MT9V032_TEST_PATTERN_ENABLE; 696cb7a01acSMauro Carvalho Chehab break; 697cb7a01acSMauro Carvalho Chehab default: 698b28d7017SLad, Prabhakar data = (mt9v032->test_pattern_color->val << 699b28d7017SLad, Prabhakar MT9V032_TEST_PATTERN_DATA_SHIFT) 700cb7a01acSMauro Carvalho Chehab | MT9V032_TEST_PATTERN_USE_DATA 701cb7a01acSMauro Carvalho Chehab | MT9V032_TEST_PATTERN_ENABLE 702cb7a01acSMauro Carvalho Chehab | MT9V032_TEST_PATTERN_FLIP; 703cb7a01acSMauro Carvalho Chehab break; 704cb7a01acSMauro Carvalho Chehab } 705cb7a01acSMauro Carvalho Chehab return mt9v032_write(client, MT9V032_TEST_PATTERN, data); 706cb7a01acSMauro Carvalho Chehab } 707cb7a01acSMauro Carvalho Chehab 708cb7a01acSMauro Carvalho Chehab return 0; 709cb7a01acSMauro Carvalho Chehab } 710cb7a01acSMauro Carvalho Chehab 711cb7a01acSMauro Carvalho Chehab static struct v4l2_ctrl_ops mt9v032_ctrl_ops = { 712cb7a01acSMauro Carvalho Chehab .s_ctrl = mt9v032_s_ctrl, 713cb7a01acSMauro Carvalho Chehab }; 714cb7a01acSMauro Carvalho Chehab 715b28d7017SLad, Prabhakar static const char * const mt9v032_test_pattern_menu[] = { 716b28d7017SLad, Prabhakar "Disabled", 717b28d7017SLad, Prabhakar "Gray Vertical Shade", 718b28d7017SLad, Prabhakar "Gray Horizontal Shade", 719b28d7017SLad, Prabhakar "Gray Diagonal Shade", 720b28d7017SLad, Prabhakar "Plain", 721b28d7017SLad, Prabhakar }; 722b28d7017SLad, Prabhakar 723b28d7017SLad, Prabhakar static const struct v4l2_ctrl_config mt9v032_test_pattern_color = { 724cb7a01acSMauro Carvalho Chehab .ops = &mt9v032_ctrl_ops, 725b28d7017SLad, Prabhakar .id = V4L2_CID_TEST_PATTERN_COLOR, 726cb7a01acSMauro Carvalho Chehab .type = V4L2_CTRL_TYPE_INTEGER, 727b28d7017SLad, Prabhakar .name = "Test Pattern Color", 728cb7a01acSMauro Carvalho Chehab .min = 0, 729cb7a01acSMauro Carvalho Chehab .max = 1023, 730cb7a01acSMauro Carvalho Chehab .step = 1, 731cb7a01acSMauro Carvalho Chehab .def = 0, 732cb7a01acSMauro Carvalho Chehab .flags = 0, 733cb7a01acSMauro Carvalho Chehab }; 734cb7a01acSMauro Carvalho Chehab 735cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------------- 736cb7a01acSMauro Carvalho Chehab * V4L2 subdev core operations 737cb7a01acSMauro Carvalho Chehab */ 738cb7a01acSMauro Carvalho Chehab 739cb7a01acSMauro Carvalho Chehab static int mt9v032_set_power(struct v4l2_subdev *subdev, int on) 740cb7a01acSMauro Carvalho Chehab { 741cb7a01acSMauro Carvalho Chehab struct mt9v032 *mt9v032 = to_mt9v032(subdev); 742cb7a01acSMauro Carvalho Chehab int ret = 0; 743cb7a01acSMauro Carvalho Chehab 744cb7a01acSMauro Carvalho Chehab mutex_lock(&mt9v032->power_lock); 745cb7a01acSMauro Carvalho Chehab 746cb7a01acSMauro Carvalho Chehab /* If the power count is modified from 0 to != 0 or from != 0 to 0, 747cb7a01acSMauro Carvalho Chehab * update the power state. 748cb7a01acSMauro Carvalho Chehab */ 749cb7a01acSMauro Carvalho Chehab if (mt9v032->power_count == !on) { 750cb7a01acSMauro Carvalho Chehab ret = __mt9v032_set_power(mt9v032, !!on); 751cb7a01acSMauro Carvalho Chehab if (ret < 0) 752cb7a01acSMauro Carvalho Chehab goto done; 753cb7a01acSMauro Carvalho Chehab } 754cb7a01acSMauro Carvalho Chehab 755cb7a01acSMauro Carvalho Chehab /* Update the power count. */ 756cb7a01acSMauro Carvalho Chehab mt9v032->power_count += on ? 1 : -1; 757cb7a01acSMauro Carvalho Chehab WARN_ON(mt9v032->power_count < 0); 758cb7a01acSMauro Carvalho Chehab 759cb7a01acSMauro Carvalho Chehab done: 760cb7a01acSMauro Carvalho Chehab mutex_unlock(&mt9v032->power_lock); 761cb7a01acSMauro Carvalho Chehab return ret; 762cb7a01acSMauro Carvalho Chehab } 763cb7a01acSMauro Carvalho Chehab 764cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------------- 765cb7a01acSMauro Carvalho Chehab * V4L2 subdev internal operations 766cb7a01acSMauro Carvalho Chehab */ 767cb7a01acSMauro Carvalho Chehab 768cb7a01acSMauro Carvalho Chehab static int mt9v032_registered(struct v4l2_subdev *subdev) 769cb7a01acSMauro Carvalho Chehab { 770cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(subdev); 771cb7a01acSMauro Carvalho Chehab struct mt9v032 *mt9v032 = to_mt9v032(subdev); 7720a466b60SLaurent Pinchart unsigned int i; 7730a466b60SLaurent Pinchart s32 version; 774cb7a01acSMauro Carvalho Chehab int ret; 775cb7a01acSMauro Carvalho Chehab 776cb7a01acSMauro Carvalho Chehab dev_info(&client->dev, "Probing MT9V032 at address 0x%02x\n", 777cb7a01acSMauro Carvalho Chehab client->addr); 778cb7a01acSMauro Carvalho Chehab 779cb7a01acSMauro Carvalho Chehab ret = mt9v032_power_on(mt9v032); 780cb7a01acSMauro Carvalho Chehab if (ret < 0) { 781cb7a01acSMauro Carvalho Chehab dev_err(&client->dev, "MT9V032 power up failed\n"); 782cb7a01acSMauro Carvalho Chehab return ret; 783cb7a01acSMauro Carvalho Chehab } 784cb7a01acSMauro Carvalho Chehab 785cb7a01acSMauro Carvalho Chehab /* Read and check the sensor version */ 7860a466b60SLaurent Pinchart version = mt9v032_read(client, MT9V032_CHIP_VERSION); 7870a466b60SLaurent Pinchart if (version < 0) { 7880a466b60SLaurent Pinchart dev_err(&client->dev, "Failed reading chip version\n"); 7890a466b60SLaurent Pinchart return version; 7900a466b60SLaurent Pinchart } 7910a466b60SLaurent Pinchart 7920a466b60SLaurent Pinchart for (i = 0; i < ARRAY_SIZE(mt9v032_versions); ++i) { 7930a466b60SLaurent Pinchart if (mt9v032_versions[i].version == version) { 7940a466b60SLaurent Pinchart mt9v032->version = &mt9v032_versions[i]; 7950a466b60SLaurent Pinchart break; 7960a466b60SLaurent Pinchart } 7970a466b60SLaurent Pinchart } 7980a466b60SLaurent Pinchart 7990a466b60SLaurent Pinchart if (mt9v032->version == NULL) { 8000a466b60SLaurent Pinchart dev_err(&client->dev, "Unsupported chip version 0x%04x\n", 8010a466b60SLaurent Pinchart version); 802cb7a01acSMauro Carvalho Chehab return -ENODEV; 803cb7a01acSMauro Carvalho Chehab } 804cb7a01acSMauro Carvalho Chehab 805cb7a01acSMauro Carvalho Chehab mt9v032_power_off(mt9v032); 806cb7a01acSMauro Carvalho Chehab 8070a466b60SLaurent Pinchart dev_info(&client->dev, "%s detected at address 0x%02x\n", 8080a466b60SLaurent Pinchart mt9v032->version->name, client->addr); 809cb7a01acSMauro Carvalho Chehab 810637f005eSLaurent Pinchart mt9v032_configure_pixel_rate(mt9v032); 81141a33a00SSakari Ailus 812cb7a01acSMauro Carvalho Chehab return ret; 813cb7a01acSMauro Carvalho Chehab } 814cb7a01acSMauro Carvalho Chehab 815cb7a01acSMauro Carvalho Chehab static int mt9v032_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) 816cb7a01acSMauro Carvalho Chehab { 817220ddc7fSLaurent Pinchart struct mt9v032 *mt9v032 = to_mt9v032(subdev); 818cb7a01acSMauro Carvalho Chehab struct v4l2_mbus_framefmt *format; 819cb7a01acSMauro Carvalho Chehab struct v4l2_rect *crop; 820cb7a01acSMauro Carvalho Chehab 821cb7a01acSMauro Carvalho Chehab crop = v4l2_subdev_get_try_crop(fh, 0); 822cb7a01acSMauro Carvalho Chehab crop->left = MT9V032_COLUMN_START_DEF; 823cb7a01acSMauro Carvalho Chehab crop->top = MT9V032_ROW_START_DEF; 824cb7a01acSMauro Carvalho Chehab crop->width = MT9V032_WINDOW_WIDTH_DEF; 825cb7a01acSMauro Carvalho Chehab crop->height = MT9V032_WINDOW_HEIGHT_DEF; 826cb7a01acSMauro Carvalho Chehab 827cb7a01acSMauro Carvalho Chehab format = v4l2_subdev_get_try_format(fh, 0); 828220ddc7fSLaurent Pinchart 829220ddc7fSLaurent Pinchart if (mt9v032->model->color) 830cb7a01acSMauro Carvalho Chehab format->code = V4L2_MBUS_FMT_SGRBG10_1X10; 831220ddc7fSLaurent Pinchart else 832220ddc7fSLaurent Pinchart format->code = V4L2_MBUS_FMT_Y10_1X10; 833220ddc7fSLaurent Pinchart 834cb7a01acSMauro Carvalho Chehab format->width = MT9V032_WINDOW_WIDTH_DEF; 835cb7a01acSMauro Carvalho Chehab format->height = MT9V032_WINDOW_HEIGHT_DEF; 836cb7a01acSMauro Carvalho Chehab format->field = V4L2_FIELD_NONE; 837cb7a01acSMauro Carvalho Chehab format->colorspace = V4L2_COLORSPACE_SRGB; 838cb7a01acSMauro Carvalho Chehab 839cb7a01acSMauro Carvalho Chehab return mt9v032_set_power(subdev, 1); 840cb7a01acSMauro Carvalho Chehab } 841cb7a01acSMauro Carvalho Chehab 842cb7a01acSMauro Carvalho Chehab static int mt9v032_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) 843cb7a01acSMauro Carvalho Chehab { 844cb7a01acSMauro Carvalho Chehab return mt9v032_set_power(subdev, 0); 845cb7a01acSMauro Carvalho Chehab } 846cb7a01acSMauro Carvalho Chehab 847cb7a01acSMauro Carvalho Chehab static struct v4l2_subdev_core_ops mt9v032_subdev_core_ops = { 848cb7a01acSMauro Carvalho Chehab .s_power = mt9v032_set_power, 849cb7a01acSMauro Carvalho Chehab }; 850cb7a01acSMauro Carvalho Chehab 851cb7a01acSMauro Carvalho Chehab static struct v4l2_subdev_video_ops mt9v032_subdev_video_ops = { 852cb7a01acSMauro Carvalho Chehab .s_stream = mt9v032_s_stream, 853cb7a01acSMauro Carvalho Chehab }; 854cb7a01acSMauro Carvalho Chehab 855cb7a01acSMauro Carvalho Chehab static struct v4l2_subdev_pad_ops mt9v032_subdev_pad_ops = { 856cb7a01acSMauro Carvalho Chehab .enum_mbus_code = mt9v032_enum_mbus_code, 857cb7a01acSMauro Carvalho Chehab .enum_frame_size = mt9v032_enum_frame_size, 858cb7a01acSMauro Carvalho Chehab .get_fmt = mt9v032_get_format, 859cb7a01acSMauro Carvalho Chehab .set_fmt = mt9v032_set_format, 860cb7a01acSMauro Carvalho Chehab .get_crop = mt9v032_get_crop, 861cb7a01acSMauro Carvalho Chehab .set_crop = mt9v032_set_crop, 862cb7a01acSMauro Carvalho Chehab }; 863cb7a01acSMauro Carvalho Chehab 864cb7a01acSMauro Carvalho Chehab static struct v4l2_subdev_ops mt9v032_subdev_ops = { 865cb7a01acSMauro Carvalho Chehab .core = &mt9v032_subdev_core_ops, 866cb7a01acSMauro Carvalho Chehab .video = &mt9v032_subdev_video_ops, 867cb7a01acSMauro Carvalho Chehab .pad = &mt9v032_subdev_pad_ops, 868cb7a01acSMauro Carvalho Chehab }; 869cb7a01acSMauro Carvalho Chehab 870cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_internal_ops mt9v032_subdev_internal_ops = { 871cb7a01acSMauro Carvalho Chehab .registered = mt9v032_registered, 872cb7a01acSMauro Carvalho Chehab .open = mt9v032_open, 873cb7a01acSMauro Carvalho Chehab .close = mt9v032_close, 874cb7a01acSMauro Carvalho Chehab }; 875cb7a01acSMauro Carvalho Chehab 876cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------------- 877cb7a01acSMauro Carvalho Chehab * Driver initialization and probing 878cb7a01acSMauro Carvalho Chehab */ 879cb7a01acSMauro Carvalho Chehab 880cb7a01acSMauro Carvalho Chehab static int mt9v032_probe(struct i2c_client *client, 881cb7a01acSMauro Carvalho Chehab const struct i2c_device_id *did) 882cb7a01acSMauro Carvalho Chehab { 883e9a50e4cSLaurent Pinchart struct mt9v032_platform_data *pdata = client->dev.platform_data; 884cb7a01acSMauro Carvalho Chehab struct mt9v032 *mt9v032; 885cb7a01acSMauro Carvalho Chehab unsigned int i; 886cb7a01acSMauro Carvalho Chehab int ret; 887cb7a01acSMauro Carvalho Chehab 888cb7a01acSMauro Carvalho Chehab if (!i2c_check_functionality(client->adapter, 889cb7a01acSMauro Carvalho Chehab I2C_FUNC_SMBUS_WORD_DATA)) { 890cb7a01acSMauro Carvalho Chehab dev_warn(&client->adapter->dev, 891cb7a01acSMauro Carvalho Chehab "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); 892cb7a01acSMauro Carvalho Chehab return -EIO; 893cb7a01acSMauro Carvalho Chehab } 894cb7a01acSMauro Carvalho Chehab 895c02b211dSLaurent Pinchart mt9v032 = devm_kzalloc(&client->dev, sizeof(*mt9v032), GFP_KERNEL); 896cb7a01acSMauro Carvalho Chehab if (!mt9v032) 897cb7a01acSMauro Carvalho Chehab return -ENOMEM; 898cb7a01acSMauro Carvalho Chehab 8993300a8fdSLaurent Pinchart mt9v032->clk = devm_clk_get(&client->dev, NULL); 9003300a8fdSLaurent Pinchart if (IS_ERR(mt9v032->clk)) 9013300a8fdSLaurent Pinchart return PTR_ERR(mt9v032->clk); 9023300a8fdSLaurent Pinchart 903cb7a01acSMauro Carvalho Chehab mutex_init(&mt9v032->power_lock); 904e9a50e4cSLaurent Pinchart mt9v032->pdata = pdata; 905220ddc7fSLaurent Pinchart mt9v032->model = (const void *)did->driver_data; 906cb7a01acSMauro Carvalho Chehab 907b28d7017SLad, Prabhakar v4l2_ctrl_handler_init(&mt9v032->ctrls, 10); 908cb7a01acSMauro Carvalho Chehab 909cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, 910cb7a01acSMauro Carvalho Chehab V4L2_CID_AUTOGAIN, 0, 1, 1, 1); 911cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, 912cb7a01acSMauro Carvalho Chehab V4L2_CID_GAIN, MT9V032_ANALOG_GAIN_MIN, 913cb7a01acSMauro Carvalho Chehab MT9V032_ANALOG_GAIN_MAX, 1, MT9V032_ANALOG_GAIN_DEF); 914cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std_menu(&mt9v032->ctrls, &mt9v032_ctrl_ops, 915cb7a01acSMauro Carvalho Chehab V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0, 916cb7a01acSMauro Carvalho Chehab V4L2_EXPOSURE_AUTO); 917cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, 9180a466b60SLaurent Pinchart V4L2_CID_EXPOSURE, mt9v032->model->data->min_shutter, 9190a466b60SLaurent Pinchart mt9v032->model->data->max_shutter, 1, 920cb7a01acSMauro Carvalho Chehab MT9V032_TOTAL_SHUTTER_WIDTH_DEF); 9219ec670e2SLaurent Pinchart v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, 9220a466b60SLaurent Pinchart V4L2_CID_HBLANK, mt9v032->model->data->min_hblank, 9239ec670e2SLaurent Pinchart MT9V032_HORIZONTAL_BLANKING_MAX, 1, 9249ec670e2SLaurent Pinchart MT9V032_HORIZONTAL_BLANKING_DEF); 9259ec670e2SLaurent Pinchart v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, 9260a466b60SLaurent Pinchart V4L2_CID_VBLANK, mt9v032->model->data->min_vblank, 9270a466b60SLaurent Pinchart mt9v032->model->data->max_vblank, 1, 9289ec670e2SLaurent Pinchart MT9V032_VERTICAL_BLANKING_DEF); 929b28d7017SLad, Prabhakar mt9v032->test_pattern = v4l2_ctrl_new_std_menu_items(&mt9v032->ctrls, 930b28d7017SLad, Prabhakar &mt9v032_ctrl_ops, V4L2_CID_TEST_PATTERN, 931b28d7017SLad, Prabhakar ARRAY_SIZE(mt9v032_test_pattern_menu) - 1, 0, 0, 932b28d7017SLad, Prabhakar mt9v032_test_pattern_menu); 933b28d7017SLad, Prabhakar mt9v032->test_pattern_color = v4l2_ctrl_new_custom(&mt9v032->ctrls, 934b28d7017SLad, Prabhakar &mt9v032_test_pattern_color, NULL); 935b28d7017SLad, Prabhakar 936b28d7017SLad, Prabhakar v4l2_ctrl_cluster(2, &mt9v032->test_pattern); 937e9a50e4cSLaurent Pinchart 93841a33a00SSakari Ailus mt9v032->pixel_rate = 93941a33a00SSakari Ailus v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, 9400ba2aeb6SHans Verkuil V4L2_CID_PIXEL_RATE, 1, INT_MAX, 1, 1); 941cb7a01acSMauro Carvalho Chehab 942e9a50e4cSLaurent Pinchart if (pdata && pdata->link_freqs) { 943e9a50e4cSLaurent Pinchart unsigned int def = 0; 944e9a50e4cSLaurent Pinchart 945e9a50e4cSLaurent Pinchart for (i = 0; pdata->link_freqs[i]; ++i) { 946e9a50e4cSLaurent Pinchart if (pdata->link_freqs[i] == pdata->link_def_freq) 947e9a50e4cSLaurent Pinchart def = i; 948e9a50e4cSLaurent Pinchart } 949e9a50e4cSLaurent Pinchart 950e9a50e4cSLaurent Pinchart mt9v032->link_freq = 951e9a50e4cSLaurent Pinchart v4l2_ctrl_new_int_menu(&mt9v032->ctrls, 952e9a50e4cSLaurent Pinchart &mt9v032_ctrl_ops, 953e9a50e4cSLaurent Pinchart V4L2_CID_LINK_FREQ, i - 1, def, 954e9a50e4cSLaurent Pinchart pdata->link_freqs); 955e9a50e4cSLaurent Pinchart v4l2_ctrl_cluster(2, &mt9v032->link_freq); 956e9a50e4cSLaurent Pinchart } 957e9a50e4cSLaurent Pinchart 958cb7a01acSMauro Carvalho Chehab 959cb7a01acSMauro Carvalho Chehab mt9v032->subdev.ctrl_handler = &mt9v032->ctrls; 960cb7a01acSMauro Carvalho Chehab 961cb7a01acSMauro Carvalho Chehab if (mt9v032->ctrls.error) 962cb7a01acSMauro Carvalho Chehab printk(KERN_INFO "%s: control initialization error %d\n", 963cb7a01acSMauro Carvalho Chehab __func__, mt9v032->ctrls.error); 964cb7a01acSMauro Carvalho Chehab 965cb7a01acSMauro Carvalho Chehab mt9v032->crop.left = MT9V032_COLUMN_START_DEF; 966cb7a01acSMauro Carvalho Chehab mt9v032->crop.top = MT9V032_ROW_START_DEF; 967cb7a01acSMauro Carvalho Chehab mt9v032->crop.width = MT9V032_WINDOW_WIDTH_DEF; 968cb7a01acSMauro Carvalho Chehab mt9v032->crop.height = MT9V032_WINDOW_HEIGHT_DEF; 969cb7a01acSMauro Carvalho Chehab 970220ddc7fSLaurent Pinchart if (mt9v032->model->color) 971cb7a01acSMauro Carvalho Chehab mt9v032->format.code = V4L2_MBUS_FMT_SGRBG10_1X10; 972220ddc7fSLaurent Pinchart else 973220ddc7fSLaurent Pinchart mt9v032->format.code = V4L2_MBUS_FMT_Y10_1X10; 974220ddc7fSLaurent Pinchart 975cb7a01acSMauro Carvalho Chehab mt9v032->format.width = MT9V032_WINDOW_WIDTH_DEF; 976cb7a01acSMauro Carvalho Chehab mt9v032->format.height = MT9V032_WINDOW_HEIGHT_DEF; 977cb7a01acSMauro Carvalho Chehab mt9v032->format.field = V4L2_FIELD_NONE; 978cb7a01acSMauro Carvalho Chehab mt9v032->format.colorspace = V4L2_COLORSPACE_SRGB; 979cb7a01acSMauro Carvalho Chehab 980637f005eSLaurent Pinchart mt9v032->hratio = 1; 981637f005eSLaurent Pinchart mt9v032->vratio = 1; 982637f005eSLaurent Pinchart 983cb7a01acSMauro Carvalho Chehab mt9v032->aec_agc = MT9V032_AEC_ENABLE | MT9V032_AGC_ENABLE; 9849ec670e2SLaurent Pinchart mt9v032->hblank = MT9V032_HORIZONTAL_BLANKING_DEF; 985e9a50e4cSLaurent Pinchart mt9v032->sysclk = MT9V032_SYSCLK_FREQ_DEF; 986cb7a01acSMauro Carvalho Chehab 987cb7a01acSMauro Carvalho Chehab v4l2_i2c_subdev_init(&mt9v032->subdev, client, &mt9v032_subdev_ops); 988cb7a01acSMauro Carvalho Chehab mt9v032->subdev.internal_ops = &mt9v032_subdev_internal_ops; 989cb7a01acSMauro Carvalho Chehab mt9v032->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 990cb7a01acSMauro Carvalho Chehab 991cb7a01acSMauro Carvalho Chehab mt9v032->pad.flags = MEDIA_PAD_FL_SOURCE; 992cb7a01acSMauro Carvalho Chehab ret = media_entity_init(&mt9v032->subdev.entity, 1, &mt9v032->pad, 0); 9939462550fSLaurent Pinchart 994c02b211dSLaurent Pinchart if (ret < 0) 9959462550fSLaurent Pinchart v4l2_ctrl_handler_free(&mt9v032->ctrls); 996cb7a01acSMauro Carvalho Chehab 997cb7a01acSMauro Carvalho Chehab return ret; 998cb7a01acSMauro Carvalho Chehab } 999cb7a01acSMauro Carvalho Chehab 1000cb7a01acSMauro Carvalho Chehab static int mt9v032_remove(struct i2c_client *client) 1001cb7a01acSMauro Carvalho Chehab { 1002cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *subdev = i2c_get_clientdata(client); 1003cb7a01acSMauro Carvalho Chehab struct mt9v032 *mt9v032 = to_mt9v032(subdev); 1004cb7a01acSMauro Carvalho Chehab 10059462550fSLaurent Pinchart v4l2_ctrl_handler_free(&mt9v032->ctrls); 1006cb7a01acSMauro Carvalho Chehab v4l2_device_unregister_subdev(subdev); 1007cb7a01acSMauro Carvalho Chehab media_entity_cleanup(&subdev->entity); 10089462550fSLaurent Pinchart 1009cb7a01acSMauro Carvalho Chehab return 0; 1010cb7a01acSMauro Carvalho Chehab } 1011cb7a01acSMauro Carvalho Chehab 1012cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id mt9v032_id[] = { 1013daecfebcSLaurent Pinchart { "mt9v032", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V032_COLOR] }, 1014daecfebcSLaurent Pinchart { "mt9v032m", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V032_MONO] }, 1015daecfebcSLaurent Pinchart { "mt9v034", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V034_COLOR] }, 1016daecfebcSLaurent Pinchart { "mt9v034m", (kernel_ulong_t)&mt9v032_models[MT9V032_MODEL_V034_MONO] }, 1017cb7a01acSMauro Carvalho Chehab { } 1018cb7a01acSMauro Carvalho Chehab }; 1019cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, mt9v032_id); 1020cb7a01acSMauro Carvalho Chehab 1021cb7a01acSMauro Carvalho Chehab static struct i2c_driver mt9v032_driver = { 1022cb7a01acSMauro Carvalho Chehab .driver = { 1023cb7a01acSMauro Carvalho Chehab .name = "mt9v032", 1024cb7a01acSMauro Carvalho Chehab }, 1025cb7a01acSMauro Carvalho Chehab .probe = mt9v032_probe, 1026cb7a01acSMauro Carvalho Chehab .remove = mt9v032_remove, 1027cb7a01acSMauro Carvalho Chehab .id_table = mt9v032_id, 1028cb7a01acSMauro Carvalho Chehab }; 1029cb7a01acSMauro Carvalho Chehab 1030cb7a01acSMauro Carvalho Chehab module_i2c_driver(mt9v032_driver); 1031cb7a01acSMauro Carvalho Chehab 1032cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("Aptina MT9V032 Camera driver"); 1033cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); 1034cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL"); 1035