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