Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[media] v4l: Add mt9v032 sensor driver

The MT9V032 is a parallel wide VGA sensor from Aptina (formerly Micron)
controlled through I2C.

The driver creates a V4L2 subdevice. It currently supports binning and
cropping, and the gain, auto gain, exposure, auto exposure and test
pattern controls.

Signed-off-by: Detlev Casanova <detlev.casanova@gmail.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>

authored by

Detlev Casanova and committed by
Mauro Carvalho Chehab
0f2ce168 9d8e1b54

+793
+7
drivers/media/video/Kconfig
··· 337 337 mt0v011 1.3 Mpixel camera. It currently only works with the 338 338 em28xx driver. 339 339 340 + config VIDEO_MT9V032 341 + tristate "Micron MT9V032 sensor support" 342 + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API 343 + ---help--- 344 + This is a Video4Linux2 sensor-level driver for the Micron 345 + MT9V032 752x480 CMOS sensor. 346 + 340 347 config VIDEO_TCM825X 341 348 tristate "TCM825x camera sensor support" 342 349 depends on I2C && VIDEO_V4L2
+1
drivers/media/video/Makefile
··· 66 66 obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o 67 67 obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o 68 68 obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o 69 + obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o 69 70 obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o 70 71 obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o 71 72
+773
drivers/media/video/mt9v032.c
··· 1 + /* 2 + * Driver for MT9V032 CMOS Image Sensor from Micron 3 + * 4 + * Copyright (C) 2010, Laurent Pinchart <laurent.pinchart@ideasonboard.com> 5 + * 6 + * Based on the MT9M001 driver, 7 + * 8 + * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> 9 + * 10 + * This program is free software; you can redistribute it and/or modify 11 + * it under the terms of the GNU General Public License version 2 as 12 + * published by the Free Software Foundation. 13 + */ 14 + 15 + #include <linux/delay.h> 16 + #include <linux/i2c.h> 17 + #include <linux/log2.h> 18 + #include <linux/mutex.h> 19 + #include <linux/slab.h> 20 + #include <linux/videodev2.h> 21 + #include <linux/v4l2-mediabus.h> 22 + 23 + #include <media/mt9v032.h> 24 + #include <media/v4l2-ctrls.h> 25 + #include <media/v4l2-device.h> 26 + #include <media/v4l2-subdev.h> 27 + 28 + #define MT9V032_PIXEL_ARRAY_HEIGHT 492 29 + #define MT9V032_PIXEL_ARRAY_WIDTH 782 30 + 31 + #define MT9V032_CHIP_VERSION 0x00 32 + #define MT9V032_CHIP_ID_REV1 0x1311 33 + #define MT9V032_CHIP_ID_REV3 0x1313 34 + #define MT9V032_ROW_START 0x01 35 + #define MT9V032_ROW_START_MIN 4 36 + #define MT9V032_ROW_START_DEF 10 37 + #define MT9V032_ROW_START_MAX 482 38 + #define MT9V032_COLUMN_START 0x02 39 + #define MT9V032_COLUMN_START_MIN 1 40 + #define MT9V032_COLUMN_START_DEF 2 41 + #define MT9V032_COLUMN_START_MAX 752 42 + #define MT9V032_WINDOW_HEIGHT 0x03 43 + #define MT9V032_WINDOW_HEIGHT_MIN 1 44 + #define MT9V032_WINDOW_HEIGHT_DEF 480 45 + #define MT9V032_WINDOW_HEIGHT_MAX 480 46 + #define MT9V032_WINDOW_WIDTH 0x04 47 + #define MT9V032_WINDOW_WIDTH_MIN 1 48 + #define MT9V032_WINDOW_WIDTH_DEF 752 49 + #define MT9V032_WINDOW_WIDTH_MAX 752 50 + #define MT9V032_HORIZONTAL_BLANKING 0x05 51 + #define MT9V032_HORIZONTAL_BLANKING_MIN 43 52 + #define MT9V032_HORIZONTAL_BLANKING_MAX 1023 53 + #define MT9V032_VERTICAL_BLANKING 0x06 54 + #define MT9V032_VERTICAL_BLANKING_MIN 4 55 + #define MT9V032_VERTICAL_BLANKING_MAX 3000 56 + #define MT9V032_CHIP_CONTROL 0x07 57 + #define MT9V032_CHIP_CONTROL_MASTER_MODE (1 << 3) 58 + #define MT9V032_CHIP_CONTROL_DOUT_ENABLE (1 << 7) 59 + #define MT9V032_CHIP_CONTROL_SEQUENTIAL (1 << 8) 60 + #define MT9V032_SHUTTER_WIDTH1 0x08 61 + #define MT9V032_SHUTTER_WIDTH2 0x09 62 + #define MT9V032_SHUTTER_WIDTH_CONTROL 0x0a 63 + #define MT9V032_TOTAL_SHUTTER_WIDTH 0x0b 64 + #define MT9V032_TOTAL_SHUTTER_WIDTH_MIN 1 65 + #define MT9V032_TOTAL_SHUTTER_WIDTH_DEF 480 66 + #define MT9V032_TOTAL_SHUTTER_WIDTH_MAX 32767 67 + #define MT9V032_RESET 0x0c 68 + #define MT9V032_READ_MODE 0x0d 69 + #define MT9V032_READ_MODE_ROW_BIN_MASK (3 << 0) 70 + #define MT9V032_READ_MODE_ROW_BIN_SHIFT 0 71 + #define MT9V032_READ_MODE_COLUMN_BIN_MASK (3 << 2) 72 + #define MT9V032_READ_MODE_COLUMN_BIN_SHIFT 2 73 + #define MT9V032_READ_MODE_ROW_FLIP (1 << 4) 74 + #define MT9V032_READ_MODE_COLUMN_FLIP (1 << 5) 75 + #define MT9V032_READ_MODE_DARK_COLUMNS (1 << 6) 76 + #define MT9V032_READ_MODE_DARK_ROWS (1 << 7) 77 + #define MT9V032_PIXEL_OPERATION_MODE 0x0f 78 + #define MT9V032_PIXEL_OPERATION_MODE_COLOR (1 << 2) 79 + #define MT9V032_PIXEL_OPERATION_MODE_HDR (1 << 6) 80 + #define MT9V032_ANALOG_GAIN 0x35 81 + #define MT9V032_ANALOG_GAIN_MIN 16 82 + #define MT9V032_ANALOG_GAIN_DEF 16 83 + #define MT9V032_ANALOG_GAIN_MAX 64 84 + #define MT9V032_MAX_ANALOG_GAIN 0x36 85 + #define MT9V032_MAX_ANALOG_GAIN_MAX 127 86 + #define MT9V032_FRAME_DARK_AVERAGE 0x42 87 + #define MT9V032_DARK_AVG_THRESH 0x46 88 + #define MT9V032_DARK_AVG_LOW_THRESH_MASK (255 << 0) 89 + #define MT9V032_DARK_AVG_LOW_THRESH_SHIFT 0 90 + #define MT9V032_DARK_AVG_HIGH_THRESH_MASK (255 << 8) 91 + #define MT9V032_DARK_AVG_HIGH_THRESH_SHIFT 8 92 + #define MT9V032_ROW_NOISE_CORR_CONTROL 0x70 93 + #define MT9V032_ROW_NOISE_CORR_ENABLE (1 << 5) 94 + #define MT9V032_ROW_NOISE_CORR_USE_BLK_AVG (1 << 7) 95 + #define MT9V032_PIXEL_CLOCK 0x74 96 + #define MT9V032_PIXEL_CLOCK_INV_LINE (1 << 0) 97 + #define MT9V032_PIXEL_CLOCK_INV_FRAME (1 << 1) 98 + #define MT9V032_PIXEL_CLOCK_XOR_LINE (1 << 2) 99 + #define MT9V032_PIXEL_CLOCK_CONT_LINE (1 << 3) 100 + #define MT9V032_PIXEL_CLOCK_INV_PXL_CLK (1 << 4) 101 + #define MT9V032_TEST_PATTERN 0x7f 102 + #define MT9V032_TEST_PATTERN_DATA_MASK (1023 << 0) 103 + #define MT9V032_TEST_PATTERN_DATA_SHIFT 0 104 + #define MT9V032_TEST_PATTERN_USE_DATA (1 << 10) 105 + #define MT9V032_TEST_PATTERN_GRAY_MASK (3 << 11) 106 + #define MT9V032_TEST_PATTERN_GRAY_NONE (0 << 11) 107 + #define MT9V032_TEST_PATTERN_GRAY_VERTICAL (1 << 11) 108 + #define MT9V032_TEST_PATTERN_GRAY_HORIZONTAL (2 << 11) 109 + #define MT9V032_TEST_PATTERN_GRAY_DIAGONAL (3 << 11) 110 + #define MT9V032_TEST_PATTERN_ENABLE (1 << 13) 111 + #define MT9V032_TEST_PATTERN_FLIP (1 << 14) 112 + #define MT9V032_AEC_AGC_ENABLE 0xaf 113 + #define MT9V032_AEC_ENABLE (1 << 0) 114 + #define MT9V032_AGC_ENABLE (1 << 1) 115 + #define MT9V032_THERMAL_INFO 0xc1 116 + 117 + struct mt9v032 { 118 + struct v4l2_subdev subdev; 119 + struct media_pad pad; 120 + 121 + struct v4l2_mbus_framefmt format; 122 + struct v4l2_rect crop; 123 + 124 + struct v4l2_ctrl_handler ctrls; 125 + 126 + struct mutex power_lock; 127 + int power_count; 128 + 129 + struct mt9v032_platform_data *pdata; 130 + u16 chip_control; 131 + u16 aec_agc; 132 + }; 133 + 134 + static struct mt9v032 *to_mt9v032(struct v4l2_subdev *sd) 135 + { 136 + return container_of(sd, struct mt9v032, subdev); 137 + } 138 + 139 + static int mt9v032_read(struct i2c_client *client, const u8 reg) 140 + { 141 + s32 data = i2c_smbus_read_word_data(client, reg); 142 + dev_dbg(&client->dev, "%s: read 0x%04x from 0x%02x\n", __func__, 143 + swab16(data), reg); 144 + return data < 0 ? data : swab16(data); 145 + } 146 + 147 + static int mt9v032_write(struct i2c_client *client, const u8 reg, 148 + const u16 data) 149 + { 150 + dev_dbg(&client->dev, "%s: writing 0x%04x to 0x%02x\n", __func__, 151 + data, reg); 152 + return i2c_smbus_write_word_data(client, reg, swab16(data)); 153 + } 154 + 155 + static int mt9v032_set_chip_control(struct mt9v032 *mt9v032, u16 clear, u16 set) 156 + { 157 + struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); 158 + u16 value = (mt9v032->chip_control & ~clear) | set; 159 + int ret; 160 + 161 + ret = mt9v032_write(client, MT9V032_CHIP_CONTROL, value); 162 + if (ret < 0) 163 + return ret; 164 + 165 + mt9v032->chip_control = value; 166 + return 0; 167 + } 168 + 169 + static int 170 + mt9v032_update_aec_agc(struct mt9v032 *mt9v032, u16 which, int enable) 171 + { 172 + struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); 173 + u16 value = mt9v032->aec_agc; 174 + int ret; 175 + 176 + if (enable) 177 + value |= which; 178 + else 179 + value &= ~which; 180 + 181 + ret = mt9v032_write(client, MT9V032_AEC_AGC_ENABLE, value); 182 + if (ret < 0) 183 + return ret; 184 + 185 + mt9v032->aec_agc = value; 186 + return 0; 187 + } 188 + 189 + static int mt9v032_power_on(struct mt9v032 *mt9v032) 190 + { 191 + struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); 192 + int ret; 193 + 194 + if (mt9v032->pdata->set_clock) { 195 + mt9v032->pdata->set_clock(&mt9v032->subdev, 25000000); 196 + udelay(1); 197 + } 198 + 199 + /* Reset the chip and stop data read out */ 200 + ret = mt9v032_write(client, MT9V032_RESET, 1); 201 + if (ret < 0) 202 + return ret; 203 + 204 + ret = mt9v032_write(client, MT9V032_RESET, 0); 205 + if (ret < 0) 206 + return ret; 207 + 208 + return mt9v032_write(client, MT9V032_CHIP_CONTROL, 0); 209 + } 210 + 211 + static void mt9v032_power_off(struct mt9v032 *mt9v032) 212 + { 213 + if (mt9v032->pdata->set_clock) 214 + mt9v032->pdata->set_clock(&mt9v032->subdev, 0); 215 + } 216 + 217 + static int __mt9v032_set_power(struct mt9v032 *mt9v032, bool on) 218 + { 219 + struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); 220 + int ret; 221 + 222 + if (!on) { 223 + mt9v032_power_off(mt9v032); 224 + return 0; 225 + } 226 + 227 + ret = mt9v032_power_on(mt9v032); 228 + if (ret < 0) 229 + return ret; 230 + 231 + /* Configure the pixel clock polarity */ 232 + if (mt9v032->pdata && mt9v032->pdata->clk_pol) { 233 + ret = mt9v032_write(client, MT9V032_PIXEL_CLOCK, 234 + MT9V032_PIXEL_CLOCK_INV_PXL_CLK); 235 + if (ret < 0) 236 + return ret; 237 + } 238 + 239 + /* Disable the noise correction algorithm and restore the controls. */ 240 + ret = mt9v032_write(client, MT9V032_ROW_NOISE_CORR_CONTROL, 0); 241 + if (ret < 0) 242 + return ret; 243 + 244 + return v4l2_ctrl_handler_setup(&mt9v032->ctrls); 245 + } 246 + 247 + /* ----------------------------------------------------------------------------- 248 + * V4L2 subdev video operations 249 + */ 250 + 251 + static struct v4l2_mbus_framefmt * 252 + __mt9v032_get_pad_format(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh, 253 + unsigned int pad, enum v4l2_subdev_format_whence which) 254 + { 255 + switch (which) { 256 + case V4L2_SUBDEV_FORMAT_TRY: 257 + return v4l2_subdev_get_try_format(fh, pad); 258 + case V4L2_SUBDEV_FORMAT_ACTIVE: 259 + return &mt9v032->format; 260 + default: 261 + return NULL; 262 + } 263 + } 264 + 265 + static struct v4l2_rect * 266 + __mt9v032_get_pad_crop(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh, 267 + unsigned int pad, enum v4l2_subdev_format_whence which) 268 + { 269 + switch (which) { 270 + case V4L2_SUBDEV_FORMAT_TRY: 271 + return v4l2_subdev_get_try_crop(fh, pad); 272 + case V4L2_SUBDEV_FORMAT_ACTIVE: 273 + return &mt9v032->crop; 274 + default: 275 + return NULL; 276 + } 277 + } 278 + 279 + static int mt9v032_s_stream(struct v4l2_subdev *subdev, int enable) 280 + { 281 + const u16 mode = MT9V032_CHIP_CONTROL_MASTER_MODE 282 + | MT9V032_CHIP_CONTROL_DOUT_ENABLE 283 + | MT9V032_CHIP_CONTROL_SEQUENTIAL; 284 + struct i2c_client *client = v4l2_get_subdevdata(subdev); 285 + struct mt9v032 *mt9v032 = to_mt9v032(subdev); 286 + struct v4l2_mbus_framefmt *format = &mt9v032->format; 287 + struct v4l2_rect *crop = &mt9v032->crop; 288 + unsigned int hratio; 289 + unsigned int vratio; 290 + int ret; 291 + 292 + if (!enable) 293 + return mt9v032_set_chip_control(mt9v032, mode, 0); 294 + 295 + /* Configure the window size and row/column bin */ 296 + hratio = DIV_ROUND_CLOSEST(crop->width, format->width); 297 + vratio = DIV_ROUND_CLOSEST(crop->height, format->height); 298 + 299 + ret = mt9v032_write(client, MT9V032_READ_MODE, 300 + (hratio - 1) << MT9V032_READ_MODE_ROW_BIN_SHIFT | 301 + (vratio - 1) << MT9V032_READ_MODE_COLUMN_BIN_SHIFT); 302 + if (ret < 0) 303 + return ret; 304 + 305 + ret = mt9v032_write(client, MT9V032_COLUMN_START, crop->left); 306 + if (ret < 0) 307 + return ret; 308 + 309 + ret = mt9v032_write(client, MT9V032_ROW_START, crop->top); 310 + if (ret < 0) 311 + return ret; 312 + 313 + ret = mt9v032_write(client, MT9V032_WINDOW_WIDTH, crop->width); 314 + if (ret < 0) 315 + return ret; 316 + 317 + ret = mt9v032_write(client, MT9V032_WINDOW_HEIGHT, crop->height); 318 + if (ret < 0) 319 + return ret; 320 + 321 + ret = mt9v032_write(client, MT9V032_HORIZONTAL_BLANKING, 322 + max(43, 660 - crop->width)); 323 + if (ret < 0) 324 + return ret; 325 + 326 + /* Switch to master "normal" mode */ 327 + return mt9v032_set_chip_control(mt9v032, 0, mode); 328 + } 329 + 330 + static int mt9v032_enum_mbus_code(struct v4l2_subdev *subdev, 331 + struct v4l2_subdev_fh *fh, 332 + struct v4l2_subdev_mbus_code_enum *code) 333 + { 334 + if (code->index > 0) 335 + return -EINVAL; 336 + 337 + code->code = V4L2_MBUS_FMT_SGRBG10_1X10; 338 + return 0; 339 + } 340 + 341 + static int mt9v032_enum_frame_size(struct v4l2_subdev *subdev, 342 + struct v4l2_subdev_fh *fh, 343 + struct v4l2_subdev_frame_size_enum *fse) 344 + { 345 + if (fse->index >= 8 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10) 346 + return -EINVAL; 347 + 348 + fse->min_width = MT9V032_WINDOW_WIDTH_DEF / fse->index; 349 + fse->max_width = fse->min_width; 350 + fse->min_height = MT9V032_WINDOW_HEIGHT_DEF / fse->index; 351 + fse->max_height = fse->min_height; 352 + 353 + return 0; 354 + } 355 + 356 + static int mt9v032_get_format(struct v4l2_subdev *subdev, 357 + struct v4l2_subdev_fh *fh, 358 + struct v4l2_subdev_format *format) 359 + { 360 + struct mt9v032 *mt9v032 = to_mt9v032(subdev); 361 + 362 + format->format = *__mt9v032_get_pad_format(mt9v032, fh, format->pad, 363 + format->which); 364 + return 0; 365 + } 366 + 367 + static int mt9v032_set_format(struct v4l2_subdev *subdev, 368 + struct v4l2_subdev_fh *fh, 369 + struct v4l2_subdev_format *format) 370 + { 371 + struct mt9v032 *mt9v032 = to_mt9v032(subdev); 372 + struct v4l2_mbus_framefmt *__format; 373 + struct v4l2_rect *__crop; 374 + unsigned int width; 375 + unsigned int height; 376 + unsigned int hratio; 377 + unsigned int vratio; 378 + 379 + __crop = __mt9v032_get_pad_crop(mt9v032, fh, format->pad, 380 + format->which); 381 + 382 + /* Clamp the width and height to avoid dividing by zero. */ 383 + width = clamp_t(unsigned int, ALIGN(format->format.width, 2), 384 + max(__crop->width / 8, MT9V032_WINDOW_WIDTH_MIN), 385 + __crop->width); 386 + height = clamp_t(unsigned int, ALIGN(format->format.height, 2), 387 + max(__crop->height / 8, MT9V032_WINDOW_HEIGHT_MIN), 388 + __crop->height); 389 + 390 + hratio = DIV_ROUND_CLOSEST(__crop->width, width); 391 + vratio = DIV_ROUND_CLOSEST(__crop->height, height); 392 + 393 + __format = __mt9v032_get_pad_format(mt9v032, fh, format->pad, 394 + format->which); 395 + __format->width = __crop->width / hratio; 396 + __format->height = __crop->height / vratio; 397 + 398 + format->format = *__format; 399 + 400 + return 0; 401 + } 402 + 403 + static int mt9v032_get_crop(struct v4l2_subdev *subdev, 404 + struct v4l2_subdev_fh *fh, 405 + struct v4l2_subdev_crop *crop) 406 + { 407 + struct mt9v032 *mt9v032 = to_mt9v032(subdev); 408 + 409 + crop->rect = *__mt9v032_get_pad_crop(mt9v032, fh, crop->pad, 410 + crop->which); 411 + return 0; 412 + } 413 + 414 + static int mt9v032_set_crop(struct v4l2_subdev *subdev, 415 + struct v4l2_subdev_fh *fh, 416 + struct v4l2_subdev_crop *crop) 417 + { 418 + struct mt9v032 *mt9v032 = to_mt9v032(subdev); 419 + struct v4l2_mbus_framefmt *__format; 420 + struct v4l2_rect *__crop; 421 + struct v4l2_rect rect; 422 + 423 + /* Clamp the crop rectangle boundaries and align them to a multiple of 2 424 + * pixels. 425 + */ 426 + rect.left = clamp(ALIGN(crop->rect.left, 2), 427 + MT9V032_COLUMN_START_MIN, 428 + MT9V032_COLUMN_START_MAX); 429 + rect.top = clamp(ALIGN(crop->rect.top, 2), 430 + MT9V032_ROW_START_MIN, 431 + MT9V032_ROW_START_MAX); 432 + rect.width = clamp(ALIGN(crop->rect.width, 2), 433 + MT9V032_WINDOW_WIDTH_MIN, 434 + MT9V032_WINDOW_WIDTH_MAX); 435 + rect.height = clamp(ALIGN(crop->rect.height, 2), 436 + MT9V032_WINDOW_HEIGHT_MIN, 437 + MT9V032_WINDOW_HEIGHT_MAX); 438 + 439 + rect.width = min(rect.width, MT9V032_PIXEL_ARRAY_WIDTH - rect.left); 440 + rect.height = min(rect.height, MT9V032_PIXEL_ARRAY_HEIGHT - rect.top); 441 + 442 + __crop = __mt9v032_get_pad_crop(mt9v032, fh, crop->pad, crop->which); 443 + 444 + if (rect.width != __crop->width || rect.height != __crop->height) { 445 + /* Reset the output image size if the crop rectangle size has 446 + * been modified. 447 + */ 448 + __format = __mt9v032_get_pad_format(mt9v032, fh, crop->pad, 449 + crop->which); 450 + __format->width = rect.width; 451 + __format->height = rect.height; 452 + } 453 + 454 + *__crop = rect; 455 + crop->rect = rect; 456 + 457 + return 0; 458 + } 459 + 460 + /* ----------------------------------------------------------------------------- 461 + * V4L2 subdev control operations 462 + */ 463 + 464 + #define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001) 465 + 466 + static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl) 467 + { 468 + struct mt9v032 *mt9v032 = 469 + container_of(ctrl->handler, struct mt9v032, ctrls); 470 + struct i2c_client *client = v4l2_get_subdevdata(&mt9v032->subdev); 471 + u16 data; 472 + 473 + switch (ctrl->id) { 474 + case V4L2_CID_AUTOGAIN: 475 + return mt9v032_update_aec_agc(mt9v032, MT9V032_AGC_ENABLE, 476 + ctrl->val); 477 + 478 + case V4L2_CID_GAIN: 479 + return mt9v032_write(client, MT9V032_ANALOG_GAIN, ctrl->val); 480 + 481 + case V4L2_CID_EXPOSURE_AUTO: 482 + return mt9v032_update_aec_agc(mt9v032, MT9V032_AEC_ENABLE, 483 + ctrl->val); 484 + 485 + case V4L2_CID_EXPOSURE: 486 + return mt9v032_write(client, MT9V032_TOTAL_SHUTTER_WIDTH, 487 + ctrl->val); 488 + 489 + case V4L2_CID_TEST_PATTERN: 490 + switch (ctrl->val) { 491 + case 0: 492 + data = 0; 493 + break; 494 + case 1: 495 + data = MT9V032_TEST_PATTERN_GRAY_VERTICAL 496 + | MT9V032_TEST_PATTERN_ENABLE; 497 + break; 498 + case 2: 499 + data = MT9V032_TEST_PATTERN_GRAY_HORIZONTAL 500 + | MT9V032_TEST_PATTERN_ENABLE; 501 + break; 502 + case 3: 503 + data = MT9V032_TEST_PATTERN_GRAY_DIAGONAL 504 + | MT9V032_TEST_PATTERN_ENABLE; 505 + break; 506 + default: 507 + data = (ctrl->val << MT9V032_TEST_PATTERN_DATA_SHIFT) 508 + | MT9V032_TEST_PATTERN_USE_DATA 509 + | MT9V032_TEST_PATTERN_ENABLE 510 + | MT9V032_TEST_PATTERN_FLIP; 511 + break; 512 + } 513 + 514 + return mt9v032_write(client, MT9V032_TEST_PATTERN, data); 515 + } 516 + 517 + return 0; 518 + } 519 + 520 + static struct v4l2_ctrl_ops mt9v032_ctrl_ops = { 521 + .s_ctrl = mt9v032_s_ctrl, 522 + }; 523 + 524 + static const struct v4l2_ctrl_config mt9v032_ctrls[] = { 525 + { 526 + .ops = &mt9v032_ctrl_ops, 527 + .id = V4L2_CID_TEST_PATTERN, 528 + .type = V4L2_CTRL_TYPE_INTEGER, 529 + .name = "Test pattern", 530 + .min = 0, 531 + .max = 1023, 532 + .step = 1, 533 + .def = 0, 534 + .flags = 0, 535 + } 536 + }; 537 + 538 + /* ----------------------------------------------------------------------------- 539 + * V4L2 subdev core operations 540 + */ 541 + 542 + static int mt9v032_set_power(struct v4l2_subdev *subdev, int on) 543 + { 544 + struct mt9v032 *mt9v032 = to_mt9v032(subdev); 545 + int ret = 0; 546 + 547 + mutex_lock(&mt9v032->power_lock); 548 + 549 + /* If the power count is modified from 0 to != 0 or from != 0 to 0, 550 + * update the power state. 551 + */ 552 + if (mt9v032->power_count == !on) { 553 + ret = __mt9v032_set_power(mt9v032, !!on); 554 + if (ret < 0) 555 + goto done; 556 + } 557 + 558 + /* Update the power count. */ 559 + mt9v032->power_count += on ? 1 : -1; 560 + WARN_ON(mt9v032->power_count < 0); 561 + 562 + done: 563 + mutex_unlock(&mt9v032->power_lock); 564 + return ret; 565 + } 566 + 567 + /* ----------------------------------------------------------------------------- 568 + * V4L2 subdev internal operations 569 + */ 570 + 571 + static int mt9v032_registered(struct v4l2_subdev *subdev) 572 + { 573 + struct i2c_client *client = v4l2_get_subdevdata(subdev); 574 + struct mt9v032 *mt9v032 = to_mt9v032(subdev); 575 + s32 data; 576 + int ret; 577 + 578 + dev_info(&client->dev, "Probing MT9V032 at address 0x%02x\n", 579 + client->addr); 580 + 581 + ret = mt9v032_power_on(mt9v032); 582 + if (ret < 0) { 583 + dev_err(&client->dev, "MT9V032 power up failed\n"); 584 + return ret; 585 + } 586 + 587 + /* Read and check the sensor version */ 588 + data = mt9v032_read(client, MT9V032_CHIP_VERSION); 589 + if (data != MT9V032_CHIP_ID_REV1 && data != MT9V032_CHIP_ID_REV3) { 590 + dev_err(&client->dev, "MT9V032 not detected, wrong version " 591 + "0x%04x\n", data); 592 + return -ENODEV; 593 + } 594 + 595 + mt9v032_power_off(mt9v032); 596 + 597 + dev_info(&client->dev, "MT9V032 detected at address 0x%02x\n", 598 + client->addr); 599 + 600 + return ret; 601 + } 602 + 603 + static int mt9v032_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) 604 + { 605 + struct v4l2_mbus_framefmt *format; 606 + struct v4l2_rect *crop; 607 + 608 + crop = v4l2_subdev_get_try_crop(fh, 0); 609 + crop->left = MT9V032_COLUMN_START_DEF; 610 + crop->top = MT9V032_ROW_START_DEF; 611 + crop->width = MT9V032_WINDOW_WIDTH_DEF; 612 + crop->height = MT9V032_WINDOW_HEIGHT_DEF; 613 + 614 + format = v4l2_subdev_get_try_format(fh, 0); 615 + format->code = V4L2_MBUS_FMT_SGRBG10_1X10; 616 + format->width = MT9V032_WINDOW_WIDTH_DEF; 617 + format->height = MT9V032_WINDOW_HEIGHT_DEF; 618 + format->field = V4L2_FIELD_NONE; 619 + format->colorspace = V4L2_COLORSPACE_SRGB; 620 + 621 + return mt9v032_set_power(subdev, 1); 622 + } 623 + 624 + static int mt9v032_close(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) 625 + { 626 + return mt9v032_set_power(subdev, 0); 627 + } 628 + 629 + static struct v4l2_subdev_core_ops mt9v032_subdev_core_ops = { 630 + .s_power = mt9v032_set_power, 631 + }; 632 + 633 + static struct v4l2_subdev_video_ops mt9v032_subdev_video_ops = { 634 + .s_stream = mt9v032_s_stream, 635 + }; 636 + 637 + static struct v4l2_subdev_pad_ops mt9v032_subdev_pad_ops = { 638 + .enum_mbus_code = mt9v032_enum_mbus_code, 639 + .enum_frame_size = mt9v032_enum_frame_size, 640 + .get_fmt = mt9v032_get_format, 641 + .set_fmt = mt9v032_set_format, 642 + .get_crop = mt9v032_get_crop, 643 + .set_crop = mt9v032_set_crop, 644 + }; 645 + 646 + static struct v4l2_subdev_ops mt9v032_subdev_ops = { 647 + .core = &mt9v032_subdev_core_ops, 648 + .video = &mt9v032_subdev_video_ops, 649 + .pad = &mt9v032_subdev_pad_ops, 650 + }; 651 + 652 + static const struct v4l2_subdev_internal_ops mt9v032_subdev_internal_ops = { 653 + .registered = mt9v032_registered, 654 + .open = mt9v032_open, 655 + .close = mt9v032_close, 656 + }; 657 + 658 + /* ----------------------------------------------------------------------------- 659 + * Driver initialization and probing 660 + */ 661 + 662 + static int mt9v032_probe(struct i2c_client *client, 663 + const struct i2c_device_id *did) 664 + { 665 + struct mt9v032 *mt9v032; 666 + unsigned int i; 667 + int ret; 668 + 669 + if (!i2c_check_functionality(client->adapter, 670 + I2C_FUNC_SMBUS_WORD_DATA)) { 671 + dev_warn(&client->adapter->dev, 672 + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n"); 673 + return -EIO; 674 + } 675 + 676 + mt9v032 = kzalloc(sizeof(*mt9v032), GFP_KERNEL); 677 + if (!mt9v032) 678 + return -ENOMEM; 679 + 680 + mutex_init(&mt9v032->power_lock); 681 + mt9v032->pdata = client->dev.platform_data; 682 + 683 + v4l2_ctrl_handler_init(&mt9v032->ctrls, ARRAY_SIZE(mt9v032_ctrls) + 4); 684 + 685 + v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, 686 + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); 687 + v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, 688 + V4L2_CID_GAIN, MT9V032_ANALOG_GAIN_MIN, 689 + MT9V032_ANALOG_GAIN_MAX, 1, MT9V032_ANALOG_GAIN_DEF); 690 + v4l2_ctrl_new_std_menu(&mt9v032->ctrls, &mt9v032_ctrl_ops, 691 + V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0, 692 + V4L2_EXPOSURE_AUTO); 693 + v4l2_ctrl_new_std(&mt9v032->ctrls, &mt9v032_ctrl_ops, 694 + V4L2_CID_EXPOSURE, MT9V032_TOTAL_SHUTTER_WIDTH_MIN, 695 + MT9V032_TOTAL_SHUTTER_WIDTH_MAX, 1, 696 + MT9V032_TOTAL_SHUTTER_WIDTH_DEF); 697 + 698 + for (i = 0; i < ARRAY_SIZE(mt9v032_ctrls); ++i) 699 + v4l2_ctrl_new_custom(&mt9v032->ctrls, &mt9v032_ctrls[i], NULL); 700 + 701 + mt9v032->subdev.ctrl_handler = &mt9v032->ctrls; 702 + 703 + if (mt9v032->ctrls.error) 704 + printk(KERN_INFO "%s: control initialization error %d\n", 705 + __func__, mt9v032->ctrls.error); 706 + 707 + mt9v032->crop.left = MT9V032_COLUMN_START_DEF; 708 + mt9v032->crop.top = MT9V032_ROW_START_DEF; 709 + mt9v032->crop.width = MT9V032_WINDOW_WIDTH_DEF; 710 + mt9v032->crop.height = MT9V032_WINDOW_HEIGHT_DEF; 711 + 712 + mt9v032->format.code = V4L2_MBUS_FMT_SGRBG10_1X10; 713 + mt9v032->format.width = MT9V032_WINDOW_WIDTH_DEF; 714 + mt9v032->format.height = MT9V032_WINDOW_HEIGHT_DEF; 715 + mt9v032->format.field = V4L2_FIELD_NONE; 716 + mt9v032->format.colorspace = V4L2_COLORSPACE_SRGB; 717 + 718 + mt9v032->aec_agc = MT9V032_AEC_ENABLE | MT9V032_AGC_ENABLE; 719 + 720 + v4l2_i2c_subdev_init(&mt9v032->subdev, client, &mt9v032_subdev_ops); 721 + mt9v032->subdev.internal_ops = &mt9v032_subdev_internal_ops; 722 + mt9v032->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 723 + 724 + mt9v032->pad.flags = MEDIA_PAD_FL_SOURCE; 725 + ret = media_entity_init(&mt9v032->subdev.entity, 1, &mt9v032->pad, 0); 726 + if (ret < 0) 727 + kfree(mt9v032); 728 + 729 + return ret; 730 + } 731 + 732 + static int mt9v032_remove(struct i2c_client *client) 733 + { 734 + struct v4l2_subdev *subdev = i2c_get_clientdata(client); 735 + struct mt9v032 *mt9v032 = to_mt9v032(subdev); 736 + 737 + v4l2_device_unregister_subdev(subdev); 738 + media_entity_cleanup(&subdev->entity); 739 + kfree(mt9v032); 740 + return 0; 741 + } 742 + 743 + static const struct i2c_device_id mt9v032_id[] = { 744 + { "mt9v032", 0 }, 745 + { } 746 + }; 747 + MODULE_DEVICE_TABLE(i2c, mt9v032_id); 748 + 749 + static struct i2c_driver mt9v032_driver = { 750 + .driver = { 751 + .name = "mt9v032", 752 + }, 753 + .probe = mt9v032_probe, 754 + .remove = mt9v032_remove, 755 + .id_table = mt9v032_id, 756 + }; 757 + 758 + static int __init mt9v032_init(void) 759 + { 760 + return i2c_add_driver(&mt9v032_driver); 761 + } 762 + 763 + static void __exit mt9v032_exit(void) 764 + { 765 + i2c_del_driver(&mt9v032_driver); 766 + } 767 + 768 + module_init(mt9v032_init); 769 + module_exit(mt9v032_exit); 770 + 771 + MODULE_DESCRIPTION("Aptina MT9V032 Camera driver"); 772 + MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); 773 + MODULE_LICENSE("GPL");
+12
include/media/mt9v032.h
··· 1 + #ifndef _MEDIA_MT9V032_H 2 + #define _MEDIA_MT9V032_H 3 + 4 + struct v4l2_subdev; 5 + 6 + struct mt9v032_platform_data { 7 + unsigned int clk_pol:1; 8 + 9 + void (*set_clock)(struct v4l2_subdev *subdev, unsigned int rate); 10 + }; 11 + 12 + #endif