aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <mchehab@redhat.com>2009-06-29 04:41:26 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2009-07-05 13:30:02 -0400
commit7dfba00d05f3c7db9510f3b54a472981cf1521af (patch)
tree0715f03c1f997a29e9aa4d5789a9bde51ff0f503
parentb179bc4579f67c6f1df524c48b28cacf0c7a1b91 (diff)
V4L/DVB (12135): Add a driver for mt9v011 sensor
Adds driver for mt9v011 based on its datasheet, available at: http://download.micron.com/pdf/datasheets/imaging/MT9V011.pdf The driver was tested with a webcam that will be added on a next patch. Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r--drivers/media/video/Kconfig8
-rw-r--r--drivers/media/video/Makefile1
-rw-r--r--drivers/media/video/mt9v011.c355
-rw-r--r--drivers/media/video/mt9v011.h35
-rw-r--r--include/media/v4l2-chip-ident.h3
5 files changed, 402 insertions, 0 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 061e147f6f26..84b6fc15519d 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -312,6 +312,14 @@ config VIDEO_OV7670
312 OV7670 VGA camera. It currently only works with the M88ALP01 312 OV7670 VGA camera. It currently only works with the M88ALP01
313 controller. 313 controller.
314 314
315config VIDEO_MT9V011
316 tristate "Micron mt9v011 sensor support"
317 depends on I2C && VIDEO_V4L2
318 ---help---
319 This is a Video4Linux2 sensor-level driver for the Micron
320 mt0v011 1.3 Mpixel camera. It currently only works with the
321 em28xx driver.
322
315config VIDEO_TCM825X 323config VIDEO_TCM825X
316 tristate "TCM825x camera sensor support" 324 tristate "TCM825x camera sensor support"
317 depends on I2C && VIDEO_V4L2 325 depends on I2C && VIDEO_V4L2
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 7fb3add1b387..9f2e3214a482 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -69,6 +69,7 @@ obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
69obj-$(CONFIG_VIDEO_OV7670) += ov7670.o 69obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
70obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o 70obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o
71obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o 71obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
72obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o
72 73
73obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o 74obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o
74obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o 75obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o
diff --git a/drivers/media/video/mt9v011.c b/drivers/media/video/mt9v011.c
new file mode 100644
index 000000000000..4389197cedd7
--- /dev/null
+++ b/drivers/media/video/mt9v011.c
@@ -0,0 +1,355 @@
1/*
2 * mt9v011 -Micron 1/4-Inch VGA Digital Image Sensor
3 *
4 * Copyright (c) 2009 Mauro Carvalho Chehab (mchehab@redhat.com)
5 * This code is placed under the terms of the GNU General Public License v2
6 */
7
8#include <linux/i2c.h>
9#include <linux/videodev2.h>
10#include <linux/delay.h>
11#include <media/v4l2-device.h>
12#include "mt9v011.h"
13#include <media/v4l2-i2c-drv.h>
14#include <media/v4l2-chip-ident.h>
15
16MODULE_DESCRIPTION("Micron mt9v011 sensor driver");
17MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
18MODULE_LICENSE("GPL");
19
20
21static int debug;
22module_param(debug, int, 0);
23MODULE_PARM_DESC(debug, "Debug level (0-2)");
24
25/* supported controls */
26static struct v4l2_queryctrl mt9v011_qctrl[] = {
27 {
28 .id = V4L2_CID_GAIN,
29 .type = V4L2_CTRL_TYPE_INTEGER,
30 .name = "Gain",
31 .minimum = 0,
32 .maximum = (1 << 10) - 1,
33 .step = 1,
34 .default_value = 0x0020,
35 .flags = 0,
36 }, {
37 .id = V4L2_CID_RED_BALANCE,
38 .type = V4L2_CTRL_TYPE_INTEGER,
39 .name = "Red Balance",
40 .minimum = -1 << 9,
41 .maximum = (1 << 9) - 1,
42 .step = 1,
43 .default_value = 0,
44 .flags = 0,
45 }, {
46 .id = V4L2_CID_BLUE_BALANCE,
47 .type = V4L2_CTRL_TYPE_INTEGER,
48 .name = "Blue Balance",
49 .minimum = -1 << 9,
50 .maximum = (1 << 9) - 1,
51 .step = 1,
52 .default_value = 0,
53 .flags = 0,
54 },
55};
56
57struct mt9v011 {
58 struct v4l2_subdev sd;
59
60 u16 global_gain, 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 if (1 != (rc = i2c_master_send(c, &addr, 1)))
75 v4l2_dbg(0, debug, sd,
76 "i2c i/o error: rc == %d (should be 1)\n", rc);
77
78 msleep(10);
79
80 if (2 != (rc = i2c_master_recv(c, (char *)&buffer, 2)))
81 v4l2_dbg(0, debug, sd,
82 "i2c i/o error: rc == %d (should be 1)\n", rc);
83
84 val = be16_to_cpu(buffer);
85
86 v4l2_dbg(2, debug, sd, "mt9v011: read 0x%02x = 0x%04x\n", addr, val);
87
88 return val;
89}
90
91static void mt9v011_write(struct v4l2_subdev *sd, unsigned char addr,
92 u16 value)
93{
94 struct i2c_client *c = v4l2_get_subdevdata(sd);
95 unsigned char buffer[3];
96 int rc;
97
98 buffer[0] = addr;
99 buffer[1] = value >> 8;
100 buffer[2] = value & 0xff;
101
102 v4l2_dbg(2, debug, sd,
103 "mt9v011: writing 0x%02x 0x%04x\n", buffer[0], value);
104 if (3 != (rc = i2c_master_send(c, buffer, 3)))
105 v4l2_dbg(0, debug, sd,
106 "i2c i/o error: rc == %d (should be 3)\n", rc);
107}
108
109
110struct i2c_reg_value {
111 unsigned char reg;
112 u16 value;
113};
114
115/*
116 * Values used at the original driver
117 * Some values are marked as Reserved at the datasheet
118 */
119static const struct i2c_reg_value mt9v011_init_default[] = {
120 /* guessed meaning - as mt9m111 */
121 { R0D_MT9V011_RESET, 0x0001 },
122 { R0D_MT9V011_RESET, 0x0000 },
123 { R01_MT9V011_ROWSTART, 0x0008 },
124 { R02_MT9V011_COLSTART, 0x0014 },
125 { R03_MT9V011_HEIGHT, 0x01e0 },
126 { R04_MT9V011_WIDTH, 0x0280 },
127 { R05_MT9V011_HBLANK, 0x0001 },
128 { R05_MT9V011_HBLANK, 0x0001 },
129 { R0A_MT9V011_CLK_SPEED, 0x0000 },
130 { R05_MT9V011_HBLANK, 0x000a },
131 { 0x30, 0x0005 },
132 { 0x34, 0x0100 },
133 { 0x3d, 0x068f },
134 { 0x40, 0x01e0 },
135 { 0x52, 0x0100 },
136 { 0x58, 0x0038 }, /* Datasheet default 0x0078 */
137 { 0x59, 0x0723 }, /* Datasheet default 0x0703 */
138 { 0x62, 0x041a }, /* Datasheet default 0x0418 */
139 { R09_MT9V011_SHUTTER_WIDTH, 0x0418 },
140 { R20_MT9V011_READ_MODE, 0x1100 },
141};
142
143static void set_balance(struct v4l2_subdev *sd)
144{
145 struct mt9v011 *core = to_mt9v011(sd);
146 u16 green1_gain, green2_gain, blue_gain, red_gain;
147
148 green1_gain = core->global_gain;
149 green2_gain = core->global_gain;
150
151 blue_gain = core->global_gain +
152 core->global_gain * core->blue_bal / (1 << 9);
153
154 red_gain = core->global_gain +
155 core->global_gain * core->blue_bal / (1 << 9);
156
157 mt9v011_write(sd, R2B_MT9V011_GREEN_1_GAIN, green1_gain);
158 mt9v011_write(sd, R2E_MT9V011_GREEN_2_GAIN, green1_gain);
159 mt9v011_write(sd, R2C_MT9V011_BLUE_GAIN, blue_gain);
160 mt9v011_write(sd, R2D_MT9V011_RED_GAIN, red_gain);
161}
162
163static int mt9v011_reset(struct v4l2_subdev *sd, u32 val)
164{
165 u16 version;
166 int i;
167
168 version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION);
169
170 if (version != MT9V011_VERSION) {
171 v4l2_info(sd, "*** unknown micron chip detected (0x%04x.\n",
172 version);
173 return -EINVAL;
174
175 }
176
177 for (i = 0; i < ARRAY_SIZE(mt9v011_init_default); i++)
178 mt9v011_write(sd, mt9v011_init_default[i].reg,
179 mt9v011_init_default[i].value);
180
181 set_balance(sd);
182
183 return 0;
184};
185
186static int mt9v011_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
187{
188 struct mt9v011 *core = to_mt9v011(sd);
189
190 v4l2_dbg(1, debug, sd, "g_ctrl called\n");
191
192 switch (ctrl->id) {
193 case V4L2_CID_GAIN:
194 ctrl->value = core->global_gain;
195 return 0;
196 case V4L2_CID_RED_BALANCE:
197 ctrl->value = core->red_bal;
198 return 0;
199 case V4L2_CID_BLUE_BALANCE:
200 ctrl->value = core->blue_bal;
201 return 0;
202 }
203 return -EINVAL;
204}
205
206static int mt9v011_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
207{
208 struct mt9v011 *core = to_mt9v011(sd);
209 u8 i, n;
210 n = ARRAY_SIZE(mt9v011_qctrl);
211
212 for (i = 0; i < n; i++) {
213 if (ctrl->id != mt9v011_qctrl[i].id)
214 continue;
215 if (ctrl->value < mt9v011_qctrl[i].minimum ||
216 ctrl->value > mt9v011_qctrl[i].maximum)
217 return -ERANGE;
218 v4l2_dbg(1, debug, sd, "s_ctrl: id=%d, value=%d\n",
219 ctrl->id, ctrl->value);
220 break;
221 }
222
223 switch (ctrl->id) {
224 case V4L2_CID_GAIN:
225 core->global_gain = ctrl->value;
226 break;
227 case V4L2_CID_RED_BALANCE:
228 core->red_bal = ctrl->value;
229 break;
230 case V4L2_CID_BLUE_BALANCE:
231 core->blue_bal = ctrl->value;
232 break;
233 default:
234 return -EINVAL;
235 }
236
237 set_balance(sd);
238
239 return 0;
240}
241
242#ifdef CONFIG_VIDEO_ADV_DEBUG
243static int mt9v011_g_register(struct v4l2_subdev *sd,
244 struct v4l2_dbg_register *reg)
245{
246 struct i2c_client *client = v4l2_get_subdevdata(sd);
247
248 if (!v4l2_chip_match_i2c_client(client, &reg->match))
249 return -EINVAL;
250 if (!capable(CAP_SYS_ADMIN))
251 return -EPERM;
252
253 reg->val = mt9v011_read(sd, reg->reg & 0xff);
254 reg->size = 2;
255
256 return 0;
257}
258
259static int mt9v011_s_register(struct v4l2_subdev *sd,
260 struct v4l2_dbg_register *reg)
261{
262 struct i2c_client *client = v4l2_get_subdevdata(sd);
263
264 if (!v4l2_chip_match_i2c_client(client, &reg->match))
265 return -EINVAL;
266 if (!capable(CAP_SYS_ADMIN))
267 return -EPERM;
268
269 mt9v011_write(sd, reg->reg & 0xff, reg->val & 0xffff);
270
271 return 0;
272}
273#endif
274
275static int mt9v011_g_chip_ident(struct v4l2_subdev *sd,
276 struct v4l2_dbg_chip_ident *chip)
277{
278 struct i2c_client *client = v4l2_get_subdevdata(sd);
279
280 return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_MT9V011,
281 MT9V011_VERSION);
282}
283
284static const struct v4l2_subdev_core_ops mt9v011_core_ops = {
285 .g_ctrl = mt9v011_g_ctrl,
286 .s_ctrl = mt9v011_s_ctrl,
287 .reset = mt9v011_reset,
288 .g_chip_ident = mt9v011_g_chip_ident,
289#ifdef CONFIG_VIDEO_ADV_DEBUG
290 .g_register = mt9v011_g_register,
291 .s_register = mt9v011_s_register,
292#endif
293};
294
295static const struct v4l2_subdev_ops mt9v011_ops = {
296 .core = &mt9v011_core_ops,
297};
298
299
300/****************************************************************************
301 I2C Client & Driver
302 ****************************************************************************/
303
304static int mt9v011_probe(struct i2c_client *c,
305 const struct i2c_device_id *id)
306{
307 struct mt9v011 *core;
308 struct v4l2_subdev *sd;
309
310 /* Check if the adapter supports the needed features */
311 if (!i2c_check_functionality(c->adapter,
312 I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
313 return -EIO;
314
315 core = kzalloc(sizeof(struct mt9v011), GFP_KERNEL);
316 if (!core)
317 return -ENOMEM;
318
319 core->global_gain = 0x0024;
320
321 sd = &core->sd;
322 v4l2_i2c_subdev_init(sd, c, &mt9v011_ops);
323 v4l_info(c, "chip found @ 0x%02x (%s)\n",
324 c->addr << 1, c->adapter->name);
325
326 return 0;
327}
328
329static int mt9v011_remove(struct i2c_client *c)
330{
331 struct v4l2_subdev *sd = i2c_get_clientdata(c);
332
333 v4l2_dbg(1, debug, sd,
334 "mt9v011.c: removing mt9v011 adapter on address 0x%x\n",
335 c->addr << 1);
336
337 v4l2_device_unregister_subdev(sd);
338 kfree(to_mt9v011(sd));
339 return 0;
340}
341
342/* ----------------------------------------------------------------------- */
343
344static const struct i2c_device_id mt9v011_id[] = {
345 { "mt9v011", 0 },
346 { }
347};
348MODULE_DEVICE_TABLE(i2c, mt9v011_id);
349
350static struct v4l2_i2c_driver_data v4l2_i2c_data = {
351 .name = "mt9v011",
352 .probe = mt9v011_probe,
353 .remove = mt9v011_remove,
354 .id_table = mt9v011_id,
355};
diff --git a/drivers/media/video/mt9v011.h b/drivers/media/video/mt9v011.h
new file mode 100644
index 000000000000..9e443ee30558
--- /dev/null
+++ b/drivers/media/video/mt9v011.h
@@ -0,0 +1,35 @@
1/*
2 * mt9v011 -Micron 1/4-Inch VGA Digital Image Sensor
3 *
4 * Copyright (c) 2009 Mauro Carvalho Chehab (mchehab@redhat.com)
5 * This code is placed under the terms of the GNU General Public License v2
6 */
7
8#ifndef MT9V011_H_
9#define MT9V011_H_
10
11#define R00_MT9V011_CHIP_VERSION 0x00
12#define R01_MT9V011_ROWSTART 0x01
13#define R02_MT9V011_COLSTART 0x02
14#define R03_MT9V011_HEIGHT 0x03
15#define R04_MT9V011_WIDTH 0x04
16#define R05_MT9V011_HBLANK 0x05
17#define R06_MT9V011_VBLANK 0x06
18#define R07_MT9V011_OUT_CTRL 0x07
19#define R09_MT9V011_SHUTTER_WIDTH 0x09
20#define R0A_MT9V011_CLK_SPEED 0x0a
21#define R0B_MT9V011_RESTART 0x0b
22#define R0C_MT9V011_SHUTTER_DELAY 0x0c
23#define R0D_MT9V011_RESET 0x0d
24#define R1E_MT9V011_DIGITAL_ZOOM 0x1e
25#define R20_MT9V011_READ_MODE 0x20
26#define R2B_MT9V011_GREEN_1_GAIN 0x2b
27#define R2C_MT9V011_BLUE_GAIN 0x2c
28#define R2D_MT9V011_RED_GAIN 0x2d
29#define R2E_MT9V011_GREEN_2_GAIN 0x2e
30#define R35_MT9V011_GLOBAL_GAIN 0x35
31#define RF1_MT9V011_CHIP_ENABLE 0xf1
32
33#define MT9V011_VERSION 0x8243
34
35#endif
diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h
index 4d7e2272c42f..11a4a2d3e364 100644
--- a/include/media/v4l2-chip-ident.h
+++ b/include/media/v4l2-chip-ident.h
@@ -155,6 +155,9 @@ enum {
155 /* module cafe_ccic, just ident 8801 */ 155 /* module cafe_ccic, just ident 8801 */
156 V4L2_IDENT_CAFE = 8801, 156 V4L2_IDENT_CAFE = 8801,
157 157
158 /* module mt9v011, just ident 8243 */
159 V4L2_IDENT_MT9V011 = 8243,
160
158 /* module tw9910: just ident 9910 */ 161 /* module tw9910: just ident 9910 */
159 V4L2_IDENT_TW9910 = 9910, 162 V4L2_IDENT_TW9910 = 9910,
160 163