1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2cb7a01acSMauro Carvalho Chehab /* 3cb7a01acSMauro Carvalho Chehab * Driver for MT9P031 CMOS Image Sensor from Aptina 4cb7a01acSMauro Carvalho Chehab * 5cb7a01acSMauro Carvalho Chehab * Copyright (C) 2011, Laurent Pinchart <laurent.pinchart@ideasonboard.com> 6cb7a01acSMauro Carvalho Chehab * Copyright (C) 2011, Javier Martin <javier.martin@vista-silicon.com> 7cb7a01acSMauro Carvalho Chehab * Copyright (C) 2011, Guennadi Liakhovetski <g.liakhovetski@gmx.de> 8cb7a01acSMauro Carvalho Chehab * 9cb7a01acSMauro Carvalho Chehab * Based on the MT9V032 driver and Bastian Hecht's code. 10cb7a01acSMauro Carvalho Chehab */ 11cb7a01acSMauro Carvalho Chehab 12d6749258SLaurent Pinchart #include <linux/clk.h> 13cb7a01acSMauro Carvalho Chehab #include <linux/delay.h> 14cb7a01acSMauro Carvalho Chehab #include <linux/device.h> 157c3be9f8SLaurent Pinchart #include <linux/gpio/consumer.h> 16cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h> 17cb7a01acSMauro Carvalho Chehab #include <linux/log2.h> 188d4da37cSLad, Prabhakar #include <linux/module.h> 1964695905SSachin Kamat #include <linux/of.h> 20fd9fdb78SPhilipp Zabel #include <linux/of_graph.h> 21cb7a01acSMauro Carvalho Chehab #include <linux/pm.h> 2297f21276SLaurent Pinchart #include <linux/regulator/consumer.h> 23cb7a01acSMauro Carvalho Chehab #include <linux/slab.h> 24cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h> 25cb7a01acSMauro Carvalho Chehab 26b5dcee22SMauro Carvalho Chehab #include <media/i2c/mt9p031.h> 279012d088SLad, Prabhakar #include <media/v4l2-async.h> 28cb7a01acSMauro Carvalho Chehab #include <media/v4l2-ctrls.h> 29cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h> 30cb7a01acSMauro Carvalho Chehab #include <media/v4l2-subdev.h> 31cb7a01acSMauro Carvalho Chehab 32cb7a01acSMauro Carvalho Chehab #include "aptina-pll.h" 33cb7a01acSMauro Carvalho Chehab 34cb7a01acSMauro Carvalho Chehab #define MT9P031_PIXEL_ARRAY_WIDTH 2752 35cb7a01acSMauro Carvalho Chehab #define MT9P031_PIXEL_ARRAY_HEIGHT 2004 36cb7a01acSMauro Carvalho Chehab 37cb7a01acSMauro Carvalho Chehab #define MT9P031_CHIP_VERSION 0x00 38cb7a01acSMauro Carvalho Chehab #define MT9P031_CHIP_VERSION_VALUE 0x1801 39cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_START 0x01 40cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_START_MIN 0 41cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_START_MAX 2004 42cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_START_DEF 54 43cb7a01acSMauro Carvalho Chehab #define MT9P031_COLUMN_START 0x02 44cb7a01acSMauro Carvalho Chehab #define MT9P031_COLUMN_START_MIN 0 45cb7a01acSMauro Carvalho Chehab #define MT9P031_COLUMN_START_MAX 2750 46cb7a01acSMauro Carvalho Chehab #define MT9P031_COLUMN_START_DEF 16 47cb7a01acSMauro Carvalho Chehab #define MT9P031_WINDOW_HEIGHT 0x03 48cb7a01acSMauro Carvalho Chehab #define MT9P031_WINDOW_HEIGHT_MIN 2 49cb7a01acSMauro Carvalho Chehab #define MT9P031_WINDOW_HEIGHT_MAX 2006 50cb7a01acSMauro Carvalho Chehab #define MT9P031_WINDOW_HEIGHT_DEF 1944 51cb7a01acSMauro Carvalho Chehab #define MT9P031_WINDOW_WIDTH 0x04 52cb7a01acSMauro Carvalho Chehab #define MT9P031_WINDOW_WIDTH_MIN 2 53cb7a01acSMauro Carvalho Chehab #define MT9P031_WINDOW_WIDTH_MAX 2752 54cb7a01acSMauro Carvalho Chehab #define MT9P031_WINDOW_WIDTH_DEF 2592 55cb7a01acSMauro Carvalho Chehab #define MT9P031_HORIZONTAL_BLANK 0x05 56cb7a01acSMauro Carvalho Chehab #define MT9P031_HORIZONTAL_BLANK_MIN 0 57cb7a01acSMauro Carvalho Chehab #define MT9P031_HORIZONTAL_BLANK_MAX 4095 58cb7a01acSMauro Carvalho Chehab #define MT9P031_VERTICAL_BLANK 0x06 595266c98bSLaurent Pinchart #define MT9P031_VERTICAL_BLANK_MIN 1 605266c98bSLaurent Pinchart #define MT9P031_VERTICAL_BLANK_MAX 4096 615266c98bSLaurent Pinchart #define MT9P031_VERTICAL_BLANK_DEF 26 62cb7a01acSMauro Carvalho Chehab #define MT9P031_OUTPUT_CONTROL 0x07 63cb7a01acSMauro Carvalho Chehab #define MT9P031_OUTPUT_CONTROL_CEN 2 64cb7a01acSMauro Carvalho Chehab #define MT9P031_OUTPUT_CONTROL_SYN 1 65cb7a01acSMauro Carvalho Chehab #define MT9P031_OUTPUT_CONTROL_DEF 0x1f82 66cb7a01acSMauro Carvalho Chehab #define MT9P031_SHUTTER_WIDTH_UPPER 0x08 67cb7a01acSMauro Carvalho Chehab #define MT9P031_SHUTTER_WIDTH_LOWER 0x09 68cb7a01acSMauro Carvalho Chehab #define MT9P031_SHUTTER_WIDTH_MIN 1 69cb7a01acSMauro Carvalho Chehab #define MT9P031_SHUTTER_WIDTH_MAX 1048575 70cb7a01acSMauro Carvalho Chehab #define MT9P031_SHUTTER_WIDTH_DEF 1943 71cb7a01acSMauro Carvalho Chehab #define MT9P031_PLL_CONTROL 0x10 72cb7a01acSMauro Carvalho Chehab #define MT9P031_PLL_CONTROL_PWROFF 0x0050 73cb7a01acSMauro Carvalho Chehab #define MT9P031_PLL_CONTROL_PWRON 0x0051 74cb7a01acSMauro Carvalho Chehab #define MT9P031_PLL_CONTROL_USEPLL 0x0052 75cb7a01acSMauro Carvalho Chehab #define MT9P031_PLL_CONFIG_1 0x11 76cb7a01acSMauro Carvalho Chehab #define MT9P031_PLL_CONFIG_2 0x12 77cb7a01acSMauro Carvalho Chehab #define MT9P031_PIXEL_CLOCK_CONTROL 0x0a 78a970449eSLaurent Pinchart #define MT9P031_PIXEL_CLOCK_INVERT (1 << 15) 79a970449eSLaurent Pinchart #define MT9P031_PIXEL_CLOCK_SHIFT(n) ((n) << 8) 80a970449eSLaurent Pinchart #define MT9P031_PIXEL_CLOCK_DIVIDE(n) ((n) << 0) 81cb7a01acSMauro Carvalho Chehab #define MT9P031_FRAME_RESTART 0x0b 82cb7a01acSMauro Carvalho Chehab #define MT9P031_SHUTTER_DELAY 0x0c 83cb7a01acSMauro Carvalho Chehab #define MT9P031_RST 0x0d 84cb7a01acSMauro Carvalho Chehab #define MT9P031_RST_ENABLE 1 85cb7a01acSMauro Carvalho Chehab #define MT9P031_RST_DISABLE 0 86cb7a01acSMauro Carvalho Chehab #define MT9P031_READ_MODE_1 0x1e 87cb7a01acSMauro Carvalho Chehab #define MT9P031_READ_MODE_2 0x20 88cb7a01acSMauro Carvalho Chehab #define MT9P031_READ_MODE_2_ROW_MIR (1 << 15) 89cb7a01acSMauro Carvalho Chehab #define MT9P031_READ_MODE_2_COL_MIR (1 << 14) 90cb7a01acSMauro Carvalho Chehab #define MT9P031_READ_MODE_2_ROW_BLC (1 << 6) 91cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_ADDRESS_MODE 0x22 92cb7a01acSMauro Carvalho Chehab #define MT9P031_COLUMN_ADDRESS_MODE 0x23 93cb7a01acSMauro Carvalho Chehab #define MT9P031_GLOBAL_GAIN 0x35 94cb7a01acSMauro Carvalho Chehab #define MT9P031_GLOBAL_GAIN_MIN 8 95cb7a01acSMauro Carvalho Chehab #define MT9P031_GLOBAL_GAIN_MAX 1024 96cb7a01acSMauro Carvalho Chehab #define MT9P031_GLOBAL_GAIN_DEF 8 97cb7a01acSMauro Carvalho Chehab #define MT9P031_GLOBAL_GAIN_MULT (1 << 6) 98cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_BLACK_TARGET 0x49 99cb7a01acSMauro Carvalho Chehab #define MT9P031_ROW_BLACK_DEF_OFFSET 0x4b 100cb7a01acSMauro Carvalho Chehab #define MT9P031_GREEN1_OFFSET 0x60 101cb7a01acSMauro Carvalho Chehab #define MT9P031_GREEN2_OFFSET 0x61 102cb7a01acSMauro Carvalho Chehab #define MT9P031_BLACK_LEVEL_CALIBRATION 0x62 103cb7a01acSMauro Carvalho Chehab #define MT9P031_BLC_MANUAL_BLC (1 << 0) 104cb7a01acSMauro Carvalho Chehab #define MT9P031_RED_OFFSET 0x63 105cb7a01acSMauro Carvalho Chehab #define MT9P031_BLUE_OFFSET 0x64 106cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN 0xa0 107cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN_SHIFT 3 108cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN_ENABLE (1 << 0) 109cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN_DISABLE (0 << 0) 110cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN_GREEN 0xa1 111cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN_RED 0xa2 112cb7a01acSMauro Carvalho Chehab #define MT9P031_TEST_PATTERN_BLUE 0xa3 113cb7a01acSMauro Carvalho Chehab 114cb7a01acSMauro Carvalho Chehab enum mt9p031_model { 115cb7a01acSMauro Carvalho Chehab MT9P031_MODEL_COLOR, 116cb7a01acSMauro Carvalho Chehab MT9P031_MODEL_MONOCHROME, 117cb7a01acSMauro Carvalho Chehab }; 118cb7a01acSMauro Carvalho Chehab 119cb7a01acSMauro Carvalho Chehab struct mt9p031 { 120cb7a01acSMauro Carvalho Chehab struct v4l2_subdev subdev; 121cb7a01acSMauro Carvalho Chehab struct media_pad pad; 122cb7a01acSMauro Carvalho Chehab struct v4l2_rect crop; /* Sensor window */ 123cb7a01acSMauro Carvalho Chehab struct v4l2_mbus_framefmt format; 124cb7a01acSMauro Carvalho Chehab struct mt9p031_platform_data *pdata; 125cb7a01acSMauro Carvalho Chehab struct mutex power_lock; /* lock to protect power_count */ 126cb7a01acSMauro Carvalho Chehab int power_count; 127cb7a01acSMauro Carvalho Chehab 128d6749258SLaurent Pinchart struct clk *clk; 1297997196cSLaurent Pinchart struct regulator_bulk_data regulators[3]; 13097f21276SLaurent Pinchart 131cb7a01acSMauro Carvalho Chehab enum mt9p031_model model; 132cb7a01acSMauro Carvalho Chehab struct aptina_pll pll; 133a970449eSLaurent Pinchart unsigned int clk_div; 134a970449eSLaurent Pinchart bool use_pll; 1357c3be9f8SLaurent Pinchart struct gpio_desc *reset; 136cb7a01acSMauro Carvalho Chehab 137cb7a01acSMauro Carvalho Chehab struct v4l2_ctrl_handler ctrls; 138cb7a01acSMauro Carvalho Chehab struct v4l2_ctrl *blc_auto; 139cb7a01acSMauro Carvalho Chehab struct v4l2_ctrl *blc_offset; 140cb7a01acSMauro Carvalho Chehab 141cb7a01acSMauro Carvalho Chehab /* Registers cache */ 142cb7a01acSMauro Carvalho Chehab u16 output_control; 143cb7a01acSMauro Carvalho Chehab u16 mode2; 144cb7a01acSMauro Carvalho Chehab }; 145cb7a01acSMauro Carvalho Chehab 146cb7a01acSMauro Carvalho Chehab static struct mt9p031 *to_mt9p031(struct v4l2_subdev *sd) 147cb7a01acSMauro Carvalho Chehab { 148cb7a01acSMauro Carvalho Chehab return container_of(sd, struct mt9p031, subdev); 149cb7a01acSMauro Carvalho Chehab } 150cb7a01acSMauro Carvalho Chehab 151cb7a01acSMauro Carvalho Chehab static int mt9p031_read(struct i2c_client *client, u8 reg) 152cb7a01acSMauro Carvalho Chehab { 153cb7a01acSMauro Carvalho Chehab return i2c_smbus_read_word_swapped(client, reg); 154cb7a01acSMauro Carvalho Chehab } 155cb7a01acSMauro Carvalho Chehab 156cb7a01acSMauro Carvalho Chehab static int mt9p031_write(struct i2c_client *client, u8 reg, u16 data) 157cb7a01acSMauro Carvalho Chehab { 158cb7a01acSMauro Carvalho Chehab return i2c_smbus_write_word_swapped(client, reg, data); 159cb7a01acSMauro Carvalho Chehab } 160cb7a01acSMauro Carvalho Chehab 161cb7a01acSMauro Carvalho Chehab static int mt9p031_set_output_control(struct mt9p031 *mt9p031, u16 clear, 162cb7a01acSMauro Carvalho Chehab u16 set) 163cb7a01acSMauro Carvalho Chehab { 164cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); 165cb7a01acSMauro Carvalho Chehab u16 value = (mt9p031->output_control & ~clear) | set; 166cb7a01acSMauro Carvalho Chehab int ret; 167cb7a01acSMauro Carvalho Chehab 168cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_OUTPUT_CONTROL, value); 169cb7a01acSMauro Carvalho Chehab if (ret < 0) 170cb7a01acSMauro Carvalho Chehab return ret; 171cb7a01acSMauro Carvalho Chehab 172cb7a01acSMauro Carvalho Chehab mt9p031->output_control = value; 173cb7a01acSMauro Carvalho Chehab return 0; 174cb7a01acSMauro Carvalho Chehab } 175cb7a01acSMauro Carvalho Chehab 176cb7a01acSMauro Carvalho Chehab static int mt9p031_set_mode2(struct mt9p031 *mt9p031, u16 clear, u16 set) 177cb7a01acSMauro Carvalho Chehab { 178cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); 179cb7a01acSMauro Carvalho Chehab u16 value = (mt9p031->mode2 & ~clear) | set; 180cb7a01acSMauro Carvalho Chehab int ret; 181cb7a01acSMauro Carvalho Chehab 182cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_READ_MODE_2, value); 183cb7a01acSMauro Carvalho Chehab if (ret < 0) 184cb7a01acSMauro Carvalho Chehab return ret; 185cb7a01acSMauro Carvalho Chehab 186cb7a01acSMauro Carvalho Chehab mt9p031->mode2 = value; 187cb7a01acSMauro Carvalho Chehab return 0; 188cb7a01acSMauro Carvalho Chehab } 189cb7a01acSMauro Carvalho Chehab 190cb7a01acSMauro Carvalho Chehab static int mt9p031_reset(struct mt9p031 *mt9p031) 191cb7a01acSMauro Carvalho Chehab { 192cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); 193cb7a01acSMauro Carvalho Chehab int ret; 194cb7a01acSMauro Carvalho Chehab 195cb7a01acSMauro Carvalho Chehab /* Disable chip output, synchronous option update */ 196cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_ENABLE); 197cb7a01acSMauro Carvalho Chehab if (ret < 0) 198cb7a01acSMauro Carvalho Chehab return ret; 199cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_RST, MT9P031_RST_DISABLE); 200cb7a01acSMauro Carvalho Chehab if (ret < 0) 201cb7a01acSMauro Carvalho Chehab return ret; 202cb7a01acSMauro Carvalho Chehab 203a970449eSLaurent Pinchart ret = mt9p031_write(client, MT9P031_PIXEL_CLOCK_CONTROL, 204a970449eSLaurent Pinchart MT9P031_PIXEL_CLOCK_DIVIDE(mt9p031->clk_div)); 205a970449eSLaurent Pinchart if (ret < 0) 206a970449eSLaurent Pinchart return ret; 207a970449eSLaurent Pinchart 208cb7a01acSMauro Carvalho Chehab return mt9p031_set_output_control(mt9p031, MT9P031_OUTPUT_CONTROL_CEN, 209cb7a01acSMauro Carvalho Chehab 0); 210cb7a01acSMauro Carvalho Chehab } 211cb7a01acSMauro Carvalho Chehab 212d6749258SLaurent Pinchart static int mt9p031_clk_setup(struct mt9p031 *mt9p031) 213cb7a01acSMauro Carvalho Chehab { 214cb7a01acSMauro Carvalho Chehab static const struct aptina_pll_limits limits = { 215cb7a01acSMauro Carvalho Chehab .ext_clock_min = 6000000, 216cb7a01acSMauro Carvalho Chehab .ext_clock_max = 27000000, 217cb7a01acSMauro Carvalho Chehab .int_clock_min = 2000000, 218cb7a01acSMauro Carvalho Chehab .int_clock_max = 13500000, 219cb7a01acSMauro Carvalho Chehab .out_clock_min = 180000000, 220cb7a01acSMauro Carvalho Chehab .out_clock_max = 360000000, 221cb7a01acSMauro Carvalho Chehab .pix_clock_max = 96000000, 222cb7a01acSMauro Carvalho Chehab .n_min = 1, 223cb7a01acSMauro Carvalho Chehab .n_max = 64, 224cb7a01acSMauro Carvalho Chehab .m_min = 16, 225cb7a01acSMauro Carvalho Chehab .m_max = 255, 226cb7a01acSMauro Carvalho Chehab .p1_min = 1, 227cb7a01acSMauro Carvalho Chehab .p1_max = 128, 228cb7a01acSMauro Carvalho Chehab }; 229cb7a01acSMauro Carvalho Chehab 230cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); 231cb7a01acSMauro Carvalho Chehab struct mt9p031_platform_data *pdata = mt9p031->pdata; 232ee2d16d7SLad, Prabhakar int ret; 233cb7a01acSMauro Carvalho Chehab 234d6749258SLaurent Pinchart mt9p031->clk = devm_clk_get(&client->dev, NULL); 235d6749258SLaurent Pinchart if (IS_ERR(mt9p031->clk)) 236d6749258SLaurent Pinchart return PTR_ERR(mt9p031->clk); 237d6749258SLaurent Pinchart 238ee2d16d7SLad, Prabhakar ret = clk_set_rate(mt9p031->clk, pdata->ext_freq); 239ee2d16d7SLad, Prabhakar if (ret < 0) 240ee2d16d7SLad, Prabhakar return ret; 241d6749258SLaurent Pinchart 242a970449eSLaurent Pinchart /* If the external clock frequency is out of bounds for the PLL use the 243a970449eSLaurent Pinchart * pixel clock divider only and disable the PLL. 244a970449eSLaurent Pinchart */ 245a970449eSLaurent Pinchart if (pdata->ext_freq > limits.ext_clock_max) { 246a970449eSLaurent Pinchart unsigned int div; 247a970449eSLaurent Pinchart 248a970449eSLaurent Pinchart div = DIV_ROUND_UP(pdata->ext_freq, pdata->target_freq); 249a970449eSLaurent Pinchart div = roundup_pow_of_two(div) / 2; 250a970449eSLaurent Pinchart 251198b47ddSEnrico Scholz mt9p031->clk_div = min_t(unsigned int, div, 64); 252a970449eSLaurent Pinchart mt9p031->use_pll = false; 253a970449eSLaurent Pinchart 254a970449eSLaurent Pinchart return 0; 255a970449eSLaurent Pinchart } 256cb7a01acSMauro Carvalho Chehab 257cb7a01acSMauro Carvalho Chehab mt9p031->pll.ext_clock = pdata->ext_freq; 258cb7a01acSMauro Carvalho Chehab mt9p031->pll.pix_clock = pdata->target_freq; 259a970449eSLaurent Pinchart mt9p031->use_pll = true; 260cb7a01acSMauro Carvalho Chehab 261cb7a01acSMauro Carvalho Chehab return aptina_pll_calculate(&client->dev, &limits, &mt9p031->pll); 262cb7a01acSMauro Carvalho Chehab } 263cb7a01acSMauro Carvalho Chehab 264cb7a01acSMauro Carvalho Chehab static int mt9p031_pll_enable(struct mt9p031 *mt9p031) 265cb7a01acSMauro Carvalho Chehab { 266cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); 267cb7a01acSMauro Carvalho Chehab int ret; 268cb7a01acSMauro Carvalho Chehab 269a970449eSLaurent Pinchart if (!mt9p031->use_pll) 270a970449eSLaurent Pinchart return 0; 271a970449eSLaurent Pinchart 272cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_PLL_CONTROL, 273cb7a01acSMauro Carvalho Chehab MT9P031_PLL_CONTROL_PWRON); 274cb7a01acSMauro Carvalho Chehab if (ret < 0) 275cb7a01acSMauro Carvalho Chehab return ret; 276cb7a01acSMauro Carvalho Chehab 277cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_PLL_CONFIG_1, 278cb7a01acSMauro Carvalho Chehab (mt9p031->pll.m << 8) | (mt9p031->pll.n - 1)); 279cb7a01acSMauro Carvalho Chehab if (ret < 0) 280cb7a01acSMauro Carvalho Chehab return ret; 281cb7a01acSMauro Carvalho Chehab 282cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_PLL_CONFIG_2, mt9p031->pll.p1 - 1); 283cb7a01acSMauro Carvalho Chehab if (ret < 0) 284cb7a01acSMauro Carvalho Chehab return ret; 285cb7a01acSMauro Carvalho Chehab 286cb7a01acSMauro Carvalho Chehab usleep_range(1000, 2000); 287cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_PLL_CONTROL, 288cb7a01acSMauro Carvalho Chehab MT9P031_PLL_CONTROL_PWRON | 289cb7a01acSMauro Carvalho Chehab MT9P031_PLL_CONTROL_USEPLL); 290cb7a01acSMauro Carvalho Chehab return ret; 291cb7a01acSMauro Carvalho Chehab } 292cb7a01acSMauro Carvalho Chehab 293cb7a01acSMauro Carvalho Chehab static inline int mt9p031_pll_disable(struct mt9p031 *mt9p031) 294cb7a01acSMauro Carvalho Chehab { 295cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); 296cb7a01acSMauro Carvalho Chehab 297a970449eSLaurent Pinchart if (!mt9p031->use_pll) 298a970449eSLaurent Pinchart return 0; 299a970449eSLaurent Pinchart 300cb7a01acSMauro Carvalho Chehab return mt9p031_write(client, MT9P031_PLL_CONTROL, 301cb7a01acSMauro Carvalho Chehab MT9P031_PLL_CONTROL_PWROFF); 302cb7a01acSMauro Carvalho Chehab } 303cb7a01acSMauro Carvalho Chehab 304cb7a01acSMauro Carvalho Chehab static int mt9p031_power_on(struct mt9p031 *mt9p031) 305cb7a01acSMauro Carvalho Chehab { 3067997196cSLaurent Pinchart int ret; 3077997196cSLaurent Pinchart 3087c3be9f8SLaurent Pinchart /* Ensure RESET_BAR is active */ 3097c3be9f8SLaurent Pinchart if (mt9p031->reset) { 3107c3be9f8SLaurent Pinchart gpiod_set_value(mt9p031->reset, 1); 311cb7a01acSMauro Carvalho Chehab usleep_range(1000, 2000); 312cb7a01acSMauro Carvalho Chehab } 313cb7a01acSMauro Carvalho Chehab 31497f21276SLaurent Pinchart /* Bring up the supplies */ 3157997196cSLaurent Pinchart ret = regulator_bulk_enable(ARRAY_SIZE(mt9p031->regulators), 3167997196cSLaurent Pinchart mt9p031->regulators); 3177997196cSLaurent Pinchart if (ret < 0) 3187997196cSLaurent Pinchart return ret; 31997f21276SLaurent Pinchart 320e8e45593SLaurent Pinchart /* Enable clock */ 321ee2d16d7SLad, Prabhakar if (mt9p031->clk) { 322ee2d16d7SLad, Prabhakar ret = clk_prepare_enable(mt9p031->clk); 323ee2d16d7SLad, Prabhakar if (ret) { 324ee2d16d7SLad, Prabhakar regulator_bulk_disable(ARRAY_SIZE(mt9p031->regulators), 325ee2d16d7SLad, Prabhakar mt9p031->regulators); 326ee2d16d7SLad, Prabhakar return ret; 327ee2d16d7SLad, Prabhakar } 328ee2d16d7SLad, Prabhakar } 329cb7a01acSMauro Carvalho Chehab 330cb7a01acSMauro Carvalho Chehab /* Now RESET_BAR must be high */ 3317c3be9f8SLaurent Pinchart if (mt9p031->reset) { 3327c3be9f8SLaurent Pinchart gpiod_set_value(mt9p031->reset, 0); 333cb7a01acSMauro Carvalho Chehab usleep_range(1000, 2000); 334cb7a01acSMauro Carvalho Chehab } 335cb7a01acSMauro Carvalho Chehab 336cb7a01acSMauro Carvalho Chehab return 0; 337cb7a01acSMauro Carvalho Chehab } 338cb7a01acSMauro Carvalho Chehab 339cb7a01acSMauro Carvalho Chehab static void mt9p031_power_off(struct mt9p031 *mt9p031) 340cb7a01acSMauro Carvalho Chehab { 3417c3be9f8SLaurent Pinchart if (mt9p031->reset) { 3427c3be9f8SLaurent Pinchart gpiod_set_value(mt9p031->reset, 1); 343cb7a01acSMauro Carvalho Chehab usleep_range(1000, 2000); 344cb7a01acSMauro Carvalho Chehab } 345cb7a01acSMauro Carvalho Chehab 3467997196cSLaurent Pinchart regulator_bulk_disable(ARRAY_SIZE(mt9p031->regulators), 3477997196cSLaurent Pinchart mt9p031->regulators); 34897f21276SLaurent Pinchart 349d6749258SLaurent Pinchart if (mt9p031->clk) 350d6749258SLaurent Pinchart clk_disable_unprepare(mt9p031->clk); 351cb7a01acSMauro Carvalho Chehab } 352cb7a01acSMauro Carvalho Chehab 353cb7a01acSMauro Carvalho Chehab static int __mt9p031_set_power(struct mt9p031 *mt9p031, bool on) 354cb7a01acSMauro Carvalho Chehab { 355cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); 356cb7a01acSMauro Carvalho Chehab int ret; 357cb7a01acSMauro Carvalho Chehab 358cb7a01acSMauro Carvalho Chehab if (!on) { 359cb7a01acSMauro Carvalho Chehab mt9p031_power_off(mt9p031); 360cb7a01acSMauro Carvalho Chehab return 0; 361cb7a01acSMauro Carvalho Chehab } 362cb7a01acSMauro Carvalho Chehab 363cb7a01acSMauro Carvalho Chehab ret = mt9p031_power_on(mt9p031); 364cb7a01acSMauro Carvalho Chehab if (ret < 0) 365cb7a01acSMauro Carvalho Chehab return ret; 366cb7a01acSMauro Carvalho Chehab 367cb7a01acSMauro Carvalho Chehab ret = mt9p031_reset(mt9p031); 368cb7a01acSMauro Carvalho Chehab if (ret < 0) { 369cb7a01acSMauro Carvalho Chehab dev_err(&client->dev, "Failed to reset the camera\n"); 370cb7a01acSMauro Carvalho Chehab return ret; 371cb7a01acSMauro Carvalho Chehab } 372cb7a01acSMauro Carvalho Chehab 373cb7a01acSMauro Carvalho Chehab return v4l2_ctrl_handler_setup(&mt9p031->ctrls); 374cb7a01acSMauro Carvalho Chehab } 375cb7a01acSMauro Carvalho Chehab 376cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------------- 377cb7a01acSMauro Carvalho Chehab * V4L2 subdev video operations 378cb7a01acSMauro Carvalho Chehab */ 379cb7a01acSMauro Carvalho Chehab 380cb7a01acSMauro Carvalho Chehab static int mt9p031_set_params(struct mt9p031 *mt9p031) 381cb7a01acSMauro Carvalho Chehab { 382cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); 383cb7a01acSMauro Carvalho Chehab struct v4l2_mbus_framefmt *format = &mt9p031->format; 384cb7a01acSMauro Carvalho Chehab const struct v4l2_rect *crop = &mt9p031->crop; 385cb7a01acSMauro Carvalho Chehab unsigned int hblank; 386cb7a01acSMauro Carvalho Chehab unsigned int vblank; 387cb7a01acSMauro Carvalho Chehab unsigned int xskip; 388cb7a01acSMauro Carvalho Chehab unsigned int yskip; 389cb7a01acSMauro Carvalho Chehab unsigned int xbin; 390cb7a01acSMauro Carvalho Chehab unsigned int ybin; 391cb7a01acSMauro Carvalho Chehab int ret; 392cb7a01acSMauro Carvalho Chehab 393cb7a01acSMauro Carvalho Chehab /* Windows position and size. 394cb7a01acSMauro Carvalho Chehab * 395cb7a01acSMauro Carvalho Chehab * TODO: Make sure the start coordinates and window size match the 396cb7a01acSMauro Carvalho Chehab * skipping, binning and mirroring (see description of registers 2 and 4 397cb7a01acSMauro Carvalho Chehab * in table 13, and Binning section on page 41). 398cb7a01acSMauro Carvalho Chehab */ 399cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_COLUMN_START, crop->left); 400cb7a01acSMauro Carvalho Chehab if (ret < 0) 401cb7a01acSMauro Carvalho Chehab return ret; 402cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_ROW_START, crop->top); 403cb7a01acSMauro Carvalho Chehab if (ret < 0) 404cb7a01acSMauro Carvalho Chehab return ret; 405cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_WINDOW_WIDTH, crop->width - 1); 406cb7a01acSMauro Carvalho Chehab if (ret < 0) 407cb7a01acSMauro Carvalho Chehab return ret; 408cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_WINDOW_HEIGHT, crop->height - 1); 409cb7a01acSMauro Carvalho Chehab if (ret < 0) 410cb7a01acSMauro Carvalho Chehab return ret; 411cb7a01acSMauro Carvalho Chehab 412cb7a01acSMauro Carvalho Chehab /* Row and column binning and skipping. Use the maximum binning value 413cb7a01acSMauro Carvalho Chehab * compatible with the skipping settings. 414cb7a01acSMauro Carvalho Chehab */ 415cb7a01acSMauro Carvalho Chehab xskip = DIV_ROUND_CLOSEST(crop->width, format->width); 416cb7a01acSMauro Carvalho Chehab yskip = DIV_ROUND_CLOSEST(crop->height, format->height); 417cb7a01acSMauro Carvalho Chehab xbin = 1 << (ffs(xskip) - 1); 418cb7a01acSMauro Carvalho Chehab ybin = 1 << (ffs(yskip) - 1); 419cb7a01acSMauro Carvalho Chehab 420cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_COLUMN_ADDRESS_MODE, 421cb7a01acSMauro Carvalho Chehab ((xbin - 1) << 4) | (xskip - 1)); 422cb7a01acSMauro Carvalho Chehab if (ret < 0) 423cb7a01acSMauro Carvalho Chehab return ret; 424cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_ROW_ADDRESS_MODE, 425cb7a01acSMauro Carvalho Chehab ((ybin - 1) << 4) | (yskip - 1)); 426cb7a01acSMauro Carvalho Chehab if (ret < 0) 427cb7a01acSMauro Carvalho Chehab return ret; 428cb7a01acSMauro Carvalho Chehab 429cb7a01acSMauro Carvalho Chehab /* Blanking - use minimum value for horizontal blanking and default 430cb7a01acSMauro Carvalho Chehab * value for vertical blanking. 431cb7a01acSMauro Carvalho Chehab */ 4325266c98bSLaurent Pinchart hblank = 346 * ybin + 64 + (80 >> min_t(unsigned int, xbin, 3)); 433cb7a01acSMauro Carvalho Chehab vblank = MT9P031_VERTICAL_BLANK_DEF; 434cb7a01acSMauro Carvalho Chehab 4355266c98bSLaurent Pinchart ret = mt9p031_write(client, MT9P031_HORIZONTAL_BLANK, hblank - 1); 436cb7a01acSMauro Carvalho Chehab if (ret < 0) 437cb7a01acSMauro Carvalho Chehab return ret; 4385266c98bSLaurent Pinchart ret = mt9p031_write(client, MT9P031_VERTICAL_BLANK, vblank - 1); 439cb7a01acSMauro Carvalho Chehab if (ret < 0) 440cb7a01acSMauro Carvalho Chehab return ret; 441cb7a01acSMauro Carvalho Chehab 442cb7a01acSMauro Carvalho Chehab return ret; 443cb7a01acSMauro Carvalho Chehab } 444cb7a01acSMauro Carvalho Chehab 445cb7a01acSMauro Carvalho Chehab static int mt9p031_s_stream(struct v4l2_subdev *subdev, int enable) 446cb7a01acSMauro Carvalho Chehab { 447cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev); 448cb7a01acSMauro Carvalho Chehab int ret; 449cb7a01acSMauro Carvalho Chehab 450cb7a01acSMauro Carvalho Chehab if (!enable) { 451cb7a01acSMauro Carvalho Chehab /* Stop sensor readout */ 452cb7a01acSMauro Carvalho Chehab ret = mt9p031_set_output_control(mt9p031, 453cb7a01acSMauro Carvalho Chehab MT9P031_OUTPUT_CONTROL_CEN, 0); 454cb7a01acSMauro Carvalho Chehab if (ret < 0) 455cb7a01acSMauro Carvalho Chehab return ret; 456cb7a01acSMauro Carvalho Chehab 457cb7a01acSMauro Carvalho Chehab return mt9p031_pll_disable(mt9p031); 458cb7a01acSMauro Carvalho Chehab } 459cb7a01acSMauro Carvalho Chehab 460cb7a01acSMauro Carvalho Chehab ret = mt9p031_set_params(mt9p031); 461cb7a01acSMauro Carvalho Chehab if (ret < 0) 462cb7a01acSMauro Carvalho Chehab return ret; 463cb7a01acSMauro Carvalho Chehab 464cb7a01acSMauro Carvalho Chehab /* Switch to master "normal" mode */ 465cb7a01acSMauro Carvalho Chehab ret = mt9p031_set_output_control(mt9p031, 0, 466cb7a01acSMauro Carvalho Chehab MT9P031_OUTPUT_CONTROL_CEN); 467cb7a01acSMauro Carvalho Chehab if (ret < 0) 468cb7a01acSMauro Carvalho Chehab return ret; 469cb7a01acSMauro Carvalho Chehab 470cb7a01acSMauro Carvalho Chehab return mt9p031_pll_enable(mt9p031); 471cb7a01acSMauro Carvalho Chehab } 472cb7a01acSMauro Carvalho Chehab 473cb7a01acSMauro Carvalho Chehab static int mt9p031_enum_mbus_code(struct v4l2_subdev *subdev, 474f7234138SHans Verkuil struct v4l2_subdev_pad_config *cfg, 475cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_mbus_code_enum *code) 476cb7a01acSMauro Carvalho Chehab { 477cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev); 478cb7a01acSMauro Carvalho Chehab 479cb7a01acSMauro Carvalho Chehab if (code->pad || code->index) 480cb7a01acSMauro Carvalho Chehab return -EINVAL; 481cb7a01acSMauro Carvalho Chehab 482cb7a01acSMauro Carvalho Chehab code->code = mt9p031->format.code; 483cb7a01acSMauro Carvalho Chehab return 0; 484cb7a01acSMauro Carvalho Chehab } 485cb7a01acSMauro Carvalho Chehab 486cb7a01acSMauro Carvalho Chehab static int mt9p031_enum_frame_size(struct v4l2_subdev *subdev, 487f7234138SHans Verkuil struct v4l2_subdev_pad_config *cfg, 488cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_frame_size_enum *fse) 489cb7a01acSMauro Carvalho Chehab { 490cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev); 491cb7a01acSMauro Carvalho Chehab 492cb7a01acSMauro Carvalho Chehab if (fse->index >= 8 || fse->code != mt9p031->format.code) 493cb7a01acSMauro Carvalho Chehab return -EINVAL; 494cb7a01acSMauro Carvalho Chehab 495cb7a01acSMauro Carvalho Chehab fse->min_width = MT9P031_WINDOW_WIDTH_DEF 496cb7a01acSMauro Carvalho Chehab / min_t(unsigned int, 7, fse->index + 1); 497cb7a01acSMauro Carvalho Chehab fse->max_width = fse->min_width; 498cb7a01acSMauro Carvalho Chehab fse->min_height = MT9P031_WINDOW_HEIGHT_DEF / (fse->index + 1); 499cb7a01acSMauro Carvalho Chehab fse->max_height = fse->min_height; 500cb7a01acSMauro Carvalho Chehab 501cb7a01acSMauro Carvalho Chehab return 0; 502cb7a01acSMauro Carvalho Chehab } 503cb7a01acSMauro Carvalho Chehab 504cb7a01acSMauro Carvalho Chehab static struct v4l2_mbus_framefmt * 505f7234138SHans Verkuil __mt9p031_get_pad_format(struct mt9p031 *mt9p031, struct v4l2_subdev_pad_config *cfg, 506cb7a01acSMauro Carvalho Chehab unsigned int pad, u32 which) 507cb7a01acSMauro Carvalho Chehab { 508cb7a01acSMauro Carvalho Chehab switch (which) { 509cb7a01acSMauro Carvalho Chehab case V4L2_SUBDEV_FORMAT_TRY: 510f7234138SHans Verkuil return v4l2_subdev_get_try_format(&mt9p031->subdev, cfg, pad); 511cb7a01acSMauro Carvalho Chehab case V4L2_SUBDEV_FORMAT_ACTIVE: 512cb7a01acSMauro Carvalho Chehab return &mt9p031->format; 513cb7a01acSMauro Carvalho Chehab default: 514cb7a01acSMauro Carvalho Chehab return NULL; 515cb7a01acSMauro Carvalho Chehab } 516cb7a01acSMauro Carvalho Chehab } 517cb7a01acSMauro Carvalho Chehab 518cb7a01acSMauro Carvalho Chehab static struct v4l2_rect * 519f7234138SHans Verkuil __mt9p031_get_pad_crop(struct mt9p031 *mt9p031, struct v4l2_subdev_pad_config *cfg, 520cb7a01acSMauro Carvalho Chehab unsigned int pad, u32 which) 521cb7a01acSMauro Carvalho Chehab { 522cb7a01acSMauro Carvalho Chehab switch (which) { 523cb7a01acSMauro Carvalho Chehab case V4L2_SUBDEV_FORMAT_TRY: 524f7234138SHans Verkuil return v4l2_subdev_get_try_crop(&mt9p031->subdev, cfg, pad); 525cb7a01acSMauro Carvalho Chehab case V4L2_SUBDEV_FORMAT_ACTIVE: 526cb7a01acSMauro Carvalho Chehab return &mt9p031->crop; 527cb7a01acSMauro Carvalho Chehab default: 528cb7a01acSMauro Carvalho Chehab return NULL; 529cb7a01acSMauro Carvalho Chehab } 530cb7a01acSMauro Carvalho Chehab } 531cb7a01acSMauro Carvalho Chehab 532cb7a01acSMauro Carvalho Chehab static int mt9p031_get_format(struct v4l2_subdev *subdev, 533f7234138SHans Verkuil struct v4l2_subdev_pad_config *cfg, 534cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_format *fmt) 535cb7a01acSMauro Carvalho Chehab { 536cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev); 537cb7a01acSMauro Carvalho Chehab 538f7234138SHans Verkuil fmt->format = *__mt9p031_get_pad_format(mt9p031, cfg, fmt->pad, 539cb7a01acSMauro Carvalho Chehab fmt->which); 540cb7a01acSMauro Carvalho Chehab return 0; 541cb7a01acSMauro Carvalho Chehab } 542cb7a01acSMauro Carvalho Chehab 543cb7a01acSMauro Carvalho Chehab static int mt9p031_set_format(struct v4l2_subdev *subdev, 544f7234138SHans Verkuil struct v4l2_subdev_pad_config *cfg, 545cb7a01acSMauro Carvalho Chehab struct v4l2_subdev_format *format) 546cb7a01acSMauro Carvalho Chehab { 547cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev); 548cb7a01acSMauro Carvalho Chehab struct v4l2_mbus_framefmt *__format; 549cb7a01acSMauro Carvalho Chehab struct v4l2_rect *__crop; 550cb7a01acSMauro Carvalho Chehab unsigned int width; 551cb7a01acSMauro Carvalho Chehab unsigned int height; 552cb7a01acSMauro Carvalho Chehab unsigned int hratio; 553cb7a01acSMauro Carvalho Chehab unsigned int vratio; 554cb7a01acSMauro Carvalho Chehab 555f7234138SHans Verkuil __crop = __mt9p031_get_pad_crop(mt9p031, cfg, format->pad, 556cb7a01acSMauro Carvalho Chehab format->which); 557cb7a01acSMauro Carvalho Chehab 558cb7a01acSMauro Carvalho Chehab /* Clamp the width and height to avoid dividing by zero. */ 559cb7a01acSMauro Carvalho Chehab width = clamp_t(unsigned int, ALIGN(format->format.width, 2), 560f90580caSRicardo Ribalda max_t(unsigned int, __crop->width / 7, 561f90580caSRicardo Ribalda MT9P031_WINDOW_WIDTH_MIN), 562cb7a01acSMauro Carvalho Chehab __crop->width); 563cb7a01acSMauro Carvalho Chehab height = clamp_t(unsigned int, ALIGN(format->format.height, 2), 564f90580caSRicardo Ribalda max_t(unsigned int, __crop->height / 8, 565f90580caSRicardo Ribalda MT9P031_WINDOW_HEIGHT_MIN), 566cb7a01acSMauro Carvalho Chehab __crop->height); 567cb7a01acSMauro Carvalho Chehab 568cb7a01acSMauro Carvalho Chehab hratio = DIV_ROUND_CLOSEST(__crop->width, width); 569cb7a01acSMauro Carvalho Chehab vratio = DIV_ROUND_CLOSEST(__crop->height, height); 570cb7a01acSMauro Carvalho Chehab 571f7234138SHans Verkuil __format = __mt9p031_get_pad_format(mt9p031, cfg, format->pad, 572cb7a01acSMauro Carvalho Chehab format->which); 573cb7a01acSMauro Carvalho Chehab __format->width = __crop->width / hratio; 574cb7a01acSMauro Carvalho Chehab __format->height = __crop->height / vratio; 575cb7a01acSMauro Carvalho Chehab 576cb7a01acSMauro Carvalho Chehab format->format = *__format; 577cb7a01acSMauro Carvalho Chehab 578cb7a01acSMauro Carvalho Chehab return 0; 579cb7a01acSMauro Carvalho Chehab } 580cb7a01acSMauro Carvalho Chehab 5811a023febSHans Verkuil static int mt9p031_get_selection(struct v4l2_subdev *subdev, 582f7234138SHans Verkuil struct v4l2_subdev_pad_config *cfg, 5831a023febSHans Verkuil struct v4l2_subdev_selection *sel) 584cb7a01acSMauro Carvalho Chehab { 585cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev); 586cb7a01acSMauro Carvalho Chehab 5871a023febSHans Verkuil if (sel->target != V4L2_SEL_TGT_CROP) 5881a023febSHans Verkuil return -EINVAL; 5891a023febSHans Verkuil 590f7234138SHans Verkuil sel->r = *__mt9p031_get_pad_crop(mt9p031, cfg, sel->pad, sel->which); 591cb7a01acSMauro Carvalho Chehab return 0; 592cb7a01acSMauro Carvalho Chehab } 593cb7a01acSMauro Carvalho Chehab 5941a023febSHans Verkuil static int mt9p031_set_selection(struct v4l2_subdev *subdev, 595f7234138SHans Verkuil struct v4l2_subdev_pad_config *cfg, 5961a023febSHans Verkuil struct v4l2_subdev_selection *sel) 597cb7a01acSMauro Carvalho Chehab { 598cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev); 599cb7a01acSMauro Carvalho Chehab struct v4l2_mbus_framefmt *__format; 600cb7a01acSMauro Carvalho Chehab struct v4l2_rect *__crop; 601cb7a01acSMauro Carvalho Chehab struct v4l2_rect rect; 602cb7a01acSMauro Carvalho Chehab 6031a023febSHans Verkuil if (sel->target != V4L2_SEL_TGT_CROP) 6041a023febSHans Verkuil return -EINVAL; 6051a023febSHans Verkuil 606cb7a01acSMauro Carvalho Chehab /* Clamp the crop rectangle boundaries and align them to a multiple of 2 607cb7a01acSMauro Carvalho Chehab * pixels to ensure a GRBG Bayer pattern. 608cb7a01acSMauro Carvalho Chehab */ 6091a023febSHans Verkuil rect.left = clamp(ALIGN(sel->r.left, 2), MT9P031_COLUMN_START_MIN, 610cb7a01acSMauro Carvalho Chehab MT9P031_COLUMN_START_MAX); 6111a023febSHans Verkuil rect.top = clamp(ALIGN(sel->r.top, 2), MT9P031_ROW_START_MIN, 612cb7a01acSMauro Carvalho Chehab MT9P031_ROW_START_MAX); 6131a023febSHans Verkuil rect.width = clamp_t(unsigned int, ALIGN(sel->r.width, 2), 614cb7a01acSMauro Carvalho Chehab MT9P031_WINDOW_WIDTH_MIN, 615cb7a01acSMauro Carvalho Chehab MT9P031_WINDOW_WIDTH_MAX); 6161a023febSHans Verkuil rect.height = clamp_t(unsigned int, ALIGN(sel->r.height, 2), 617cb7a01acSMauro Carvalho Chehab MT9P031_WINDOW_HEIGHT_MIN, 618cb7a01acSMauro Carvalho Chehab MT9P031_WINDOW_HEIGHT_MAX); 619cb7a01acSMauro Carvalho Chehab 620f90580caSRicardo Ribalda rect.width = min_t(unsigned int, rect.width, 621f90580caSRicardo Ribalda MT9P031_PIXEL_ARRAY_WIDTH - rect.left); 622f90580caSRicardo Ribalda rect.height = min_t(unsigned int, rect.height, 623f90580caSRicardo Ribalda MT9P031_PIXEL_ARRAY_HEIGHT - rect.top); 624cb7a01acSMauro Carvalho Chehab 625f7234138SHans Verkuil __crop = __mt9p031_get_pad_crop(mt9p031, cfg, sel->pad, sel->which); 626cb7a01acSMauro Carvalho Chehab 627cb7a01acSMauro Carvalho Chehab if (rect.width != __crop->width || rect.height != __crop->height) { 628cb7a01acSMauro Carvalho Chehab /* Reset the output image size if the crop rectangle size has 629cb7a01acSMauro Carvalho Chehab * been modified. 630cb7a01acSMauro Carvalho Chehab */ 631f7234138SHans Verkuil __format = __mt9p031_get_pad_format(mt9p031, cfg, sel->pad, 6321a023febSHans Verkuil sel->which); 633cb7a01acSMauro Carvalho Chehab __format->width = rect.width; 634cb7a01acSMauro Carvalho Chehab __format->height = rect.height; 635cb7a01acSMauro Carvalho Chehab } 636cb7a01acSMauro Carvalho Chehab 637cb7a01acSMauro Carvalho Chehab *__crop = rect; 6381a023febSHans Verkuil sel->r = rect; 639cb7a01acSMauro Carvalho Chehab 640cb7a01acSMauro Carvalho Chehab return 0; 641cb7a01acSMauro Carvalho Chehab } 642cb7a01acSMauro Carvalho Chehab 643cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------------- 644cb7a01acSMauro Carvalho Chehab * V4L2 subdev control operations 645cb7a01acSMauro Carvalho Chehab */ 646cb7a01acSMauro Carvalho Chehab 647cb7a01acSMauro Carvalho Chehab #define V4L2_CID_BLC_AUTO (V4L2_CID_USER_BASE | 0x1002) 648cb7a01acSMauro Carvalho Chehab #define V4L2_CID_BLC_TARGET_LEVEL (V4L2_CID_USER_BASE | 0x1003) 649cb7a01acSMauro Carvalho Chehab #define V4L2_CID_BLC_ANALOG_OFFSET (V4L2_CID_USER_BASE | 0x1004) 650cb7a01acSMauro Carvalho Chehab #define V4L2_CID_BLC_DIGITAL_OFFSET (V4L2_CID_USER_BASE | 0x1005) 651cb7a01acSMauro Carvalho Chehab 652535ec214SLaurent Pinchart static int mt9p031_restore_blc(struct mt9p031 *mt9p031) 653535ec214SLaurent Pinchart { 654535ec214SLaurent Pinchart struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); 655535ec214SLaurent Pinchart int ret; 656535ec214SLaurent Pinchart 657535ec214SLaurent Pinchart if (mt9p031->blc_auto->cur.val != 0) { 658535ec214SLaurent Pinchart ret = mt9p031_set_mode2(mt9p031, 0, 659535ec214SLaurent Pinchart MT9P031_READ_MODE_2_ROW_BLC); 660535ec214SLaurent Pinchart if (ret < 0) 661535ec214SLaurent Pinchart return ret; 662535ec214SLaurent Pinchart } 663535ec214SLaurent Pinchart 664535ec214SLaurent Pinchart if (mt9p031->blc_offset->cur.val != 0) { 665535ec214SLaurent Pinchart ret = mt9p031_write(client, MT9P031_ROW_BLACK_TARGET, 666535ec214SLaurent Pinchart mt9p031->blc_offset->cur.val); 667535ec214SLaurent Pinchart if (ret < 0) 668535ec214SLaurent Pinchart return ret; 669535ec214SLaurent Pinchart } 670535ec214SLaurent Pinchart 671535ec214SLaurent Pinchart return 0; 672535ec214SLaurent Pinchart } 673535ec214SLaurent Pinchart 674cb7a01acSMauro Carvalho Chehab static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) 675cb7a01acSMauro Carvalho Chehab { 676cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = 677cb7a01acSMauro Carvalho Chehab container_of(ctrl->handler, struct mt9p031, ctrls); 678cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev); 679cb7a01acSMauro Carvalho Chehab u16 data; 680cb7a01acSMauro Carvalho Chehab int ret; 681cb7a01acSMauro Carvalho Chehab 6828bf54c43SLaurent Pinchart if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) 6838bf54c43SLaurent Pinchart return 0; 6848bf54c43SLaurent Pinchart 685cb7a01acSMauro Carvalho Chehab switch (ctrl->id) { 686cb7a01acSMauro Carvalho Chehab case V4L2_CID_EXPOSURE: 687cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_SHUTTER_WIDTH_UPPER, 688cb7a01acSMauro Carvalho Chehab (ctrl->val >> 16) & 0xffff); 689cb7a01acSMauro Carvalho Chehab if (ret < 0) 690cb7a01acSMauro Carvalho Chehab return ret; 691cb7a01acSMauro Carvalho Chehab 692cb7a01acSMauro Carvalho Chehab return mt9p031_write(client, MT9P031_SHUTTER_WIDTH_LOWER, 693cb7a01acSMauro Carvalho Chehab ctrl->val & 0xffff); 694cb7a01acSMauro Carvalho Chehab 695cb7a01acSMauro Carvalho Chehab case V4L2_CID_GAIN: 696cb7a01acSMauro Carvalho Chehab /* Gain is controlled by 2 analog stages and a digital stage. 697cb7a01acSMauro Carvalho Chehab * Valid values for the 3 stages are 698cb7a01acSMauro Carvalho Chehab * 699cb7a01acSMauro Carvalho Chehab * Stage Min Max Step 700cb7a01acSMauro Carvalho Chehab * ------------------------------------------ 701cb7a01acSMauro Carvalho Chehab * First analog stage x1 x2 1 702cb7a01acSMauro Carvalho Chehab * Second analog stage x1 x4 0.125 703cb7a01acSMauro Carvalho Chehab * Digital stage x1 x16 0.125 704cb7a01acSMauro Carvalho Chehab * 705cb7a01acSMauro Carvalho Chehab * To minimize noise, the gain stages should be used in the 706cb7a01acSMauro Carvalho Chehab * second analog stage, first analog stage, digital stage order. 707cb7a01acSMauro Carvalho Chehab * Gain from a previous stage should be pushed to its maximum 708cb7a01acSMauro Carvalho Chehab * value before the next stage is used. 709cb7a01acSMauro Carvalho Chehab */ 710cb7a01acSMauro Carvalho Chehab if (ctrl->val <= 32) { 711cb7a01acSMauro Carvalho Chehab data = ctrl->val; 712cb7a01acSMauro Carvalho Chehab } else if (ctrl->val <= 64) { 713cb7a01acSMauro Carvalho Chehab ctrl->val &= ~1; 714cb7a01acSMauro Carvalho Chehab data = (1 << 6) | (ctrl->val >> 1); 715cb7a01acSMauro Carvalho Chehab } else { 716cb7a01acSMauro Carvalho Chehab ctrl->val &= ~7; 717cb7a01acSMauro Carvalho Chehab data = ((ctrl->val - 64) << 5) | (1 << 6) | 32; 718cb7a01acSMauro Carvalho Chehab } 719cb7a01acSMauro Carvalho Chehab 720cb7a01acSMauro Carvalho Chehab return mt9p031_write(client, MT9P031_GLOBAL_GAIN, data); 721cb7a01acSMauro Carvalho Chehab 722cb7a01acSMauro Carvalho Chehab case V4L2_CID_HFLIP: 723cb7a01acSMauro Carvalho Chehab if (ctrl->val) 724cb7a01acSMauro Carvalho Chehab return mt9p031_set_mode2(mt9p031, 725cb7a01acSMauro Carvalho Chehab 0, MT9P031_READ_MODE_2_COL_MIR); 726cb7a01acSMauro Carvalho Chehab else 727cb7a01acSMauro Carvalho Chehab return mt9p031_set_mode2(mt9p031, 728cb7a01acSMauro Carvalho Chehab MT9P031_READ_MODE_2_COL_MIR, 0); 729cb7a01acSMauro Carvalho Chehab 730cb7a01acSMauro Carvalho Chehab case V4L2_CID_VFLIP: 731cb7a01acSMauro Carvalho Chehab if (ctrl->val) 732cb7a01acSMauro Carvalho Chehab return mt9p031_set_mode2(mt9p031, 733cb7a01acSMauro Carvalho Chehab 0, MT9P031_READ_MODE_2_ROW_MIR); 734cb7a01acSMauro Carvalho Chehab else 735cb7a01acSMauro Carvalho Chehab return mt9p031_set_mode2(mt9p031, 736cb7a01acSMauro Carvalho Chehab MT9P031_READ_MODE_2_ROW_MIR, 0); 737cb7a01acSMauro Carvalho Chehab 738cb7a01acSMauro Carvalho Chehab case V4L2_CID_TEST_PATTERN: 7398bf54c43SLaurent Pinchart /* The digital side of the Black Level Calibration function must 7408bf54c43SLaurent Pinchart * be disabled when generating a test pattern to avoid artifacts 7418bf54c43SLaurent Pinchart * in the image. Activate (deactivate) the BLC-related controls 7428bf54c43SLaurent Pinchart * when the test pattern is enabled (disabled). 7438bf54c43SLaurent Pinchart */ 7448bf54c43SLaurent Pinchart v4l2_ctrl_activate(mt9p031->blc_auto, ctrl->val == 0); 7458bf54c43SLaurent Pinchart v4l2_ctrl_activate(mt9p031->blc_offset, ctrl->val == 0); 7468bf54c43SLaurent Pinchart 747cb7a01acSMauro Carvalho Chehab if (!ctrl->val) { 7488bf54c43SLaurent Pinchart /* Restore the BLC settings. */ 749535ec214SLaurent Pinchart ret = mt9p031_restore_blc(mt9p031); 750cb7a01acSMauro Carvalho Chehab if (ret < 0) 751cb7a01acSMauro Carvalho Chehab return ret; 752535ec214SLaurent Pinchart 753cb7a01acSMauro Carvalho Chehab return mt9p031_write(client, MT9P031_TEST_PATTERN, 754cb7a01acSMauro Carvalho Chehab MT9P031_TEST_PATTERN_DISABLE); 755cb7a01acSMauro Carvalho Chehab } 756cb7a01acSMauro Carvalho Chehab 757cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_TEST_PATTERN_GREEN, 0x05a0); 758cb7a01acSMauro Carvalho Chehab if (ret < 0) 759cb7a01acSMauro Carvalho Chehab return ret; 760cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_TEST_PATTERN_RED, 0x0a50); 761cb7a01acSMauro Carvalho Chehab if (ret < 0) 762cb7a01acSMauro Carvalho Chehab return ret; 763cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_TEST_PATTERN_BLUE, 0x0aa0); 764cb7a01acSMauro Carvalho Chehab if (ret < 0) 765cb7a01acSMauro Carvalho Chehab return ret; 766cb7a01acSMauro Carvalho Chehab 7678bf54c43SLaurent Pinchart /* Disable digital BLC when generating a test pattern. */ 768cb7a01acSMauro Carvalho Chehab ret = mt9p031_set_mode2(mt9p031, MT9P031_READ_MODE_2_ROW_BLC, 769cb7a01acSMauro Carvalho Chehab 0); 770cb7a01acSMauro Carvalho Chehab if (ret < 0) 771cb7a01acSMauro Carvalho Chehab return ret; 772cb7a01acSMauro Carvalho Chehab 773cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, 0); 774cb7a01acSMauro Carvalho Chehab if (ret < 0) 775cb7a01acSMauro Carvalho Chehab return ret; 776cb7a01acSMauro Carvalho Chehab 777cb7a01acSMauro Carvalho Chehab return mt9p031_write(client, MT9P031_TEST_PATTERN, 778cb7a01acSMauro Carvalho Chehab ((ctrl->val - 1) << MT9P031_TEST_PATTERN_SHIFT) 779cb7a01acSMauro Carvalho Chehab | MT9P031_TEST_PATTERN_ENABLE); 780cb7a01acSMauro Carvalho Chehab 781cb7a01acSMauro Carvalho Chehab case V4L2_CID_BLC_AUTO: 782cb7a01acSMauro Carvalho Chehab ret = mt9p031_set_mode2(mt9p031, 783cb7a01acSMauro Carvalho Chehab ctrl->val ? 0 : MT9P031_READ_MODE_2_ROW_BLC, 784cb7a01acSMauro Carvalho Chehab ctrl->val ? MT9P031_READ_MODE_2_ROW_BLC : 0); 785cb7a01acSMauro Carvalho Chehab if (ret < 0) 786cb7a01acSMauro Carvalho Chehab return ret; 787cb7a01acSMauro Carvalho Chehab 788cb7a01acSMauro Carvalho Chehab return mt9p031_write(client, MT9P031_BLACK_LEVEL_CALIBRATION, 789cb7a01acSMauro Carvalho Chehab ctrl->val ? 0 : MT9P031_BLC_MANUAL_BLC); 790cb7a01acSMauro Carvalho Chehab 791cb7a01acSMauro Carvalho Chehab case V4L2_CID_BLC_TARGET_LEVEL: 792cb7a01acSMauro Carvalho Chehab return mt9p031_write(client, MT9P031_ROW_BLACK_TARGET, 793cb7a01acSMauro Carvalho Chehab ctrl->val); 794cb7a01acSMauro Carvalho Chehab 795cb7a01acSMauro Carvalho Chehab case V4L2_CID_BLC_ANALOG_OFFSET: 796cb7a01acSMauro Carvalho Chehab data = ctrl->val & ((1 << 9) - 1); 797cb7a01acSMauro Carvalho Chehab 798cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_GREEN1_OFFSET, data); 799cb7a01acSMauro Carvalho Chehab if (ret < 0) 800cb7a01acSMauro Carvalho Chehab return ret; 801cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_GREEN2_OFFSET, data); 802cb7a01acSMauro Carvalho Chehab if (ret < 0) 803cb7a01acSMauro Carvalho Chehab return ret; 804cb7a01acSMauro Carvalho Chehab ret = mt9p031_write(client, MT9P031_RED_OFFSET, data); 805cb7a01acSMauro Carvalho Chehab if (ret < 0) 806cb7a01acSMauro Carvalho Chehab return ret; 807cb7a01acSMauro Carvalho Chehab return mt9p031_write(client, MT9P031_BLUE_OFFSET, data); 808cb7a01acSMauro Carvalho Chehab 809cb7a01acSMauro Carvalho Chehab case V4L2_CID_BLC_DIGITAL_OFFSET: 810cb7a01acSMauro Carvalho Chehab return mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, 811cb7a01acSMauro Carvalho Chehab ctrl->val & ((1 << 12) - 1)); 812cb7a01acSMauro Carvalho Chehab } 813cb7a01acSMauro Carvalho Chehab 814cb7a01acSMauro Carvalho Chehab return 0; 815cb7a01acSMauro Carvalho Chehab } 816cb7a01acSMauro Carvalho Chehab 817217bdb07SJulia Lawall static const struct v4l2_ctrl_ops mt9p031_ctrl_ops = { 818cb7a01acSMauro Carvalho Chehab .s_ctrl = mt9p031_s_ctrl, 819cb7a01acSMauro Carvalho Chehab }; 820cb7a01acSMauro Carvalho Chehab 821cb7a01acSMauro Carvalho Chehab static const char * const mt9p031_test_pattern_menu[] = { 822cb7a01acSMauro Carvalho Chehab "Disabled", 823cb7a01acSMauro Carvalho Chehab "Color Field", 824cb7a01acSMauro Carvalho Chehab "Horizontal Gradient", 825cb7a01acSMauro Carvalho Chehab "Vertical Gradient", 826cb7a01acSMauro Carvalho Chehab "Diagonal Gradient", 827cb7a01acSMauro Carvalho Chehab "Classic Test Pattern", 828cb7a01acSMauro Carvalho Chehab "Walking 1s", 829cb7a01acSMauro Carvalho Chehab "Monochrome Horizontal Bars", 830cb7a01acSMauro Carvalho Chehab "Monochrome Vertical Bars", 831cb7a01acSMauro Carvalho Chehab "Vertical Color Bars", 832cb7a01acSMauro Carvalho Chehab }; 833cb7a01acSMauro Carvalho Chehab 834cb7a01acSMauro Carvalho Chehab static const struct v4l2_ctrl_config mt9p031_ctrls[] = { 835cb7a01acSMauro Carvalho Chehab { 836cb7a01acSMauro Carvalho Chehab .ops = &mt9p031_ctrl_ops, 837cb7a01acSMauro Carvalho Chehab .id = V4L2_CID_BLC_AUTO, 838cb7a01acSMauro Carvalho Chehab .type = V4L2_CTRL_TYPE_BOOLEAN, 839cb7a01acSMauro Carvalho Chehab .name = "BLC, Auto", 840cb7a01acSMauro Carvalho Chehab .min = 0, 841cb7a01acSMauro Carvalho Chehab .max = 1, 842cb7a01acSMauro Carvalho Chehab .step = 1, 843cb7a01acSMauro Carvalho Chehab .def = 1, 844cb7a01acSMauro Carvalho Chehab .flags = 0, 845cb7a01acSMauro Carvalho Chehab }, { 846cb7a01acSMauro Carvalho Chehab .ops = &mt9p031_ctrl_ops, 847cb7a01acSMauro Carvalho Chehab .id = V4L2_CID_BLC_TARGET_LEVEL, 848cb7a01acSMauro Carvalho Chehab .type = V4L2_CTRL_TYPE_INTEGER, 849cb7a01acSMauro Carvalho Chehab .name = "BLC Target Level", 850cb7a01acSMauro Carvalho Chehab .min = 0, 851cb7a01acSMauro Carvalho Chehab .max = 4095, 852cb7a01acSMauro Carvalho Chehab .step = 1, 853cb7a01acSMauro Carvalho Chehab .def = 168, 854cb7a01acSMauro Carvalho Chehab .flags = 0, 855cb7a01acSMauro Carvalho Chehab }, { 856cb7a01acSMauro Carvalho Chehab .ops = &mt9p031_ctrl_ops, 857cb7a01acSMauro Carvalho Chehab .id = V4L2_CID_BLC_ANALOG_OFFSET, 858cb7a01acSMauro Carvalho Chehab .type = V4L2_CTRL_TYPE_INTEGER, 859cb7a01acSMauro Carvalho Chehab .name = "BLC Analog Offset", 860cb7a01acSMauro Carvalho Chehab .min = -255, 861cb7a01acSMauro Carvalho Chehab .max = 255, 862cb7a01acSMauro Carvalho Chehab .step = 1, 863cb7a01acSMauro Carvalho Chehab .def = 32, 864cb7a01acSMauro Carvalho Chehab .flags = 0, 865cb7a01acSMauro Carvalho Chehab }, { 866cb7a01acSMauro Carvalho Chehab .ops = &mt9p031_ctrl_ops, 867cb7a01acSMauro Carvalho Chehab .id = V4L2_CID_BLC_DIGITAL_OFFSET, 868cb7a01acSMauro Carvalho Chehab .type = V4L2_CTRL_TYPE_INTEGER, 869cb7a01acSMauro Carvalho Chehab .name = "BLC Digital Offset", 870cb7a01acSMauro Carvalho Chehab .min = -2048, 871cb7a01acSMauro Carvalho Chehab .max = 2047, 872cb7a01acSMauro Carvalho Chehab .step = 1, 873cb7a01acSMauro Carvalho Chehab .def = 40, 874cb7a01acSMauro Carvalho Chehab .flags = 0, 875cb7a01acSMauro Carvalho Chehab } 876cb7a01acSMauro Carvalho Chehab }; 877cb7a01acSMauro Carvalho Chehab 878cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------------- 879cb7a01acSMauro Carvalho Chehab * V4L2 subdev core operations 880cb7a01acSMauro Carvalho Chehab */ 881cb7a01acSMauro Carvalho Chehab 882cb7a01acSMauro Carvalho Chehab static int mt9p031_set_power(struct v4l2_subdev *subdev, int on) 883cb7a01acSMauro Carvalho Chehab { 884cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev); 885cb7a01acSMauro Carvalho Chehab int ret = 0; 886cb7a01acSMauro Carvalho Chehab 887cb7a01acSMauro Carvalho Chehab mutex_lock(&mt9p031->power_lock); 888cb7a01acSMauro Carvalho Chehab 889cb7a01acSMauro Carvalho Chehab /* If the power count is modified from 0 to != 0 or from != 0 to 0, 890cb7a01acSMauro Carvalho Chehab * update the power state. 891cb7a01acSMauro Carvalho Chehab */ 892cb7a01acSMauro Carvalho Chehab if (mt9p031->power_count == !on) { 893cb7a01acSMauro Carvalho Chehab ret = __mt9p031_set_power(mt9p031, !!on); 894cb7a01acSMauro Carvalho Chehab if (ret < 0) 895cb7a01acSMauro Carvalho Chehab goto out; 896cb7a01acSMauro Carvalho Chehab } 897cb7a01acSMauro Carvalho Chehab 898cb7a01acSMauro Carvalho Chehab /* Update the power count. */ 899cb7a01acSMauro Carvalho Chehab mt9p031->power_count += on ? 1 : -1; 900cb7a01acSMauro Carvalho Chehab WARN_ON(mt9p031->power_count < 0); 901cb7a01acSMauro Carvalho Chehab 902cb7a01acSMauro Carvalho Chehab out: 903cb7a01acSMauro Carvalho Chehab mutex_unlock(&mt9p031->power_lock); 904cb7a01acSMauro Carvalho Chehab return ret; 905cb7a01acSMauro Carvalho Chehab } 906cb7a01acSMauro Carvalho Chehab 907cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------------- 908cb7a01acSMauro Carvalho Chehab * V4L2 subdev internal operations 909cb7a01acSMauro Carvalho Chehab */ 910cb7a01acSMauro Carvalho Chehab 911cb7a01acSMauro Carvalho Chehab static int mt9p031_registered(struct v4l2_subdev *subdev) 912cb7a01acSMauro Carvalho Chehab { 913cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(subdev); 914cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev); 915cb7a01acSMauro Carvalho Chehab s32 data; 916cb7a01acSMauro Carvalho Chehab int ret; 917cb7a01acSMauro Carvalho Chehab 918cb7a01acSMauro Carvalho Chehab ret = mt9p031_power_on(mt9p031); 919cb7a01acSMauro Carvalho Chehab if (ret < 0) { 920cb7a01acSMauro Carvalho Chehab dev_err(&client->dev, "MT9P031 power up failed\n"); 921cb7a01acSMauro Carvalho Chehab return ret; 922cb7a01acSMauro Carvalho Chehab } 923cb7a01acSMauro Carvalho Chehab 924cb7a01acSMauro Carvalho Chehab /* Read out the chip version register */ 925cb7a01acSMauro Carvalho Chehab data = mt9p031_read(client, MT9P031_CHIP_VERSION); 926bbcc9fa0SGuennadi Liakhovetski mt9p031_power_off(mt9p031); 927bbcc9fa0SGuennadi Liakhovetski 928cb7a01acSMauro Carvalho Chehab if (data != MT9P031_CHIP_VERSION_VALUE) { 929cb7a01acSMauro Carvalho Chehab dev_err(&client->dev, "MT9P031 not detected, wrong version " 930cb7a01acSMauro Carvalho Chehab "0x%04x\n", data); 931cb7a01acSMauro Carvalho Chehab return -ENODEV; 932cb7a01acSMauro Carvalho Chehab } 933cb7a01acSMauro Carvalho Chehab 934cb7a01acSMauro Carvalho Chehab dev_info(&client->dev, "MT9P031 detected at address 0x%02x\n", 935cb7a01acSMauro Carvalho Chehab client->addr); 936cb7a01acSMauro Carvalho Chehab 937bbcc9fa0SGuennadi Liakhovetski return 0; 938cb7a01acSMauro Carvalho Chehab } 939cb7a01acSMauro Carvalho Chehab 940cb7a01acSMauro Carvalho Chehab static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) 941cb7a01acSMauro Carvalho Chehab { 942cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev); 943cb7a01acSMauro Carvalho Chehab struct v4l2_mbus_framefmt *format; 944cb7a01acSMauro Carvalho Chehab struct v4l2_rect *crop; 945cb7a01acSMauro Carvalho Chehab 946f7234138SHans Verkuil crop = v4l2_subdev_get_try_crop(subdev, fh->pad, 0); 947cb7a01acSMauro Carvalho Chehab crop->left = MT9P031_COLUMN_START_DEF; 948cb7a01acSMauro Carvalho Chehab crop->top = MT9P031_ROW_START_DEF; 949cb7a01acSMauro Carvalho Chehab crop->width = MT9P031_WINDOW_WIDTH_DEF; 950cb7a01acSMauro Carvalho Chehab crop->height = MT9P031_WINDOW_HEIGHT_DEF; 951cb7a01acSMauro Carvalho Chehab 952f7234138SHans Verkuil format = v4l2_subdev_get_try_format(subdev, fh->pad, 0); 953cb7a01acSMauro Carvalho Chehab 954cb7a01acSMauro Carvalho Chehab if (mt9p031->model == MT9P031_MODEL_MONOCHROME) 955f5fe58fdSBoris BREZILLON format->code = MEDIA_BUS_FMT_Y12_1X12; 956cb7a01acSMauro Carvalho Chehab else 957f5fe58fdSBoris BREZILLON format->code = MEDIA_BUS_FMT_SGRBG12_1X12; 958cb7a01acSMauro Carvalho Chehab 959cb7a01acSMauro Carvalho Chehab format->width = MT9P031_WINDOW_WIDTH_DEF; 960cb7a01acSMauro Carvalho Chehab format->height = MT9P031_WINDOW_HEIGHT_DEF; 961cb7a01acSMauro Carvalho Chehab format->field = V4L2_FIELD_NONE; 962cb7a01acSMauro Carvalho Chehab format->colorspace = V4L2_COLORSPACE_SRGB; 963cb7a01acSMauro Carvalho Chehab 964cb7a01acSMauro Carvalho Chehab return mt9p031_set_power(subdev, 1); 965cb7a01acSMauro Carvalho Chehab } 966cb7a01acSMauro Carvalho Chehab 967cb7a01acSMauro Carvalho Chehab static int mt9p031_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) 968cb7a01acSMauro Carvalho Chehab { 969cb7a01acSMauro Carvalho Chehab return mt9p031_set_power(subdev, 0); 970cb7a01acSMauro Carvalho Chehab } 971cb7a01acSMauro Carvalho Chehab 9727c137c60SBhumika Goyal static const struct v4l2_subdev_core_ops mt9p031_subdev_core_ops = { 973cb7a01acSMauro Carvalho Chehab .s_power = mt9p031_set_power, 974cb7a01acSMauro Carvalho Chehab }; 975cb7a01acSMauro Carvalho Chehab 9767c137c60SBhumika Goyal static const struct v4l2_subdev_video_ops mt9p031_subdev_video_ops = { 977cb7a01acSMauro Carvalho Chehab .s_stream = mt9p031_s_stream, 978cb7a01acSMauro Carvalho Chehab }; 979cb7a01acSMauro Carvalho Chehab 9807c137c60SBhumika Goyal static const struct v4l2_subdev_pad_ops mt9p031_subdev_pad_ops = { 981cb7a01acSMauro Carvalho Chehab .enum_mbus_code = mt9p031_enum_mbus_code, 982cb7a01acSMauro Carvalho Chehab .enum_frame_size = mt9p031_enum_frame_size, 983cb7a01acSMauro Carvalho Chehab .get_fmt = mt9p031_get_format, 984cb7a01acSMauro Carvalho Chehab .set_fmt = mt9p031_set_format, 9851a023febSHans Verkuil .get_selection = mt9p031_get_selection, 9861a023febSHans Verkuil .set_selection = mt9p031_set_selection, 987cb7a01acSMauro Carvalho Chehab }; 988cb7a01acSMauro Carvalho Chehab 9897c137c60SBhumika Goyal static const struct v4l2_subdev_ops mt9p031_subdev_ops = { 990cb7a01acSMauro Carvalho Chehab .core = &mt9p031_subdev_core_ops, 991cb7a01acSMauro Carvalho Chehab .video = &mt9p031_subdev_video_ops, 992cb7a01acSMauro Carvalho Chehab .pad = &mt9p031_subdev_pad_ops, 993cb7a01acSMauro Carvalho Chehab }; 994cb7a01acSMauro Carvalho Chehab 995cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_internal_ops mt9p031_subdev_internal_ops = { 996cb7a01acSMauro Carvalho Chehab .registered = mt9p031_registered, 997cb7a01acSMauro Carvalho Chehab .open = mt9p031_open, 998cb7a01acSMauro Carvalho Chehab .close = mt9p031_close, 999cb7a01acSMauro Carvalho Chehab }; 1000cb7a01acSMauro Carvalho Chehab 1001cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------------- 1002cb7a01acSMauro Carvalho Chehab * Driver initialization and probing 1003cb7a01acSMauro Carvalho Chehab */ 1004cb7a01acSMauro Carvalho Chehab 10058d4da37cSLad, Prabhakar static struct mt9p031_platform_data * 10068d4da37cSLad, Prabhakar mt9p031_get_pdata(struct i2c_client *client) 10078d4da37cSLad, Prabhakar { 10088d4da37cSLad, Prabhakar struct mt9p031_platform_data *pdata; 10098d4da37cSLad, Prabhakar struct device_node *np; 10108d4da37cSLad, Prabhakar 10118d4da37cSLad, Prabhakar if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node) 10128d4da37cSLad, Prabhakar return client->dev.platform_data; 10138d4da37cSLad, Prabhakar 1014fd9fdb78SPhilipp Zabel np = of_graph_get_next_endpoint(client->dev.of_node, NULL); 10158d4da37cSLad, Prabhakar if (!np) 10168d4da37cSLad, Prabhakar return NULL; 10178d4da37cSLad, Prabhakar 10188d4da37cSLad, Prabhakar pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); 10198d4da37cSLad, Prabhakar if (!pdata) 10208d4da37cSLad, Prabhakar goto done; 10218d4da37cSLad, Prabhakar 10228d4da37cSLad, Prabhakar of_property_read_u32(np, "input-clock-frequency", &pdata->ext_freq); 10238d4da37cSLad, Prabhakar of_property_read_u32(np, "pixel-clock-frequency", &pdata->target_freq); 10248d4da37cSLad, Prabhakar 10258d4da37cSLad, Prabhakar done: 10268d4da37cSLad, Prabhakar of_node_put(np); 10278d4da37cSLad, Prabhakar return pdata; 10288d4da37cSLad, Prabhakar } 10298d4da37cSLad, Prabhakar 1030cb7a01acSMauro Carvalho Chehab static int mt9p031_probe(struct i2c_client *client, 1031cb7a01acSMauro Carvalho Chehab const struct i2c_device_id *did) 1032cb7a01acSMauro Carvalho Chehab { 10338d4da37cSLad, Prabhakar struct mt9p031_platform_data *pdata = mt9p031_get_pdata(client); 1034cb7a01acSMauro Carvalho Chehab struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); 1035cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031; 1036cb7a01acSMauro Carvalho Chehab unsigned int i; 1037cb7a01acSMauro Carvalho Chehab int ret; 1038cb7a01acSMauro Carvalho Chehab 1039cb7a01acSMauro Carvalho Chehab if (pdata == NULL) { 1040cb7a01acSMauro Carvalho Chehab dev_err(&client->dev, "No platform data\n"); 1041cb7a01acSMauro Carvalho Chehab return -EINVAL; 1042cb7a01acSMauro Carvalho Chehab } 1043cb7a01acSMauro Carvalho Chehab 1044cb7a01acSMauro Carvalho Chehab if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { 1045cb7a01acSMauro Carvalho Chehab dev_warn(&client->dev, 1046cb7a01acSMauro Carvalho Chehab "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); 1047cb7a01acSMauro Carvalho Chehab return -EIO; 1048cb7a01acSMauro Carvalho Chehab } 1049cb7a01acSMauro Carvalho Chehab 105037b9f211SLaurent Pinchart mt9p031 = devm_kzalloc(&client->dev, sizeof(*mt9p031), GFP_KERNEL); 1051cb7a01acSMauro Carvalho Chehab if (mt9p031 == NULL) 1052cb7a01acSMauro Carvalho Chehab return -ENOMEM; 1053cb7a01acSMauro Carvalho Chehab 1054cb7a01acSMauro Carvalho Chehab mt9p031->pdata = pdata; 1055cb7a01acSMauro Carvalho Chehab mt9p031->output_control = MT9P031_OUTPUT_CONTROL_DEF; 1056cb7a01acSMauro Carvalho Chehab mt9p031->mode2 = MT9P031_READ_MODE_2_ROW_BLC; 1057cb7a01acSMauro Carvalho Chehab mt9p031->model = did->driver_data; 1058cb7a01acSMauro Carvalho Chehab 10597997196cSLaurent Pinchart mt9p031->regulators[0].supply = "vdd"; 10607997196cSLaurent Pinchart mt9p031->regulators[1].supply = "vdd_io"; 10617997196cSLaurent Pinchart mt9p031->regulators[2].supply = "vaa"; 106297f21276SLaurent Pinchart 10637997196cSLaurent Pinchart ret = devm_regulator_bulk_get(&client->dev, 3, mt9p031->regulators); 10647997196cSLaurent Pinchart if (ret < 0) { 106597f21276SLaurent Pinchart dev_err(&client->dev, "Unable to get regulators\n"); 10667997196cSLaurent Pinchart return ret; 106797f21276SLaurent Pinchart } 106897f21276SLaurent Pinchart 106915af4a53SLad, Prabhakar mutex_init(&mt9p031->power_lock); 107015af4a53SLad, Prabhakar 1071b28d7017SLad, Prabhakar v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 6); 1072cb7a01acSMauro Carvalho Chehab 1073cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, 1074cb7a01acSMauro Carvalho Chehab V4L2_CID_EXPOSURE, MT9P031_SHUTTER_WIDTH_MIN, 1075cb7a01acSMauro Carvalho Chehab MT9P031_SHUTTER_WIDTH_MAX, 1, 1076cb7a01acSMauro Carvalho Chehab MT9P031_SHUTTER_WIDTH_DEF); 1077cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, 1078cb7a01acSMauro Carvalho Chehab V4L2_CID_GAIN, MT9P031_GLOBAL_GAIN_MIN, 1079cb7a01acSMauro Carvalho Chehab MT9P031_GLOBAL_GAIN_MAX, 1, MT9P031_GLOBAL_GAIN_DEF); 1080cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, 1081cb7a01acSMauro Carvalho Chehab V4L2_CID_HFLIP, 0, 1, 1, 0); 1082cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, 1083cb7a01acSMauro Carvalho Chehab V4L2_CID_VFLIP, 0, 1, 1, 0); 1084cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&mt9p031->ctrls, &mt9p031_ctrl_ops, 1085cb7a01acSMauro Carvalho Chehab V4L2_CID_PIXEL_RATE, pdata->target_freq, 1086cb7a01acSMauro Carvalho Chehab pdata->target_freq, 1, pdata->target_freq); 1087b28d7017SLad, Prabhakar v4l2_ctrl_new_std_menu_items(&mt9p031->ctrls, &mt9p031_ctrl_ops, 1088b28d7017SLad, Prabhakar V4L2_CID_TEST_PATTERN, 1089b28d7017SLad, Prabhakar ARRAY_SIZE(mt9p031_test_pattern_menu) - 1, 0, 1090b28d7017SLad, Prabhakar 0, mt9p031_test_pattern_menu); 1091cb7a01acSMauro Carvalho Chehab 1092cb7a01acSMauro Carvalho Chehab for (i = 0; i < ARRAY_SIZE(mt9p031_ctrls); ++i) 1093cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_custom(&mt9p031->ctrls, &mt9p031_ctrls[i], NULL); 1094cb7a01acSMauro Carvalho Chehab 1095cb7a01acSMauro Carvalho Chehab mt9p031->subdev.ctrl_handler = &mt9p031->ctrls; 1096cb7a01acSMauro Carvalho Chehab 1097cb7a01acSMauro Carvalho Chehab if (mt9p031->ctrls.error) { 1098cb7a01acSMauro Carvalho Chehab printk(KERN_INFO "%s: control initialization error %d\n", 1099cb7a01acSMauro Carvalho Chehab __func__, mt9p031->ctrls.error); 1100cb7a01acSMauro Carvalho Chehab ret = mt9p031->ctrls.error; 1101cb7a01acSMauro Carvalho Chehab goto done; 1102cb7a01acSMauro Carvalho Chehab } 1103cb7a01acSMauro Carvalho Chehab 1104cb7a01acSMauro Carvalho Chehab mt9p031->blc_auto = v4l2_ctrl_find(&mt9p031->ctrls, V4L2_CID_BLC_AUTO); 1105cb7a01acSMauro Carvalho Chehab mt9p031->blc_offset = v4l2_ctrl_find(&mt9p031->ctrls, 1106cb7a01acSMauro Carvalho Chehab V4L2_CID_BLC_DIGITAL_OFFSET); 1107cb7a01acSMauro Carvalho Chehab 1108cb7a01acSMauro Carvalho Chehab v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops); 1109cb7a01acSMauro Carvalho Chehab mt9p031->subdev.internal_ops = &mt9p031_subdev_internal_ops; 1110cb7a01acSMauro Carvalho Chehab 1111173bf6e5SHans Verkuil mt9p031->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR; 1112cb7a01acSMauro Carvalho Chehab mt9p031->pad.flags = MEDIA_PAD_FL_SOURCE; 1113ab22e77cSMauro Carvalho Chehab ret = media_entity_pads_init(&mt9p031->subdev.entity, 1, &mt9p031->pad); 1114cb7a01acSMauro Carvalho Chehab if (ret < 0) 1115cb7a01acSMauro Carvalho Chehab goto done; 1116cb7a01acSMauro Carvalho Chehab 1117cb7a01acSMauro Carvalho Chehab mt9p031->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 1118cb7a01acSMauro Carvalho Chehab 1119cb7a01acSMauro Carvalho Chehab mt9p031->crop.width = MT9P031_WINDOW_WIDTH_DEF; 1120cb7a01acSMauro Carvalho Chehab mt9p031->crop.height = MT9P031_WINDOW_HEIGHT_DEF; 1121cb7a01acSMauro Carvalho Chehab mt9p031->crop.left = MT9P031_COLUMN_START_DEF; 1122cb7a01acSMauro Carvalho Chehab mt9p031->crop.top = MT9P031_ROW_START_DEF; 1123cb7a01acSMauro Carvalho Chehab 1124cb7a01acSMauro Carvalho Chehab if (mt9p031->model == MT9P031_MODEL_MONOCHROME) 1125f5fe58fdSBoris BREZILLON mt9p031->format.code = MEDIA_BUS_FMT_Y12_1X12; 1126cb7a01acSMauro Carvalho Chehab else 1127f5fe58fdSBoris BREZILLON mt9p031->format.code = MEDIA_BUS_FMT_SGRBG12_1X12; 1128cb7a01acSMauro Carvalho Chehab 1129cb7a01acSMauro Carvalho Chehab mt9p031->format.width = MT9P031_WINDOW_WIDTH_DEF; 1130cb7a01acSMauro Carvalho Chehab mt9p031->format.height = MT9P031_WINDOW_HEIGHT_DEF; 1131cb7a01acSMauro Carvalho Chehab mt9p031->format.field = V4L2_FIELD_NONE; 1132cb7a01acSMauro Carvalho Chehab mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB; 1133cb7a01acSMauro Carvalho Chehab 11347c3be9f8SLaurent Pinchart mt9p031->reset = devm_gpiod_get_optional(&client->dev, "reset", 11357c3be9f8SLaurent Pinchart GPIOD_OUT_HIGH); 1136cb7a01acSMauro Carvalho Chehab 1137d6749258SLaurent Pinchart ret = mt9p031_clk_setup(mt9p031); 11389012d088SLad, Prabhakar if (ret) 11399012d088SLad, Prabhakar goto done; 11409012d088SLad, Prabhakar 11419012d088SLad, Prabhakar ret = v4l2_async_register_subdev(&mt9p031->subdev); 1142cb7a01acSMauro Carvalho Chehab 1143cb7a01acSMauro Carvalho Chehab done: 1144cb7a01acSMauro Carvalho Chehab if (ret < 0) { 1145cb7a01acSMauro Carvalho Chehab v4l2_ctrl_handler_free(&mt9p031->ctrls); 1146cb7a01acSMauro Carvalho Chehab media_entity_cleanup(&mt9p031->subdev.entity); 114715af4a53SLad, Prabhakar mutex_destroy(&mt9p031->power_lock); 1148cb7a01acSMauro Carvalho Chehab } 1149cb7a01acSMauro Carvalho Chehab 1150cb7a01acSMauro Carvalho Chehab return ret; 1151cb7a01acSMauro Carvalho Chehab } 1152cb7a01acSMauro Carvalho Chehab 1153cb7a01acSMauro Carvalho Chehab static int mt9p031_remove(struct i2c_client *client) 1154cb7a01acSMauro Carvalho Chehab { 1155cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *subdev = i2c_get_clientdata(client); 1156cb7a01acSMauro Carvalho Chehab struct mt9p031 *mt9p031 = to_mt9p031(subdev); 1157cb7a01acSMauro Carvalho Chehab 1158cb7a01acSMauro Carvalho Chehab v4l2_ctrl_handler_free(&mt9p031->ctrls); 11599012d088SLad, Prabhakar v4l2_async_unregister_subdev(subdev); 1160cb7a01acSMauro Carvalho Chehab media_entity_cleanup(&subdev->entity); 116115af4a53SLad, Prabhakar mutex_destroy(&mt9p031->power_lock); 1162cb7a01acSMauro Carvalho Chehab 1163cb7a01acSMauro Carvalho Chehab return 0; 1164cb7a01acSMauro Carvalho Chehab } 1165cb7a01acSMauro Carvalho Chehab 1166cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id mt9p031_id[] = { 1167cb7a01acSMauro Carvalho Chehab { "mt9p031", MT9P031_MODEL_COLOR }, 1168cb7a01acSMauro Carvalho Chehab { "mt9p031m", MT9P031_MODEL_MONOCHROME }, 1169cb7a01acSMauro Carvalho Chehab { } 1170cb7a01acSMauro Carvalho Chehab }; 1171cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, mt9p031_id); 1172cb7a01acSMauro Carvalho Chehab 11738d4da37cSLad, Prabhakar #if IS_ENABLED(CONFIG_OF) 11748d4da37cSLad, Prabhakar static const struct of_device_id mt9p031_of_match[] = { 11758d4da37cSLad, Prabhakar { .compatible = "aptina,mt9p031", }, 11768d4da37cSLad, Prabhakar { .compatible = "aptina,mt9p031m", }, 11778d4da37cSLad, Prabhakar { /* sentinel */ }, 11788d4da37cSLad, Prabhakar }; 11798d4da37cSLad, Prabhakar MODULE_DEVICE_TABLE(of, mt9p031_of_match); 11808d4da37cSLad, Prabhakar #endif 11818d4da37cSLad, Prabhakar 1182cb7a01acSMauro Carvalho Chehab static struct i2c_driver mt9p031_i2c_driver = { 1183cb7a01acSMauro Carvalho Chehab .driver = { 11848d4da37cSLad, Prabhakar .of_match_table = of_match_ptr(mt9p031_of_match), 1185cb7a01acSMauro Carvalho Chehab .name = "mt9p031", 1186cb7a01acSMauro Carvalho Chehab }, 1187cb7a01acSMauro Carvalho Chehab .probe = mt9p031_probe, 1188cb7a01acSMauro Carvalho Chehab .remove = mt9p031_remove, 1189cb7a01acSMauro Carvalho Chehab .id_table = mt9p031_id, 1190cb7a01acSMauro Carvalho Chehab }; 1191cb7a01acSMauro Carvalho Chehab 1192cb7a01acSMauro Carvalho Chehab module_i2c_driver(mt9p031_i2c_driver); 1193cb7a01acSMauro Carvalho Chehab 1194cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("Aptina MT9P031 Camera driver"); 1195cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Bastian Hecht <hechtb@gmail.com>"); 1196cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL v2"); 1197