diff options
Diffstat (limited to 'drivers/media')
-rw-r--r-- | drivers/media/video/mt9v022.c | 265 |
1 files changed, 107 insertions, 158 deletions
diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c index 53149a73874b..7e2aeda21752 100644 --- a/drivers/media/video/mt9v022.c +++ b/drivers/media/video/mt9v022.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <media/soc_mediabus.h> | 18 | #include <media/soc_mediabus.h> |
19 | #include <media/v4l2-subdev.h> | 19 | #include <media/v4l2-subdev.h> |
20 | #include <media/v4l2-chip-ident.h> | 20 | #include <media/v4l2-chip-ident.h> |
21 | #include <media/v4l2-ctrls.h> | ||
21 | 22 | ||
22 | /* | 23 | /* |
23 | * mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c | 24 | * mt9v022 i2c address 0x48, 0x4c, 0x58, 0x5c |
@@ -101,6 +102,17 @@ static const struct mt9v022_datafmt mt9v022_monochrome_fmts[] = { | |||
101 | 102 | ||
102 | struct mt9v022 { | 103 | struct mt9v022 { |
103 | struct v4l2_subdev subdev; | 104 | struct v4l2_subdev subdev; |
105 | struct v4l2_ctrl_handler hdl; | ||
106 | struct { | ||
107 | /* exposure/auto-exposure cluster */ | ||
108 | struct v4l2_ctrl *autoexposure; | ||
109 | struct v4l2_ctrl *exposure; | ||
110 | }; | ||
111 | struct { | ||
112 | /* gain/auto-gain cluster */ | ||
113 | struct v4l2_ctrl *autogain; | ||
114 | struct v4l2_ctrl *gain; | ||
115 | }; | ||
104 | struct v4l2_rect rect; /* Sensor window */ | 116 | struct v4l2_rect rect; /* Sensor window */ |
105 | const struct mt9v022_datafmt *fmt; | 117 | const struct mt9v022_datafmt *fmt; |
106 | const struct mt9v022_datafmt *fmts; | 118 | const struct mt9v022_datafmt *fmts; |
@@ -179,6 +191,8 @@ static int mt9v022_init(struct i2c_client *client) | |||
179 | ret = reg_clear(client, MT9V022_BLACK_LEVEL_CALIB_CTRL, 1); | 191 | ret = reg_clear(client, MT9V022_BLACK_LEVEL_CALIB_CTRL, 1); |
180 | if (!ret) | 192 | if (!ret) |
181 | ret = reg_write(client, MT9V022_DIGITAL_TEST_PATTERN, 0); | 193 | ret = reg_write(client, MT9V022_DIGITAL_TEST_PATTERN, 0); |
194 | if (!ret) | ||
195 | return v4l2_ctrl_handler_setup(&mt9v022->hdl); | ||
182 | 196 | ||
183 | return ret; | 197 | return ret; |
184 | } | 198 | } |
@@ -431,215 +445,117 @@ static int mt9v022_s_register(struct v4l2_subdev *sd, | |||
431 | } | 445 | } |
432 | #endif | 446 | #endif |
433 | 447 | ||
434 | static const struct v4l2_queryctrl mt9v022_controls[] = { | 448 | static int mt9v022_g_volatile_ctrl(struct v4l2_ctrl *ctrl) |
435 | { | ||
436 | .id = V4L2_CID_VFLIP, | ||
437 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
438 | .name = "Flip Vertically", | ||
439 | .minimum = 0, | ||
440 | .maximum = 1, | ||
441 | .step = 1, | ||
442 | .default_value = 0, | ||
443 | }, { | ||
444 | .id = V4L2_CID_HFLIP, | ||
445 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
446 | .name = "Flip Horizontally", | ||
447 | .minimum = 0, | ||
448 | .maximum = 1, | ||
449 | .step = 1, | ||
450 | .default_value = 0, | ||
451 | }, { | ||
452 | .id = V4L2_CID_GAIN, | ||
453 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
454 | .name = "Analog Gain", | ||
455 | .minimum = 64, | ||
456 | .maximum = 127, | ||
457 | .step = 1, | ||
458 | .default_value = 64, | ||
459 | .flags = V4L2_CTRL_FLAG_SLIDER, | ||
460 | }, { | ||
461 | .id = V4L2_CID_EXPOSURE, | ||
462 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
463 | .name = "Exposure", | ||
464 | .minimum = 1, | ||
465 | .maximum = 255, | ||
466 | .step = 1, | ||
467 | .default_value = 255, | ||
468 | .flags = V4L2_CTRL_FLAG_SLIDER, | ||
469 | }, { | ||
470 | .id = V4L2_CID_AUTOGAIN, | ||
471 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
472 | .name = "Automatic Gain", | ||
473 | .minimum = 0, | ||
474 | .maximum = 1, | ||
475 | .step = 1, | ||
476 | .default_value = 1, | ||
477 | }, { | ||
478 | .id = V4L2_CID_EXPOSURE_AUTO, | ||
479 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
480 | .name = "Automatic Exposure", | ||
481 | .minimum = 0, | ||
482 | .maximum = 1, | ||
483 | .step = 1, | ||
484 | .default_value = 1, | ||
485 | } | ||
486 | }; | ||
487 | |||
488 | static struct soc_camera_ops mt9v022_ops = { | ||
489 | .controls = mt9v022_controls, | ||
490 | .num_controls = ARRAY_SIZE(mt9v022_controls), | ||
491 | }; | ||
492 | |||
493 | static int mt9v022_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) | ||
494 | { | 449 | { |
450 | struct mt9v022 *mt9v022 = container_of(ctrl->handler, | ||
451 | struct mt9v022, hdl); | ||
452 | struct v4l2_subdev *sd = &mt9v022->subdev; | ||
495 | struct i2c_client *client = v4l2_get_subdevdata(sd); | 453 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
496 | const struct v4l2_queryctrl *qctrl; | 454 | struct v4l2_ctrl *gain = mt9v022->gain; |
455 | struct v4l2_ctrl *exp = mt9v022->exposure; | ||
497 | unsigned long range; | 456 | unsigned long range; |
498 | int data; | 457 | int data; |
499 | 458 | ||
500 | qctrl = soc_camera_find_qctrl(&mt9v022_ops, ctrl->id); | ||
501 | |||
502 | switch (ctrl->id) { | 459 | switch (ctrl->id) { |
503 | case V4L2_CID_VFLIP: | ||
504 | data = reg_read(client, MT9V022_READ_MODE); | ||
505 | if (data < 0) | ||
506 | return -EIO; | ||
507 | ctrl->value = !!(data & 0x10); | ||
508 | break; | ||
509 | case V4L2_CID_HFLIP: | ||
510 | data = reg_read(client, MT9V022_READ_MODE); | ||
511 | if (data < 0) | ||
512 | return -EIO; | ||
513 | ctrl->value = !!(data & 0x20); | ||
514 | break; | ||
515 | case V4L2_CID_EXPOSURE_AUTO: | ||
516 | data = reg_read(client, MT9V022_AEC_AGC_ENABLE); | ||
517 | if (data < 0) | ||
518 | return -EIO; | ||
519 | ctrl->value = !!(data & 0x1); | ||
520 | break; | ||
521 | case V4L2_CID_AUTOGAIN: | 460 | case V4L2_CID_AUTOGAIN: |
522 | data = reg_read(client, MT9V022_AEC_AGC_ENABLE); | ||
523 | if (data < 0) | ||
524 | return -EIO; | ||
525 | ctrl->value = !!(data & 0x2); | ||
526 | break; | ||
527 | case V4L2_CID_GAIN: | ||
528 | data = reg_read(client, MT9V022_ANALOG_GAIN); | 461 | data = reg_read(client, MT9V022_ANALOG_GAIN); |
529 | if (data < 0) | 462 | if (data < 0) |
530 | return -EIO; | 463 | return -EIO; |
531 | 464 | ||
532 | range = qctrl->maximum - qctrl->minimum; | 465 | range = gain->maximum - gain->minimum; |
533 | ctrl->value = ((data - 16) * range + 24) / 48 + qctrl->minimum; | 466 | gain->val = ((data - 16) * range + 24) / 48 + gain->minimum; |
534 | 467 | return 0; | |
535 | break; | 468 | case V4L2_CID_EXPOSURE_AUTO: |
536 | case V4L2_CID_EXPOSURE: | ||
537 | data = reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH); | 469 | data = reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH); |
538 | if (data < 0) | 470 | if (data < 0) |
539 | return -EIO; | 471 | return -EIO; |
540 | 472 | ||
541 | range = qctrl->maximum - qctrl->minimum; | 473 | range = exp->maximum - exp->minimum; |
542 | ctrl->value = ((data - 1) * range + 239) / 479 + qctrl->minimum; | 474 | exp->val = ((data - 1) * range + 239) / 479 + exp->minimum; |
543 | 475 | return 0; | |
544 | break; | ||
545 | } | 476 | } |
546 | return 0; | 477 | return -EINVAL; |
547 | } | 478 | } |
548 | 479 | ||
549 | static int mt9v022_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) | 480 | static int mt9v022_s_ctrl(struct v4l2_ctrl *ctrl) |
550 | { | 481 | { |
551 | int data; | 482 | struct mt9v022 *mt9v022 = container_of(ctrl->handler, |
483 | struct mt9v022, hdl); | ||
484 | struct v4l2_subdev *sd = &mt9v022->subdev; | ||
552 | struct i2c_client *client = v4l2_get_subdevdata(sd); | 485 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
553 | const struct v4l2_queryctrl *qctrl; | 486 | int data; |
554 | |||
555 | qctrl = soc_camera_find_qctrl(&mt9v022_ops, ctrl->id); | ||
556 | if (!qctrl) | ||
557 | return -EINVAL; | ||
558 | 487 | ||
559 | switch (ctrl->id) { | 488 | switch (ctrl->id) { |
560 | case V4L2_CID_VFLIP: | 489 | case V4L2_CID_VFLIP: |
561 | if (ctrl->value) | 490 | if (ctrl->val) |
562 | data = reg_set(client, MT9V022_READ_MODE, 0x10); | 491 | data = reg_set(client, MT9V022_READ_MODE, 0x10); |
563 | else | 492 | else |
564 | data = reg_clear(client, MT9V022_READ_MODE, 0x10); | 493 | data = reg_clear(client, MT9V022_READ_MODE, 0x10); |
565 | if (data < 0) | 494 | if (data < 0) |
566 | return -EIO; | 495 | return -EIO; |
567 | break; | 496 | return 0; |
568 | case V4L2_CID_HFLIP: | 497 | case V4L2_CID_HFLIP: |
569 | if (ctrl->value) | 498 | if (ctrl->val) |
570 | data = reg_set(client, MT9V022_READ_MODE, 0x20); | 499 | data = reg_set(client, MT9V022_READ_MODE, 0x20); |
571 | else | 500 | else |
572 | data = reg_clear(client, MT9V022_READ_MODE, 0x20); | 501 | data = reg_clear(client, MT9V022_READ_MODE, 0x20); |
573 | if (data < 0) | 502 | if (data < 0) |
574 | return -EIO; | 503 | return -EIO; |
575 | break; | 504 | return 0; |
576 | case V4L2_CID_GAIN: | 505 | case V4L2_CID_AUTOGAIN: |
577 | /* mt9v022 has minimum == default */ | 506 | if (ctrl->val) { |
578 | if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum) | 507 | if (reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0) |
579 | return -EINVAL; | 508 | return -EIO; |
580 | else { | 509 | } else { |
581 | unsigned long range = qctrl->maximum - qctrl->minimum; | 510 | struct v4l2_ctrl *gain = mt9v022->gain; |
511 | /* mt9v022 has minimum == default */ | ||
512 | unsigned long range = gain->maximum - gain->minimum; | ||
582 | /* Valid values 16 to 64, 32 to 64 must be even. */ | 513 | /* Valid values 16 to 64, 32 to 64 must be even. */ |
583 | unsigned long gain = ((ctrl->value - qctrl->minimum) * | 514 | unsigned long gain_val = ((gain->val - gain->minimum) * |
584 | 48 + range / 2) / range + 16; | 515 | 48 + range / 2) / range + 16; |
585 | if (gain >= 32) | 516 | |
586 | gain &= ~1; | 517 | if (gain_val >= 32) |
518 | gain_val &= ~1; | ||
519 | |||
587 | /* | 520 | /* |
588 | * The user wants to set gain manually, hope, she | 521 | * The user wants to set gain manually, hope, she |
589 | * knows, what she's doing... Switch AGC off. | 522 | * knows, what she's doing... Switch AGC off. |
590 | */ | 523 | */ |
591 | |||
592 | if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0) | 524 | if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0) |
593 | return -EIO; | 525 | return -EIO; |
594 | 526 | ||
595 | dev_dbg(&client->dev, "Setting gain from %d to %lu\n", | 527 | dev_dbg(&client->dev, "Setting gain from %d to %lu\n", |
596 | reg_read(client, MT9V022_ANALOG_GAIN), gain); | 528 | reg_read(client, MT9V022_ANALOG_GAIN), gain_val); |
597 | if (reg_write(client, MT9V022_ANALOG_GAIN, gain) < 0) | 529 | if (reg_write(client, MT9V022_ANALOG_GAIN, gain_val) < 0) |
598 | return -EIO; | 530 | return -EIO; |
599 | } | 531 | } |
600 | break; | 532 | return 0; |
601 | case V4L2_CID_EXPOSURE: | 533 | case V4L2_CID_EXPOSURE_AUTO: |
602 | /* mt9v022 has maximum == default */ | 534 | if (ctrl->val == V4L2_EXPOSURE_AUTO) { |
603 | if (ctrl->value > qctrl->maximum || ctrl->value < qctrl->minimum) | 535 | data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x1); |
604 | return -EINVAL; | 536 | } else { |
605 | else { | 537 | struct v4l2_ctrl *exp = mt9v022->exposure; |
606 | unsigned long range = qctrl->maximum - qctrl->minimum; | 538 | unsigned long range = exp->maximum - exp->minimum; |
607 | unsigned long shutter = ((ctrl->value - qctrl->minimum) * | 539 | unsigned long shutter = ((exp->val - exp->minimum) * |
608 | 479 + range / 2) / range + 1; | 540 | 479 + range / 2) / range + 1; |
541 | |||
609 | /* | 542 | /* |
610 | * The user wants to set shutter width manually, hope, | 543 | * The user wants to set shutter width manually, hope, |
611 | * she knows, what she's doing... Switch AEC off. | 544 | * she knows, what she's doing... Switch AEC off. |
612 | */ | 545 | */ |
613 | 546 | data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1); | |
614 | if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1) < 0) | 547 | if (data < 0) |
615 | return -EIO; | 548 | return -EIO; |
616 | |||
617 | dev_dbg(&client->dev, "Shutter width from %d to %lu\n", | 549 | dev_dbg(&client->dev, "Shutter width from %d to %lu\n", |
618 | reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH), | 550 | reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH), |
619 | shutter); | 551 | shutter); |
620 | if (reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, | 552 | if (reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, |
621 | shutter) < 0) | 553 | shutter) < 0) |
622 | return -EIO; | 554 | return -EIO; |
623 | } | 555 | } |
624 | break; | 556 | return 0; |
625 | case V4L2_CID_AUTOGAIN: | ||
626 | if (ctrl->value) | ||
627 | data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x2); | ||
628 | else | ||
629 | data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x2); | ||
630 | if (data < 0) | ||
631 | return -EIO; | ||
632 | break; | ||
633 | case V4L2_CID_EXPOSURE_AUTO: | ||
634 | if (ctrl->value) | ||
635 | data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x1); | ||
636 | else | ||
637 | data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1); | ||
638 | if (data < 0) | ||
639 | return -EIO; | ||
640 | break; | ||
641 | } | 557 | } |
642 | return 0; | 558 | return -EINVAL; |
643 | } | 559 | } |
644 | 560 | ||
645 | /* | 561 | /* |
@@ -752,9 +668,12 @@ static int mt9v022_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines) | |||
752 | return 0; | 668 | return 0; |
753 | } | 669 | } |
754 | 670 | ||
671 | static const struct v4l2_ctrl_ops mt9v022_ctrl_ops = { | ||
672 | .g_volatile_ctrl = mt9v022_g_volatile_ctrl, | ||
673 | .s_ctrl = mt9v022_s_ctrl, | ||
674 | }; | ||
675 | |||
755 | static struct v4l2_subdev_core_ops mt9v022_subdev_core_ops = { | 676 | static struct v4l2_subdev_core_ops mt9v022_subdev_core_ops = { |
756 | .g_ctrl = mt9v022_g_ctrl, | ||
757 | .s_ctrl = mt9v022_s_ctrl, | ||
758 | .g_chip_ident = mt9v022_g_chip_ident, | 677 | .g_chip_ident = mt9v022_g_chip_ident, |
759 | #ifdef CONFIG_VIDEO_ADV_DEBUG | 678 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
760 | .g_register = mt9v022_g_register, | 679 | .g_register = mt9v022_g_register, |
@@ -906,10 +825,39 @@ static int mt9v022_probe(struct i2c_client *client, | |||
906 | return -ENOMEM; | 825 | return -ENOMEM; |
907 | 826 | ||
908 | v4l2_i2c_subdev_init(&mt9v022->subdev, client, &mt9v022_subdev_ops); | 827 | v4l2_i2c_subdev_init(&mt9v022->subdev, client, &mt9v022_subdev_ops); |
828 | v4l2_ctrl_handler_init(&mt9v022->hdl, 6); | ||
829 | v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, | ||
830 | V4L2_CID_VFLIP, 0, 1, 1, 0); | ||
831 | v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, | ||
832 | V4L2_CID_HFLIP, 0, 1, 1, 0); | ||
833 | mt9v022->autogain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, | ||
834 | V4L2_CID_AUTOGAIN, 0, 1, 1, 1); | ||
835 | mt9v022->gain = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, | ||
836 | V4L2_CID_GAIN, 0, 127, 1, 64); | ||
837 | |||
838 | /* | ||
839 | * Simulated autoexposure. If enabled, we calculate shutter width | ||
840 | * ourselves in the driver based on vertical blanking and frame width | ||
841 | */ | ||
842 | mt9v022->autoexposure = v4l2_ctrl_new_std_menu(&mt9v022->hdl, | ||
843 | &mt9v022_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0, | ||
844 | V4L2_EXPOSURE_AUTO); | ||
845 | mt9v022->exposure = v4l2_ctrl_new_std(&mt9v022->hdl, &mt9v022_ctrl_ops, | ||
846 | V4L2_CID_EXPOSURE, 1, 255, 1, 255); | ||
847 | |||
848 | mt9v022->subdev.ctrl_handler = &mt9v022->hdl; | ||
849 | if (mt9v022->hdl.error) { | ||
850 | int err = mt9v022->hdl.error; | ||
851 | |||
852 | kfree(mt9v022); | ||
853 | return err; | ||
854 | } | ||
855 | v4l2_ctrl_auto_cluster(2, &mt9v022->autoexposure, | ||
856 | V4L2_EXPOSURE_MANUAL, true); | ||
857 | v4l2_ctrl_auto_cluster(2, &mt9v022->autogain, 0, true); | ||
909 | 858 | ||
910 | mt9v022->chip_control = MT9V022_CHIP_CONTROL_DEFAULT; | 859 | mt9v022->chip_control = MT9V022_CHIP_CONTROL_DEFAULT; |
911 | 860 | ||
912 | icd->ops = &mt9v022_ops; | ||
913 | /* | 861 | /* |
914 | * MT9V022 _really_ corrupts the first read out line. | 862 | * MT9V022 _really_ corrupts the first read out line. |
915 | * TODO: verify on i.MX31 | 863 | * TODO: verify on i.MX31 |
@@ -922,7 +870,7 @@ static int mt9v022_probe(struct i2c_client *client, | |||
922 | 870 | ||
923 | ret = mt9v022_video_probe(icd, client); | 871 | ret = mt9v022_video_probe(icd, client); |
924 | if (ret) { | 872 | if (ret) { |
925 | icd->ops = NULL; | 873 | v4l2_ctrl_handler_free(&mt9v022->hdl); |
926 | kfree(mt9v022); | 874 | kfree(mt9v022); |
927 | } | 875 | } |
928 | 876 | ||
@@ -934,8 +882,9 @@ static int mt9v022_remove(struct i2c_client *client) | |||
934 | struct mt9v022 *mt9v022 = to_mt9v022(client); | 882 | struct mt9v022 *mt9v022 = to_mt9v022(client); |
935 | struct soc_camera_device *icd = client->dev.platform_data; | 883 | struct soc_camera_device *icd = client->dev.platform_data; |
936 | 884 | ||
937 | icd->ops = NULL; | 885 | v4l2_device_unregister_subdev(&mt9v022->subdev); |
938 | mt9v022_video_remove(icd); | 886 | mt9v022_video_remove(icd); |
887 | v4l2_ctrl_handler_free(&mt9v022->hdl); | ||
939 | kfree(mt9v022); | 888 | kfree(mt9v022); |
940 | 889 | ||
941 | return 0; | 890 | return 0; |