diff options
Diffstat (limited to 'drivers/media/video/ov7670.c')
| -rw-r--r-- | drivers/media/video/ov7670.c | 268 |
1 files changed, 182 insertions, 86 deletions
diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c index 91c886ab15c6..c881a64b41fd 100644 --- a/drivers/media/video/ov7670.c +++ b/drivers/media/video/ov7670.c | |||
| @@ -18,8 +18,9 @@ | |||
| 18 | #include <linux/videodev2.h> | 18 | #include <linux/videodev2.h> |
| 19 | #include <media/v4l2-device.h> | 19 | #include <media/v4l2-device.h> |
| 20 | #include <media/v4l2-chip-ident.h> | 20 | #include <media/v4l2-chip-ident.h> |
| 21 | #include <media/v4l2-i2c-drv.h> | 21 | #include <media/v4l2-mediabus.h> |
| 22 | 22 | ||
| 23 | #include "ov7670.h" | ||
| 23 | 24 | ||
| 24 | MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>"); | 25 | MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>"); |
| 25 | MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors"); | 26 | MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors"); |
| @@ -43,11 +44,6 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); | |||
| 43 | #define QCIF_HEIGHT 144 | 44 | #define QCIF_HEIGHT 144 |
| 44 | 45 | ||
| 45 | /* | 46 | /* |
| 46 | * Our nominal (default) frame rate. | ||
| 47 | */ | ||
| 48 | #define OV7670_FRAME_RATE 30 | ||
| 49 | |||
| 50 | /* | ||
| 51 | * The 7670 sits on i2c with ID 0x42 | 47 | * The 7670 sits on i2c with ID 0x42 |
| 52 | */ | 48 | */ |
| 53 | #define OV7670_I2C_ADDR 0x42 | 49 | #define OV7670_I2C_ADDR 0x42 |
| @@ -198,7 +194,11 @@ struct ov7670_info { | |||
| 198 | struct ov7670_format_struct *fmt; /* Current format */ | 194 | struct ov7670_format_struct *fmt; /* Current format */ |
| 199 | unsigned char sat; /* Saturation value */ | 195 | unsigned char sat; /* Saturation value */ |
| 200 | int hue; /* Hue value */ | 196 | int hue; /* Hue value */ |
| 197 | int min_width; /* Filter out smaller sizes */ | ||
| 198 | int min_height; /* Filter out smaller sizes */ | ||
| 199 | int clock_speed; /* External clock speed (MHz) */ | ||
| 201 | u8 clkrc; /* Clock divider value */ | 200 | u8 clkrc; /* Clock divider value */ |
| 201 | bool use_smbus; /* Use smbus I/O instead of I2C */ | ||
| 202 | }; | 202 | }; |
| 203 | 203 | ||
| 204 | static inline struct ov7670_info *to_state(struct v4l2_subdev *sd) | 204 | static inline struct ov7670_info *to_state(struct v4l2_subdev *sd) |
| @@ -415,8 +415,7 @@ static struct regval_list ov7670_fmt_raw[] = { | |||
| 415 | * ov7670 is not really an SMBUS device, though, so the communication | 415 | * ov7670 is not really an SMBUS device, though, so the communication |
| 416 | * is not always entirely reliable. | 416 | * is not always entirely reliable. |
| 417 | */ | 417 | */ |
| 418 | #ifdef CONFIG_OLPC_XO_1 | 418 | static int ov7670_read_smbus(struct v4l2_subdev *sd, unsigned char reg, |
| 419 | static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg, | ||
| 420 | unsigned char *value) | 419 | unsigned char *value) |
| 421 | { | 420 | { |
| 422 | struct i2c_client *client = v4l2_get_subdevdata(sd); | 421 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
| @@ -431,7 +430,7 @@ static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg, | |||
| 431 | } | 430 | } |
| 432 | 431 | ||
| 433 | 432 | ||
| 434 | static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, | 433 | static int ov7670_write_smbus(struct v4l2_subdev *sd, unsigned char reg, |
| 435 | unsigned char value) | 434 | unsigned char value) |
| 436 | { | 435 | { |
| 437 | struct i2c_client *client = v4l2_get_subdevdata(sd); | 436 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
| @@ -442,11 +441,10 @@ static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, | |||
| 442 | return ret; | 441 | return ret; |
| 443 | } | 442 | } |
| 444 | 443 | ||
| 445 | #else /* ! CONFIG_OLPC_XO_1 */ | ||
| 446 | /* | 444 | /* |
| 447 | * On most platforms, we'd rather do straight i2c I/O. | 445 | * On most platforms, we'd rather do straight i2c I/O. |
| 448 | */ | 446 | */ |
| 449 | static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg, | 447 | static int ov7670_read_i2c(struct v4l2_subdev *sd, unsigned char reg, |
| 450 | unsigned char *value) | 448 | unsigned char *value) |
| 451 | { | 449 | { |
| 452 | struct i2c_client *client = v4l2_get_subdevdata(sd); | 450 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
| @@ -479,7 +477,7 @@ static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg, | |||
| 479 | } | 477 | } |
| 480 | 478 | ||
| 481 | 479 | ||
| 482 | static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, | 480 | static int ov7670_write_i2c(struct v4l2_subdev *sd, unsigned char reg, |
| 483 | unsigned char value) | 481 | unsigned char value) |
| 484 | { | 482 | { |
| 485 | struct i2c_client *client = v4l2_get_subdevdata(sd); | 483 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
| @@ -498,8 +496,26 @@ static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, | |||
| 498 | msleep(5); /* Wait for reset to run */ | 496 | msleep(5); /* Wait for reset to run */ |
| 499 | return ret; | 497 | return ret; |
| 500 | } | 498 | } |
| 501 | #endif /* CONFIG_OLPC_XO_1 */ | ||
| 502 | 499 | ||
| 500 | static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg, | ||
| 501 | unsigned char *value) | ||
| 502 | { | ||
| 503 | struct ov7670_info *info = to_state(sd); | ||
| 504 | if (info->use_smbus) | ||
| 505 | return ov7670_read_smbus(sd, reg, value); | ||
| 506 | else | ||
| 507 | return ov7670_read_i2c(sd, reg, value); | ||
| 508 | } | ||
| 509 | |||
| 510 | static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, | ||
| 511 | unsigned char value) | ||
| 512 | { | ||
| 513 | struct ov7670_info *info = to_state(sd); | ||
| 514 | if (info->use_smbus) | ||
| 515 | return ov7670_write_smbus(sd, reg, value); | ||
| 516 | else | ||
| 517 | return ov7670_write_i2c(sd, reg, value); | ||
| 518 | } | ||
| 503 | 519 | ||
| 504 | /* | 520 | /* |
| 505 | * Write a list of register settings; ff/ff stops the process. | 521 | * Write a list of register settings; ff/ff stops the process. |
| @@ -572,42 +588,37 @@ static int ov7670_detect(struct v4l2_subdev *sd) | |||
| 572 | /* | 588 | /* |
| 573 | * Store information about the video data format. The color matrix | 589 | * Store information about the video data format. The color matrix |
| 574 | * is deeply tied into the format, so keep the relevant values here. | 590 | * is deeply tied into the format, so keep the relevant values here. |
| 575 | * The magic matrix nubmers come from OmniVision. | 591 | * The magic matrix numbers come from OmniVision. |
| 576 | */ | 592 | */ |
| 577 | static struct ov7670_format_struct { | 593 | static struct ov7670_format_struct { |
| 578 | __u8 *desc; | 594 | enum v4l2_mbus_pixelcode mbus_code; |
| 579 | __u32 pixelformat; | 595 | enum v4l2_colorspace colorspace; |
| 580 | struct regval_list *regs; | 596 | struct regval_list *regs; |
| 581 | int cmatrix[CMATRIX_LEN]; | 597 | int cmatrix[CMATRIX_LEN]; |
| 582 | int bpp; /* Bytes per pixel */ | ||
| 583 | } ov7670_formats[] = { | 598 | } ov7670_formats[] = { |
| 584 | { | 599 | { |
| 585 | .desc = "YUYV 4:2:2", | 600 | .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, |
| 586 | .pixelformat = V4L2_PIX_FMT_YUYV, | 601 | .colorspace = V4L2_COLORSPACE_JPEG, |
| 587 | .regs = ov7670_fmt_yuv422, | 602 | .regs = ov7670_fmt_yuv422, |
| 588 | .cmatrix = { 128, -128, 0, -34, -94, 128 }, | 603 | .cmatrix = { 128, -128, 0, -34, -94, 128 }, |
| 589 | .bpp = 2, | ||
| 590 | }, | 604 | }, |
| 591 | { | 605 | { |
| 592 | .desc = "RGB 444", | 606 | .mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE, |
| 593 | .pixelformat = V4L2_PIX_FMT_RGB444, | 607 | .colorspace = V4L2_COLORSPACE_SRGB, |
| 594 | .regs = ov7670_fmt_rgb444, | 608 | .regs = ov7670_fmt_rgb444, |
| 595 | .cmatrix = { 179, -179, 0, -61, -176, 228 }, | 609 | .cmatrix = { 179, -179, 0, -61, -176, 228 }, |
| 596 | .bpp = 2, | ||
| 597 | }, | 610 | }, |
| 598 | { | 611 | { |
| 599 | .desc = "RGB 565", | 612 | .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE, |
| 600 | .pixelformat = V4L2_PIX_FMT_RGB565, | 613 | .colorspace = V4L2_COLORSPACE_SRGB, |
| 601 | .regs = ov7670_fmt_rgb565, | 614 | .regs = ov7670_fmt_rgb565, |
| 602 | .cmatrix = { 179, -179, 0, -61, -176, 228 }, | 615 | .cmatrix = { 179, -179, 0, -61, -176, 228 }, |
| 603 | .bpp = 2, | ||
| 604 | }, | 616 | }, |
| 605 | { | 617 | { |
| 606 | .desc = "Raw RGB Bayer", | 618 | .mbus_code = V4L2_MBUS_FMT_SBGGR8_1X8, |
| 607 | .pixelformat = V4L2_PIX_FMT_SBGGR8, | 619 | .colorspace = V4L2_COLORSPACE_SRGB, |
| 608 | .regs = ov7670_fmt_raw, | 620 | .regs = ov7670_fmt_raw, |
| 609 | .cmatrix = { 0, 0, 0, 0, 0, 0 }, | 621 | .cmatrix = { 0, 0, 0, 0, 0, 0 }, |
| 610 | .bpp = 1 | ||
| 611 | }, | 622 | }, |
| 612 | }; | 623 | }; |
| 613 | #define N_OV7670_FMTS ARRAY_SIZE(ov7670_formats) | 624 | #define N_OV7670_FMTS ARRAY_SIZE(ov7670_formats) |
| @@ -680,10 +691,10 @@ static struct ov7670_win_size { | |||
| 680 | .width = QVGA_WIDTH, | 691 | .width = QVGA_WIDTH, |
| 681 | .height = QVGA_HEIGHT, | 692 | .height = QVGA_HEIGHT, |
| 682 | .com7_bit = COM7_FMT_QVGA, | 693 | .com7_bit = COM7_FMT_QVGA, |
| 683 | .hstart = 164, /* Empirically determined */ | 694 | .hstart = 168, /* Empirically determined */ |
| 684 | .hstop = 20, | 695 | .hstop = 24, |
| 685 | .vstart = 14, | 696 | .vstart = 12, |
| 686 | .vstop = 494, | 697 | .vstop = 492, |
| 687 | .regs = NULL, | 698 | .regs = NULL, |
| 688 | }, | 699 | }, |
| 689 | /* QCIF */ | 700 | /* QCIF */ |
| @@ -734,51 +745,45 @@ static int ov7670_set_hw(struct v4l2_subdev *sd, int hstart, int hstop, | |||
| 734 | } | 745 | } |
| 735 | 746 | ||
| 736 | 747 | ||
| 737 | static int ov7670_enum_fmt(struct v4l2_subdev *sd, struct v4l2_fmtdesc *fmt) | 748 | static int ov7670_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, |
| 749 | enum v4l2_mbus_pixelcode *code) | ||
| 738 | { | 750 | { |
| 739 | struct ov7670_format_struct *ofmt; | 751 | if (index >= N_OV7670_FMTS) |
| 740 | |||
| 741 | if (fmt->index >= N_OV7670_FMTS) | ||
| 742 | return -EINVAL; | 752 | return -EINVAL; |
| 743 | 753 | ||
| 744 | ofmt = ov7670_formats + fmt->index; | 754 | *code = ov7670_formats[index].mbus_code; |
| 745 | fmt->flags = 0; | ||
| 746 | strcpy(fmt->description, ofmt->desc); | ||
| 747 | fmt->pixelformat = ofmt->pixelformat; | ||
| 748 | return 0; | 755 | return 0; |
| 749 | } | 756 | } |
| 750 | 757 | ||
| 751 | |||
| 752 | static int ov7670_try_fmt_internal(struct v4l2_subdev *sd, | 758 | static int ov7670_try_fmt_internal(struct v4l2_subdev *sd, |
| 753 | struct v4l2_format *fmt, | 759 | struct v4l2_mbus_framefmt *fmt, |
| 754 | struct ov7670_format_struct **ret_fmt, | 760 | struct ov7670_format_struct **ret_fmt, |
| 755 | struct ov7670_win_size **ret_wsize) | 761 | struct ov7670_win_size **ret_wsize) |
| 756 | { | 762 | { |
| 757 | int index; | 763 | int index; |
| 758 | struct ov7670_win_size *wsize; | 764 | struct ov7670_win_size *wsize; |
| 759 | struct v4l2_pix_format *pix = &fmt->fmt.pix; | ||
| 760 | 765 | ||
| 761 | for (index = 0; index < N_OV7670_FMTS; index++) | 766 | for (index = 0; index < N_OV7670_FMTS; index++) |
| 762 | if (ov7670_formats[index].pixelformat == pix->pixelformat) | 767 | if (ov7670_formats[index].mbus_code == fmt->code) |
| 763 | break; | 768 | break; |
| 764 | if (index >= N_OV7670_FMTS) { | 769 | if (index >= N_OV7670_FMTS) { |
| 765 | /* default to first format */ | 770 | /* default to first format */ |
| 766 | index = 0; | 771 | index = 0; |
| 767 | pix->pixelformat = ov7670_formats[0].pixelformat; | 772 | fmt->code = ov7670_formats[0].mbus_code; |
| 768 | } | 773 | } |
| 769 | if (ret_fmt != NULL) | 774 | if (ret_fmt != NULL) |
| 770 | *ret_fmt = ov7670_formats + index; | 775 | *ret_fmt = ov7670_formats + index; |
| 771 | /* | 776 | /* |
| 772 | * Fields: the OV devices claim to be progressive. | 777 | * Fields: the OV devices claim to be progressive. |
| 773 | */ | 778 | */ |
| 774 | pix->field = V4L2_FIELD_NONE; | 779 | fmt->field = V4L2_FIELD_NONE; |
| 775 | /* | 780 | /* |
| 776 | * Round requested image size down to the nearest | 781 | * Round requested image size down to the nearest |
| 777 | * we support, but not below the smallest. | 782 | * we support, but not below the smallest. |
| 778 | */ | 783 | */ |
| 779 | for (wsize = ov7670_win_sizes; wsize < ov7670_win_sizes + N_WIN_SIZES; | 784 | for (wsize = ov7670_win_sizes; wsize < ov7670_win_sizes + N_WIN_SIZES; |
| 780 | wsize++) | 785 | wsize++) |
| 781 | if (pix->width >= wsize->width && pix->height >= wsize->height) | 786 | if (fmt->width >= wsize->width && fmt->height >= wsize->height) |
| 782 | break; | 787 | break; |
| 783 | if (wsize >= ov7670_win_sizes + N_WIN_SIZES) | 788 | if (wsize >= ov7670_win_sizes + N_WIN_SIZES) |
| 784 | wsize--; /* Take the smallest one */ | 789 | wsize--; /* Take the smallest one */ |
| @@ -787,14 +792,14 @@ static int ov7670_try_fmt_internal(struct v4l2_subdev *sd, | |||
| 787 | /* | 792 | /* |
| 788 | * Note the size we'll actually handle. | 793 | * Note the size we'll actually handle. |
| 789 | */ | 794 | */ |
| 790 | pix->width = wsize->width; | 795 | fmt->width = wsize->width; |
| 791 | pix->height = wsize->height; | 796 | fmt->height = wsize->height; |
| 792 | pix->bytesperline = pix->width*ov7670_formats[index].bpp; | 797 | fmt->colorspace = ov7670_formats[index].colorspace; |
| 793 | pix->sizeimage = pix->height*pix->bytesperline; | ||
| 794 | return 0; | 798 | return 0; |
| 795 | } | 799 | } |
| 796 | 800 | ||
| 797 | static int ov7670_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) | 801 | static int ov7670_try_mbus_fmt(struct v4l2_subdev *sd, |
| 802 | struct v4l2_mbus_framefmt *fmt) | ||
| 798 | { | 803 | { |
| 799 | return ov7670_try_fmt_internal(sd, fmt, NULL, NULL); | 804 | return ov7670_try_fmt_internal(sd, fmt, NULL, NULL); |
| 800 | } | 805 | } |
| @@ -802,15 +807,17 @@ static int ov7670_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) | |||
| 802 | /* | 807 | /* |
| 803 | * Set a format. | 808 | * Set a format. |
| 804 | */ | 809 | */ |
| 805 | static int ov7670_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) | 810 | static int ov7670_s_mbus_fmt(struct v4l2_subdev *sd, |
| 811 | struct v4l2_mbus_framefmt *fmt) | ||
| 806 | { | 812 | { |
| 807 | int ret; | ||
| 808 | struct ov7670_format_struct *ovfmt; | 813 | struct ov7670_format_struct *ovfmt; |
| 809 | struct ov7670_win_size *wsize; | 814 | struct ov7670_win_size *wsize; |
| 810 | struct ov7670_info *info = to_state(sd); | 815 | struct ov7670_info *info = to_state(sd); |
| 811 | unsigned char com7; | 816 | unsigned char com7; |
| 817 | int ret; | ||
| 812 | 818 | ||
| 813 | ret = ov7670_try_fmt_internal(sd, fmt, &ovfmt, &wsize); | 819 | ret = ov7670_try_fmt_internal(sd, fmt, &ovfmt, &wsize); |
| 820 | |||
| 814 | if (ret) | 821 | if (ret) |
| 815 | return ret; | 822 | return ret; |
| 816 | /* | 823 | /* |
| @@ -845,7 +852,7 @@ static int ov7670_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) | |||
| 845 | */ | 852 | */ |
| 846 | if (ret == 0) | 853 | if (ret == 0) |
| 847 | ret = ov7670_write(sd, REG_CLKRC, info->clkrc); | 854 | ret = ov7670_write(sd, REG_CLKRC, info->clkrc); |
| 848 | return ret; | 855 | return 0; |
| 849 | } | 856 | } |
| 850 | 857 | ||
| 851 | /* | 858 | /* |
| @@ -863,7 +870,7 @@ static int ov7670_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) | |||
| 863 | memset(cp, 0, sizeof(struct v4l2_captureparm)); | 870 | memset(cp, 0, sizeof(struct v4l2_captureparm)); |
| 864 | cp->capability = V4L2_CAP_TIMEPERFRAME; | 871 | cp->capability = V4L2_CAP_TIMEPERFRAME; |
| 865 | cp->timeperframe.numerator = 1; | 872 | cp->timeperframe.numerator = 1; |
| 866 | cp->timeperframe.denominator = OV7670_FRAME_RATE; | 873 | cp->timeperframe.denominator = info->clock_speed; |
| 867 | if ((info->clkrc & CLK_EXT) == 0 && (info->clkrc & CLK_SCALE) > 1) | 874 | if ((info->clkrc & CLK_EXT) == 0 && (info->clkrc & CLK_SCALE) > 1) |
| 868 | cp->timeperframe.denominator /= (info->clkrc & CLK_SCALE); | 875 | cp->timeperframe.denominator /= (info->clkrc & CLK_SCALE); |
| 869 | return 0; | 876 | return 0; |
| @@ -884,26 +891,72 @@ static int ov7670_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) | |||
| 884 | if (tpf->numerator == 0 || tpf->denominator == 0) | 891 | if (tpf->numerator == 0 || tpf->denominator == 0) |
| 885 | div = 1; /* Reset to full rate */ | 892 | div = 1; /* Reset to full rate */ |
| 886 | else | 893 | else |
| 887 | div = (tpf->numerator*OV7670_FRAME_RATE)/tpf->denominator; | 894 | div = (tpf->numerator * info->clock_speed) / tpf->denominator; |
| 888 | if (div == 0) | 895 | if (div == 0) |
| 889 | div = 1; | 896 | div = 1; |
| 890 | else if (div > CLK_SCALE) | 897 | else if (div > CLK_SCALE) |
| 891 | div = CLK_SCALE; | 898 | div = CLK_SCALE; |
| 892 | info->clkrc = (info->clkrc & 0x80) | div; | 899 | info->clkrc = (info->clkrc & 0x80) | div; |
| 893 | tpf->numerator = 1; | 900 | tpf->numerator = 1; |
| 894 | tpf->denominator = OV7670_FRAME_RATE/div; | 901 | tpf->denominator = info->clock_speed / div; |
| 895 | return ov7670_write(sd, REG_CLKRC, info->clkrc); | 902 | return ov7670_write(sd, REG_CLKRC, info->clkrc); |
| 896 | } | 903 | } |
| 897 | 904 | ||
| 898 | 905 | ||
| 899 | |||
| 900 | /* | 906 | /* |
| 901 | * Code for dealing with controls. | 907 | * Frame intervals. Since frame rates are controlled with the clock |
| 908 | * divider, we can only do 30/n for integer n values. So no continuous | ||
| 909 | * or stepwise options. Here we just pick a handful of logical values. | ||
| 902 | */ | 910 | */ |
| 903 | 911 | ||
| 912 | static int ov7670_frame_rates[] = { 30, 15, 10, 5, 1 }; | ||
| 913 | |||
| 914 | static int ov7670_enum_frameintervals(struct v4l2_subdev *sd, | ||
| 915 | struct v4l2_frmivalenum *interval) | ||
| 916 | { | ||
| 917 | if (interval->index >= ARRAY_SIZE(ov7670_frame_rates)) | ||
| 918 | return -EINVAL; | ||
| 919 | interval->type = V4L2_FRMIVAL_TYPE_DISCRETE; | ||
| 920 | interval->discrete.numerator = 1; | ||
| 921 | interval->discrete.denominator = ov7670_frame_rates[interval->index]; | ||
| 922 | return 0; | ||
| 923 | } | ||
| 924 | |||
| 925 | /* | ||
| 926 | * Frame size enumeration | ||
| 927 | */ | ||
| 928 | static int ov7670_enum_framesizes(struct v4l2_subdev *sd, | ||
| 929 | struct v4l2_frmsizeenum *fsize) | ||
| 930 | { | ||
| 931 | struct ov7670_info *info = to_state(sd); | ||
| 932 | int i; | ||
| 933 | int num_valid = -1; | ||
| 934 | __u32 index = fsize->index; | ||
| 904 | 935 | ||
| 936 | /* | ||
| 937 | * If a minimum width/height was requested, filter out the capture | ||
| 938 | * windows that fall outside that. | ||
| 939 | */ | ||
| 940 | for (i = 0; i < N_WIN_SIZES; i++) { | ||
| 941 | struct ov7670_win_size *win = &ov7670_win_sizes[index]; | ||
| 942 | if (info->min_width && win->width < info->min_width) | ||
| 943 | continue; | ||
| 944 | if (info->min_height && win->height < info->min_height) | ||
| 945 | continue; | ||
| 946 | if (index == ++num_valid) { | ||
| 947 | fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; | ||
| 948 | fsize->discrete.width = win->width; | ||
| 949 | fsize->discrete.height = win->height; | ||
| 950 | return 0; | ||
| 951 | } | ||
| 952 | } | ||
| 905 | 953 | ||
| 954 | return -EINVAL; | ||
| 955 | } | ||
| 906 | 956 | ||
| 957 | /* | ||
| 958 | * Code for dealing with controls. | ||
| 959 | */ | ||
| 907 | 960 | ||
| 908 | static int ov7670_store_cmatrix(struct v4l2_subdev *sd, | 961 | static int ov7670_store_cmatrix(struct v4l2_subdev *sd, |
| 909 | int matrix[CMATRIX_LEN]) | 962 | int matrix[CMATRIX_LEN]) |
| @@ -1396,6 +1449,47 @@ static int ov7670_g_chip_ident(struct v4l2_subdev *sd, | |||
| 1396 | return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_OV7670, 0); | 1449 | return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_OV7670, 0); |
| 1397 | } | 1450 | } |
| 1398 | 1451 | ||
| 1452 | static int ov7670_s_config(struct v4l2_subdev *sd, int dumb, void *data) | ||
| 1453 | { | ||
| 1454 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
| 1455 | struct ov7670_config *config = data; | ||
| 1456 | struct ov7670_info *info = to_state(sd); | ||
| 1457 | int ret; | ||
| 1458 | |||
| 1459 | info->clock_speed = 30; /* default: a guess */ | ||
| 1460 | |||
| 1461 | /* | ||
| 1462 | * Must apply configuration before initializing device, because it | ||
| 1463 | * selects I/O method. | ||
| 1464 | */ | ||
| 1465 | if (config) { | ||
| 1466 | info->min_width = config->min_width; | ||
| 1467 | info->min_height = config->min_height; | ||
| 1468 | info->use_smbus = config->use_smbus; | ||
| 1469 | |||
| 1470 | if (config->clock_speed) | ||
| 1471 | info->clock_speed = config->clock_speed; | ||
| 1472 | } | ||
| 1473 | |||
| 1474 | /* Make sure it's an ov7670 */ | ||
| 1475 | ret = ov7670_detect(sd); | ||
| 1476 | if (ret) { | ||
| 1477 | v4l_dbg(1, debug, client, | ||
| 1478 | "chip found @ 0x%x (%s) is not an ov7670 chip.\n", | ||
| 1479 | client->addr << 1, client->adapter->name); | ||
| 1480 | kfree(info); | ||
| 1481 | return ret; | ||
| 1482 | } | ||
| 1483 | v4l_info(client, "chip found @ 0x%02x (%s)\n", | ||
| 1484 | client->addr << 1, client->adapter->name); | ||
| 1485 | |||
| 1486 | info->fmt = &ov7670_formats[0]; | ||
| 1487 | info->sat = 128; /* Review this */ | ||
| 1488 | info->clkrc = info->clock_speed / 30; | ||
| 1489 | |||
| 1490 | return 0; | ||
| 1491 | } | ||
| 1492 | |||
| 1399 | #ifdef CONFIG_VIDEO_ADV_DEBUG | 1493 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
| 1400 | static int ov7670_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) | 1494 | static int ov7670_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) |
| 1401 | { | 1495 | { |
| @@ -1434,6 +1528,7 @@ static const struct v4l2_subdev_core_ops ov7670_core_ops = { | |||
| 1434 | .s_ctrl = ov7670_s_ctrl, | 1528 | .s_ctrl = ov7670_s_ctrl, |
| 1435 | .queryctrl = ov7670_queryctrl, | 1529 | .queryctrl = ov7670_queryctrl, |
| 1436 | .reset = ov7670_reset, | 1530 | .reset = ov7670_reset, |
| 1531 | .s_config = ov7670_s_config, | ||
| 1437 | .init = ov7670_init, | 1532 | .init = ov7670_init, |
| 1438 | #ifdef CONFIG_VIDEO_ADV_DEBUG | 1533 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
| 1439 | .g_register = ov7670_g_register, | 1534 | .g_register = ov7670_g_register, |
| @@ -1442,11 +1537,13 @@ static const struct v4l2_subdev_core_ops ov7670_core_ops = { | |||
| 1442 | }; | 1537 | }; |
| 1443 | 1538 | ||
| 1444 | static const struct v4l2_subdev_video_ops ov7670_video_ops = { | 1539 | static const struct v4l2_subdev_video_ops ov7670_video_ops = { |
| 1445 | .enum_fmt = ov7670_enum_fmt, | 1540 | .enum_mbus_fmt = ov7670_enum_mbus_fmt, |
| 1446 | .try_fmt = ov7670_try_fmt, | 1541 | .try_mbus_fmt = ov7670_try_mbus_fmt, |
| 1447 | .s_fmt = ov7670_s_fmt, | 1542 | .s_mbus_fmt = ov7670_s_mbus_fmt, |
| 1448 | .s_parm = ov7670_s_parm, | 1543 | .s_parm = ov7670_s_parm, |
| 1449 | .g_parm = ov7670_g_parm, | 1544 | .g_parm = ov7670_g_parm, |
| 1545 | .enum_frameintervals = ov7670_enum_frameintervals, | ||
| 1546 | .enum_framesizes = ov7670_enum_framesizes, | ||
| 1450 | }; | 1547 | }; |
| 1451 | 1548 | ||
| 1452 | static const struct v4l2_subdev_ops ov7670_ops = { | 1549 | static const struct v4l2_subdev_ops ov7670_ops = { |
| @@ -1461,7 +1558,6 @@ static int ov7670_probe(struct i2c_client *client, | |||
| 1461 | { | 1558 | { |
| 1462 | struct v4l2_subdev *sd; | 1559 | struct v4l2_subdev *sd; |
| 1463 | struct ov7670_info *info; | 1560 | struct ov7670_info *info; |
| 1464 | int ret; | ||
| 1465 | 1561 | ||
| 1466 | info = kzalloc(sizeof(struct ov7670_info), GFP_KERNEL); | 1562 | info = kzalloc(sizeof(struct ov7670_info), GFP_KERNEL); |
| 1467 | if (info == NULL) | 1563 | if (info == NULL) |
| @@ -1469,22 +1565,6 @@ static int ov7670_probe(struct i2c_client *client, | |||
| 1469 | sd = &info->sd; | 1565 | sd = &info->sd; |
| 1470 | v4l2_i2c_subdev_init(sd, client, &ov7670_ops); | 1566 | v4l2_i2c_subdev_init(sd, client, &ov7670_ops); |
| 1471 | 1567 | ||
| 1472 | /* Make sure it's an ov7670 */ | ||
| 1473 | ret = ov7670_detect(sd); | ||
| 1474 | if (ret) { | ||
| 1475 | v4l_dbg(1, debug, client, | ||
| 1476 | "chip found @ 0x%x (%s) is not an ov7670 chip.\n", | ||
| 1477 | client->addr << 1, client->adapter->name); | ||
| 1478 | kfree(info); | ||
| 1479 | return ret; | ||
| 1480 | } | ||
| 1481 | v4l_info(client, "chip found @ 0x%02x (%s)\n", | ||
| 1482 | client->addr << 1, client->adapter->name); | ||
| 1483 | |||
| 1484 | info->fmt = &ov7670_formats[0]; | ||
| 1485 | info->sat = 128; /* Review this */ | ||
| 1486 | info->clkrc = 1; /* 30fps */ | ||
| 1487 | |||
| 1488 | return 0; | 1568 | return 0; |
| 1489 | } | 1569 | } |
| 1490 | 1570 | ||
| @@ -1504,9 +1584,25 @@ static const struct i2c_device_id ov7670_id[] = { | |||
| 1504 | }; | 1584 | }; |
| 1505 | MODULE_DEVICE_TABLE(i2c, ov7670_id); | 1585 | MODULE_DEVICE_TABLE(i2c, ov7670_id); |
| 1506 | 1586 | ||
| 1507 | static struct v4l2_i2c_driver_data v4l2_i2c_data = { | 1587 | static struct i2c_driver ov7670_driver = { |
| 1508 | .name = "ov7670", | 1588 | .driver = { |
| 1509 | .probe = ov7670_probe, | 1589 | .owner = THIS_MODULE, |
| 1510 | .remove = ov7670_remove, | 1590 | .name = "ov7670", |
| 1511 | .id_table = ov7670_id, | 1591 | }, |
| 1592 | .probe = ov7670_probe, | ||
| 1593 | .remove = ov7670_remove, | ||
| 1594 | .id_table = ov7670_id, | ||
| 1512 | }; | 1595 | }; |
| 1596 | |||
| 1597 | static __init int init_ov7670(void) | ||
| 1598 | { | ||
| 1599 | return i2c_add_driver(&ov7670_driver); | ||
| 1600 | } | ||
| 1601 | |||
| 1602 | static __exit void exit_ov7670(void) | ||
| 1603 | { | ||
| 1604 | i2c_del_driver(&ov7670_driver); | ||
| 1605 | } | ||
| 1606 | |||
| 1607 | module_init(init_ov7670); | ||
| 1608 | module_exit(exit_ov7670); | ||
