diff options
Diffstat (limited to 'drivers/media/video/mt9v022.c')
-rw-r--r-- | drivers/media/video/mt9v022.c | 131 |
1 files changed, 104 insertions, 27 deletions
diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c index ab1965425289..35ea0ddd0715 100644 --- a/drivers/media/video/mt9v022.c +++ b/drivers/media/video/mt9v022.c | |||
@@ -55,6 +55,13 @@ MODULE_PARM_DESC(sensor_type, "Sensor type: \"colour\" or \"monochrome\""); | |||
55 | /* Progressive scan, master, defaults */ | 55 | /* Progressive scan, master, defaults */ |
56 | #define MT9V022_CHIP_CONTROL_DEFAULT 0x188 | 56 | #define MT9V022_CHIP_CONTROL_DEFAULT 0x188 |
57 | 57 | ||
58 | #define MT9V022_MAX_WIDTH 752 | ||
59 | #define MT9V022_MAX_HEIGHT 480 | ||
60 | #define MT9V022_MIN_WIDTH 48 | ||
61 | #define MT9V022_MIN_HEIGHT 32 | ||
62 | #define MT9V022_COLUMN_SKIP 1 | ||
63 | #define MT9V022_ROW_SKIP 4 | ||
64 | |||
58 | static const struct soc_camera_data_format mt9v022_colour_formats[] = { | 65 | static const struct soc_camera_data_format mt9v022_colour_formats[] = { |
59 | /* Order important: first natively supported, | 66 | /* Order important: first natively supported, |
60 | * second supported with a GPIO extender */ | 67 | * second supported with a GPIO extender */ |
@@ -86,6 +93,8 @@ static const struct soc_camera_data_format mt9v022_monochrome_formats[] = { | |||
86 | 93 | ||
87 | struct mt9v022 { | 94 | struct mt9v022 { |
88 | struct v4l2_subdev subdev; | 95 | struct v4l2_subdev subdev; |
96 | struct v4l2_rect rect; /* Sensor window */ | ||
97 | __u32 fourcc; | ||
89 | int model; /* V4L2_IDENT_MT9V022* codes from v4l2-chip-ident.h */ | 98 | int model; /* V4L2_IDENT_MT9V022* codes from v4l2-chip-ident.h */ |
90 | u16 chip_control; | 99 | u16 chip_control; |
91 | }; | 100 | }; |
@@ -250,44 +259,101 @@ static unsigned long mt9v022_query_bus_param(struct soc_camera_device *icd) | |||
250 | 259 | ||
251 | static int mt9v022_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) | 260 | static int mt9v022_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) |
252 | { | 261 | { |
253 | struct v4l2_rect *rect = &a->c; | ||
254 | struct i2c_client *client = sd->priv; | 262 | struct i2c_client *client = sd->priv; |
263 | struct mt9v022 *mt9v022 = to_mt9v022(client); | ||
264 | struct v4l2_rect rect = a->c; | ||
255 | struct soc_camera_device *icd = client->dev.platform_data; | 265 | struct soc_camera_device *icd = client->dev.platform_data; |
256 | int ret; | 266 | int ret; |
257 | 267 | ||
268 | /* Bayer format - even size lengths */ | ||
269 | if (mt9v022->fourcc == V4L2_PIX_FMT_SBGGR8 || | ||
270 | mt9v022->fourcc == V4L2_PIX_FMT_SBGGR16) { | ||
271 | rect.width = ALIGN(rect.width, 2); | ||
272 | rect.height = ALIGN(rect.height, 2); | ||
273 | /* Let the user play with the starting pixel */ | ||
274 | } | ||
275 | |||
276 | soc_camera_limit_side(&rect.left, &rect.width, | ||
277 | MT9V022_COLUMN_SKIP, MT9V022_MIN_WIDTH, MT9V022_MAX_WIDTH); | ||
278 | |||
279 | soc_camera_limit_side(&rect.top, &rect.height, | ||
280 | MT9V022_ROW_SKIP, MT9V022_MIN_HEIGHT, MT9V022_MAX_HEIGHT); | ||
281 | |||
258 | /* Like in example app. Contradicts the datasheet though */ | 282 | /* Like in example app. Contradicts the datasheet though */ |
259 | ret = reg_read(client, MT9V022_AEC_AGC_ENABLE); | 283 | ret = reg_read(client, MT9V022_AEC_AGC_ENABLE); |
260 | if (ret >= 0) { | 284 | if (ret >= 0) { |
261 | if (ret & 1) /* Autoexposure */ | 285 | if (ret & 1) /* Autoexposure */ |
262 | ret = reg_write(client, MT9V022_MAX_TOTAL_SHUTTER_WIDTH, | 286 | ret = reg_write(client, MT9V022_MAX_TOTAL_SHUTTER_WIDTH, |
263 | rect->height + icd->y_skip_top + 43); | 287 | rect.height + icd->y_skip_top + 43); |
264 | else | 288 | else |
265 | ret = reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, | 289 | ret = reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, |
266 | rect->height + icd->y_skip_top + 43); | 290 | rect.height + icd->y_skip_top + 43); |
267 | } | 291 | } |
268 | /* Setup frame format: defaults apart from width and height */ | 292 | /* Setup frame format: defaults apart from width and height */ |
269 | if (!ret) | 293 | if (!ret) |
270 | ret = reg_write(client, MT9V022_COLUMN_START, rect->left); | 294 | ret = reg_write(client, MT9V022_COLUMN_START, rect.left); |
271 | if (!ret) | 295 | if (!ret) |
272 | ret = reg_write(client, MT9V022_ROW_START, rect->top); | 296 | ret = reg_write(client, MT9V022_ROW_START, rect.top); |
273 | if (!ret) | 297 | if (!ret) |
274 | /* Default 94, Phytec driver says: | 298 | /* Default 94, Phytec driver says: |
275 | * "width + horizontal blank >= 660" */ | 299 | * "width + horizontal blank >= 660" */ |
276 | ret = reg_write(client, MT9V022_HORIZONTAL_BLANKING, | 300 | ret = reg_write(client, MT9V022_HORIZONTAL_BLANKING, |
277 | rect->width > 660 - 43 ? 43 : | 301 | rect.width > 660 - 43 ? 43 : |
278 | 660 - rect->width); | 302 | 660 - rect.width); |
279 | if (!ret) | 303 | if (!ret) |
280 | ret = reg_write(client, MT9V022_VERTICAL_BLANKING, 45); | 304 | ret = reg_write(client, MT9V022_VERTICAL_BLANKING, 45); |
281 | if (!ret) | 305 | if (!ret) |
282 | ret = reg_write(client, MT9V022_WINDOW_WIDTH, rect->width); | 306 | ret = reg_write(client, MT9V022_WINDOW_WIDTH, rect.width); |
283 | if (!ret) | 307 | if (!ret) |
284 | ret = reg_write(client, MT9V022_WINDOW_HEIGHT, | 308 | ret = reg_write(client, MT9V022_WINDOW_HEIGHT, |
285 | rect->height + icd->y_skip_top); | 309 | rect.height + icd->y_skip_top); |
286 | 310 | ||
287 | if (ret < 0) | 311 | if (ret < 0) |
288 | return ret; | 312 | return ret; |
289 | 313 | ||
290 | dev_dbg(&client->dev, "Frame %ux%u pixel\n", rect->width, rect->height); | 314 | dev_dbg(&client->dev, "Frame %ux%u pixel\n", rect.width, rect.height); |
315 | |||
316 | mt9v022->rect = rect; | ||
317 | |||
318 | return 0; | ||
319 | } | ||
320 | |||
321 | static int mt9v022_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) | ||
322 | { | ||
323 | struct i2c_client *client = sd->priv; | ||
324 | struct mt9v022 *mt9v022 = to_mt9v022(client); | ||
325 | |||
326 | a->c = mt9v022->rect; | ||
327 | a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
328 | |||
329 | return 0; | ||
330 | } | ||
331 | |||
332 | static int mt9v022_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) | ||
333 | { | ||
334 | a->bounds.left = MT9V022_COLUMN_SKIP; | ||
335 | a->bounds.top = MT9V022_ROW_SKIP; | ||
336 | a->bounds.width = MT9V022_MAX_WIDTH; | ||
337 | a->bounds.height = MT9V022_MAX_HEIGHT; | ||
338 | a->defrect = a->bounds; | ||
339 | a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | ||
340 | a->pixelaspect.numerator = 1; | ||
341 | a->pixelaspect.denominator = 1; | ||
342 | |||
343 | return 0; | ||
344 | } | ||
345 | |||
346 | static int mt9v022_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) | ||
347 | { | ||
348 | struct i2c_client *client = sd->priv; | ||
349 | struct mt9v022 *mt9v022 = to_mt9v022(client); | ||
350 | struct v4l2_pix_format *pix = &f->fmt.pix; | ||
351 | |||
352 | pix->width = mt9v022->rect.width; | ||
353 | pix->height = mt9v022->rect.height; | ||
354 | pix->pixelformat = mt9v022->fourcc; | ||
355 | pix->field = V4L2_FIELD_NONE; | ||
356 | pix->colorspace = V4L2_COLORSPACE_SRGB; | ||
291 | 357 | ||
292 | return 0; | 358 | return 0; |
293 | } | 359 | } |
@@ -296,16 +362,16 @@ static int mt9v022_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) | |||
296 | { | 362 | { |
297 | struct i2c_client *client = sd->priv; | 363 | struct i2c_client *client = sd->priv; |
298 | struct mt9v022 *mt9v022 = to_mt9v022(client); | 364 | struct mt9v022 *mt9v022 = to_mt9v022(client); |
299 | struct soc_camera_device *icd = client->dev.platform_data; | ||
300 | struct v4l2_pix_format *pix = &f->fmt.pix; | 365 | struct v4l2_pix_format *pix = &f->fmt.pix; |
301 | struct v4l2_crop a = { | 366 | struct v4l2_crop a = { |
302 | .c = { | 367 | .c = { |
303 | .left = icd->rect_current.left, | 368 | .left = mt9v022->rect.left, |
304 | .top = icd->rect_current.top, | 369 | .top = mt9v022->rect.top, |
305 | .width = pix->width, | 370 | .width = pix->width, |
306 | .height = pix->height, | 371 | .height = pix->height, |
307 | }, | 372 | }, |
308 | }; | 373 | }; |
374 | int ret; | ||
309 | 375 | ||
310 | /* The caller provides a supported format, as verified per call to | 376 | /* The caller provides a supported format, as verified per call to |
311 | * icd->try_fmt(), datawidth is from our supported format list */ | 377 | * icd->try_fmt(), datawidth is from our supported format list */ |
@@ -328,7 +394,14 @@ static int mt9v022_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) | |||
328 | } | 394 | } |
329 | 395 | ||
330 | /* No support for scaling on this camera, just crop. */ | 396 | /* No support for scaling on this camera, just crop. */ |
331 | return mt9v022_s_crop(sd, &a); | 397 | ret = mt9v022_s_crop(sd, &a); |
398 | if (!ret) { | ||
399 | pix->width = mt9v022->rect.width; | ||
400 | pix->height = mt9v022->rect.height; | ||
401 | mt9v022->fourcc = pix->pixelformat; | ||
402 | } | ||
403 | |||
404 | return ret; | ||
332 | } | 405 | } |
333 | 406 | ||
334 | static int mt9v022_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) | 407 | static int mt9v022_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) |
@@ -336,10 +409,13 @@ static int mt9v022_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) | |||
336 | struct i2c_client *client = sd->priv; | 409 | struct i2c_client *client = sd->priv; |
337 | struct soc_camera_device *icd = client->dev.platform_data; | 410 | struct soc_camera_device *icd = client->dev.platform_data; |
338 | struct v4l2_pix_format *pix = &f->fmt.pix; | 411 | struct v4l2_pix_format *pix = &f->fmt.pix; |
412 | int align = pix->pixelformat == V4L2_PIX_FMT_SBGGR8 || | ||
413 | pix->pixelformat == V4L2_PIX_FMT_SBGGR16; | ||
339 | 414 | ||
340 | v4l_bound_align_image(&pix->width, 48, 752, 2 /* ? */, | 415 | v4l_bound_align_image(&pix->width, MT9V022_MIN_WIDTH, |
341 | &pix->height, 32 + icd->y_skip_top, | 416 | MT9V022_MAX_WIDTH, align, |
342 | 480 + icd->y_skip_top, 0, 0); | 417 | &pix->height, MT9V022_MIN_HEIGHT + icd->y_skip_top, |
418 | MT9V022_MAX_HEIGHT + icd->y_skip_top, align, 0); | ||
343 | 419 | ||
344 | return 0; | 420 | return 0; |
345 | } | 421 | } |
@@ -669,6 +745,8 @@ static int mt9v022_video_probe(struct soc_camera_device *icd, | |||
669 | if (flags & SOCAM_DATAWIDTH_8) | 745 | if (flags & SOCAM_DATAWIDTH_8) |
670 | icd->num_formats++; | 746 | icd->num_formats++; |
671 | 747 | ||
748 | mt9v022->fourcc = icd->formats->fourcc; | ||
749 | |||
672 | dev_info(&client->dev, "Detected a MT9V022 chip ID %x, %s sensor\n", | 750 | dev_info(&client->dev, "Detected a MT9V022 chip ID %x, %s sensor\n", |
673 | data, mt9v022->model == V4L2_IDENT_MT9V022IX7ATM ? | 751 | data, mt9v022->model == V4L2_IDENT_MT9V022IX7ATM ? |
674 | "monochrome" : "colour"); | 752 | "monochrome" : "colour"); |
@@ -679,10 +757,9 @@ ei2c: | |||
679 | 757 | ||
680 | static void mt9v022_video_remove(struct soc_camera_device *icd) | 758 | static void mt9v022_video_remove(struct soc_camera_device *icd) |
681 | { | 759 | { |
682 | struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd)); | ||
683 | struct soc_camera_link *icl = to_soc_camera_link(icd); | 760 | struct soc_camera_link *icl = to_soc_camera_link(icd); |
684 | 761 | ||
685 | dev_dbg(&client->dev, "Video %x removed: %p, %p\n", client->addr, | 762 | dev_dbg(&icd->dev, "Video removed: %p, %p\n", |
686 | icd->dev.parent, icd->vdev); | 763 | icd->dev.parent, icd->vdev); |
687 | if (icl->free_bus) | 764 | if (icl->free_bus) |
688 | icl->free_bus(icl); | 765 | icl->free_bus(icl); |
@@ -701,8 +778,11 @@ static struct v4l2_subdev_core_ops mt9v022_subdev_core_ops = { | |||
701 | static struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = { | 778 | static struct v4l2_subdev_video_ops mt9v022_subdev_video_ops = { |
702 | .s_stream = mt9v022_s_stream, | 779 | .s_stream = mt9v022_s_stream, |
703 | .s_fmt = mt9v022_s_fmt, | 780 | .s_fmt = mt9v022_s_fmt, |
781 | .g_fmt = mt9v022_g_fmt, | ||
704 | .try_fmt = mt9v022_try_fmt, | 782 | .try_fmt = mt9v022_try_fmt, |
705 | .s_crop = mt9v022_s_crop, | 783 | .s_crop = mt9v022_s_crop, |
784 | .g_crop = mt9v022_g_crop, | ||
785 | .cropcap = mt9v022_cropcap, | ||
706 | }; | 786 | }; |
707 | 787 | ||
708 | static struct v4l2_subdev_ops mt9v022_subdev_ops = { | 788 | static struct v4l2_subdev_ops mt9v022_subdev_ops = { |
@@ -745,16 +825,13 @@ static int mt9v022_probe(struct i2c_client *client, | |||
745 | mt9v022->chip_control = MT9V022_CHIP_CONTROL_DEFAULT; | 825 | mt9v022->chip_control = MT9V022_CHIP_CONTROL_DEFAULT; |
746 | 826 | ||
747 | icd->ops = &mt9v022_ops; | 827 | icd->ops = &mt9v022_ops; |
748 | icd->rect_max.left = 1; | ||
749 | icd->rect_max.top = 4; | ||
750 | icd->rect_max.width = 752; | ||
751 | icd->rect_max.height = 480; | ||
752 | icd->rect_current.left = 1; | ||
753 | icd->rect_current.top = 4; | ||
754 | icd->width_min = 48; | ||
755 | icd->height_min = 32; | ||
756 | icd->y_skip_top = 1; | 828 | icd->y_skip_top = 1; |
757 | 829 | ||
830 | mt9v022->rect.left = MT9V022_COLUMN_SKIP; | ||
831 | mt9v022->rect.top = MT9V022_ROW_SKIP; | ||
832 | mt9v022->rect.width = MT9V022_MAX_WIDTH; | ||
833 | mt9v022->rect.height = MT9V022_MAX_HEIGHT; | ||
834 | |||
758 | ret = mt9v022_video_probe(icd, client); | 835 | ret = mt9v022_video_probe(icd, client); |
759 | if (ret) { | 836 | if (ret) { |
760 | icd->ops = NULL; | 837 | icd->ops = NULL; |