This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

AM572x VIP interface with ADV7180

Hi, 

    We are interfacing Adv7180 decoder with  Am5738 processor's video input port VINA. I am having following doubts regarding this.

1. Is there any driver required other than v4l2 subsystem driver  and adv7180 driver ?

2 What are the device tree entry for the same.

Thanks & Regards

Abhishek

  • I will forward this to the software team.
  • Hi,

    I checked the latest TI SDK02.00.02.11 (kernel 4.1.18), and I see that adv7180.c driver is NOT device tree compatible... You can see that there are no dt references in it, like of_device_id adv76xx_of_id[]  in adv7604.c. 

    You may need to modify the source in order to use it with the dt kernels. 

    Best Regards, 
    Yordan

  • Hi Yordan,
    Yes I have already seen that.
    I am just curious to know if is there any other way to link it with vin1a port of processor and v4l2 subsystem ?
  • Hi,

    In DT compatible kernels, AFAIK, there is no other way to link the driver. You need to add the dts entries, and in order to bind the dts to the driver, you need to modify adv7180.c to be device tree compatible (which IMO is quite time consuming). 

    Best Regards, 
    Yordan

  • Hi Yordan,
    Thanks
    Just want to know if you can provide any document or any pointer. or Any one else in your concern who has done the same thing. That can be very helpful for me to make this driver device tree compatible ?
  • Hi,

    You can integrate the adv7180.c from the mainline kernel:
    https://www.kernel.org/

    I checked version 4.7, there the adv driver is already dt compliant.

    Best Regards,
    Yordan
  • Hi Yordan,
    Thanks for your support I will take care of that.
    Can you please see following threads. It can be helpful for me if you suggest something regarding following issue.
    e2e.ti.com/.../524339

    e2e.ti.com/.../522972
  • Abhishek,

    The ADV7180 driver has to be conformed to v4l2 sub device spec. Look how mt9t11x driver is written and follow the device tree entry example for same in arch/arm/boot/dts/am57xx-evm-common.dtsi.

    Regards,

    Manisha

  • Hi  Yordan & Manisha,

                                           I have changed the adv7180 driver  to make it device tree compatible. The changes i made in adv7180.c is as follows

    28d27
    < #include <linux/of.h>
    1167c1166
    <       printk("probing adv7180 driver\n");
    ---
    >
    1328,1336d1326
    < #ifdef CONFIG_OF
    < static const struct of_device_id adv7180_of_id[] = {
    <       { .compatible = "adi,adv7180", },
    <       { },
    < };
    <
    < MODULE_DEVICE_TABLE(of, adv7180_of_id);
    < #endif
    <
    1338a1329
    >                  .owner = THIS_MODULE,
    1341d1331
    <                  .of_match_table = of_match_ptr(adv7180_of_id),

     

    My dts entries are as:


    &i2c1{
                    status = "okay";
                    clock-frequency = <400000>;
                    adv7180@20 {
                            compatible = "adi,adv7180";
                            reg = <0x20>;
                            port {
                                    adv7180_1: endpoint{
                                                    remore-endpoint=<&vin1a>;
                                                    };

                    };
            };
    };


    &vin1a {
            status = "okay";
            endpoint {
                    slave-mode;
                    remote-endpoint = <&adv7180_1>;
            };
    };

    FYI,    

      I am able to probe i2c0 from uboot and able to see my decoder chip address as 0x20.

    Based on above changes and entries i am having  following issues.

    1. I took reference of  "mt9t11" dts entry in arch/arm/boot/dts/am57xx-evm.dts (In my Kernel "arch/arm/boot/dts/am57xx-evm-common.dtsi." is not there ). But i am unable to see /dev/i2c-0 node.

    2.  I am able to see /dev/video0 node (which is not capture driver node its m2m driver  node) after booting. Why i am not getting video capture driver node my .config file entries for video input port is as :

    CONFIG_V4L_PLATFORM_DRIVERS=y
    # CONFIG_VIDEO_CAFE_CCIC is not set
    # CONFIG_SOC_CAMERA is not set
    # CONFIG_VIDEO_XILINX is not set
    CONFIG_VIDEO_TI_CAL=m
    CONFIG_VIDEO_TI_VIP=y
    CONFIG_V4L_MEM2MEM_DRIVERS=y
    # CONFIG_VIDEO_MEM2MEM_DEINTERLACE is not set
    # CONFIG_VIDEO_SH_VEU is not set
    CONFIG_VIDEO_TI_VPE=y
    # CONFIG_VIDEO_TI_VPE_DEBUG is not set
    CONFIG_VIDEO_TI_VPDMA_HELPER=y

    Thanks & Regards

    Abhishek KUmar





  • Are you ensuring that adv7180 driver is complying to v4l2 sub device spec?
  • Hi manisha,

                          I think I am missing this step. Is there any document i can follow ? or can you  suggest me  in which file i should make entry for the same?


    But i think  capture driver should create /dev/video1 node without even registering decoder as sub device right?

  • Hi Manisha,
    I think I am missing that. Can I get any document to follow the procedure ? Or Can you suggest me in which file i should make entries for that.
    But i think capture driver should create /dev/video1 node even if i am not registering adv7180 as v4l2 sub device correct me if i am wrong ?
    Below are my vip related dmesg log.

    [ 1.858594] vip 48970000.vip: loading firmware vpdma-1b8.bin
    [ 3.787014] vip 48970000.vip: VPDMA firmware loaded
    [ 3.792254] vip1-s0: Port A enabled but no endpoints found

    Thanks & Regards
    Abhishek
  • Hi Manisha,

                         Now I am able to see probe function getting called from adv7180 driver. But  i am unable to create /dev/video1 node.

    My current dts entries are as follows

    &i2c1{
                    status = "okay";
                    clock-frequency = <400000>;
                   adv7180@20 {
                            compatible = "adi,adv7180";
                            reg = <0x20>;

                            
                            port {
                                    adv7180_1: endpoint{
                                            
                                                    remote-endpoint=<&vin1a>;
                                                 
                                                                                     

                                                    };

                    };
         };
    };

    &vin1a {
            status = "okay";
            endpoint@0 {
                   slave-mode;
                   remote-endpoint = <&adv7180_1>;
            };
    };


    While booting i am getting some error log as follows:

    i2c /dev entries driver
    [    1.791983] vpe 489d0000.vpe: loading firmware vpdma-1b8.bin
    [    1.797761] vpe 489d0000.vpe: Direct firmware load for vpdma-1b8.bin failed with error -2 //loading vpdma firmware in case of vpe failing while i can see below its available in /lib/firmware
    [    1.805977] vpe 489d0000.vpe: Falling back to user helper
    [    1.812718] vip 48970000.vip: loading firmware vpdma-1b8.bin
    [    1.820538] gpio-fan gpio_fan: GPIO fan initialized
    [    1.828176] omap_hsmmc 4809c000.mmc: Got CD GPIO
    [    1.867681] omap_hsmmc 480b4000.mmc: no pinctrl state for sdr25 mode
    [    1.874065] omap_hsmmc 480b4000.mmc: no pinctrl state for sdr12 mode
    [    1.880724] vdd_3v3: supplied by regen1
    [    1.948147] mmc0: host does not support reading read-only switch, assuming write-enable
    [    1.957442] ledtrig-cpu: registered to indicate activity on CPUs
    [    1.961933] mmc0: new high speed SDHC card at address aaaa
    [    1.962264] mmcblk0: mmc0:aaaa SL16G 14.8 GiB
    [    1.973885] omap-aes 4b500000.aes: OMAP AES hw accel rev: 3.3
    [    1.978508]  mmcblk0: p1 p2
    [    1.983259] omap-aes 4b700000.aes: OMAP AES hw accel rev: 3.3
    [    1.989523] omap-des 480a5000.des: OMAP DES hw accel rev: 2.2
    [    1.996268] omap-sham 4b101000.sham: hw accel on OMAP rev 4.3
    [    2.003325] fbtft_of_value: buswidth = 9
    [    2.007290] fbtft_of_value: debug = 3
    [    2.010969] fbtft_of_value: rotate = 270
    [    2.014909] fbtft_of_value: fps = 30
    [    2.018686] fb_ili9341 spi4.0: fbtft_gamma_parse_str() str=
    [    2.020825] mmc1: MAN_BKOPS_EN bit is not set
    [    2.024342] mmc1: new DDR MMC card at address 0001
    [    2.024667] mmcblk1: mmc1:0001 MMC32G 29.5 GiB
    [    2.027096] mmcblk1boot0: mmc1:0001 MMC32G partition 1 16.0 MiB
    [    2.027248] mmcblk1boot1: mmc1:0001 MMC32G partition 2 16.0 MiB
    [    2.038234]  mmcblk1: p1 p2
    [    2.052816] fb_ili9341 spi4.0: 1F 1A 18 0A 0F 06 45 87 32 0A 07 02 07 05 00
    [    2.052816] 00 25 27 05 10 09 3A 78 4D 05 18 0D 38 3A 1F
    [    2.065223] fb_ili9341 spi4.0: fbtft_verify_gpios()
    [    2.070177] fb_ili9341 spi4.0: init_display()
    [    2.199941] fb_ili9341 spi4.0: set_var()
    [    2.288413] fb_ili9341 spi4.0: Display update: 1774 kB/s (84.498 ms), fps=0 (0.000 ms)
    [    2.296369] fb_ili9341 spi4.0: set_gamma()
    [    2.301162] Console: switching to colour frame buffer device 40x30
    [    2.307752] graphics fb0: fb_ili9341 frame buffer, 320x240, 150 KiB video memory, 16 KiB DMA buffer memory, fps=33, spi4.0 at 32 MHz
    [    2.323030] davinci-mcasp 48468000.mcasp: ERRATA i868 workaround is enabled
    [    2.332830] omap-hdmi-audio omap-hdmi-audio.0.auto: snd-soc-dummy-dai <-> 58040000.encoder mapping ok
    [    2.343410] oprofile: using timer interrupt.
    [    2.348010] Initializing XFRM netlink socket
    [    2.352402] NET: Registered protocol family 17
    [    2.356970] NET: Registered protocol family 15
    [    2.361608] Key type dns_resolver registered
    [    2.366097] omap_voltage_late_init: Voltage driver support not added
    [    2.372833] Adding alias for supply vdd,cpu0 -> vdd,4a003b20.voltdm
    [    2.379233] Adding alias for supply vbb,cpu0 -> vbb,4a003b20.voltdm
    [    2.386135] Adding alias for supply vdd,cpu0 -> vdd,4a003b20.voltdm
    [    2.392468] Adding alias for supply vbb,cpu0 -> vbb,4a003b20.voltdm
    [    2.400250] Power Management for TI OMAP4+ devices.
    [    2.405326] ThumbEE CPU extension supported.
    [    2.409638] Registering SWP/SWPB emulation handler
    [    2.415464] dmm 4e000000.dmm: workaround for errata i878 in use
    [    2.422802] dmm 4e000000.dmm: initialized all PAT entries
    [    2.448948] [drm] Supports vblank timestamp caching Rev 2 (21.10.2013).
    [    2.455590] [drm] No driver support for vblank timestamp query.
    [    2.462685] omapdss error: HDMI I2C Master Error // its not bothering me as of now but if you have seen this kind of error earlier please suggest something
    [    2.471666] [drm] Enabling DMM ywrap scrolling
    [    2.476278] omapdrm omapdrm.0: fb1: omapdrm frame buffer device
    [    2.482255] omapdrm omapdrm.0: registered panic notifier
    [    2.517335] omapwb-m2m: Device registered as video10
    [    2.522321] [drm] Initialized omapdrm 10.0 20110917 on minor 0
    [    2.647412] asoc-simple-card sound@0: parse error -2
    [    2.652409] asoc-simple-card: probe of sound@0 failed with error -2
    [    2.658962] hctosys: unable to open rtc device (rtc0)
    [    2.677367] omap_hsmmc 480ad000.mmc: card claims to support voltages below defined range
    [    2.685701] ldousb: disabling
    [    2.688960] aic_dvdd_fixed: disabling
    [    2.692813] ALSA device list:
    [    2.695491] mmc2: queuing unknown CIS tuple 0x91 (3 bytes)
    [    2.696529] mmc2: new SDIO card at address 0001
    [    2.705853]   #1: HDMI 58040000.encoder
    [    4.296776] EXT4-fs (mmcblk1p2): recovery complete
    [    4.303538] EXT4-fs (mmcblk1p2): mounted filesystem with ordered data mode. Opts: (null)
    [    4.311712] VFS: Mounted root (ext4 filesystem) on device 179:10.
    [    4.321589] devtmpfs: mounted
    [    4.324782] Freeing unused kernel memory: 340K (c089d000 - c08f2000)
    INIT: version 288 booting
    Starting udev
    [    4.940931] udevd[126]: starting version 182
    [    5.617175] vip 48970000.vip: VPDMA firmware loaded
    [    5.626620] vpe 489d0000.vpe: Device registered as /dev/video0 //here it should give  /dev/video1 also for vip but i am not getting.
    [    6.593411] FAT-fs (mmcblk1p1): Volume was not properly unmounted. Some data may be corrupt. Please run fsck.
    [    6.610182] kjournald starting.  Commit interval 5 seconds
    [    6.618165] EXT3-fs (mmcblk0p2): using internal journal



    Croot@am57xx-evm:~# ls /lib/firmware/
    NameServerApp.xe66                ducati-m3-core0.xem3.ipc-test-fw
    am57xx-pru1_0-fw                  fault.xe66
    am57xx-pru1_1-fw                  gatempapp.xe66
    am57xx-pru2_0-fw                  messageq_multi.xe66
    am57xx-pru2_1-fw                  messageq_multimulti.xe66
    dra7-dsp1-fw.xe66                 messageq_single.xe66
    dra7-dsp1-fw.xe66.dspdce-fw       ping_rpmsg.xe66
    dra7-dsp1-fw.xe66.ipc-test-fw     ping_tasks.xe66
    dra7-dsp1-fw.xe66.opencl-monitor  pru/
    dra7-dsp2-fw.xe66                 tesla-dsp.xe64T
    dra7-dsp2-fw.xe66.ipc-test-fw     tesla-dsp.xe64T.ipc-test-fw
    dra7-dsp2-fw.xe66.opencl-monitor  test_omx_dsp1_vayu.xe66
    dra7-ipu1-fw.xem4                 test_omx_dsp2_vayu.xe66
    dra7-ipu1-fw.xem4.ipc-test-fw     ti-connectivity/
    dra7-ipu2-fw.xem4                 tigon/
    dra7-ipu2-fw.xem4.ipc-test-fw     vpdma-1b8.bin //here i can see firware files is available
    ducati-m3-core0.xem3

    When  i am loading adv7180 driver its giving following messages:

    root@am57xx-evm:~/video# insmod adv7180.ko
    [  532.403512] probing adv7180 driver
    [  532.406937] adv7180 0-0020: chip found @ 0x20 (OMAP I2C adapter)
    [  532.413969] init control state
    [  532.438057] device init
    532.447621] vip1-s0: Port A: Using subdev adv7180 0-0020 for capture //for your doubt here we can see vip1 using subdevice adv7180
    [  532.454006] vip1-s0: No suitable format reported by subdev adv7180 0-002

    Please tell me where i am going wrong,  its very urgent for us to proceed further.

    Thanks & Regards

    Abhishek

  • V4l2 capture drivers using the v4l2 asynchronous sub-device registration mechanism are supposed to register their device node only after a successful hand shake with their configured sub-device driver.

    This is how all of our capture driver operates.

    If you want to use the VIP driver with your encoder device, you need to create a v4l2 subdevice driver that conforms to that mechanism.

    A quick grep in the kernel tree will provide you with plenty of examples.

    # cd <top of linux kernel sources>

    # git grep v4l2_async_register_subdev

    Regards,

    Manisha

  • Hi Manisha,

                           We are already registering adv7180 as v4l2 sub device, I have compared it with mt9t11x.c also but could not find any difference. We can see it in below code and in attached adv7180.c file.

    Can you please check the attached driver  file if  there  is any mistake ?

    static int adv7180_probe(struct i2c_client *client,
                             const struct i2c_device_id *id)
    {
            struct adv7180_state *state;
            struct v4l2_subdev *sd;
            int ret;
            printk("probing adv7180 driver\n");
            /* Check if the adapter supports the needed features */
            if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
                    return -EIO;

            v4l_info(client, "chip found @ 0x%02x (%s)\n",
                     client->addr, client->adapter->name);

            state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
            if (state == NULL)
                    return -ENOMEM;

            state->client = client;
            state->field = V4L2_FIELD_INTERLACED;
            state->chip_info = (struct adv7180_chip_info *)id->driver_data;

            if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
                    state->csi_client = i2c_new_dummy(client->adapter,
                                    ADV7180_DEFAULT_CSI_I2C_ADDR);
                    if (!state->csi_client)
                            return -ENOMEM;
    }

            if (state->chip_info->flags & ADV7180_FLAG_I2P) {
                    state->vpp_client = i2c_new_dummy(client->adapter,
                                    ADV7180_DEFAULT_VPP_I2C_ADDR);
                    if (!state->vpp_client) {
                            ret = -ENOMEM;
                            goto err_unregister_csi_client;
                    }
            }

            state->irq = client->irq;
            mutex_init(&state->mutex);
            state->autodetect = true;
            if (state->chip_info->flags & ADV7180_FLAG_RESET_POWERED)
                    state->powered = true;
            else
                    state->powered = false;
            state->input = 0;
            sd = &state->sd;
            v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
            sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;

            ret = adv7180_init_controls(state);
            if (ret)
                    goto err_unregister_vpp_client;

            state->pad.flags = MEDIA_PAD_FL_SOURCE;
            sd->entity.flags |= MEDIA_ENT_T_V4L2_SUBDEV_DECODER;
            ret = media_entity_init(&sd->entity, 1, &state->pad, 0);
            if (ret)
                    goto err_free_ctrl;

            ret = init_device(state);
    if (ret)
                    goto err_media_entity_cleanup;

            if (state->irq) {
                    ret = request_threaded_irq(client->irq, NULL, adv7180_irq,
                                               IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
                                               KBUILD_MODNAME, state);
                    if (ret)
                            goto err_media_entity_cleanup;
            }

            ret = v4l2_async_register_subdev(sd);
            if (ret)
                    goto err_free_irq;

            return 0;

    err_free_irq:
            if (state->irq > 0)
                    free_irq(client->irq, state);
    err_media_entity_cleanup:
            media_entity_cleanup(&sd->entity);
    err_free_ctrl:
            adv7180_exit_controls(state);
    err_unregister_vpp_client:
            if (state->chip_info->flags & ADV7180_FLAG_I2P)
                    i2c_unregister_device(state->vpp_client);
    err_unregister_csi_client:
            if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2)
                    i2c_unregister_device(state->csi_client);
            mutex_destroy(&state->mutex);
            return ret;
    }



    /*
     * adv7180.c Analog Devices ADV7180 video decoder driver
     * Copyright (c) 2009 Intel Corporation
     * Copyright (C) 2013 Cogent Embedded, Inc.
     * Copyright (C) 2013 Renesas Solutions Corp.
     *
     * This program is free software; you can redistribute it and/or modify
     * it under the terms of the GNU General Public License version 2 as
     * published by the Free Software Foundation.
     *
     * This program 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.
     *
     * You should have received a copy of the GNU General Public License
     * along with this program; if not, write to the Free Software
     * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     */
    
    #include <linux/module.h>
    #include <linux/init.h>
    #include <linux/errno.h>
    #include <linux/kernel.h>
    #include <linux/interrupt.h>
    #include <linux/i2c.h>
    #include <linux/slab.h>
    #include <linux/of.h>
    #include <media/v4l2-ioctl.h>
    #include <linux/videodev2.h>
    #include <media/v4l2-device.h>
    #include <media/v4l2-ctrls.h>
    #include <linux/mutex.h>
    #include <linux/delay.h>
    
    #define ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM		0x0
    #define ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM_PED		0x1
    #define ADV7180_STD_AD_PAL_N_NTSC_J_SECAM		0x2
    #define ADV7180_STD_AD_PAL_N_NTSC_M_SECAM		0x3
    #define ADV7180_STD_NTSC_J				0x4
    #define ADV7180_STD_NTSC_M				0x5
    #define ADV7180_STD_PAL60				0x6
    #define ADV7180_STD_NTSC_443				0x7
    #define ADV7180_STD_PAL_BG				0x8
    #define ADV7180_STD_PAL_N				0x9
    #define ADV7180_STD_PAL_M				0xa
    #define ADV7180_STD_PAL_M_PED				0xb
    #define ADV7180_STD_PAL_COMB_N				0xc
    #define ADV7180_STD_PAL_COMB_N_PED			0xd
    #define ADV7180_STD_PAL_SECAM				0xe
    #define ADV7180_STD_PAL_SECAM_PED			0xf
    
    #define ADV7180_REG_INPUT_CONTROL			0x0000
    #define ADV7180_INPUT_CONTROL_INSEL_MASK		0x0f
    
    #define ADV7182_REG_INPUT_VIDSEL			0x0002
    
    #define ADV7180_REG_EXTENDED_OUTPUT_CONTROL		0x0004
    #define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS		0xC5
    
    #define ADV7180_REG_AUTODETECT_ENABLE			0x07
    #define ADV7180_AUTODETECT_DEFAULT			0x7f
    /* Contrast */
    #define ADV7180_REG_CON		0x0008	/*Unsigned */
    #define ADV7180_CON_MIN		0
    #define ADV7180_CON_DEF		128
    #define ADV7180_CON_MAX		255
    /* Brightness*/
    #define ADV7180_REG_BRI		0x000a	/*Signed */
    #define ADV7180_BRI_MIN		-128
    #define ADV7180_BRI_DEF		0
    #define ADV7180_BRI_MAX		127
    /* Hue */
    #define ADV7180_REG_HUE		0x000b	/*Signed, inverted */
    #define ADV7180_HUE_MIN		-127
    #define ADV7180_HUE_DEF		0
    #define ADV7180_HUE_MAX		128
    
    #define ADV7180_REG_CTRL		0x000e
    #define ADV7180_CTRL_IRQ_SPACE		0x20
    
    #define ADV7180_REG_PWR_MAN		0x0f
    #define ADV7180_PWR_MAN_ON		0x04
    #define ADV7180_PWR_MAN_OFF		0x24
    #define ADV7180_PWR_MAN_RES		0x80
    
    #define ADV7180_REG_STATUS1		0x0010
    #define ADV7180_STATUS1_IN_LOCK		0x01
    #define ADV7180_STATUS1_AUTOD_MASK	0x70
    #define ADV7180_STATUS1_AUTOD_NTSM_M_J	0x00
    #define ADV7180_STATUS1_AUTOD_NTSC_4_43 0x10
    #define ADV7180_STATUS1_AUTOD_PAL_M	0x20
    #define ADV7180_STATUS1_AUTOD_PAL_60	0x30
    #define ADV7180_STATUS1_AUTOD_PAL_B_G	0x40
    #define ADV7180_STATUS1_AUTOD_SECAM	0x50
    #define ADV7180_STATUS1_AUTOD_PAL_COMB	0x60
    #define ADV7180_STATUS1_AUTOD_SECAM_525	0x70
    
    #define ADV7180_REG_IDENT 0x0011
    #define ADV7180_ID_7180 0x18
    
    #define ADV7180_REG_ICONF1		0x0040
    #define ADV7180_ICONF1_ACTIVE_LOW	0x01
    #define ADV7180_ICONF1_PSYNC_ONLY	0x10
    #define ADV7180_ICONF1_ACTIVE_TO_CLR	0xC0
    /* Saturation */
    #define ADV7180_REG_SD_SAT_CB	0x00e3	/*Unsigned */
    #define ADV7180_REG_SD_SAT_CR	0x00e4	/*Unsigned */
    #define ADV7180_SAT_MIN		0
    #define ADV7180_SAT_DEF		128
    #define ADV7180_SAT_MAX		255
    
    #define ADV7180_IRQ1_LOCK	0x01
    #define ADV7180_IRQ1_UNLOCK	0x02
    #define ADV7180_REG_ISR1	0x0042
    #define ADV7180_REG_ICR1	0x0043
    #define ADV7180_REG_IMR1	0x0044
    #define ADV7180_REG_IMR2	0x0048
    #define ADV7180_IRQ3_AD_CHANGE	0x08
    #define ADV7180_REG_ISR3	0x004A
    #define ADV7180_REG_ICR3	0x004B
    #define ADV7180_REG_IMR3	0x004C
    #define ADV7180_REG_IMR4	0x50
    
    #define ADV7180_REG_NTSC_V_BIT_END	0x00E6
    #define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND	0x4F
    
    #define ADV7180_REG_VPP_SLAVE_ADDR	0xFD
    #define ADV7180_REG_CSI_SLAVE_ADDR	0xFE
    
    #define ADV7180_REG_FLCONTROL 0x40e0
    #define ADV7180_FLCONTROL_FL_ENABLE 0x1
    
    #define ADV7180_CSI_REG_PWRDN	0x00
    #define ADV7180_CSI_PWRDN	0x80
    
    #define ADV7180_INPUT_CVBS_AIN1 0x00
    #define ADV7180_INPUT_CVBS_AIN2 0x01
    #define ADV7180_INPUT_CVBS_AIN3 0x02
    #define ADV7180_INPUT_CVBS_AIN4 0x03
    #define ADV7180_INPUT_CVBS_AIN5 0x04
    #define ADV7180_INPUT_CVBS_AIN6 0x05
    #define ADV7180_INPUT_SVIDEO_AIN1_AIN2 0x06
    #define ADV7180_INPUT_SVIDEO_AIN3_AIN4 0x07
    #define ADV7180_INPUT_SVIDEO_AIN5_AIN6 0x08
    #define ADV7180_INPUT_YPRPB_AIN1_AIN2_AIN3 0x09
    #define ADV7180_INPUT_YPRPB_AIN4_AIN5_AIN6 0x0a
    
    #define ADV7182_INPUT_CVBS_AIN1 0x00
    #define ADV7182_INPUT_CVBS_AIN2 0x01
    #define ADV7182_INPUT_CVBS_AIN3 0x02
    #define ADV7182_INPUT_CVBS_AIN4 0x03
    #define ADV7182_INPUT_CVBS_AIN5 0x04
    #define ADV7182_INPUT_CVBS_AIN6 0x05
    #define ADV7182_INPUT_CVBS_AIN7 0x06
    #define ADV7182_INPUT_CVBS_AIN8 0x07
    #define ADV7182_INPUT_SVIDEO_AIN1_AIN2 0x08
    #define ADV7182_INPUT_SVIDEO_AIN3_AIN4 0x09
    #define ADV7182_INPUT_SVIDEO_AIN5_AIN6 0x0a
    #define ADV7182_INPUT_SVIDEO_AIN7_AIN8 0x0b
    #define ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3 0x0c
    #define ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6 0x0d
    #define ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2 0x0e
    #define ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4 0x0f
    #define ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6 0x10
    #define ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8 0x11
    
    #define ADV7180_DEFAULT_CSI_I2C_ADDR 0x44
    #define ADV7180_DEFAULT_VPP_I2C_ADDR 0x42
    
    #define V4L2_CID_ADV_FAST_SWITCH	(V4L2_CID_USER_ADV7180_BASE + 0x00)
    
    struct adv7180_state;
    
    #define ADV7180_FLAG_RESET_POWERED	BIT(0)
    #define ADV7180_FLAG_V2			BIT(1)
    #define ADV7180_FLAG_MIPI_CSI2		BIT(2)
    #define ADV7180_FLAG_I2P		BIT(3)
    
    struct adv7180_chip_info {
    	unsigned int flags;
    	unsigned int valid_input_mask;
    	int (*set_std)(struct adv7180_state *st, unsigned int std);
    	int (*select_input)(struct adv7180_state *st, unsigned int input);
    	int (*init)(struct adv7180_state *state);
    };
    
    struct adv7180_state {
    	struct v4l2_ctrl_handler ctrl_hdl;
    	struct v4l2_subdev	sd;
    	struct media_pad	pad;
    	struct mutex		mutex; /* mutual excl. when accessing chip */
    	int			irq;
    	v4l2_std_id		curr_norm;
    	bool			autodetect;
    	bool			powered;
    	u8			input;
    
    	struct i2c_client	*client;
    	unsigned int		register_page;
    	struct i2c_client	*csi_client;
    	struct i2c_client	*vpp_client;
    	const struct adv7180_chip_info *chip_info;
    	enum v4l2_field		field;
    };
    #define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler,		\
    					    struct adv7180_state,	\
    					    ctrl_hdl)->sd)
    
    static int adv7180_select_page(struct adv7180_state *state, unsigned int page)
    {
    	if (state->register_page != page) {
    		i2c_smbus_write_byte_data(state->client, ADV7180_REG_CTRL,
    			page);
    		state->register_page = page;
    	}
    
    	return 0;
    }
    
    static int adv7180_write(struct adv7180_state *state, unsigned int reg,
    	unsigned int value)
    {
    	lockdep_assert_held(&state->mutex);
    	adv7180_select_page(state, reg >> 8);
    	return i2c_smbus_write_byte_data(state->client, reg & 0xff, value);
    }
    
    static int adv7180_read(struct adv7180_state *state, unsigned int reg)
    {
    	lockdep_assert_held(&state->mutex);
    	adv7180_select_page(state, reg >> 8);
    	return i2c_smbus_read_byte_data(state->client, reg & 0xff);
    }
    
    static int adv7180_csi_write(struct adv7180_state *state, unsigned int reg,
    	unsigned int value)
    {
    	return i2c_smbus_write_byte_data(state->csi_client, reg, value);
    }
    
    static int adv7180_set_video_standard(struct adv7180_state *state,
    	unsigned int std)
    {
    	return state->chip_info->set_std(state, std);
    }
    
    static int adv7180_vpp_write(struct adv7180_state *state, unsigned int reg,
    	unsigned int value)
    {
    	return i2c_smbus_write_byte_data(state->vpp_client, reg, value);
    }
    
    static v4l2_std_id adv7180_std_to_v4l2(u8 status1)
    {
    	/* in case V4L2_IN_ST_NO_SIGNAL */
    	if (!(status1 & ADV7180_STATUS1_IN_LOCK))
    		return V4L2_STD_UNKNOWN;
    
    	switch (status1 & ADV7180_STATUS1_AUTOD_MASK) {
    	case ADV7180_STATUS1_AUTOD_NTSM_M_J:
    		return V4L2_STD_NTSC;
    	case ADV7180_STATUS1_AUTOD_NTSC_4_43:
    		return V4L2_STD_NTSC_443;
    	case ADV7180_STATUS1_AUTOD_PAL_M:
    		return V4L2_STD_PAL_M;
    	case ADV7180_STATUS1_AUTOD_PAL_60:
    		return V4L2_STD_PAL_60;
    	case ADV7180_STATUS1_AUTOD_PAL_B_G:
    		return V4L2_STD_PAL;
    	case ADV7180_STATUS1_AUTOD_SECAM:
    		return V4L2_STD_SECAM;
    	case ADV7180_STATUS1_AUTOD_PAL_COMB:
    		return V4L2_STD_PAL_Nc | V4L2_STD_PAL_N;
    	case ADV7180_STATUS1_AUTOD_SECAM_525:
    		return V4L2_STD_SECAM;
    	default:
    		return V4L2_STD_UNKNOWN;
    	}
    }
    
    static int v4l2_std_to_adv7180(v4l2_std_id std)
    {
    	if (std == V4L2_STD_PAL_60)
    		return ADV7180_STD_PAL60;
    	if (std == V4L2_STD_NTSC_443)
    		return ADV7180_STD_NTSC_443;
    	if (std == V4L2_STD_PAL_N)
    		return ADV7180_STD_PAL_N;
    	if (std == V4L2_STD_PAL_M)
    		return ADV7180_STD_PAL_M;
    	if (std == V4L2_STD_PAL_Nc)
    		return ADV7180_STD_PAL_COMB_N;
    
    	if (std & V4L2_STD_PAL)
    		return ADV7180_STD_PAL_BG;
    	if (std & V4L2_STD_NTSC)
    		return ADV7180_STD_NTSC_M;
    	if (std & V4L2_STD_SECAM)
    		return ADV7180_STD_PAL_SECAM;
    
    	return -EINVAL;
    }
    
    static u32 adv7180_status_to_v4l2(u8 status1)
    {
    	if (!(status1 & ADV7180_STATUS1_IN_LOCK))
    		return V4L2_IN_ST_NO_SIGNAL;
    
    	return 0;
    }
    
    static int __adv7180_status(struct adv7180_state *state, u32 *status,
    			    v4l2_std_id *std)
    {
    	int status1 = adv7180_read(state, ADV7180_REG_STATUS1);
    
    	if (status1 < 0)
    		return status1;
    
    	if (status)
    		*status = adv7180_status_to_v4l2(status1);
    	if (std)
    		*std = adv7180_std_to_v4l2(status1);
    
    	return 0;
    }
    
    static inline struct adv7180_state *to_state(struct v4l2_subdev *sd)
    {
    	return container_of(sd, struct adv7180_state, sd);
    }
    
    static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
    {
    	struct adv7180_state *state = to_state(sd);
    	int err = mutex_lock_interruptible(&state->mutex);
    	if (err)
    		return err;
    
    	/* when we are interrupt driven we know the state */
    	if (!state->autodetect || state->irq > 0)
    		*std = state->curr_norm;
    	else
    		err = __adv7180_status(state, NULL, std);
    
    	mutex_unlock(&state->mutex);
    	return err;
    }
    
    static int adv7180_s_routing(struct v4l2_subdev *sd, u32 input,
    			     u32 output, u32 config)
    {
    	struct adv7180_state *state = to_state(sd);
    	int ret = mutex_lock_interruptible(&state->mutex);
    
    	if (ret)
    		return ret;
    
    	if (input > 31 || !(BIT(input) & state->chip_info->valid_input_mask)) {
    		ret = -EINVAL;
    		goto out;
    	}
    
    	ret = state->chip_info->select_input(state, input);
    
    	if (ret == 0)
    		state->input = input;
    out:
    	mutex_unlock(&state->mutex);
    	return ret;
    }
    
    static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status)
    {
    	struct adv7180_state *state = to_state(sd);
    	int ret = mutex_lock_interruptible(&state->mutex);
    	if (ret)
    		return ret;
    
    	ret = __adv7180_status(state, status, NULL);
    	mutex_unlock(&state->mutex);
    	return ret;
    }
    
    static int adv7180_program_std(struct adv7180_state *state)
    {
    	int ret;
    
    	if (state->autodetect) {
    		ret = adv7180_set_video_standard(state,
    			ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM);
    		if (ret < 0)
    			return ret;
    
    		__adv7180_status(state, NULL, &state->curr_norm);
    	} else {
    		ret = v4l2_std_to_adv7180(state->curr_norm);
    		if (ret < 0)
    			return ret;
    
    		ret = adv7180_set_video_standard(state, ret);
    		if (ret < 0)
    			return ret;
    	}
    
    	return 0;
    }
    
    static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
    {
    	struct adv7180_state *state = to_state(sd);
    	int ret = mutex_lock_interruptible(&state->mutex);
    
    	if (ret)
    		return ret;
    
    	/* all standards -> autodetect */
    	if (std == V4L2_STD_ALL) {
    		state->autodetect = true;
    	} else {
    		/* Make sure we can support this std */
    		ret = v4l2_std_to_adv7180(std);
    		if (ret < 0)
    			goto out;
    
    		state->curr_norm = std;
    		state->autodetect = false;
    	}
    
    	ret = adv7180_program_std(state);
    out:
    	mutex_unlock(&state->mutex);
    	return ret;
    }
    
    static int adv7180_set_power(struct adv7180_state *state, bool on)
    {
    	u8 val;
    	int ret;
    
    	if (on)
    		val = ADV7180_PWR_MAN_ON;
    	else
    		val = ADV7180_PWR_MAN_OFF;
    
    	ret = adv7180_write(state, ADV7180_REG_PWR_MAN, val);
    	if (ret)
    		return ret;
    
    	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
    		if (on) {
    			adv7180_csi_write(state, 0xDE, 0x02);
    			adv7180_csi_write(state, 0xD2, 0xF7);
    			adv7180_csi_write(state, 0xD8, 0x65);
    			adv7180_csi_write(state, 0xE0, 0x09);
    			adv7180_csi_write(state, 0x2C, 0x00);
    			if (state->field == V4L2_FIELD_NONE)
    				adv7180_csi_write(state, 0x1D, 0x80);
    			adv7180_csi_write(state, 0x00, 0x00);
    		} else {
    			adv7180_csi_write(state, 0x00, 0x80);
    		}
    	}
    
    	return 0;
    }
    
    static int adv7180_s_power(struct v4l2_subdev *sd, int on)
    {
    	struct adv7180_state *state = to_state(sd);
    	int ret;
    
    	ret = mutex_lock_interruptible(&state->mutex);
    	if (ret)
    		return ret;
    
    	ret = adv7180_set_power(state, on);
    	if (ret == 0)
    		state->powered = on;
    
    	mutex_unlock(&state->mutex);
    	return ret;
    }
    
    static int adv7180_s_ctrl(struct v4l2_ctrl *ctrl)
    {
    	struct v4l2_subdev *sd = to_adv7180_sd(ctrl);
    	struct adv7180_state *state = to_state(sd);
    	int ret = mutex_lock_interruptible(&state->mutex);
    	int val;
    
    	if (ret)
    		return ret;
    	val = ctrl->val;
    	switch (ctrl->id) {
    	case V4L2_CID_BRIGHTNESS:
    		ret = adv7180_write(state, ADV7180_REG_BRI, val);
    		break;
    	case V4L2_CID_HUE:
    		/*Hue is inverted according to HSL chart */
    		ret = adv7180_write(state, ADV7180_REG_HUE, -val);
    		break;
    	case V4L2_CID_CONTRAST:
    		ret = adv7180_write(state, ADV7180_REG_CON, val);
    		break;
    	case V4L2_CID_SATURATION:
    		/*
    		 *This could be V4L2_CID_BLUE_BALANCE/V4L2_CID_RED_BALANCE
    		 *Let's not confuse the user, everybody understands saturation
    		 */
    		ret = adv7180_write(state, ADV7180_REG_SD_SAT_CB, val);
    		if (ret < 0)
    			break;
    		ret = adv7180_write(state, ADV7180_REG_SD_SAT_CR, val);
    		break;
    	case V4L2_CID_ADV_FAST_SWITCH:
    		if (ctrl->val) {
    			/* ADI required write */
    			adv7180_write(state, 0x80d9, 0x44);
    			adv7180_write(state, ADV7180_REG_FLCONTROL,
    				ADV7180_FLCONTROL_FL_ENABLE);
    		} else {
    			/* ADI required write */
    			adv7180_write(state, 0x80d9, 0xc4);
    			adv7180_write(state, ADV7180_REG_FLCONTROL, 0x00);
    		}
    		break;
    	default:
    		ret = -EINVAL;
    	}
    
    	mutex_unlock(&state->mutex);
    	return ret;
    }
    
    static const struct v4l2_ctrl_ops adv7180_ctrl_ops = {
    	.s_ctrl = adv7180_s_ctrl,
    };
    
    static const struct v4l2_ctrl_config adv7180_ctrl_fast_switch = {
    	.ops = &adv7180_ctrl_ops,
    	.id = V4L2_CID_ADV_FAST_SWITCH,
    	.name = "Fast Switching",
    	.type = V4L2_CTRL_TYPE_BOOLEAN,
    	.min = 0,
    	.max = 1,
    	.step = 1,
    };
    
    static int adv7180_init_controls(struct adv7180_state *state)
    {
    	printk("init control state\n");
    	v4l2_ctrl_handler_init(&state->ctrl_hdl, 4);
    
    	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
    			  V4L2_CID_BRIGHTNESS, ADV7180_BRI_MIN,
    			  ADV7180_BRI_MAX, 1, ADV7180_BRI_DEF);
    	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
    			  V4L2_CID_CONTRAST, ADV7180_CON_MIN,
    			  ADV7180_CON_MAX, 1, ADV7180_CON_DEF);
    	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
    			  V4L2_CID_SATURATION, ADV7180_SAT_MIN,
    			  ADV7180_SAT_MAX, 1, ADV7180_SAT_DEF);
    	v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops,
    			  V4L2_CID_HUE, ADV7180_HUE_MIN,
    			  ADV7180_HUE_MAX, 1, ADV7180_HUE_DEF);
    	v4l2_ctrl_new_custom(&state->ctrl_hdl, &adv7180_ctrl_fast_switch, NULL);
    
    	state->sd.ctrl_handler = &state->ctrl_hdl;
    	if (state->ctrl_hdl.error) {
    		int err = state->ctrl_hdl.error;
    
    		v4l2_ctrl_handler_free(&state->ctrl_hdl);
    		return err;
    	}
    	v4l2_ctrl_handler_setup(&state->ctrl_hdl);
    
    	return 0;
    }
    static void adv7180_exit_controls(struct adv7180_state *state)
    {
    	v4l2_ctrl_handler_free(&state->ctrl_hdl);
    }
    
    static int adv7180_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_YUYV8_2X8;
    
    	return 0;
    }
    
    static int adv7180_mbus_fmt(struct v4l2_subdev *sd,
    			    struct v4l2_mbus_framefmt *fmt)
    {
    	struct adv7180_state *state = to_state(sd);
    
    	fmt->code = MEDIA_BUS_FMT_YUYV8_2X8;
    	fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
    	fmt->width = 720;
    	fmt->height = state->curr_norm & V4L2_STD_525_60 ? 480 : 576;
    
    	return 0;
    }
    
    static int adv7180_set_field_mode(struct adv7180_state *state)
    {
    	if (!(state->chip_info->flags & ADV7180_FLAG_I2P))
    		return 0;
    
    	if (state->field == V4L2_FIELD_NONE) {
    		if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
    			adv7180_csi_write(state, 0x01, 0x20);
    			adv7180_csi_write(state, 0x02, 0x28);
    			adv7180_csi_write(state, 0x03, 0x38);
    			adv7180_csi_write(state, 0x04, 0x30);
    			adv7180_csi_write(state, 0x05, 0x30);
    			adv7180_csi_write(state, 0x06, 0x80);
    			adv7180_csi_write(state, 0x07, 0x70);
    			adv7180_csi_write(state, 0x08, 0x50);
    		}
    		adv7180_vpp_write(state, 0xa3, 0x00);
    		adv7180_vpp_write(state, 0x5b, 0x00);
    		adv7180_vpp_write(state, 0x55, 0x80);
    	} else {
    		if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
    			adv7180_csi_write(state, 0x01, 0x18);
    			adv7180_csi_write(state, 0x02, 0x18);
    			adv7180_csi_write(state, 0x03, 0x30);
    			adv7180_csi_write(state, 0x04, 0x20);
    			adv7180_csi_write(state, 0x05, 0x28);
    			adv7180_csi_write(state, 0x06, 0x40);
    			adv7180_csi_write(state, 0x07, 0x58);
    			adv7180_csi_write(state, 0x08, 0x30);
    		}
    		adv7180_vpp_write(state, 0xa3, 0x70);
    		adv7180_vpp_write(state, 0x5b, 0x80);
    		adv7180_vpp_write(state, 0x55, 0x00);
    	}
    
    	return 0;
    }
    
    static int adv7180_get_pad_format(struct v4l2_subdev *sd,
    				  struct v4l2_subdev_pad_config *cfg,
    				  struct v4l2_subdev_format *format)
    {
    	struct adv7180_state *state = to_state(sd);
    
    	if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
    		format->format = *v4l2_subdev_get_try_format(sd, cfg, 0);
    	} else {
    		adv7180_mbus_fmt(sd, &format->format);
    		format->format.field = state->field;
    	}
    
    	return 0;
    }
    
    static int adv7180_set_pad_format(struct v4l2_subdev *sd,
    				  struct v4l2_subdev_pad_config *cfg,
    				  struct v4l2_subdev_format *format)
    {
    	struct adv7180_state *state = to_state(sd);
    	struct v4l2_mbus_framefmt *framefmt;
    
    	switch (format->format.field) {
    	case V4L2_FIELD_NONE:
    		if (!(state->chip_info->flags & ADV7180_FLAG_I2P))
    			format->format.field = V4L2_FIELD_INTERLACED;
    		break;
    	default:
    		format->format.field = V4L2_FIELD_INTERLACED;
    		break;
    	}
    
    	if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
    		framefmt = &format->format;
    		if (state->field != format->format.field) {
    			state->field = format->format.field;
    			adv7180_set_power(state, false);
    			adv7180_set_field_mode(state);
    			adv7180_set_power(state, true);
    		}
    	} else {
    		framefmt = v4l2_subdev_get_try_format(sd, cfg, 0);
    		*framefmt = format->format;
    	}
    
    	return adv7180_mbus_fmt(sd, framefmt);
    }
    
    static int adv7180_g_mbus_config(struct v4l2_subdev *sd,
    				 struct v4l2_mbus_config *cfg)
    {
    	struct adv7180_state *state = to_state(sd);
    
    	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
    		cfg->type = V4L2_MBUS_CSI2;
    		cfg->flags = V4L2_MBUS_CSI2_1_LANE |
    				V4L2_MBUS_CSI2_CHANNEL_0 |
    				V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
    	} else {
    		/*
    		 * The ADV7180 sensor supports BT.601/656 output modes.
    		 * The BT.656 is default and not yet configurable by s/w.
    		 */
    		cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING |
    				 V4L2_MBUS_DATA_ACTIVE_HIGH;
    		cfg->type = V4L2_MBUS_BT656;
    	}
    
    	return 0;
    }
    
    static const struct v4l2_subdev_video_ops adv7180_video_ops = {
    	.s_std = adv7180_s_std,
    	.querystd = adv7180_querystd,
    	.g_input_status = adv7180_g_input_status,
    	.s_routing = adv7180_s_routing,
    	.g_mbus_config = adv7180_g_mbus_config,
    };
    
    
    static const struct v4l2_subdev_core_ops adv7180_core_ops = {
    	.s_power = adv7180_s_power,
    };
    
    static const struct v4l2_subdev_pad_ops adv7180_pad_ops = {
    	.enum_mbus_code = adv7180_enum_mbus_code,
    	.set_fmt = adv7180_set_pad_format,
    	.get_fmt = adv7180_get_pad_format,
    };
    
    static const struct v4l2_subdev_ops adv7180_ops = {
    	.core = &adv7180_core_ops,
    	.video = &adv7180_video_ops,
    	.pad = &adv7180_pad_ops,
    };
    
    static irqreturn_t adv7180_irq(int irq, void *devid)
    {
    	struct adv7180_state *state = devid;
    	u8 isr3;
    
    	mutex_lock(&state->mutex);
    	isr3 = adv7180_read(state, ADV7180_REG_ISR3);
    	/* clear */
    	adv7180_write(state, ADV7180_REG_ICR3, isr3);
    
    	if (isr3 & ADV7180_IRQ3_AD_CHANGE && state->autodetect)
    		__adv7180_status(state, NULL, &state->curr_norm);
    	mutex_unlock(&state->mutex);
    
    	return IRQ_HANDLED;
    }
    
    static int adv7180_init(struct adv7180_state *state)
    {
    	int ret;
    
    	/* ITU-R BT.656-4 compatible */
    	ret = adv7180_write(state, ADV7180_REG_EXTENDED_OUTPUT_CONTROL,
    			ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS);
    	if (ret < 0)
    		return ret;
    
    	/* Manually set V bit end position in NTSC mode */
    	return adv7180_write(state, ADV7180_REG_NTSC_V_BIT_END,
    					ADV7180_NTSC_V_BIT_END_MANUAL_NVEND);
    }
    
    static int adv7180_set_std(struct adv7180_state *state, unsigned int std)
    {
    	return adv7180_write(state, ADV7180_REG_INPUT_CONTROL,
    		(std << 4) | state->input);
    }
    
    static int adv7180_select_input(struct adv7180_state *state, unsigned int input)
    {
    	int ret;
    
    	ret = adv7180_read(state, ADV7180_REG_INPUT_CONTROL);
    	if (ret < 0)
    		return ret;
    
    	ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK;
    	ret |= input;
    	return adv7180_write(state, ADV7180_REG_INPUT_CONTROL, ret);
    }
    
    static int adv7182_init(struct adv7180_state *state)
    {
    	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2)
    		adv7180_write(state, ADV7180_REG_CSI_SLAVE_ADDR,
    			ADV7180_DEFAULT_CSI_I2C_ADDR << 1);
    
    	if (state->chip_info->flags & ADV7180_FLAG_I2P)
    		adv7180_write(state, ADV7180_REG_VPP_SLAVE_ADDR,
    			ADV7180_DEFAULT_VPP_I2C_ADDR << 1);
    
    	if (state->chip_info->flags & ADV7180_FLAG_V2) {
    		/* ADI recommended writes for improved video quality */
    		adv7180_write(state, 0x0080, 0x51);
    		adv7180_write(state, 0x0081, 0x51);
    		adv7180_write(state, 0x0082, 0x68);
    	}
    
    	/* ADI required writes */
    	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
    		adv7180_write(state, 0x0003, 0x4e);
    		adv7180_write(state, 0x0004, 0x57);
    		adv7180_write(state, 0x001d, 0xc0);
    	} else {
    		if (state->chip_info->flags & ADV7180_FLAG_V2)
    			adv7180_write(state, 0x0004, 0x17);
    		else
    			adv7180_write(state, 0x0004, 0x07);
    		adv7180_write(state, 0x0003, 0x0c);
    		adv7180_write(state, 0x001d, 0x40);
    	}
    
    	adv7180_write(state, 0x0013, 0x00);
    
    	return 0;
    }
    
    static int adv7182_set_std(struct adv7180_state *state, unsigned int std)
    {
    	return adv7180_write(state, ADV7182_REG_INPUT_VIDSEL, std << 4);
    }
    
    enum adv7182_input_type {
    	ADV7182_INPUT_TYPE_CVBS,
    	ADV7182_INPUT_TYPE_DIFF_CVBS,
    	ADV7182_INPUT_TYPE_SVIDEO,
    	ADV7182_INPUT_TYPE_YPBPR,
    };
    
    static enum adv7182_input_type adv7182_get_input_type(unsigned int input)
    {
    	switch (input) {
    	case ADV7182_INPUT_CVBS_AIN1:
    	case ADV7182_INPUT_CVBS_AIN2:
    	case ADV7182_INPUT_CVBS_AIN3:
    	case ADV7182_INPUT_CVBS_AIN4:
    	case ADV7182_INPUT_CVBS_AIN5:
    	case ADV7182_INPUT_CVBS_AIN6:
    	case ADV7182_INPUT_CVBS_AIN7:
    	case ADV7182_INPUT_CVBS_AIN8:
    		return ADV7182_INPUT_TYPE_CVBS;
    	case ADV7182_INPUT_SVIDEO_AIN1_AIN2:
    	case ADV7182_INPUT_SVIDEO_AIN3_AIN4:
    	case ADV7182_INPUT_SVIDEO_AIN5_AIN6:
    	case ADV7182_INPUT_SVIDEO_AIN7_AIN8:
    		return ADV7182_INPUT_TYPE_SVIDEO;
    	case ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3:
    	case ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6:
    		return ADV7182_INPUT_TYPE_YPBPR;
    	case ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2:
    	case ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4:
    	case ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6:
    	case ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8:
    		return ADV7182_INPUT_TYPE_DIFF_CVBS;
    	default: /* Will never happen */
    		return 0;
    	}
    }
    
    /* ADI recommended writes to registers 0x52, 0x53, 0x54 */
    static unsigned int adv7182_lbias_settings[][3] = {
    	[ADV7182_INPUT_TYPE_CVBS] = { 0xCB, 0x4E, 0x80 },
    	[ADV7182_INPUT_TYPE_DIFF_CVBS] = { 0xC0, 0x4E, 0x80 },
    	[ADV7182_INPUT_TYPE_SVIDEO] = { 0x0B, 0xCE, 0x80 },
    	[ADV7182_INPUT_TYPE_YPBPR] = { 0x0B, 0x4E, 0xC0 },
    };
    
    static unsigned int adv7280_lbias_settings[][3] = {
    	[ADV7182_INPUT_TYPE_CVBS] = { 0xCD, 0x4E, 0x80 },
    	[ADV7182_INPUT_TYPE_DIFF_CVBS] = { 0xC0, 0x4E, 0x80 },
    	[ADV7182_INPUT_TYPE_SVIDEO] = { 0x0B, 0xCE, 0x80 },
    	[ADV7182_INPUT_TYPE_YPBPR] = { 0x0B, 0x4E, 0xC0 },
    };
    
    static int adv7182_select_input(struct adv7180_state *state, unsigned int input)
    {
    	enum adv7182_input_type input_type;
    	unsigned int *lbias;
    	unsigned int i;
    	int ret;
    
    	ret = adv7180_write(state, ADV7180_REG_INPUT_CONTROL, input);
    	if (ret)
    		return ret;
    
    	/* Reset clamp circuitry - ADI recommended writes */
    	adv7180_write(state, 0x809c, 0x00);
    	adv7180_write(state, 0x809c, 0xff);
    
    	input_type = adv7182_get_input_type(input);
    
    	switch (input_type) {
    	case ADV7182_INPUT_TYPE_CVBS:
    	case ADV7182_INPUT_TYPE_DIFF_CVBS:
    		/* ADI recommends to use the SH1 filter */
    		adv7180_write(state, 0x0017, 0x41);
    		break;
    	default:
    		adv7180_write(state, 0x0017, 0x01);
    		break;
    	}
    
    	if (state->chip_info->flags & ADV7180_FLAG_V2)
    		lbias = adv7280_lbias_settings[input_type];
    	else
    		lbias = adv7182_lbias_settings[input_type];
    
    	for (i = 0; i < ARRAY_SIZE(adv7182_lbias_settings[0]); i++)
    		adv7180_write(state, 0x0052 + i, lbias[i]);
    
    	if (input_type == ADV7182_INPUT_TYPE_DIFF_CVBS) {
    		/* ADI required writes to make differential CVBS work */
    		adv7180_write(state, 0x005f, 0xa8);
    		adv7180_write(state, 0x005a, 0x90);
    		adv7180_write(state, 0x0060, 0xb0);
    		adv7180_write(state, 0x80b6, 0x08);
    		adv7180_write(state, 0x80c0, 0xa0);
    	} else {
    		adv7180_write(state, 0x005f, 0xf0);
    		adv7180_write(state, 0x005a, 0xd0);
    		adv7180_write(state, 0x0060, 0x10);
    		adv7180_write(state, 0x80b6, 0x9c);
    		adv7180_write(state, 0x80c0, 0x00);
    	}
    
    	return 0;
    }
    
    static const struct adv7180_chip_info adv7180_info = {
    	.flags = ADV7180_FLAG_RESET_POWERED,
    	/* We cannot discriminate between LQFP and 40-pin LFCSP, so accept
    	 * all inputs and let the card driver take care of validation
    	 */
    	.valid_input_mask = BIT(ADV7180_INPUT_CVBS_AIN1) |
    		BIT(ADV7180_INPUT_CVBS_AIN2) |
    		BIT(ADV7180_INPUT_CVBS_AIN3) |
    		BIT(ADV7180_INPUT_CVBS_AIN4) |
    		BIT(ADV7180_INPUT_CVBS_AIN5) |
    		BIT(ADV7180_INPUT_CVBS_AIN6) |
    		BIT(ADV7180_INPUT_SVIDEO_AIN1_AIN2) |
    		BIT(ADV7180_INPUT_SVIDEO_AIN3_AIN4) |
    		BIT(ADV7180_INPUT_SVIDEO_AIN5_AIN6) |
    		BIT(ADV7180_INPUT_YPRPB_AIN1_AIN2_AIN3) |
    		BIT(ADV7180_INPUT_YPRPB_AIN4_AIN5_AIN6),
    	.init = adv7180_init,
    	.set_std = adv7180_set_std,
    	.select_input = adv7180_select_input,
    };
    
    static const struct adv7180_chip_info adv7182_info = {
    	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
    		BIT(ADV7182_INPUT_CVBS_AIN2) |
    		BIT(ADV7182_INPUT_CVBS_AIN3) |
    		BIT(ADV7182_INPUT_CVBS_AIN4) |
    		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
    		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
    		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
    		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
    		BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4),
    	.init = adv7182_init,
    	.set_std = adv7182_set_std,
    	.select_input = adv7182_select_input,
    };
    
    static const struct adv7180_chip_info adv7280_info = {
    	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_I2P,
    	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
    		BIT(ADV7182_INPUT_CVBS_AIN2) |
    		BIT(ADV7182_INPUT_CVBS_AIN3) |
    		BIT(ADV7182_INPUT_CVBS_AIN4) |
    		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
    		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
    		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3),
    	.init = adv7182_init,
    	.set_std = adv7182_set_std,
    	.select_input = adv7182_select_input,
    };
    
    static const struct adv7180_chip_info adv7280_m_info = {
    	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | ADV7180_FLAG_I2P,
    	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
    		BIT(ADV7182_INPUT_CVBS_AIN2) |
    		BIT(ADV7182_INPUT_CVBS_AIN3) |
    		BIT(ADV7182_INPUT_CVBS_AIN4) |
    		BIT(ADV7182_INPUT_CVBS_AIN5) |
    		BIT(ADV7182_INPUT_CVBS_AIN6) |
    		BIT(ADV7182_INPUT_CVBS_AIN7) |
    		BIT(ADV7182_INPUT_CVBS_AIN8) |
    		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
    		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
    		BIT(ADV7182_INPUT_SVIDEO_AIN5_AIN6) |
    		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
    		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
    		BIT(ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6),
    	.init = adv7182_init,
    	.set_std = adv7182_set_std,
    	.select_input = adv7182_select_input,
    };
    
    static const struct adv7180_chip_info adv7281_info = {
    	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2,
    	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
    		BIT(ADV7182_INPUT_CVBS_AIN2) |
    		BIT(ADV7182_INPUT_CVBS_AIN7) |
    		BIT(ADV7182_INPUT_CVBS_AIN8) |
    		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
    		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
    		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
    		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
    	.init = adv7182_init,
    	.set_std = adv7182_set_std,
    	.select_input = adv7182_select_input,
    };
    
    static const struct adv7180_chip_info adv7281_m_info = {
    	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2,
    	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
    		BIT(ADV7182_INPUT_CVBS_AIN2) |
    		BIT(ADV7182_INPUT_CVBS_AIN3) |
    		BIT(ADV7182_INPUT_CVBS_AIN4) |
    		BIT(ADV7182_INPUT_CVBS_AIN7) |
    		BIT(ADV7182_INPUT_CVBS_AIN8) |
    		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
    		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
    		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
    		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
    		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
    		BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) |
    		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
    	.init = adv7182_init,
    	.set_std = adv7182_set_std,
    	.select_input = adv7182_select_input,
    };
    
    static const struct adv7180_chip_info adv7281_ma_info = {
    	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2,
    	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
    		BIT(ADV7182_INPUT_CVBS_AIN2) |
    		BIT(ADV7182_INPUT_CVBS_AIN3) |
    		BIT(ADV7182_INPUT_CVBS_AIN4) |
    		BIT(ADV7182_INPUT_CVBS_AIN5) |
    		BIT(ADV7182_INPUT_CVBS_AIN6) |
    		BIT(ADV7182_INPUT_CVBS_AIN7) |
    		BIT(ADV7182_INPUT_CVBS_AIN8) |
    		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
    		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
    		BIT(ADV7182_INPUT_SVIDEO_AIN5_AIN6) |
    		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
    		BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) |
    		BIT(ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6) |
    		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
    		BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) |
    		BIT(ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6) |
    		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
    	.init = adv7182_init,
    	.set_std = adv7182_set_std,
    	.select_input = adv7182_select_input,
    };
    
    static const struct adv7180_chip_info adv7282_info = {
    	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_I2P,
    	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
    		BIT(ADV7182_INPUT_CVBS_AIN2) |
    		BIT(ADV7182_INPUT_CVBS_AIN7) |
    		BIT(ADV7182_INPUT_CVBS_AIN8) |
    		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
    		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
    		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
    		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
    	.init = adv7182_init,
    	.set_std = adv7182_set_std,
    	.select_input = adv7182_select_input,
    };
    
    static const struct adv7180_chip_info adv7282_m_info = {
    	.flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | ADV7180_FLAG_I2P,
    	.valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) |
    		BIT(ADV7182_INPUT_CVBS_AIN2) |
    		BIT(ADV7182_INPUT_CVBS_AIN3) |
    		BIT(ADV7182_INPUT_CVBS_AIN4) |
    		BIT(ADV7182_INPUT_CVBS_AIN7) |
    		BIT(ADV7182_INPUT_CVBS_AIN8) |
    		BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) |
    		BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) |
    		BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) |
    		BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) |
    		BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) |
    		BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8),
    	.init = adv7182_init,
    	.set_std = adv7182_set_std,
    	.select_input = adv7182_select_input,
    };
    
    static int init_device(struct adv7180_state *state)
    {
    	printk("device init\n");
    	int ret;
    
    	mutex_lock(&state->mutex);
    
    	adv7180_write(state, ADV7180_REG_PWR_MAN, ADV7180_PWR_MAN_RES);
    	usleep_range(2000, 10000);
    
    	ret = state->chip_info->init(state);
    	if (ret)
    		goto out_unlock;
    
    	ret = adv7180_program_std(state);
    	if (ret)
    		goto out_unlock;
    
    	adv7180_set_field_mode(state);
    
    	/* register for interrupts */
    	if (state->irq > 0) {
    		/* config the Interrupt pin to be active low */
    		ret = adv7180_write(state, ADV7180_REG_ICONF1,
    						ADV7180_ICONF1_ACTIVE_LOW |
    						ADV7180_ICONF1_PSYNC_ONLY);
    		if (ret < 0)
    			goto out_unlock;
    
    		ret = adv7180_write(state, ADV7180_REG_IMR1, 0);
    		if (ret < 0)
    			goto out_unlock;
    
    		ret = adv7180_write(state, ADV7180_REG_IMR2, 0);
    		if (ret < 0)
    			goto out_unlock;
    
    		/* enable AD change interrupts interrupts */
    		ret = adv7180_write(state, ADV7180_REG_IMR3,
    						ADV7180_IRQ3_AD_CHANGE);
    		if (ret < 0)
    			goto out_unlock;
    
    		ret = adv7180_write(state, ADV7180_REG_IMR4, 0);
    		if (ret < 0)
    			goto out_unlock;
    	}
    
    out_unlock:
    	mutex_unlock(&state->mutex);
    
    	return ret;
    }
    
    static int adv7180_probe(struct i2c_client *client,
    			 const struct i2c_device_id *id)
    {
    	struct adv7180_state *state;
    	struct v4l2_subdev *sd;
    	int ret;
    	printk("probing adv7180 driver\n");
    	/* Check if the adapter supports the needed features */
    	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
    		return -EIO;
    
    	v4l_info(client, "chip found @ 0x%02x (%s)\n",
    		 client->addr, client->adapter->name);
    
    	state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
    	if (state == NULL)
    		return -ENOMEM;
    
    	state->client = client;
    	state->field = V4L2_FIELD_INTERLACED;
    	state->chip_info = (struct adv7180_chip_info *)id->driver_data;
    
    	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) {
    		state->csi_client = i2c_new_dummy(client->adapter,
    				ADV7180_DEFAULT_CSI_I2C_ADDR);
    		if (!state->csi_client)
    			return -ENOMEM;
    	}
    
    	if (state->chip_info->flags & ADV7180_FLAG_I2P) {
    		state->vpp_client = i2c_new_dummy(client->adapter,
    				ADV7180_DEFAULT_VPP_I2C_ADDR);
    		if (!state->vpp_client) {
    			ret = -ENOMEM;
    			goto err_unregister_csi_client;
    		}
    	}
    
    	state->irq = client->irq;
    	mutex_init(&state->mutex);
    	state->autodetect = true;
    	if (state->chip_info->flags & ADV7180_FLAG_RESET_POWERED)
    		state->powered = true;
    	else
    		state->powered = false;
    	state->input = 0;
    	sd = &state->sd;
    	v4l2_i2c_subdev_init(sd, client, &adv7180_ops);
    	sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
    
    	ret = adv7180_init_controls(state);
    	if (ret)
    		goto err_unregister_vpp_client;
    
    	state->pad.flags = MEDIA_PAD_FL_SOURCE;
    	sd->entity.flags |= MEDIA_ENT_T_V4L2_SUBDEV_DECODER;
    	ret = media_entity_init(&sd->entity, 1, &state->pad, 0);
    	if (ret)
    		goto err_free_ctrl;
    
    	ret = init_device(state);
    	if (ret)
    		goto err_media_entity_cleanup;
    
    	if (state->irq) {
    		ret = request_threaded_irq(client->irq, NULL, adv7180_irq,
    					   IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
    					   KBUILD_MODNAME, state);
    		if (ret)
    			goto err_media_entity_cleanup;
    	}
    
    	ret = v4l2_async_register_subdev(sd);
    	if (ret)
    		goto err_free_irq;
    
    	return 0;
    
    err_free_irq:
    	if (state->irq > 0)
    		free_irq(client->irq, state);
    err_media_entity_cleanup:
    	media_entity_cleanup(&sd->entity);
    err_free_ctrl:
    	adv7180_exit_controls(state);
    err_unregister_vpp_client:
    	if (state->chip_info->flags & ADV7180_FLAG_I2P)
    		i2c_unregister_device(state->vpp_client);
    err_unregister_csi_client:
    	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2)
    		i2c_unregister_device(state->csi_client);
    	mutex_destroy(&state->mutex);
    	return ret;
    }
    
    static int adv7180_remove(struct i2c_client *client)
    {
    	struct v4l2_subdev *sd = i2c_get_clientdata(client);
    	struct adv7180_state *state = to_state(sd);
    
    	v4l2_async_unregister_subdev(sd);
    
    	if (state->irq > 0)
    		free_irq(client->irq, state);
    
    	media_entity_cleanup(&sd->entity);
    	adv7180_exit_controls(state);
    
    	if (state->chip_info->flags & ADV7180_FLAG_I2P)
    		i2c_unregister_device(state->vpp_client);
    	if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2)
    		i2c_unregister_device(state->csi_client);
    
    	mutex_destroy(&state->mutex);
    
    	return 0;
    }
    
    static const struct i2c_device_id adv7180_id[] = {
    	{ "adv7180", (kernel_ulong_t)&adv7180_info },
    	{ "adv7182", (kernel_ulong_t)&adv7182_info },
    	{ "adv7280", (kernel_ulong_t)&adv7280_info },
    	{ "adv7280-m", (kernel_ulong_t)&adv7280_m_info },
    	{ "adv7281", (kernel_ulong_t)&adv7281_info },
    	{ "adv7281-m", (kernel_ulong_t)&adv7281_m_info },
    	{ "adv7281-ma", (kernel_ulong_t)&adv7281_ma_info },
    	{ "adv7282", (kernel_ulong_t)&adv7282_info },
    	{ "adv7282-m", (kernel_ulong_t)&adv7282_m_info },
    	{},
    };
    MODULE_DEVICE_TABLE(i2c, adv7180_id);
    
    #ifdef CONFIG_PM_SLEEP
    static int adv7180_suspend(struct device *dev)
    {
    	struct i2c_client *client = to_i2c_client(dev);
    	struct v4l2_subdev *sd = i2c_get_clientdata(client);
    	struct adv7180_state *state = to_state(sd);
    
    	return adv7180_set_power(state, false);
    }
    
    static int adv7180_resume(struct device *dev)
    {
    	struct i2c_client *client = to_i2c_client(dev);
    	struct v4l2_subdev *sd = i2c_get_clientdata(client);
    	struct adv7180_state *state = to_state(sd);
    	int ret;
    
    	ret = init_device(state);
    	if (ret < 0)
    		return ret;
    
    	ret = adv7180_set_power(state, state->powered);
    	if (ret)
    		return ret;
    
    	return 0;
    }
    
    static SIMPLE_DEV_PM_OPS(adv7180_pm_ops, adv7180_suspend, adv7180_resume);
    #define ADV7180_PM_OPS (&adv7180_pm_ops)
    
    #else
    #define ADV7180_PM_OPS NULL
    #endif
    
    #ifdef CONFIG_OF
    static const struct of_device_id adv7180_of_id[] = {
    	{ .compatible = "adi,adv7180", },
    	{ },
    };
    
    MODULE_DEVICE_TABLE(of, adv7180_of_id);
    #endif
    
    static struct i2c_driver adv7180_driver = {
    	.driver = {
    		   .name = KBUILD_MODNAME,
    		   .pm = ADV7180_PM_OPS,
    		   .of_match_table = of_match_ptr(adv7180_of_id),
    		   },
    	.probe = adv7180_probe,
    	.remove = adv7180_remove,
    	.id_table = adv7180_id,
    };
    
    module_i2c_driver(adv7180_driver);
    
    MODULE_DESCRIPTION("Analog Devices ADV7180 video decoder driver");
    MODULE_AUTHOR("Mocean Laboratories");
    MODULE_LICENSE("GPL v2");
    

    Regards

    Abhishek

  • Hi Manisha,
    Now I am able to create /dev/video1 node but unable to open it. It shows no such file while i can see /dev/video1 device is available there.
    root# ls /dev/video*
    /dev/video0 /dev/video1 /dev/video10
    root#
    Regards
    Abhishek
  • Hi,
    i am facing the following error when trying to test using /dev/video1 capture node.
    root@am57xx-evm:~# yavta -c2 -p -F --skip 0 -f NV24 -s 1280x720 /dev/video1
    Error opening device /dev/video1: No such device (19).

    Please tell what is the reason of this error and how can i solve this ?
    Regards
    Abhishek
  • Hi Manisha,

                      Any solution to this problem ?

                     1. Am i missing any driver, Since i am not having document to follow there are chances of  that. I am inserting three drivers

    i ti-vpdma.ko

    ii ti-vip.ko

    iii adv7180.ko

    I have probe the hsync vsync and clock signal from decoder  we are getting those signals clock is 27 mhz.

    2.After inserting these three drivers i am able to create /dev/video1 node but unable to open it.

    root@am57xx-evm:~# yavta -c2 -p -F --skip 0 -f NV24 -s 1280x720 /dev/video1

    Error opening device /dev/video1: No such device (19).

    what else could be the reason Please reply its very important for us to proceed further.

    Kindly let me know if you want any further information.

    Thanks & regards

    abhishek

  • Hi Abhishek,

    Sometimes udev cache goes out of sync with the registered devices, you may see more devices than are actually present.

    udev may create a device /dev/video1 but there has to be a char device major(64) and minor(0) registered for this to be accessible.

    A simple check is to check whether VIP driver is registering the video1 device or not.

    In one of your eearlier responces, I saw VIP driver could negotiate the formats with the decoder subdevice driver.

    As soon as the VIP driver binds with the subdev driver, it'll call the get_fmt, enum_mbus_fmt and enum_frmsizes subdev_ops.

    Make sure none of these ops are not failing.

    You can enable VIP logs for detailed info on what exactly is failing.

    echo 15> /sys/module/ti_vip/dev_debug

    Regards,

    Nikhil D

  • Hi Nikhil,
    Thanks for your kind response
    Now I have solved the above issue .
  • Hi Abhishek

    Can you share the steps?
    I have the same problem with interfacing decoder IC and VIP VIN1A port. VIP can detect the chip but can not negotiate the format.
    I can not see /dev/video1 and there is only /dev/video0 and /dev/video10 !
    How could you get /dev/video1 to work?
    Is there any need to add DRA7XX_CORE_IOPAD functions to dts file for vin1a pins or the default pins will took up?

    Thanks a lot