1cb7a01acSMauro Carvalho Chehab /* 2cb7a01acSMauro Carvalho Chehab * Driver for MT9P031 CMOS Image Sensor from Aptina 3cb7a01acSMauro Carvalho Chehab * 4cb7a01acSMauro Carvalho Chehab * Copyright (C) 2011, Laurent Pinchart <laurent.pinchart@ideasonboard.com> 5cb7a01acSMauro Carvalho Chehab * Copyright (C) 2011, Javier Martin <javier.martin@vista-silicon.com> 6cb7a01acSMauro Carvalho Chehab * Copyright (C) 2011, Guennadi Liakhovetski <g.liakhovetski@gmx.de> 7cb7a01acSMauro Carvalho Chehab * 8cb7a01acSMauro Carvalho Chehab * Based on the MT9V032 driver and Bastian Hecht's code. 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 15*d6749258SLaurent Pinchart #include <linux/clk.h> 16cb7a01acSMauro Carvalho Chehab #include <linux/delay.h> 17cb7a01acSMauro Carvalho Chehab #include <linux/device.h> 18cb7a01acSMauro Carvalho Chehab #include <linux/gpio.h> 19cb7a01acSMauro Carvalho Chehab #include <linux/module.h> 20cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h> 21cb7a01acSMauro Carvalho Chehab #include <linux/log2.h> 22cb7a01acSMauro Carvalho Chehab #include <linux/pm.h> 2397f21276SLaurent Pinchart #include <linux/regulator/consumer.h> 24cb7a01acSMauro Carvalho Chehab #include <linux/slab.h> 25cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h> 26cb7a01acSMauro Carvalho Chehab 27cb7a01acSMauro Carvalho Chehab #include <media/mt9p031.h> 28cb7a01acSMauro Carvalho Chehab #include <media/v4l2-chip-ident.h> 29cb7a01acSMauro Carvalho Chehab #include <media/v4l2-ctrls.h> 30cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h> 31cb7a01acSMauro Carvalho Chehab #include <media/v4l2-subdev.h> 32cb7a01acSMauro Carvalho Chehab 33cb7a01acSMauro Carvalho Chehab #include "aptina-pll.h" 34cb7a01acSMauro Carvalho Chehab 35cb7a01acSMauro Carvalho Chehab #define MT9P031_PIXEL_ARRAY_WIDTH 2752 36cb7a01acSMauro Carvalho Chehab #define MT9P031_PIXEL_ARRAY_HEIGHT 2004 37cb7a01acSMauro Carvalho Chehab 38cb7a01acSMauro Carvalho Chehab #define MT9P031_CHIP_VERSION 0x00 39cb7a01acSMauro Carvalho Chehab #define MT9P031_CHIP_VERSION_VALUE 0x1801 40cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_START 0x01 41cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_START_MIN 0 42cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_START_MAX 2004 43cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_START_DEF 54 44cb7a01acSMauro Carvalho Chehab #define MT9P031_COLUMN_START 0x02 45cb7a01acSMauro Carvalho Chehab #define MT9P031_COLUMN_START_MIN 0 46cb7a01acSMauro Carvalho Chehab #define MT9P031_COLUMN_START_MAX 2750 47cb7a01acSMauro Carvalho Chehab #define MT9P031_COLUMN_START_DEF 16 48cb7a01acSMauro Carvalho Chehab #define MT9P031_WINDOW_HEIGHT 0x03 49cb7a01acSMauro Carvalho Chehab #define MT9P031_WINDOW_HEIGHT_MIN 2 50cb7a01acSMauro Carvalho Chehab #define MT9P031_WINDOW_HEIGHT_MAX 2006 51cb7a01acSMauro Carvalho Chehab #define MT9P031_WINDOW_HEIGHT_DEF 1944 52cb7a01acSMauro Carvalho Chehab #define MT9P031_WINDOW_WIDTH 0x04 53cb7a01acSMauro Carvalho Chehab #define MT9P031_WINDOW_WIDTH_MIN 2 54cb7a01acSMauro Carvalho Chehab #define MT9P031_WINDOW_WIDTH_MAX 2752 55cb7a01acSMauro Carvalho Chehab #define MT9P031_WINDOW_WIDTH_DEF 2592 56cb7a01acSMauro Carvalho Chehab #define MT9P031_HORIZONTAL_BLANK 0x05 57cb7a01acSMauro Carvalho Chehab #define MT9P031_HORIZONTAL_BLANK_MIN 0 58cb7a01acSMauro Carvalho Chehab #define MT9P031_HORIZONTAL_BLANK_MAX 4095 59cb7a01acSMauro Carvalho Chehab #define MT9P031_VERTICAL_BLANK 0x06 605266c98bSLaurent Pinchart #define MT9P031_VERTICAL_BLANK_MIN 1 615266c98bSLaurent Pinchart #define MT9P031_VERTICAL_BLANK_MAX 4096 625266c98bSLaurent Pinchart #define MT9P031_VERTICAL_BLANK_DEF 26 63cb7a01acSMauro Carvalho Chehab #define MT9P031_OUTPUT_CONTROL 0x07 64cb7a01acSMauro Carvalho Chehab #define MT9P031_OUTPUT_CONTROL_CEN 2 65cb7a01acSMauro Carvalho Chehab #define MT9P031_OUTPUT_CONTROL_SYN 1 66cb7a01acSMauro Carvalho Chehab #define MT9P031_OUTPUT_CONTROL_DEF 0x1f82 67cb7a01acSMauro Carvalho Chehab #define MT9P031_SHUTTER_WIDTH_UPPER 0x08 68cb7a01acSMauro Carvalho Chehab #define MT9P031_SHUTTER_WIDTH_LOWER 0x09 69cb7a01acSMauro Carvalho Chehab #define MT9P031_SHUTTER_WIDTH_MIN 1 70cb7a01acSMauro Carvalho Chehab #define MT9P031_SHUTTER_WIDTH_MAX 1048575 71cb7a01acSMauro Carvalho Chehab #define MT9P031_SHUTTER_WIDTH_DEF 1943 72cb7a01acSMauro Carvalho Chehab #define MT9P031_PLL_CONTROL 0x10 73cb7a01acSMauro Carvalho Chehab #define MT9P031_PLL_CONTROL_PWROFF 0x0050 74cb7a01acSMauro Carvalho Chehab #define MT9P031_PLL_CONTROL_PWRON 0x0051 75cb7a01acSMauro Carvalho Chehab #define MT9P031_PLL_CONTROL_USEPLL 0x0052 76cb7a01acSMauro Carvalho Chehab #define MT9P031_PLL_CONFIG_1 0x11 77cb7a01acSMauro Carvalho Chehab #define MT9P031_PLL_CONFIG_2 0x12 78cb7a01acSMauro Carvalho Chehab #define MT9P031_PIXEL_CLOCK_CONTROL 0x0a 79cb7a01acSMauro Carvalho Chehab #define MT9P031_FRAME_RESTART 0x0b 80cb7a01acSMauro Carvalho Chehab #define MT9P031_SHUTTER_DELAY 0x0c 81cb7a01acSMauro Carvalho Chehab #define MT9P031_RST 0x0d 82cb7a01acSMauro Carvalho Chehab #define MT9P031_RST_ENABLE 1 83cb7a01acSMauro Carvalho Chehab #define MT9P031_RST_DISABLE 0 84cb7a01acSMauro Carvalho Chehab #define MT9P031_READ_MODE_1 0x1e 85cb7a01acSMauro Carvalho Chehab #define MT9P031_READ_MODE_2 0x20 86cb7a01acSMauro Carvalho Chehab #define MT9P031_READ_MODE_2_ROW_MIR (1 << 15) 87cb7a01acSMauro Carvalho Chehab #define MT9P031_READ_MODE_2_COL_MIR (1 << 14) 88cb7a01acSMauro Carvalho Chehab #define MT9P031_READ_MODE_2_ROW_BLC (1 << 6) 89cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_ADDRESS_MODE 0x22 90cb7a01acSMauro Carvalho Chehab #define MT9P031_COLUMN_ADDRESS_MODE 0x23 91cb7a01acSMauro Carvalho Chehab #define MT9P031_GLOBAL_GAIN 0x35 92cb7a01acSMauro Carvalho Chehab #define MT9P031_GLOBAL_GAIN_MIN 8 93cb7a01acSMauro Carvalho Chehab #define MT9P031_GLOBAL_GAIN_MAX 1024 94cb7a01acSMauro Carvalho Chehab #define MT9P031_GLOBAL_GAIN_DEF 8 95cb7a01acSMauro Carvalho Chehab #define MT9P031_GLOBAL_GAIN_MULT (1 << 6) 96cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_BLACK_TARGET 0x49 97cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_BLACK_DEF_OFFSET 0x4b 98cb7a01acSMauro Carvalho Chehab #define MT9P031_GREEN1_OFFSET 0x60 99cb7a01acSMauro Carvalho Chehab #define MT9P031_GREEN2_OFFSET 0x61 100cb7a01acSMauro Carvalho Chehab #define MT9P031_BLACK_LEVEL_CALIBRATION 0x62 101cb7a01acSMauro Carvalho Chehab #define MT9P031_BLC_MANUAL_BLC (1 << 0) 102cb7a01acSMauro Carvalho Chehab #define MT9P031_RED_OFFSET 0x63 103cb7a01acSMauro Carvalho Chehab #define MT9P031_BLUE_OFFSET 0x64 104cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN 0xa0 105cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN_SHIFT 3 106cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN_ENABLE (1 << 0) 107cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN_DISABLE (0 << 0) 108cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN_GREEN 0xa1 109cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN_RED 0xa2 110cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN_BLUE 0xa3 111cb7a01acSMauro Carvalho Chehab 112cb7a01acSMauro Carvalho Chehab enum mt9p031_model { 113cb7a01acSMauro Carvalho Chehab MT9P031_MODEL_COLOR, 114cb7a01acSMauro Carvalho Chehab MT9P031_MODEL_MONOCHROME, 115cb7a01acSMauro Carvalho Chehab }; 116cb7a01acSMauro Carvalho Chehab 117cb7a01acSMauro Carvalho Chehab struct mt9p031 { 118cb7a01acSMauro Carvalho Chehab struct v4l2_subdev subdev; 119cb7a01acSMauro Carvalho Chehab struct media_pad pad; 120cb7a01acSMauro Carvalho Chehab struct v4l2_rect crop; /* Sensor window */ 121cb7a01acSMauro Carvalho Chehab struct v4l2_mbus_framefmt format; 122cb7a01acSMauro Carvalho Chehab struct mt9p031_platform_data *pdata; 123cb7a01acSMauro Carvalho Chehab struct mutex power_lock; /* lock to protect power_count */ 124cb7a01acSMauro Carvalho Chehab int power_count; 125cb7a01acSMauro Carvalho Chehab 126*d6749258SLaurent Pinchart struct clk *clk; 12797f21276SLaurent Pinchart struct regulator *vaa; 12897f21276SLaurent Pinchart struct regulator *vdd; 12997f21276SLaurent Pinchart struct regulator *vdd_io; 13097f21276SLaurent Pinchart 131cb7a01acSMauro Carvalho Chehab enum mt9p031_model model; 132cb7a01acSMauro Carvalho Chehab struct aptina_pll pll; 133cb7a01acSMauro Carvalho Chehab int reset; 134cb7a01acSMauro Carvalho Chehab 135cb7a01acSMauro Carvalho Chehab struct v4l2_ctrl_handler ctrls; 136cb7a01acSMauro Carvalho Chehab struct v4l2_ctrl *blc_auto; 137cb7a01acSMauro Carvalho Chehab struct v4l2_ctrl *blc_offset; 138cb7a01acSMauro Carvalho Chehab 139cb7a01acSMauro Carvalho Chehab /* Registers cache */ 140cb7a01acSMauro Carvalho Chehab u16 output_control; 141cb7a01acSMauro Carvalho Chehab u16 mode2; 142cb7a01acSMauro Carvalho Chehab }; 143cb7a01acSMauro Carvalho Chehab 144cb7a01acSMauro Carvalho Chehab static struct mt9p031 *to_mt9p031(struct v4l2_subdev *sd) 145cb7a01acSMauro Carvalho Chehab { 146cb7a01acSMauro Carvalho Chehab return container_of(sd, struct mt9p031, subdev); 147cb7a01acSMauro Carvalho Chehab } 148cb7a01acSMauro Carvalho Chehab 149cb7a01acSMauro Carvalho Chehab static int mt9p031_read(struct i2c_client *client, u8 reg) 150cb7a01acSMauro Carvalho Chehab { 151cb7a01acSMauro Carvalho Chehab return i2c_smbus_read_word_swapped(client, reg); 152cb7a01acSMauro Carvalho Chehab } 153cb7a01acSMauro Carvalho Chehab 154cb7a01acSMauro Carvalho Chehab static int mt9p031_write(struct i2c_client *client, u8 reg, u16 data) 155cb7a01acSMauro Carvalho Chehab { 156cb7a01acSMauro Carvalho Chehab return i2c_smbus_write_word_swapped(client, reg, data); 157cb7a01acSMauro Carvalho Chehab } 158cb7a01acSMauro Carvalho Chehab 159cb7a01acSMauro Carvalho Chehab static int mt9p031_set_output_control(struct mt9p031 *mt9p031, u16 clear, 160cb7a01acSMauro Carvalho Chehab u16 set) 161cb7a01acSMauro Carvalho Chehab { 162cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); 163cb7a01acSMauro Carvalho Chehab u16 value = (mt9p031->output_control & ~clear) | set; 164cb7a01acSMauro Carvalho Chehab int ret; 165cb7a01acSMauro Carvalho Chehab 166cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_OUTPUT_CONTROL, value); 167cb7a01acSMauro Carvalho Chehab if (ret < 0) 168cb7a01acSMauro Carvalho Chehab return ret; 169cb7a01acSMauro Carvalho Chehab 170cb7a01acSMauro Carvalho Chehab mt9p031->output_control = value; 171cb7a01acSMauro Carvalho Chehab return 0; 172cb7a01acSMauro Carvalho Chehab } 173cb7a01acSMauro Carvalho Chehab 174cb7a01acSMauro Carvalho Chehab static int mt9p031_set_mode2(struct mt9p031 *mt9p031, u16 clear, u16 set) 175cb7a01acSMauro Carvalho Chehab { 176cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); 177cb7a01acSMauro Carvalho Chehab u16 value = (mt9p031->mode2 & ~clear) | set; 178cb7a01acSMauro Carvalho Chehab int ret; 179cb7a01acSMauro Carvalho Chehab 180cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_READ_MODE_2, value); 181cb7a01acSMauro Carvalho Chehab if (ret < 0) 182cb7a01acSMauro Carvalho Chehab return ret; 183cb7a01acSMauro Carvalho Chehab 184cb7a01acSMauro Carvalho Chehab mt9p031->mode2 = value; 185cb7a01acSMauro Carvalho Chehab return 0; 186cb7a01acSMauro Carvalho Chehab } 187cb7a01acSMauro Carvalho Chehab 188cb7a01acSMauro Carvalho Chehab static int mt9p031_reset(struct mt9p031 *mt9p031) 189cb7a01acSMauro Carvalho Chehab { 190cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); 191cb7a01acSMauro Carvalho Chehab int ret; 192cb7a01acSMauro Carvalho Chehab 193cb7a01acSMauro Carvalho Chehab /* Disable chip output, synchronous option update */ 194cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_ENABLE); 195cb7a01acSMauro Carvalho Chehab if (ret < 0) 196cb7a01acSMauro Carvalho Chehab return ret; 197cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_DISABLE); 198cb7a01acSMauro Carvalho Chehab if (ret < 0) 199cb7a01acSMauro Carvalho Chehab return ret; 200cb7a01acSMauro Carvalho Chehab 201cb7a01acSMauro Carvalho Chehab return mt9p031_set_output_control(mt9p031, MT9P031_OUTPUT_CONTROL_CEN, 202cb7a01acSMauro Carvalho Chehab 0); 203cb7a01acSMauro Carvalho Chehab } 204cb7a01acSMauro Carvalho Chehab 205*d6749258SLaurent Pinchart static int mt9p031_clk_setup(struct mt9p031 *mt9p031) 206cb7a01acSMauro Carvalho Chehab { 207cb7a01acSMauro Carvalho Chehab static const struct aptina_pll_limits limits = { 208cb7a01acSMauro Carvalho Chehab .ext_clock_min = 6000000, 209cb7a01acSMauro Carvalho Chehab .ext_clock_max = 27000000, 210cb7a01acSMauro Carvalho Chehab .int_clock_min = 2000000, 211cb7a01acSMauro Carvalho Chehab .int_clock_max = 13500000, 212cb7a01acSMauro Carvalho Chehab .out_clock_min = 180000000, 213cb7a01acSMauro Carvalho Chehab .out_clock_max = 360000000, 214cb7a01acSMauro Carvalho Chehab .pix_clock_max = 96000000, 215cb7a01acSMauro Carvalho Chehab .n_min = 1, 216cb7a01acSMauro Carvalho Chehab .n_max = 64, 217cb7a01acSMauro Carvalho Chehab .m_min = 16, 218cb7a01acSMauro Carvalho Chehab .m_max = 255, 219cb7a01acSMauro Carvalho Chehab .p1_min = 1, 220cb7a01acSMauro Carvalho Chehab .p1_max = 128, 221cb7a01acSMauro Carvalho Chehab }; 222cb7a01acSMauro Carvalho Chehab 223cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); 224cb7a01acSMauro Carvalho Chehab struct mt9p031_platform_data *pdata = mt9p031->pdata; 225cb7a01acSMauro Carvalho Chehab 226*d6749258SLaurent Pinchart mt9p031->clk = devm_clk_get(&client->dev, NULL); 227*d6749258SLaurent Pinchart if (IS_ERR(mt9p031->clk)) 228*d6749258SLaurent Pinchart return PTR_ERR(mt9p031->clk); 229*d6749258SLaurent Pinchart 230*d6749258SLaurent Pinchart clk_set_rate(mt9p031->clk, pdata->ext_freq); 231*d6749258SLaurent Pinchart 232cb7a01acSMauro Carvalho Chehab mt9p031->pll.ext_clock = pdata->ext_freq; 233cb7a01acSMauro Carvalho Chehab mt9p031->pll.pix_clock = pdata->target_freq; 234cb7a01acSMauro Carvalho Chehab 235cb7a01acSMauro Carvalho Chehab return aptina_pll_calculate(&client->dev, &limits, &mt9p031->pll); 236cb7a01acSMauro Carvalho Chehab } 237cb7a01acSMauro Carvalho Chehab 238cb7a01acSMauro Carvalho Chehab static int mt9p031_pll_enable(struct mt9p031 *mt9p031) 239cb7a01acSMauro Carvalho Chehab { 240cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); 241cb7a01acSMauro Carvalho Chehab int ret; 242cb7a01acSMauro Carvalho Chehab 243cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_PLL_CONTROL, 244cb7a01acSMauro Carvalho Chehab MT9P031_PLL_CONTROL_PWRON); 245cb7a01acSMauro Carvalho Chehab if (ret < 0) 246cb7a01acSMauro Carvalho Chehab return ret; 247cb7a01acSMauro Carvalho Chehab 248cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_PLL_CONFIG_1, 249cb7a01acSMauro Carvalho Chehab (mt9p031->pll.m << 8) | (mt9p031->pll.n - 1)); 250cb7a01acSMauro Carvalho Chehab if (ret < 0) 251cb7a01acSMauro Carvalho Chehab return ret; 252cb7a01acSMauro Carvalho Chehab 253cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_PLL_CONFIG_2, mt9p031->pll.p1 - 1); 254cb7a01acSMauro Carvalho Chehab if (ret < 0) 255cb7a01acSMauro Carvalho Chehab return ret; 256cb7a01acSMauro Carvalho Chehab 257cb7a01acSMauro Carvalho Chehab usleep_range(1000, 2000); 258cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_PLL_CONTROL, 259cb7a01acSMauro Carvalho Chehab MT9P031_PLL_CONTROL_PWRON | 260cb7a01acSMauro Carvalho Chehab MT9P031_PLL_CONTROL_USEPLL); 261cb7a01acSMauro Carvalho Chehab return ret; 262cb7a01acSMauro Carvalho Chehab } 263cb7a01acSMauro Carvalho Chehab 264cb7a01acSMauro Carvalho Chehab static inline int mt9p031_pll_disable(struct mt9p031 *mt9p031) 265cb7a01acSMauro Carvalho Chehab { 266cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); 267cb7a01acSMauro Carvalho Chehab 268cb7a01acSMauro Carvalho Chehab return mt9p031_write(client, MT9P031_PLL_CONTROL, 269cb7a01acSMauro Carvalho Chehab MT9P031_PLL_CONTROL_PWROFF); 270cb7a01acSMauro Carvalho Chehab } 271cb7a01acSMauro Carvalho Chehab 272cb7a01acSMauro Carvalho Chehab static int mt9p031_power_on(struct mt9p031 *mt9p031) 273cb7a01acSMauro Carvalho Chehab { 274cb7a01acSMauro Carvalho Chehab /* Ensure RESET_BAR is low */ 275cb7a01acSMauro Carvalho Chehab if (mt9p031->reset != -1) { 276cb7a01acSMauro Carvalho Chehab gpio_set_value(mt9p031->reset, 0); 277cb7a01acSMauro Carvalho Chehab usleep_range(1000, 2000); 278cb7a01acSMauro Carvalho Chehab } 279cb7a01acSMauro Carvalho Chehab 28097f21276SLaurent Pinchart /* Bring up the supplies */ 28197f21276SLaurent Pinchart regulator_enable(mt9p031->vdd); 28297f21276SLaurent Pinchart regulator_enable(mt9p031->vdd_io); 28397f21276SLaurent Pinchart regulator_enable(mt9p031->vaa); 28497f21276SLaurent Pinchart 285cb7a01acSMauro Carvalho Chehab /* Emable clock */ 286*d6749258SLaurent Pinchart if (mt9p031->clk) 287*d6749258SLaurent Pinchart clk_prepare_enable(mt9p031->clk); 288cb7a01acSMauro Carvalho Chehab 289cb7a01acSMauro Carvalho Chehab /* Now RESET_BAR must be high */ 290cb7a01acSMauro Carvalho Chehab if (mt9p031->reset != -1) { 291cb7a01acSMauro Carvalho Chehab gpio_set_value(mt9p031->reset, 1); 292cb7a01acSMauro Carvalho Chehab usleep_range(1000, 2000); 293cb7a01acSMauro Carvalho Chehab } 294cb7a01acSMauro Carvalho Chehab 295cb7a01acSMauro Carvalho Chehab return 0; 296cb7a01acSMauro Carvalho Chehab } 297cb7a01acSMauro Carvalho Chehab 298cb7a01acSMauro Carvalho Chehab static void mt9p031_power_off(struct mt9p031 *mt9p031) 299cb7a01acSMauro Carvalho Chehab { 300cb7a01acSMauro Carvalho Chehab if (mt9p031->reset != -1) { 301cb7a01acSMauro Carvalho Chehab gpio_set_value(mt9p031->reset, 0); 302cb7a01acSMauro Carvalho Chehab usleep_range(1000, 2000); 303cb7a01acSMauro Carvalho Chehab } 304cb7a01acSMauro Carvalho Chehab 30597f21276SLaurent Pinchart regulator_disable(mt9p031->vaa); 30697f21276SLaurent Pinchart regulator_disable(mt9p031->vdd_io); 30797f21276SLaurent Pinchart regulator_disable(mt9p031->vdd); 30897f21276SLaurent Pinchart 309*d6749258SLaurent Pinchart if (mt9p031->clk) 310*d6749258SLaurent Pinchart clk_disable_unprepare(mt9p031->clk); 311cb7a01acSMauro Carvalho Chehab } 312cb7a01acSMauro Carvalho Chehab 313cb7a01acSMauro Carvalho Chehab static int __mt9p031_set_power(struct mt9p031 *mt9p031, bool on) 314cb7a01acSMauro Carvalho Chehab { 315cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); 316cb7a01acSMauro Carvalho Chehab int ret; 317cb7a01acSMauro Carvalho Chehab 318cb7a01acSMauro Carvalho Chehab if (!on) { 319cb7a01acSMauro Carvalho Chehab mt9p031_power_off(mt9p031); 320cb7a01acSMauro Carvalho Chehab return 0; 321cb7a01acSMauro Carvalho Chehab } 322cb7a01acSMauro Carvalho Chehab 323cb7a01acSMauro Carvalho Chehab ret = mt9p031_power_on(mt9p031); 324cb7a01acSMauro Carvalho Chehab if (ret < 0) 325cb7a01acSMauro Carvalho Chehab return ret; 326cb7a01acSMauro Carvalho Chehab 327cb7a01acSMauro Carvalho Chehab ret = mt9p031_reset(mt9p031); 328cb7a01acSMauro Carvalho Chehab if (ret < 0) { 329cb7a01acSMauro Carvalho Chehab dev_err(&client->dev, "Failed to reset the camera\n"); 330cb7a01acSMauro Carvalho Chehab return ret; 331cb7a01acSMauro Carvalho Chehab } 332cb7a01acSMauro Carvalho Chehab 333cb7a01acSMauro Carvalho Chehab return v4l2_ctrl_handler_setup(&mt9p031->ctrls); 334cb7a01acSMauro Carvalho Chehab } 335cb7a01acSMauro Carvalho Chehab 336cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------------- 337cb7a01acSMauro Carvalho Chehab * V4L2 subdev video operations 338cb7a01acSMauro Carvalho Chehab */ 339cb7a01acSMauro Carvalho Chehab 340cb7a01acSMauro Carvalho Chehab static int mt9p031_set_params(struct mt9p031 *mt9p031) 341cb7a01acSMauro Carvalho Chehab { 342cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); 343cb7a01acSMauro Carvalho Chehab struct v4l2_mbus_framefmt *format = &mt9p031->format; 344cb7a01acSMauro Carvalho Chehab const struct v4l2_rect *crop = &mt9p031->crop; 345cb7a01acSMauro Carvalho Chehab unsigned int hblank; 346cb7a01acSMauro Carvalho Chehab unsigned int vblank; 347cb7a01acSMauro Carvalho Chehab unsigned int xskip; 348cb7a01acSMauro Carvalho Chehab unsigned int yskip; 349cb7a01acSMauro Carvalho Chehab unsigned int xbin; 350cb7a01acSMauro Carvalho Chehab unsigned int ybin; 351cb7a01acSMauro Carvalho Chehab int ret; 352cb7a01acSMauro Carvalho Chehab 353cb7a01acSMauro Carvalho Chehab /* Windows position and size. 354cb7a01acSMauro Carvalho Chehab * 355cb7a01acSMauro Carvalho Chehab * TODO: Make sure the start coordinates and window size match the 356cb7a01acSMauro Carvalho Chehab * skipping, binning and mirroring (see description of registers 2 and 4 357cb7a01acSMauro Carvalho Chehab * in table 13, and Binning section on page 41). 358cb7a01acSMauro Carvalho Chehab */ 359cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_COLUMN_START, crop->left); 360cb7a01acSMauro Carvalho Chehab if (ret < 0) 361cb7a01acSMauro Carvalho Chehab return ret; 362cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_ROW_START, crop->top); 363cb7a01acSMauro Carvalho Chehab if (ret < 0) 364cb7a01acSMauro Carvalho Chehab return ret; 365cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_WINDOW_WIDTH, crop->width - 1); 366cb7a01acSMauro Carvalho Chehab if (ret < 0) 367cb7a01acSMauro Carvalho Chehab return ret; 368cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_WINDOW_HEIGHT, crop->height - 1); 369cb7a01acSMauro Carvalho Chehab if (ret < 0) 370cb7a01acSMauro Carvalho Chehab return ret; 371cb7a01acSMauro Carvalho Chehab 372cb7a01acSMauro Carvalho Chehab /* Row and column binning and skipping. Use the maximum binning value 373cb7a01acSMauro Carvalho Chehab * compatible with the skipping settings. 374cb7a01acSMauro Carvalho Chehab */ 375cb7a01acSMauro Carvalho Chehab xskip = DIV_ROUND_CLOSEST(crop->width, format->width); 376cb7a01acSMauro Carvalho Chehab yskip = DIV_ROUND_CLOSEST(crop->height, format->height); 377cb7a01acSMauro Carvalho Chehab xbin = 1 << (ffs(xskip) - 1); 378cb7a01acSMauro Carvalho Chehab ybin = 1 << (ffs(yskip) - 1); 379cb7a01acSMauro Carvalho Chehab 380cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_COLUMN_ADDRESS_MODE, 381cb7a01acSMauro Carvalho Chehab ((xbin - 1) << 4) | (xskip - 1)); 382cb7a01acSMauro Carvalho Chehab if (ret < 0) 383cb7a01acSMauro Carvalho Chehab return ret; 384cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_ROW_ADDRESS_MODE, 385cb7a01acSMauro Carvalho Chehab ((ybin - 1) << 4) | (yskip - 1)); 386cb7a01acSMauro Carvalho Chehab if (ret < 0) 387cb7a01acSMauro Carvalho Chehab return ret; 388cb7a01acSMauro Carvalho Chehab 389cb7a01acSMauro Carvalho Chehab /* Blanking - use minimum value for horizontal blanking and default 390cb7a01acSMauro Carvalho Chehab * value for vertical blanking. 391cb7a01acSMauro Carvalho Chehab */ 3925266c98bSLaurent Pinchart hblank = 346 * ybin + 64 + (80 >> min_t(unsigned int, xbin, 3)); 393cb7a01acSMauro Carvalho Chehab vblank = MT9P031_VERTICAL_BLANK_DEF; 394cb7a01acSMauro Carvalho Chehab 3955266c98bSLaurent Pinchart ret = mt9p031_write(client, MT9P031_HORIZONTAL_BLANK, hblank - 1); 396cb7a01acSMauro Carvalho Chehab if (ret < 0) 397cb7a01acSMauro Carvalho Chehab return ret; 3985266c98bSLaurent Pinchart ret = mt9p031_write(client, MT9P031_VERTICAL_BLANK, vblank - 1); 399cb7a01acSMauro Carvalho Chehab if (ret < 0) 400cb7a01acSMauro Carvalho Chehab return ret; 401cb7a01acSMauro Carvalho Chehab 402cb7a01acSMauro Carvalho Chehab return ret; 403cb7a01acSMauro Carvalho Chehab } 404cb7a01acSMauro Carvalho Chehab 405cb7a01acSMauro Carvalho Chehab static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable) 406cb7a01acSMauro Carvalho Chehab { 407cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev); 408cb7a01acSMauro Carvalho Chehab int ret; 409cb7a01acSMauro Carvalho Chehab 410cb7a01acSMauro Carvalho Chehab if (!enable) { 411cb7a01acSMauro Carvalho Chehab /* Stop sensor readout */ 412cb7a01acSMauro Carvalho Chehab ret = mt9p031_set_output_control(mt9p031, 413cb7a01acSMauro Carvalho Chehab MT9P031_OUTPUT_CONTROL_CEN, 0); 414cb7a01acSMauro Carvalho Chehab if (ret < 0) 415cb7a01acSMauro Carvalho Chehab return ret; 416cb7a01acSMauro Carvalho Chehab 417cb7a01acSMauro Carvalho Chehab return mt9p031_pll_disable(mt9p031); 418cb7a01acSMauro Carvalho Chehab } 419cb7a01acSMauro Carvalho Chehab 420cb7a01acSMauro Carvalho Chehab ret = mt9p031_set_params(mt9p031); 421cb7a01acSMauro Carvalho Chehab if (ret < 0) 422cb7a01acSMauro Carvalho Chehab return ret; 423cb7a01acSMauro Carvalho Chehab 424cb7a01acSMauro Carvalho Chehab /* Switch to master "normal" mode */ 425cb7a01acSMauro Carvalho Chehab ret = mt9p031_set_output_control(mt9p031, 0, 426cb7a01acSMauro Carvalho Chehab MT9P031_OUTPUT_CONTROL_CEN); 427cb7a01acSMauro Carvalho Chehab if (ret < 0) 428cb7a01acSMauro Carvalho Chehab return ret; 429cb7a01acSMauro Carvalho Chehab 430cb7a01acSMauro Carvalho Chehab return mt9p031_pll_enable(mt9p031); 431cb7a01acSMauro Carvalho Chehab } 432cb7a01acSMauro Carvalho Chehab 433cb7a01acSMauro Carvalho Chehab static int mt9p031_enum_mbus_code(struct v4l2_subdev *subdev, 434cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_fh *fh, 435cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_mbus_code_enum *code) 436cb7a01acSMauro Carvalho Chehab { 437cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev); 438cb7a01acSMauro Carvalho Chehab 439cb7a01acSMauro Carvalho Chehab if (code->pad || code->index) 440cb7a01acSMauro Carvalho Chehab return -EINVAL; 441cb7a01acSMauro Carvalho Chehab 442cb7a01acSMauro Carvalho Chehab code->code = mt9p031->format.code; 443cb7a01acSMauro Carvalho Chehab return 0; 444cb7a01acSMauro Carvalho Chehab } 445cb7a01acSMauro Carvalho Chehab 446cb7a01acSMauro Carvalho Chehab static int mt9p031_enum_frame_size(struct v4l2_subdev *subdev, 447cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_fh *fh, 448cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_frame_size_enum *fse) 449cb7a01acSMauro Carvalho Chehab { 450cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev); 451cb7a01acSMauro Carvalho Chehab 452cb7a01acSMauro Carvalho Chehab if (fse->index >= 8 || fse->code != mt9p031->format.code) 453cb7a01acSMauro Carvalho Chehab return -EINVAL; 454cb7a01acSMauro Carvalho Chehab 455cb7a01acSMauro Carvalho Chehab fse->min_width = MT9P031_WINDOW_WIDTH_DEF 456cb7a01acSMauro Carvalho Chehab / min_t(unsigned int, 7, fse->index + 1); 457cb7a01acSMauro Carvalho Chehab fse->max_width = fse->min_width; 458cb7a01acSMauro Carvalho Chehab fse->min_height = MT9P031_WINDOW_HEIGHT_DEF / (fse->index + 1); 459cb7a01acSMauro Carvalho Chehab fse->max_height = fse->min_height; 460cb7a01acSMauro Carvalho Chehab 461cb7a01acSMauro Carvalho Chehab return 0; 462cb7a01acSMauro Carvalho Chehab } 463cb7a01acSMauro Carvalho Chehab 464cb7a01acSMauro Carvalho Chehab static struct v4l2_mbus_framefmt * 465cb7a01acSMauro Carvalho Chehab __mt9p031_get_pad_format(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh, 466cb7a01acSMauro Carvalho Chehab unsigned int pad, u32 which) 467cb7a01acSMauro Carvalho Chehab { 468cb7a01acSMauro Carvalho Chehab switch (which) { 469cb7a01acSMauro Carvalho Chehab case V4L2_SUBDEV_FORMAT_TRY: 470cb7a01acSMauro Carvalho Chehab return v4l2_subdev_get_try_format(fh, pad); 471cb7a01acSMauro Carvalho Chehab case V4L2_SUBDEV_FORMAT_ACTIVE: 472cb7a01acSMauro Carvalho Chehab return &mt9p031->format; 473cb7a01acSMauro Carvalho Chehab default: 474cb7a01acSMauro Carvalho Chehab return NULL; 475cb7a01acSMauro Carvalho Chehab } 476cb7a01acSMauro Carvalho Chehab } 477cb7a01acSMauro Carvalho Chehab 478cb7a01acSMauro Carvalho Chehab static struct v4l2_rect * 479cb7a01acSMauro Carvalho Chehab __mt9p031_get_pad_crop(struct mt9p031 *mt9p031, struct v4l2_subdev_fh *fh, 480cb7a01acSMauro Carvalho Chehab unsigned int pad, u32 which) 481cb7a01acSMauro Carvalho Chehab { 482cb7a01acSMauro Carvalho Chehab switch (which) { 483cb7a01acSMauro Carvalho Chehab case V4L2_SUBDEV_FORMAT_TRY: 484cb7a01acSMauro Carvalho Chehab return v4l2_subdev_get_try_crop(fh, pad); 485cb7a01acSMauro Carvalho Chehab case V4L2_SUBDEV_FORMAT_ACTIVE: 486cb7a01acSMauro Carvalho Chehab return &mt9p031->crop; 487cb7a01acSMauro Carvalho Chehab default: 488cb7a01acSMauro Carvalho Chehab return NULL; 489cb7a01acSMauro Carvalho Chehab } 490cb7a01acSMauro Carvalho Chehab } 491cb7a01acSMauro Carvalho Chehab 492cb7a01acSMauro Carvalho Chehab static int mt9p031_get_format(struct v4l2_subdev *subdev, 493cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_fh *fh, 494cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_format *fmt) 495cb7a01acSMauro Carvalho Chehab { 496cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev); 497cb7a01acSMauro Carvalho Chehab 498cb7a01acSMauro Carvalho Chehab fmt->format = *__mt9p031_get_pad_format(mt9p031, fh, fmt->pad, 499cb7a01acSMauro Carvalho Chehab fmt->which); 500cb7a01acSMauro Carvalho Chehab return 0; 501cb7a01acSMauro Carvalho Chehab } 502cb7a01acSMauro Carvalho Chehab 503cb7a01acSMauro Carvalho Chehab static int mt9p031_set_format(struct v4l2_subdev *subdev, 504cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_fh *fh, 505cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_format *format) 506cb7a01acSMauro Carvalho Chehab { 507cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev); 508cb7a01acSMauro Carvalho Chehab struct v4l2_mbus_framefmt *__format; 509cb7a01acSMauro Carvalho Chehab struct v4l2_rect *__crop; 510cb7a01acSMauro Carvalho Chehab unsigned int width; 511cb7a01acSMauro Carvalho Chehab unsigned int height; 512cb7a01acSMauro Carvalho Chehab unsigned int hratio; 513cb7a01acSMauro Carvalho Chehab unsigned int vratio; 514cb7a01acSMauro Carvalho Chehab 515cb7a01acSMauro Carvalho Chehab __crop = __mt9p031_get_pad_crop(mt9p031, fh, format->pad, 516cb7a01acSMauro Carvalho Chehab format->which); 517cb7a01acSMauro Carvalho Chehab 518cb7a01acSMauro Carvalho Chehab /* Clamp the width and height to avoid dividing by zero. */ 519cb7a01acSMauro Carvalho Chehab width = clamp_t(unsigned int, ALIGN(format->format.width, 2), 520cb7a01acSMauro Carvalho Chehab max(__crop->width / 7, MT9P031_WINDOW_WIDTH_MIN), 521cb7a01acSMauro Carvalho Chehab __crop->width); 522cb7a01acSMauro Carvalho Chehab height = clamp_t(unsigned int, ALIGN(format->format.height, 2), 523cb7a01acSMauro Carvalho Chehab max(__crop->height / 8, MT9P031_WINDOW_HEIGHT_MIN), 524cb7a01acSMauro Carvalho Chehab __crop->height); 525cb7a01acSMauro Carvalho Chehab 526cb7a01acSMauro Carvalho Chehab hratio = DIV_ROUND_CLOSEST(__crop->width, width); 527cb7a01acSMauro Carvalho Chehab vratio = DIV_ROUND_CLOSEST(__crop->height, height); 528cb7a01acSMauro Carvalho Chehab 529cb7a01acSMauro Carvalho Chehab __format = __mt9p031_get_pad_format(mt9p031, fh, format->pad, 530cb7a01acSMauro Carvalho Chehab format->which); 531cb7a01acSMauro Carvalho Chehab __format->width = __crop->width / hratio; 532cb7a01acSMauro Carvalho Chehab __format->height = __crop->height / vratio; 533cb7a01acSMauro Carvalho Chehab 534cb7a01acSMauro Carvalho Chehab format->format = *__format; 535cb7a01acSMauro Carvalho Chehab 536cb7a01acSMauro Carvalho Chehab return 0; 537cb7a01acSMauro Carvalho Chehab } 538cb7a01acSMauro Carvalho Chehab 539cb7a01acSMauro Carvalho Chehab static int mt9p031_get_crop(struct v4l2_subdev *subdev, 540cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_fh *fh, 541cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_crop *crop) 542cb7a01acSMauro Carvalho Chehab { 543cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev); 544cb7a01acSMauro Carvalho Chehab 545cb7a01acSMauro Carvalho Chehab crop->rect = *__mt9p031_get_pad_crop(mt9p031, fh, crop->pad, 546cb7a01acSMauro Carvalho Chehab crop->which); 547cb7a01acSMauro Carvalho Chehab return 0; 548cb7a01acSMauro Carvalho Chehab } 549cb7a01acSMauro Carvalho Chehab 550cb7a01acSMauro Carvalho Chehab static int mt9p031_set_crop(struct v4l2_subdev *subdev, 551cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_fh *fh, 552cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_crop *crop) 553cb7a01acSMauro Carvalho Chehab { 554cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev); 555cb7a01acSMauro Carvalho Chehab struct v4l2_mbus_framefmt *__format; 556cb7a01acSMauro Carvalho Chehab struct v4l2_rect *__crop; 557cb7a01acSMauro Carvalho Chehab struct v4l2_rect rect; 558cb7a01acSMauro Carvalho Chehab 559cb7a01acSMauro Carvalho Chehab /* Clamp the crop rectangle boundaries and align them to a multiple of 2 560cb7a01acSMauro Carvalho Chehab * pixels to ensure a GRBG Bayer pattern. 561cb7a01acSMauro Carvalho Chehab */ 562cb7a01acSMauro Carvalho Chehab rect.left = clamp(ALIGN(crop->rect.left, 2), MT9P031_COLUMN_START_MIN, 563cb7a01acSMauro Carvalho Chehab MT9P031_COLUMN_START_MAX); 564cb7a01acSMauro Carvalho Chehab rect.top = clamp(ALIGN(crop->rect.top, 2), MT9P031_ROW_START_MIN, 565cb7a01acSMauro Carvalho Chehab MT9P031_ROW_START_MAX); 566cb7a01acSMauro Carvalho Chehab rect.width = clamp(ALIGN(crop->rect.width, 2), 567cb7a01acSMauro Carvalho Chehab MT9P031_WINDOW_WIDTH_MIN, 568cb7a01acSMauro Carvalho Chehab MT9P031_WINDOW_WIDTH_MAX); 569cb7a01acSMauro Carvalho Chehab rect.height = clamp(ALIGN(crop->rect.height, 2), 570cb7a01acSMauro Carvalho Chehab MT9P031_WINDOW_HEIGHT_MIN, 571cb7a01acSMauro Carvalho Chehab MT9P031_WINDOW_HEIGHT_MAX); 572cb7a01acSMauro Carvalho Chehab 573cb7a01acSMauro Carvalho Chehab rect.width = min(rect.width, MT9P031_PIXEL_ARRAY_WIDTH - rect.left); 574cb7a01acSMauro Carvalho Chehab rect.height = min(rect.height, MT9P031_PIXEL_ARRAY_HEIGHT - rect.top); 575cb7a01acSMauro Carvalho Chehab 576cb7a01acSMauro Carvalho Chehab __crop = __mt9p031_get_pad_crop(mt9p031, fh, crop->pad, crop->which); 577cb7a01acSMauro Carvalho Chehab 578cb7a01acSMauro Carvalho Chehab if (rect.width != __crop->width || rect.height != __crop->height) { 579cb7a01acSMauro Carvalho Chehab /* Reset the output image size if the crop rectangle size has 580cb7a01acSMauro Carvalho Chehab * been modified. 581cb7a01acSMauro Carvalho Chehab */ 582cb7a01acSMauro Carvalho Chehab __format = __mt9p031_get_pad_format(mt9p031, fh, crop->pad, 583cb7a01acSMauro Carvalho Chehab crop->which); 584cb7a01acSMauro Carvalho Chehab __format->width = rect.width; 585cb7a01acSMauro Carvalho Chehab __format->height = rect.height; 586cb7a01acSMauro Carvalho Chehab } 587cb7a01acSMauro Carvalho Chehab 588cb7a01acSMauro Carvalho Chehab *__crop = rect; 589cb7a01acSMauro Carvalho Chehab crop->rect = rect; 590cb7a01acSMauro Carvalho Chehab 591cb7a01acSMauro Carvalho Chehab return 0; 592cb7a01acSMauro Carvalho Chehab } 593cb7a01acSMauro Carvalho Chehab 594cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------------- 595cb7a01acSMauro Carvalho Chehab * V4L2 subdev control operations 596cb7a01acSMauro Carvalho Chehab */ 597cb7a01acSMauro Carvalho Chehab 598cb7a01acSMauro Carvalho Chehab #define V4L2_CID_BLC_AUTO (V4L2_CID_USER_BASE | 0x1002) 599cb7a01acSMauro Carvalho Chehab #define V4L2_CID_BLC_TARGET_LEVEL (V4L2_CID_USER_BASE | 0x1003) 600cb7a01acSMauro Carvalho Chehab #define V4L2_CID_BLC_ANALOG_OFFSET (V4L2_CID_USER_BASE | 0x1004) 601cb7a01acSMauro Carvalho Chehab #define V4L2_CID_BLC_DIGITAL_OFFSET (V4L2_CID_USER_BASE | 0x1005) 602cb7a01acSMauro Carvalho Chehab 603cb7a01acSMauro Carvalho Chehab static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) 604cb7a01acSMauro Carvalho Chehab { 605cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = 606cb7a01acSMauro Carvalho Chehab container_of(ctrl->handler, struct mt9p031, ctrls); 607cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); 608cb7a01acSMauro Carvalho Chehab u16 data; 609cb7a01acSMauro Carvalho Chehab int ret; 610cb7a01acSMauro Carvalho Chehab 611cb7a01acSMauro Carvalho Chehab switch (ctrl->id) { 612cb7a01acSMauro Carvalho Chehab case V4L2_CID_EXPOSURE: 613cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_SHUTTER_WIDTH_UPPER, 614cb7a01acSMauro Carvalho Chehab (ctrl->val >> 16) & 0xffff); 615cb7a01acSMauro Carvalho Chehab if (ret < 0) 616cb7a01acSMauro Carvalho Chehab return ret; 617cb7a01acSMauro Carvalho Chehab 618cb7a01acSMauro Carvalho Chehab return mt9p031_write(client, MT9P031_SHUTTER_WIDTH_LOWER, 619cb7a01acSMauro Carvalho Chehab ctrl->val & 0xffff); 620cb7a01acSMauro Carvalho Chehab 621cb7a01acSMauro Carvalho Chehab case V4L2_CID_GAIN: 622cb7a01acSMauro Carvalho Chehab /* Gain is controlled by 2 analog stages and a digital stage. 623cb7a01acSMauro Carvalho Chehab * Valid values for the 3 stages are 624cb7a01acSMauro Carvalho Chehab * 625cb7a01acSMauro Carvalho Chehab * Stage Min Max Step 626cb7a01acSMauro Carvalho Chehab * ------------------------------------------ 627cb7a01acSMauro Carvalho Chehab * First analog stage x1 x2 1 628cb7a01acSMauro Carvalho Chehab * Second analog stage x1 x4 0.125 629cb7a01acSMauro Carvalho Chehab * Digital stage x1 x16 0.125 630cb7a01acSMauro Carvalho Chehab * 631cb7a01acSMauro Carvalho Chehab * To minimize noise, the gain stages should be used in the 632cb7a01acSMauro Carvalho Chehab * second analog stage, first analog stage, digital stage order. 633cb7a01acSMauro Carvalho Chehab * Gain from a previous stage should be pushed to its maximum 634cb7a01acSMauro Carvalho Chehab * value before the next stage is used. 635cb7a01acSMauro Carvalho Chehab */ 636cb7a01acSMauro Carvalho Chehab if (ctrl->val <= 32) { 637cb7a01acSMauro Carvalho Chehab data = ctrl->val; 638cb7a01acSMauro Carvalho Chehab } else if (ctrl->val <= 64) { 639cb7a01acSMauro Carvalho Chehab ctrl->val &= ~1; 640cb7a01acSMauro Carvalho Chehab data = (1 << 6) | (ctrl->val >> 1); 641cb7a01acSMauro Carvalho Chehab } else { 642cb7a01acSMauro Carvalho Chehab ctrl->val &= ~7; 643cb7a01acSMauro Carvalho Chehab data = ((ctrl->val - 64) << 5) | (1 << 6) | 32; 644cb7a01acSMauro Carvalho Chehab } 645cb7a01acSMauro Carvalho Chehab 646cb7a01acSMauro Carvalho Chehab return mt9p031_write(client, MT9P031_GLOBAL_GAIN, data); 647cb7a01acSMauro Carvalho Chehab 648cb7a01acSMauro Carvalho Chehab case V4L2_CID_HFLIP: 649cb7a01acSMauro Carvalho Chehab if (ctrl->val) 650cb7a01acSMauro Carvalho Chehab return mt9p031_set_mode2(mt9p031, 651cb7a01acSMauro Carvalho Chehab 0, MT9P031_READ_MODE_2_COL_MIR); 652cb7a01acSMauro Carvalho Chehab else 653cb7a01acSMauro Carvalho Chehab return mt9p031_set_mode2(mt9p031, 654cb7a01acSMauro Carvalho Chehab MT9P031_READ_MODE_2_COL_MIR, 0); 655cb7a01acSMauro Carvalho Chehab 656cb7a01acSMauro Carvalho Chehab case V4L2_CID_VFLIP: 657cb7a01acSMauro Carvalho Chehab if (ctrl->val) 658cb7a01acSMauro Carvalho Chehab return mt9p031_set_mode2(mt9p031, 659cb7a01acSMauro Carvalho Chehab 0, MT9P031_READ_MODE_2_ROW_MIR); 660cb7a01acSMauro Carvalho Chehab else 661cb7a01acSMauro Carvalho Chehab return mt9p031_set_mode2(mt9p031, 662cb7a01acSMauro Carvalho Chehab MT9P031_READ_MODE_2_ROW_MIR, 0); 663cb7a01acSMauro Carvalho Chehab 664cb7a01acSMauro Carvalho Chehab case V4L2_CID_TEST_PATTERN: 665cb7a01acSMauro Carvalho Chehab if (!ctrl->val) { 666cb7a01acSMauro Carvalho Chehab /* Restore the black level compensation settings. */ 667cb7a01acSMauro Carvalho Chehab if (mt9p031->blc_auto->cur.val != 0) { 668cb7a01acSMauro Carvalho Chehab ret = mt9p031_s_ctrl(mt9p031->blc_auto); 669cb7a01acSMauro Carvalho Chehab if (ret < 0) 670cb7a01acSMauro Carvalho Chehab return ret; 671cb7a01acSMauro Carvalho Chehab } 672cb7a01acSMauro Carvalho Chehab if (mt9p031->blc_offset->cur.val != 0) { 673cb7a01acSMauro Carvalho Chehab ret = mt9p031_s_ctrl(mt9p031->blc_offset); 674cb7a01acSMauro Carvalho Chehab if (ret < 0) 675cb7a01acSMauro Carvalho Chehab return ret; 676cb7a01acSMauro Carvalho Chehab } 677cb7a01acSMauro Carvalho Chehab return mt9p031_write(client, MT9P031_TEST_PATTERN, 678cb7a01acSMauro Carvalho Chehab MT9P031_TEST_PATTERN_DISABLE); 679cb7a01acSMauro Carvalho Chehab } 680cb7a01acSMauro Carvalho Chehab 681cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_TEST_PATTERN_GREEN, 0x05a0); 682cb7a01acSMauro Carvalho Chehab if (ret < 0) 683cb7a01acSMauro Carvalho Chehab return ret; 684cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_TEST_PATTERN_RED, 0x0a50); 685cb7a01acSMauro Carvalho Chehab if (ret < 0) 686cb7a01acSMauro Carvalho Chehab return ret; 687cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_TEST_PATTERN_BLUE, 0x0aa0); 688cb7a01acSMauro Carvalho Chehab if (ret < 0) 689cb7a01acSMauro Carvalho Chehab return ret; 690cb7a01acSMauro Carvalho Chehab 691cb7a01acSMauro Carvalho Chehab /* Disable digital black level compensation when using a test 692cb7a01acSMauro Carvalho Chehab * pattern. 693cb7a01acSMauro Carvalho Chehab */ 694cb7a01acSMauro Carvalho Chehab ret = mt9p031_set_mode2(mt9p031, MT9P031_READ_MODE_2_ROW_BLC, 695cb7a01acSMauro Carvalho Chehab 0); 696cb7a01acSMauro Carvalho Chehab if (ret < 0) 697cb7a01acSMauro Carvalho Chehab return ret; 698cb7a01acSMauro Carvalho Chehab 699cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, 0); 700cb7a01acSMauro Carvalho Chehab if (ret < 0) 701cb7a01acSMauro Carvalho Chehab return ret; 702cb7a01acSMauro Carvalho Chehab 703cb7a01acSMauro Carvalho Chehab return mt9p031_write(client, MT9P031_TEST_PATTERN, 704cb7a01acSMauro Carvalho Chehab ((ctrl->val - 1) << MT9P031_TEST_PATTERN_SHIFT) 705cb7a01acSMauro Carvalho Chehab | MT9P031_TEST_PATTERN_ENABLE); 706cb7a01acSMauro Carvalho Chehab 707cb7a01acSMauro Carvalho Chehab case V4L2_CID_BLC_AUTO: 708cb7a01acSMauro Carvalho Chehab ret = mt9p031_set_mode2(mt9p031, 709cb7a01acSMauro Carvalho Chehab ctrl->val ? 0 : MT9P031_READ_MODE_2_ROW_BLC, 710cb7a01acSMauro Carvalho Chehab ctrl->val ? MT9P031_READ_MODE_2_ROW_BLC : 0); 711cb7a01acSMauro Carvalho Chehab if (ret < 0) 712cb7a01acSMauro Carvalho Chehab return ret; 713cb7a01acSMauro Carvalho Chehab 714cb7a01acSMauro Carvalho Chehab return mt9p031_write(client, MT9P031_BLACK_LEVEL_CALIBRATION, 715cb7a01acSMauro Carvalho Chehab ctrl->val ? 0 : MT9P031_BLC_MANUAL_BLC); 716cb7a01acSMauro Carvalho Chehab 717cb7a01acSMauro Carvalho Chehab case V4L2_CID_BLC_TARGET_LEVEL: 718cb7a01acSMauro Carvalho Chehab return mt9p031_write(client, MT9P031_ROW_BLACK_TARGET, 719cb7a01acSMauro Carvalho Chehab ctrl->val); 720cb7a01acSMauro Carvalho Chehab 721cb7a01acSMauro Carvalho Chehab case V4L2_CID_BLC_ANALOG_OFFSET: 722cb7a01acSMauro Carvalho Chehab data = ctrl->val & ((1 << 9) - 1); 723cb7a01acSMauro Carvalho Chehab 724cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_GREEN1_OFFSET, data); 725cb7a01acSMauro Carvalho Chehab if (ret < 0) 726cb7a01acSMauro Carvalho Chehab return ret; 727cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_GREEN2_OFFSET, data); 728cb7a01acSMauro Carvalho Chehab if (ret < 0) 729cb7a01acSMauro Carvalho Chehab return ret; 730cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_RED_OFFSET, data); 731cb7a01acSMauro Carvalho Chehab if (ret < 0) 732cb7a01acSMauro Carvalho Chehab return ret; 733cb7a01acSMauro Carvalho Chehab return mt9p031_write(client, MT9P031_BLUE_OFFSET, data); 734cb7a01acSMauro Carvalho Chehab 735cb7a01acSMauro Carvalho Chehab case V4L2_CID_BLC_DIGITAL_OFFSET: 736cb7a01acSMauro Carvalho Chehab return mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, 737cb7a01acSMauro Carvalho Chehab ctrl->val & ((1 << 12) - 1)); 738cb7a01acSMauro Carvalho Chehab } 739cb7a01acSMauro Carvalho Chehab 740cb7a01acSMauro Carvalho Chehab return 0; 741cb7a01acSMauro Carvalho Chehab } 742cb7a01acSMauro Carvalho Chehab 743cb7a01acSMauro Carvalho Chehab static struct v4l2_ctrl_ops mt9p031_ctrl_ops = { 744cb7a01acSMauro Carvalho Chehab .s_ctrl = mt9p031_s_ctrl, 745cb7a01acSMauro Carvalho Chehab }; 746cb7a01acSMauro Carvalho Chehab 747cb7a01acSMauro Carvalho Chehab static const char * const mt9p031_test_pattern_menu[] = { 748cb7a01acSMauro Carvalho Chehab "Disabled", 749cb7a01acSMauro Carvalho Chehab "Color Field", 750cb7a01acSMauro Carvalho Chehab "Horizontal Gradient", 751cb7a01acSMauro Carvalho Chehab "Vertical Gradient", 752cb7a01acSMauro Carvalho Chehab "Diagonal Gradient", 753cb7a01acSMauro Carvalho Chehab "Classic Test Pattern", 754cb7a01acSMauro Carvalho Chehab "Walking 1s", 755cb7a01acSMauro Carvalho Chehab "Monochrome Horizontal Bars", 756cb7a01acSMauro Carvalho Chehab "Monochrome Vertical Bars", 757cb7a01acSMauro Carvalho Chehab "Vertical Color Bars", 758cb7a01acSMauro Carvalho Chehab }; 759cb7a01acSMauro Carvalho Chehab 760cb7a01acSMauro Carvalho Chehab static const struct v4l2_ctrl_config mt9p031_ctrls[] = { 761cb7a01acSMauro Carvalho Chehab { 762cb7a01acSMauro Carvalho Chehab .ops = &mt9p031_ctrl_ops, 763cb7a01acSMauro Carvalho Chehab .id = V4L2_CID_BLC_AUTO, 764cb7a01acSMauro Carvalho Chehab .type = V4L2_CTRL_TYPE_BOOLEAN, 765cb7a01acSMauro Carvalho Chehab .name = "BLC, Auto", 766cb7a01acSMauro Carvalho Chehab .min = 0, 767cb7a01acSMauro Carvalho Chehab .max = 1, 768cb7a01acSMauro Carvalho Chehab .step = 1, 769cb7a01acSMauro Carvalho Chehab .def = 1, 770cb7a01acSMauro Carvalho Chehab .flags = 0, 771cb7a01acSMauro Carvalho Chehab }, { 772cb7a01acSMauro Carvalho Chehab .ops = &mt9p031_ctrl_ops, 773cb7a01acSMauro Carvalho Chehab .id = V4L2_CID_BLC_TARGET_LEVEL, 774cb7a01acSMauro Carvalho Chehab .type = V4L2_CTRL_TYPE_INTEGER, 775cb7a01acSMauro Carvalho Chehab .name = "BLC Target Level", 776cb7a01acSMauro Carvalho Chehab .min = 0, 777cb7a01acSMauro Carvalho Chehab .max = 4095, 778cb7a01acSMauro Carvalho Chehab .step = 1, 779cb7a01acSMauro Carvalho Chehab .def = 168, 780cb7a01acSMauro Carvalho Chehab .flags = 0, 781cb7a01acSMauro Carvalho Chehab }, { 782cb7a01acSMauro Carvalho Chehab .ops = &mt9p031_ctrl_ops, 783cb7a01acSMauro Carvalho Chehab .id = V4L2_CID_BLC_ANALOG_OFFSET, 784cb7a01acSMauro Carvalho Chehab .type = V4L2_CTRL_TYPE_INTEGER, 785cb7a01acSMauro Carvalho Chehab .name = "BLC Analog Offset", 786cb7a01acSMauro Carvalho Chehab .min = -255, 787cb7a01acSMauro Carvalho Chehab .max = 255, 788cb7a01acSMauro Carvalho Chehab .step = 1, 789cb7a01acSMauro Carvalho Chehab .def = 32, 790cb7a01acSMauro Carvalho Chehab .flags = 0, 791cb7a01acSMauro Carvalho Chehab }, { 792cb7a01acSMauro Carvalho Chehab .ops = &mt9p031_ctrl_ops, 793cb7a01acSMauro Carvalho Chehab .id = V4L2_CID_BLC_DIGITAL_OFFSET, 794cb7a01acSMauro Carvalho Chehab .type = V4L2_CTRL_TYPE_INTEGER, 795cb7a01acSMauro Carvalho Chehab .name = "BLC Digital Offset", 796cb7a01acSMauro Carvalho Chehab .min = -2048, 797cb7a01acSMauro Carvalho Chehab .max = 2047, 798cb7a01acSMauro Carvalho Chehab .step = 1, 799cb7a01acSMauro Carvalho Chehab .def = 40, 800cb7a01acSMauro Carvalho Chehab .flags = 0, 801cb7a01acSMauro Carvalho Chehab } 802cb7a01acSMauro Carvalho Chehab }; 803cb7a01acSMauro Carvalho Chehab 804cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------------- 805cb7a01acSMauro Carvalho Chehab * V4L2 subdev core operations 806cb7a01acSMauro Carvalho Chehab */ 807cb7a01acSMauro Carvalho Chehab 808cb7a01acSMauro Carvalho Chehab static int mt9p031_set_power(struct v4l2_subdev *subdev, int on) 809cb7a01acSMauro Carvalho Chehab { 810cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev); 811cb7a01acSMauro Carvalho Chehab int ret = 0; 812cb7a01acSMauro Carvalho Chehab 813cb7a01acSMauro Carvalho Chehab mutex_lock(&mt9p031->power_lock); 814cb7a01acSMauro Carvalho Chehab 815cb7a01acSMauro Carvalho Chehab /* If the power count is modified from 0 to != 0 or from != 0 to 0, 816cb7a01acSMauro Carvalho Chehab * update the power state. 817cb7a01acSMauro Carvalho Chehab */ 818cb7a01acSMauro Carvalho Chehab if (mt9p031->power_count == !on) { 819cb7a01acSMauro Carvalho Chehab ret = __mt9p031_set_power(mt9p031, !!on); 820cb7a01acSMauro Carvalho Chehab if (ret < 0) 821cb7a01acSMauro Carvalho Chehab goto out; 822cb7a01acSMauro Carvalho Chehab } 823cb7a01acSMauro Carvalho Chehab 824cb7a01acSMauro Carvalho Chehab /* Update the power count. */ 825cb7a01acSMauro Carvalho Chehab mt9p031->power_count += on ? 1 : -1; 826cb7a01acSMauro Carvalho Chehab WARN_ON(mt9p031->power_count < 0); 827cb7a01acSMauro Carvalho Chehab 828cb7a01acSMauro Carvalho Chehab out: 829cb7a01acSMauro Carvalho Chehab mutex_unlock(&mt9p031->power_lock); 830cb7a01acSMauro Carvalho Chehab return ret; 831cb7a01acSMauro Carvalho Chehab } 832cb7a01acSMauro Carvalho Chehab 833cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------------- 834cb7a01acSMauro Carvalho Chehab * V4L2 subdev internal operations 835cb7a01acSMauro Carvalho Chehab */ 836cb7a01acSMauro Carvalho Chehab 837cb7a01acSMauro Carvalho Chehab static int mt9p031_registered(struct v4l2_subdev *subdev) 838cb7a01acSMauro Carvalho Chehab { 839cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(subdev); 840cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev); 841cb7a01acSMauro Carvalho Chehab s32 data; 842cb7a01acSMauro Carvalho Chehab int ret; 843cb7a01acSMauro Carvalho Chehab 844cb7a01acSMauro Carvalho Chehab ret = mt9p031_power_on(mt9p031); 845cb7a01acSMauro Carvalho Chehab if (ret < 0) { 846cb7a01acSMauro Carvalho Chehab dev_err(&client->dev, "MT9P031 power up failed\n"); 847cb7a01acSMauro Carvalho Chehab return ret; 848cb7a01acSMauro Carvalho Chehab } 849cb7a01acSMauro Carvalho Chehab 850cb7a01acSMauro Carvalho Chehab /* Read out the chip version register */ 851cb7a01acSMauro Carvalho Chehab data = mt9p031_read(client, MT9P031_CHIP_VERSION); 852cb7a01acSMauro Carvalho Chehab if (data != MT9P031_CHIP_VERSION_VALUE) { 853cb7a01acSMauro Carvalho Chehab dev_err(&client->dev, "MT9P031 not detected, wrong version " 854cb7a01acSMauro Carvalho Chehab "0x%04x\n", data); 855cb7a01acSMauro Carvalho Chehab return -ENODEV; 856cb7a01acSMauro Carvalho Chehab } 857cb7a01acSMauro Carvalho Chehab 858cb7a01acSMauro Carvalho Chehab mt9p031_power_off(mt9p031); 859cb7a01acSMauro Carvalho Chehab 860cb7a01acSMauro Carvalho Chehab dev_info(&client->dev, "MT9P031 detected at address 0x%02x\n", 861cb7a01acSMauro Carvalho Chehab client->addr); 862cb7a01acSMauro Carvalho Chehab 863cb7a01acSMauro Carvalho Chehab return ret; 864cb7a01acSMauro Carvalho Chehab } 865cb7a01acSMauro Carvalho Chehab 866cb7a01acSMauro Carvalho Chehab static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) 867cb7a01acSMauro Carvalho Chehab { 868cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev); 869cb7a01acSMauro Carvalho Chehab struct v4l2_mbus_framefmt *format; 870cb7a01acSMauro Carvalho Chehab struct v4l2_rect *crop; 871cb7a01acSMauro Carvalho Chehab 872cb7a01acSMauro Carvalho Chehab crop = v4l2_subdev_get_try_crop(fh, 0); 873cb7a01acSMauro Carvalho Chehab crop->left = MT9P031_COLUMN_START_DEF; 874cb7a01acSMauro Carvalho Chehab crop->top = MT9P031_ROW_START_DEF; 875cb7a01acSMauro Carvalho Chehab crop->width = MT9P031_WINDOW_WIDTH_DEF; 876cb7a01acSMauro Carvalho Chehab crop->height = MT9P031_WINDOW_HEIGHT_DEF; 877cb7a01acSMauro Carvalho Chehab 878cb7a01acSMauro Carvalho Chehab format = v4l2_subdev_get_try_format(fh, 0); 879cb7a01acSMauro Carvalho Chehab 880cb7a01acSMauro Carvalho Chehab if (mt9p031->model == MT9P031_MODEL_MONOCHROME) 881cb7a01acSMauro Carvalho Chehab format->code = V4L2_MBUS_FMT_Y12_1X12; 882cb7a01acSMauro Carvalho Chehab else 883cb7a01acSMauro Carvalho Chehab format->code = V4L2_MBUS_FMT_SGRBG12_1X12; 884cb7a01acSMauro Carvalho Chehab 885cb7a01acSMauro Carvalho Chehab format->width = MT9P031_WINDOW_WIDTH_DEF; 886cb7a01acSMauro Carvalho Chehab format->height = MT9P031_WINDOW_HEIGHT_DEF; 887cb7a01acSMauro Carvalho Chehab format->field = V4L2_FIELD_NONE; 888cb7a01acSMauro Carvalho Chehab format->colorspace = V4L2_COLORSPACE_SRGB; 889cb7a01acSMauro Carvalho Chehab 890cb7a01acSMauro Carvalho Chehab return mt9p031_set_power(subdev, 1); 891cb7a01acSMauro Carvalho Chehab } 892cb7a01acSMauro Carvalho Chehab 893cb7a01acSMauro Carvalho Chehab static int mt9p031_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) 894cb7a01acSMauro Carvalho Chehab { 895cb7a01acSMauro Carvalho Chehab return mt9p031_set_power(subdev, 0); 896cb7a01acSMauro Carvalho Chehab } 897cb7a01acSMauro Carvalho Chehab 898cb7a01acSMauro Carvalho Chehab static struct v4l2_subdev_core_ops mt9p031_subdev_core_ops = { 899cb7a01acSMauro Carvalho Chehab .s_power = mt9p031_set_power, 900cb7a01acSMauro Carvalho Chehab }; 901cb7a01acSMauro Carvalho Chehab 902cb7a01acSMauro Carvalho Chehab static struct v4l2_subdev_video_ops mt9p031_subdev_video_ops = { 903cb7a01acSMauro Carvalho Chehab .s_stream = mt9p031_s_stream, 904cb7a01acSMauro Carvalho Chehab }; 905cb7a01acSMauro Carvalho Chehab 906cb7a01acSMauro Carvalho Chehab static struct v4l2_subdev_pad_ops mt9p031_subdev_pad_ops = { 907cb7a01acSMauro Carvalho Chehab .enum_mbus_code = mt9p031_enum_mbus_code, 908cb7a01acSMauro Carvalho Chehab .enum_frame_size = mt9p031_enum_frame_size, 909cb7a01acSMauro Carvalho Chehab .get_fmt = mt9p031_get_format, 910cb7a01acSMauro Carvalho Chehab .set_fmt = mt9p031_set_format, 911cb7a01acSMauro Carvalho Chehab .get_crop = mt9p031_get_crop, 912cb7a01acSMauro Carvalho Chehab .set_crop = mt9p031_set_crop, 913cb7a01acSMauro Carvalho Chehab }; 914cb7a01acSMauro Carvalho Chehab 915cb7a01acSMauro Carvalho Chehab static struct v4l2_subdev_ops mt9p031_subdev_ops = { 916cb7a01acSMauro Carvalho Chehab .core = &mt9p031_subdev_core_ops, 917cb7a01acSMauro Carvalho Chehab .video = &mt9p031_subdev_video_ops, 918cb7a01acSMauro Carvalho Chehab .pad = &mt9p031_subdev_pad_ops, 919cb7a01acSMauro Carvalho Chehab }; 920cb7a01acSMauro Carvalho Chehab 921cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_internal_ops mt9p031_subdev_internal_ops = { 922cb7a01acSMauro Carvalho Chehab .registered = mt9p031_registered, 923cb7a01acSMauro Carvalho Chehab .open = mt9p031_open, 924cb7a01acSMauro Carvalho Chehab .close = mt9p031_close, 925cb7a01acSMauro Carvalho Chehab }; 926cb7a01acSMauro Carvalho Chehab 927cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------------- 928cb7a01acSMauro Carvalho Chehab * Driver initialization and probing 929cb7a01acSMauro Carvalho Chehab */ 930cb7a01acSMauro Carvalho Chehab 931cb7a01acSMauro Carvalho Chehab static int mt9p031_probe(struct i2c_client *client, 932cb7a01acSMauro Carvalho Chehab const struct i2c_device_id *did) 933cb7a01acSMauro Carvalho Chehab { 934cb7a01acSMauro Carvalho Chehab struct mt9p031_platform_data *pdata = client->dev.platform_data; 935cb7a01acSMauro Carvalho Chehab struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); 936cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031; 937cb7a01acSMauro Carvalho Chehab unsigned int i; 938cb7a01acSMauro Carvalho Chehab int ret; 939cb7a01acSMauro Carvalho Chehab 940cb7a01acSMauro Carvalho Chehab if (pdata == NULL) { 941cb7a01acSMauro Carvalho Chehab dev_err(&client->dev, "No platform data\n"); 942cb7a01acSMauro Carvalho Chehab return -EINVAL; 943cb7a01acSMauro Carvalho Chehab } 944cb7a01acSMauro Carvalho Chehab 945cb7a01acSMauro Carvalho Chehab if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { 946cb7a01acSMauro Carvalho Chehab dev_warn(&client->dev, 947cb7a01acSMauro Carvalho Chehab "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); 948cb7a01acSMauro Carvalho Chehab return -EIO; 949cb7a01acSMauro Carvalho Chehab } 950cb7a01acSMauro Carvalho Chehab 95137b9f211SLaurent Pinchart mt9p031 = devm_kzalloc(&client->dev, sizeof(*mt9p031), GFP_KERNEL); 952cb7a01acSMauro Carvalho Chehab if (mt9p031 == NULL) 953cb7a01acSMauro Carvalho Chehab return -ENOMEM; 954cb7a01acSMauro Carvalho Chehab 955cb7a01acSMauro Carvalho Chehab mt9p031->pdata = pdata; 956cb7a01acSMauro Carvalho Chehab mt9p031->output_control = MT9P031_OUTPUT_CONTROL_DEF; 957cb7a01acSMauro Carvalho Chehab mt9p031->mode2 = MT9P031_READ_MODE_2_ROW_BLC; 958cb7a01acSMauro Carvalho Chehab mt9p031->model = did->driver_data; 959cb7a01acSMauro Carvalho Chehab mt9p031->reset = -1; 960cb7a01acSMauro Carvalho Chehab 96197f21276SLaurent Pinchart mt9p031->vaa = devm_regulator_get(&client->dev, "vaa"); 96297f21276SLaurent Pinchart mt9p031->vdd = devm_regulator_get(&client->dev, "vdd"); 96397f21276SLaurent Pinchart mt9p031->vdd_io = devm_regulator_get(&client->dev, "vdd_io"); 96497f21276SLaurent Pinchart 96597f21276SLaurent Pinchart if (IS_ERR(mt9p031->vaa) || IS_ERR(mt9p031->vdd) || 96697f21276SLaurent Pinchart IS_ERR(mt9p031->vdd_io)) { 96797f21276SLaurent Pinchart dev_err(&client->dev, "Unable to get regulators\n"); 96897f21276SLaurent Pinchart return -ENODEV; 96997f21276SLaurent Pinchart } 97097f21276SLaurent Pinchart 971b28d7017SLad, Prabhakar v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 6); 972cb7a01acSMauro Carvalho Chehab 973cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, 974cb7a01acSMauro Carvalho Chehab V4L2_CID_EXPOSURE, MT9P031_SHUTTER_WIDTH_MIN, 975cb7a01acSMauro Carvalho Chehab MT9P031_SHUTTER_WIDTH_MAX, 1, 976cb7a01acSMauro Carvalho Chehab MT9P031_SHUTTER_WIDTH_DEF); 977cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, 978cb7a01acSMauro Carvalho Chehab V4L2_CID_GAIN, MT9P031_GLOBAL_GAIN_MIN, 979cb7a01acSMauro Carvalho Chehab MT9P031_GLOBAL_GAIN_MAX, 1, MT9P031_GLOBAL_GAIN_DEF); 980cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, 981cb7a01acSMauro Carvalho Chehab V4L2_CID_HFLIP, 0, 1, 1, 0); 982cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, 983cb7a01acSMauro Carvalho Chehab V4L2_CID_VFLIP, 0, 1, 1, 0); 984cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, 985cb7a01acSMauro Carvalho Chehab V4L2_CID_PIXEL_RATE, pdata->target_freq, 986cb7a01acSMauro Carvalho Chehab pdata->target_freq, 1, pdata->target_freq); 987b28d7017SLad, Prabhakar v4l2_ctrl_new_std_menu_items(&mt9p031->ctrls, &mt9p031_ctrl_ops, 988b28d7017SLad, Prabhakar V4L2_CID_TEST_PATTERN, 989b28d7017SLad, Prabhakar ARRAY_SIZE(mt9p031_test_pattern_menu) - 1, 0, 990b28d7017SLad, Prabhakar 0, mt9p031_test_pattern_menu); 991cb7a01acSMauro Carvalho Chehab 992cb7a01acSMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(mt9p031_ctrls); ++i) 993cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_custom(&mt9p031->ctrls, &mt9p031_ctrls[i], NULL); 994cb7a01acSMauro Carvalho Chehab 995cb7a01acSMauro Carvalho Chehab mt9p031->subdev.ctrl_handler = &mt9p031->ctrls; 996cb7a01acSMauro Carvalho Chehab 997cb7a01acSMauro Carvalho Chehab if (mt9p031->ctrls.error) { 998cb7a01acSMauro Carvalho Chehab printk(KERN_INFO "%s: control initialization error %d\n", 999cb7a01acSMauro Carvalho Chehab __func__, mt9p031->ctrls.error); 1000cb7a01acSMauro Carvalho Chehab ret = mt9p031->ctrls.error; 1001cb7a01acSMauro Carvalho Chehab goto done; 1002cb7a01acSMauro Carvalho Chehab } 1003cb7a01acSMauro Carvalho Chehab 1004cb7a01acSMauro Carvalho Chehab mt9p031->blc_auto = v4l2_ctrl_find(&mt9p031->ctrls, V4L2_CID_BLC_AUTO); 1005cb7a01acSMauro Carvalho Chehab mt9p031->blc_offset = v4l2_ctrl_find(&mt9p031->ctrls, 1006cb7a01acSMauro Carvalho Chehab V4L2_CID_BLC_DIGITAL_OFFSET); 1007cb7a01acSMauro Carvalho Chehab 1008cb7a01acSMauro Carvalho Chehab mutex_init(&mt9p031->power_lock); 1009cb7a01acSMauro Carvalho Chehab v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops); 1010cb7a01acSMauro Carvalho Chehab mt9p031->subdev.internal_ops = &mt9p031_subdev_internal_ops; 1011cb7a01acSMauro Carvalho Chehab 1012cb7a01acSMauro Carvalho Chehab mt9p031->pad.flags = MEDIA_PAD_FL_SOURCE; 1013cb7a01acSMauro Carvalho Chehab ret = media_entity_init(&mt9p031->subdev.entity, 1, &mt9p031->pad, 0); 1014cb7a01acSMauro Carvalho Chehab if (ret < 0) 1015cb7a01acSMauro Carvalho Chehab goto done; 1016cb7a01acSMauro Carvalho Chehab 1017cb7a01acSMauro Carvalho Chehab mt9p031->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 1018cb7a01acSMauro Carvalho Chehab 1019cb7a01acSMauro Carvalho Chehab mt9p031->crop.width = MT9P031_WINDOW_WIDTH_DEF; 1020cb7a01acSMauro Carvalho Chehab mt9p031->crop.height = MT9P031_WINDOW_HEIGHT_DEF; 1021cb7a01acSMauro Carvalho Chehab mt9p031->crop.left = MT9P031_COLUMN_START_DEF; 1022cb7a01acSMauro Carvalho Chehab mt9p031->crop.top = MT9P031_ROW_START_DEF; 1023cb7a01acSMauro Carvalho Chehab 1024cb7a01acSMauro Carvalho Chehab if (mt9p031->model == MT9P031_MODEL_MONOCHROME) 1025cb7a01acSMauro Carvalho Chehab mt9p031->format.code = V4L2_MBUS_FMT_Y12_1X12; 1026cb7a01acSMauro Carvalho Chehab else 1027cb7a01acSMauro Carvalho Chehab mt9p031->format.code = V4L2_MBUS_FMT_SGRBG12_1X12; 1028cb7a01acSMauro Carvalho Chehab 1029cb7a01acSMauro Carvalho Chehab mt9p031->format.width = MT9P031_WINDOW_WIDTH_DEF; 1030cb7a01acSMauro Carvalho Chehab mt9p031->format.height = MT9P031_WINDOW_HEIGHT_DEF; 1031cb7a01acSMauro Carvalho Chehab mt9p031->format.field = V4L2_FIELD_NONE; 1032cb7a01acSMauro Carvalho Chehab mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB; 1033cb7a01acSMauro Carvalho Chehab 1034cb7a01acSMauro Carvalho Chehab if (pdata->reset != -1) { 103537b9f211SLaurent Pinchart ret = devm_gpio_request_one(&client->dev, pdata->reset, 103637b9f211SLaurent Pinchart GPIOF_OUT_INIT_LOW, "mt9p031_rst"); 1037cb7a01acSMauro Carvalho Chehab if (ret < 0) 1038cb7a01acSMauro Carvalho Chehab goto done; 1039cb7a01acSMauro Carvalho Chehab 1040cb7a01acSMauro Carvalho Chehab mt9p031->reset = pdata->reset; 1041cb7a01acSMauro Carvalho Chehab } 1042cb7a01acSMauro Carvalho Chehab 1043*d6749258SLaurent Pinchart ret = mt9p031_clk_setup(mt9p031); 1044cb7a01acSMauro Carvalho Chehab 1045cb7a01acSMauro Carvalho Chehab done: 1046cb7a01acSMauro Carvalho Chehab if (ret < 0) { 1047cb7a01acSMauro Carvalho Chehab v4l2_ctrl_handler_free(&mt9p031->ctrls); 1048cb7a01acSMauro Carvalho Chehab media_entity_cleanup(&mt9p031->subdev.entity); 1049cb7a01acSMauro Carvalho Chehab } 1050cb7a01acSMauro Carvalho Chehab 1051cb7a01acSMauro Carvalho Chehab return ret; 1052cb7a01acSMauro Carvalho Chehab } 1053cb7a01acSMauro Carvalho Chehab 1054cb7a01acSMauro Carvalho Chehab static int mt9p031_remove(struct i2c_client *client) 1055cb7a01acSMauro Carvalho Chehab { 1056cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *subdev = i2c_get_clientdata(client); 1057cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev); 1058cb7a01acSMauro Carvalho Chehab 1059cb7a01acSMauro Carvalho Chehab v4l2_ctrl_handler_free(&mt9p031->ctrls); 1060cb7a01acSMauro Carvalho Chehab v4l2_device_unregister_subdev(subdev); 1061cb7a01acSMauro Carvalho Chehab media_entity_cleanup(&subdev->entity); 1062cb7a01acSMauro Carvalho Chehab 1063cb7a01acSMauro Carvalho Chehab return 0; 1064cb7a01acSMauro Carvalho Chehab } 1065cb7a01acSMauro Carvalho Chehab 1066cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id mt9p031_id[] = { 1067cb7a01acSMauro Carvalho Chehab { "mt9p031", MT9P031_MODEL_COLOR }, 1068cb7a01acSMauro Carvalho Chehab { "mt9p031m", MT9P031_MODEL_MONOCHROME }, 1069cb7a01acSMauro Carvalho Chehab { } 1070cb7a01acSMauro Carvalho Chehab }; 1071cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, mt9p031_id); 1072cb7a01acSMauro Carvalho Chehab 1073cb7a01acSMauro Carvalho Chehab static struct i2c_driver mt9p031_i2c_driver = { 1074cb7a01acSMauro Carvalho Chehab .driver = { 1075cb7a01acSMauro Carvalho Chehab .name = "mt9p031", 1076cb7a01acSMauro Carvalho Chehab }, 1077cb7a01acSMauro Carvalho Chehab .probe = mt9p031_probe, 1078cb7a01acSMauro Carvalho Chehab .remove = mt9p031_remove, 1079cb7a01acSMauro Carvalho Chehab .id_table = mt9p031_id, 1080cb7a01acSMauro Carvalho Chehab }; 1081cb7a01acSMauro Carvalho Chehab 1082cb7a01acSMauro Carvalho Chehab module_i2c_driver(mt9p031_i2c_driver); 1083cb7a01acSMauro Carvalho Chehab 1084cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("Aptina MT9P031 Camera driver"); 1085cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Bastian Hecht <hechtb@gmail.com>"); 1086cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL v2"); 1087