| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * V4L2 subdevice driver for OmniVision OV6650 Camera Sensor |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 15 | 16 | * Copyright (C) 2008 Magnus Damm |
|---|
| 16 | 17 | * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> |
|---|
| 17 | 18 | * |
|---|
| 18 | | - * Hardware specific bits initialy based on former work by Matt Callow |
|---|
| 19 | + * Hardware specific bits initially based on former work by Matt Callow |
|---|
| 19 | 20 | * drivers/media/video/omap/sensor_ov6650.c |
|---|
| 20 | 21 | * Copyright (C) 2006 Matt Callow |
|---|
| 21 | | - * |
|---|
| 22 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 23 | | - * it under the terms of the GNU General Public License version 2 as |
|---|
| 24 | | - * published by the Free Software Foundation. |
|---|
| 25 | 22 | */ |
|---|
| 26 | 23 | |
|---|
| 27 | 24 | #include <linux/bitops.h> |
|---|
| .. | .. |
|---|
| 127 | 124 | |
|---|
| 128 | 125 | #define DEF_AECH 0x4D |
|---|
| 129 | 126 | |
|---|
| 130 | | -#define CLKRC_6MHz 0x00 |
|---|
| 127 | +#define CLKRC_8MHz 0x00 |
|---|
| 131 | 128 | #define CLKRC_12MHz 0x40 |
|---|
| 132 | 129 | #define CLKRC_16MHz 0x80 |
|---|
| 133 | 130 | #define CLKRC_24MHz 0xc0 |
|---|
| 134 | 131 | #define CLKRC_DIV_MASK 0x3f |
|---|
| 135 | 132 | #define GET_CLKRC_DIV(x) (((x) & CLKRC_DIV_MASK) + 1) |
|---|
| 133 | +#define DEF_CLKRC 0x00 |
|---|
| 136 | 134 | |
|---|
| 137 | 135 | #define COMA_RESET BIT(7) |
|---|
| 138 | 136 | #define COMA_QCIF BIT(5) |
|---|
| .. | .. |
|---|
| 199 | 197 | struct v4l2_clk *clk; |
|---|
| 200 | 198 | bool half_scale; /* scale down output by 2 */ |
|---|
| 201 | 199 | struct v4l2_rect rect; /* sensor cropping window */ |
|---|
| 202 | | - unsigned long pclk_limit; /* from host */ |
|---|
| 203 | | - unsigned long pclk_max; /* from resolution and format */ |
|---|
| 204 | 200 | struct v4l2_fract tpf; /* as requested with s_frame_interval */ |
|---|
| 205 | 201 | u32 code; |
|---|
| 206 | 202 | }; |
|---|
| 207 | 203 | |
|---|
| 204 | +struct ov6650_xclk { |
|---|
| 205 | + unsigned long rate; |
|---|
| 206 | + u8 clkrc; |
|---|
| 207 | +}; |
|---|
| 208 | + |
|---|
| 209 | +static const struct ov6650_xclk ov6650_xclk[] = { |
|---|
| 210 | +{ |
|---|
| 211 | + .rate = 8000000, |
|---|
| 212 | + .clkrc = CLKRC_8MHz, |
|---|
| 213 | +}, |
|---|
| 214 | +{ |
|---|
| 215 | + .rate = 12000000, |
|---|
| 216 | + .clkrc = CLKRC_12MHz, |
|---|
| 217 | +}, |
|---|
| 218 | +{ |
|---|
| 219 | + .rate = 16000000, |
|---|
| 220 | + .clkrc = CLKRC_16MHz, |
|---|
| 221 | +}, |
|---|
| 222 | +{ |
|---|
| 223 | + .rate = 24000000, |
|---|
| 224 | + .clkrc = CLKRC_24MHz, |
|---|
| 225 | +}, |
|---|
| 226 | +}; |
|---|
| 208 | 227 | |
|---|
| 209 | 228 | static u32 ov6650_codes[] = { |
|---|
| 210 | 229 | MEDIA_BUS_FMT_YUYV8_2X8, |
|---|
| .. | .. |
|---|
| 459 | 478 | |
|---|
| 460 | 479 | switch (sel->target) { |
|---|
| 461 | 480 | case V4L2_SEL_TGT_CROP_BOUNDS: |
|---|
| 462 | | - case V4L2_SEL_TGT_CROP_DEFAULT: |
|---|
| 463 | 481 | sel->r.left = DEF_HSTRT << 1; |
|---|
| 464 | 482 | sel->r.top = DEF_VSTRT << 1; |
|---|
| 465 | 483 | sel->r.width = W_CIF; |
|---|
| .. | .. |
|---|
| 549 | 567 | return width > rect->width >> 1 || height > rect->height >> 1; |
|---|
| 550 | 568 | } |
|---|
| 551 | 569 | |
|---|
| 552 | | -static u8 to_clkrc(struct v4l2_fract *timeperframe, |
|---|
| 553 | | - unsigned long pclk_limit, unsigned long pclk_max) |
|---|
| 554 | | -{ |
|---|
| 555 | | - unsigned long pclk; |
|---|
| 556 | | - |
|---|
| 557 | | - if (timeperframe->numerator && timeperframe->denominator) |
|---|
| 558 | | - pclk = pclk_max * timeperframe->denominator / |
|---|
| 559 | | - (FRAME_RATE_MAX * timeperframe->numerator); |
|---|
| 560 | | - else |
|---|
| 561 | | - pclk = pclk_max; |
|---|
| 562 | | - |
|---|
| 563 | | - if (pclk_limit && pclk_limit < pclk) |
|---|
| 564 | | - pclk = pclk_limit; |
|---|
| 565 | | - |
|---|
| 566 | | - return (pclk_max - 1) / pclk; |
|---|
| 567 | | -} |
|---|
| 570 | +#define to_clkrc(div) ((div) - 1) |
|---|
| 568 | 571 | |
|---|
| 569 | 572 | /* set the format we will capture in */ |
|---|
| 570 | 573 | static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) |
|---|
| .. | .. |
|---|
| 583 | 586 | .r.height = mf->height << half_scale, |
|---|
| 584 | 587 | }; |
|---|
| 585 | 588 | u32 code = mf->code; |
|---|
| 586 | | - unsigned long mclk, pclk; |
|---|
| 587 | | - u8 coma_set = 0, coma_mask = 0, coml_set, coml_mask, clkrc; |
|---|
| 589 | + u8 coma_set = 0, coma_mask = 0, coml_set, coml_mask; |
|---|
| 588 | 590 | int ret; |
|---|
| 589 | 591 | |
|---|
| 590 | 592 | /* select color matrix configuration for given color encoding */ |
|---|
| .. | .. |
|---|
| 638 | 640 | code == MEDIA_BUS_FMT_SBGGR8_1X8) { |
|---|
| 639 | 641 | coml_mask = COML_ONE_CHANNEL; |
|---|
| 640 | 642 | coml_set = 0; |
|---|
| 641 | | - priv->pclk_max = 4000000; |
|---|
| 642 | 643 | } else { |
|---|
| 643 | 644 | coml_mask = 0; |
|---|
| 644 | 645 | coml_set = COML_ONE_CHANNEL; |
|---|
| 645 | | - priv->pclk_max = 8000000; |
|---|
| 646 | 646 | } |
|---|
| 647 | 647 | |
|---|
| 648 | 648 | if (half_scale) { |
|---|
| 649 | 649 | dev_dbg(&client->dev, "max resolution: QCIF\n"); |
|---|
| 650 | 650 | coma_set |= COMA_QCIF; |
|---|
| 651 | | - priv->pclk_max /= 2; |
|---|
| 652 | 651 | } else { |
|---|
| 653 | 652 | dev_dbg(&client->dev, "max resolution: CIF\n"); |
|---|
| 654 | 653 | coma_mask |= COMA_QCIF; |
|---|
| 655 | 654 | } |
|---|
| 656 | 655 | |
|---|
| 657 | | - clkrc = CLKRC_12MHz; |
|---|
| 658 | | - mclk = 12000000; |
|---|
| 659 | | - priv->pclk_limit = 1334000; |
|---|
| 660 | | - dev_dbg(&client->dev, "using 12MHz input clock\n"); |
|---|
| 661 | | - |
|---|
| 662 | | - clkrc |= to_clkrc(&priv->tpf, priv->pclk_limit, priv->pclk_max); |
|---|
| 663 | | - |
|---|
| 664 | | - pclk = priv->pclk_max / GET_CLKRC_DIV(clkrc); |
|---|
| 665 | | - dev_dbg(&client->dev, "pixel clock divider: %ld.%ld\n", |
|---|
| 666 | | - mclk / pclk, 10 * mclk % pclk / pclk); |
|---|
| 667 | | - |
|---|
| 668 | 656 | ret = ov6650_set_selection(sd, NULL, &sel); |
|---|
| 669 | 657 | if (!ret) |
|---|
| 670 | 658 | ret = ov6650_reg_rmw(client, REG_COMA, coma_set, coma_mask); |
|---|
| 671 | | - if (!ret) |
|---|
| 672 | | - ret = ov6650_reg_write(client, REG_CLKRC, clkrc); |
|---|
| 673 | 659 | if (!ret) { |
|---|
| 674 | 660 | priv->half_scale = half_scale; |
|---|
| 675 | 661 | |
|---|
| .. | .. |
|---|
| 699 | 685 | switch (mf->code) { |
|---|
| 700 | 686 | case MEDIA_BUS_FMT_Y10_1X10: |
|---|
| 701 | 687 | mf->code = MEDIA_BUS_FMT_Y8_1X8; |
|---|
| 702 | | - /* fall through */ |
|---|
| 688 | + fallthrough; |
|---|
| 703 | 689 | case MEDIA_BUS_FMT_Y8_1X8: |
|---|
| 704 | 690 | case MEDIA_BUS_FMT_YVYU8_2X8: |
|---|
| 705 | 691 | case MEDIA_BUS_FMT_YUYV8_2X8: |
|---|
| .. | .. |
|---|
| 708 | 694 | break; |
|---|
| 709 | 695 | default: |
|---|
| 710 | 696 | mf->code = MEDIA_BUS_FMT_SBGGR8_1X8; |
|---|
| 711 | | - /* fall through */ |
|---|
| 697 | + fallthrough; |
|---|
| 712 | 698 | case MEDIA_BUS_FMT_SBGGR8_1X8: |
|---|
| 713 | 699 | break; |
|---|
| 714 | 700 | } |
|---|
| .. | .. |
|---|
| 758 | 744 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
|---|
| 759 | 745 | struct ov6650 *priv = to_ov6650(client); |
|---|
| 760 | 746 | |
|---|
| 761 | | - ival->interval.numerator = GET_CLKRC_DIV(to_clkrc(&priv->tpf, |
|---|
| 762 | | - priv->pclk_limit, priv->pclk_max)); |
|---|
| 763 | | - ival->interval.denominator = FRAME_RATE_MAX; |
|---|
| 747 | + ival->interval = priv->tpf; |
|---|
| 764 | 748 | |
|---|
| 765 | 749 | dev_dbg(&client->dev, "Frame interval: %u/%u s\n", |
|---|
| 766 | 750 | ival->interval.numerator, ival->interval.denominator); |
|---|
| .. | .. |
|---|
| 775 | 759 | struct ov6650 *priv = to_ov6650(client); |
|---|
| 776 | 760 | struct v4l2_fract *tpf = &ival->interval; |
|---|
| 777 | 761 | int div, ret; |
|---|
| 778 | | - u8 clkrc; |
|---|
| 779 | 762 | |
|---|
| 780 | 763 | if (tpf->numerator == 0 || tpf->denominator == 0) |
|---|
| 781 | 764 | div = 1; /* Reset to full rate */ |
|---|
| .. | .. |
|---|
| 787 | 770 | else if (div > GET_CLKRC_DIV(CLKRC_DIV_MASK)) |
|---|
| 788 | 771 | div = GET_CLKRC_DIV(CLKRC_DIV_MASK); |
|---|
| 789 | 772 | |
|---|
| 790 | | - /* |
|---|
| 791 | | - * Keep result to be used as tpf limit |
|---|
| 792 | | - * for subseqent clock divider calculations |
|---|
| 793 | | - */ |
|---|
| 794 | | - priv->tpf.numerator = div; |
|---|
| 795 | | - priv->tpf.denominator = FRAME_RATE_MAX; |
|---|
| 796 | | - |
|---|
| 797 | | - clkrc = to_clkrc(&priv->tpf, priv->pclk_limit, priv->pclk_max); |
|---|
| 798 | | - |
|---|
| 799 | | - ret = ov6650_reg_rmw(client, REG_CLKRC, clkrc, CLKRC_DIV_MASK); |
|---|
| 773 | + ret = ov6650_reg_rmw(client, REG_CLKRC, to_clkrc(div), CLKRC_DIV_MASK); |
|---|
| 800 | 774 | if (!ret) { |
|---|
| 801 | | - tpf->numerator = GET_CLKRC_DIV(clkrc); |
|---|
| 802 | | - tpf->denominator = FRAME_RATE_MAX; |
|---|
| 775 | + priv->tpf.numerator = div; |
|---|
| 776 | + priv->tpf.denominator = FRAME_RATE_MAX; |
|---|
| 777 | + |
|---|
| 778 | + *tpf = priv->tpf; |
|---|
| 803 | 779 | } |
|---|
| 804 | 780 | |
|---|
| 805 | 781 | return ret; |
|---|
| .. | .. |
|---|
| 821 | 797 | } |
|---|
| 822 | 798 | |
|---|
| 823 | 799 | /* program default register values */ |
|---|
| 824 | | -static int ov6650_prog_dflt(struct i2c_client *client) |
|---|
| 800 | +static int ov6650_prog_dflt(struct i2c_client *client, u8 clkrc) |
|---|
| 825 | 801 | { |
|---|
| 826 | 802 | int ret; |
|---|
| 827 | 803 | |
|---|
| .. | .. |
|---|
| 829 | 805 | |
|---|
| 830 | 806 | ret = ov6650_reg_write(client, REG_COMA, 0); /* ~COMA_RESET */ |
|---|
| 831 | 807 | if (!ret) |
|---|
| 808 | + ret = ov6650_reg_write(client, REG_CLKRC, clkrc); |
|---|
| 809 | + if (!ret) |
|---|
| 832 | 810 | ret = ov6650_reg_rmw(client, REG_COMB, 0, COMB_BAND_FILTER); |
|---|
| 833 | 811 | |
|---|
| 834 | 812 | return ret; |
|---|
| 835 | 813 | } |
|---|
| 836 | 814 | |
|---|
| 837 | | -static int ov6650_video_probe(struct i2c_client *client) |
|---|
| 815 | +static int ov6650_video_probe(struct v4l2_subdev *sd) |
|---|
| 838 | 816 | { |
|---|
| 817 | + struct i2c_client *client = v4l2_get_subdevdata(sd); |
|---|
| 839 | 818 | struct ov6650 *priv = to_ov6650(client); |
|---|
| 840 | | - u8 pidh, pidl, midh, midl; |
|---|
| 841 | | - int ret; |
|---|
| 819 | + const struct ov6650_xclk *xclk = NULL; |
|---|
| 820 | + unsigned long rate; |
|---|
| 821 | + u8 pidh, pidl, midh, midl; |
|---|
| 822 | + int i, ret = 0; |
|---|
| 842 | 823 | |
|---|
| 843 | 824 | priv->clk = v4l2_clk_get(&client->dev, NULL); |
|---|
| 844 | 825 | if (IS_ERR(priv->clk)) { |
|---|
| .. | .. |
|---|
| 847 | 828 | return ret; |
|---|
| 848 | 829 | } |
|---|
| 849 | 830 | |
|---|
| 850 | | - ret = ov6650_s_power(&priv->subdev, 1); |
|---|
| 831 | + rate = v4l2_clk_get_rate(priv->clk); |
|---|
| 832 | + for (i = 0; rate && i < ARRAY_SIZE(ov6650_xclk); i++) { |
|---|
| 833 | + if (rate != ov6650_xclk[i].rate) |
|---|
| 834 | + continue; |
|---|
| 835 | + |
|---|
| 836 | + xclk = &ov6650_xclk[i]; |
|---|
| 837 | + dev_info(&client->dev, "using host default clock rate %lukHz\n", |
|---|
| 838 | + rate / 1000); |
|---|
| 839 | + break; |
|---|
| 840 | + } |
|---|
| 841 | + for (i = 0; !xclk && i < ARRAY_SIZE(ov6650_xclk); i++) { |
|---|
| 842 | + ret = v4l2_clk_set_rate(priv->clk, ov6650_xclk[i].rate); |
|---|
| 843 | + if (ret || v4l2_clk_get_rate(priv->clk) != ov6650_xclk[i].rate) |
|---|
| 844 | + continue; |
|---|
| 845 | + |
|---|
| 846 | + xclk = &ov6650_xclk[i]; |
|---|
| 847 | + dev_info(&client->dev, "using negotiated clock rate %lukHz\n", |
|---|
| 848 | + xclk->rate / 1000); |
|---|
| 849 | + break; |
|---|
| 850 | + } |
|---|
| 851 | + if (!xclk) { |
|---|
| 852 | + dev_err(&client->dev, "unable to get supported clock rate\n"); |
|---|
| 853 | + if (!ret) |
|---|
| 854 | + ret = -EINVAL; |
|---|
| 855 | + goto eclkput; |
|---|
| 856 | + } |
|---|
| 857 | + |
|---|
| 858 | + ret = ov6650_s_power(sd, 1); |
|---|
| 851 | 859 | if (ret < 0) |
|---|
| 852 | 860 | goto eclkput; |
|---|
| 853 | 861 | |
|---|
| .. | .. |
|---|
| 880 | 888 | |
|---|
| 881 | 889 | ret = ov6650_reset(client); |
|---|
| 882 | 890 | if (!ret) |
|---|
| 883 | | - ret = ov6650_prog_dflt(client); |
|---|
| 891 | + ret = ov6650_prog_dflt(client, xclk->clkrc); |
|---|
| 892 | + if (!ret) { |
|---|
| 893 | + struct v4l2_mbus_framefmt mf = ov6650_def_fmt; |
|---|
| 894 | + |
|---|
| 895 | + ret = ov6650_s_fmt(sd, &mf); |
|---|
| 896 | + } |
|---|
| 884 | 897 | if (!ret) |
|---|
| 885 | 898 | ret = v4l2_ctrl_handler_setup(&priv->hdl); |
|---|
| 886 | 899 | |
|---|
| 887 | 900 | done: |
|---|
| 888 | | - ov6650_s_power(&priv->subdev, 0); |
|---|
| 901 | + ov6650_s_power(sd, 0); |
|---|
| 889 | 902 | if (!ret) |
|---|
| 890 | 903 | return 0; |
|---|
| 891 | 904 | eclkput: |
|---|
| .. | .. |
|---|
| 908 | 921 | }; |
|---|
| 909 | 922 | |
|---|
| 910 | 923 | /* Request bus settings on camera side */ |
|---|
| 911 | | -static int ov6650_g_mbus_config(struct v4l2_subdev *sd, |
|---|
| 912 | | - struct v4l2_mbus_config *cfg) |
|---|
| 924 | +static int ov6650_get_mbus_config(struct v4l2_subdev *sd, |
|---|
| 925 | + unsigned int pad, |
|---|
| 926 | + struct v4l2_mbus_config *cfg) |
|---|
| 913 | 927 | { |
|---|
| 928 | + struct i2c_client *client = v4l2_get_subdevdata(sd); |
|---|
| 929 | + u8 comj, comf; |
|---|
| 930 | + int ret; |
|---|
| 914 | 931 | |
|---|
| 915 | | - cfg->flags = V4L2_MBUS_MASTER | |
|---|
| 916 | | - V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | |
|---|
| 917 | | - V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_LOW | |
|---|
| 918 | | - V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | |
|---|
| 919 | | - V4L2_MBUS_DATA_ACTIVE_HIGH; |
|---|
| 932 | + ret = ov6650_reg_read(client, REG_COMJ, &comj); |
|---|
| 933 | + if (ret) |
|---|
| 934 | + return ret; |
|---|
| 935 | + |
|---|
| 936 | + ret = ov6650_reg_read(client, REG_COMF, &comf); |
|---|
| 937 | + if (ret) |
|---|
| 938 | + return ret; |
|---|
| 939 | + |
|---|
| 940 | + cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_DATA_ACTIVE_HIGH |
|---|
| 941 | + | ((comj & COMJ_VSYNC_HIGH) ? V4L2_MBUS_VSYNC_ACTIVE_HIGH |
|---|
| 942 | + : V4L2_MBUS_VSYNC_ACTIVE_LOW) |
|---|
| 943 | + | ((comf & COMF_HREF_LOW) ? V4L2_MBUS_HSYNC_ACTIVE_LOW |
|---|
| 944 | + : V4L2_MBUS_HSYNC_ACTIVE_HIGH) |
|---|
| 945 | + | ((comj & COMJ_PCLK_RISING) ? V4L2_MBUS_PCLK_SAMPLE_RISING |
|---|
| 946 | + : V4L2_MBUS_PCLK_SAMPLE_FALLING); |
|---|
| 920 | 947 | cfg->type = V4L2_MBUS_PARALLEL; |
|---|
| 921 | 948 | |
|---|
| 922 | 949 | return 0; |
|---|
| 923 | 950 | } |
|---|
| 924 | 951 | |
|---|
| 925 | 952 | /* Alter bus settings on camera side */ |
|---|
| 926 | | -static int ov6650_s_mbus_config(struct v4l2_subdev *sd, |
|---|
| 927 | | - const struct v4l2_mbus_config *cfg) |
|---|
| 953 | +static int ov6650_set_mbus_config(struct v4l2_subdev *sd, |
|---|
| 954 | + unsigned int pad, |
|---|
| 955 | + struct v4l2_mbus_config *cfg) |
|---|
| 928 | 956 | { |
|---|
| 929 | 957 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
|---|
| 930 | | - int ret; |
|---|
| 958 | + int ret = 0; |
|---|
| 931 | 959 | |
|---|
| 932 | 960 | if (cfg->flags & V4L2_MBUS_PCLK_SAMPLE_RISING) |
|---|
| 933 | 961 | ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_PCLK_RISING, 0); |
|---|
| 934 | | - else |
|---|
| 962 | + else if (cfg->flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) |
|---|
| 935 | 963 | ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_PCLK_RISING); |
|---|
| 936 | 964 | if (ret) |
|---|
| 937 | 965 | return ret; |
|---|
| 938 | 966 | |
|---|
| 939 | 967 | if (cfg->flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) |
|---|
| 940 | 968 | ret = ov6650_reg_rmw(client, REG_COMF, COMF_HREF_LOW, 0); |
|---|
| 941 | | - else |
|---|
| 969 | + else if (cfg->flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) |
|---|
| 942 | 970 | ret = ov6650_reg_rmw(client, REG_COMF, 0, COMF_HREF_LOW); |
|---|
| 943 | 971 | if (ret) |
|---|
| 944 | 972 | return ret; |
|---|
| 945 | 973 | |
|---|
| 946 | 974 | if (cfg->flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) |
|---|
| 947 | 975 | ret = ov6650_reg_rmw(client, REG_COMJ, COMJ_VSYNC_HIGH, 0); |
|---|
| 948 | | - else |
|---|
| 976 | + else if (cfg->flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) |
|---|
| 949 | 977 | ret = ov6650_reg_rmw(client, REG_COMJ, 0, COMJ_VSYNC_HIGH); |
|---|
| 978 | + if (ret) |
|---|
| 979 | + return ret; |
|---|
| 950 | 980 | |
|---|
| 951 | | - return ret; |
|---|
| 981 | + /* |
|---|
| 982 | + * Update the configuration to report what is actually applied to |
|---|
| 983 | + * the hardware. |
|---|
| 984 | + */ |
|---|
| 985 | + return ov6650_get_mbus_config(sd, pad, cfg); |
|---|
| 952 | 986 | } |
|---|
| 953 | 987 | |
|---|
| 954 | 988 | static const struct v4l2_subdev_video_ops ov6650_video_ops = { |
|---|
| 955 | 989 | .s_stream = ov6650_s_stream, |
|---|
| 956 | 990 | .g_frame_interval = ov6650_g_frame_interval, |
|---|
| 957 | 991 | .s_frame_interval = ov6650_s_frame_interval, |
|---|
| 958 | | - .g_mbus_config = ov6650_g_mbus_config, |
|---|
| 959 | | - .s_mbus_config = ov6650_s_mbus_config, |
|---|
| 960 | 992 | }; |
|---|
| 961 | 993 | |
|---|
| 962 | 994 | static const struct v4l2_subdev_pad_ops ov6650_pad_ops = { |
|---|
| .. | .. |
|---|
| 965 | 997 | .set_selection = ov6650_set_selection, |
|---|
| 966 | 998 | .get_fmt = ov6650_get_fmt, |
|---|
| 967 | 999 | .set_fmt = ov6650_set_fmt, |
|---|
| 1000 | + .get_mbus_config = ov6650_get_mbus_config, |
|---|
| 1001 | + .set_mbus_config = ov6650_set_mbus_config, |
|---|
| 968 | 1002 | }; |
|---|
| 969 | 1003 | |
|---|
| 970 | 1004 | static const struct v4l2_subdev_ops ov6650_subdev_ops = { |
|---|
| 971 | 1005 | .core = &ov6650_core_ops, |
|---|
| 972 | 1006 | .video = &ov6650_video_ops, |
|---|
| 973 | 1007 | .pad = &ov6650_pad_ops, |
|---|
| 1008 | +}; |
|---|
| 1009 | + |
|---|
| 1010 | +static const struct v4l2_subdev_internal_ops ov6650_internal_ops = { |
|---|
| 1011 | + .registered = ov6650_video_probe, |
|---|
| 974 | 1012 | }; |
|---|
| 975 | 1013 | |
|---|
| 976 | 1014 | /* |
|---|
| .. | .. |
|---|
| 1017 | 1055 | V4L2_CID_GAMMA, 0, 0xff, 1, 0x12); |
|---|
| 1018 | 1056 | |
|---|
| 1019 | 1057 | priv->subdev.ctrl_handler = &priv->hdl; |
|---|
| 1020 | | - if (priv->hdl.error) |
|---|
| 1021 | | - return priv->hdl.error; |
|---|
| 1058 | + if (priv->hdl.error) { |
|---|
| 1059 | + ret = priv->hdl.error; |
|---|
| 1060 | + goto ectlhdlfree; |
|---|
| 1061 | + } |
|---|
| 1022 | 1062 | |
|---|
| 1023 | 1063 | v4l2_ctrl_auto_cluster(2, &priv->autogain, 0, true); |
|---|
| 1024 | 1064 | v4l2_ctrl_auto_cluster(3, &priv->autowb, 0, true); |
|---|
| .. | .. |
|---|
| 1029 | 1069 | priv->rect.top = DEF_VSTRT << 1; |
|---|
| 1030 | 1070 | priv->rect.width = W_CIF; |
|---|
| 1031 | 1071 | priv->rect.height = H_CIF; |
|---|
| 1032 | | - priv->half_scale = false; |
|---|
| 1033 | | - priv->code = MEDIA_BUS_FMT_YUYV8_2X8; |
|---|
| 1034 | 1072 | |
|---|
| 1035 | | - ret = ov6650_video_probe(client); |
|---|
| 1036 | | - if (ret) |
|---|
| 1037 | | - v4l2_ctrl_handler_free(&priv->hdl); |
|---|
| 1073 | + /* Hardware default frame interval */ |
|---|
| 1074 | + priv->tpf.numerator = GET_CLKRC_DIV(DEF_CLKRC); |
|---|
| 1075 | + priv->tpf.denominator = FRAME_RATE_MAX; |
|---|
| 1076 | + |
|---|
| 1077 | + priv->subdev.internal_ops = &ov6650_internal_ops; |
|---|
| 1078 | + |
|---|
| 1079 | + ret = v4l2_async_register_subdev(&priv->subdev); |
|---|
| 1080 | + if (!ret) |
|---|
| 1081 | + return 0; |
|---|
| 1082 | +ectlhdlfree: |
|---|
| 1083 | + v4l2_ctrl_handler_free(&priv->hdl); |
|---|
| 1038 | 1084 | |
|---|
| 1039 | 1085 | return ret; |
|---|
| 1040 | 1086 | } |
|---|
| .. | .. |
|---|
| 1044 | 1090 | struct ov6650 *priv = to_ov6650(client); |
|---|
| 1045 | 1091 | |
|---|
| 1046 | 1092 | v4l2_clk_put(priv->clk); |
|---|
| 1047 | | - v4l2_device_unregister_subdev(&priv->subdev); |
|---|
| 1093 | + v4l2_async_unregister_subdev(&priv->subdev); |
|---|
| 1048 | 1094 | v4l2_ctrl_handler_free(&priv->hdl); |
|---|
| 1049 | 1095 | return 0; |
|---|
| 1050 | 1096 | } |
|---|
| .. | .. |
|---|
| 1066 | 1112 | |
|---|
| 1067 | 1113 | module_i2c_driver(ov6650_i2c_driver); |
|---|
| 1068 | 1114 | |
|---|
| 1069 | | -MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV6650"); |
|---|
| 1070 | | -MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>"); |
|---|
| 1115 | +MODULE_DESCRIPTION("V4L2 subdevice driver for OmniVision OV6650 camera sensor"); |
|---|
| 1116 | +MODULE_AUTHOR("Janusz Krzysztofik <jmkrzyszt@gmail.com"); |
|---|
| 1071 | 1117 | MODULE_LICENSE("GPL v2"); |
|---|