aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/mt9m001.c
diff options
context:
space:
mode:
authorGuennadi Liakhovetski <g.liakhovetski@gmx.de>2009-08-25 10:50:46 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2009-09-18 23:19:17 -0400
commit6a6c8786725c0b3d143674effa8b772f47b1c189 (patch)
tree8bb76c5dcbd579f13e876bd1a0bb56bee4bcebdd /drivers/media/video/mt9m001.c
parent0166b74374cae3fa8bff0caef726a3d960a9a50a (diff)
V4L/DVB (12534): soc-camera: V4L2 API compliant scaling (S_FMT) and cropping (S_CROP)
The initial soc-camera scaling and cropping implementation turned out to be incompliant with the V4L2 API, e.g., it expected the user to specify cropping in output window pixels, instead of input window pixels. This patch converts the soc-camera core and all drivers to comply with the standard. Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
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;