]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - glsdk/meta-ti-glsdk.git/blob - recipes-kernel/linux/linux-omap-2.6.39/camera/0001-Add-mt9p031-sensor-support.patch
linux-omap 2.6.39: add v6 of the MT9P031 sensor support
[glsdk/meta-ti-glsdk.git] / recipes-kernel / linux / linux-omap-2.6.39 / camera / 0001-Add-mt9p031-sensor-support.patch
1 Delivered-To: koen@beagleboard.org
2 Received: by 10.151.113.15 with SMTP id q15cs142302ybm;
3         Wed, 1 Jun 2011 08:40:02 -0700 (PDT)
4 Received: by 10.101.218.19 with SMTP id v19mr4614809anq.152.1306942801792;
5         Wed, 01 Jun 2011 08:40:01 -0700 (PDT)
6 Return-Path: <beagleboard+bncCLbVq8bYBxDHupnvBBoErrmKKA@googlegroups.com>
7 Received: from mail-gw0-f56.google.com (mail-gw0-f56.google.com [74.125.83.56])
8         by mx.google.com with ESMTPS id q9si2830757ani.54.2011.06.01.08.40.00
9         (version=TLSv1/SSLv3 cipher=OTHER);
10         Wed, 01 Jun 2011 08:40:00 -0700 (PDT)
11 Received-SPF: pass (google.com: domain of beagleboard+bncCLbVq8bYBxDHupnvBBoErrmKKA@googlegroups.com designates 74.125.83.56 as permitted sender) client-ip=74.125.83.56;
12 Authentication-Results: mx.google.com; spf=pass (google.com: domain of beagleboard+bncCLbVq8bYBxDHupnvBBoErrmKKA@googlegroups.com designates 74.125.83.56 as permitted sender) smtp.mail=beagleboard+bncCLbVq8bYBxDHupnvBBoErrmKKA@googlegroups.com; dkim=pass (test mode) header.i=@googlegroups.com
13 Received: by gwaa11 with SMTP id a11sf7473558gwa.11
14         for <multiple recipients>; Wed, 01 Jun 2011 08:40:00 -0700 (PDT)
15 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
16         d=googlegroups.com; s=beta;
17         h=domainkey-signature:mime-version:x-beenthere:received-spf:from:to
18          :cc:subject:date:message-id:x-mailer:x-original-sender
19          :x-original-authentication-results:reply-to:precedence:mailing-list
20          :list-id:x-google-group-id:list-post:list-help:list-archive:sender
21          :list-unsubscribe:content-type;
22         bh=YBNkufpLpgUhP0Ja/p5xbR4lygvbGJf0W+WAfI5IdB4=;
23         b=iZQ8TroGI9c5zX6Vw7INxR2O7jT5tg6fO4GUDr3lxYXfWP0o3J/E4yqZ8zZwb9bxmI
24          gXtiV1FBeOjS0u6+ZrrqCt9HtCMEKFHMI4kYdoD2ABO1pjAXmL6lQtMYgYWM9j+RNIrk
25          F1mzfEnykk+pYvLCfPf5UU7QbhoSCd1Oc5pv0=
26 DomainKey-Signature: a=rsa-sha1; c=nofws;
27         d=googlegroups.com; s=beta;
28         h=mime-version:x-beenthere:received-spf:from:to:cc:subject:date
29          :message-id:x-mailer:x-original-sender
30          :x-original-authentication-results:reply-to:precedence:mailing-list
31          :list-id:x-google-group-id:list-post:list-help:list-archive:sender
32          :list-unsubscribe:content-type;
33         b=40K6jHETfDcMlLEICg+kctIZRvOz6uePTaKT9ZUCteaABL3LZMqHmFe/MEqyr2+chL
34          KsyhPXLjKwYXTz1Qut5mn+PeALOQZYNDlWk8x0hAlQbiM7ydGppZMWyTs+N6q3hUTY1z
35          jzGEUEKgH+QrbcNJwR7jEXpbLiMaQrxsIVlVI=
36 Received: by 10.91.101.12 with SMTP id d12mr1050131agm.33.1306942797975;
37         Wed, 01 Jun 2011 08:39:57 -0700 (PDT)
38 MIME-Version: 1.0
39 X-BeenThere: beagleboard@googlegroups.com
40 Received: by 10.101.212.20 with SMTP id o20ls99205anq.3.gmail; Wed, 01 Jun
41  2011 08:39:51 -0700 (PDT)
42 Received: by 10.236.141.14 with SMTP id f14mr4899631yhj.62.1306942791154;
43         Wed, 01 Jun 2011 08:39:51 -0700 (PDT)
44 Received: by 10.150.33.26 with SMTP id g26msybg;
45         Wed, 1 Jun 2011 08:37:12 -0700 (PDT)
46 Received: by 10.42.140.134 with SMTP id k6mr2743913icu.80.1306942632159;
47         Wed, 01 Jun 2011 08:37:12 -0700 (PDT)
48 Received: by 10.42.140.134 with SMTP id k6mr2743912icu.80.1306942632143;
49         Wed, 01 Jun 2011 08:37:12 -0700 (PDT)
50 Received: from mail-wy0-f170.google.com (mail-wy0-f170.google.com [74.125.82.170])
51         by gmr-mx.google.com with ESMTPS id c14si398207icn.7.2011.06.01.08.37.10
52         (version=TLSv1/SSLv3 cipher=OTHER);
53         Wed, 01 Jun 2011 08:37:10 -0700 (PDT)
54 Received-SPF: neutral (google.com: 74.125.82.170 is neither permitted nor denied by best guess record for domain of javier.martin@vista-silicon.com) client-ip=74.125.82.170;
55 Received: by mail-wy0-f170.google.com with SMTP id 34so8538295wyb.1
56         for <beagleboard@googlegroups.com>; Wed, 01 Jun 2011 08:37:10 -0700 (PDT)
57 Received: by 10.227.165.10 with SMTP id g10mr7741970wby.91.1306942629902;
58         Wed, 01 Jun 2011 08:37:09 -0700 (PDT)
59 Received: from localhost.localdomain (242.51.18.95.dynamic.jazztel.es [95.18.51.242])
60         by mx.google.com with ESMTPS id fw15sm820685wbb.61.2011.06.01.08.37.01
61         (version=TLSv1/SSLv3 cipher=OTHER);
62         Wed, 01 Jun 2011 08:37:08 -0700 (PDT)
63 From: Javier Martin <javier.martin@vista-silicon.com>
64 To: linux-media@vger.kernel.org
65 Cc: g.liakhovetski@gmx.de,
66         laurent.pinchart@ideasonboard.com,
67         carlighting@yahoo.co.nz,
68         beagleboard@googlegroups.com,
69         mch_kot@yahoo.com.cn,
70         Javier Martin <javier.martin@vista-silicon.com>
71 Subject: [beagleboard] [PATCH v6 1/2] Add driver for Aptina (Micron) mt9p031 sensor.
72 Date: Wed,  1 Jun 2011 17:36:48 +0200
73 Message-Id: <1306942609-2440-1-git-send-email-javier.martin@vista-silicon.com>
74 X-Mailer: git-send-email 1.7.0.4
75 X-Original-Sender: javier.martin@vista-silicon.com
76 X-Original-Authentication-Results: gmr-mx.google.com; spf=neutral (google.com:
77  74.125.82.170 is neither permitted nor denied by best guess record for domain
78  of javier.martin@vista-silicon.com) smtp.mail=javier.martin@vista-silicon.com
79 Reply-To: beagleboard@googlegroups.com
80 Precedence: list
81 Mailing-list: list beagleboard@googlegroups.com; contact beagleboard+owners@googlegroups.com
82 List-ID: <beagleboard.googlegroups.com>
83 X-Google-Group-Id: 1035534660134
84 List-Post: <http://groups.google.com/group/beagleboard/post?hl=en_US>, <mailto:beagleboard@googlegroups.com>
85 List-Help: <http://groups.google.com/support/?hl=en_US>, <mailto:beagleboard+help@googlegroups.com>
86 List-Archive: <http://groups.google.com/group/beagleboard?hl=en_US>
87 Sender: beagleboard@googlegroups.com
88 List-Unsubscribe: <http://groups.google.com/group/beagleboard/subscribe?hl=en_US>,
89  <mailto:beagleboard+unsubscribe@googlegroups.com>
90 Content-Type: text/plain; charset=ISO-8859-1
92 Clock frequency of 57MHz used in previous version was wrong since
93 when VDD_IO is 1.8V it can only support 48MHz.
95 Two new platform flags have been added:
97 - vdd_io: indicates whether the chip is powered with 1.8 or 2.8 VDD_IO.
98 So that it can use the maximum allowed frequency.
99 - version: monochrome and color versions of the chip have exactly
100 the same ID, so the only way to select one of them is through
101 platform data.
103 Internal PLL is now used to generate PIXCLK depending on VDD_IO.
105 Signed-off-by: Javier Martin <javier.martin@vista-silicon.com>
106 ---
107  drivers/media/video/Kconfig   |    7 +
108  drivers/media/video/Makefile  |    1 +
109  drivers/media/video/mt9p031.c |  763 +++++++++++++++++++++++++++++++++++++++++
110  include/media/mt9p031.h       |   23 ++
111  4 files changed, 794 insertions(+), 0 deletions(-)
112  create mode 100644 drivers/media/video/mt9p031.c
113  create mode 100644 include/media/mt9p031.h
115 diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
116 index 00f51dd..cb87e35 100644
117 --- a/drivers/media/video/Kconfig
118 +++ b/drivers/media/video/Kconfig
119 @@ -329,6 +329,13 @@ config VIDEO_OV7670
120           OV7670 VGA camera.  It currently only works with the M88ALP01
121           controller.
122  
123 +config VIDEO_MT9P031
124 +       tristate "Aptina MT9P031 support"
125 +       depends on I2C && VIDEO_V4L2
126 +       ---help---
127 +        This is a Video4Linux2 sensor-level driver for the Aptina
128 +        (Micron) mt9p031 5 Mpixel camera.
130  config VIDEO_MT9V011
131         tristate "Micron mt9v011 sensor support"
132         depends on I2C && VIDEO_V4L2
133 diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
134 index ace5d8b..912b29b 100644
135 --- a/drivers/media/video/Makefile
136 +++ b/drivers/media/video/Makefile
137 @@ -65,6 +65,7 @@ obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
138  obj-$(CONFIG_VIDEO_OV7670)     += ov7670.o
139  obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o
140  obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
141 +obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o
142  obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o
143  obj-$(CONFIG_VIDEO_SR030PC30)  += sr030pc30.o
144  obj-$(CONFIG_VIDEO_NOON010PC30)        += noon010pc30.o
145 diff --git a/drivers/media/video/mt9p031.c b/drivers/media/video/mt9p031.c
146 new file mode 100644
147 index 0000000..cd830b1
148 --- /dev/null
149 +++ b/drivers/media/video/mt9p031.c
150 @@ -0,0 +1,763 @@
151 +/*
152 + * Driver for MT9P031 CMOS Image Sensor from Aptina
153 + *
154 + * Copyright (C) 2011, Javier Martin <javier.martin@vista-silicon.com>
155 + *
156 + * Copyright (C) 2011, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
157 + *
158 + * Based on the MT9V032 driver and Bastian Hecht's code.
159 + *
160 + * This program is free software; you can redistribute it and/or modify
161 + * it under the terms of the GNU General Public License version 2 as
162 + * published by the Free Software Foundation.
163 + */
165 +#include <linux/delay.h>
166 +#include <linux/device.h>
167 +#include <linux/i2c.h>
168 +#include <linux/log2.h>
169 +#include <linux/pm.h>
170 +#include <linux/slab.h>
171 +#include <media/v4l2-subdev.h>
172 +#include <linux/videodev2.h>
174 +#include <media/mt9p031.h>
175 +#include <media/v4l2-chip-ident.h>
176 +#include <media/v4l2-subdev.h>
177 +#include <media/v4l2-device.h>
179 +#define MT9P031_EXTCLK_FREQ                    20000000
181 +#define MT9P031_CHIP_VERSION                   0x00
182 +#define                MT9P031_CHIP_VERSION_VALUE      0x1801
183 +#define MT9P031_ROW_START                      0x01
184 +#define                MT9P031_ROW_START_MIN           1
185 +#define                MT9P031_ROW_START_MAX           2004
186 +#define                MT9P031_ROW_START_DEF           54
187 +#define MT9P031_COLUMN_START                   0x02
188 +#define                MT9P031_COLUMN_START_MIN        1
189 +#define                MT9P031_COLUMN_START_MAX        2750
190 +#define                MT9P031_COLUMN_START_DEF        16
191 +#define MT9P031_WINDOW_HEIGHT                  0x03
192 +#define                MT9P031_WINDOW_HEIGHT_MIN       2
193 +#define                MT9P031_WINDOW_HEIGHT_MAX       2003
194 +#define                MT9P031_WINDOW_HEIGHT_DEF       2003
195 +#define MT9P031_WINDOW_WIDTH                   0x04
196 +#define                MT9P031_WINDOW_WIDTH_MIN        18
197 +#define                MT9P031_WINDOW_WIDTH_MAX        2751
198 +#define                MT9P031_WINDOW_WIDTH_DEF        2751
199 +#define MT9P031_H_BLANKING                     0x05
200 +#define                MT9P031_H_BLANKING_VALUE        0
201 +#define MT9P031_V_BLANKING                     0x06
202 +#define                MT9P031_V_BLANKING_VALUE        25
203 +#define MT9P031_OUTPUT_CONTROL                 0x07
204 +#define                MT9P031_OUTPUT_CONTROL_CEN      2
205 +#define                MT9P031_OUTPUT_CONTROL_SYN      1
206 +#define MT9P031_SHUTTER_WIDTH_UPPER            0x08
207 +#define MT9P031_SHUTTER_WIDTH                  0x09
208 +#define        MT9P031_PLL_CONTROL                     0x10
209 +#define                MT9P031_PLL_CONTROL_PWROFF      0x0050
210 +#define                MT9P031_PLL_CONTROL_PWRON       0x0051
211 +#define                MT9P031_PLL_CONTROL_USEPLL      0x0052
212 +#define        MT9P031_PLL_CONFIG_1                    0x11
213 +#define                MT9P031_PLL_CONFIG_1_M_48MHZ    0x5000
214 +#define                MT9P031_PLL_CONFIG_1_N_48MHZ    0x05
215 +#define                MT9P031_PLL_CONFIG_1_M_96MHZ    0x3600
216 +#define                MT9P031_PLL_CONFIG_1_N_96MHZ    0x05
217 +#define        MT9P031_PLL_CONFIG_2                    0x12
218 +#define                MT9P031_PLL_CONFIG_2_P1_48MHZ   5
219 +#define                MT9P031_PLL_CONFIG_2_P1_96MHZ   2
220 +#define MT9P031_PIXEL_CLOCK_CONTROL            0x0a
221 +#define MT9P031_FRAME_RESTART                  0x0b
222 +#define MT9P031_SHUTTER_DELAY                  0x0c
223 +#define MT9P031_RST                            0x0d
224 +#define                MT9P031_RST_ENABLE              1
225 +#define                MT9P031_RST_DISABLE             0
226 +#define MT9P031_READ_MODE_1                    0x1e
227 +#define MT9P031_READ_MODE_2                    0x20
228 +#define                MT9P031_READ_MODE_2_ROW_MIR     0x8000
229 +#define                MT9P031_READ_MODE_2_COL_MIR     0x4000
230 +#define MT9P031_ROW_ADDRESS_MODE               0x22
231 +#define MT9P031_COLUMN_ADDRESS_MODE            0x23
232 +#define MT9P031_GLOBAL_GAIN                    0x35
234 +struct mt9p031 {
235 +       struct v4l2_subdev subdev;
236 +       struct media_pad pad;
237 +       struct v4l2_rect rect;  /* Sensor window */
238 +       struct v4l2_mbus_framefmt format;
239 +       struct mt9p031_platform_data *pdata;
240 +       struct mutex power_lock; /* lock to protect power_count */
241 +       int power_count;
242 +       u16 xskip;
243 +       u16 yskip;
244 +       /* cache register values */
245 +       u16 output_control;
246 +};
248 +static struct mt9p031 *to_mt9p031(const struct i2c_client *client)
249 +{
250 +       return container_of(i2c_get_clientdata(client), struct mt9p031, subdev);
251 +}
253 +static int reg_read(struct i2c_client *client, const u8 reg)
254 +{
255 +       s32 data = i2c_smbus_read_word_data(client, reg);
256 +       return data < 0 ? data : swab16(data);
257 +}
259 +static int reg_write(struct i2c_client *client, const u8 reg,
260 +                       const u16 data)
261 +{
262 +       return i2c_smbus_write_word_data(client, reg, swab16(data));
263 +}
265 +static int mt9p031_set_output_control(struct mt9p031 *mt9p031, u16 clear,
266 +                                       u16 set)
267 +{
268 +       struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
269 +       u16 value = (mt9p031->output_control & ~clear) | set;
270 +       int ret;
272 +       ret = reg_write(client, MT9P031_OUTPUT_CONTROL, value);
273 +       if (ret < 0)
274 +               return ret;
275 +       mt9p031->output_control = value;
276 +       return 0;
277 +}
279 +static int mt9p031_reset(struct i2c_client *client)
280 +{
281 +       struct mt9p031 *mt9p031 = to_mt9p031(client);
282 +       int ret;
284 +       /* Disable chip output, synchronous option update */
285 +       ret = reg_write(client, MT9P031_RST, MT9P031_RST_ENABLE);
286 +       if (ret < 0)
287 +               return ret;
288 +       ret = reg_write(client, MT9P031_RST, MT9P031_RST_DISABLE);
289 +       if (ret < 0)
290 +               return ret;
291 +       return mt9p031_set_output_control(mt9p031,
292 +                                       MT9P031_OUTPUT_CONTROL_CEN, 0);
293 +}
295 +static int mt9p031_power_on(struct mt9p031 *mt9p031)
296 +{
297 +       struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
298 +       int ret;
300 +       /* Ensure RESET_BAR is low */
301 +       if (mt9p031->pdata->reset) {
302 +               mt9p031->pdata->reset(&mt9p031->subdev, 1);
303 +               msleep(1);
304 +       }
305 +       /* Emable clock */
306 +       if (mt9p031->pdata->set_xclk)
307 +               mt9p031->pdata->set_xclk(&mt9p031->subdev, MT9P031_EXTCLK_FREQ);
308 +       /* Now RESET_BAR must be high */
309 +       if (mt9p031->pdata->reset) {
310 +               mt9p031->pdata->reset(&mt9p031->subdev, 0);
311 +               msleep(1);
312 +       }
313 +       /* soft reset */
314 +       ret = mt9p031_reset(client);
315 +       if (ret < 0) {
316 +               dev_err(&client->dev, "Failed to reset the camera\n");
317 +               return ret;
318 +       }
319 +       return 0;
320 +}
322 +static void mt9p031_power_off(struct mt9p031 *mt9p031)
323 +{
324 +       if (mt9p031->pdata->reset) {
325 +               mt9p031->pdata->reset(&mt9p031->subdev, 1);
326 +               msleep(1);
327 +       }
328 +       if (mt9p031->pdata->set_xclk)
329 +               mt9p031->pdata->set_xclk(&mt9p031->subdev, 0);
330 +}
332 +static int mt9p031_enum_mbus_code(struct v4l2_subdev *sd,
333 +                               struct v4l2_subdev_fh *fh,
334 +                               struct v4l2_subdev_mbus_code_enum *code)
335 +{
336 +       struct mt9p031 *mt9p031 = container_of(sd, struct mt9p031, subdev);
338 +       if (code->pad || code->index)
339 +               return -EINVAL;
341 +       code->code = mt9p031->format.code;
342 +       return 0;
343 +}
345 +static struct v4l2_mbus_framefmt *mt9p031_get_pad_format(
346 +       struct mt9p031 *mt9p031,
347 +       struct v4l2_subdev_fh *fh,
348 +       unsigned int pad, u32 which)
349 +{
350 +       switch (which) {
351 +       case V4L2_SUBDEV_FORMAT_TRY:
352 +               return v4l2_subdev_get_try_format(fh, pad);
353 +       case V4L2_SUBDEV_FORMAT_ACTIVE:
354 +               return &mt9p031->format;
355 +       default:
356 +               return NULL;
357 +       }
358 +}
360 +static struct v4l2_rect *mt9p031_get_pad_crop(struct mt9p031 *mt9p031,
361 +                       struct v4l2_subdev_fh *fh, unsigned int pad, u32 which)
362 +{
363 +       switch (which) {
364 +       case V4L2_SUBDEV_FORMAT_TRY:
365 +               return v4l2_subdev_get_try_crop(fh, pad);
366 +       case V4L2_SUBDEV_FORMAT_ACTIVE:
367 +               return &mt9p031->rect;
368 +       default:
369 +               return NULL;
370 +       }
371 +}
373 +static int mt9p031_get_crop(struct v4l2_subdev *sd,
374 +                               struct v4l2_subdev_fh *fh,
375 +                               struct v4l2_subdev_crop *crop)
376 +{
377 +       struct mt9p031 *mt9p031 = container_of(sd, struct mt9p031, subdev);
378 +       struct v4l2_rect *rect = mt9p031_get_pad_crop(mt9p031, fh, crop->pad,
379 +                                                       crop->which);
380 +       if (!rect)
381 +               return -EINVAL;
383 +       crop->rect = *rect;
385 +       return 0;
386 +}
388 +static u16 mt9p031_skip_for_crop(s32 source, s32 *target, s32 max_skip)
389 +{
390 +       unsigned int skip;
392 +       if (source - source / 4 < *target) {
393 +               *target = source;
394 +               return 1;
395 +       }
397 +       skip = DIV_ROUND_CLOSEST(source, *target);
398 +       if (skip > max_skip)
399 +               skip = max_skip;
400 +       *target = 2 * DIV_ROUND_UP(source, 2 * skip);
402 +       return skip;
403 +}
405 +static int mt9p031_set_params(struct i2c_client *client,
406 +                               struct v4l2_rect *rect, u16 xskip, u16 yskip)
407 +{
408 +       struct mt9p031 *mt9p031 = to_mt9p031(client);
409 +       int ret;
410 +       u16 xbin, ybin;
411 +       const u16 hblank = MT9P031_H_BLANKING_VALUE,
412 +               vblank = MT9P031_V_BLANKING_VALUE;
413 +       __s32 left;
415 +       /*
416 +       * TODO: Attention! When implementing horizontal flipping, adjust
417 +       * alignment according to R2 "Column Start" description in the datasheet
418 +       */
419 +       if (xskip & 1) {
420 +               xbin = 1;
421 +               left = rect->left & (~3);
422 +       } else if (xskip & 2) {
423 +               xbin = 2;
424 +               left = rect->left & (~7);
425 +       } else {
426 +               xbin = 4;
427 +               left = rect->left & (~15);
428 +       }
429 +       ybin = min(yskip, (u16)4);
431 +       /* Disable register update, reconfigure atomically */
432 +       ret = mt9p031_set_output_control(mt9p031, 0,
433 +                                       MT9P031_OUTPUT_CONTROL_SYN);
434 +       if (ret < 0)
435 +               return ret;
437 +       dev_dbg(&client->dev, "skip %u:%u, rect %ux%u@%u:%u\n",
438 +               xskip, yskip, rect->width, rect->height, rect->left, rect->top);
440 +       /* Blanking and start values - default... */
441 +       ret = reg_write(client, MT9P031_H_BLANKING, hblank);
442 +       if (ret < 0)
443 +               return ret;
444 +       ret = reg_write(client, MT9P031_V_BLANKING, vblank);
445 +       if (ret < 0)
446 +               return ret;
448 +       ret = reg_write(client, MT9P031_COLUMN_ADDRESS_MODE,
449 +                               ((xbin - 1) << 4) | (xskip - 1));
450 +       if (ret < 0)
451 +               return ret;
452 +       ret = reg_write(client, MT9P031_ROW_ADDRESS_MODE,
453 +                               ((ybin - 1) << 4) | (yskip - 1));
454 +       if (ret < 0)
455 +               return ret;
457 +       dev_dbg(&client->dev, "new physical left %u, top %u\n",
458 +               rect->left, rect->top);
460 +       ret = reg_write(client, MT9P031_COLUMN_START,
461 +                               rect->left);
462 +       if (ret < 0)
463 +               return ret;
464 +       ret = reg_write(client, MT9P031_ROW_START,
465 +                               rect->top);
466 +       if (ret < 0)
467 +               return ret;
469 +       ret = reg_write(client, MT9P031_WINDOW_WIDTH,
470 +                               rect->width - 1);
471 +       if (ret < 0)
472 +               return ret;
473 +       ret = reg_write(client, MT9P031_WINDOW_HEIGHT,
474 +                               rect->height - 1);
475 +       if (ret < 0)
476 +               return ret;
478 +       /* Re-enable register update, commit all changes */
479 +       ret = mt9p031_set_output_control(mt9p031,
480 +                                       MT9P031_OUTPUT_CONTROL_SYN, 0);
481 +       if (ret < 0)
482 +               return ret;
484 +       mt9p031->xskip = xskip;
485 +       mt9p031->yskip = yskip;
486 +       return ret;
487 +}
489 +static int mt9p031_set_crop(struct v4l2_subdev *sd,
490 +                               struct v4l2_subdev_fh *fh,
491 +                               struct v4l2_subdev_crop *crop)
492 +{
493 +       struct mt9p031 *mt9p031 = container_of(sd, struct mt9p031, subdev);
494 +       struct v4l2_mbus_framefmt *f;
495 +       struct v4l2_rect *c;
496 +       struct v4l2_rect rect;
497 +       u16 xskip, yskip;
498 +       s32 width, height;
500 +       dev_dbg(mt9p031->subdev.v4l2_dev->dev, "%s(%ux%u@%u:%u : %u)\n",
501 +                       __func__, crop->rect.width, crop->rect.height,
502 +                       crop->rect.left, crop->rect.top, crop->which);
504 +       /*
505 +       * Clamp the crop rectangle boundaries and align them to a multiple of 2
506 +       * pixels.
507 +       */
508 +       rect.width = ALIGN(clamp(crop->rect.width,
509 +                               MT9P031_WINDOW_WIDTH_MIN,
510 +                               MT9P031_WINDOW_WIDTH_MAX), 2);
511 +       rect.height = ALIGN(clamp(crop->rect.height,
512 +                               MT9P031_WINDOW_HEIGHT_MIN,
513 +                               MT9P031_WINDOW_HEIGHT_MAX), 2);
514 +       rect.left = ALIGN(clamp(crop->rect.left,
515 +                               MT9P031_COLUMN_START_MIN,
516 +                               MT9P031_COLUMN_START_MAX), 2);
517 +       rect.top = ALIGN(clamp(crop->rect.top,
518 +                               MT9P031_ROW_START_MIN,
519 +                               MT9P031_ROW_START_MAX), 2);
521 +       c = mt9p031_get_pad_crop(mt9p031, fh, crop->pad, crop->which);
523 +       if (rect.width != c->width || rect.height != c->height) {
524 +               /*
525 +               * Reset the output image size if the crop rectangle size has
526 +               * been modified.
527 +               */
528 +               f = mt9p031_get_pad_format(mt9p031, fh, crop->pad,
529 +                                               crop->which);
530 +               width = f->width;
531 +               height = f->height;
533 +               xskip = mt9p031_skip_for_crop(rect.width, &width, 7);
534 +               yskip = mt9p031_skip_for_crop(rect.height, &height, 8);
535 +       } else {
536 +               xskip = mt9p031->xskip;
537 +               yskip = mt9p031->yskip;
538 +               f = NULL;
539 +       }
540 +       if (f) {
541 +               f->width = width;
542 +               f->height = height;
543 +       }
545 +       *c = rect;
546 +       crop->rect = rect;
548 +       mt9p031->xskip = xskip;
549 +       mt9p031->yskip = yskip;
550 +       mt9p031->rect = *c;
551 +       return 0;
552 +}
554 +static int mt9p031_get_format(struct v4l2_subdev *sd,
555 +                               struct v4l2_subdev_fh *fh,
556 +                               struct v4l2_subdev_format *fmt)
557 +{
558 +       struct mt9p031 *mt9p031 = container_of(sd, struct mt9p031, subdev);
560 +       fmt->format =
561 +               *mt9p031_get_pad_format(mt9p031, fh, fmt->pad, fmt->which);
562 +       return 0;
563 +}
565 +static u16 mt9p031_skip_for_scale(s32 *source, s32 target,
566 +                                       s32 max_skip, s32 max)
567 +{
568 +       unsigned int skip;
570 +       if (*source - *source / 4 < target) {
571 +               *source = target;
572 +               return 1;
573 +       }
575 +       skip = min(max, *source + target / 2) / target;
576 +       if (skip > max_skip)
577 +               skip = max_skip;
578 +       *source = target * skip;
580 +       return skip;
581 +}
583 +static int mt9p031_set_format(struct v4l2_subdev *sd,
584 +                               struct v4l2_subdev_fh *fh,
585 +                               struct v4l2_subdev_format *format)
586 +{
587 +       struct v4l2_mbus_framefmt *__format;
588 +       struct v4l2_rect *__crop, rect;
589 +       struct mt9p031 *mt9p031 = container_of(sd, struct mt9p031, subdev);
590 +       unsigned int width;
591 +       unsigned int height;
592 +       u16 xskip, yskip;
594 +       __crop = mt9p031_get_pad_crop(mt9p031, fh, format->pad, format->which);
596 +       width = clamp_t(int, ALIGN(format->format.width, 2), 2,
597 +                                               MT9P031_WINDOW_WIDTH_MAX);
598 +       height = clamp_t(int, ALIGN(format->format.height, 2), 2,
599 +                                               MT9P031_WINDOW_HEIGHT_MAX);
601 +       rect.width = __crop->width;
602 +       rect.height = __crop->height;
604 +       xskip = mt9p031_skip_for_scale(&rect.width, width, 7,
605 +                               MT9P031_WINDOW_WIDTH_MAX);
606 +       if (rect.width + __crop->left > MT9P031_WINDOW_WIDTH_MAX)
607 +               rect.left = (MT9P031_WINDOW_WIDTH_MAX - rect.width) / 2;
608 +       else
609 +               rect.left = __crop->left;
610 +       yskip = mt9p031_skip_for_scale(&rect.height, height, 8,
611 +                               MT9P031_WINDOW_HEIGHT_MAX);
612 +       if (rect.height + __crop->top > MT9P031_WINDOW_HEIGHT_MAX)
613 +               rect.top = (MT9P031_WINDOW_HEIGHT_MAX - rect.height) / 2;
614 +       else
615 +               rect.top = __crop->top;
617 +       dev_dbg(mt9p031->subdev.v4l2_dev->dev, "%s(%ux%u : %u)\n", __func__,
618 +               width, height, format->which);
619 +       if (__crop)
620 +               *__crop = rect;
622 +       __format = mt9p031_get_pad_format(mt9p031, fh, format->pad,
623 +                                               format->which);
624 +       __format->width = width;
625 +       __format->height = height;
626 +       format->format = *__format;
628 +       mt9p031->xskip = xskip;
629 +       mt9p031->yskip = yskip;
630 +       mt9p031->rect = *__crop;
631 +       return 0;
632 +}
634 +static int mt9p031_pll_enable(struct i2c_client *client)
635 +{
636 +       struct mt9p031 *mt9p031 = to_mt9p031(client);
637 +       int ret;
639 +       ret = reg_write(client, MT9P031_PLL_CONTROL, MT9P031_PLL_CONTROL_PWRON);
640 +       if (ret < 0)
641 +               return ret;
643 +       /* Always set the maximum frequency allowed by VDD_IO */
644 +       if (mt9p031->pdata->vdd_io == MT9P031_VDD_IO_2V8) {
645 +               ret = reg_write(client, MT9P031_PLL_CONFIG_1,
646 +                       MT9P031_PLL_CONFIG_1_M_96MHZ |
647 +                       MT9P031_PLL_CONFIG_1_N_96MHZ);
648 +               if (ret < 0)
649 +                       return ret;
650 +               ret = reg_write(client, MT9P031_PLL_CONFIG_2,
651 +                               MT9P031_PLL_CONFIG_2_P1_96MHZ);
652 +               if (ret < 0)
653 +                       return ret;
654 +       } else {
655 +               ret = reg_write(client, MT9P031_PLL_CONFIG_1,
656 +                       MT9P031_PLL_CONFIG_1_M_48MHZ |
657 +                       MT9P031_PLL_CONFIG_1_N_48MHZ);
658 +               if (ret < 0)
659 +                       return ret;
660 +               ret = reg_write(client, MT9P031_PLL_CONFIG_2,
661 +                               MT9P031_PLL_CONFIG_2_P1_48MHZ);
662 +               if (ret < 0)
663 +                       return ret;
664 +       }
665 +       mdelay(1);
666 +       ret = reg_write(client, MT9P031_PLL_CONTROL,
667 +                       MT9P031_PLL_CONTROL_PWRON |
668 +                       MT9P031_PLL_CONTROL_USEPLL);
669 +       mdelay(1);
670 +       return ret;
671 +}
673 +static inline int mt9p031_pll_disable(struct i2c_client *client)
674 +{
675 +       return reg_write(client, MT9P031_PLL_CONTROL,
676 +                        MT9P031_PLL_CONTROL_PWROFF);
677 +}
679 +static int mt9p031_s_stream(struct v4l2_subdev *sd, int enable)
680 +{
681 +       struct mt9p031 *mt9p031 = container_of(sd, struct mt9p031, subdev);
682 +       struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
683 +       struct v4l2_rect rect = mt9p031->rect;
684 +       u16 xskip = mt9p031->xskip;
685 +       u16 yskip = mt9p031->yskip;
686 +       int ret;
688 +       if (enable) {
689 +               ret = mt9p031_set_params(client, &rect, xskip, yskip);
690 +               if (ret < 0)
691 +                       return ret;
692 +               /* Switch to master "normal" mode */
693 +               ret = mt9p031_set_output_control(mt9p031, 0,
694 +                                               MT9P031_OUTPUT_CONTROL_CEN);
695 +               if (ret < 0)
696 +                       return ret;
697 +               ret = mt9p031_pll_enable(client);
698 +       } else {
699 +               /* Stop sensor readout */
700 +               ret = mt9p031_set_output_control(mt9p031,
701 +                                               MT9P031_OUTPUT_CONTROL_CEN, 0);
702 +               if (ret < 0)
703 +                       return ret;
704 +               ret = mt9p031_pll_disable(client);
705 +       }
706 +       return ret;
707 +}
709 +static int mt9p031_video_probe(struct i2c_client *client)
710 +{
711 +       s32 data;
713 +       /* Read out the chip version register */
714 +       data = reg_read(client, MT9P031_CHIP_VERSION);
715 +       if (data != MT9P031_CHIP_VERSION_VALUE) {
716 +               dev_err(&client->dev,
717 +                       "No MT9P031 chip detected, register read %x\n", data);
718 +               return -ENODEV;
719 +       }
721 +       dev_info(&client->dev, "Detected a MT9P031 chip ID %x\n", data);
723 +       return 0;
724 +}
726 +static int mt9p031_set_power(struct v4l2_subdev *sd, int on)
727 +{
728 +       struct mt9p031 *mt9p031 = container_of(sd, struct mt9p031, subdev);
729 +       int ret = 0;
731 +       mutex_lock(&mt9p031->power_lock);
733 +       /*
734 +       * If the power count is modified from 0 to != 0 or from != 0 to 0,
735 +       * update the power state.
736 +       */
737 +       if (mt9p031->power_count == !on) {
738 +               if (on) {
739 +                       ret = mt9p031_power_on(mt9p031);
740 +                       if (ret) {
741 +                               dev_err(mt9p031->subdev.v4l2_dev->dev,
742 +                               "Failed to power on: %d\n", ret);
743 +                               goto out;
744 +                       }
745 +               } else {
746 +                       mt9p031_power_off(mt9p031);
747 +               }
748 +       }
750 +       /* Update the power count. */
751 +       mt9p031->power_count += on ? 1 : -1;
752 +       WARN_ON(mt9p031->power_count < 0);
754 +out:
755 +       mutex_unlock(&mt9p031->power_lock);
756 +       return ret;
757 +}
759 +static int mt9p031_registered(struct v4l2_subdev *sd)
760 +{
761 +       struct mt9p031 *mt9p031 = container_of(sd, struct mt9p031, subdev);
762 +       struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
763 +       int ret;
765 +       ret = mt9p031_set_power(&mt9p031->subdev, 1);
766 +       if (ret) {
767 +               dev_err(&client->dev,
768 +                       "Failed to power on device: %d\n", ret);
769 +               return ret;
770 +       }
772 +       ret = mt9p031_video_probe(client);
774 +       mt9p031_set_power(&mt9p031->subdev, 0);
776 +       return ret;
777 +}
779 +static int mt9p031_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
780 +{
781 +       struct mt9p031 *mt9p031;
782 +       mt9p031 = container_of(sd, struct mt9p031, subdev);
784 +       mt9p031->rect.width     = MT9P031_WINDOW_WIDTH_DEF;
785 +       mt9p031->rect.height    = MT9P031_WINDOW_HEIGHT_DEF;
786 +       mt9p031->rect.left      = MT9P031_COLUMN_START_DEF;
787 +       mt9p031->rect.top       = MT9P031_ROW_START_DEF;
789 +       if (mt9p031->pdata->version == MT9P031_MONOCHROME_VERSION)
790 +               mt9p031->format.code = V4L2_MBUS_FMT_Y12_1X12;
791 +       else
792 +               mt9p031->format.code = V4L2_MBUS_FMT_SGRBG12_1X12;
794 +       mt9p031->format.width = MT9P031_WINDOW_WIDTH_DEF;
795 +       mt9p031->format.height = MT9P031_WINDOW_HEIGHT_DEF;
796 +       mt9p031->format.field = V4L2_FIELD_NONE;
797 +       mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB;
799 +       mt9p031->xskip = 1;
800 +       mt9p031->yskip = 1;
801 +       return mt9p031_set_power(sd, 1);
802 +}
804 +static int mt9p031_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
805 +{
806 +       return mt9p031_set_power(sd, 0);
807 +}
809 +static struct v4l2_subdev_core_ops mt9p031_subdev_core_ops = {
810 +       .s_power        = mt9p031_set_power,
811 +};
813 +static struct v4l2_subdev_video_ops mt9p031_subdev_video_ops = {
814 +       .s_stream       = mt9p031_s_stream,
815 +};
817 +static struct v4l2_subdev_pad_ops mt9p031_subdev_pad_ops = {
818 +       .enum_mbus_code = mt9p031_enum_mbus_code,
819 +       .get_fmt = mt9p031_get_format,
820 +       .set_fmt = mt9p031_set_format,
821 +       .get_crop = mt9p031_get_crop,
822 +       .set_crop = mt9p031_set_crop,
823 +};
825 +static struct v4l2_subdev_ops mt9p031_subdev_ops = {
826 +       .core   = &mt9p031_subdev_core_ops,
827 +       .video  = &mt9p031_subdev_video_ops,
828 +       .pad    = &mt9p031_subdev_pad_ops,
829 +};
831 +static const struct v4l2_subdev_internal_ops mt9p031_subdev_internal_ops = {
832 +       .registered = mt9p031_registered,
833 +       .open = mt9p031_open,
834 +       .close = mt9p031_close,
835 +};
837 +static int mt9p031_probe(struct i2c_client *client,
838 +                               const struct i2c_device_id *did)
839 +{
840 +       int ret;
841 +       struct mt9p031 *mt9p031;
842 +       struct mt9p031_platform_data *pdata = client->dev.platform_data;
843 +       struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
845 +       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
846 +               dev_warn(&adapter->dev,
847 +                       "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
848 +               return -EIO;
849 +       }
851 +       mt9p031 = kzalloc(sizeof(struct mt9p031), GFP_KERNEL);
852 +       if (!mt9p031)
853 +               return -ENOMEM;
855 +       mutex_init(&mt9p031->power_lock);
856 +       v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops);
857 +       mt9p031->subdev.internal_ops = &mt9p031_subdev_internal_ops;
859 +       mt9p031->pdata          = pdata;
861 +       mt9p031->pad.flags = MEDIA_PAD_FL_SOURCE;
862 +       ret = media_entity_init(&mt9p031->subdev.entity, 1, &mt9p031->pad, 0);
863 +       if (ret)
864 +               return ret;
866 +       mt9p031->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
868 +       return 0;
869 +}
871 +static int mt9p031_remove(struct i2c_client *client)
872 +{
873 +       struct v4l2_subdev *sd = i2c_get_clientdata(client);
874 +       struct mt9p031 *mt9p031 = container_of(sd, struct mt9p031, subdev);
876 +       v4l2_device_unregister_subdev(sd);
877 +       media_entity_cleanup(&sd->entity);
878 +       kfree(mt9p031);
880 +       return 0;
881 +}
883 +static const struct i2c_device_id mt9p031_id[] = {
884 +       { "mt9p031", 0 },
885 +       { }
886 +};
887 +MODULE_DEVICE_TABLE(i2c, mt9p031_id);
889 +static struct i2c_driver mt9p031_i2c_driver = {
890 +       .driver = {
891 +               .name = "mt9p031",
892 +       },
893 +       .probe          = mt9p031_probe,
894 +       .remove         = mt9p031_remove,
895 +       .id_table       = mt9p031_id,
896 +};
898 +static int __init mt9p031_mod_init(void)
899 +{
900 +       return i2c_add_driver(&mt9p031_i2c_driver);
901 +}
903 +static void __exit mt9p031_mod_exit(void)
904 +{
905 +       i2c_del_driver(&mt9p031_i2c_driver);
906 +}
908 +module_init(mt9p031_mod_init);
909 +module_exit(mt9p031_mod_exit);
911 +MODULE_DESCRIPTION("Aptina MT9P031 Camera driver");
912 +MODULE_AUTHOR("Bastian Hecht <hechtb@gmail.com>");
913 +MODULE_LICENSE("GPL v2");
914 diff --git a/include/media/mt9p031.h b/include/media/mt9p031.h
915 new file mode 100644
916 index 0000000..27b4c75
917 --- /dev/null
918 +++ b/include/media/mt9p031.h
919 @@ -0,0 +1,23 @@
920 +#ifndef MT9P031_H
921 +#define MT9P031_H
923 +struct v4l2_subdev;
925 +enum {
926 +       MT9P031_COLOR_VERSION = 0,
927 +       MT9P031_MONOCHROME_VERSION = 1,
928 +};
930 +enum {
931 +       MT9P031_VDD_IO_1V8 = 0,
932 +       MT9P031_VDD_IO_2V8 = 1,
933 +};
935 +struct mt9p031_platform_data {
936 +       int (*set_xclk)(struct v4l2_subdev *subdev, int hz);
937 +       int (*reset)(struct v4l2_subdev *subdev, int active);
938 +       int vdd_io; /* MT9P031_VDD_IO_1V8 or MT9P031_VDD_IO_2V8 */
939 +       int version; /* MT9P031_COLOR_VERSION or MT9P031_MONOCHROME_VERSION */
940 +};
942 +#endif
943 -- 
944 1.7.0.4
946 -- 
947 You received this message because you are subscribed to the Google Groups "Beagle Board" group.
948 To post to this group, send email to beagleboard@googlegroups.com.
949 To unsubscribe from this group, send email to beagleboard+unsubscribe@googlegroups.com.
950 For more options, visit this group at http://groups.google.com/group/beagleboard?hl=en.