⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mt9v022.c

📁 trident tm5600的linux驱动
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * 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 + -