aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/mt9m001.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/mt9m001.c')
-rw-r--r--drivers/media/video/mt9m001.c142
1 files changed, 114 insertions, 28 deletions
diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c
index 775e1a3c98d3..e8cf56189ef1 100644
--- a/drivers/media/video/mt9m001.c
+++ b/drivers/media/video/mt9m001.c
@@ -39,6 +39,13 @@
39#define MT9M001_GLOBAL_GAIN 0x35 39#define MT9M001_GLOBAL_GAIN 0x35
40#define MT9M001_CHIP_ENABLE 0xF1 40#define MT9M001_CHIP_ENABLE 0xF1
41 41
42#define MT9M001_MAX_WIDTH 1280
43#define MT9M001_MAX_HEIGHT 1024
44#define MT9M001_MIN_WIDTH 48
45#define MT9M001_MIN_HEIGHT 32
46#define MT9M001_COLUMN_SKIP 20
47#define MT9M001_ROW_SKIP 12
48
42static const struct soc_camera_data_format mt9m001_colour_formats[] = { 49static const struct soc_camera_data_format mt9m001_colour_formats[] = {
43 /* Order important: first natively supported, 50 /* Order important: first natively supported,
44 * second supported with a GPIO extender */ 51 * second supported with a GPIO extender */
@@ -70,6 +77,8 @@ static const struct soc_camera_data_format mt9m001_monochrome_formats[] = {
70 77
71struct mt9m001 { 78struct mt9m001 {
72 struct v4l2_subdev subdev; 79 struct v4l2_subdev subdev;
80 struct v4l2_rect rect; /* Sensor window */
81 __u32 fourcc;
73 int model; /* V4L2_IDENT_MT9M001* codes from v4l2-chip-ident.h */ 82 int model; /* V4L2_IDENT_MT9M001* codes from v4l2-chip-ident.h */
74 unsigned char autoexposure; 83 unsigned char autoexposure;
75}; 84};
@@ -196,13 +205,31 @@ static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd)
196 205
197static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) 206static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
198{ 207{
199 struct v4l2_rect *rect = &a->c;
200 struct i2c_client *client = sd->priv; 208 struct i2c_client *client = sd->priv;
201 struct mt9m001 *mt9m001 = to_mt9m001(client); 209 struct mt9m001 *mt9m001 = to_mt9m001(client);
210 struct v4l2_rect rect = a->c;
202 struct soc_camera_device *icd = client->dev.platform_data; 211 struct soc_camera_device *icd = client->dev.platform_data;
203 int ret; 212 int ret;
204 const u16 hblank = 9, vblank = 25; 213 const u16 hblank = 9, vblank = 25;
205 214
215 if (mt9m001->fourcc == V4L2_PIX_FMT_SBGGR8 ||
216 mt9m001->fourcc == V4L2_PIX_FMT_SBGGR16)
217 /*
218 * Bayer format - even number of rows for simplicity,
219 * but let the user play with the top row.
220 */
221 rect.height = ALIGN(rect.height, 2);
222
223 /* Datasheet requirement: see register description */
224 rect.width = ALIGN(rect.width, 2);
225 rect.left = ALIGN(rect.left, 2);
226
227 soc_camera_limit_side(&rect.left, &rect.width,
228 MT9M001_COLUMN_SKIP, MT9M001_MIN_WIDTH, MT9M001_MAX_WIDTH);
229
230 soc_camera_limit_side(&rect.top, &rect.height,
231 MT9M001_ROW_SKIP, MT9M001_MIN_HEIGHT, MT9M001_MAX_HEIGHT);
232
206 /* Blanking and start values - default... */ 233 /* Blanking and start values - default... */
207 ret = reg_write(client, MT9M001_HORIZONTAL_BLANKING, hblank); 234 ret = reg_write(client, MT9M001_HORIZONTAL_BLANKING, hblank);
208 if (!ret) 235 if (!ret)
@@ -211,46 +238,98 @@ static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
211 /* The caller provides a supported format, as verified per 238 /* The caller provides a supported format, as verified per
212 * call to icd->try_fmt() */ 239 * call to icd->try_fmt() */
213 if (!ret) 240 if (!ret)
214 ret = reg_write(client, MT9M001_COLUMN_START, rect->left); 241 ret = reg_write(client, MT9M001_COLUMN_START, rect.left);
215 if (!ret) 242 if (!ret)
216 ret = reg_write(client, MT9M001_ROW_START, rect->top); 243 ret = reg_write(client, MT9M001_ROW_START, rect.top);
217 if (!ret) 244 if (!ret)
218 ret = reg_write(client, MT9M001_WINDOW_WIDTH, rect->width - 1); 245 ret = reg_write(client, MT9M001_WINDOW_WIDTH, rect.width - 1);
219 if (!ret) 246 if (!ret)
220 ret = reg_write(client, MT9M001_WINDOW_HEIGHT, 247 ret = reg_write(client, MT9M001_WINDOW_HEIGHT,
221 rect->height + icd->y_skip_top - 1); 248 rect.height + icd->y_skip_top - 1);
222 if (!ret && mt9m001->autoexposure) { 249 if (!ret && mt9m001->autoexposure) {
223 ret = reg_write(client, MT9M001_SHUTTER_WIDTH, 250 ret = reg_write(client, MT9M001_SHUTTER_WIDTH,
224 rect->height + icd->y_skip_top + vblank); 251 rect.height + icd->y_skip_top + vblank);
225 if (!ret) { 252 if (!ret) {
226 const struct v4l2_queryctrl *qctrl = 253 const struct v4l2_queryctrl *qctrl =
227 soc_camera_find_qctrl(icd->ops, 254 soc_camera_find_qctrl(icd->ops,
228 V4L2_CID_EXPOSURE); 255 V4L2_CID_EXPOSURE);
229 icd->exposure = (524 + (rect->height + icd->y_skip_top + 256 icd->exposure = (524 + (rect.height + icd->y_skip_top +
230 vblank - 1) * 257 vblank - 1) *
231 (qctrl->maximum - qctrl->minimum)) / 258 (qctrl->maximum - qctrl->minimum)) /
232 1048 + qctrl->minimum; 259 1048 + qctrl->minimum;
233 } 260 }
234 } 261 }
235 262
263 if (!ret)
264 mt9m001->rect = rect;
265
236 return ret; 266 return ret;
237} 267}
238 268
269static int mt9m001_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
270{
271 struct i2c_client *client = sd->priv;
272 struct mt9m001 *mt9m001 = to_mt9m001(client);
273
274 a->c = mt9m001->rect;
275 a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
276
277 return 0;
278}
279
280static int mt9m001_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
281{
282 a->bounds.left = MT9M001_COLUMN_SKIP;
283 a->bounds.top = MT9M001_ROW_SKIP;
284 a->bounds.width = MT9M001_MAX_WIDTH;
285 a->bounds.height = MT9M001_MAX_HEIGHT;
286 a->defrect = a->bounds;
287 a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
288 a->pixelaspect.numerator = 1;
289 a->pixelaspect.denominator = 1;
290
291 return 0;
292}
293
294static int mt9m001_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
295{
296 struct i2c_client *client = sd->priv;
297 struct mt9m001 *mt9m001 = to_mt9m001(client);
298 struct v4l2_pix_format *pix = &f->fmt.pix;
299
300 pix->width = mt9m001->rect.width;
301 pix->height = mt9m001->rect.height;
302 pix->pixelformat = mt9m001->fourcc;
303 pix->field = V4L2_FIELD_NONE;
304 pix->colorspace = V4L2_COLORSPACE_SRGB;
305
306 return 0;
307}
308
239static int mt9m001_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) 309static int mt9m001_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
240{ 310{
241 struct i2c_client *client = sd->priv; 311 struct i2c_client *client = sd->priv;
242 struct soc_camera_device *icd = client->dev.platform_data; 312 struct mt9m001 *mt9m001 = to_mt9m001(client);
313 struct v4l2_pix_format *pix = &f->fmt.pix;
243 struct v4l2_crop a = { 314 struct v4l2_crop a = {
244 .c = { 315 .c = {
245 .left = icd->rect_current.left, 316 .left = mt9m001->rect.left,
246 .top = icd->rect_current.top, 317 .top = mt9m001->rect.top,
247 .width = f->fmt.pix.width, 318 .width = pix->width,
248 .height = f->fmt.pix.height, 319 .height = pix->height,
249 }, 320 },
250 }; 321 };
322 int ret;
251 323
252 /* No support for scaling so far, just crop. TODO: use skipping */ 324 /* No support for scaling so far, just crop. TODO: use skipping */
253 return mt9m001_s_crop(sd, &a); 325 ret = mt9m001_s_crop(sd, &a);
326 if (!ret) {
327 pix->width = mt9m001->rect.width;
328 pix->height = mt9m001->rect.height;
329 mt9m001->fourcc = pix->pixelformat;
330 }
331
332 return ret;
254} 333}
255 334
256static int mt9m001_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) 335static int mt9m001_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
@@ -259,9 +338,14 @@ static int mt9m001_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
259 struct soc_camera_device *icd = client->dev.platform_data; 338 struct soc_camera_device *icd = client->dev.platform_data;
260 struct v4l2_pix_format *pix = &f->fmt.pix; 339 struct v4l2_pix_format *pix = &f->fmt.pix;
261 340
262 v4l_bound_align_image(&pix->width, 48, 1280, 1, 341 v4l_bound_align_image(&pix->width, MT9M001_MIN_WIDTH,
263 &pix->height, 32 + icd->y_skip_top, 342 MT9M001_MAX_WIDTH, 1,
264 1024 + icd->y_skip_top, 0, 0); 343 &pix->height, MT9M001_MIN_HEIGHT + icd->y_skip_top,
344 MT9M001_MAX_HEIGHT + icd->y_skip_top, 0, 0);
345
346 if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8 ||
347 pix->pixelformat == V4L2_PIX_FMT_SBGGR16)
348 pix->height = ALIGN(pix->height - 1, 2);
265 349
266 return 0; 350 return 0;
267} 351}
@@ -472,11 +556,11 @@ static int mt9m001_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
472 if (ctrl->value) { 556 if (ctrl->value) {
473 const u16 vblank = 25; 557 const u16 vblank = 25;
474 if (reg_write(client, MT9M001_SHUTTER_WIDTH, 558 if (reg_write(client, MT9M001_SHUTTER_WIDTH,
475 icd->rect_current.height + 559 mt9m001->rect.height +
476 icd->y_skip_top + vblank) < 0) 560 icd->y_skip_top + vblank) < 0)
477 return -EIO; 561 return -EIO;
478 qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE); 562 qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE);
479 icd->exposure = (524 + (icd->rect_current.height + 563 icd->exposure = (524 + (mt9m001->rect.height +
480 icd->y_skip_top + vblank - 1) * 564 icd->y_skip_top + vblank - 1) *
481 (qctrl->maximum - qctrl->minimum)) / 565 (qctrl->maximum - qctrl->minimum)) /
482 1048 + qctrl->minimum; 566 1048 + qctrl->minimum;
@@ -548,6 +632,8 @@ static int mt9m001_video_probe(struct soc_camera_device *icd,
548 if (flags & SOCAM_DATAWIDTH_8) 632 if (flags & SOCAM_DATAWIDTH_8)
549 icd->num_formats++; 633 icd->num_formats++;
550 634
635 mt9m001->fourcc = icd->formats->fourcc;
636
551 dev_info(&client->dev, "Detected a MT9M001 chip ID %x (%s)\n", data, 637 dev_info(&client->dev, "Detected a MT9M001 chip ID %x (%s)\n", data,
552 data == 0x8431 ? "C12STM" : "C12ST"); 638 data == 0x8431 ? "C12STM" : "C12ST");
553 639
@@ -556,10 +642,9 @@ static int mt9m001_video_probe(struct soc_camera_device *icd,
556 642
557static void mt9m001_video_remove(struct soc_camera_device *icd) 643static void mt9m001_video_remove(struct soc_camera_device *icd)
558{ 644{
559 struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd));
560 struct soc_camera_link *icl = to_soc_camera_link(icd); 645 struct soc_camera_link *icl = to_soc_camera_link(icd);
561 646
562 dev_dbg(&client->dev, "Video %x removed: %p, %p\n", client->addr, 647 dev_dbg(&icd->dev, "Video removed: %p, %p\n",
563 icd->dev.parent, icd->vdev); 648 icd->dev.parent, icd->vdev);
564 if (icl->free_bus) 649 if (icl->free_bus)
565 icl->free_bus(icl); 650 icl->free_bus(icl);
@@ -578,8 +663,11 @@ static struct v4l2_subdev_core_ops mt9m001_subdev_core_ops = {
578static struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = { 663static struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = {
579 .s_stream = mt9m001_s_stream, 664 .s_stream = mt9m001_s_stream,
580 .s_fmt = mt9m001_s_fmt, 665 .s_fmt = mt9m001_s_fmt,
666 .g_fmt = mt9m001_g_fmt,
581 .try_fmt = mt9m001_try_fmt, 667 .try_fmt = mt9m001_try_fmt,
582 .s_crop = mt9m001_s_crop, 668 .s_crop = mt9m001_s_crop,
669 .g_crop = mt9m001_g_crop,
670 .cropcap = mt9m001_cropcap,
583}; 671};
584 672
585static struct v4l2_subdev_ops mt9m001_subdev_ops = { 673static struct v4l2_subdev_ops mt9m001_subdev_ops = {
@@ -621,15 +709,13 @@ static int mt9m001_probe(struct i2c_client *client,
621 709
622 /* Second stage probe - when a capture adapter is there */ 710 /* Second stage probe - when a capture adapter is there */
623 icd->ops = &mt9m001_ops; 711 icd->ops = &mt9m001_ops;
624 icd->rect_max.left = 20;
625 icd->rect_max.top = 12;
626 icd->rect_max.width = 1280;
627 icd->rect_max.height = 1024;
628 icd->rect_current.left = 20;
629 icd->rect_current.top = 12;
630 icd->width_min = 48;
631 icd->height_min = 32;
632 icd->y_skip_top = 1; 712 icd->y_skip_top = 1;
713
714 mt9m001->rect.left = MT9M001_COLUMN_SKIP;
715 mt9m001->rect.top = MT9M001_ROW_SKIP;
716 mt9m001->rect.width = MT9M001_MAX_WIDTH;
717 mt9m001->rect.height = MT9M001_MAX_HEIGHT;
718
633 /* Simulated autoexposure. If enabled, we calculate shutter width 719 /* Simulated autoexposure. If enabled, we calculate shutter width
634 * ourselves in the driver based on vertical blanking and frame width */ 720 * ourselves in the driver based on vertical blanking and frame width */
635 mt9m001->autoexposure = 1; 721 mt9m001->autoexposure = 1;