brintos

brintos / linux-shallow public Read only

0
0
Text · 14.6 KiB · 055b791 Raw
600 lines · c
1// SPDX-License-Identifier: GPL-2.02//3// mt9v011 -Micron 1/4-Inch VGA Digital Image Sensor4//5// Copyright (c) 2009 Mauro Carvalho Chehab <mchehab@kernel.org>6 7#include <linux/i2c.h>8#include <linux/slab.h>9#include <linux/videodev2.h>10#include <linux/delay.h>11#include <linux/module.h>12#include <asm/div64.h>13#include <media/v4l2-device.h>14#include <media/v4l2-ctrls.h>15#include <media/i2c/mt9v011.h>16 17MODULE_DESCRIPTION("Micron mt9v011 sensor driver");18MODULE_AUTHOR("Mauro Carvalho Chehab");19MODULE_LICENSE("GPL v2");20 21static int debug;22module_param(debug, int, 0);23MODULE_PARM_DESC(debug, "Debug level (0-2)");24 25#define R00_MT9V011_CHIP_VERSION	0x0026#define R01_MT9V011_ROWSTART		0x0127#define R02_MT9V011_COLSTART		0x0228#define R03_MT9V011_HEIGHT		0x0329#define R04_MT9V011_WIDTH		0x0430#define R05_MT9V011_HBLANK		0x0531#define R06_MT9V011_VBLANK		0x0632#define R07_MT9V011_OUT_CTRL		0x0733#define R09_MT9V011_SHUTTER_WIDTH	0x0934#define R0A_MT9V011_CLK_SPEED		0x0a35#define R0B_MT9V011_RESTART		0x0b36#define R0C_MT9V011_SHUTTER_DELAY	0x0c37#define R0D_MT9V011_RESET		0x0d38#define R1E_MT9V011_DIGITAL_ZOOM	0x1e39#define R20_MT9V011_READ_MODE		0x2040#define R2B_MT9V011_GREEN_1_GAIN	0x2b41#define R2C_MT9V011_BLUE_GAIN		0x2c42#define R2D_MT9V011_RED_GAIN		0x2d43#define R2E_MT9V011_GREEN_2_GAIN	0x2e44#define R35_MT9V011_GLOBAL_GAIN		0x3545#define RF1_MT9V011_CHIP_ENABLE		0xf146 47#define MT9V011_VERSION			0x823248#define MT9V011_REV_B_VERSION		0x824349 50struct mt9v011 {51	struct v4l2_subdev sd;52	struct media_pad pad;53	struct v4l2_ctrl_handler ctrls;54	unsigned width, height;55	unsigned xtal;56	unsigned hflip:1;57	unsigned vflip:1;58 59	u16 global_gain, exposure;60	s16 red_bal, blue_bal;61};62 63static inline struct mt9v011 *to_mt9v011(struct v4l2_subdev *sd)64{65	return container_of(sd, struct mt9v011, sd);66}67 68static int mt9v011_read(struct v4l2_subdev *sd, unsigned char addr)69{70	struct i2c_client *c = v4l2_get_subdevdata(sd);71	__be16 buffer;72	int rc, val;73 74	rc = i2c_master_send(c, &addr, 1);75	if (rc != 1)76		v4l2_dbg(0, debug, sd,77			 "i2c i/o error: rc == %d (should be 1)\n", rc);78 79	msleep(10);80 81	rc = i2c_master_recv(c, (char *)&buffer, 2);82	if (rc != 2)83		v4l2_dbg(0, debug, sd,84			 "i2c i/o error: rc == %d (should be 2)\n", rc);85 86	val = be16_to_cpu(buffer);87 88	v4l2_dbg(2, debug, sd, "mt9v011: read 0x%02x = 0x%04x\n", addr, val);89 90	return val;91}92 93static void mt9v011_write(struct v4l2_subdev *sd, unsigned char addr,94				 u16 value)95{96	struct i2c_client *c = v4l2_get_subdevdata(sd);97	unsigned char buffer[3];98	int rc;99 100	buffer[0] = addr;101	buffer[1] = value >> 8;102	buffer[2] = value & 0xff;103 104	v4l2_dbg(2, debug, sd,105		 "mt9v011: writing 0x%02x 0x%04x\n", buffer[0], value);106	rc = i2c_master_send(c, buffer, 3);107	if (rc != 3)108		v4l2_dbg(0, debug, sd,109			 "i2c i/o error: rc == %d (should be 3)\n", rc);110}111 112 113struct i2c_reg_value {114	unsigned char reg;115	u16           value;116};117 118/*119 * Values used at the original driver120 * Some values are marked as Reserved at the datasheet121 */122static const struct i2c_reg_value mt9v011_init_default[] = {123		{ R0D_MT9V011_RESET, 0x0001 },124		{ R0D_MT9V011_RESET, 0x0000 },125 126		{ R0C_MT9V011_SHUTTER_DELAY, 0x0000 },127		{ R09_MT9V011_SHUTTER_WIDTH, 0x1fc },128 129		{ R0A_MT9V011_CLK_SPEED, 0x0000 },130		{ R1E_MT9V011_DIGITAL_ZOOM,  0x0000 },131 132		{ R07_MT9V011_OUT_CTRL, 0x0002 },	/* chip enable */133};134 135 136static u16 calc_mt9v011_gain(s16 lineargain)137{138 139	u16 digitalgain = 0;140	u16 analogmult = 0;141	u16 analoginit = 0;142 143	if (lineargain < 0)144		lineargain = 0;145 146	/* recommended minimum */147	lineargain += 0x0020;148 149	if (lineargain > 2047)150		lineargain = 2047;151 152	if (lineargain > 1023) {153		digitalgain = 3;154		analogmult = 3;155		analoginit = lineargain / 16;156	} else if (lineargain > 511) {157		digitalgain = 1;158		analogmult = 3;159		analoginit = lineargain / 8;160	} else if (lineargain > 255) {161		analogmult = 3;162		analoginit = lineargain / 4;163	} else if (lineargain > 127) {164		analogmult = 1;165		analoginit = lineargain / 2;166	} else167		analoginit = lineargain;168 169	return analoginit + (analogmult << 7) + (digitalgain << 9);170 171}172 173static void set_balance(struct v4l2_subdev *sd)174{175	struct mt9v011 *core = to_mt9v011(sd);176	u16 green_gain, blue_gain, red_gain;177	u16 exposure;178	s16 bal;179 180	exposure = core->exposure;181 182	green_gain = calc_mt9v011_gain(core->global_gain);183 184	bal = core->global_gain;185	bal += (core->blue_bal * core->global_gain / (1 << 7));186	blue_gain = calc_mt9v011_gain(bal);187 188	bal = core->global_gain;189	bal += (core->red_bal * core->global_gain / (1 << 7));190	red_gain = calc_mt9v011_gain(bal);191 192	mt9v011_write(sd, R2B_MT9V011_GREEN_1_GAIN, green_gain);193	mt9v011_write(sd, R2E_MT9V011_GREEN_2_GAIN, green_gain);194	mt9v011_write(sd, R2C_MT9V011_BLUE_GAIN, blue_gain);195	mt9v011_write(sd, R2D_MT9V011_RED_GAIN, red_gain);196	mt9v011_write(sd, R09_MT9V011_SHUTTER_WIDTH, exposure);197}198 199static void calc_fps(struct v4l2_subdev *sd, u32 *numerator, u32 *denominator)200{201	struct mt9v011 *core = to_mt9v011(sd);202	unsigned height, width, hblank, vblank, speed;203	unsigned row_time, t_time;204	u64 frames_per_ms;205	unsigned tmp;206 207	height = mt9v011_read(sd, R03_MT9V011_HEIGHT);208	width = mt9v011_read(sd, R04_MT9V011_WIDTH);209	hblank = mt9v011_read(sd, R05_MT9V011_HBLANK);210	vblank = mt9v011_read(sd, R06_MT9V011_VBLANK);211	speed = mt9v011_read(sd, R0A_MT9V011_CLK_SPEED);212 213	row_time = (width + 113 + hblank) * (speed + 2);214	t_time = row_time * (height + vblank + 1);215 216	frames_per_ms = core->xtal * 1000l;217	do_div(frames_per_ms, t_time);218	tmp = frames_per_ms;219 220	v4l2_dbg(1, debug, sd, "Programmed to %u.%03u fps (%d pixel clcks)\n",221		tmp / 1000, tmp % 1000, t_time);222 223	if (numerator && denominator) {224		*numerator = 1000;225		*denominator = (u32)frames_per_ms;226	}227}228 229static u16 calc_speed(struct v4l2_subdev *sd, u32 numerator, u32 denominator)230{231	struct mt9v011 *core = to_mt9v011(sd);232	unsigned height, width, hblank, vblank;233	unsigned row_time, line_time;234	u64 t_time, speed;235 236	/* Avoid bogus calculus */237	if (!numerator || !denominator)238		return 0;239 240	height = mt9v011_read(sd, R03_MT9V011_HEIGHT);241	width = mt9v011_read(sd, R04_MT9V011_WIDTH);242	hblank = mt9v011_read(sd, R05_MT9V011_HBLANK);243	vblank = mt9v011_read(sd, R06_MT9V011_VBLANK);244 245	row_time = width + 113 + hblank;246	line_time = height + vblank + 1;247 248	t_time = core->xtal * ((u64)numerator);249	/* round to the closest value */250	t_time += denominator / 2;251	do_div(t_time, denominator);252 253	speed = t_time;254	do_div(speed, row_time * line_time);255 256	/* Avoid having a negative value for speed */257	if (speed < 2)258		speed = 0;259	else260		speed -= 2;261 262	/* Avoid speed overflow */263	if (speed > 15)264		return 15;265 266	return (u16)speed;267}268 269static void set_res(struct v4l2_subdev *sd)270{271	struct mt9v011 *core = to_mt9v011(sd);272	unsigned vstart, hstart;273 274	/*275	 * The mt9v011 doesn't have scaling. So, in order to select the desired276	 * resolution, we're cropping at the middle of the sensor.277	 * hblank and vblank should be adjusted, in order to warrant that278	 * we'll preserve the line timings for 30 fps, no matter what resolution279	 * is selected.280	 * NOTE: datasheet says that width (and height) should be filled with281	 * width-1. However, this doesn't work, since one pixel per line will282	 * be missing.283	 */284 285	hstart = 20 + (640 - core->width) / 2;286	mt9v011_write(sd, R02_MT9V011_COLSTART, hstart);287	mt9v011_write(sd, R04_MT9V011_WIDTH, core->width);288	mt9v011_write(sd, R05_MT9V011_HBLANK, 771 - core->width);289 290	vstart = 8 + (480 - core->height) / 2;291	mt9v011_write(sd, R01_MT9V011_ROWSTART, vstart);292	mt9v011_write(sd, R03_MT9V011_HEIGHT, core->height);293	mt9v011_write(sd, R06_MT9V011_VBLANK, 508 - core->height);294 295	calc_fps(sd, NULL, NULL);296};297 298static void set_read_mode(struct v4l2_subdev *sd)299{300	struct mt9v011 *core = to_mt9v011(sd);301	unsigned mode = 0x1000;302 303	if (core->hflip)304		mode |= 0x4000;305 306	if (core->vflip)307		mode |= 0x8000;308 309	mt9v011_write(sd, R20_MT9V011_READ_MODE, mode);310}311 312static int mt9v011_reset(struct v4l2_subdev *sd, u32 val)313{314	int i;315 316	for (i = 0; i < ARRAY_SIZE(mt9v011_init_default); i++)317		mt9v011_write(sd, mt9v011_init_default[i].reg,318			       mt9v011_init_default[i].value);319 320	set_balance(sd);321	set_res(sd);322	set_read_mode(sd);323 324	return 0;325}326 327static int mt9v011_enum_mbus_code(struct v4l2_subdev *sd,328		struct v4l2_subdev_state *sd_state,329		struct v4l2_subdev_mbus_code_enum *code)330{331	if (code->pad || code->index > 0)332		return -EINVAL;333 334	code->code = MEDIA_BUS_FMT_SGRBG8_1X8;335	return 0;336}337 338static int mt9v011_set_fmt(struct v4l2_subdev *sd,339		struct v4l2_subdev_state *sd_state,340		struct v4l2_subdev_format *format)341{342	struct v4l2_mbus_framefmt *fmt = &format->format;343	struct mt9v011 *core = to_mt9v011(sd);344 345	if (format->pad || fmt->code != MEDIA_BUS_FMT_SGRBG8_1X8)346		return -EINVAL;347 348	v4l_bound_align_image(&fmt->width, 48, 639, 1,349			      &fmt->height, 32, 480, 1, 0);350	fmt->field = V4L2_FIELD_NONE;351	fmt->colorspace = V4L2_COLORSPACE_SRGB;352 353	if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {354		core->width = fmt->width;355		core->height = fmt->height;356 357		set_res(sd);358	} else {359		*v4l2_subdev_state_get_format(sd_state, 0) = *fmt;360	}361 362	return 0;363}364 365static int mt9v011_get_frame_interval(struct v4l2_subdev *sd,366				      struct v4l2_subdev_state *sd_state,367				      struct v4l2_subdev_frame_interval *ival)368{369	/*370	 * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2371	 * subdev active state API.372	 */373	if (ival->which != V4L2_SUBDEV_FORMAT_ACTIVE)374		return -EINVAL;375 376	calc_fps(sd,377		 &ival->interval.numerator,378		 &ival->interval.denominator);379 380	return 0;381}382 383static int mt9v011_set_frame_interval(struct v4l2_subdev *sd,384				      struct v4l2_subdev_state *sd_state,385				      struct v4l2_subdev_frame_interval *ival)386{387	struct v4l2_fract *tpf = &ival->interval;388	u16 speed;389 390	/*391	 * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2392	 * subdev active state API.393	 */394	if (ival->which != V4L2_SUBDEV_FORMAT_ACTIVE)395		return -EINVAL;396 397	speed = calc_speed(sd, tpf->numerator, tpf->denominator);398 399	mt9v011_write(sd, R0A_MT9V011_CLK_SPEED, speed);400	v4l2_dbg(1, debug, sd, "Setting speed to %d\n", speed);401 402	/* Recalculate and update fps info */403	calc_fps(sd, &tpf->numerator, &tpf->denominator);404 405	return 0;406}407 408#ifdef CONFIG_VIDEO_ADV_DEBUG409static int mt9v011_g_register(struct v4l2_subdev *sd,410			      struct v4l2_dbg_register *reg)411{412	reg->val = mt9v011_read(sd, reg->reg & 0xff);413	reg->size = 2;414 415	return 0;416}417 418static int mt9v011_s_register(struct v4l2_subdev *sd,419			      const struct v4l2_dbg_register *reg)420{421	mt9v011_write(sd, reg->reg & 0xff, reg->val & 0xffff);422 423	return 0;424}425#endif426 427static int mt9v011_s_ctrl(struct v4l2_ctrl *ctrl)428{429	struct mt9v011 *core =430		container_of(ctrl->handler, struct mt9v011, ctrls);431	struct v4l2_subdev *sd = &core->sd;432 433	switch (ctrl->id) {434	case V4L2_CID_GAIN:435		core->global_gain = ctrl->val;436		break;437	case V4L2_CID_EXPOSURE:438		core->exposure = ctrl->val;439		break;440	case V4L2_CID_RED_BALANCE:441		core->red_bal = ctrl->val;442		break;443	case V4L2_CID_BLUE_BALANCE:444		core->blue_bal = ctrl->val;445		break;446	case V4L2_CID_HFLIP:447		core->hflip = ctrl->val;448		set_read_mode(sd);449		return 0;450	case V4L2_CID_VFLIP:451		core->vflip = ctrl->val;452		set_read_mode(sd);453		return 0;454	default:455		return -EINVAL;456	}457 458	set_balance(sd);459	return 0;460}461 462static const struct v4l2_ctrl_ops mt9v011_ctrl_ops = {463	.s_ctrl = mt9v011_s_ctrl,464};465 466static const struct v4l2_subdev_core_ops mt9v011_core_ops = {467	.reset = mt9v011_reset,468#ifdef CONFIG_VIDEO_ADV_DEBUG469	.g_register = mt9v011_g_register,470	.s_register = mt9v011_s_register,471#endif472};473 474static const struct v4l2_subdev_pad_ops mt9v011_pad_ops = {475	.enum_mbus_code = mt9v011_enum_mbus_code,476	.set_fmt = mt9v011_set_fmt,477	.get_frame_interval = mt9v011_get_frame_interval,478	.set_frame_interval = mt9v011_set_frame_interval,479};480 481static const struct v4l2_subdev_ops mt9v011_ops = {482	.core  = &mt9v011_core_ops,483	.pad   = &mt9v011_pad_ops,484};485 486 487/****************************************************************************488			I2C Client & Driver489 ****************************************************************************/490 491static int mt9v011_probe(struct i2c_client *c)492{493	u16 version;494	struct mt9v011 *core;495	struct v4l2_subdev *sd;496	int ret;497 498	/* Check if the adapter supports the needed features */499	if (!i2c_check_functionality(c->adapter,500	     I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))501		return -EIO;502 503	core = devm_kzalloc(&c->dev, sizeof(struct mt9v011), GFP_KERNEL);504	if (!core)505		return -ENOMEM;506 507	sd = &core->sd;508	v4l2_i2c_subdev_init(sd, c, &mt9v011_ops);509 510	core->pad.flags = MEDIA_PAD_FL_SOURCE;511	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;512 513	ret = media_entity_pads_init(&sd->entity, 1, &core->pad);514	if (ret < 0)515		return ret;516 517	/* Check if the sensor is really a MT9V011 */518	version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION);519	if ((version != MT9V011_VERSION) &&520	    (version != MT9V011_REV_B_VERSION)) {521		v4l2_info(sd, "*** unknown micron chip detected (0x%04x).\n",522			  version);523		return -EINVAL;524	}525 526	v4l2_ctrl_handler_init(&core->ctrls, 5);527	v4l2_ctrl_new_std(&core->ctrls, &mt9v011_ctrl_ops,528			  V4L2_CID_GAIN, 0, (1 << 12) - 1 - 0x20, 1, 0x20);529	v4l2_ctrl_new_std(&core->ctrls, &mt9v011_ctrl_ops,530			  V4L2_CID_EXPOSURE, 0, 2047, 1, 0x01fc);531	v4l2_ctrl_new_std(&core->ctrls, &mt9v011_ctrl_ops,532			  V4L2_CID_RED_BALANCE, -(1 << 9), (1 << 9) - 1, 1, 0);533	v4l2_ctrl_new_std(&core->ctrls, &mt9v011_ctrl_ops,534			  V4L2_CID_BLUE_BALANCE, -(1 << 9), (1 << 9) - 1, 1, 0);535	v4l2_ctrl_new_std(&core->ctrls, &mt9v011_ctrl_ops,536			  V4L2_CID_HFLIP, 0, 1, 1, 0);537	v4l2_ctrl_new_std(&core->ctrls, &mt9v011_ctrl_ops,538			  V4L2_CID_VFLIP, 0, 1, 1, 0);539 540	if (core->ctrls.error) {541		int ret = core->ctrls.error;542 543		v4l2_err(sd, "control initialization error %d\n", ret);544		v4l2_ctrl_handler_free(&core->ctrls);545		return ret;546	}547	core->sd.ctrl_handler = &core->ctrls;548 549	core->global_gain = 0x0024;550	core->exposure = 0x01fc;551	core->width  = 640;552	core->height = 480;553	core->xtal = 27000000;	/* Hz */554 555	if (c->dev.platform_data) {556		struct mt9v011_platform_data *pdata = c->dev.platform_data;557 558		core->xtal = pdata->xtal;559		v4l2_dbg(1, debug, sd, "xtal set to %d.%03d MHz\n",560			core->xtal / 1000000, (core->xtal / 1000) % 1000);561	}562 563	v4l_info(c, "chip found @ 0x%02x (%s - chip version 0x%04x)\n",564		 c->addr << 1, c->adapter->name, version);565 566	return 0;567}568 569static void mt9v011_remove(struct i2c_client *c)570{571	struct v4l2_subdev *sd = i2c_get_clientdata(c);572	struct mt9v011 *core = to_mt9v011(sd);573 574	v4l2_dbg(1, debug, sd,575		"mt9v011.c: removing mt9v011 adapter on address 0x%x\n",576		c->addr << 1);577 578	v4l2_device_unregister_subdev(sd);579	v4l2_ctrl_handler_free(&core->ctrls);580}581 582/* ----------------------------------------------------------------------- */583 584static const struct i2c_device_id mt9v011_id[] = {585	{ "mt9v011" },586	{ }587};588MODULE_DEVICE_TABLE(i2c, mt9v011_id);589 590static struct i2c_driver mt9v011_driver = {591	.driver = {592		.name	= "mt9v011",593	},594	.probe		= mt9v011_probe,595	.remove		= mt9v011_remove,596	.id_table	= mt9v011_id,597};598 599module_i2c_driver(mt9v011_driver);600