/*
 * qce-ga, linux V4L driver for the Quickcam Express and Dexxa Quickcam
 *
 * pb0100.c - PB0100 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 "pb0100.h"

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

    // Set screen output size
    if (usb_quickcam_set1(dev, STV_Y_CTRL, (mode?2:1))<0) // 1: Y-full, 2: y-half
	goto error;
    if (usb_quickcam_set1(dev, STV_X_CTRL, (mode?6:0x0a))<0) // 06/0a : Half/Full
	goto error;

	return(0);
error:
	return(-1);
}

/* 
 * initialise parameters of PB100 sensor.
 */
static int pb0100_init(struct usb_device *dev, int mode,
int *rgain, int *bgain, int *ggain, struct sensorctrl *sensor_ctrl)
{
struct quickcam_i2c i2cbuff;
 
        usb_quickcam_i2c_new(&i2cbuff);

	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->width      = 176;
            sensor_ctrl->height     = 144;
        } else {
            sensor_ctrl->width      = 352;
            sensor_ctrl->height     = 288;
        }

	if (usb_quickcam_set1(dev, STV_REG00, 1)<0)
		goto error;
	if (usb_quickcam_set1(dev, STV_SCAN_RATE, 0)<0)
		goto error;

	// Reset sensor
        usb_quickcam_i2c_add2(&i2cbuff, PB_RESET, 1);
        if (usb_quickcam_i2c_send(dev,&i2cbuff,PB_ADDR)<0)
		goto error;
        usb_quickcam_i2c_add2(&i2cbuff, PB_RESET, 0);
        if (usb_quickcam_i2c_send(dev,&i2cbuff,PB_ADDR)<0)
		goto error;

	// Disable chip
        usb_quickcam_i2c_add2(&i2cbuff, PB_CONTROL, 0x28);
        if (usb_quickcam_i2c_send(dev,&i2cbuff,PB_ADDR)<0)
		goto error;
	
        // Gain stuff...
        usb_quickcam_i2c_add2(&i2cbuff, PB_PREADCTRL, 0x1440);
        usb_quickcam_i2c_add2(&i2cbuff, PB_ADCMAXGAIN, 0x7F); /* gain max */
        usb_quickcam_i2c_add2(&i2cbuff, PB_ADCMINGAIN, 1);    /* gain min */
        if (usb_quickcam_i2c_send(dev,&i2cbuff,PB_ADDR)<0)
		goto error;

        // Enable auto-exposure...
        usb_quickcam_i2c_add2(&i2cbuff, PB_EXPGAIN, 17);

        usb_quickcam_i2c_add2(&i2cbuff, PB_UPDATEINT, 1);

        usb_quickcam_i2c_add2(&i2cbuff, PB_VOFFSET, 0); /* 0x14 */
        usb_quickcam_i2c_add2(&i2cbuff, PB_ADCGAINH, 0x38); /* 0xd */
        usb_quickcam_i2c_add2(&i2cbuff, PB_ADCGAINL, 0x0);  /* 0x1 */
        if (usb_quickcam_i2c_send(dev,&i2cbuff,PB_ADDR)<0)
		goto error;
        
        // Set individual colour gains
        usb_quickcam_i2c_add2(&i2cbuff, PB_RGAIN, *rgain);
        usb_quickcam_i2c_add2(&i2cbuff, PB_G1GAIN, *ggain);
        usb_quickcam_i2c_add2(&i2cbuff, PB_G2GAIN, *ggain);
        usb_quickcam_i2c_add2(&i2cbuff, PB_BGAIN, *bgain);
        if (usb_quickcam_i2c_send(dev,&i2cbuff,PB_ADDR)<0)
		goto error;

	// ???
        if (usb_quickcam_set1(dev, STV_REG04, 0x07)<0)
		goto error;
        if (usb_quickcam_set1(dev, STV_REG03, 0x45)<0)
		goto error;
        if (usb_quickcam_set1(dev, STV_REG00, 0x11)<0)
		goto error;

        // Set screen output size
        if (pb0100_set_size(dev, mode) <0)
		goto error;

	// 0x27b: 635... why? - HDCS uses 847 -
        if (usb_quickcam_set2(dev, STV_ISO_SIZE, 847)<0) // ISO-Size 
		goto error;

        // Setup sensor window
        usb_quickcam_i2c_add2(&i2cbuff, PB_RSTART, 0);
        usb_quickcam_i2c_add2(&i2cbuff, PB_CSTART, 0);
        usb_quickcam_i2c_add2(&i2cbuff, PB_RWSIZE, 0x11f); // 0xf7: 240
        usb_quickcam_i2c_add2(&i2cbuff, PB_CWSIZE, 0x15f); // 0x13f: 320
        if (usb_quickcam_i2c_send(dev,&i2cbuff,PB_ADDR)<0)
		goto error;

        // Scan rate?
        if (usb_quickcam_set1(dev, STV_SCAN_RATE, (mode?0x10:0x20))<0) // larger -> slower
		goto error;
                
        // Scan/timing for the sensor
        usb_quickcam_i2c_add2(&i2cbuff, PB_ROWSPEED, 0x1a);
        usb_quickcam_i2c_add2(&i2cbuff, PB_CFILLIN, 0x2f);
        usb_quickcam_i2c_add2(&i2cbuff, PB_VBL, 0);
        usb_quickcam_i2c_add2(&i2cbuff, PB_FINTTIME, 0);
        usb_quickcam_i2c_add2(&i2cbuff, PB_RINTTIME, 0x7b);
        if (usb_quickcam_i2c_send(dev,&i2cbuff,PB_ADDR)<0)
		goto error;

        if (usb_quickcam_set1(dev, STV_REG01, 0xc2)<0)
		goto error;
        if (usb_quickcam_set1(dev, STV_REG02, 0xb0)<0)
		goto error;
	return(0);
error:
	return(-1);
}

static int pb0100_set_shutter(struct usb_device * dev, int sval, int xval)
{
	return(0);
}

static int pb0100_set_gains(struct usb_device * dev,
int rgain, int bgain, int ggain)
{
	return(0);
}
  /* Window location and size are controlled by R1, R2, R3 and R4.
   * The default size is CIF (352x288) with to right at (4,12)
   * and bottom left at (355, 299)
   *
   * We try to ensure that the captured area is in the center of
   * the camera purely because that's nicer.  It would be better
   * if the PB0100 sensor supported capture scaling!
   *
   * We do it in on step otherwise size changemay take more
   * than one frame (like xawtv who tests 64x48 and uses 352x288)
   * 3072 = 64x48, 16896 = 352x48, 101376 = 352x288.
   */
static int pb0100_set_window(struct usb_device *dev,
int x, int y, int w, int h, struct sensorctrl *sensor_ctrl)
{
struct quickcam_i2c i2cbuff;
 
        usb_quickcam_i2c_new(&i2cbuff);

	/* PB_RSTART = 12 + y */
        usb_quickcam_i2c_add2(&i2cbuff,PB_RSTART,12 + y);

	/* PB_CSTART = 4 + x */
        usb_quickcam_i2c_add2(&i2cbuff,PB_CSTART,4 + x);

	/* PB_RWSIZE = h - 1 */
        usb_quickcam_i2c_add2(&i2cbuff,PB_RWSIZE,h - 1);

	/* PB_CWSIZE = w - 1 */
        usb_quickcam_i2c_add2(&i2cbuff,PB_CWSIZE, w - 1);

        if (sensor_ctrl->mode) {
            sensor_ctrl->width=w/2;
            sensor_ctrl->height=h/2;
        } else {
            sensor_ctrl->width=w;
            sensor_ctrl->height=h;
        }

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

/* start grabbing */

static int pb0100_start(struct usb_device * dev, struct sensorctrl *sensor_ctrl)
{
struct quickcam_i2c i2cbuff;
 
        usb_quickcam_i2c_new(&i2cbuff);

        usb_quickcam_i2c_add2(&i2cbuff,PB_CONTROL, 0x2b);

        return(usb_quickcam_i2c_send(dev,&i2cbuff,PB_ADDR));
}
/* stop grabbing */
static int pb0100_stop(struct usb_device * dev, struct sensorctrl *sensor_ctrl)
{
struct quickcam_i2c i2cbuff;
 
        usb_quickcam_i2c_new(&i2cbuff);

        usb_quickcam_i2c_add2(&i2cbuff,PB_ABORTFRAME, 1);
        if (usb_quickcam_i2c_send(dev,&i2cbuff,PB_ADDR)<0)
		return (-3);

        usb_quickcam_i2c_add2(&i2cbuff,PB_CONTROL, 0x28);
        if (usb_quickcam_i2c_send(dev,&i2cbuff,PB_ADDR)<0)
		return  (-2);

	return(0);
}

/*
 * Fill the pointers so that the routines are called from quickcam.c
 */
void load_pb0100_mod(struct sensorctrl *sensor_ctrl)
{
	sensor_ctrl->init	= pb0100_init;
	sensor_ctrl->set_shutter = pb0100_set_shutter;
	sensor_ctrl->set_gains	= pb0100_set_gains;
	sensor_ctrl->set_window	= pb0100_set_window;
	sensor_ctrl->set_size	= pb0100_set_size;
	sensor_ctrl->start	= pb0100_start;
	sensor_ctrl->stop	= pb0100_stop;
        sensor_ctrl->width      = 176;
        sensor_ctrl->height     = 144;
        sensor_ctrl->mode       = 1;
}
