📄 mt9m001.c
字号:
/* * Driver for MT9M001 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/log2.h>#include <linux/gpio.h>#include <media/v4l2-common.h>#include <media/v4l2-chip-ident.h>#include <media/soc_camera.h>/* mt9m001 i2c address 0x5d * The platform has to define i2c_board_info * and call i2c_register_board_info() *//* mt9m001 selected register addresses */#define MT9M001_CHIP_VERSION 0x00#define MT9M001_ROW_START 0x01#define MT9M001_COLUMN_START 0x02#define MT9M001_WINDOW_HEIGHT 0x03#define MT9M001_WINDOW_WIDTH 0x04#define MT9M001_HORIZONTAL_BLANKING 0x05#define MT9M001_VERTICAL_BLANKING 0x06#define MT9M001_OUTPUT_CONTROL 0x07#define MT9M001_SHUTTER_WIDTH 0x09#define MT9M001_FRAME_RESTART 0x0b#define MT9M001_SHUTTER_DELAY 0x0c#define MT9M001_RESET 0x0d#define MT9M001_READ_OPTIONS1 0x1e#define MT9M001_READ_OPTIONS2 0x20#define MT9M001_GLOBAL_GAIN 0x35#define MT9M001_CHIP_ENABLE 0xF1static const struct soc_camera_data_format mt9m001_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 mt9m001_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 mt9m001 { struct i2c_client *client; struct soc_camera_device icd; int model; /* V4L2_IDENT_MT9M001* codes from v4l2-chip-ident.h */ int switch_gpio; unsigned char autoexposure; unsigned char datawidth;};static int reg_read(struct soc_camera_device *icd, const u8 reg){ struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); struct i2c_client *client = mt9m001->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 mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); return i2c_smbus_write_word_data(mt9m001->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 mt9m001_init(struct soc_camera_device *icd){ struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); struct soc_camera_link *icl = mt9m001->client->dev.platform_data; int ret; dev_dbg(icd->vdev->parent, "%s\n", __func__); if (icl->power) { ret = icl->power(&mt9m001->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 reset it additionally */ if (icl->reset) ret = icl->reset(&mt9m001->client->dev); else ret = -ENODEV; if (ret < 0) { /* Either no platform reset, or platform reset failed */ ret = reg_write(icd, MT9M001_RESET, 1); if (!ret) ret = reg_write(icd, MT9M001_RESET, 0); } /* Disable chip, synchronous option update */ if (!ret) ret = reg_write(icd, MT9M001_OUTPUT_CONTROL, 0); return ret;}static int mt9m001_release(struct soc_camera_device *icd){ struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); struct soc_camera_link *icl = mt9m001->client->dev.platform_data; /* Disable the chip */ reg_write(icd, MT9M001_OUTPUT_CONTROL, 0); if (icl->power) icl->power(&mt9m001->client->dev, 0); return 0;}static int mt9m001_start_capture(struct soc_camera_device *icd){ /* Switch to master "normal" mode */ if (reg_write(icd, MT9M001_OUTPUT_CONTROL, 2) < 0) return -EIO; return 0;}static int mt9m001_stop_capture(struct soc_camera_device *icd){ /* Stop sensor readout */ if (reg_write(icd, MT9M001_OUTPUT_CONTROL, 0) < 0) return -EIO; return 0;}static int bus_switch_request(struct mt9m001 *mt9m001, struct soc_camera_link *icl){#ifdef CONFIG_MT9M001_PCA9536_SWITCH int ret; unsigned int gpio = icl->gpio; if (gpio_is_valid(gpio)) { /* We have a data bus switch. */ ret = gpio_request(gpio, "mt9m001"); if (ret < 0) { dev_err(&mt9m001->client->dev, "Cannot get GPIO %u\n", gpio); return ret; } ret = gpio_direction_output(gpio, 0); if (ret < 0) { dev_err(&mt9m001->client->dev, "Cannot set GPIO %u to output\n", gpio); gpio_free(gpio); return ret; } } mt9m001->switch_gpio = gpio;#else mt9m001->switch_gpio = -EINVAL;#endif return 0;}static void bus_switch_release(struct mt9m001 *mt9m001){#ifdef CONFIG_MT9M001_PCA9536_SWITCH if (gpio_is_valid(mt9m001->switch_gpio)) gpio_free(mt9m001->switch_gpio);#endif}static int bus_switch_act(struct mt9m001 *mt9m001, int go8bit){#ifdef CONFIG_MT9M001_PCA9536_SWITCH if (!gpio_is_valid(mt9m001->switch_gpio)) return -ENODEV; gpio_set_value_cansleep(mt9m001->switch_gpio, go8bit); return 0;#else return -ENODEV;#endif}static int bus_switch_possible(struct mt9m001 *mt9m001){#ifdef CONFIG_MT9M001_PCA9536_SWITCH return gpio_is_valid(mt9m001->switch_gpio);#else return 0;#endif}static int mt9m001_set_bus_param(struct soc_camera_device *icd, unsigned long flags){ struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); unsigned int width_flag = flags & SOCAM_DATAWIDTH_MASK; int ret; /* Flags validity verified in test_bus_param */ if ((mt9m001->datawidth != 10 && (width_flag == SOCAM_DATAWIDTH_10)) || (mt9m001->datawidth != 9 && (width_flag == SOCAM_DATAWIDTH_9)) || (mt9m001->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(mt9m001, width_flag == SOCAM_DATAWIDTH_8); if (ret < 0) return ret; mt9m001->datawidth = width_flag == SOCAM_DATAWIDTH_8 ? 8 : 10; } return 0;}static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd){ struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); unsigned int width_flag = SOCAM_DATAWIDTH_10; if (bus_switch_possible(mt9m001)) width_flag |= SOCAM_DATAWIDTH_8; /* MT9M001 has all capture_format parameters fixed */ return SOCAM_PCLK_SAMPLE_RISING | SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_MASTER | width_flag;}static int mt9m001_set_fmt_cap(struct soc_camera_device *icd, __u32 pixfmt, struct v4l2_rect *rect){ struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); int ret; const u16 hblank = 9, vblank = 25; /* Blanking and start values - default... */ ret = reg_write(icd, MT9M001_HORIZONTAL_BLANKING, hblank); if (!ret) ret = reg_write(icd, MT9M001_VERTICAL_BLANKING, vblank); /* The caller provides a supported format, as verified per * call to icd->try_fmt_cap() */ if (!ret) ret = reg_write(icd, MT9M001_COLUMN_START, rect->left); if (!ret) ret = reg_write(icd, MT9M001_ROW_START, rect->top); if (!ret) ret = reg_write(icd, MT9M001_WINDOW_WIDTH, rect->width - 1); if (!ret) ret = reg_write(icd, MT9M001_WINDOW_HEIGHT, rect->height + icd->y_skip_top - 1); if (!ret && mt9m001->autoexposure) { ret = reg_write(icd, MT9M001_SHUTTER_WIDTH, rect->height + icd->y_skip_top + vblank); if (!ret) { const struct v4l2_queryctrl *qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE); icd->exposure = (524 + (rect->height + icd->y_skip_top + vblank - 1) * (qctrl->maximum - qctrl->minimum)) / 1048 + qctrl->minimum; } } return ret;}static int mt9m001_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 > 1024 + icd->y_skip_top) f->fmt.pix.height = 1024 + icd->y_skip_top; if (f->fmt.pix.width < 48) f->fmt.pix.width = 48; if (f->fmt.pix.width > 1280) f->fmt.pix.width = 1280; f->fmt.pix.width &= ~0x01; /* has to be even, unsure why was ~3 */ return 0;}static int mt9m001_get_chip_id(struct soc_camera_device *icd, struct v4l2_chip_ident *id){ struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); if (id->match_type != V4L2_CHIP_MATCH_I2C_ADDR) return -EINVAL; if (id->match_chip != mt9m001->client->addr) return -ENODEV; id->ident = mt9m001->model; id->revision = 0; return 0;}#ifdef CONFIG_VIDEO_ADV_DEBUGstatic int mt9m001_get_register(struct soc_camera_device *icd, struct v4l2_register *reg){ struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); if (reg->match_type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) return -EINVAL; if (reg->match_chip != mt9m001->client->addr) return -ENODEV; reg->val = reg_read(icd, reg->reg); if (reg->val > 0xffff) return -EIO; return 0;}static int mt9m001_set_register(struct soc_camera_device *icd, struct v4l2_register *reg)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -