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.

Interfacing cmos with video capture dm8168

Other Parts Discussed in Thread: TVP7002

Hello we study the video capture driver for v4l2. We see that for set up the correct video capture port parameters, we have to modify ti81xx_vpss.c, is this correct? It seems that the settings on ti81xxvin.c are overwritten by the sdev_info of ti81xx_vpss. So the setup pf the capture format can't be done by saLoobback file, is this correct? So if i want to capture my streaming coming from cmos, i have to setup tivpss with

- external vsync and hsync

- rgb888

- 24 bit

Is this correct? I do this operations but i can not capture images with saLoopback.

thanks

  • Hi,

    You can refer to application  note http://processors.wiki.ti.com/index.php/DM81xx_AM38xx_Adding_External_Decoders_to_V4L2_Capture_Driver on how to add support for new decoders. Further please take care of Silicon errata advisory Advisory 2.0.53@http://www.ti.com/lit/er/sprz329b/sprz329b.pdf

    Regards,

    Hardik Shah

  • Hello

    we read the lecterature first but i can not understand why i can not capture images. What i want to do is acquire from a sensor in RGB mode with External syncs.I want to acquire 1280x720. I do this modifies

    - ti81xx_vpss:

    struct ti81xxvin_interface sensor_pdata = {
        .clk_polarity = 1,
        .hs_polarity = 1,
        .vs_polarity = 1,
        .fid_polarity = 1,
        .sog_polarity = 0,
    };

            .name    = SENSOR_INST0,
            .board_info = {
                /* TODO Find the correct address
                    of the TVP7002 connected */
                I2C_BOARD_INFO(SENSOR_INST0, SENSOR_INST0_I2C_ADD),
                .platform_data = &sensor_pdata,
            },
            .vip_port_cfg = {
                .ctrlChanSel = VPS_VIP_CTRL_CHAN_SEL_15_8,
                .ancChSel8b = VPS_VIP_ANC_CH_SEL_DONT_CARE,
                .pixClkEdgePol = VPS_VIP_PIX_CLK_EDGE_POL_RISING,
                .invertFidPol = 0,
                .embConfig = {
                    .errCorrEnable = 1,
                    .srcNumPos = VPS_VIP_SRC_NUM_POS_DONT_CARE,
                    .isMaxChan3Bits = 0,
                },
                .disConfig = {
                    .fidSkewPostCnt = 0,
                    .fidSkewPreCnt = 0,
                    .lineCaptureStyle = VPS_VIP_LINE_CAPTURE_STYLE_HSYNC,
                        .fidDetectMode = VPS_VIP_FID_DETECT_MODE_VSYNC,
                        .actvidPol = VPS_VIP_POLARITY_HIGH,
                        .vsyncPol =  VPS_VIP_POLARITY_HIGH,
                        .hsyncPol = VPS_VIP_POLARITY_HIGH,
                }

            .video_capture_mode = VPS_CAPT_VIDEO_CAPTURE_MODE_SINGLE_CH_NON_MUX_DISCRETE_SYNC_HSYNC_VSYNC,
            .video_if_mode = VPS_CAPT_VIDEO_IF_MODE_24BIT,
            .input_data_format = FVID2_DF_RGB24_888,

    I also see the errata at 1.1.10. At hardware level i connect FrameValid inverted of the sensor to VSYNC (the signal is high during vertical blanking), LineValid inverted to HSYNC (the signal is high during horizzontal blanking) and LineValid to DE pin (the signal is high during a line), is this correct? I use Aptina MT9T001 device and i set up "line valid continous" so the line valid signal is  like the errata.

    For capture images i modify the capture saLoopbak to save images in files. I see that the program fails, i see errors like "port a overflowed"

  • hello

    we try other tests. We try to double the clock frequency out of the FPGA to dm8168. In this way we want to simulate the YUV format with external sync. So the saLoopback remains the same of the PSP. In ti81xxvpss.c we setup this parameters:

    .name    = SENSOR_INST0,
            .board_info = {
                /* TODO Find the correct address
                    of the TVP7002 connected */
                I2C_BOARD_INFO(SENSOR_INST0, SENSOR_INST0_I2C_ADD),
                .platform_data = &sensor_pdata,
            },
            .vip_port_cfg = {
                .ctrlChanSel = VPS_VIP_CTRL_CHAN_SEL_15_8,
                .ancChSel8b = VPS_VIP_ANC_CH_SEL_DONT_CARE,
                .pixClkEdgePol = VPS_VIP_PIX_CLK_EDGE_POL_RISING,
                .invertFidPol = 0,
                .embConfig = {
                    .errCorrEnable = 1,
                    .srcNumPos = VPS_VIP_SRC_NUM_POS_DONT_CARE,
                    .isMaxChan3Bits = 0,
                },
                .disConfig = {
                    .fidSkewPostCnt = 0,
                    .fidSkewPreCnt = 0,
                    .lineCaptureStyle = VPS_VIP_LINE_CAPTURE_STYLE_HSYNC,
                        .fidDetectMode = VPS_VIP_FID_DETECT_MODE_VSYNC,
                        .actvidPol = VPS_VIP_POLARITY_HIGH,
                        .vsyncPol =  VPS_VIP_POLARITY_HIGH,
                        .hsyncPol = VPS_VIP_POLARITY_HIGH,
                }
            },       

            .video_capture_mode = VPS_CAPT_VIDEO_CAPTURE_MODE_SINGLE_CH_NON_MUX_DISCRETE_SYNC_HSYNC_VSYNC,
            .video_if_mode = VPS_CAPT_VIDEO_IF_MODE_16BIT,
            .input_data_format = FVID2_DF_YUV422P,

    The program halts in this way

    Driver Name: ti81xxvin
    Driver bus info: TI81xx Platform
    Driver is capable of dmt9t001 2-005d: Function mt9t001_query_dv_preset
    oing capture
    mt9t001 2-005d: Index: 0, height: 720, width: 1280 | preset_height: 720, preset_width 1280
    mt9t001 2-005d: SUCCESS
    mt9t001 2-005d: Function mt9t001_mbus_fmt
    saLoopBack:
     Mode set is 720P60
    mt9t001 2-005d: Function mt9t001_s_dv_preset...
    mt9t001 2-005d: Preset: 8, 8
    mt9t001 2-005d: Function mt9t001_write_inittab
    mt9t001 2-005d: Reg: 3, Val: 719
    mt9t001 2-005d: Reg: 4, Val: 1279
    mt9t001 2-005d: Reg: 32, Val: 8704
    mt9t001 2-005d: SUCCESS, 0
    mt9t001 2-005d: Function mt9t001_mbus_fmt
    Driver Name:
    Driver bus info: Ð
    =============================================================
    Capture Format:
    =============================================================
    fmt.type                 = 1
    fmt.width                = 1280
    fmt.height               = 720
    fmt.pixelformat  = 1448695129
    fmt.bytesperline         = 2560
    fmt.sizeimage    = 1843200
    =============================================================
    =============================================================
    Display Format:
    =============================================================
    fmt.type                 = 2
    fmt.width                = 1280
    fmt.height               = 720
    fmt.pixelformat  = 1448695129
    fmt.bytesperline         = 2560
    fmt.sizeimage    = 1843200
    =============================================================
    mt9t001 2-005d: Function mt9t001_s_stream
    mt9t001 2-005d: Enable stream 1
    mt9t001 2-005d: Function mt9t001_set_output_control
    mt9t001 2-005d: Value: 2, Clear: 0, Set: 2
    ------------[ cut here ]------------
    WARNING: at kernel/softirq.c:159 local_bh_enable+0x54/0xc4()
    Modules linked in: ti81xxvin mt9t001 ti81xxvo ti81xxfb vpss syslink ipv6
    Backtrace:
    [<c0049bb8>] (dump_backtrace+0x0/0x110) from [<c035a254>] (dump_stack+0x18/0x1c)
     r7:00000000 r6:c0072d10 r5:c0417fca r4:0000009f
    [<c035a23c>] (dump_stack+0x0/0x1c) from [<c006d794>] (warn_slowpath_common+0x54/0x6c)
    [<c006d740>] (warn_slowpath_common+0x0/0x6c) from [<c006d7d0>] (warn_slowpath_null+0x24/0x2c)
     r9:c04b7b2c r8:d0878800 r7:0000000e r6:cca1ac00 r5:c0496590
    r4:c04dfe00
    [<c006d7ac>] (warn_slowpath_null+0x0/0x2c) from [<c0072d10>] (local_bh_enable+0x54/0xc4)
    [<c0072cbc>] (local_bh_enable+0x0/0xc4) from [<c006768c>] (omap_mbox_msg_send+0xcc/0xdc)
     r5:c0496590 r4:00000000
    [<c00675c0>] (omap_mbox_msg_send+0x0/0xdc) from [<c02ab620>] (notify_shm_drv_send_event+0x1c8/0x208)
     r5:00000001 r4:00000000
    [<c02ab458>] (notify_shm_drv_send_event+0x0/0x208) from [<c02a8db0>] (notify_send_event+0x114/0x26c)
    [<c02a8c9c>] (notify_send_event+0x0/0x26c) from [<bf18ea08>] (vps_fvid2_queue+0xe4/0x21c [vpss])
    [<bf18e924>] (vps_fvid2_queue+0x0/0x21c [vpss]) from [<bf197cfc>] (capture_queue+0x50/0x64 [vpss])
     r8:bf1d0414 r7:60000013 r6:c7f7b900 r5:00000000 r4:cb745400
    [<bf197cac>] (capture_queue+0x0/0x64 [vpss]) from [<bf1cf1d4>] (ti81xxvin_buffer_queue+0x9c/0xe8 [ti81xxvin])
     r5:c7f0a400 r4:cb745400
    [<bf1cf138>] (ti81xxvin_buffer_queue+0x0/0xe8 [ti81xxvin]) from [<c0290cb4>] (videobuf_streamon+0x80/0xd0)
     r7:60000013 r6:c7f0a5c4 r5:c7f0a504 r4:c7f7b900
    [<c0290c34>] (videobuf_streamon+0x0/0xd0) from [<bf1cec4c>] (vidioc_streamon+0x244/0x400 [ti81xxvin])
     r7:cb7e3600 r6:00000001 r5:c7f0a400 r4:c7f0a504
    [<bf1cea08>] (vidioc_streamon+0x0/0x400 [ti81xxvin]) from [<c02865bc>] (__video_do_ioctl+0x1618/0x3f34)
     r6:40045612 r5:00000000 r4:00000001
    [<c0284fa4>] (__video_do_ioctl+0x0/0x3f34) from [<c0284d94>] (__video_usercopy+0x2e4/0x428)
    [<c0284ab0>] (__video_usercopy+0x0/0x428) from [<c0284f08>] (video_ioctl2+0x30/0x38)
    [<c0284ed8>] (video_ioctl2+0x0/0x38) from [<c0283f48>] (v4l2_ioctl+0xe8/0x11c)
     r5:cb7e3600 r4:c7f7b880
    [<c0283e60>] (v4l2_ioctl+0x0/0x11c) from [<c00d2db4>] (vfs_ioctl+0x28/0x44)
     r9:ccb3e000 r8:be9b4c74 r7:00000003 r6:00000003 r5:c7f7b880
    r4:00000000
    [<c00d2d8c>] (vfs_ioctl+0x0/0x44) from [<c00d34c4>] (do_vfs_ioctl+0x500/0x540)
    [<c00d2fc4>] (do_vfs_ioctl+0x0/0x540) from [<c00d355c>] (sys_ioctl+0x58/0x7c)
    [<c00d3504>] (sys_ioctl+0x0/0x7c) from [<c0045e00>] (ret_fast_syscall+0x0/0x30)
     r8:c0045fa8 r7:00000036 r6:000124d0 r5:0001215c r4:be9b4c74
    ---[ end trace 0556f35c06c1dee8 ]---
    Count=0
    Port a overflowemt9t001 2-005d: Function mt9t001_s_streamd







    mt9t001 2-005d: Enable stream 0
    mt9t001 2-005d: Function mt9t001_set_output_control
    mt9t001 2-005d: Value: 0, Clear: 2, Set: 0
    =============================================================
    Capture Format:
    =============================================================
    fmt.type                 = 1
    fmt.width                = 1280
    fmt.height               = 720
    fmt.pixelformat  = 1448695129
    fmt.bytesperline         = 2560
    fmt.sizeimage    = 1843200
    =============================================================
    =============================================================
    Display Format:
    =============================================================
    fmt.type                 = 2
    fmt.width                = 1280
    fmt.height               = 720
    fmt.pixelformat  = 1448695129
    fmt.bytesperline         = 2560
    fmt.sizeimage    = 1843200
    =============================================================
    mt9t001 2-005d: Function mt9t001_s_stream
    mt9t001 2-005d: Enable stream 1
    mt9t001 2-005d: Function mt9t001_set_output_control
    mt9t001 2-005d: Value: 2, Clear: 0, Set: 2

    We still can not capture images and we do not mind what can be the reason. Can you suggest me any test to do?

  • Hi,

    Since you are getting the kernel dump, you are able to capture atleast once frame. But what is happening is that VIP port is getting overflowed. In this case you need to stop the capture and start it all over again. You can refer to saLoopBack application to do same. It restarts capture and display once the overflow is detected on VIP. But good news is that you are able to capture one frame atleast. Try starting Sensor first in driver and vip afterwards, this will prevent initial overflow.

    Regards,

    Hardik Shah

  • Hi

    thanks for the reply. I already start my sensor in driver before starting saLoopback (i can see the frame valid and line valid). I use the file attached to try to capture without display, but if you see the code, is the same of the TI81XX Video Capture Driver User Guide.

    //
    // Simple capture applications
    //
    
    
    /*
     * Header File Inclusion
     */
    #include <stdio.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <stdlib.h>
    #include <sys/ioctl.h>
    #include <sys/mman.h>
    #include <errno.h>
    #include <string.h>
    #include <linux/videodev2.h>
    #include <linux/fb.h>
    #include <linux/ti81xxfb.h>
    #include <linux/ti81xxhdmi.h>
    #include <linux/ti81xxvin.h>
    
    /* device node to be used for capture */
    #define CAPTURE_DEVICE      "/dev/video0"
    #define CAPTURE_NAME        "Capture"
    #define APP_NAME            "CaptureTest"
    
    #define MAX_BUFFER              5
    #define FRAME_TO_CAPTURE        10
    
    
    unsigned long m_buffer_addr[MAX_BUFFER];
    
    /* Structure for storing application data like file pointers, format etc. */
    struct app_obj
    {
        int fd;
        struct v4l2_capability cap;
        struct v4l2_format fmt;
        struct v4l2_dv_preset dv_preset;
        struct v4l2_requestbuffers reqbuf;
        int numbuffers;
        struct v4l2_buffer buf;
        struct ti81xxvin_overflow_status over_flow;
    };
    
    struct app_obj  m_cap;
    
    
    /* Utility function for printing format */
    static void printFormat(char *string, struct v4l2_format *fmt)
    {
        printf("=============================================================\n");
        printf("%s Format:\n", string);
        printf("=============================================================\n");
        printf("fmt.type\t\t = %d\n", fmt->type);
        printf("fmt.width\t\t = %d\n", fmt->fmt.pix.width);
        printf("fmt.height\t\t = %d\n", fmt->fmt.pix.height);
        printf("fmt.pixelformat\t = %d\n", fmt->fmt.pix.pixelformat);
        printf("fmt.bytesperline\t = %d\n", fmt->fmt.pix.bytesperline);
        printf("fmt.sizeimage\t = %d\n", fmt->fmt.pix.sizeimage);
        printf("=============================================================\n");
    }
    
    
    void SetInput()
    {
        int input_index = 0;
    
        int ret = ioctl(m_cap.fd, VIDIOC_S_INPUT, &input_index);
    
        if (ret)
        {
             printf("%s: Setting Input failed\n", APP_NAME);
             printf("Return Value = %d\n", ret);
             exit(2);
        }
        else
        	 printf("%s: Setting Input OK\n", APP_NAME);
    
    }
    
    //
    // Query if present
    //
    void Query_DV_Present()
    {
    
        m_cap.dv_preset.preset = -1;
    
        if (ioctl(m_cap.fd, VIDIOC_QUERY_DV_PRESET, &m_cap.dv_preset))
        {
             printf("Querying DV Preset failed\n");
             exit(2);
        }
    
    
        switch (m_cap.dv_preset.preset) {
        case V4L2_DV_720P60:
                printf("%s:\n Mode set is 720P60\n", APP_NAME);
                break;
        case V4L2_DV_1080I60:
                printf("%s:\n Mode set is 1080I60\n", APP_NAME);
                break;
        case V4L2_DV_1080P60:
                printf("%s:\n Mode set is 1080P60\n", APP_NAME);
                break;
        case V4L2_DV_1080P30:
                printf("%s:\n Mode set is 1080P30\n", APP_NAME);
                break;
        default:
               printf("%s:\n Failed to query preset\n", APP_NAME);
        }
    
        
        if (ioctl(m_cap.fd, VIDIOC_S_DV_PRESET, &m_cap.dv_preset))
        {
    	printf("Setting DV Preset failed\n");
    	exit(2);
        }
    }
    
    void GetBufferFormat()
    {
    	/* Get the default format */
    	m_cap.fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    
    	if (ioctl(m_cap.fd, VIDIOC_G_FMT, &m_cap.fmt))
    	{
    		 printf("Getting Buffer format failed");
    		  exit(2);
    	}
    
    	printFormat("Capture",&m_cap.fmt);
    
    
    	/* Change required parameters and set the format */
    	m_cap.fmt.fmt.pix.bytesperline = m_cap.fmt.fmt.pix.width * 2;
    	m_cap.fmt.fmt.pix.sizeimage = m_cap.fmt.fmt.pix.bytesperline * m_cap.fmt.fmt.pix.height;
    	m_cap.fmt.fmt.pix.colorspace = V4L2_COLORSPACE_REC709;
    
    	//m_cap.fmt.fmt.pix.field = V4L2_PIX_FMT_YUYV;
    	//m_cap.fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
    
    	m_cap.fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    
    	printf ("Calling set routine......\n");
    
    	if (ioctl(m_cap.fd, VIDIOC_S_FMT, &m_cap.fmt))
    	{
    	  printf("Setting format failed\n");
    	  exit(2);
    	}
    
    	printFormat("Capture",&m_cap.fmt);
    
    }
    
    
    void RequestBuffers(int numbuffers)
    {
        /* structure to store buffer request parameters */
        m_cap.reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        m_cap.reqbuf.count = numbuffers;
        m_cap.reqbuf.memory = V4L2_MEMORY_USERPTR;
    
        int ret = ioctl(m_cap.fd , VIDIOC_REQBUFS, &m_cap.reqbuf);
        if(ret < 0)
        {
            printf("cannot allocate memory\n");
            close(m_cap.fd);
            exit(4);
        }
    
    }
    
    //
    //  start streaming
    //
    void StartStreaming()
    {
        /* Start streaming */
        int cmd = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    
        int ret = ioctl(m_cap.fd, VIDIOC_STREAMON, &cmd);
    
        if (ret < 0)
        {
            perror("VIDIOC_STREAMON\n");
            exit(5);
        }
    }
    
    //
    // Stop streaming
    //
    void StopStreaming()
    {
        /* Stop streaming */
        int cmd = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    
        int ret = ioctl(m_cap.fd, VIDIOC_STREAMOFF, &cmd);
    
        if (ret < 0)
        {
            perror("VIDIOC_STREAMOFF\n");
            exit(6);
        }
    
    }
    
    void PrimeBuffers()
    {
        /* Prime buffers */
        int ret;
        int i;
    
        int imageSize = m_cap.fmt.fmt.pix.sizeimage;
    
        printf("Image size = %d\n", imageSize);
    
        for (i = 0; i < MAX_BUFFER; i++)
        {
             m_cap.buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
             m_cap.buf.memory = V4L2_MEMORY_USERPTR;
             m_cap.buf.index = i;
             //m_cap.buf.field = V4L2_FIELD_INTERLACED;
    
    
             int* buffer = (int*)calloc (1, imageSize);
             m_buffer_addr[m_cap.buf.index] = buffer;
    
             m_cap.buf.m.userptr = m_buffer_addr[m_cap.buf.index];
             m_cap.buf.length = imageSize;
    
             ret = ioctl( m_cap.fd, VIDIOC_QBUF, &m_cap.buf);
    
              if (ret < 0)
              {
                  perror("VIDIOC_QBUF\n");
                  return -1;
              }
        }
    
        printf("Prime buffer complete \n");
    
    
    }
    
    
    int CaptureData()
    {
       int i;
       int ret;
    	FILE *fp = NULL;
    	char str[32];
    
       /* Start the loop of capture */
       for (i = 0; i < FRAME_TO_CAPTURE; i++)
       {
           /* Dequeue the captured buffer */
           ret = ioctl(m_cap.fd, VIDIOC_DQBUF, &m_cap.buf);
            if (ret < 0)
            {
                perror("VIDIOC_DQBUF\n");
                 return -1;
            }
    
            // Process the buffer
            printf("Capturing data.....\n");
    
    		printf("File save\n");
    		sleep(2);
    		
    		sprintf(str,"capure_frame%d.rgb",i);
    		fp = fopen(str,"r");
    
    		if (fp != NULL)
    		{
    			fclose(fp);
    			remove(str);			
    		}
    
    		fp = fopen(str,"w");
    		if (fp == NULL)
    		{
    			printf("Nancje un file tu rivis? No sta impaciati di che robis chi");
    			return ret;
    		}
    		fwrite(m_cap.buf.m.userptr,1,m_cap.fmt.fmt.pix.sizeimage,fp);
    		fclose(fp);
    
            /* Queue the buffer after processing is done */
            ret = ioctl(m_cap.fd, VIDIOC_QBUF, &m_cap.buf);
    
            if (ret < 0)
            {
                  perror("VIDIOC_QBUF\n");
                  return -1;
            }
    
       }
    }
    
    void free_buffers()
    {
        /* Prime buffers */
        printf("Freeing buffers\n");
        int ret;
        int i;
    
        for (i = 0; i < MAX_BUFFER; i++)
        {
             int* buffer =   m_buffer_addr[i];
             free(buffer);
        }
    
    }
    
    
    
    int main()
    {
        int mode = O_RDWR;
    
        /* Open capture driver */
        m_cap.fd = open((const char *)CAPTURE_DEVICE, mode);
    
        // Open the channel
        if (m_cap.fd != -1)
        {
            SetInput();
    	sleep(2);
    
            Query_DV_Present();
    	sleep(2);
    
            GetBufferFormat();
    	sleep(2);
    
            RequestBuffers(MAX_BUFFER);
    	sleep(2);
    
            PrimeBuffers();
    	sleep(2);
    
            StartStreaming();
    	sleep(2);
    
            CaptureData();
    	sleep(2);
    
            StopStreaming();
    	sleep(2);
    
            free_buffers();
    
            close(m_cap.fd);   // Close the channel
        }
        else
        {
            printf("failed to open capture device\n");
        }
    
    
    
    }
    
    
    .
    /*
     * Driver for MT9T001 CMOS Image Sensor from Micron
     *
     * Copyright (C) 2010, Laurent Pinchart <laurent.pinchart@ideasonboard.com>
     *
     * Based on the MT9M001 driver,
     *
     * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de>
     *
     * 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.
     */
    
    #include <linux/i2c.h>
    #include <linux/log2.h>
    #include <linux/slab.h>
    #include <linux/videodev2.h>
    #include <linux/v4l2-mediabus.h>
    
    #include <media/mt9t001.h>
    #include <media/v4l2-ctrls.h>
    #include <media/v4l2-device.h>
    #include <media/v4l2-subdev.h>
    #include <media/v4l2-chip-ident.h>
    #include <media/v4l2-common.h>
    
    /*
     * mt9m001 i2c address 0x5d
     */
    
    #define MT9T001_CHIP_VERSION				0x00
    #define		MT9T001_CHIP_ID				0x1621
    #define MT9T001_ROW_START				0x01
    #define		MT9T001_ROW_START_MIN			0
    #define		MT9T001_ROW_START_DEF			20
    #define		MT9T001_ROW_START_MAX			1534
    #define MT9T001_COLUMN_START				0x02
    #define		MT9T001_COLUMN_START_MIN		0
    #define		MT9T001_COLUMN_START_DEF		32
    #define		MT9T001_COLUMN_START_MAX		2046
    #define MT9T001_WINDOW_HEIGHT				0x03
    #define		MT9T001_WINDOW_HEIGHT_MIN		1
    #define		MT9T001_WINDOW_HEIGHT_DEF		1535
    #define		MT9T001_WINDOW_HEIGHT_MAX		1567
    #define MT9T001_WINDOW_WIDTH				0x04
    #define		MT9T001_WINDOW_WIDTH_MIN		1
    #define		MT9T001_WINDOW_WIDTH_DEF		2047
    #define		MT9T001_WINDOW_WIDTH_MAX		2111
    #define MT9T001_HORIZONTAL_BLANKING			0x05
    #define		MT9T001_HORIZONTAL_BLANKING_MIN		21
    #define		MT9T001_HORIZONTAL_BLANKING_MAX		1023
    #define MT9T001_VERTICAL_BLANKING			0x06
    #define		MT9T001_VERTICAL_BLANKING_MIN		3
    #define		MT9T001_VERTICAL_BLANKING_MAX		1023
    #define MT9T001_OUTPUT_CONTROL				0x07
    #define		MT9T001_OUTPUT_CONTROL_SYNC		(1 << 0)
    #define		MT9T001_OUTPUT_CONTROL_CHIP_ENABLE	(1 << 1)
    #define		MT9T001_OUTPUT_CONTROL_TEST_DATA	(1 << 6)
    #define MT9T001_SHUTTER_WIDTH_HIGH			0x08
    #define MT9T001_SHUTTER_WIDTH_LOW			0x09
    #define		MT9T001_SHUTTER_WIDTH_MIN		1
    #define		MT9T001_SHUTTER_WIDTH_DEF		1561
    #define		MT9T001_SHUTTER_WIDTH_MAX		(1024 * 1024)
    #define MT9T001_PIXEL_CLOCK				0x0a
    #define		MT9T001_PIXEL_CLOCK_INVERT		(1 << 15)
    #define		MT9T001_PIXEL_CLOCK_SHIFT_MASK		(7 << 8)
    #define		MT9T001_PIXEL_CLOCK_SHIFT_SHIFT		8
    #define		MT9T001_PIXEL_CLOCK_DIVIDE_MASK		(0x7f << 0)
    #define MT9T001_FRAME_RESTART				0x0b
    #define MT9T001_SHUTTER_DELAY				0x0c
    #define		MT9T001_SHUTTER_DELAY_MAX		2047
    #define MT9T001_RESET					0x0d
    #define MT9T001_READ_MODE1				0x1e
    #define		MT9T001_READ_MODE_SNAPSHOT		(1 << 8)
    #define		MT9T001_READ_MODE_STROBE_ENABLE		(1 << 9)
    #define		MT9T001_READ_MODE_STROBE_WIDTH		(1 << 10)
    #define		MT9T001_READ_MODE_STROBE_OVERRIDE	(1 << 11)
    #define MT9T001_READ_MODE2				0x20
    #define		MT9T001_READ_MODE_BAD_FRAMES		(1 << 0)
    #define		MT9T001_READ_MODE_LINE_VALID_CONTINUOUS	(1 << 9)
    #define		MT9T001_READ_MODE_LINE_VALID_FRAME	(1 << 10)
    #define MT9T001_READ_MODE3				0x21
    #define		MT9T001_READ_MODE_GLOBAL_RESET		(1 << 0)
    #define		MT9T001_READ_MODE_GHST_CTL		(1 << 1)
    #define MT9T001_ROW_ADDRESS_MODE			0x22
    #define		MT9T001_ROW_SKIP_MASK			(7 << 0)
    #define		MT9T001_ROW_BIN_MASK			(3 << 3)
    #define		MT9T001_ROW_BIN_SHIFT			3
    #define MT9T001_COLUMN_ADDRESS_MODE			0x23
    #define		MT9T001_COLUMN_SKIP_MASK		(7 << 0)
    #define		MT9T001_COLUMN_BIN_MASK			(3 << 3)
    #define		MT9T001_COLUMN_BIN_SHIFT		3
    #define MT9T001_GREEN1_GAIN				0x2b
    #define MT9T001_BLUE_GAIN				0x2c
    #define MT9T001_RED_GAIN				0x2d
    #define MT9T001_GREEN2_GAIN				0x2e
    #define MT9T001_TEST_DATA				0x32
    #define MT9T001_GLOBAL_GAIN				0x35
    #define		MT9T001_GLOBAL_GAIN_MIN			8
    #define		MT9T001_GLOBAL_GAIN_MAX			1024
    #define MT9T001_BLACK_LEVEL				0x49
    #define MT9T001_ROW_BLACK_DEFAULT_OFFSET		0x4b
    #define MT9T001_BLC_DELTA_THRESHOLDS			0x5d
    #define MT9T001_CAL_THRESHOLDS				0x5f
    #define MT9T001_GREEN1_OFFSET				0x60
    #define MT9T001_GREEN2_OFFSET				0x61
    #define MT9T001_BLACK_LEVEL_CALIBRATION			0x62
    #define		MT9T001_BLACK_LEVEL_OVERRIDE		(1 << 0)
    #define		MT9T001_BLACK_LEVEL_DISABLE_OFFSET	(1 << 1)
    #define		MT9T001_BLACK_LEVEL_RECALCULATE		(1 << 12)
    #define		MT9T001_BLACK_LEVEL_LOCK_RED_BLUE	(1 << 13)
    #define		MT9T001_BLACK_LEVEL_LOCK_GREEN		(1 << 14)
    #define MT9T001_RED_OFFSET				0x63
    #define MT9T001_BLUE_OFFSET				0x64
    
    
    
    /* Debug functions */
    static int debug;
    module_param(debug, bool, 0644);
    MODULE_PARM_DESC(debug, "Debug level (0-2)");
    
    /* End of registers */
    #define MT9T001_EOR		0x5c
    
    /* Read write definition for registers */
    #define MT9T001_READ		0
    #define MT9T001_WRITE		1
    #define MT9T001_RESERVED	2
    
    /* Structure for register values */
    struct i2c_reg_value {
    	u8 reg;
    	u16 value;
    	u8 type;
    };
    
    /*
     * Register default values (according to mt9t001 datasheet)
     * In the case of read-only registers, the value (0xff) is
     * never written. R/W functionality is controlled by the
     * writable bit in the register struct definition.
     */
    
    
    /* Register parameters for 480P */
    static const struct i2c_reg_value mt9t001_parms_480P[] = {
    	{ MT9T001_WINDOW_HEIGHT, 479, MT9T001_WRITE },
    	{ MT9T001_WINDOW_WIDTH, 639, MT9T001_WRITE },
    	{ MT9T001_READ_MODE2, 0x2200, MT9T001_WRITE },
    	{ MT9T001_EOR, 0xff, MT9T001_RESERVED }
    };
    
    
    
    /* Register parameters for 1080I60 */
    static const struct i2c_reg_value mt9t001_parms_1080P60[] = {
    	{ MT9T001_WINDOW_HEIGHT, 1079, MT9T001_WRITE },
    	{ MT9T001_WINDOW_WIDTH, 1919, MT9T001_WRITE },
    	{ MT9T001_READ_MODE2, 0x2200, MT9T001_WRITE },
    	{ MT9T001_EOR, 0xff, MT9T001_RESERVED }
    };
    
    /* Register parameters for 720P60 */
    static const struct i2c_reg_value mt9t001_parms_720P60[] = {
    	{ MT9T001_WINDOW_HEIGHT, 719, MT9T001_WRITE },
    	{ MT9T001_WINDOW_WIDTH, 1279, MT9T001_WRITE },
    	{ MT9T001_READ_MODE2, 0x2200, MT9T001_WRITE },
    	{ MT9T001_EOR, 0xff, MT9T001_RESERVED }
    };
    
    
    /* Preset definition for handling device operation */
    struct mt9t001_preset_definition {
    	u32 preset;
    	const struct i2c_reg_value *p_settings;
    	enum v4l2_colorspace color_space;
    	enum v4l2_field scanmode;
    	u32  height;
    	u32  width;
    };
    
    /* Struct list for digital video presets */
    static const struct mt9t001_preset_definition mt9t001_presets[] = {
    	{
    		V4L2_DV_720P60,
    		mt9t001_parms_720P60,
    		V4L2_COLORSPACE_REC709,
    		V4L2_FIELD_NONE,
    		720,
    		1280
    	},
    	{
    		V4L2_DV_1080P60,
    		mt9t001_parms_1080P60,
    		V4L2_COLORSPACE_REC709,
    		V4L2_FIELD_NONE,
    		1080,
    		1920
    	},
    	{
    		V4L2_DV_480P59_94,
    		V4L2_COLORSPACE_REC709,
    		V4L2_FIELD_NONE,
    		480,
    		640
    	}
    };
    
    #define NUM_PRESETS	ARRAY_SIZE(mt9t001_presets)
    
    struct mt9t001 {
    	struct v4l2_subdev sd;
    	struct media_pad pad;
    
    	struct v4l2_mbus_framefmt format;
    	struct v4l2_rect crop;
    
    	struct v4l2_ctrl_handler ctrls;
    
    	const struct mt9t001_preset_definition *current_preset;
    
    	u32 gain;
    	int streaming;
    
    	u8 output_control;
    };
    
    static inline struct mt9t001 *to_mt9t001(struct v4l2_subdev *sd)
    {
    	return container_of(sd, struct mt9t001, sd);
    }
    
    static int mt9t001_read(struct i2c_client *client, const u8 reg)
    {
    	s32 data = i2c_smbus_read_word_data(client, reg);
    	return data < 0 ? data : swab16(data);
    }
    
    static int mt9t001_write(struct i2c_client *client, const u8 reg,
    			 const u16 data)
    {
    	return i2c_smbus_write_word_data(client, reg, swab16(data));
    }
    
    static int mt9t001_set_output_control(struct mt9t001 *mt9t001, u8 clear, u8 set)
    {
    	struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->sd);
    	u8 value = (mt9t001->output_control & ~clear) | set;
    	int ret;
    
    	dev_info(&client->dev, "Function mt9t001_set_output_control");
    	dev_info(&client->dev, "Value: %d, Clear: %d, Set: %d", value, clear, set);
    
    	ret = mt9t001_write(client, MT9T001_OUTPUT_CONTROL, value);
    	if (ret < 0)
    		return ret;
    
    	mt9t001->output_control = value;
    	return 0;
    }
    
    /* -----------------------------------------------------------------------------
     * V4L2 subdev video operations
     */
    
    static int mt9t001_s_stream(struct v4l2_subdev *subdev, int enable)
    {
    	struct i2c_client *client = v4l2_get_subdevdata(subdev);
    	struct mt9t001 *device = to_mt9t001(subdev);
    	
    	dev_info(&client->dev, "Function mt9t001_s_stream");
    
    	device->streaming = enable;
    
    	dev_info(&client->dev, "Enable stream %d", device->streaming);
    
    
    	/* Switch to master "normal" mode or stop sensor readout */
    	return mt9t001_set_output_control(device,
    		enable ? 0 : MT9T001_OUTPUT_CONTROL_CHIP_ENABLE,
    		enable ? MT9T001_OUTPUT_CONTROL_CHIP_ENABLE : 0);
    }
    
    static int mt9t001_enum_mbus_code(struct v4l2_subdev *subdev,
    				  struct v4l2_subdev_fh *fh,
    				  struct v4l2_subdev_mbus_code_enum *code)
    {
    	if (code->index > 0)
    		return -EINVAL;
    
    	code->code = V4L2_MBUS_FMT_SGRBG10_1X10;
    	return 0;
    }
    
    static int mt9t001_enum_frame_size(struct v4l2_subdev *subdev,
    				   struct v4l2_subdev_fh *fh,
    				   struct v4l2_subdev_frame_size_enum *fse)
    {
    	if (fse->index >= 8 || fse->code != V4L2_MBUS_FMT_SGRBG10_1X10)
    		return -EINVAL;
    
    	fse->min_width = (MT9T001_WINDOW_WIDTH_DEF + 1) / fse->index;
    	fse->max_width = fse->min_width;
    	fse->min_height = (MT9T001_WINDOW_HEIGHT_DEF + 1) / fse->index;
    	fse->max_height = fse->min_height;
    
    	return 0;
    }
    
    static struct v4l2_mbus_framefmt *
    __mt9t001_get_pad_format(struct mt9t001 *mt9t001, struct v4l2_subdev_fh *fh,
    			 unsigned int pad, enum v4l2_subdev_format_whence which)
    {
    	switch (which) {
    	case V4L2_SUBDEV_FORMAT_TRY:
    		return v4l2_subdev_get_try_format(fh, pad);
    	case V4L2_SUBDEV_FORMAT_ACTIVE:
    		return &mt9t001->format;
    	default:
    		return NULL;
    	}
    }
    
    static struct v4l2_rect *
    __mt9t001_get_pad_crop(struct mt9t001 *mt9t001, struct v4l2_subdev_fh *fh,
    		       unsigned int pad, enum v4l2_subdev_format_whence which)
    {
    	switch (which) {
    	case V4L2_SUBDEV_FORMAT_TRY:
    		return v4l2_subdev_get_try_crop(fh, pad);
    	case V4L2_SUBDEV_FORMAT_ACTIVE:
    		return &mt9t001->crop;
    	default:
    		return NULL;
    	}
    }
    
    static int mt9t001_get_format(struct v4l2_subdev *subdev,
    			      struct v4l2_subdev_fh *fh,
    			      struct v4l2_subdev_format *format)
    {
    	struct mt9t001 *mt9t001 = to_mt9t001(subdev);
    
    	format->format = *__mt9t001_get_pad_format(mt9t001, fh, format->pad,
    						   format->which);
    	return 0;
    }
    
    static int mt9t001_set_format(struct v4l2_subdev *subdev,
    			      struct v4l2_subdev_fh *fh,
    			      struct v4l2_subdev_format *format)
    {
    	struct i2c_client *client = v4l2_get_subdevdata(subdev);
    	struct mt9t001 *mt9t001 = to_mt9t001(subdev);
    	struct v4l2_mbus_framefmt *__format;
    	struct v4l2_rect *__crop;
    	unsigned int width;
    	unsigned int height;
    	unsigned int hratio;
    	unsigned int vratio;
    	int ret;
    
    	__crop = __mt9t001_get_pad_crop(mt9t001, fh, format->pad,
    					format->which);
    
    	/* Clamp the width and height to avoid dividing by zero. */
    	width = clamp_t(unsigned int, ALIGN(format->format.width, 2),
    			max(__crop->width / 8, MT9T001_WINDOW_HEIGHT_MIN + 1),
    			__crop->width);
    	height = clamp_t(unsigned int, ALIGN(format->format.height, 2),
    			 max(__crop->height / 8, MT9T001_WINDOW_HEIGHT_MIN + 1),
    			 __crop->height);
    
    	hratio = DIV_ROUND_CLOSEST(__crop->width, width);
    	vratio = DIV_ROUND_CLOSEST(__crop->height, height);
    
    	if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
    		ret = mt9t001_write(client, MT9T001_ROW_ADDRESS_MODE,
    				    hratio - 1);
    		if (ret < 0)
    			return ret;
    
    		ret = mt9t001_write(client, MT9T001_COLUMN_ADDRESS_MODE,
    				    vratio - 1);
    		if (ret < 0)
    			return ret;
    	}
    
    	__format = __mt9t001_get_pad_format(mt9t001, fh, format->pad,
    					    format->which);
    	__format->width = __crop->width / hratio;
    	__format->height = __crop->height / vratio;
    
    	format->format = *__format;
    
    	return 0;
    }
    
    static int mt9t001_get_crop(struct v4l2_subdev *subdev,
    			    struct v4l2_subdev_fh *fh,
    			    struct v4l2_subdev_crop *crop)
    {
    	struct mt9t001 *mt9t001 = to_mt9t001(subdev);
    
    	crop->rect = *__mt9t001_get_pad_crop(mt9t001, fh, crop->pad,
    					     crop->which);
    	return 0;
    }
    
    static int mt9t001_set_crop(struct v4l2_subdev *subdev,
    			    struct v4l2_subdev_fh *fh,
    			    struct v4l2_subdev_crop *crop)
    {
    	struct i2c_client *client = v4l2_get_subdevdata(subdev);
    	struct mt9t001 *mt9t001 = to_mt9t001(subdev);
    	struct v4l2_mbus_framefmt *__format;
    	struct v4l2_rect *__crop;
    	struct v4l2_rect rect;
    	int ret;
    
    	/* Clamp the crop rectangle boundaries and align them to a multiple of 2
    	 * pixels.
    	 */
    	rect.left = clamp(ALIGN(crop->rect.left, 2),
    			  MT9T001_COLUMN_START_MIN,
    			  MT9T001_COLUMN_START_MAX);
    	rect.top = clamp(ALIGN(crop->rect.top, 2),
    			 MT9T001_ROW_START_MIN,
    			 MT9T001_ROW_START_MAX);
    	rect.width = clamp(ALIGN(crop->rect.width, 2),
    			   MT9T001_WINDOW_WIDTH_MIN + 1,
    			   MT9T001_WINDOW_WIDTH_MAX + 1 - rect.left);
    	rect.height = clamp(ALIGN(crop->rect.height, 2),
    			    MT9T001_WINDOW_HEIGHT_MIN + 1,
    			    MT9T001_WINDOW_HEIGHT_MAX + 1 - rect.top);
    
    	if (crop->which == V4L2_SUBDEV_FORMAT_TRY) {
    		ret = mt9t001_write(client, MT9T001_COLUMN_START, rect.left);
    		if (ret < 0)
    			return ret;
    
    		ret = mt9t001_write(client, MT9T001_ROW_START, rect.top);
    		if (ret < 0)
    			return ret;
    
    		ret = mt9t001_write(client, MT9T001_WINDOW_WIDTH,
    				    rect.width - 1);
    		if (ret < 0)
    			return ret;
    
    		ret = mt9t001_write(client, MT9T001_WINDOW_HEIGHT,
    				    rect.height - 1);
    		if (ret < 0)
    			return ret;
    	}
    
    	__crop = __mt9t001_get_pad_crop(mt9t001, fh, crop->pad, crop->which);
    
    	if (rect.width != __crop->width || rect.height != __crop->height) {
    		/* Reset the output image size if the crop rectangle size has
    		 * been modified.
    		 */
    		__format = __mt9t001_get_pad_format(mt9t001, fh, crop->pad,
    						    crop->which);
    		__format->width = rect.width;
    		__format->height = rect.height;
    
    		if (crop->which == V4L2_SUBDEV_FORMAT_TRY) {
    			ret = mt9t001_write(client, MT9T001_ROW_ADDRESS_MODE,
    					    0);
    			if (ret < 0)
    				return ret;
    
    			ret = mt9t001_write(client, MT9T001_COLUMN_ADDRESS_MODE,
    					    0);
    			if (ret < 0)
    				return ret;
    		}
    	}
    
    	*__crop = rect;
    	crop->rect = rect;
    
    	return 0;
    }
    
    /* VSYSADD */
    /*
     * mt9t001_query_dv_preset() - query DV preset
     * @sd: pointer to standard V4L2 sub-device structure
     * @qpreset: standard V4L2 v4l2_dv_preset structure
     *
     * Leggo lo standard impostato sul sensore (deve corrispondere a quanto impostato)
     */
    static int mt9t001_query_dv_preset(struct v4l2_subdev *sd,
    						struct v4l2_dv_preset *qpreset)
    {
    	const struct mt9t001_preset_definition *presets = mt9t001_presets;
    	struct i2c_client *client = v4l2_get_subdevdata(sd);
    	struct mt9t001 *mt9t001;
    	u16 height;
    	u16 width;
    	int index;
    
    	mt9t001 = to_mt9t001(sd);
    
    
    	dev_info(&client->dev, "Function mt9t001_query_dv_preset\n");
    
    	height = mt9t001_read(client, MT9T001_WINDOW_HEIGHT) + 1;
    	width = mt9t001_read(client, MT9T001_WINDOW_WIDTH) + 1;
    
    	/* Do checking of video modes */
    	for (index = 0; index < NUM_PRESETS; index++, presets++)
    	{
    		dev_info(&client->dev, "Index: %d, height: %d, width: %d | preset_height: %d, preset_width %d", index, height, width, presets->height, presets->width);		
    		if ((height  == presets->height) && (width == presets->width))
    		{
    				dev_info(&client->dev, "SUCCESS");
    				break;
    		}
    	}
    
    	if (index == NUM_PRESETS)
    	{
    		dev_info(&client->dev, "detection failed: height = %d, width = %d",height, width);
    		v4l2_dbg(1, debug, sd, "detection failed: height = %d, width = %d",height, width);
    
    		/* Could not detect a signal, so return the 'invalid' preset */
    		qpreset->preset = V4L2_DV_INVALID;
    		return 0;
    	}
    
    	/* Set values in found preset */
    	qpreset->preset = presets->preset;
    
    	/* Update lines per frame and clocks per line info */
    	v4l2_dbg(1, debug, sd, "detected preset: %d\n", presets->preset);
    	return 0;
    }
    
    /* VSYSADD */
    /*
     * mt9t001_mbus_fmt() - V4L2 decoder interface handler for try/s/g_mbus_fmt
     * @sd: pointer to standard V4L2 sub-device structure
     * @f: pointer to mediabus format structure
     *
     * Negotiate the image capture size and mediabus format.
     * There is only one possible format, so this single function works for
     * get, set and try.
     */
    
    static int mt9t001_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f)
    {
    	struct mt9t001 *device = to_mt9t001(sd);
    	struct i2c_client *client = v4l2_get_subdevdata(sd);
    	struct v4l2_dv_enum_preset e_preset;
    	int error;
    
    	dev_info(&client->dev, "Function mt9t001_mbus_fmt");
    
    	/* Calculate height and width based on current standard */
    	error = v4l_fill_dv_preset_info(device->current_preset->preset, &e_preset);
    	if (error)
    		return error;
    
    	f->width = e_preset.width;
    	f->height = e_preset.height;
    	f->code = V4L2_MBUS_FMT_YUYV10_1X20;
    	f->field = device->current_preset->scanmode;
    	f->colorspace = device->current_preset->color_space;
    
    	v4l2_dbg(1, debug, sd, "MBUS_FMT: Width - %d, Height - %d",
    			f->width, f->height);
    	return 0;
    }
    
    /* VSYSADD */
    /*
     * mt9t001_write_inittab() - Write initialization values
     * @sd: ptr to v4l2_subdev struct
     * @regs: ptr to i2c_reg_value struct
     *
     * Write initialization values.
     * Returns zero or -EINVAL if read operation fails.
     */
    static int mt9t001_write_inittab(struct v4l2_subdev *sd,
    					const struct i2c_reg_value *regs)
    {
    	int error = 0;
    	struct i2c_client *client = v4l2_get_subdevdata(sd);
    
    	dev_info(&client->dev, "Function mt9t001_write_inittab");
    
    	/* Initialize the first (defined) registers */
    	while (MT9T001_EOR != regs->reg) {
    		if (MT9T001_WRITE == regs->type)
    		{
    	                mt9t001_write(client, regs->reg, regs->value);
    			dev_info(&client->dev, "Reg: %d, Val: %d", regs->reg,regs->value);
    		}
    		regs++;
    	}
    	
    
    	return error;
    }
    
    /* VSYSADD */
    /*
     * mt9t001_s_dv_preset() - Set digital video preset
     * @sd: ptr to v4l2_subdev struct
     * @dv_preset: ptr to v4l2_dv_preset struct
     *
     * Set the digital video preset for a mt9t001 decoder device.
     * Returns zero when successful or -EINVAL if register access fails.
     */
    static int mt9t001_s_dv_preset(struct v4l2_subdev *sd,
    					struct v4l2_dv_preset *dv_preset)
    {
    	struct mt9t001 *device = to_mt9t001(sd);
    	const struct mt9t001_preset_definition *presets = mt9t001_presets;
    	struct i2c_client *client = v4l2_get_subdevdata(sd);
    	u32 preset;
    	int i;
    	int ret;
    
    	dev_info(&client->dev, "Function mt9t001_s_dv_preset...");
    
    	for (i = 0; i < NUM_PRESETS; i++) {
    		preset = presets[i].preset;
    		dev_info(&client->dev, "Preset: %d, %d",presets[i].preset,dv_preset->preset);
    		if (preset == dv_preset->preset) 
    		{
    			device->current_preset = &presets[i];
    			ret = mt9t001_write_inittab(sd, presets[i].p_settings);
    			dev_info(&client->dev, "SUCCESS, %d",ret);
    			return ret;
    		}
    	}
    	dev_info(&client->dev, "ERROR");
    
    	return -EINVAL;
    }
    
    /* VSYSADD */
    /*
     * mt9t001_enum_dv_presets() - Enum supported digital video formats
     * @sd: pointer to standard V4L2 sub-device structure
     * @preset: pointer to format struct
     *
     * Enumerate supported digital video formats.
     */
    static int mt9t001_enum_dv_presets(struct v4l2_subdev *sd,
    		struct v4l2_dv_enum_preset *preset)
    {
    	struct i2c_client *client = v4l2_get_subdevdata(sd);
    	dev_info(&client->dev, "Function mt9t001_s_dv_preset");
    
    	/* Check requested format index is within range */
    	if (preset->index >= NUM_PRESETS)
    		return -EINVAL;
    
    	return v4l_fill_dv_preset_info(mt9t001_presets[preset->index].preset, preset);
    }
    
    /* -----------------------------------------------------------------------------
     * V4L2 subdev core
     */
    
    /* VSYSADD */
    
    /*
     * mt9t001_g_chip_ident() - Get chip identification number
     * @sd: ptr to v4l2_subdev struct
     * @chip: ptr to v4l2_dbg_chip_ident struct
     *
     * Obtains the chip's identification number.
     * Returns zero or -EINVAL if read operation fails.
     */
    static int mt9t001_g_chip_ident(struct v4l2_subdev *sd,
    					struct v4l2_dbg_chip_ident *chip)
    {
    	u8 rev;
    	struct i2c_client *client = v4l2_get_subdevdata(sd);
    
    	rev = mt9t001_read(client, MT9T001_CHIP_VERSION);
    
    	return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_MT9T001, rev);
    }
    
    /* VSYSADD */
    
    /*
     * mt9t001_log_status() - Print information about register settings
     * @sd: ptr to v4l2_subdev struct
     *
     * Log register values of a MT9T001 decoder device.
     * Returns zero or -EINVAL if read operation fails.
     */
    static int mt9t001_log_status(struct v4l2_subdev *sd)
    {
    	const struct mt9t001_preset_definition *presets = mt9t001_presets;
    	struct mt9t001 *device = to_mt9t001(sd);
    	struct v4l2_dv_enum_preset e_preset;
    	struct v4l2_dv_preset detected;
    	int i;
    
    	detected.preset = V4L2_DV_INVALID;
    	/* Find my current standard*/
    	mt9t001_query_dv_preset(sd, &detected);
    
    	/* Print standard related code values */
    	for (i = 0; i < NUM_PRESETS; i++, presets++)
    		if (presets->preset == detected.preset)
    			break;
    
    	if (v4l_fill_dv_preset_info(device->current_preset->preset, &e_preset))
    		return -EINVAL;
    
    	v4l2_info(sd, "Selected DV Preset: %s\n", e_preset.name);
    	v4l2_info(sd, "   Pixels per line: %u\n", e_preset.width);
    	v4l2_info(sd, "   Lines per frame: %u\n\n", e_preset.height);
    	if (i == NUM_PRESETS) {
    		v4l2_info(sd, "Detected DV Preset: None\n");
    	} else {
    		if (v4l_fill_dv_preset_info(presets->preset, &e_preset))
    			return -EINVAL;
    		v4l2_info(sd, "Detected DV Preset: %s\n", e_preset.name);
    		v4l2_info(sd, "  Pixels per line: %u\n", e_preset.width);
    		v4l2_info(sd, "  Lines per frame: %u\n\n", e_preset.height);
    	}
    
    	v4l2_info(sd, "Streaming enabled: %s\n", device->streaming ? "yes" : "no");
    
    	/* Print the current value of the gain control */
    	v4l2_info(sd, "Gain: %u\n", device->gain);
    
    	return 0;
    }
    
    /* VSYSADD */
    
    /*
     * mt9t001_g_ctrl() - Get a control
     * @sd: ptr to v4l2_subdev struct
     * @ctrl: ptr to v4l2_control struct
     *
     * Get a control for a MT9T001 decoder device.
     * Returns zero when successful or -EINVAL if register access fails.
     */
    static int mt9t001_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
    {
    	struct mt9t001 *device = to_mt9t001(sd);
    	switch (ctrl->id) {
    	case V4L2_CID_GAIN:
    		ctrl->value = device->gain;
    		return 0;
    	default:
    		return -EINVAL;
    	}
    }
    
    /* VSYSADD */
    
    /*
     * mt9t001_queryctrl() - Query a control
     * @sd: ptr to v4l2_subdev struct
     * @qc: ptr to v4l2_queryctrl struct
     *
     * Query a control of a mt9t001 decoder device.
     * Returns zero when successful or -EINVAL if register read fails.
     */
    static int mt9t001_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
    {
    	switch (qc->id) {
    	case V4L2_CID_GAIN:
    		/*
    		 * Gain is supported [0-255, default=0, step=1]
    		 */
    		return v4l2_ctrl_query_fill(qc, 0, 255, 1, 0);
    	default:
    		return -EINVAL;
    	}
    }
    
    /* -----------------------------------------------------------------------------
     * V4L2 subdev controls
     */
    
    #define V4L2_CID_TEST_PATTERN          (V4L2_CID_USER_BASE | 0x1001)
    
    static int mt9t001_s_ctrl(struct v4l2_ctrl *ctrl)
    {
    	struct mt9t001 *mt9t001 =
    			container_of(ctrl->handler, struct mt9t001, ctrls);
    	struct i2c_client *client = v4l2_get_subdevdata(&mt9t001->sd);
    	int data;
    	int ret;
    
    	switch (ctrl->id) {
    	case V4L2_CID_GAIN:
    		/* Gain is controlled by 2 analog stages and a digital state.
    		 * Valid values for the 3 stages are
    		 *
    		 * Stage		Min	Max	Step
    		 * ------------------------------------------
    		 * First analog stage	x1	x2	1
    		 * Second analog stage	x1	x4	0.125
    		 * Digital stage	x1	x16	0.125
    		 *
    		 * To minimize noise, the gain stages should be used in the
    		 * second analog stage, first analog stage, digital stage order.
    		 * Gain from a previous stage should be pushed to its maximum
    		 * value before the next stage is used.
    		 */
    		if (ctrl->val <= 32) {
    			data = ctrl->val;
    		} else if (ctrl->val <= 64) {
    			ctrl->val &= ~1;
    			data = (1 << 6) | (ctrl->val >> 1);
    		} else {
    			ctrl->val &= ~7;
    			data = ((ctrl->val - 64) << 5) | (1 << 6) | 32;
    		}
    
    		ret = mt9t001_write(client, MT9T001_GLOBAL_GAIN, data);
    		if (ret < 0)
    			return -EIO;
    
    		break;
    
    	case V4L2_CID_EXPOSURE:
    		ret = mt9t001_write(client, MT9T001_SHUTTER_WIDTH_LOW,
    				    ctrl->val & 0xffff);
    		if (ret < 0)
    			return -EIO;
    
    		ret = mt9t001_write(client, MT9T001_SHUTTER_WIDTH_HIGH,
    				    ctrl->val >> 16);
    		if (ret < 0)
    			return -EIO;
    
    		break;
    
    	case V4L2_CID_TEST_PATTERN:
    		ret = mt9t001_set_output_control(mt9t001,
    			ctrl->val ? 0 : MT9T001_OUTPUT_CONTROL_TEST_DATA,
    			ctrl->val ? MT9T001_OUTPUT_CONTROL_TEST_DATA : 0);
    		if (ret < 0)
    			return -EIO;
    
    		ret = mt9t001_write(client, MT9T001_TEST_DATA, ctrl->val << 2);
    		if (ret < 0)
    			return -EIO;
    
    		break;
    
    	case V4L2_CID_BLACK_LEVEL:
    		ret = mt9t001_write(client, MT9T001_BLACK_LEVEL_CALIBRATION,
    				    MT9T001_BLACK_LEVEL_RECALCULATE);
    		break;
    	}
    
    	return 0;
    }
    
    static struct v4l2_ctrl_ops mt9t001_ctrl_ops = {
    	.s_ctrl = mt9t001_s_ctrl,
    };
    
    static const struct v4l2_ctrl_config mt9t001_ctrls[] = {
    	{
    		.id		= V4L2_CID_TEST_PATTERN,
    		.type		= V4L2_CTRL_TYPE_INTEGER,
    		.name		= "Test pattern",
    		.min		= 0,
    		.max		= 1023,
    		.step		= 1,
    		.def		= 0,
    		.flags		= 0,
    	},
    };
    
    /* -----------------------------------------------------------------------------
     * V4L2 subdev file operations
     */
    
    static int mt9t001_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
    {
    	struct v4l2_mbus_framefmt *format;
    	struct v4l2_rect *crop;
    
    	crop = v4l2_subdev_get_try_crop(fh, 0);
    	crop->left = MT9T001_COLUMN_START_DEF;
    	crop->top = MT9T001_ROW_START_DEF;
    	crop->width = MT9T001_WINDOW_WIDTH_DEF + 1;
    	crop->height = MT9T001_WINDOW_HEIGHT_DEF + 1;
    
    	format = v4l2_subdev_get_try_format(fh, 0);
    	format->code = V4L2_MBUS_FMT_SGRBG10_1X10;
    	format->width = MT9T001_WINDOW_WIDTH_DEF + 1;
    	format->height = MT9T001_WINDOW_HEIGHT_DEF + 1;
    	format->field = V4L2_FIELD_NONE;
    	format->colorspace = V4L2_COLORSPACE_SRGB;
    
    	return 0;
    }
    
    static struct v4l2_subdev_file_ops mt9t001_subdev_file_ops = {
    	.open		= mt9t001_open,
    };
    
    static struct v4l2_subdev_video_ops mt9t001_subdev_video_ops = {
    	.s_stream = mt9t001_s_stream,
    	.query_dv_preset = mt9t001_query_dv_preset,
    	.g_mbus_fmt = mt9t001_mbus_fmt,
    	.s_dv_preset = mt9t001_s_dv_preset,
    	.enum_dv_presets = mt9t001_enum_dv_presets,
    };
    
    static struct v4l2_subdev_pad_ops mt9t001_subdev_pad_ops = {
    	.enum_mbus_code = mt9t001_enum_mbus_code,
    	.enum_frame_size = mt9t001_enum_frame_size,
    	.get_fmt = mt9t001_get_format,
    	.set_fmt = mt9t001_set_format,
    	.get_crop = mt9t001_get_crop,
    	.set_crop = mt9t001_set_crop,
    };
    
    static const struct v4l2_subdev_core_ops mt9t001_subdev_core_ops = {
    	.g_chip_ident = mt9t001_g_chip_ident,
    	.log_status = mt9t001_log_status,
    	.g_ctrl = mt9t001_g_ctrl,
    	.queryctrl = mt9t001_queryctrl,
    };
    
    static struct v4l2_subdev_ops mt9t001_subdev_ops = {
    	.file	= &mt9t001_subdev_file_ops,
    	.video	= &mt9t001_subdev_video_ops,
    	.pad	= &mt9t001_subdev_pad_ops,
    	.core	= &mt9t001_subdev_core_ops,
    };
    
    static int mt9t001_video_probe(struct i2c_client *client)
    {
    	struct mt9t001_platform_data *pdata = client->dev.platform_data;
    	s32 data;
    	int ret = 0;
    
    	dev_info(&client->dev, "Probing MT9T001 at address 0x%02x\n",
    		 client->addr);
    
    #if 0
    	/* Reset the chip and stop data read out */
    	ret = mt9t001_write(client, MT9T001_RESET, 1);
    	if (ret < 0)
    		return ret;
    
    	ret = mt9t001_write(client, MT9T001_RESET, 0);
    	if (ret < 0)
    		return ret;
    
    	ret  = mt9t001_write(client, MT9T001_OUTPUT_CONTROL, 0);
    	if (ret < 0)
    		return ret;
    
    	/* Configure the pixel clock polarity */
    	if (pdata && pdata->clk_pol) {
    		ret  = mt9t001_write(client, MT9T001_PIXEL_CLOCK,
    				     MT9T001_PIXEL_CLOCK_INVERT);
    		if (ret < 0)
    			return ret;
    	}
    #endif
    
    	/* Read and check the sensor version */
    	data = mt9t001_read(client, MT9T001_CHIP_VERSION);
    	if (data != MT9T001_CHIP_ID) {
    		dev_err(&client->dev, "MT9T001 not detected, wrong version "
    			"0x%04x\n", data);
    		return -ENODEV;
    	}
    
    	dev_info(&client->dev, "MT9T001 detected at address 0x%02x\n",
    		 client->addr);
    
    	return ret;
    }
    
    static int mt9t001_probe(struct i2c_client *client,
    			 const struct i2c_device_id *did)
    {
    	struct mt9t001 *device;
    	struct v4l2_dv_preset preset;
    	unsigned int i;
    	int ret;
    
    	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
    		dev_warn(&client->adapter->dev,
    			 "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
    		return -EIO;
    	}
    
    	ret = mt9t001_video_probe(client);
    	if (ret < 0)
    		return ret;
    
    	device = kzalloc(sizeof(struct mt9t001), GFP_KERNEL);
    	if (!device)
    		return -ENOMEM;
    
    	v4l2_ctrl_handler_init(&device->ctrls, ARRAY_SIZE(mt9t001_ctrls) + 3);
    
    	v4l2_ctrl_new_std(&device->ctrls, &mt9t001_ctrl_ops,
    			  V4L2_CID_GAIN, MT9T001_GLOBAL_GAIN_MIN,
    			  MT9T001_GLOBAL_GAIN_MAX, 1, MT9T001_GLOBAL_GAIN_MIN);
    	v4l2_ctrl_new_std(&device->ctrls, &mt9t001_ctrl_ops,
    			  V4L2_CID_EXPOSURE, MT9T001_SHUTTER_WIDTH_MIN,
    			  MT9T001_SHUTTER_WIDTH_MAX, 1,
    			  MT9T001_SHUTTER_WIDTH_DEF);
    	v4l2_ctrl_new_std(&device->ctrls, &mt9t001_ctrl_ops,
    			  V4L2_CID_BLACK_LEVEL, 1, 1, 1, 1);
    
    	for (i = 0; i < ARRAY_SIZE(mt9t001_ctrls); ++i)
    		v4l2_ctrl_new_custom(&device->ctrls, &mt9t001_ctrls[i], NULL);
    
    	device->sd.ctrl_handler = &device->ctrls;
    
    	if (device->ctrls.error)
    		printk(KERN_INFO "%s: control initialization error %d\n",
    		       __func__, device->ctrls.error);
    
    	// aggiunte per v4l2 loopback simil-tvp7002
    	device->streaming = 1;
    	device->current_preset = mt9t001_presets;
    	device->gain = 10;
    
    	device->crop.left = MT9T001_COLUMN_START_DEF;
    	device->crop.top = MT9T001_ROW_START_DEF;
    	device->crop.width = MT9T001_WINDOW_WIDTH_DEF + 1;
    	device->crop.height = MT9T001_WINDOW_HEIGHT_DEF + 1;
    
    	device->format.code = V4L2_MBUS_FMT_SGRBG10_1X10;
    	device->format.width = MT9T001_WINDOW_WIDTH_DEF + 1;
    	device->format.height = MT9T001_WINDOW_HEIGHT_DEF + 1;
    	device->format.field = V4L2_FIELD_NONE;
    	device->format.colorspace = V4L2_COLORSPACE_SRGB;
    
    	v4l2_i2c_subdev_init(&device->sd, client, &mt9t001_subdev_ops);
    	device->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
    
    	device->pad.flags = MEDIA_PAD_FLAG_OUTPUT;
    	ret = media_entity_init(&device->sd.entity, 1, &device->pad, 0);
    	if (ret < 0)
    		kfree(device);
    
    	/* Set registers according to default video mode */
    	preset.preset = device->current_preset->preset;
    	ret = mt9t001_s_dv_preset(&device->sd, &preset);
    	
    	dev_info(&client->dev, "Current Preset %d", preset.preset);
    	dev_info(&client->dev, "MT9T001 probing end...%d", ret);
    
    	return ret;
    }
    
    static int mt9t001_remove(struct i2c_client *client)
    {
    	struct v4l2_subdev *sd = i2c_get_clientdata(client);
    	struct mt9t001 *mt9t001 = to_mt9t001(sd);
    
    	v4l2_device_unregister_subdev(sd);
    	media_entity_cleanup(&sd->entity);
    	kfree(mt9t001);
    	return 0;
    }
    
    static const struct i2c_device_id mt9t001_id[] = {
    	{ "mt9t001", 0 },
    	{ }
    };
    MODULE_DEVICE_TABLE(i2c, mt9t001_id);
    
    static struct i2c_driver mt9t001_driver = {
    	.driver = {
    		.name = "mt9t001",
    	},
    	.probe		= mt9t001_probe,
    	.remove		= mt9t001_remove,
    	.id_table	= mt9t001_id,
    };
    
    static int __init mt9t001_init(void)
    {
    	return i2c_add_driver(&mt9t001_driver);
    }
    
    static void __exit mt9t001_exit(void)
    {
    	i2c_del_driver(&mt9t001_driver);
    }
    
    module_init(mt9t001_init);
    module_exit(mt9t001_exit);
    
    MODULE_DESCRIPTION("Micron MT9T001 Camera driver");
    MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
    MODULE_LICENSE("GPL");
    
    What do you think about the settings that i made in ti81xxvpss.c? Are there others changes to do? The hardware connections that i made are correct? The saCapture runs but the files that i see saved are all zeros, so i'm not sure that the capture is running

    Fabio