📄 mt9v022.c
字号:
/* * Driver for MT9V022 CMOS Image Sensor from Micron * * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */#include <linux/videodev2.h>#include <linux/slab.h>#include <linux/i2c.h>#include <linux/delay.h>#include <linux/log2.h>#include <linux/gpio.h>#include <media/v4l2-common.h>#include <media/v4l2-chip-ident.h>#include <media/soc_camera.h>/* mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c * The platform has to define i2c_board_info * and call i2c_register_board_info() */static char *sensor_type;module_param(sensor_type, charp, S_IRUGO);MODULE_PARM_DESC(sensor_type, "Sensor type: \"colour\" or \"monochrome\"");/* mt9v022 selected register addresses */#define MT9V022_CHIP_VERSION 0x00#define MT9V022_COLUMN_START 0x01#define MT9V022_ROW_START 0x02#define MT9V022_WINDOW_HEIGHT 0x03#define MT9V022_WINDOW_WIDTH 0x04#define MT9V022_HORIZONTAL_BLANKING 0x05#define MT9V022_VERTICAL_BLANKING 0x06#define MT9V022_CHIP_CONTROL 0x07#define MT9V022_SHUTTER_WIDTH1 0x08#define MT9V022_SHUTTER_WIDTH2 0x09#define MT9V022_SHUTTER_WIDTH_CTRL 0x0a#define MT9V022_TOTAL_SHUTTER_WIDTH 0x0b#define MT9V022_RESET 0x0c#define MT9V022_READ_MODE 0x0d#define MT9V022_MONITOR_MODE 0x0e#define MT9V022_PIXEL_OPERATION_MODE 0x0f#define MT9V022_LED_OUT_CONTROL 0x1b#define MT9V022_ADC_MODE_CONTROL 0x1c#define MT9V022_ANALOG_GAIN 0x34#define MT9V022_BLACK_LEVEL_CALIB_CTRL 0x47#define MT9V022_PIXCLK_FV_LV 0x74#define MT9V022_DIGITAL_TEST_PATTERN 0x7f#define MT9V022_AEC_AGC_ENABLE 0xAF#define MT9V022_MAX_TOTAL_SHUTTER_WIDTH 0xBD/* Progressive scan, master, defaults */#define MT9V022_CHIP_CONTROL_DEFAULT 0x188static const struct soc_camera_data_format mt9v022_colour_formats[] = { /* Order important: first natively supported, * second supported with a GPIO extender */ { .name = "Bayer (sRGB) 10 bit", .depth = 10, .fourcc = V4L2_PIX_FMT_SBGGR16, .colorspace = V4L2_COLORSPACE_SRGB, }, { .name = "Bayer (sRGB) 8 bit", .depth = 8, .fourcc = V4L2_PIX_FMT_SBGGR8, .colorspace = V4L2_COLORSPACE_SRGB, }};static const struct soc_camera_data_format mt9v022_monochrome_formats[] = { /* Order important - see above */ { .name = "Monochrome 10 bit", .depth = 10, .fourcc = V4L2_PIX_FMT_Y16, }, { .name = "Monochrome 8 bit", .depth = 8, .fourcc = V4L2_PIX_FMT_GREY, },};struct mt9v022 { struct i2c_client *client; struct soc_camera_device icd; int model; /* V4L2_IDENT_MT9V022* codes from v4l2-chip-ident.h */ int switch_gpio; u16 chip_control; unsigned char datawidth;};static int reg_read(struct soc_camera_device *icd, const u8 reg){ struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); struct i2c_client *client = mt9v022->client; s32 data = i2c_smbus_read_word_data(client, reg); return data < 0 ? data : swab16(data);}static int reg_write(struct soc_camera_device *icd, const u8 reg, const u16 data){ struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); return i2c_smbus_write_word_data(mt9v022->client, reg, swab16(data));}static int reg_set(struct soc_camera_device *icd, const u8 reg, const u16 data){ int ret; ret = reg_read(icd, reg); if (ret < 0) return ret; return reg_write(icd, reg, ret | data);}static int reg_clear(struct soc_camera_device *icd, const u8 reg, const u16 data){ int ret; ret = reg_read(icd, reg); if (ret < 0) return ret; return reg_write(icd, reg, ret & ~data);}static int mt9v022_init(struct soc_camera_device *icd){ struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); struct soc_camera_link *icl = mt9v022->client->dev.platform_data; int ret; if (icl->power) { ret = icl->power(&mt9v022->client->dev, 1); if (ret < 0) { dev_err(icd->vdev->parent, "Platform failed to power-on the camera.\n"); return ret; } } /* * The camera could have been already on, we hard-reset it additionally, * if available. Soft reset is done in video_probe(). */ if (icl->reset) icl->reset(&mt9v022->client->dev); /* Almost the default mode: master, parallel, simultaneous, and an * undocumented bit 0x200, which is present in table 7, but not in 8, * plus snapshot mode to disable scan for now */ mt9v022->chip_control |= 0x10; ret = reg_write(icd, MT9V022_CHIP_CONTROL, mt9v022->chip_control); if (!ret) ret = reg_write(icd, MT9V022_READ_MODE, 0x300); /* All defaults */ if (!ret) /* AEC, AGC on */ ret = reg_set(icd, MT9V022_AEC_AGC_ENABLE, 0x3); if (!ret) ret = reg_write(icd, MT9V022_MAX_TOTAL_SHUTTER_WIDTH, 480); if (!ret) /* default - auto */ ret = reg_clear(icd, MT9V022_BLACK_LEVEL_CALIB_CTRL, 1); if (!ret) ret = reg_write(icd, MT9V022_DIGITAL_TEST_PATTERN, 0); return ret;}static int mt9v022_release(struct soc_camera_device *icd){ struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); struct soc_camera_link *icl = mt9v022->client->dev.platform_data; if (icl->power) icl->power(&mt9v022->client->dev, 0); return 0;}static int mt9v022_start_capture(struct soc_camera_device *icd){ struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); /* Switch to master "normal" mode */ mt9v022->chip_control &= ~0x10; if (reg_write(icd, MT9V022_CHIP_CONTROL, mt9v022->chip_control) < 0) return -EIO; return 0;}static int mt9v022_stop_capture(struct soc_camera_device *icd){ struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); /* Switch to snapshot mode */ mt9v022->chip_control |= 0x10; if (reg_write(icd, MT9V022_CHIP_CONTROL, mt9v022->chip_control) < 0) return -EIO; return 0;}static int bus_switch_request(struct mt9v022 *mt9v022, struct soc_camera_link *icl){#ifdef CONFIG_MT9V022_PCA9536_SWITCH int ret; unsigned int gpio = icl->gpio; if (gpio_is_valid(gpio)) { /* We have a data bus switch. */ ret = gpio_request(gpio, "mt9v022"); if (ret < 0) { dev_err(&mt9v022->client->dev, "Cannot get GPIO %u\n", gpio); return ret; } ret = gpio_direction_output(gpio, 0); if (ret < 0) { dev_err(&mt9v022->client->dev, "Cannot set GPIO %u to output\n", gpio); gpio_free(gpio); return ret; } } mt9v022->switch_gpio = gpio;#else mt9v022->switch_gpio = -EINVAL;#endif return 0;}static void bus_switch_release(struct mt9v022 *mt9v022){#ifdef CONFIG_MT9V022_PCA9536_SWITCH if (gpio_is_valid(mt9v022->switch_gpio)) gpio_free(mt9v022->switch_gpio);#endif}static int bus_switch_act(struct mt9v022 *mt9v022, int go8bit){#ifdef CONFIG_MT9V022_PCA9536_SWITCH if (!gpio_is_valid(mt9v022->switch_gpio)) return -ENODEV; gpio_set_value_cansleep(mt9v022->switch_gpio, go8bit); return 0;#else return -ENODEV;#endif}static int bus_switch_possible(struct mt9v022 *mt9v022){#ifdef CONFIG_MT9V022_PCA9536_SWITCH return gpio_is_valid(mt9v022->switch_gpio);#else return 0;#endif}static int mt9v022_set_bus_param(struct soc_camera_device *icd, unsigned long flags){ struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); unsigned int width_flag = flags & SOCAM_DATAWIDTH_MASK; int ret; u16 pixclk = 0; /* Only one width bit may be set */ if (!is_power_of_2(width_flag)) return -EINVAL; if ((mt9v022->datawidth != 10 && (width_flag == SOCAM_DATAWIDTH_10)) || (mt9v022->datawidth != 9 && (width_flag == SOCAM_DATAWIDTH_9)) || (mt9v022->datawidth != 8 && (width_flag == SOCAM_DATAWIDTH_8))) { /* Well, we actually only can do 10 or 8 bits... */ if (width_flag == SOCAM_DATAWIDTH_9) return -EINVAL; ret = bus_switch_act(mt9v022, width_flag == SOCAM_DATAWIDTH_8); if (ret < 0) return ret; mt9v022->datawidth = width_flag == SOCAM_DATAWIDTH_8 ? 8 : 10; } if (flags & SOCAM_PCLK_SAMPLE_RISING) pixclk |= 0x10; if (!(flags & SOCAM_HSYNC_ACTIVE_HIGH)) pixclk |= 0x1; if (!(flags & SOCAM_VSYNC_ACTIVE_HIGH)) pixclk |= 0x2; ret = reg_write(icd, MT9V022_PIXCLK_FV_LV, pixclk); if (ret < 0) return ret; if (!(flags & SOCAM_MASTER)) mt9v022->chip_control &= ~0x8; ret = reg_write(icd, MT9V022_CHIP_CONTROL, mt9v022->chip_control); if (ret < 0) return ret; dev_dbg(&icd->dev, "Calculated pixclk 0x%x, chip control 0x%x\n", pixclk, mt9v022->chip_control); return 0;}static unsigned long mt9v022_query_bus_param(struct soc_camera_device *icd){ struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); unsigned int width_flag = SOCAM_DATAWIDTH_10; if (bus_switch_possible(mt9v022)) width_flag |= SOCAM_DATAWIDTH_8; return SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW | SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW | SOCAM_MASTER | SOCAM_SLAVE | width_flag;}static int mt9v022_set_fmt_cap(struct soc_camera_device *icd, __u32 pixfmt, struct v4l2_rect *rect){ struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); int ret; /* The caller provides a supported format, as verified per call to * icd->try_fmt_cap(), datawidth is from our supported format list */ switch (pixfmt) { case V4L2_PIX_FMT_GREY: case V4L2_PIX_FMT_Y16: if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATM) return -EINVAL; break; case V4L2_PIX_FMT_SBGGR8: case V4L2_PIX_FMT_SBGGR16: if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATC) return -EINVAL; break; case 0: /* No format change, only geometry */ break; default: return -EINVAL; } /* Like in example app. Contradicts the datasheet though */ ret = reg_read(icd, MT9V022_AEC_AGC_ENABLE); if (ret >= 0) { if (ret & 1) /* Autoexposure */ ret = reg_write(icd, MT9V022_MAX_TOTAL_SHUTTER_WIDTH, rect->height + icd->y_skip_top + 43); else ret = reg_write(icd, MT9V022_TOTAL_SHUTTER_WIDTH, rect->height + icd->y_skip_top + 43); } /* Setup frame format: defaults apart from width and height */ if (!ret) ret = reg_write(icd, MT9V022_COLUMN_START, rect->left); if (!ret) ret = reg_write(icd, MT9V022_ROW_START, rect->top); if (!ret) /* Default 94, Phytec driver says: * "width + horizontal blank >= 660" */ ret = reg_write(icd, MT9V022_HORIZONTAL_BLANKING, rect->width > 660 - 43 ? 43 : 660 - rect->width); if (!ret) ret = reg_write(icd, MT9V022_VERTICAL_BLANKING, 45); if (!ret) ret = reg_write(icd, MT9V022_WINDOW_WIDTH, rect->width); if (!ret) ret = reg_write(icd, MT9V022_WINDOW_HEIGHT, rect->height + icd->y_skip_top); if (ret < 0) return ret; dev_dbg(&icd->dev, "Frame %ux%u pixel\n", rect->width, rect->height); return 0;}static int mt9v022_try_fmt_cap(struct soc_camera_device *icd, struct v4l2_format *f){ if (f->fmt.pix.height < 32 + icd->y_skip_top) f->fmt.pix.height = 32 + icd->y_skip_top; if (f->fmt.pix.height > 480 + icd->y_skip_top) f->fmt.pix.height = 480 + icd->y_skip_top; if (f->fmt.pix.width < 48) f->fmt.pix.width = 48; if (f->fmt.pix.width > 752) f->fmt.pix.width = 752; f->fmt.pix.width &= ~0x03; /* ? */ return 0;}static int mt9v022_get_chip_id(struct soc_camera_device *icd, struct v4l2_chip_ident *id){ struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); if (id->match_type != V4L2_CHIP_MATCH_I2C_ADDR) return -EINVAL; if (id->match_chip != mt9v022->client->addr) return -ENODEV; id->ident = mt9v022->model; id->revision = 0; return 0;}#ifdef CONFIG_VIDEO_ADV_DEBUGstatic int mt9v022_get_register(struct soc_camera_device *icd, struct v4l2_register *reg)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -