diff options
Diffstat (limited to 'drivers/media/video/mt9m001.c')
-rw-r--r-- | drivers/media/video/mt9m001.c | 142 |
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 | |||
42 | static const struct soc_camera_data_format mt9m001_colour_formats[] = { | 49 | static 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 | ||
71 | struct mt9m001 { | 78 | struct 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 | ||
197 | static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) | 206 | static 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 | ||
269 | static 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 | |||
280 | static 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 | |||
294 | static 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 | |||
239 | static int mt9m001_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) | 309 | static 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 | ||
256 | static int mt9m001_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) | 335 | static 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 | ||
557 | static void mt9m001_video_remove(struct soc_camera_device *icd) | 643 | static 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 = { | |||
578 | static struct v4l2_subdev_video_ops mt9m001_subdev_video_ops = { | 663 | static 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 | ||
585 | static struct v4l2_subdev_ops mt9m001_subdev_ops = { | 673 | static 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; |