20231229在Firefly的AIO-3399J开发板的Android11使用挖掘机的DTS配置单前后摄像头ov13850

20231229在Firefly的AIO-3399J开发板的Android11使用挖掘机的DTS配置单前后摄像头ov13850
2023/12/29 11:10


开发板:Firefly的AIO-3399J【RK3399】
SDK:rk3399-android-11-r20211216.tar.xz【Android11】
Android11.0.tar.bz2.aa【ToyBrick】
Android11.0.tar.bz2.ab
Android11.0.tar.bz2.ac

https://wiki.t-firefly.com/AIO-3399J/prepare_compile_android.html
AIO-3399J产品规格书 立即购买
AIO-3399J 采用 RK3399 六核(A72x2+A53x4) 64 位处理器,主频高达1.8GHz,集成了四核 Mali-T860 GPU,性能优异。


1、简略步骤:
rootroot@rootroot-X99-Turbo:~/3TB$ cat Android11.0.tar.bz2.a* > Android11.0.tar.bz2
rootroot@rootroot-X99-Turbo:~/3TB$ tar jxvf Android11.0.tar.bz2 
rootroot@rootroot-X99-Turbo:~/3TB$ mv Android11.0 64rk3399-android-11
rootroot@rootroot-X99-Turbo:~/3TB$ cd 64rk3399-android-11
rootroot@rootroot-X99-Turbo:~/3TB/64rk3399-android-11$ cd u-boot
rootroot@rootroot-X99-Turbo:~/3TB/64rk3399-android-11/u-boot$ ./make.sh rk3399
rootroot@rootroot-X99-Turbo:~/3TB/64rk3399-android-11/u-boot$ cd ..
rootroot@rootroot-X99-Turbo:~/3TB/64rk3399-android-11$ cd kernel/
rootroot@rootroot-X99-Turbo:~/3TB/64rk3399-android-11/kernel$ make ARCH=arm64 rockchip_defconfig android-11.config -j36
rootroot@rootroot-X99-Turbo:~/3TB/64rk3399-android-11/kernel$ make ARCH=arm64 BOOT_IMG=../rockdev/Image-rk3399_Android11/boot.img rk3399-sapphire-excavator-edp-avb.img -j36
rootroot@rootroot-X99-Turbo:~/3TB/64rk3399-android-11$ source build/envsetup.sh 
rootroot@rootroot-X99-Turbo:~/3TB/64rk3399-android-11$ lunch
     36. rk3399_Android11-userdebug
Which would you like? [aosp_arm-eng] 36
rootroot@rootroot-X99-Turbo:~/3TB/64rk3399-android-11$ 
rootroot@rootroot-X99-Turbo:~/3TB/64rk3399-android-11$ make -j36
rootroot@rootroot-X99-Turbo:~/3TB/64rk3399-android-11$ ./build.sh -u


2、挖掘机的DTS修改:
Z:\3TB\64rk3399-android-11\kernel\arch\arm64\boot\dts\rockchip\rk3399-sapphire-excavator-edp.dtsi
/*
 * Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
 *
 * This file is dual-licensed: you can use it either under the terms
 * of the GPL or the X11 license, at your option. Note that this dual
 * licensing only applies to this file, and not this project as a
 * whole.
 *
 *  a) This file is free software; you can redistribute it and/or
 *     modify it under the terms of the GNU General Public License as
 *     published by the Free Software Foundation; either version 2 of the
 *     License, or (at your option) any later version.
 *
 *     This file is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 * Or, alternatively,
 *
 *  b) Permission is hereby granted, free of charge, to any person
 *     obtaining a copy of this software and associated documentation
 *     files (the "Software"), to deal in the Software without
 *     restriction, including without limitation the rights to use,
 *     copy, modify, merge, publish, distribute, sublicense, and/or
 *     sell copies of the Software, and to permit persons to whom the
 *     Software is furnished to do so, subject to the following
 *     conditions:
 *
 *     The above copyright notice and this permission notice shall be
 *     included in all copies or substantial portions of the Software.
 *
 *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *     OTHER DEALINGS IN THE SOFTWARE.
 */

/dts-v1/;

#include "rk3399-excavator-sapphire.dtsi"
#include "rk3399-android.dtsi"
#include "rk3399-vop-clk-set.dtsi"

/ {
    vcc_lcd: vcc-lcd {
        compatible = "regulator-fixed";
        regulator-name = "vcc_lcd";
        gpio = <&gpio4 30 GPIO_ACTIVE_HIGH>;
        startup-delay-us = <20000>;
        enable-active-high;
        regulator-min-microvolt = <3300000>;
        regulator-max-microvolt = <3300000>;
        regulator-boot-on;
        vin-supply = <&vcc5v0_sys>;
    };

    panel: panel {
        compatible = "simple-panel";
        backlight = <&backlight>;
        power-supply = <&vcc_lcd>;
        enable-gpios = <&gpio1 13 GPIO_ACTIVE_HIGH>;
        prepare-delay-ms = <20>;
        enable-delay-ms = <20>;

        display-timings {
            native-mode = <&timing0>;

            timing0: timing0 {
                clock-frequency = <200000000>;
                hactive = <1536>;
                vactive = <2048>;
                hfront-porch = <12>;
                hsync-len = <16>;
                hback-porch = <48>;
                vfront-porch = <8>;
                vsync-len = <4>;
                vback-porch = <8>;
                hsync-active = <0>;
                vsync-active = <0>;
                de-active = <0>;
                pixelclk-active = <0>;
            };
        };

        ports {
            panel_in: endpoint {
                remote-endpoint = <&edp_out>;
            };
        };
    };

    test-power {
        status = "okay";
    };

    hdmiin_sound: hdmiin-sound {
        compatible = "rockchip,rockchip-rt5651-sound";
        rockchip,cpu = <&i2s0>;
        rockchip,codec = <&rt5651 &rt5651>;
        status = "okay";
    };

    vcc_mipi: vcc_mipi {
            compatible = "regulator-fixed";
            enable-active-high;
            gpio = <&gpio1 22 GPIO_ACTIVE_HIGH>;
            pinctrl-names = "default";
            pinctrl-0 = <&cif_pwr>;
            regulator-name = "vcc_mipi";
    };

};

&backlight {
    status = "okay";
    enable-gpios = <&gpio4 29 GPIO_ACTIVE_HIGH>;
};

&edp {
    status = "okay";
    force-hpd;

    ports {
        port@1 {
            reg = <1>;

            edp_out: endpoint {
                remote-endpoint = <&panel_in>;
            };
        };
    };
};

&edp_in_vopl {
    status = "disabled";
};

&hdmi_in_vopb {
    status = "disabled";
};

&rt5651 {
    status = "okay";
};

&cdn_dp {
    status = "disabled";
    extcon = <&fusb0>;
    phys = <&tcphy0_dp>;
};

&hdmi_dp_sound {
    status = "okay";
};

&hdmiin_sound {
    status = "disabled";
};

&dp_in_vopb {
    status = "disabled";
};

&i2s2 {
    status = "okay";
};

&i2c1 {
    status = "okay";

    gsl3673: gsl3673@40 {
        compatible = "GSL,GSL3673";
        reg = <0x40>;
        screen_max_x = <1536>;
        screen_max_y = <2048>;
        irq_gpio_number = <&gpio1 20 IRQ_TYPE_LEVEL_LOW>;
        rst_gpio_number = <&gpio4 22 GPIO_ACTIVE_HIGH>;
    };

    sgm3784: sgm3784@30 {
        #address-cells = <1>;
        #size-cells = <0>;
        compatible = "sgmicro,gsm3784";
        reg = <0x30>;
        rockchip,camera-module-index = <0>;
        rockchip,camera-module-facing = "back";
        enable-gpio = <&gpio2 RK_PB4 GPIO_ACTIVE_HIGH>;
        strobe-gpio = <&gpio1 RK_PA3 GPIO_ACTIVE_HIGH>;
        status = "okay";
        sgm3784_led0: led@0 {
            reg = <0x0>;
            led-max-microamp = <299200>;
            flash-max-microamp = <1122000>;
            flash-max-timeout-us = <1600000>;
        };

        sgm3784_led1: led@1 {
            reg = <0x1>;
            led-max-microamp = <299200>;
            flash-max-microamp = <1122000>;
            flash-max-timeout-us = <1600000>;
        };
    };

    tc358749x: tc358749x@0f {
        compatible = "toshiba,tc358749x";
        reg = <0x0f>;
        power-gpios = <&gpio2 6 GPIO_ACTIVE_HIGH>;
        power18-gpios = <&gpio2 9 GPIO_ACTIVE_HIGH>;
        power33-gpios = <&gpio2 5 GPIO_ACTIVE_HIGH>;
        csi-ctl-gpios = <&gpio2 10 GPIO_ACTIVE_HIGH>;
        stanby-gpios = <&gpio2 8 GPIO_ACTIVE_HIGH>;
        reset-gpios = <&gpio2 7 GPIO_ACTIVE_HIGH>;
        int-gpios = <&gpio2 12 GPIO_ACTIVE_HIGH>;
        pinctrl-names = "default";
        pinctrl-0 = <&hdmiin_gpios>;
        status = "disabled";
    };

    gc2145: gc2145@3c{
        status = "okay";
        compatible = "galaxycore,gc2145";
        reg = <0x3c>;
        pinctrl-names = "default";
        pinctrl-0 = <&cif_clkout>;

        clocks = <&cru SCLK_CIF_OUT>;
        clock-names = "xvclk";

        /* avdd-supply = <>; */
        /* dvdd-supply = <>; */
        /* dovdd-supply = <>; */
        pwdn-gpios = <&gpio2 28 GPIO_ACTIVE_HIGH>;    //ok
        rockchip,camera-module-index = <1>;
        rockchip,camera-module-facing = "front";
        rockchip,camera-module-name = "CameraKing";
        rockchip,camera-module-lens-name = "Largan";
        port {
            gc2145_out: endpoint {
                remote-endpoint = <&dvp_in_fcam>;
            };
        };
    };

        vm149c: vm149c@0c {
                compatible = "silicon touch,vm149c";
                status = "okay";
                reg = <0x0c>;
                rockchip,camera-module-index = <0>;
                rockchip,camera-module-facing = "back";
        };

    ov13850b: ov13850b@10 {
        compatible = "ovti,ov13850";
        status = "disabled";
        reg = <0x10>;
        clocks = <&cru SCLK_CIF_OUT>;
        clock-names = "xvclk";
        //avdd-supply = <&vcc_mipi>; /* VCC28_MIPI */
        //dovdd-supply = <&vcc_mipi>; /* VCC18_MIPI */
        //dvdd-supply = <&dvdd_1v2>; /* DVDD_1V2 */
        reset-gpios = <&gpio0 8 GPIO_ACTIVE_HIGH>;
        pwdn-gpios = <&gpio2 2 GPIO_ACTIVE_HIGH>;
        pinctrl-names = "rockchip,camera_default";
        pinctrl-0 = <&cif_clkout>;
        firefly,clkout-enabled-index = <0>;
        rockchip,camera-module-index = <0>;
        rockchip,camera-module-facing = "back";
        rockchip,camera-module-name = "CMK-CT0116";
        rockchip,camera-module-lens-name = "Largan-50013A1";
        lens-focus = <&vm149c>;
        port {
            ucam_out0: endpoint {
                remote-endpoint = <&mipi_in_ucam0>;
                data-lanes = <1 2>;
            };
        };
    };

        vm149c_front: vm149c_front@0c {
                compatible = "silicon touch,vm149c";
                status = "okay";
                reg = <0x0c>;
                rockchip,camera-module-index = <1>;
                rockchip,camera-module-facing = "front";
        };
    
    ov13850f: ov13850f@10 {
        compatible = "ovti,ov13850";
        status = "disabled";
        reg = <0x10>;
        clocks = <&cru SCLK_CIF_OUT>;
        clock-names = "xvclk";
        //avdd-supply = <&vcc_mipi>; /* VCC28_MIPI */
        //dovdd-supply = <&vcc_mipi>; /* VCC18_MIPI */
        //dvdd-supply = <&dvdd_1v2>; /* DVDD_1V2 */
        reset-gpios = <&gpio0 8 GPIO_ACTIVE_HIGH>;
        pwdn-gpios = <&gpio2 3 GPIO_ACTIVE_HIGH>;
        pinctrl-names = "rockchip,camera_default";
        pinctrl-0 = <&cif_clkout>;
        firefly,second-enabled-index = <1>;
            firefly,clkout-enabled-index = <0>;
        rockchip,camera-module-index = <1>;
        rockchip,camera-module-facing = "front";
        rockchip,camera-module-name = "CMK-CT0116";
        rockchip,camera-module-lens-name = "Largan-50013A1";
        lens-focus = <&vm149c_front>;
        port {
            ucam_out1: endpoint {
                remote-endpoint = <&mipi_in_ucam1>;
                data-lanes = <1 2>;
            };
        };
    };

};

&i2c6 {
    cw2015@62 {
        status = "disabled";
        compatible = "cw201x";
        reg = <0x62>;
        bat_config_info = <0x15 0x42 0x60 0x59 0x52 0x58 0x4D 0x48
                   0x48 0x44 0x44 0x46 0x49 0x48 0x32 0x24
                   0x20 0x17 0x13 0x0F 0x19 0x3E 0x51 0x45
                   0x08 0x76 0x0B 0x85 0x0E 0x1C 0x2E 0x3E
                   0x4D 0x52 0x52 0x57 0x3D 0x1B 0x6A 0x2D
                   0x25 0x43 0x52 0x87 0x8F 0x91 0x94 0x52
                   0x82 0x8C 0x92 0x96 0xFF 0x7B 0xBB 0xCB
                   0x2F 0x7D 0x72 0xA5 0xB5 0xC1 0x46 0xAE>;
        monitor_sec = <5>;
        virtual_power = <0>;
    };
};

&isp0_mmu {
    status = "okay";
};

&isp1_mmu {
    status = "okay";
};

&mipi_dphy_rx0 {
    status = "okay";

    ports {
        #address-cells = <1>;
        #size-cells = <0>;

        port@0 {
            reg = <0>;
            #address-cells = <1>;
            #size-cells = <0>;

            mipi_in_ucam0: endpoint@1 {
                reg = <1>;
                remote-endpoint = <&ucam_out0>;
                data-lanes = <1 2>;
            };
        };

        port@1 {
            reg = <1>;
            #address-cells = <1>;
            #size-cells = <0>;

            dphy_rx0_out: endpoint@0 {
                reg = <0>;
                remote-endpoint = <&isp0_mipi_in>;
            };
        };
    };
};

&mipi_dphy_tx1rx1 {
    status = "okay";

    ports {
        #address-cells = <1>;
        #size-cells = <0>;

        port@0 {
            reg = <0>;
            #address-cells = <1>;
            #size-cells = <0>;

            mipi_in_ucam1: endpoint@1 {
                reg = <1>;
                remote-endpoint = <&ucam_out1>;
                data-lanes = <1 2>;
            };
        };

        port@1 {
            reg = <1>;
            #address-cells = <1>;
            #size-cells = <0>;

            dphy_tx1rx1_out: endpoint@0 {
                reg = <0>;
                remote-endpoint = <&isp1_mipi_in>;
            };
        };
    };
};

&vopb {
    status = "okay";
    assigned-clocks = <&cru DCLK_VOP0_DIV>;
    assigned-clock-parents = <&cru PLL_CPLL>;
};

&vopl {
    status = "okay";
    assigned-clocks = <&cru DCLK_VOP1_DIV>;
    assigned-clock-parents = <&cru PLL_VPLL>;
};

&pcie_phy {
    status = "okay";
};

&pcie0 {
    status = "okay";
};

&rkisp1_0 {
    status = "okay";

    port {
        #address-cells = <1>;
        #size-cells = <0>;

        isp0_mipi_in: endpoint@0 {
            reg = <0>;
            remote-endpoint = <&dphy_rx0_out>;
        };
    };
};

&rkisp1_1 {
    status = "okay";

    port {
        #address-cells = <1>;
        #size-cells = <0>;

        isp1_mipi_in: endpoint@0 {
            reg = <0>;
            remote-endpoint = <&dphy_tx1rx1_out>;
        };
        dvp_in_fcam: endpoint@1 {
            reg = <1>;
            remote-endpoint = <&gc2145_out>;
        };
    };
};

&route_edp {
    status = "okay";
};

&route_hdmi {
    status = "okay";
    connect = <&vopl_out_hdmi>;
};

&rt5651_sound {
    status = "okay";
};

&pinctrl {
    cam0 {
         cif_pwr: cif-pwr {
            rockchip,pins = <1 22 RK_FUNC_GPIO &pcfg_pull_up>;
         };
    };

    
    lcd-panel {
        lcd_panel_reset: lcd-panel-reset {
            rockchip,pins = <4 RK_PD6 RK_FUNC_GPIO &pcfg_pull_up>;
        };
    };

    hdmiin {
        hdmiin_gpios: hdmiin_gpios {
        rockchip,pins =
                <2 RK_PA5 RK_FUNC_GPIO &pcfg_pull_none>,
                <2 RK_PA6 RK_FUNC_GPIO &pcfg_pull_none>,
                <2 RK_PA7 RK_FUNC_GPIO &pcfg_pull_none>,
                <2 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>,
                <2 RK_PB1 RK_FUNC_GPIO &pcfg_pull_none>,
                <2 RK_PB4 RK_FUNC_GPIO &pcfg_pull_none>;
        };
    };
};

3、
Z:\3TB\64rk3399-android-11\kernel\arch\arm64\boot\dts\rockchip\rk3399-sapphire-excavator-edp-avb.dts
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
 * Copyright (c) 2018 Fuzhou Rockchip Electronics Co., Ltd
 */

/dts-v1/;
#include "rk3399-sapphire-excavator-edp.dtsi"

/ {
    model = "Rockchip RK3399 Excavator Board edp avb (Android)";
    compatible = "rockchip,android", "rockchip,rk3399-excavator-edp-avb", "rockchip,rk3399";
    chosen: chosen {
    bootargs = "earlycon=uart8250,mmio32,0xff1a0000 console=ttyFIQ0 androidboot.baseband=N/A androidboot.veritymode=enforcing androidboot.hardware=rk30board androidboot.console=ttyFIQ0 init=/init initrd=0x62000001,0x00800000 coherent_pool=1m";
    };

    ext_cam_clk: external-camera-clock {
        compatible = "fixed-clock";
        clock-frequency = <27000000>;
        clock-output-names = "CLK_CAMERA_27MHZ";
        #clock-cells = <0>;
    };
};

&i2c1 {
    status = "okay";

    /delete-node/ tc358749x@0f;

    tc35874x: tc35874x@0f {
        status = "disabled";
        reg = <0x0f>;
        compatible = "toshiba,tc358749";
        clocks = <&ext_cam_clk>;
        clock-names = "refclk";
        reset-gpios = <&gpio2 7 GPIO_ACTIVE_LOW>;
        /* interrupt-parent = <&gpio2>; */
        /* interrupts = <12 IRQ_TYPE_LEVEL_HIGH>; */
        pinctrl-names = "default";
        pinctrl-0 = <&tc35874x_gpios>;
        rockchip,camera-module-index = <0>;
        rockchip,camera-module-facing = "back";
        rockchip,camera-module-name = "TC358749XBG";
        rockchip,camera-module-lens-name = "NC";

        port {
            hdmiin_out0: endpoint {
                remote-endpoint = <&hdmi_to_mipi_in>;
                data-lanes = <1 2 3 4>;
                clock-noncontinuous;
                link-frequencies =
                    /bits/ 64 <297000000>;
            };
        };
    };
    
        ov13850b: ov13850b@10 {
        status = "okay";
                avdd-supply = <&vcc_mipi>;
        power-gpios = <&gpio1 23 GPIO_ACTIVE_HIGH>;
                reset-gpios = <&gpio0 8 GPIO_ACTIVE_HIGH>;
                pwdn-gpios = <&gpio2 1 GPIO_ACTIVE_HIGH>;
        };

        ov13850f: ov13850f@10 {
        status = "okay";
                avdd-supply = <&vcc_mipi>;
        power-gpios = <&gpio1 23 GPIO_ACTIVE_HIGH>;
                reset-gpios = <&gpio0 8 GPIO_ACTIVE_HIGH>;
                pwdn-gpios = <&gpio2 28 GPIO_ACTIVE_HIGH>;
        };

};

&mipi_dphy_rx0 {
    status = "okay";

    ports {
        #address-cells = <1>;
        #size-cells = <0>;

        port@0 {
            reg = <0>;
            #address-cells = <1>;
            #size-cells = <0>;

            mipi_in_ucam0: endpoint@1 {
                reg = <1>;
                remote-endpoint = <&ucam_out0>;
                data-lanes = <1 2>;
            };

            hdmi_to_mipi_in: endpoint@2 {
                reg = <2>;
                remote-endpoint = <&hdmiin_out0>;
                data-lanes = <1 2 3 4>;
            };
        };

        port@1 {
            reg = <1>;
            #address-cells = <1>;
            #size-cells = <0>;

            dphy_rx0_out: endpoint@0 {
                reg = <0>;
                remote-endpoint = <&isp0_mipi_in>;
            };
        };
    };
};

&mipi_dphy_tx1rx1 {
    status = "okay";

    ports {
        #address-cells = <1>;
        #size-cells = <0>;

        port@0 {
            reg = <0>;
            #address-cells = <1>;
            #size-cells = <0>;

            mipi_in_ucam1: endpoint@1 {
                reg = <1>;
                remote-endpoint = <&ucam_out1>;
                data-lanes = <1 2>;
            };
        };

        port@1 {
            reg = <1>;
            #address-cells = <1>;
            #size-cells = <0>;

            dphy_tx1rx1_out: endpoint@0 {
                reg = <0>;
                remote-endpoint = <&isp1_mipi_in>;
            };
        };
    };

};

&pinctrl {
    hdmiin {
        tc35874x_gpios: tc35874x_gpios {
            rockchip,pins =
                /* PWREN_3.3 */
                <2 RK_PA5 RK_FUNC_GPIO &pcfg_output_high>,
                /* PWREN_1.2 */
                <2 RK_PA6 RK_FUNC_GPIO &pcfg_output_high>,
                /* HDMIIN_RST */
                <2 RK_PA7 RK_FUNC_GPIO &pcfg_pull_none>,
                /* HDMIIN_STBY */
                <2 RK_PB0 RK_FUNC_GPIO &pcfg_output_high>,
                /* MIPI_RST */
                <2 RK_PB1 RK_FUNC_GPIO &pcfg_output_high>,
                /* CSI_CTL */
                <2 RK_PB2 RK_FUNC_GPIO &pcfg_output_low>,
                /* HDMIIN_INT */
                <2 RK_PB4 RK_FUNC_GPIO &pcfg_pull_none>;
        };
    };
};

&rkisp1_0 {
    status = "okay";
};

&rkisp1_1 {
    status = "okay";
};

这里【必须】配置mipi_dphy_tx1rx1,如果不配置,出不来/dev/video9,也就是你没有前置摄像头了!


4、摄像头ov13850前后兼容的魔改!
Z:\3TB\64rk3399-android-11\kernel\drivers\media\i2c\ov13850.c
// SPDX-License-Identifier: GPL-2.0
/*
 * ov13850 driver
 *
 * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd.
 *
 * V0.0X01.0X01 add poweron function.
 * V0.0X01.0X02 fix mclk issue when probe multiple camera.
 * V0.0X01.0X03 add enum_frame_interval function.
 * V0.0X01.0X04 add quick stream on/off
 * V0.0X01.0X05 add function g_mbus_config
 */

#include <linux/clk.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/sysfs.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <linux/rk-camera-module.h>
#include <media/media-entity.h>
#include <media/v4l2-async.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-subdev.h>
#include <linux/pinctrl/consumer.h>
#include <linux/of_gpio.h>

#define DRIVER_VERSION            KERNEL_VERSION(0, 0x01, 0x05)

#ifndef V4L2_CID_DIGITAL_GAIN
#define V4L2_CID_DIGITAL_GAIN        V4L2_CID_GAIN
#endif

#define OV13850_LINK_FREQ_300MHZ    300000000
/* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */
#define OV13850_PIXEL_RATE        (OV13850_LINK_FREQ_300MHZ * 2 * 2 / 10)
#define OV13850_XVCLK_FREQ        24000000

#define CHIP_ID                0x00d850
#define OV13850_REG_CHIP_ID        0x300a

#define OV13850_REG_CTRL_MODE        0x0100
#define OV13850_MODE_SW_STANDBY        0x0
#define OV13850_MODE_STREAMING        BIT(0)

#define OV13850_REG_EXPOSURE        0x3500
#define    OV13850_EXPOSURE_MIN        4
#define    OV13850_EXPOSURE_STEP        1
#define OV13850_VTS_MAX            0x7fff

#define OV13850_REG_GAIN_H        0x350a
#define OV13850_REG_GAIN_L        0x350b
#define OV13850_GAIN_H_MASK        0x07
#define OV13850_GAIN_H_SHIFT        8
#define OV13850_GAIN_L_MASK        0xff
#define OV13850_GAIN_MIN        0x10
#define OV13850_GAIN_MAX        0xf8
#define OV13850_GAIN_STEP        1
#define OV13850_GAIN_DEFAULT        0x10

#define OV13850_REG_TEST_PATTERN    0x5e00
#define    OV13850_TEST_PATTERN_ENABLE    0x80
#define    OV13850_TEST_PATTERN_DISABLE    0x0

#define OV13850_REG_VTS            0x380e

#define REG_NULL            0xFFFF

#define OV13850_REG_VALUE_08BIT        1
#define OV13850_REG_VALUE_16BIT        2
#define OV13850_REG_VALUE_24BIT        3

#define OV13850_LANES            2
#define OV13850_BITS_PER_SAMPLE        10

#define OV13850_CHIP_REVISION_REG    0x302A
#define OV13850_R1A            0xb1
#define OV13850_R2A            0xb2

#define OF_CAMERA_PINCTRL_STATE_DEFAULT    "rockchip,camera_default"
#define OF_CAMERA_PINCTRL_STATE_SLEEP    "rockchip,camera_sleep"

#define OV13850_I2C_ADDR0        0x10
#define OV13850_I2C_ADDR1        0x36

#define OV13850_NAME            "ov13850"

static const struct regval *ov13850_global_regs;
static u32 clkout_enabled_index;

static const char * const ov13850_supply_names[] = {
    "avdd",        /* Analog power */
    "dovdd",    /* Digital I/O power */
    "dvdd",        /* Digital core power */
};

#define OV13850_NUM_SUPPLIES ARRAY_SIZE(ov13850_supply_names)

struct regval {
    u16 addr;
    u8 val;
};

struct ov13850_mode {
    u32 width;
    u32 height;
    struct v4l2_fract max_fps;
    u32 hts_def;
    u32 vts_def;
    u32 exp_def;
    const struct regval *reg_list;
};

struct ov13850 {
    struct i2c_client    *client;
    struct clk        *xvclk;
    struct gpio_desc    *power_gpio;
    struct gpio_desc    *reset_gpio;
    struct gpio_desc    *pwdn_gpio;
    struct regulator_bulk_data supplies[OV13850_NUM_SUPPLIES];

    struct pinctrl        *pinctrl;
    struct pinctrl_state    *pins_default;
    struct pinctrl_state    *pins_sleep;

    struct v4l2_subdev    subdev;
    struct media_pad    pad;
    struct v4l2_ctrl_handler ctrl_handler;
    struct v4l2_ctrl    *exposure;
    struct v4l2_ctrl    *anal_gain;
    struct v4l2_ctrl    *digi_gain;
    struct v4l2_ctrl    *hblank;
    struct v4l2_ctrl    *vblank;
    struct v4l2_ctrl    *test_pattern;
    struct mutex        mutex;
    bool            streaming;
    bool            power_on;
    const struct ov13850_mode *cur_mode;
    u32            module_index;
    const char        *module_facing;
    const char        *module_name;
    const char        *len_name;
};

#define to_ov13850(sd) container_of(sd, struct ov13850, subdev)

/*
 * Xclk 24Mhz
 */
static const struct regval ov13850_global_regs_r1a[] = {
    {0x0103, 0x01},
    {0x0300, 0x00},
    {0x0301, 0x00},
    {0x0302, 0x32},
    {0x0303, 0x01},
    {0x030a, 0x00},
    {0x300f, 0x11},
    {0x3010, 0x01},
    {0x3011, 0x76},
    {0x3012, 0x21},
    {0x3013, 0x12},
    {0x3014, 0x11},
    {0x3015, 0xc0},
    {0x301f, 0x03},
    {0x3106, 0x00},
    {0x3210, 0x47},
    {0x3500, 0x00},
    {0x3501, 0x60},
    {0x3502, 0x00},
    {0x3506, 0x00},
    {0x3507, 0x02},
    {0x3508, 0x00},
    {0x350a, 0x00},
    {0x350b, 0x80},
    {0x350e, 0x00},
    {0x350f, 0x10},
    {0x3600, 0x40},
    {0x3601, 0xfc},
    {0x3602, 0x02},
    {0x3603, 0x48},
    {0x3604, 0xa5},
    {0x3605, 0x9f},
    {0x3607, 0x00},
    {0x360a, 0x40},
    {0x360b, 0x91},
    {0x360c, 0x49},
    {0x360f, 0x8a},
    {0x3611, 0x10},
    {0x3612, 0x27},
    {0x3613, 0x33},
    {0x3615, 0x08},
    {0x3641, 0x02},
    {0x3660, 0x82},
    {0x3668, 0x54},
    {0x3669, 0x40},
    {0x3667, 0xa0},
    {0x3702, 0x40},
    {0x3703, 0x44},
    {0x3704, 0x2c},
    {0x3705, 0x24},
    {0x3706, 0x50},
    {0x3707, 0x44},
    {0x3708, 0x3c},
    {0x3709, 0x1f},
    {0x370a, 0x26},
    {0x370b, 0x3c},
    {0x3720, 0x66},
    {0x3722, 0x84},
    {0x3728, 0x40},
    {0x372a, 0x00},
    {0x372f, 0x90},
    {0x3710, 0x28},
    {0x3716, 0x03},
    {0x3718, 0x10},
    {0x3719, 0x08},
    {0x371c, 0xfc},
    {0x3760, 0x13},
    {0x3761, 0x34},
    {0x3767, 0x24},
    {0x3768, 0x06},
    {0x3769, 0x45},
    {0x376c, 0x23},
    {0x3d84, 0x00},
    {0x3d85, 0x17},
    {0x3d8c, 0x73},
    {0x3d8d, 0xbf},
    {0x3800, 0x00},
    {0x3801, 0x08},
    {0x3802, 0x00},
    {0x3803, 0x04},
    {0x3804, 0x10},
    {0x3805, 0x97},
    {0x3806, 0x0c},
    {0x3807, 0x4b},
    {0x3808, 0x08},
    {0x3809, 0x40},
    {0x380a, 0x06},
    {0x380b, 0x20},
    {0x380c, 0x12},
    {0x380d, 0xc0},
    {0x380e, 0x06},
    {0x380f, 0x80},
    {0x3810, 0x00},
    {0x3811, 0x04},
    {0x3812, 0x00},
    {0x3813, 0x02},
    {0x3814, 0x31},
    {0x3815, 0x31},
    {0x3820, 0x02},
    {0x3821, 0x05},
    {0x3834, 0x00},
    {0x3835, 0x1c},
    {0x3836, 0x08},
    {0x3837, 0x02},
    {0x4000, 0xf1},
    {0x4001, 0x00},
    {0x400b, 0x0c},
    {0x4011, 0x00},
    {0x401a, 0x00},
    {0x401b, 0x00},
    {0x401c, 0x00},
    {0x401d, 0x00},
    {0x4020, 0x00},
    {0x4021, 0xE4},
    {0x4022, 0x07},
    {0x4023, 0x5F},
    {0x4024, 0x08},
    {0x4025, 0x44},
    {0x4026, 0x08},
    {0x4027, 0x47},
    {0x4028, 0x00},
    {0x4029, 0x02},
    {0x402a, 0x04},
    {0x402b, 0x08},
    {0x402c, 0x02},
    {0x402d, 0x02},
    {0x402e, 0x0c},
    {0x402f, 0x08},
    {0x403d, 0x2c},
    {0x403f, 0x7f},
    {0x4500, 0x82},
    {0x4501, 0x38},
    {0x4601, 0x04},
    {0x4602, 0x22},
    {0x4603, 0x01},
    {0x4800, 0x24}, //MIPI CLK control
    {0x4837, 0x1b},
    {0x4d00, 0x04},
    {0x4d01, 0x42},
    {0x4d02, 0xd1},
    {0x4d03, 0x90},
    {0x4d04, 0x66},
    {0x4d05, 0x65},
    {0x5000, 0x0e},
    {0x5001, 0x01},
    {0x5002, 0x07},
    {0x5013, 0x40},
    {0x501c, 0x00},
    {0x501d, 0x10},
    {0x5242, 0x00},
    {0x5243, 0xb8},
    {0x5244, 0x00},
    {0x5245, 0xf9},
    {0x5246, 0x00},
    {0x5247, 0xf6},
    {0x5248, 0x00},
    {0x5249, 0xa6},
    {0x5300, 0xfc},
    {0x5301, 0xdf},
    {0x5302, 0x3f},
    {0x5303, 0x08},
    {0x5304, 0x0c},
    {0x5305, 0x10},
    {0x5306, 0x20},
    {0x5307, 0x40},
    {0x5308, 0x08},
    {0x5309, 0x08},
    {0x530a, 0x02},
    {0x530b, 0x01},
    {0x530c, 0x01},
    {0x530d, 0x0c},
    {0x530e, 0x02},
    {0x530f, 0x01},
    {0x5310, 0x01},
    {0x5400, 0x00},
    {0x5401, 0x61},
    {0x5402, 0x00},
    {0x5403, 0x00},
    {0x5404, 0x00},
    {0x5405, 0x40},
    {0x540c, 0x05},
    {0x5b00, 0x00},
    {0x5b01, 0x00},
    {0x5b02, 0x01},
    {0x5b03, 0xff},
    {0x5b04, 0x02},
    {0x5b05, 0x6c},
    {0x5b09, 0x02},
    {0x5e00, 0x00},
    {0x5e10, 0x1c},
    {0x0102, 0x01}, //Fast standby enable
    {REG_NULL, 0x00},
};

/*
 * Xclk 24Mhz
 */
static const struct regval ov13850_global_regs_r2a[] = {
    {0x0300, 0x01},
    {0x0301, 0x00},
    {0x0302, 0x28},
    {0x0303, 0x00},
    {0x030a, 0x00},
    {0x300f, 0x11},
    {0x3010, 0x01},
    {0x3011, 0x76},
    {0x3012, 0x21},
    {0x3013, 0x12},
    {0x3014, 0x11},
    {0x301f, 0x03},
    {0x3106, 0x00},
    {0x3210, 0x47},
    {0x3500, 0x00},
    {0x3501, 0x60},
    {0x3502, 0x00},
    {0x3506, 0x00},
    {0x3507, 0x02},
    {0x3508, 0x00},
    {0x350a, 0x00},
    {0x350b, 0x80},
    {0x350e, 0x00},
    {0x350f, 0x10},
    {0x351a, 0x00},
    {0x351b, 0x10},
    {0x351c, 0x00},
    {0x351d, 0x20},
    {0x351e, 0x00},
    {0x351f, 0x40},
    {0x3520, 0x00},
    {0x3521, 0x80},
    {0x3600, 0xc0},
    {0x3601, 0xfc},
    {0x3602, 0x02},
    {0x3603, 0x78},
    {0x3604, 0xb1},
    {0x3605, 0xb5},
    {0x3606, 0x73},
    {0x3607, 0x07},
    {0x3609, 0x40},
    {0x360a, 0x30},
    {0x360b, 0x91},
    {0x360c, 0x09},
    {0x360f, 0x02},
    {0x3611, 0x10},
    {0x3612, 0x27},
    {0x3613, 0x33},
    {0x3615, 0x0c},
    {0x3616, 0x0e},
    {0x3641, 0x02},
    {0x3660, 0x82},
    {0x3668, 0x54},
    {0x3669, 0x00},
    {0x366a, 0x3f},
    {0x3667, 0xa0},
    {0x3702, 0x40},
    {0x3703, 0x44},
    {0x3704, 0x2c},
    {0x3705, 0x01},
    {0x3706, 0x15},
    {0x3707, 0x44},
    {0x3708, 0x3c},
    {0x3709, 0x1f},
    {0x370a, 0x27},
    {0x370b, 0x3c},
    {0x3720, 0x55},
    {0x3722, 0x84},
    {0x3728, 0x40},
    {0x372a, 0x00},
    {0x372b, 0x02},
    {0x372e, 0x22},
    {0x372f, 0x90},
    {0x3730, 0x00},
    {0x3731, 0x00},
    {0x3732, 0x00},
    {0x3733, 0x00},
    {0x3710, 0x28},
    {0x3716, 0x03},
    {0x3718, 0x10},
    {0x3719, 0x0c},
    {0x371a, 0x08},
    {0x371c, 0xfc},
    {0x3748, 0x00},
    {0x3760, 0x13},
    {0x3761, 0x33},
    {0x3762, 0x86},
    {0x3763, 0x16},
    {0x3767, 0x24},
    {0x3768, 0x06},
    {0x3769, 0x45},
    {0x376c, 0x23},
    {0x376f, 0x80},
    {0x3773, 0x06},
    {0x3d84, 0x00},
    {0x3d85, 0x17},
    {0x3d8c, 0x73},
    {0x3d8d, 0xbf},
    {0x3800, 0x00},
    {0x3801, 0x08},
    {0x3802, 0x00},
    {0x3803, 0x04},
    {0x3804, 0x10},
    {0x3805, 0x97},
    {0x3806, 0x0c},
    {0x3807, 0x4b},
    {0x3808, 0x08},
    {0x3809, 0x40},
    {0x380a, 0x06},
    {0x380b, 0x20},
    {0x380c, 0x12},
    {0x380d, 0xc0},
    {0x380e, 0x06},
    {0x380f, 0x80},
    {0x3810, 0x00},
    {0x3811, 0x04},
    {0x3812, 0x00},
    {0x3813, 0x02},
    {0x3814, 0x31},
    {0x3815, 0x31},
    {0x3820, 0x02},
    {0x3821, 0x06},
    {0x3823, 0x00},
    {0x3826, 0x00},
    {0x3827, 0x02},
    {0x3834, 0x00},
    {0x3835, 0x1c},
    {0x3836, 0x08},
    {0x3837, 0x02},
    {0x4000, 0xf1},
    {0x4001, 0x00},
    {0x4006, 0x04},
    {0x4007, 0x04},
    {0x400b, 0x0c},
    {0x4011, 0x00},
    {0x401a, 0x00},
    {0x401b, 0x00},
    {0x401c, 0x00},
    {0x401d, 0x00},
    {0x4020, 0x00},
    {0x4021, 0xe4},
    {0x4022, 0x04},
    {0x4023, 0xd7},
    {0x4024, 0x05},
    {0x4025, 0xbc},
    {0x4026, 0x05},
    {0x4027, 0xbf},
    {0x4028, 0x00},
    {0x4029, 0x02},
    {0x402a, 0x04},
    {0x402b, 0x08},
    {0x402c, 0x02},
    {0x402d, 0x02},
    {0x402e, 0x0c},
    {0x402f, 0x08},
    {0x403d, 0x2c},
    {0x403f, 0x7f},
    {0x4041, 0x07},
    {0x4500, 0x82},
    {0x4501, 0x3c},
    {0x458b, 0x00},
    {0x459c, 0x00},
    {0x459d, 0x00},
    {0x459e, 0x00},
    {0x4601, 0x83},
    {0x4602, 0x22},
    {0x4603, 0x01},
    {0x4800, 0x24}, //MIPI CLK control
    {0x4837, 0x19},
    {0x4d00, 0x04},
    {0x4d01, 0x42},
    {0x4d02, 0xd1},
    {0x4d03, 0x90},
    {0x4d04, 0x66},
    {0x4d05, 0x65},
    {0x4d0b, 0x00},
    {0x5000, 0x0e},
    {0x5001, 0x01},
    {0x5002, 0x07},
    {0x5013, 0x40},
    {0x501c, 0x00},
    {0x501d, 0x10},
    {0x510f, 0xfc},
    {0x5110, 0xf0},
    {0x5111, 0x10},
    {0x536d, 0x02},
    {0x536e, 0x67},
    {0x536f, 0x01},
    {0x5370, 0x4c},
    {0x5400, 0x00},
    {0x5400, 0x00},
    {0x5401, 0x61},
    {0x5402, 0x00},
    {0x5403, 0x00},
    {0x5404, 0x00},
    {0x5405, 0x40},
    {0x540c, 0x05},
    {0x5501, 0x00},
    {0x5b00, 0x00},
    {0x5b01, 0x00},
    {0x5b02, 0x01},
    {0x5b03, 0xff},
    {0x5b04, 0x02},
    {0x5b05, 0x6c},
    {0x5b09, 0x02},
    {0x5e00, 0x00},
    {0x5e10, 0x1c},
    {0x0102, 0x01}, //Fast standby enable
    {REG_NULL, 0x00},
};

/*
 * Xclk 24Mhz
 * max_framerate 30fps
 * mipi_datarate per lane 600Mbps
 */
static const struct regval ov13850_2112x1568_regs[] = {
    {0x3612, 0x27},
    {0x370a, 0x26},
    {0x372a, 0x00},
    {0x372f, 0x90},
    {0x3801, 0x08},
    {0x3805, 0x97},
    {0x3807, 0x4b},
    {0x3808, 0x08},
    {0x3809, 0x40},
    {0x380a, 0x06},
    {0x380b, 0x20},
    {0x380c, 0x12},
    {0x380d, 0xc0},
    {0x380e, 0x06},
    {0x380f, 0x80},
    {0x3813, 0x02},
    {0x3814, 0x31},
    {0x3815, 0x31},
    {0x3820, 0x02},
    {0x3821, 0x05},
    {0x3836, 0x08},
    {0x3837, 0x02},
    {0x4601, 0x04},
    {0x4603, 0x00},
    {0x4020, 0x00},
    {0x4021, 0xE4},
    {0x4022, 0x07},
    {0x4023, 0x5F},
    {0x4024, 0x08},
    {0x4025, 0x44},
    {0x4026, 0x08},
    {0x4027, 0x47},
    {0x4603, 0x01},
    {0x5401, 0x61},
    {0x5405, 0x40},
    {REG_NULL, 0x00},
};

/*
 * Xclk 24Mhz
 * max_framerate 7fps
 * mipi_datarate per lane 600Mbps
 */
static const struct regval ov13850_4224x3136_regs[] = {
    {0x3612, 0x2f},
    {0x370a, 0x24},
    {0x372a, 0x04},
    {0x372f, 0xa0},
    {0x3801, 0x0C},
    {0x3805, 0x93},
    {0x3807, 0x4B},
    {0x3808, 0x10},
    {0x3809, 0x80},
    {0x380a, 0x0c},
    {0x380b, 0x40},
    {0x380e, 0x0d},
    {0x380f, 0x00},
    {0x3813, 0x04},
    {0x3814, 0x11},
    {0x3815, 0x11},
    {0x3820, 0x00},
    {0x3821, 0x04},
    {0x3836, 0x04},
    {0x3837, 0x01},
    {0x4601, 0x87},
    {0x4603, 0x01},
    {0x4020, 0x02},
    {0x4021, 0x4C},
    {0x4022, 0x0E},
    {0x4023, 0x37},
    {0x4024, 0x0F},
    {0x4025, 0x1C},
    {0x4026, 0x0F},
    {0x4027, 0x1F},
    {0x4603, 0x00},
    {0x5401, 0x71},
    {0x5405, 0x80},
    {REG_NULL, 0x00},
};

static const struct ov13850_mode supported_modes[] = {
    {
        .width = 2112,
        .height = 1568,
        .max_fps = {
            .numerator = 10000,
            .denominator = 300000,
        },
        .exp_def = 0x0600,
        .hts_def = 0x12c0,
        .vts_def = 0x0680,
        .reg_list = ov13850_2112x1568_regs,
    },{
        .width = 4224,
        .height = 3136,
        .max_fps = {
            .numerator = 20000,
            .denominator = 150000,
        },
        .exp_def = 0x0600,
        .hts_def = 0x12c0,
        .vts_def = 0x0d00,
        .reg_list = ov13850_4224x3136_regs,
    },
};

static const s64 link_freq_menu_items[] = {
    OV13850_LINK_FREQ_300MHZ
};

static const char * const ov13850_test_pattern_menu[] = {
    "Disabled",
    "Vertical Color Bar Type 1",
    "Vertical Color Bar Type 2",
    "Vertical Color Bar Type 3",
    "Vertical Color Bar Type 4"
};

/* Write registers up to 4 at a time */
static int ov13850_write_reg(struct i2c_client *client, u16 reg,
                 u32 len, u32 val)
{
    u32 buf_i, val_i;
    u8 buf[6];
    u8 *val_p;
    __be32 val_be;

    dev_dbg(&client->dev, "write reg(0x%x val:0x%x)!\n", reg, val);

    if (len > 4)
        return -EINVAL;

    buf[0] = reg >> 8;
    buf[1] = reg & 0xff;

    val_be = cpu_to_be32(val);
    val_p = (u8 *)&val_be;
    buf_i = 2;
    val_i = 4 - len;

    while (val_i < 4)
        buf[buf_i++] = val_p[val_i++];

    if (i2c_master_send(client, buf, len + 2) != len + 2)
        return -EIO;

    return 0;
}

static int ov13850_write_array(struct i2c_client *client,
                   const struct regval *regs)
{
    u32 i;
    int ret = 0;

    for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
        ret = ov13850_write_reg(client, regs[i].addr,
                    OV13850_REG_VALUE_08BIT,
                    regs[i].val);

    return ret;
}

/* Read registers up to 4 at a time */
static int ov13850_read_reg(struct i2c_client *client, u16 reg,
                unsigned int len, u32 *val)
{
    struct i2c_msg msgs[2];
    u8 *data_be_p;
    __be32 data_be = 0;
    __be16 reg_addr_be = cpu_to_be16(reg);
    int ret;

    if (len > 4 || !len)
        return -EINVAL;

    data_be_p = (u8 *)&data_be;
    /* Write register address */
    msgs[0].addr = client->addr;
    msgs[0].flags = 0;
    msgs[0].len = 2;
    msgs[0].buf = (u8 *)&reg_addr_be;

    /* Read data from register */
    msgs[1].addr = client->addr;
    msgs[1].flags = I2C_M_RD;
    msgs[1].len = len;
    msgs[1].buf = &data_be_p[4 - len];

    ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
    if (ret != ARRAY_SIZE(msgs))
        return -EIO;

    *val = be32_to_cpu(data_be);

    return 0;
}

static int ov13850_get_reso_dist(const struct ov13850_mode *mode,
                 struct v4l2_mbus_framefmt *framefmt)
{
    return abs(mode->width - framefmt->width) +
           abs(mode->height - framefmt->height);
}

static const struct ov13850_mode *
ov13850_find_best_fit(struct v4l2_subdev_format *fmt)
{
    struct v4l2_mbus_framefmt *framefmt = &fmt->format;
    int dist;
    int cur_best_fit = 0;
    int cur_best_fit_dist = -1;
    unsigned int i;

    for (i = 0; i < ARRAY_SIZE(supported_modes); i++) {
        dist = ov13850_get_reso_dist(&supported_modes[i], framefmt);
        if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) {
            cur_best_fit_dist = dist;
            cur_best_fit = i;
        }
    }

    return &supported_modes[cur_best_fit];
}

static int ov13850_set_fmt(struct v4l2_subdev *sd,
               struct v4l2_subdev_pad_config *cfg,
              struct v4l2_subdev_format *fmt)
{
    struct ov13850 *ov13850 = to_ov13850(sd);
    const struct ov13850_mode *mode;
    s64 h_blank, vblank_def;

    mutex_lock(&ov13850->mutex);

    mode = ov13850_find_best_fit(fmt);
    fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10;
    fmt->format.width = mode->width;
    fmt->format.height = mode->height;
    fmt->format.field = V4L2_FIELD_NONE;
    if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
        *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format;
#else
        mutex_unlock(&ov13850->mutex);
        return -ENOTTY;
#endif
    } else {
        ov13850->cur_mode = mode;
        h_blank = mode->hts_def - mode->width;
        __v4l2_ctrl_modify_range(ov13850->hblank, h_blank,
                     h_blank, 1, h_blank);
        vblank_def = mode->vts_def - mode->height;
        __v4l2_ctrl_modify_range(ov13850->vblank, vblank_def,
                     OV13850_VTS_MAX - mode->height,
                     1, vblank_def);
    }

    mutex_unlock(&ov13850->mutex);

    return 0;
}

static int ov13850_get_fmt(struct v4l2_subdev *sd,
               struct v4l2_subdev_pad_config *cfg,
               struct v4l2_subdev_format *fmt)
{
    struct ov13850 *ov13850 = to_ov13850(sd);
    const struct ov13850_mode *mode = ov13850->cur_mode;

    mutex_lock(&ov13850->mutex);
    if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
        fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
#else
        mutex_unlock(&ov13850->mutex);
        return -ENOTTY;
#endif
    } else {
        fmt->format.width = mode->width;
        fmt->format.height = mode->height;
        fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10;
        fmt->format.field = V4L2_FIELD_NONE;
    }
    mutex_unlock(&ov13850->mutex);

    return 0;
}

static int ov13850_enum_mbus_code(struct v4l2_subdev *sd,
                  struct v4l2_subdev_pad_config *cfg,
                  struct v4l2_subdev_mbus_code_enum *code)
{
    if (code->index != 0)
        return -EINVAL;
    code->code = MEDIA_BUS_FMT_SBGGR10_1X10;

    return 0;
}

static int ov13850_enum_frame_sizes(struct v4l2_subdev *sd,
                    struct v4l2_subdev_pad_config *cfg,
                   struct v4l2_subdev_frame_size_enum *fse)
{
    if (fse->index >= ARRAY_SIZE(supported_modes))
        return -EINVAL;

    if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10)
        return -EINVAL;

    fse->min_width  = supported_modes[fse->index].width;
    fse->max_width  = supported_modes[fse->index].width;
    fse->max_height = supported_modes[fse->index].height;
    fse->min_height = supported_modes[fse->index].height;

    return 0;
}

static int ov13850_enable_test_pattern(struct ov13850 *ov13850, u32 pattern)
{
    u32 val;

    if (pattern)
        val = (pattern - 1) | OV13850_TEST_PATTERN_ENABLE;
    else
        val = OV13850_TEST_PATTERN_DISABLE;

    return ov13850_write_reg(ov13850->client,
                 OV13850_REG_TEST_PATTERN,
                 OV13850_REG_VALUE_08BIT,
                 val);
}

static int ov13850_g_frame_interval(struct v4l2_subdev *sd,
                    struct v4l2_subdev_frame_interval *fi)
{
    struct ov13850 *ov13850 = to_ov13850(sd);
    const struct ov13850_mode *mode = ov13850->cur_mode;

    mutex_lock(&ov13850->mutex);
    fi->interval = mode->max_fps;
    mutex_unlock(&ov13850->mutex);

    return 0;
}

static void ov13850_get_module_inf(struct ov13850 *ov13850,
                   struct rkmodule_inf *inf)
{
    memset(inf, 0, sizeof(*inf));
    strlcpy(inf->base.sensor, OV13850_NAME, sizeof(inf->base.sensor));
    strlcpy(inf->base.module, ov13850->module_name,
        sizeof(inf->base.module));
    strlcpy(inf->base.lens, ov13850->len_name, sizeof(inf->base.lens));
}

static long ov13850_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
    struct ov13850 *ov13850 = to_ov13850(sd);
    long ret = 0;
    u32 stream = 0;

    switch (cmd) {
    case RKMODULE_GET_MODULE_INFO:
        ov13850_get_module_inf(ov13850, (struct rkmodule_inf *)arg);
        break;
    case RKMODULE_SET_QUICK_STREAM:

        stream = *((u32 *)arg);

        if (stream)
            ret = ov13850_write_reg(ov13850->client,
                 OV13850_REG_CTRL_MODE,
                 OV13850_REG_VALUE_08BIT,
                 OV13850_MODE_STREAMING);
        else
            ret = ov13850_write_reg(ov13850->client,
                 OV13850_REG_CTRL_MODE,
                 OV13850_REG_VALUE_08BIT,
                 OV13850_MODE_SW_STANDBY);
        break;
    default:
        ret = -ENOIOCTLCMD;
        break;
    }

    return ret;
}

#ifdef CONFIG_COMPAT
static long ov13850_compat_ioctl32(struct v4l2_subdev *sd,
                   unsigned int cmd, unsigned long arg)
{
    void __user *up = compat_ptr(arg);
    struct rkmodule_inf *inf;
    struct rkmodule_awb_cfg *cfg;
    long ret;
    u32 stream = 0;

    switch (cmd) {
    case RKMODULE_GET_MODULE_INFO:
        inf = kzalloc(sizeof(*inf), GFP_KERNEL);
        if (!inf) {
            ret = -ENOMEM;
            return ret;
        }

        ret = ov13850_ioctl(sd, cmd, inf);
        if (!ret)
            ret = copy_to_user(up, inf, sizeof(*inf));
        kfree(inf);
        break;
    case RKMODULE_AWB_CFG:
        cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
        if (!cfg) {
            ret = -ENOMEM;
            return ret;
        }

        ret = copy_from_user(cfg, up, sizeof(*cfg));
        if (!ret)
            ret = ov13850_ioctl(sd, cmd, cfg);
        kfree(cfg);
        break;
    case RKMODULE_SET_QUICK_STREAM:
        ret = copy_from_user(&stream, up, sizeof(u32));
        if (!ret)
            ret = ov13850_ioctl(sd, cmd, &stream);
        break;
    default:
        ret = -ENOIOCTLCMD;
        break;
    }

    return ret;
}
#endif

static int __ov13850_start_stream(struct ov13850 *ov13850)
{
    int ret;

    ret = ov13850_write_array(ov13850->client, ov13850->cur_mode->reg_list);
    if (ret)
        return ret;

    /* In case these controls are set before streaming */
    mutex_unlock(&ov13850->mutex);
    ret = v4l2_ctrl_handler_setup(&ov13850->ctrl_handler);
    mutex_lock(&ov13850->mutex);
    if (ret)
        return ret;

    return ov13850_write_reg(ov13850->client,
                 OV13850_REG_CTRL_MODE,
                 OV13850_REG_VALUE_08BIT,
                 OV13850_MODE_STREAMING);
}

static int __ov13850_stop_stream(struct ov13850 *ov13850)
{
    return ov13850_write_reg(ov13850->client,
                 OV13850_REG_CTRL_MODE,
                 OV13850_REG_VALUE_08BIT,
                 OV13850_MODE_SW_STANDBY);
}

static int ov13850_s_stream(struct v4l2_subdev *sd, int on)
{
    struct ov13850 *ov13850 = to_ov13850(sd);
    struct i2c_client *client = ov13850->client;
    int ret = 0;

    mutex_lock(&ov13850->mutex);
    on = !!on;
    if (on == ov13850->streaming)
        goto unlock_and_return;

    if (on) {
        ret = pm_runtime_get_sync(&client->dev);
        if (ret < 0) {
            pm_runtime_put_noidle(&client->dev);
            goto unlock_and_return;
        }

        ret = __ov13850_start_stream(ov13850);
        if (ret) {
            v4l2_err(sd, "start stream failed while write regs\n");
            pm_runtime_put(&client->dev);
            goto unlock_and_return;
        }
    } else {
        __ov13850_stop_stream(ov13850);
        pm_runtime_put(&client->dev);
    }

    ov13850->streaming = on;

unlock_and_return:
    mutex_unlock(&ov13850->mutex);

    return ret;
}

static int __ov13850_power_on(struct ov13850 *ov13850);
static void __ov13850_power_off(struct ov13850 *ov13850);

static int ov13850_s_power(struct v4l2_subdev *sd, int on)
{
    struct ov13850 *ov13850 = to_ov13850(sd);
    struct i2c_client *client = ov13850->client;
    int ret = 0;

    mutex_lock(&ov13850->mutex);

    /* If the power state is not modified - no work to do. */
    if (ov13850->power_on == !!on)
        goto unlock_and_return;

    if (on) {
        ret = pm_runtime_get_sync(&client->dev);
        if (ret < 0) {
            pm_runtime_put_noidle(&client->dev);
            goto unlock_and_return;
        }

        __ov13850_power_on(ov13850);
        ret = ov13850_write_array(ov13850->client, ov13850_global_regs);
        if (ret) {
            v4l2_err(sd, "could not set init registers\n");
            pm_runtime_put_noidle(&client->dev);
            goto unlock_and_return;
        }

        ov13850->power_on = true;
        /* export gpio */
        if (!IS_ERR(ov13850->reset_gpio))
               gpiod_export(ov13850->reset_gpio, false);
        if (!IS_ERR(ov13850->pwdn_gpio))
               gpiod_export(ov13850->pwdn_gpio, false);
    } else {
        pm_runtime_put(&client->dev);
        __ov13850_power_off(ov13850);
        ov13850->power_on = false;
        /* unexport gpio */
        if (!IS_ERR(ov13850->reset_gpio))
               gpiod_unexport(ov13850->reset_gpio);
        if (!IS_ERR(ov13850->pwdn_gpio))
               gpiod_unexport(ov13850->pwdn_gpio);
    }

unlock_and_return:
    mutex_unlock(&ov13850->mutex);

    return ret;
}

/* Calculate the delay in us by clock rate and clock cycles */
static inline u32 ov13850_cal_delay(u32 cycles)
{
    return DIV_ROUND_UP(cycles, OV13850_XVCLK_FREQ / 1000 / 1000);
}

static int __ov13850_power_on(struct ov13850 *ov13850)
{
    int ret;
    u32 delay_us;
    struct device *dev = &ov13850->client->dev;

    if (!IS_ERR_OR_NULL(ov13850->pins_default)) {
        ret = pinctrl_select_state(ov13850->pinctrl,
                       ov13850->pins_default);
        if (ret < 0)
            dev_err(dev, "could not set pins\n");
    }

    if (clkout_enabled_index){
        ret = clk_set_rate(ov13850->xvclk, OV13850_XVCLK_FREQ);
        if (ret < 0)
            dev_warn(dev, "Failed to set xvclk rate (24MHz)\n");
        if (clk_get_rate(ov13850->xvclk) != OV13850_XVCLK_FREQ)
            dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
        ret = clk_prepare_enable(ov13850->xvclk);
        if (ret < 0) {
            dev_err(dev, "Failed to enable xvclk\n");
            return ret;
        }
    }

    if (!IS_ERR(ov13850->reset_gpio))
        gpiod_set_value_cansleep(ov13850->reset_gpio, 0);

    ret = regulator_bulk_enable(OV13850_NUM_SUPPLIES, ov13850->supplies);
    if (ret < 0) {
        dev_err(dev, "Failed to enable regulators\n");
        goto disable_clk;
    }

    if (!IS_ERR(ov13850->power_gpio))
        gpiod_set_value_cansleep(ov13850->power_gpio, 1);

    if (!IS_ERR(ov13850->reset_gpio))
        gpiod_set_value_cansleep(ov13850->reset_gpio, 1);

    usleep_range(500, 1000);

    if (!IS_ERR(ov13850->pwdn_gpio))
        gpiod_set_value_cansleep(ov13850->pwdn_gpio, 1);

    /* 8192 cycles prior to first SCCB transaction */
    delay_us = ov13850_cal_delay(8192);
    usleep_range(delay_us, delay_us * 2);

    return 0;

disable_clk:
    if (clkout_enabled_index)
        clk_disable_unprepare(ov13850->xvclk);

    return ret;
}

static void __ov13850_power_off(struct ov13850 *ov13850)
{
    int ret;
    struct device *dev = &ov13850->client->dev;

    if (!IS_ERR(ov13850->pwdn_gpio))
        gpiod_set_value_cansleep(ov13850->pwdn_gpio, 0);
    if (clkout_enabled_index)
        clk_disable_unprepare(ov13850->xvclk);
        //if (!IS_ERR(ov13850->power_gpio))
        //        gpiod_set_value_cansleep(ov13850->power_gpio, 0);
    //if (!IS_ERR(ov13850->reset_gpio))
    //    gpiod_set_value_cansleep(ov13850->reset_gpio, 0);

    if (!IS_ERR_OR_NULL(ov13850->pins_sleep)) {
        ret = pinctrl_select_state(ov13850->pinctrl,
                       ov13850->pins_sleep);
        if (ret < 0)
            dev_dbg(dev, "could not set pins\n");
    }

    regulator_bulk_disable(OV13850_NUM_SUPPLIES, ov13850->supplies);
}

static int ov13850_runtime_resume(struct device *dev)
{
    struct i2c_client *client = to_i2c_client(dev);
    struct v4l2_subdev *sd = i2c_get_clientdata(client);
    struct ov13850 *ov13850 = to_ov13850(sd);

    return __ov13850_power_on(ov13850);
}

static int ov13850_runtime_suspend(struct device *dev)
{
    struct i2c_client *client = to_i2c_client(dev);
    struct v4l2_subdev *sd = i2c_get_clientdata(client);
    struct ov13850 *ov13850 = to_ov13850(sd);

    __ov13850_power_off(ov13850);

    return 0;
}

#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
static int ov13850_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
    struct ov13850 *ov13850 = to_ov13850(sd);
    struct v4l2_mbus_framefmt *try_fmt =
                v4l2_subdev_get_try_format(sd, fh->pad, 0);
    const struct ov13850_mode *def_mode = &supported_modes[0];

    mutex_lock(&ov13850->mutex);
    /* Initialize try_fmt */
    try_fmt->width = def_mode->width;
    try_fmt->height = def_mode->height;
    try_fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
    try_fmt->field = V4L2_FIELD_NONE;

    mutex_unlock(&ov13850->mutex);
    /* No crop or compose */

    return 0;
}
#endif

static int ov13850_enum_frame_interval(struct v4l2_subdev *sd,
                       struct v4l2_subdev_pad_config *cfg,
                       struct v4l2_subdev_frame_interval_enum *fie)
{
    if (fie->index >= ARRAY_SIZE(supported_modes))
        return -EINVAL;

    if (fie->code != MEDIA_BUS_FMT_SBGGR10_1X10)
        return -EINVAL;

    fie->width = supported_modes[fie->index].width;
    fie->height = supported_modes[fie->index].height;
    fie->interval = supported_modes[fie->index].max_fps;
    return 0;
}

static int ov13850_g_mbus_config(struct v4l2_subdev *sd,
                struct v4l2_mbus_config *config)
{
    u32 val = 0;

    val = 1 << (OV13850_LANES - 1) |
          V4L2_MBUS_CSI2_CHANNEL_0 |
          V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
    config->type = V4L2_MBUS_CSI2;
    config->flags = val;

    return 0;
}

static const struct dev_pm_ops ov13850_pm_ops = {
    SET_RUNTIME_PM_OPS(ov13850_runtime_suspend,
               ov13850_runtime_resume, NULL)
};

#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
static const struct v4l2_subdev_internal_ops ov13850_internal_ops = {
    .open = ov13850_open,
};
#endif

static const struct v4l2_subdev_core_ops ov13850_core_ops = {
    .s_power = ov13850_s_power,
    .ioctl = ov13850_ioctl,
#ifdef CONFIG_COMPAT
    .compat_ioctl32 = ov13850_compat_ioctl32,
#endif
};

static const struct v4l2_subdev_video_ops ov13850_video_ops = {
    .s_stream = ov13850_s_stream,
    .g_frame_interval = ov13850_g_frame_interval,
    .g_mbus_config = ov13850_g_mbus_config,
};

static const struct v4l2_subdev_pad_ops ov13850_pad_ops = {
    .enum_mbus_code = ov13850_enum_mbus_code,
    .enum_frame_size = ov13850_enum_frame_sizes,
    .enum_frame_interval = ov13850_enum_frame_interval,
    .get_fmt = ov13850_get_fmt,
    .set_fmt = ov13850_set_fmt,
};

static const struct v4l2_subdev_ops ov13850_subdev_ops = {
    .core    = &ov13850_core_ops,
    .video    = &ov13850_video_ops,
    .pad    = &ov13850_pad_ops,
};

static int ov13850_set_ctrl(struct v4l2_ctrl *ctrl)
{
    struct ov13850 *ov13850 = container_of(ctrl->handler,
                         struct ov13850, ctrl_handler);
    struct i2c_client *client = ov13850->client;
    s64 max;
    int ret = 0;

    /* Propagate change of current control to all related controls */
    switch (ctrl->id) {
    case V4L2_CID_VBLANK:
        /* Update max exposure while meeting expected vblanking */
        max = ov13850->cur_mode->height + ctrl->val - 4;
        __v4l2_ctrl_modify_range(ov13850->exposure,
                     ov13850->exposure->minimum, max,
                     ov13850->exposure->step,
                     ov13850->exposure->default_value);
        break;
    }

    if (!pm_runtime_get_if_in_use(&client->dev))
        return 0;

    switch (ctrl->id) {
    case V4L2_CID_EXPOSURE:
        /* 4 least significant bits of expsoure are fractional part */
        ret = ov13850_write_reg(ov13850->client,
                    OV13850_REG_EXPOSURE,
                    OV13850_REG_VALUE_24BIT,
                    ctrl->val << 4);
        break;
    case V4L2_CID_ANALOGUE_GAIN:
        ret = ov13850_write_reg(ov13850->client,
                    OV13850_REG_GAIN_H,
                    OV13850_REG_VALUE_08BIT,
                    (ctrl->val >> OV13850_GAIN_H_SHIFT) &
                    OV13850_GAIN_H_MASK);
        ret |= ov13850_write_reg(ov13850->client,
                     OV13850_REG_GAIN_L,
                     OV13850_REG_VALUE_08BIT,
                     ctrl->val & OV13850_GAIN_L_MASK);
        break;
    case V4L2_CID_VBLANK:
        ret = ov13850_write_reg(ov13850->client,
                    OV13850_REG_VTS,
                    OV13850_REG_VALUE_16BIT,
                    ctrl->val + ov13850->cur_mode->height);
        break;
    case V4L2_CID_TEST_PATTERN:
        ret = ov13850_enable_test_pattern(ov13850, ctrl->val);
        break;
    default:
        dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
             __func__, ctrl->id, ctrl->val);
        break;
    }

    pm_runtime_put(&client->dev);

    return ret;
}

static const struct v4l2_ctrl_ops ov13850_ctrl_ops = {
    .s_ctrl = ov13850_set_ctrl,
};

static int ov13850_initialize_controls(struct ov13850 *ov13850)
{
    const struct ov13850_mode *mode;
    struct v4l2_ctrl_handler *handler;
    struct v4l2_ctrl *ctrl;
    s64 exposure_max, vblank_def;
    u32 h_blank;
    int ret;

    handler = &ov13850->ctrl_handler;
    mode = ov13850->cur_mode;
    ret = v4l2_ctrl_handler_init(handler, 8);
    if (ret)
        return ret;
    handler->lock = &ov13850->mutex;

    ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ,
                      0, 0, link_freq_menu_items);
    if (ctrl)
        ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;

    v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE,
              0, OV13850_PIXEL_RATE, 1, OV13850_PIXEL_RATE);

    h_blank = mode->hts_def - mode->width;
    ov13850->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
                h_blank, h_blank, 1, h_blank);
    if (ov13850->hblank)
        ov13850->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;

    vblank_def = mode->vts_def - mode->height;
    ov13850->vblank = v4l2_ctrl_new_std(handler, &ov13850_ctrl_ops,
                V4L2_CID_VBLANK, vblank_def,
                OV13850_VTS_MAX - mode->height,
                1, vblank_def);

    exposure_max = mode->vts_def - 4;
    ov13850->exposure = v4l2_ctrl_new_std(handler, &ov13850_ctrl_ops,
                V4L2_CID_EXPOSURE, OV13850_EXPOSURE_MIN,
                exposure_max, OV13850_EXPOSURE_STEP,
                mode->exp_def);

    ov13850->anal_gain = v4l2_ctrl_new_std(handler, &ov13850_ctrl_ops,
                V4L2_CID_ANALOGUE_GAIN, OV13850_GAIN_MIN,
                OV13850_GAIN_MAX, OV13850_GAIN_STEP,
                OV13850_GAIN_DEFAULT);

    ov13850->test_pattern = v4l2_ctrl_new_std_menu_items(handler,
                &ov13850_ctrl_ops, V4L2_CID_TEST_PATTERN,
                ARRAY_SIZE(ov13850_test_pattern_menu) - 1,
                0, 0, ov13850_test_pattern_menu);

    if (handler->error) {
        ret = handler->error;
        dev_err(&ov13850->client->dev,
            "Failed to init controls(%d)\n", ret);
        goto err_free_handler;
    }

    ov13850->subdev.ctrl_handler = handler;

    return 0;

err_free_handler:
    v4l2_ctrl_handler_free(handler);

    return ret;
}

static int ov13850_check_sensor_id(struct ov13850 *ov13850,
                   struct i2c_client *client)
{
    struct device *dev = &ov13850->client->dev;
    u32 id = 0;
    int ret;
    int time=5;

    while (time--){
        ret = ov13850_read_reg(client, OV13850_REG_CHIP_ID,
                       OV13850_REG_VALUE_16BIT, &id);
        if (id != CHIP_ID) {
            dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret);
            if (time==1){
                if(client->addr == OV13850_I2C_ADDR1)
                    client->addr = OV13850_I2C_ADDR0;
                else
                    client->addr = OV13850_I2C_ADDR1;
            }
        udelay(10);

        }
    }

    ret = ov13850_read_reg(client, OV13850_CHIP_REVISION_REG,
                   OV13850_REG_VALUE_08BIT, &id);
    if (ret) {
        dev_err(dev, "Read chip revision register error\n");
        return ret;
    }

    if (id == OV13850_R2A)
        ov13850_global_regs = ov13850_global_regs_r2a;
    else
        ov13850_global_regs = ov13850_global_regs_r1a;
    dev_info(dev, "Detected OV%06x sensor, REVISION 0x%x\n", CHIP_ID, id);

    return 0;
}

static int ov13850_configure_regulators(struct ov13850 *ov13850)
{
    unsigned int i;

    for (i = 0; i < OV13850_NUM_SUPPLIES; i++)
        ov13850->supplies[i].supply = ov13850_supply_names[i];

    return devm_regulator_bulk_get(&ov13850->client->dev,
                       OV13850_NUM_SUPPLIES,
                       ov13850->supplies);
}

static void free_gpio(struct ov13850 *ov13850)
{
    dev_info(&ov13850->client->dev, "%s(%d) enter!\n", __func__, __LINE__);

    if (IS_ERR(ov13850->pwdn_gpio)){
           gpio_free(desc_to_gpio(ov13850->pwdn_gpio));
    }
    if (IS_ERR(ov13850->reset_gpio)){
           gpio_free(desc_to_gpio(ov13850->reset_gpio));
    }
}

static int ov13850_probe(struct i2c_client *client,
             const struct i2c_device_id *id)
{
    struct device *dev = &client->dev;
    struct device_node *node = dev->of_node;
    struct ov13850 *ov13850;
    struct v4l2_subdev *sd;
    char facing[2];
    int ret;
    struct gpio_desc *pwdn_gpios = NULL, *reset_gpios = NULL;
    unsigned int pwdn = -1, reset = -1;
    enum of_gpio_flags flags;    

    dev_info(dev, "driver version: %02x.%02x.%02x",
        DRIVER_VERSION >> 16,
        (DRIVER_VERSION & 0xff00) >> 8,
        DRIVER_VERSION & 0x00ff);

    ov13850 = devm_kzalloc(dev, sizeof(*ov13850), GFP_KERNEL);
    if (!ov13850)
        return -ENOMEM;

    ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
                   &ov13850->module_index);
    ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
                       &ov13850->module_facing);
    ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
                       &ov13850->module_name);
    ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
                       &ov13850->len_name);
    if (ret) {
        dev_err(dev, "could not get module information!\n");
        return -EINVAL;
    }

    ov13850->client = client;
    ov13850->cur_mode = &supported_modes[0];

    ret = of_property_read_u32(node, "firefly,clkout-enabled-index", &clkout_enabled_index);
    if (ret){
        dev_err(dev, "could not get firefly,clkout-enabled-index, default output xvclk .");
        clkout_enabled_index = 1;
    }

    if (clkout_enabled_index){
        ov13850->xvclk = devm_clk_get(dev, "xvclk");
        if (IS_ERR(ov13850->xvclk)) {
            dev_err(dev, "Failed to get xvclk\n");
            return -EINVAL;
        }
    }

    ov13850->power_gpio = devm_gpiod_get(dev, "power", GPIOD_OUT_LOW);
    if (IS_ERR(ov13850->power_gpio))
        dev_warn(dev, "Failed to get power-gpios, maybe no use\n");

    ov13850->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
    if (IS_ERR(ov13850->reset_gpio)) {
       dev_info(dev, "Failed to get reset-gpios, maybe no use\n");
       reset = of_get_named_gpio_flags(node, "reset-gpios", 0, &flags);
       reset_gpios = gpio_to_desc(reset);
       if (IS_ERR(reset_gpios))
          dev_info(dev, "Failed to get reset-gpios again\n");
       else
           ov13850->reset_gpio = reset_gpios;
    }

    ov13850->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW);
    if (IS_ERR(ov13850->pwdn_gpio)) {
      dev_info(dev, "Failed to get pwdn-gpios, maybe no use\n");
       pwdn = of_get_named_gpio_flags(node, "pwdn-gpios", 0, &flags);
       pwdn_gpios = gpio_to_desc(pwdn);
       if (IS_ERR(pwdn_gpios))
           dev_info(dev, "Failed to get pwdn-gpios again\n");
       else
           ov13850->pwdn_gpio = pwdn_gpios;
    }


    ret = ov13850_configure_regulators(ov13850);
    if (ret) {
        dev_err(dev, "Failed to get power regulators\n");
        return ret;
    }

    ov13850->pinctrl = devm_pinctrl_get(dev);
    if (!IS_ERR(ov13850->pinctrl)) {
        ov13850->pins_default =
            pinctrl_lookup_state(ov13850->pinctrl,
                         OF_CAMERA_PINCTRL_STATE_DEFAULT);
        if (IS_ERR(ov13850->pins_default))
            dev_err(dev, "could not get default pinstate\n");

        ov13850->pins_sleep =
            pinctrl_lookup_state(ov13850->pinctrl,
                         OF_CAMERA_PINCTRL_STATE_SLEEP);
        if (IS_ERR(ov13850->pins_sleep))
            dev_err(dev, "could not get sleep pinstate\n");
    }

    mutex_init(&ov13850->mutex);

    sd = &ov13850->subdev;
    v4l2_i2c_subdev_init(sd, client, &ov13850_subdev_ops);
    ret = ov13850_initialize_controls(ov13850);
    if (ret)
        goto err_destroy_mutex;

    ret = __ov13850_power_on(ov13850);
    if (ret)
        goto err_free_handler;

    ret = ov13850_check_sensor_id(ov13850, client);
    if (ret)
        goto err_power_off;

#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
    sd->internal_ops = &ov13850_internal_ops;
    sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
#endif
#if defined(CONFIG_MEDIA_CONTROLLER)
    ov13850->pad.flags = MEDIA_PAD_FL_SOURCE;
    sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
    ret = media_entity_pads_init(&sd->entity, 1, &ov13850->pad);
    if (ret < 0)
        goto err_power_off;
#endif

    memset(facing, 0, sizeof(facing));
    if (strcmp(ov13850->module_facing, "back") == 0)
        facing[0] = 'b';
    else
        facing[0] = 'f';

    snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
         ov13850->module_index, facing,
         OV13850_NAME, dev_name(sd->dev));
    ret = v4l2_async_register_subdev_sensor_common(sd);
    if (ret) {
        dev_err(dev, "v4l2 async register subdev failed\n");
        goto err_clean_entity;
    }

    pm_runtime_set_active(dev);
    pm_runtime_enable(dev);
    pm_runtime_idle(dev);

    return 0;

err_clean_entity:
#if defined(CONFIG_MEDIA_CONTROLLER)
    media_entity_cleanup(&sd->entity);
#endif
err_power_off:
    __ov13850_power_off(ov13850);
    free_gpio(ov13850);
err_free_handler:
    v4l2_ctrl_handler_free(&ov13850->ctrl_handler);
err_destroy_mutex:
    mutex_destroy(&ov13850->mutex);

    return ret;
}

static int ov13850_remove(struct i2c_client *client)
{
    struct v4l2_subdev *sd = i2c_get_clientdata(client);
    struct ov13850 *ov13850 = to_ov13850(sd);

    v4l2_async_unregister_subdev(sd);
#if defined(CONFIG_MEDIA_CONTROLLER)
    media_entity_cleanup(&sd->entity);
#endif
    v4l2_ctrl_handler_free(&ov13850->ctrl_handler);
    mutex_destroy(&ov13850->mutex);

    pm_runtime_disable(&client->dev);
    if (!pm_runtime_status_suspended(&client->dev))
        __ov13850_power_off(ov13850);
    pm_runtime_set_suspended(&client->dev);

    return 0;
}

#if IS_ENABLED(CONFIG_OF)
static const struct of_device_id ov13850_of_match[] = {
    { .compatible = "ovti,ov13850" },
    {},
};
MODULE_DEVICE_TABLE(of, ov13850_of_match);
#endif

static const struct i2c_device_id ov13850_match_id[] = {
    { "ovti,ov13850", 0 },
    { },
};

static struct i2c_driver ov13850_i2c_driver = {
    .driver = {
        .name = OV13850_NAME,
        .pm = &ov13850_pm_ops,
        .of_match_table = of_match_ptr(ov13850_of_match),
    },
    .probe        = &ov13850_probe,
    .remove        = &ov13850_remove,
    .id_table    = ov13850_match_id,
};

static int __init sensor_mod_init(void)
{
    return i2c_add_driver(&ov13850_i2c_driver);
}

static void __exit sensor_mod_exit(void)
{
    i2c_del_driver(&ov13850_i2c_driver);
}

device_initcall_sync(sensor_mod_init);
module_exit(sensor_mod_exit);

MODULE_DESCRIPTION("OmniVision ov13850 sensor driver");
MODULE_LICENSE("GPL v2");


参考资料:
http://www.friendlyelec.com.cn/agent.asp
http://www.friendlyelec.com.cn/
https://download.friendlyelec.com/NanoPC-T4
https://wiki.friendlyelec.com/wiki/index.php/NanoPC-T4/zh#.E4.B8.8B.E8.BD.BDAndroid10.E6.BA.90.E4.BB.A3.E7.A0.81


https://item.taobao.com/item.htm?spm=a1z09.2.0.0.37562e8dcotDm6&id=570312633249&_u=7ju3ku004a
友善NanoPC-T4瑞芯微RK3399开发板ROS双摄4K播放开源AI智能安卓10

WiKi维基教程(固件介绍,使用说明,操作步骤等)
http://wiki.friendlyelec.com/wiki/index.php/NanoPC-T4
系统固件下载
https://dl.friendlyelec.com/nanopct4
原理图(pdf格式)
http://wiki.friendlyelec.com/wiki/images/e/e0/NanoPC-T4-1902-Schematic.pdf
尺寸图(dxf格式)
http://wiki.friendlyelec.com/wiki/images/b/bc/NanoPC-T4_1802_Drawing%28dxf%29.zip


http://www.friendlyelec.com.cn/nanopi-m4.asp
NanoPi M4 | NanoPi M4V2

https://wiki.friendlyelec.com/wiki/index.php/NanoPi_M4/zh
15.3 编译Android10源代码
15.3.1 下载Android10源代码
有以下两种途径获取 Android10 的源代码,都需要联网:

使用网盘里的git repo压缩包
网盘下载地址: 点击进入

https://download.friendlyelec.com/NanoPiM4
https://pan.baidu.com/share/init?surl=oBLn9H31hILJKEPQXgrUog
提取码:yn6r

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/587067.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2023年03月21日_chatgpt宕机事件的简单回顾

你能想象吗 ChatGPT挂了 昨天半夜呢 来自全球各地的用户纷纷发现 ChatGPT的网站弹出了报错警告的信息 然后立即就无法使用了 即使是有特权的plus账户也未能幸免 一时之间呢 chatgptdown的话题在Twitter刷屏 不少重度的用户表示很着急 有的用户说呢没了ChatGPT 这工作…

MySQL入门教程-函数,索引

4MySQL函数 常用函数 -- 数学运算SELECT ABS(-8); -- 绝对值SELECT CEIL(5.1); -- 向上取整SELECT CEILING(5.1); -- 向上取整SELECT RAND(); -- 返回0~1之间的一个随机数SELECT SIGN(-10); -- 返回一个数的符号;0返回0;正数返回1;负数返回-1​-- 字符串函数SELECT CHAR_LENGT…

推荐系统中的 业务指标 覆盖率

覆盖率&#xff08;Coverage&#xff09;是推荐系统评估指标之一&#xff0c;用于衡量推荐系统是否能够覆盖物品空间中的多样性&#xff0c;即是否能够推荐系统中的每个物品都能够被推荐给用户。覆盖率通常是一个百分比&#xff0c;表示被推荐的物品占总物品集合的比例。 覆盖…

设计模式-调停者模式

设计模式专栏 模式介绍模式特点应用场景调停者模式与命令模式的比较代码示例Java实现调停者模式Python实现调停者模式 调停者模式在spring中的应用 模式介绍 调停者模式是一种软件设计模式&#xff0c;主要用于模块间的解耦&#xff0c;通过避免对象之间显式的互相指向&#x…

使用软件解决T490笔记本57摄氏度温度墙的问题

项目场景&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a; 客户使用LenovoT490跑GQRX SDR&#xff0c;接入SDR在5MHz采样率下&#xff0c;机器卡顿。这对于10代i7CPU显然是不正常的。后续发现上网页也卡&#xff0c;卸载杀毒、重装系统、BIOS电源设置、系统最…

24、Web攻防-通用漏洞SQL注入MYSQL跨库ACCESS偏移

文章目录 一、SQL注入原理   脚本代码在与数据库进行数据通讯时&#xff08;从数据库取出相关数据进行页面显示&#xff09;&#xff0c;使用预定义的SQL查询语句进行数据查询。能通过参数传递自定义值来实现SQL语句的控制&#xff0c;执行恶意的查询操作&#xff0c;例如查询…

【BERT】深入BERT模型2——模型中的重点内容,两个任务

前言 BERT出自论文&#xff1a;《BERT&#xff1a;Pre-training of Deep Bidirectional Transformers for Language Understanding》 2019年 近年来&#xff0c;在自然语言处理领域&#xff0c;BERT模型受到了极为广泛的关注&#xff0c;很多模型中都用到了BERT-base或者是BE…

Stable Diffusion WebUI制作光影文字效果

在huggingface上下载control_v1p_sd15_brightness模型。 将模型放在stable-diffusion-webui\extensions\sd-webui-controlnet\models目录下。 SD参数配置 正向提示词&#xff1a; city,Building,tall building,Neon Light, gentle light shines through, anime style, paint…

美团后端Java实习一面面经

说一下AOP&#xff1f; 面向切面编程&#xff0c;通过预编译方式和运行期动态代理实现程序功能的统一维护的技术。可以减少程序中相同代码的编写&#xff0c;简化开发&#xff0c;使得接口更加专注于业务 相关概念 Aspect&#xff08;切面&#xff09;&#xff1a; Aspect 声…

Python中property特性属性是什么

在Java中&#xff0c;通常在类中定义的成员变量为私有变量&#xff0c;在类的实例中不能直接通过对象.属性直接操作&#xff0c;而是要通过getter和setter来操作私有变量。 而在Python中&#xff0c;因为有property这个概念&#xff0c;所以不需要写getter和setter一堆重复的代…

当你遇到这些情况的时候,发版到白了少年头,代码还是不会更新...

转载说明&#xff1a;如果您喜欢这篇文章并打算转载它&#xff0c;请私信作者取得授权。感谢您喜爱本文&#xff0c;请文明转载&#xff0c;谢谢。 一、问题描述&#xff1a; 之前遇到过几次这种情况&#xff1a;研发将代码提交之后&#xff0c;通过打包部署&#xff0c;发现部…

K8S容器的一则故障记录

一、故障现象 XXX反馈说某某业务服务异常&#xff0c;无法启动&#xff0c;需要进行协助排查。经常会接到这样一个需求&#xff0c;一开始无法清楚知道具体什么问题&#xff0c;需要跟一线运维人员详细做沟通&#xff0c;了解故障问题的细节。 根据一线运维人员的反馈&#xff…

听GPT 讲Rust源代码--src/tools(36)

File: rust/src/tools/clippy/clippy_lints/src/loops/empty_loop.rs 在Rust源代码中&#xff0c;empty_loop.rs文件位于src/tools/clippy/clippy_lints/src/loops/目录下&#xff0c;它的作用是实现并提供一个名为EMPTY_LOOP的Lint规则。Clippy是一个Rust的静态分析工具&#…

个人财务管理软件Money Pro mac功能特点

Money Pro mac是一款专为Mac用户设计的个人财务管理软件&#xff0c;具有全面的账户管理、智能的预算规划、强大的投资分析、丰富的报表和图表、安全的数据保护以及易于使用的界面设计等特点。 Money Pro mac功能和特点 全面的账户管理&#xff1a;支持多种账户类型&#xff0…

Spring Boot 基于Redisson实现注解式分布式锁

依赖版本 JDK 17 Spring Boot 3.2.0 Redisson 3.25.0 源码地址&#xff1a;Gitee 导入依赖 <properties><redisson.version>3.25.0</redisson.version> </properties><dependencies><dependency><groupId>org.projectlombok</…

CLion中使用C/C++ Single File Execution插件编译和运行单个文件

在开发C/C程序时&#xff0c;尽管项目通常以组织良好的结构进行管理&#xff0c;但有时我们可能只需要快速测试或运行单个C或C源文件。对于这种情况&#xff0c;JetBrains CLion IDE提供了一个便捷的解决方案——通过安装名为“C/C Single File Execution”的插件来实现对单个源…

【OpenCV】OpenCV 4.9.0 正式发布

​ 开源计算机视觉库 OpenCV 4.9.0 已于2023年12月29日正式发布。 此次发布有DNN模块对ONNX Attention、Einsum等层的支持、新的fastGEMM实现、transformers的实验性支持等诸多亮点。 OpenCV 4.9.0 更新内容&#xff1a; &#xff08;来自OpenCV中国团队以及中国社区的贡献…

antv/x6_2.0学习使用(二、画布)

画布 一. 创建容器 在页面中创建一个 div 标签&#xff0c;用来容纳画布 <div id"container"></div>画布常用配置信息 const graph new Graph({container: graphRef.value, // 画布容器width: 800, // 画布宽度&#xff0c;默认使用容器宽度height:…

2017年喜茶数字营销变化

1. 什么是数字营销&#xff1f;数字化时代&#xff0c;消费者行为模式发生了哪些变化&#xff1f; 数字营销是指使用数字渠道和平台&#xff0c;通过在线手段推广产品或服务&#xff0c;与目标受众进行互动和沟通的一种营销方式。它涵盖了多种在线渠道&#xff0c;包括社交媒…

华为云云耀云服务器L实例评测|Python Selenium加Chrome Driver构建UI自动化测试实践

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;CSDN领军人物&#xff0c;全栈领域优质创作者✌&#xff0c;CSDN博客专家&#xff0c;阿里云社区专家博主&#xff0c;2023年6月CSDN上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;AWS/阿里云资深使用…