aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuennadi Liakhovetski <g.liakhovetski@gmx.de>2010-10-05 11:33:25 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2010-10-21 05:55:41 -0400
commit67826235ee4c40b20ea0d8ddfbd32bb20e9bb457 (patch)
treeea01864f8627c2d54e8a775ba3f70bd93d29f506
parent32e1f777274b0e642a8af381a4e07d7858506f37 (diff)
[media] V4L: add an IMX074 sensor soc-camera / v4l2-subdev driver
This patch adds an initial driver for the IMXъ74 image sensor from Sony. Lacking documentation, only very basic functionality in one specific image format has been implemented and tested. Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r--drivers/media/video/Kconfig6
-rw-r--r--drivers/media/video/Makefile1
-rw-r--r--drivers/media/video/imx074.c508
3 files changed, 515 insertions, 0 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 31d8a2bc3203..8db8c6fd6fb1 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -715,6 +715,12 @@ config SOC_CAMERA
715 over a bus like PCI or USB. For example some i2c camera connected 715 over a bus like PCI or USB. For example some i2c camera connected
716 directly to the data bus of an SoC. 716 directly to the data bus of an SoC.
717 717
718config SOC_CAMERA_IMX074
719 tristate "imx074 support"
720 depends on SOC_CAMERA && I2C
721 help
722 This driver supports IMX074 cameras from Sony
723
718config SOC_CAMERA_MT9M001 724config SOC_CAMERA_MT9M001
719 tristate "mt9m001 support" 725 tristate "mt9m001 support"
720 depends on SOC_CAMERA && I2C 726 depends on SOC_CAMERA && I2C
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 134f616f7a38..4f0952a758da 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -72,6 +72,7 @@ obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o
72obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o 72obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
73obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o 73obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o
74 74
75obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o
75obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o 76obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o
76obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o 77obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o
77obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o 78obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o
diff --git a/drivers/media/video/imx074.c b/drivers/media/video/imx074.c
new file mode 100644
index 000000000000..380e459f899d
--- /dev/null
+++ b/drivers/media/video/imx074.c
@@ -0,0 +1,508 @@
1/*
2 * Driver for IMX074 CMOS Image Sensor from Sony
3 *
4 * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
5 *
6 * Partially inspired by the IMX074 driver from the Android / MSM tree
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/delay.h>
14#include <linux/i2c.h>
15#include <linux/slab.h>
16#include <linux/videodev2.h>
17
18#include <media/soc_camera.h>
19#include <media/soc_mediabus.h>
20#include <media/v4l2-subdev.h>
21#include <media/v4l2-chip-ident.h>
22
23/* IMX074 registers */
24
25#define MODE_SELECT 0x0100
26#define IMAGE_ORIENTATION 0x0101
27#define GROUPED_PARAMETER_HOLD 0x0104
28
29/* Integration Time */
30#define COARSE_INTEGRATION_TIME_HI 0x0202
31#define COARSE_INTEGRATION_TIME_LO 0x0203
32/* Gain */
33#define ANALOGUE_GAIN_CODE_GLOBAL_HI 0x0204
34#define ANALOGUE_GAIN_CODE_GLOBAL_LO 0x0205
35
36/* PLL registers */
37#define PRE_PLL_CLK_DIV 0x0305
38#define PLL_MULTIPLIER 0x0307
39#define PLSTATIM 0x302b
40#define VNDMY_ABLMGSHLMT 0x300a
41#define Y_OPBADDR_START_DI 0x3014
42/* mode setting */
43#define FRAME_LENGTH_LINES_HI 0x0340
44#define FRAME_LENGTH_LINES_LO 0x0341
45#define LINE_LENGTH_PCK_HI 0x0342
46#define LINE_LENGTH_PCK_LO 0x0343
47#define YADDR_START 0x0347
48#define YADDR_END 0x034b
49#define X_OUTPUT_SIZE_MSB 0x034c
50#define X_OUTPUT_SIZE_LSB 0x034d
51#define Y_OUTPUT_SIZE_MSB 0x034e
52#define Y_OUTPUT_SIZE_LSB 0x034f
53#define X_EVEN_INC 0x0381
54#define X_ODD_INC 0x0383
55#define Y_EVEN_INC 0x0385
56#define Y_ODD_INC 0x0387
57
58#define HMODEADD 0x3001
59#define VMODEADD 0x3016
60#define VAPPLINE_START 0x3069
61#define VAPPLINE_END 0x306b
62#define SHUTTER 0x3086
63#define HADDAVE 0x30e8
64#define LANESEL 0x3301
65
66/* IMX074 supported geometry */
67#define IMX074_WIDTH 1052
68#define IMX074_HEIGHT 780
69
70/* IMX074 has only one fixed colorspace per pixelcode */
71struct imx074_datafmt {
72 enum v4l2_mbus_pixelcode code;
73 enum v4l2_colorspace colorspace;
74};
75
76struct imx074 {
77 struct v4l2_subdev subdev;
78 const struct imx074_datafmt *fmt;
79};
80
81static const struct imx074_datafmt imx074_colour_fmts[] = {
82 {V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB},
83};
84
85static struct imx074 *to_imx074(const struct i2c_client *client)
86{
87 return container_of(i2c_get_clientdata(client), struct imx074, subdev);
88}
89
90/* Find a data format by a pixel code in an array */
91static const struct imx074_datafmt *imx074_find_datafmt(enum v4l2_mbus_pixelcode code)
92{
93 int i;
94
95 for (i = 0; i < ARRAY_SIZE(imx074_colour_fmts); i++)
96 if (imx074_colour_fmts[i].code == code)
97 return imx074_colour_fmts + i;
98
99 return NULL;
100}
101
102static int reg_write(struct i2c_client *client, const u16 addr, const u8 data)
103{
104 struct i2c_adapter *adap = client->adapter;
105 struct i2c_msg msg;
106 unsigned char tx[3];
107 int ret;
108
109 msg.addr = client->addr;
110 msg.buf = tx;
111 msg.len = 3;
112 msg.flags = 0;
113
114 tx[0] = addr >> 8;
115 tx[1] = addr & 0xff;
116 tx[2] = data;
117
118 ret = i2c_transfer(adap, &msg, 1);
119
120 mdelay(2);
121
122 return ret == 1 ? 0 : -EIO;
123}
124
125static int reg_read(struct i2c_client *client, const u16 addr)
126{
127 u8 buf[2] = {addr >> 8, addr & 0xff};
128 int ret;
129 struct i2c_msg msgs[] = {
130 {
131 .addr = client->addr,
132 .flags = 0,
133 .len = 2,
134 .buf = buf,
135 }, {
136 .addr = client->addr,
137 .flags = I2C_M_RD,
138 .len = 2,
139 .buf = buf,
140 },
141 };
142
143 ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
144 if (ret < 0) {
145 dev_warn(&client->dev, "Reading register %x from %x failed\n",
146 addr, client->addr);
147 return ret;
148 }
149
150 return buf[0] & 0xff; /* no sign-extension */
151}
152
153static int imx074_try_fmt(struct v4l2_subdev *sd,
154 struct v4l2_mbus_framefmt *mf)
155{
156 const struct imx074_datafmt *fmt = imx074_find_datafmt(mf->code);
157
158 dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code);
159
160 if (!fmt) {
161 mf->code = imx074_colour_fmts[0].code;
162 mf->colorspace = imx074_colour_fmts[0].colorspace;
163 }
164
165 mf->width = IMX074_WIDTH;
166 mf->height = IMX074_HEIGHT;
167 mf->field = V4L2_FIELD_NONE;
168
169 return 0;
170}
171
172static int imx074_s_fmt(struct v4l2_subdev *sd,
173 struct v4l2_mbus_framefmt *mf)
174{
175 struct i2c_client *client = v4l2_get_subdevdata(sd);
176 struct imx074 *priv = to_imx074(client);
177
178 dev_dbg(sd->v4l2_dev->dev, "%s(%u)\n", __func__, mf->code);
179
180 /* MIPI CSI could have changed the format, double-check */
181 if (!imx074_find_datafmt(mf->code))
182 return -EINVAL;
183
184 imx074_try_fmt(sd, mf);
185
186 priv->fmt = imx074_find_datafmt(mf->code);
187
188 return 0;
189}
190
191static int imx074_g_fmt(struct v4l2_subdev *sd,
192 struct v4l2_mbus_framefmt *mf)
193{
194 struct i2c_client *client = v4l2_get_subdevdata(sd);
195 struct imx074 *priv = to_imx074(client);
196
197 const struct imx074_datafmt *fmt = priv->fmt;
198
199 mf->code = fmt->code;
200 mf->colorspace = fmt->colorspace;
201 mf->width = IMX074_WIDTH;
202 mf->height = IMX074_HEIGHT;
203 mf->field = V4L2_FIELD_NONE;
204
205 return 0;
206}
207
208static int imx074_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
209{
210 struct v4l2_rect *rect = &a->c;
211
212 a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
213 rect->top = 0;
214 rect->left = 0;
215 rect->width = IMX074_WIDTH;
216 rect->height = IMX074_HEIGHT;
217
218 return 0;
219}
220
221static int imx074_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
222{
223 a->bounds.left = 0;
224 a->bounds.top = 0;
225 a->bounds.width = IMX074_WIDTH;
226 a->bounds.height = IMX074_HEIGHT;
227 a->defrect = a->bounds;
228 a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
229 a->pixelaspect.numerator = 1;
230 a->pixelaspect.denominator = 1;
231
232 return 0;
233}
234
235static int imx074_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
236 enum v4l2_mbus_pixelcode *code)
237{
238 if ((unsigned int)index >= ARRAY_SIZE(imx074_colour_fmts))
239 return -EINVAL;
240
241 *code = imx074_colour_fmts[index].code;
242 return 0;
243}
244
245static int imx074_s_stream(struct v4l2_subdev *sd, int enable)
246{
247 struct i2c_client *client = v4l2_get_subdevdata(sd);
248
249 /* MODE_SELECT: stream or standby */
250 return reg_write(client, MODE_SELECT, !!enable);
251}
252
253static int imx074_g_chip_ident(struct v4l2_subdev *sd,
254 struct v4l2_dbg_chip_ident *id)
255{
256 struct i2c_client *client = v4l2_get_subdevdata(sd);
257
258 if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
259 return -EINVAL;
260
261 if (id->match.addr != client->addr)
262 return -ENODEV;
263
264 id->ident = V4L2_IDENT_IMX074;
265 id->revision = 0;
266
267 return 0;
268}
269
270static struct v4l2_subdev_video_ops imx074_subdev_video_ops = {
271 .s_stream = imx074_s_stream,
272 .s_mbus_fmt = imx074_s_fmt,
273 .g_mbus_fmt = imx074_g_fmt,
274 .try_mbus_fmt = imx074_try_fmt,
275 .enum_mbus_fmt = imx074_enum_fmt,
276 .g_crop = imx074_g_crop,
277 .cropcap = imx074_cropcap,
278};
279
280static struct v4l2_subdev_core_ops imx074_subdev_core_ops = {
281 .g_chip_ident = imx074_g_chip_ident,
282};
283
284static struct v4l2_subdev_ops imx074_subdev_ops = {
285 .core = &imx074_subdev_core_ops,
286 .video = &imx074_subdev_video_ops,
287};
288
289/*
290 * We have to provide soc-camera operations, but we don't have anything to say
291 * there. The MIPI CSI2 driver will provide .query_bus_param and .set_bus_param
292 */
293static unsigned long imx074_query_bus_param(struct soc_camera_device *icd)
294{
295 return 0;
296}
297
298static int imx074_set_bus_param(struct soc_camera_device *icd,
299 unsigned long flags)
300{
301 return -1;
302}
303
304static struct soc_camera_ops imx074_ops = {
305 .query_bus_param = imx074_query_bus_param,
306 .set_bus_param = imx074_set_bus_param,
307};
308
309static int imx074_video_probe(struct soc_camera_device *icd,
310 struct i2c_client *client)
311{
312 int ret;
313 u16 id;
314
315 /* Read sensor Model ID */
316 ret = reg_read(client, 0);
317 if (ret < 0)
318 return ret;
319
320 id = ret << 8;
321
322 ret = reg_read(client, 1);
323 if (ret < 0)
324 return ret;
325
326 id |= ret;
327
328 dev_info(&client->dev, "Chip ID 0x%04x detected\n", id);
329
330 if (id != 0x74)
331 return -ENODEV;
332
333 /* PLL Setting EXTCLK=24MHz, 22.5times */
334 reg_write(client, PLL_MULTIPLIER, 0x2D);
335 reg_write(client, PRE_PLL_CLK_DIV, 0x02);
336 reg_write(client, PLSTATIM, 0x4B);
337
338 /* 2-lane mode */
339 reg_write(client, 0x3024, 0x00);
340
341 reg_write(client, IMAGE_ORIENTATION, 0x00);
342
343 /* select RAW mode:
344 * 0x08+0x08 = top 8 bits
345 * 0x0a+0x08 = compressed 8-bits
346 * 0x0a+0x0a = 10 bits
347 */
348 reg_write(client, 0x0112, 0x08);
349 reg_write(client, 0x0113, 0x08);
350
351 /* Base setting for High frame mode */
352 reg_write(client, VNDMY_ABLMGSHLMT, 0x80);
353 reg_write(client, Y_OPBADDR_START_DI, 0x08);
354 reg_write(client, 0x3015, 0x37);
355 reg_write(client, 0x301C, 0x01);
356 reg_write(client, 0x302C, 0x05);
357 reg_write(client, 0x3031, 0x26);
358 reg_write(client, 0x3041, 0x60);
359 reg_write(client, 0x3051, 0x24);
360 reg_write(client, 0x3053, 0x34);
361 reg_write(client, 0x3057, 0xC0);
362 reg_write(client, 0x305C, 0x09);
363 reg_write(client, 0x305D, 0x07);
364 reg_write(client, 0x3060, 0x30);
365 reg_write(client, 0x3065, 0x00);
366 reg_write(client, 0x30AA, 0x08);
367 reg_write(client, 0x30AB, 0x1C);
368 reg_write(client, 0x30B0, 0x32);
369 reg_write(client, 0x30B2, 0x83);
370 reg_write(client, 0x30D3, 0x04);
371 reg_write(client, 0x3106, 0x78);
372 reg_write(client, 0x310C, 0x82);
373 reg_write(client, 0x3304, 0x05);
374 reg_write(client, 0x3305, 0x04);
375 reg_write(client, 0x3306, 0x11);
376 reg_write(client, 0x3307, 0x02);
377 reg_write(client, 0x3308, 0x0C);
378 reg_write(client, 0x3309, 0x06);
379 reg_write(client, 0x330A, 0x08);
380 reg_write(client, 0x330B, 0x04);
381 reg_write(client, 0x330C, 0x08);
382 reg_write(client, 0x330D, 0x06);
383 reg_write(client, 0x330E, 0x01);
384 reg_write(client, 0x3381, 0x00);
385
386 /* V : 1/2V-addition (1,3), H : 1/2H-averaging (1,3) -> Full HD */
387 /* 1608 = 1560 + 48 (black lines) */
388 reg_write(client, FRAME_LENGTH_LINES_HI, 0x06);
389 reg_write(client, FRAME_LENGTH_LINES_LO, 0x48);
390 reg_write(client, YADDR_START, 0x00);
391 reg_write(client, YADDR_END, 0x2F);
392 /* 0x838 == 2104 */
393 reg_write(client, X_OUTPUT_SIZE_MSB, 0x08);
394 reg_write(client, X_OUTPUT_SIZE_LSB, 0x38);
395 /* 0x618 == 1560 */
396 reg_write(client, Y_OUTPUT_SIZE_MSB, 0x06);
397 reg_write(client, Y_OUTPUT_SIZE_LSB, 0x18);
398 reg_write(client, X_EVEN_INC, 0x01);
399 reg_write(client, X_ODD_INC, 0x03);
400 reg_write(client, Y_EVEN_INC, 0x01);
401 reg_write(client, Y_ODD_INC, 0x03);
402 reg_write(client, HMODEADD, 0x00);
403 reg_write(client, VMODEADD, 0x16);
404 reg_write(client, VAPPLINE_START, 0x24);
405 reg_write(client, VAPPLINE_END, 0x53);
406 reg_write(client, SHUTTER, 0x00);
407 reg_write(client, HADDAVE, 0x80);
408
409 reg_write(client, LANESEL, 0x00);
410
411 reg_write(client, GROUPED_PARAMETER_HOLD, 0x00); /* off */
412
413 return 0;
414}
415
416static int imx074_probe(struct i2c_client *client,
417 const struct i2c_device_id *did)
418{
419 struct imx074 *priv;
420 struct soc_camera_device *icd = client->dev.platform_data;
421 struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
422 struct soc_camera_link *icl;
423 int ret;
424
425 if (!icd) {
426 dev_err(&client->dev, "IMX074: missing soc-camera data!\n");
427 return -EINVAL;
428 }
429
430 icl = to_soc_camera_link(icd);
431 if (!icl) {
432 dev_err(&client->dev, "IMX074: missing platform data!\n");
433 return -EINVAL;
434 }
435
436 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
437 dev_warn(&adapter->dev,
438 "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n");
439 return -EIO;
440 }
441
442 priv = kzalloc(sizeof(struct imx074), GFP_KERNEL);
443 if (!priv)
444 return -ENOMEM;
445
446 v4l2_i2c_subdev_init(&priv->subdev, client, &imx074_subdev_ops);
447
448 icd->ops = &imx074_ops;
449 priv->fmt = &imx074_colour_fmts[0];
450
451 ret = imx074_video_probe(icd, client);
452 if (ret < 0) {
453 icd->ops = NULL;
454 i2c_set_clientdata(client, NULL);
455 kfree(priv);
456 return ret;
457 }
458
459 return ret;
460}
461
462static int imx074_remove(struct i2c_client *client)
463{
464 struct imx074 *priv = to_imx074(client);
465 struct soc_camera_device *icd = client->dev.platform_data;
466 struct soc_camera_link *icl = to_soc_camera_link(icd);
467
468 icd->ops = NULL;
469 if (icl->free_bus)
470 icl->free_bus(icl);
471 i2c_set_clientdata(client, NULL);
472 client->driver = NULL;
473 kfree(priv);
474
475 return 0;
476}
477
478static const struct i2c_device_id imx074_id[] = {
479 { "imx074", 0 },
480 { }
481};
482MODULE_DEVICE_TABLE(i2c, imx074_id);
483
484static struct i2c_driver imx074_i2c_driver = {
485 .driver = {
486 .name = "imx074",
487 },
488 .probe = imx074_probe,
489 .remove = imx074_remove,
490 .id_table = imx074_id,
491};
492
493static int __init imx074_mod_init(void)
494{
495 return i2c_add_driver(&imx074_i2c_driver);
496}
497
498static void __exit imx074_mod_exit(void)
499{
500 i2c_del_driver(&imx074_i2c_driver);
501}
502
503module_init(imx074_mod_init);
504module_exit(imx074_mod_exit);
505
506MODULE_DESCRIPTION("Sony IMX074 Camera driver");
507MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
508MODULE_LICENSE("GPL v2");