/*
 * qce-ga, linux V4L driver for the Quickcam Express and Dexxa Quickcam
 *
 * hdcs.c - HDCS Sensor Implementation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
*/

#include "quickcam.h"
#include "hdcs.h"
#include "helper.h"

static unsigned char control;
static unsigned char config;

/* start grabbing */
static int hdcs_start(struct usb_device * dev, struct sensorctrl *sensor_ctrl)
{
	struct quickcam_i2c i2cbuff;

        usb_quickcam_i2c_new(&i2cbuff);
        usb_quickcam_i2c_add(&i2cbuff,control,0x04);
        return(usb_quickcam_i2c_send(dev,&i2cbuff,HDCS_ADDR));
}
/* stop grabbing */
static int hdcs_stop(struct usb_device * dev, struct sensorctrl *sensor_ctrl)
{
struct quickcam_i2c i2cbuff;

        usb_quickcam_i2c_new(&i2cbuff);
        usb_quickcam_i2c_add(&i2cbuff,control,0x00);
        if (usb_quickcam_i2c_send(dev,&i2cbuff,HDCS_ADDR) < 0)
                return  (-2);
	return(0);
}

/* sensor window to read out */

static int hdcs_set_size(struct usb_device *dev, int mode)
{
    if(mode != 0 && mode != 1)
	goto error;

    // 1: Y-full, 2: y-half
    if (usb_quickcam_set1(dev, STV_Y_CTRL, 1) < 0)
	goto error;
    // 06/0a : Half/Full
    if (usb_quickcam_set1(dev, STV_X_CTRL, 0x0a) < 0)
	goto error;
    
    return(0);
error:
    return(-1);
}


/* 
 * initialise parameters - copy + paste from Georg Acher's user module
 * for hdcs sensor.
 */
static int hdcs_init(struct usb_device *dev, int mode,
int *rgain, int *bgain, int *ggain, struct sensorctrl *sensor_ctrl)
{
struct quickcam_i2c i2cbuff;
short isosize;

        if (*rgain<=0 || *rgain>255) *rgain=RGAIN_DEF;
        if (*bgain<=0 || *bgain>255) *bgain=BGAIN_DEF;
        if (*ggain<=0 || *ggain>255) *ggain=GGAIN_DEF;

        sensor_ctrl->mode=mode;
	if (mode) {
            sensor_ctrl->mode=2; // quater.
            sensor_ctrl->width      = 176;
            sensor_ctrl->height     = 144;
        } else {
            sensor_ctrl->width      = 352;
            sensor_ctrl->height     = 288;
        }

        if (usb_quickcam_set1(dev, STV_REG23, 0) < 0)
		goto error;

        /* set the STV0602AA in STV0600 emulation mode */
        if (dev->descriptor.idProduct==0x0870)
            if (usb_quickcam_set1(dev, 0x1446, 1) < 0)
                goto error;

	/*
	 * reset the Image Sensor. (keeping it to 1 is a problem).
	 */
        usb_quickcam_i2c_new(&i2cbuff);
        usb_quickcam_i2c_add(&i2cbuff,control,1);
        if (usb_quickcam_i2c_send(dev,&i2cbuff,HDCS_ADDR) < 0) {
		printk(KERN_ERR "usb_quickcam_i2c_send HDCS_CONTROL(0x01) failed\n");
		goto error;
	}
        usb_quickcam_i2c_add(&i2cbuff,control,0);
        if (usb_quickcam_i2c_send(dev,&i2cbuff,HDCS_ADDR) < 0) {
		printk(KERN_ERR "usb_quickcam_i2c_send HDCS_CONTROL(0x00) failed\n");
		goto error;
	}

	// clear status.
        usb_quickcam_i2c_add(&i2cbuff,HDCS_STATUS, 0x00);

	// clear interrupt mask.
        usb_quickcam_i2c_add(&i2cbuff,HDCS_IMASK, 0x00);
        if (usb_quickcam_i2c_send(dev,&i2cbuff,HDCS_ADDR) < 0)
		goto error;

	/* was the place where Georg sets gains */

        if (usb_quickcam_set1(dev, STV_REG00, 0x1d) < 0)
		goto error;
        if (usb_quickcam_set1(dev, STV_REG04, 0x07) < 0)
		goto error;
        if (usb_quickcam_set1(dev, STV_REG03, 0x95) < 0)
		goto error;
 
        if (usb_quickcam_set1(dev, STV_REG23, 0) < 0) 
		goto error;
 
	// larger -> slower 
        if (usb_quickcam_set1(dev, STV_SCAN_RATE, 0x20) <0)
		goto error;
	// ISO-Size, 0x34f = 847 .. 0x284 = 644
        if (dev->descriptor.idProduct==0x0870)
            isosize = 644;
        else
            isosize = 847;
        if (usb_quickcam_set2(dev, STV_ISO_SIZE, isosize) < 0) {
		printk(KERN_ERR "usb_quickcam_set2 STV_ISO_SIZE(847) failed\n");
		goto error;
	}

	// Set Size 
	if (hdcs_set_size(dev, mode) < 0)
		goto error;
 
        usb_quickcam_i2c_add(&i2cbuff,HDCS_STARTY, 2);
        usb_quickcam_i2c_add(&i2cbuff,HDCS_STARTX, 4);
        usb_quickcam_i2c_add(&i2cbuff,HDCS_STOPY, 0x4e);
        usb_quickcam_i2c_add(&i2cbuff,HDCS_STOPX, 0x5b);

	// 0x7-0x50
        if  (dev->descriptor.idProduct==0x0870)
            usb_quickcam_i2c_add(&i2cbuff,HDCS_INTEGRATE, 0x7e);
        else
            usb_quickcam_i2c_add(&i2cbuff,HDCS_INTEGRATE, 0x09);

        usb_quickcam_i2c_add(&i2cbuff,control, 0);
        usb_quickcam_i2c_add(&i2cbuff,HDCS_REG2A, 0x0);
        usb_quickcam_i2c_add(&i2cbuff,HDCS_REG2C, 0x0);

        if (usb_quickcam_i2c_send(dev,&i2cbuff,HDCS_ADDR) < 0)
		goto error;
 
        if (usb_quickcam_set1(dev, STV_REG01, 0xb5) < 0)
		goto error;
        if (usb_quickcam_set1(dev, STV_REG02, 0xa8) < 0)
		goto error;

        usb_quickcam_i2c_add(&i2cbuff,HDCS_REG06, 0x63);
        usb_quickcam_i2c_add(&i2cbuff,HDCS_REG08, 0x00);
        usb_quickcam_i2c_add(&i2cbuff,HDCS_REG0A, 0x20);
        usb_quickcam_i2c_add(&i2cbuff,HDCS_REG0C, 0x12);

	/*
         * CONFIG: 0x08 Continous Frame Capture.
	 *         0x04 Stop when Frame Complete.
         */
        usb_quickcam_i2c_add(&i2cbuff,config, (mode?0x38:0x08));
        usb_quickcam_i2c_add(&i2cbuff,HDCS_ADC_BITS, 10);

        if (usb_quickcam_i2c_send(dev,&i2cbuff,HDCS_ADDR) < 0)
                goto error;

	return(0);
error:
	return(-1);
}
/* set_shutter */

static int hdcs_set_shutter(struct usb_device *dev, int sval, int xval)
{
struct quickcam_i2c i2cbuff;

        usb_quickcam_i2c_new(&i2cbuff);

        usb_quickcam_i2c_add(&i2cbuff,control, 0);
        usb_quickcam_i2c_add(&i2cbuff,HDCS_SHUTTERL, sval&255);
        usb_quickcam_i2c_add(&i2cbuff,HDCS_SHUTTERH, sval>>8);
        usb_quickcam_i2c_add(&i2cbuff,HDCS_REG2A, xval&255);
        usb_quickcam_i2c_add(&i2cbuff,HDCS_REG2C, xval>>8);
        usb_quickcam_i2c_add(&i2cbuff,control, 4);

        return(usb_quickcam_i2c_send(dev,&i2cbuff,HDCS_ADDR));
}

/*
 * set gains
 * According to spec's 128 multiplies by 2 the values.
 */
static int hdcs_set_gains(struct usb_device *dev,
int rgain, int bgain, int ggain)
{
struct quickcam_i2c i2cbuff;

        usb_quickcam_i2c_new(&i2cbuff);

        usb_quickcam_i2c_add(&i2cbuff,HDCS_GAIN_GREEN1,
            128+qcmax(0,qcmin(127,ggain/2)));
        usb_quickcam_i2c_add(&i2cbuff,HDCS_GAIN_RED,
            128+qcmax(0,qcmin(127,rgain/2)));
        usb_quickcam_i2c_add(&i2cbuff,HDCS_GAIN_BLUE,
            128+qcmax(0,qcmin(127,bgain/2)));
        usb_quickcam_i2c_add(&i2cbuff,HDCS_GAIN_GREEN2,
            128+qcmax(0,qcmin(127,ggain/2)));

        return(usb_quickcam_i2c_send(dev,&i2cbuff,HDCS_ADDR));
}

/*
 *  Sets the size (scaling) of the capture window.
 *  If subsample could return the image size we use subsample.
 */
static int hdcs_set_window(struct usb_device *dev, int x, int y,
int width, int height, struct sensorctrl *sensor_ctrl)
{
struct quickcam_i2c i2cbuff;

        usb_quickcam_i2c_new(&i2cbuff);

        if (!sensor_ctrl->mode) {
            sensor_ctrl->width=width;
            sensor_ctrl->height=height;
            if (width<=176 && height<=144) {
                width =width*2;
                height=height*2;
                usb_quickcam_i2c_add(&i2cbuff,config, 0x38);
                }
            else
                usb_quickcam_i2c_add(&i2cbuff,config, 0x08);
        } else {
            sensor_ctrl->width=width/2;
            sensor_ctrl->height=height/2;
        }                                                                       

        usb_quickcam_i2c_add(&i2cbuff,HDCS_STARTY,2+y);
        usb_quickcam_i2c_add(&i2cbuff,HDCS_STARTX,4+x);
        usb_quickcam_i2c_add(&i2cbuff,HDCS_STOPY,(height+4)/4);  // 4
        usb_quickcam_i2c_add(&i2cbuff,HDCS_STOPX,(width+12)/4);  // 12

        return(usb_quickcam_i2c_send(dev,&i2cbuff,HDCS_ADDR)); 
}


/*
 * Fill the pointers so that the routines are called from quickcam.c
 */
void load_hdcs_mod(struct sensorctrl *sensor_ctrl)
{
	sensor_ctrl->init	= hdcs_init;
	sensor_ctrl->set_shutter = hdcs_set_shutter;
	sensor_ctrl->set_gains	= hdcs_set_gains;
	sensor_ctrl->set_window	= hdcs_set_window;
	sensor_ctrl->set_size	= hdcs_set_size;
	sensor_ctrl->start	= hdcs_start;
	sensor_ctrl->stop	= hdcs_stop;
	control = HDCS_CONTROL;
	config = HDCS_CONFIG;
}
void load_hdcs20_mod(struct sensorctrl *sensor_ctrl)
{
	load_hdcs_mod(sensor_ctrl);
	control = HDCS20_CONTROL;
	config  = HDCS20_CONFIG;
}
