/*
 * qce-ga, linux V4L driver for the Quickcam Express and Dexxa Quickcam
 *
 * vv6450.c - Sensor Implementation for Quickcam Messenger???
 *            The sensor is NOT called 6450, I just copied the vv6410.c
 *            to have something to work with.
 *
 * 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
 *
*/

#ifdef NOKERNEL
#include "quickcam.h"
#else
#include <linux/quickcam.h>
#endif

/* LSB bit of I2C address signifies write (0) or read (1) */

/* I2C Registers */

/* Status registers */
#define VV6450_DEVICEH		0xE00A	/* Chip identification number including revision indicator */
#define VV6450_STATUS0		0x02	/* User can determine whether timed I2C data has been consumed by interrogating flag states */
#define VV6450_LINECOUNTH	0x03	/* Current line counter value */
#define VV6450_LINECOUNTL	0x04
#define VV6450_XENDH		0x05	/* End x coordinate of image size */
#define VV6450_XENDL		0x06
#define VV6450_YENDH		0x07	/* End y coordinate of image size */
#define VV6450_YENDL		0x08
#define VV6450_DARKAVGH		0x09	/* This is the average pixel value returned from the dark line offset cancellation algorithm */
#define VV6450_DARKAVGL		0x0A
#define VV6450_BLACKAVGH	0x0B	/* This is the average pixel value returned from the black line offset cancellation algorithm  */
#define VV6450_BLACKAVGL	0x0C
#define VV6450_STATUS1		0x0D	/* Flags to indicate whether the x or y image coordinates have been clipped */

/* Setup registers */
#define VV6450_SETUP0		0x10	/* Low-power/sleep modes & video timing */
#define VV6450_SETUP1		0x11	/* Various parameters */
#define VV6450_SYNCVALUE	0x12	/* Contains pixel counter reset value used by external sync */
#define VV6450_FGMODES		0x14	/* Frame grabbing modes (FST, LST and QCK) */
#define VV6450_PINMAPPING	0x15	/* FST and QCK mapping modes. */
#define VV6450_DATAFORMAT	0x16	/* Data resolution */
#define VV6450_OPFORMAT		0x17	/* Output coding formats */
#define VV6450_MODESELECT	0x18	/* Various mode select bits */

/* Exposure registers */
#define VV6450_FINEH		0x20	/* Fine exposure. */
#define VV6450_FINEL		0x21
#define VV6450_COARSEH		0x22	/* Coarse exposure */
#define VV6450_COARSEL		0x23
#define VV6450_ANALOGGAIN	0x24	/* Analog gain setting */
#define VV6450_CLKDIV		0x25	/* Clock division */
#define VV6450_DARKOFFSETH	0x2C	/* Dark line offset cancellation value */
#define VV6450_DARKOFFSETL	0x2D
#define VV6450_DARKOFFSETSETUP	0x2E	/* Dark line offset cancellation enable */

/* Colour registers (none on this camera!) */

/* Video timing registers */
#define VV6450_LINELENGTHH	0x52	/* Line Length (Pixel Clocks) */
#define VV6450_LINELENGTHL	0x53
#define VV6450_XOFFSETH		0x57	/* X-co-ordinate of top left corner of region of interest (x-offset) */
#define VV6450_XOFFSETL		0x58
#define VV6450_YOFFSETH		0x59	/* Y-co-ordinate of top left corner of region of interest (y-offset) */
#define VV6450_YOFFSETL		0x5A
#define VV6450_FIELDLENGTHH	0x61	/* Field length (Lines) */
#define VV6450_FIELDLENGTHL	0x62

/* Text overlay registers (none on this camera!) */

/* I2C autoload registers (none on this camera!) */

/* System registers */
#define VV6450_BLACKOFFSETH	0x70	/* Black offset cancellation default value */
#define VV6450_BLACKOFFSETL	0x71
#define VV6450_BLACKOFFSETSETUP	0x72	/* Black offset cancellation setup */
#define VV6450_CR0		0x75	/* Analog Control Register 0 */
#define VV6450_CR1		0x76	/* Analog Control Register 1 */
#define VV6450_AS0		0x77	/* ADC Setup Register */
#define VV6450_AT0		0x78	/* Analog Test Register */
#define VV6450_AT1		0x79	/* Audio Amplifier Setup Register */

//#define I2C_SET_CHECK(reg,val)	if ((r = qc_i2c_set(qc,(reg),(val)))<0) goto fail
#define I2C_SET_CHECK(reg,val)	do {} while(0)
#define STV_SET_CHECK(reg,val)	if ((r = qc_stv_set(qc,(reg),(val)))<0) goto fail
#define STV_SETW_CHECK(reg,val)	if ((r = qc_stv_setw(qc,(reg),(val)))<0) goto fail

/**
 * Set window size
 */
static int vv6450_set_size(struct quickcam *qc, unsigned int width, unsigned int height)
{
	int r = 0;
	struct qc_sensor_data *sd = &qc->sensor_data;
	if (qcdebug&QC_DEBUGLOGIC)
	  PDEBUG("vv6450_set_size(quickcam=%p,width=%i,height=%i)",qc,width,height);

	if ( (width <= 162) && (height <= 124) )
	{
		sd->width = 162;
		sd->height = 124;
		STV_SET_CHECK(0x1505, 0x0f);
		if(r) goto fail;
	} else
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
	if ( (width <= 176) && (height <= 144) )
	{
		/* exception for small QCIF even if not good fit */
		sd->width = 162;
		sd->height = 124;
		qc->vwin.width = MIN(sd->width, width);
		qc->vwin.height = MIN(sd->height, height);
		STV_SET_CHECK(0x1505, 0x0f);
		if(r) goto fail;
		//return 0;
	} else
	if ( (width <= 162) && (height <= 248) )
	{
		sd->width = 162;
		sd->height = 248;
		STV_SET_CHECK(0x1505, 0x08);
		if(r) goto fail;
	} else
	if ( (width <= 324) && (height <= 124) )
	{
		sd->width = 324;
		sd->height = 124;
		STV_SET_CHECK(0x1505, 0x04);
		if(r) goto fail;
	} else
	if ( (width <= 324) && (height <= 248) )
	{
		sd->width = 324;
		sd->height = 248;
		STV_SET_CHECK(0x1505, 0x02);
		if(r) goto fail;
	} else
	if ( (width <= 352) && (height <= 288) )
	{
		/* exception for CIF even if not good fit */
		sd->width = 324;
		sd->height = 248;
		qc->vwin.width = MIN(sd->width, width);
		qc->vwin.height = MIN(sd->height, height);
		STV_SET_CHECK(0x1505, 0x02);
		if(r) goto fail;
		//return 0;
	} else
#ifdef TRY_HIGHRES
	if ( (width <= 648) && (height <= 496) )
	{
		// this doesn't work yet
		sd->width = 648;
		sd->height = 496;
		STV_SET_CHECK(0x1505, 0x01);
		if(r) goto fail;
	} else
#endif
	{
#if 1
		PDEBUG("Unknown resolution %dx%d, set 324x248", width, height);
		sd->width = 324;
		sd->height = 248;
		qc->vwin.width = sd->width;
		qc->vwin.height = sd->height;
		STV_SET_CHECK(0x1505, 0x02);
		if(r) goto fail;
#endif
		goto fail;
	}
#if 0
	qc->vwin.width = width;
	qc->vwin.height = height;
	qc->vwin.width = sd->width;
	qc->vwin.height = sd->height;
#endif
	qc->vwin.width = MIN(sd->width, width);
	qc->vwin.height = MIN(sd->height, height);

	PDEBUG("set sensor=%dx%d vwin=%dx%d", sd->width, sd->height, qc->vwin.width, qc->vwin.height);
	return 0;
fail:	return -1;
}

/* start grabbing */
static int vv6450_start(struct quickcam *qc)
{
	int r = 0;
	
	if (qcdebug&QC_DEBUGLOGIC) PDEBUG("vv6450_start(quickcam=%p)",qc);
	if (PARANOID && !qc)  { PDEBUG("qc==NULL"); return -EINVAL; }

	r = qc_i2c_wait(qc);
	STV_SET_CHECK(STV_ISO_ENABLE, 0x01);
	return 0;
fail:
	//PDEBUG("Error start");
	return r;
}

/* stop grabbing */
static int vv6450_stop(struct quickcam *qc)
{
	static const int low_power_mode = 1;
	static const int sleep_mode     = 1;
	struct qc_sensor_data *sd = &qc->sensor_data;
	unsigned char cmd;
	int r = 0;
	if (qcdebug&QC_DEBUGLOGIC) PDEBUG("vv6450_stop(quickcam=%p)",qc);

	cmd = (sleep_mode << 1) | low_power_mode;
	if (sd->subsample) cmd |= BIT(7)|BIT(6);		/* sub-sampled QCIF mode */
	r = qc_i2c_wait(qc);
	
	STV_SET_CHECK(STV_ISO_ENABLE, 0x00);
	return 0;
fail:
	//PDEBUG("Error stop");
	return r;
}

struct stv_init {
	const u8 *data;	/* If NULL, only single value to write, stored in len */
	u16 start;
        u16 len;
};

/* 
 * initialise parameters for vv6450 sensor.
 * Just try to send the same commands as Windoze Quickcam soft.
 */
static int vv6450_init(struct quickcam *qc)
{
	struct qc_sensor_data *sd = &qc->sensor_data;
	int r = 0;
	static const struct stv_init stv_init[] = {

/* LOGTAG */ { NULL, STV_ISO_ENABLE, 0x00 },	/* disable capture */
/* LOGTAG */ { NULL, 0x1436, 0x00 },
/* LOGTAG */ { NULL, 0x1432, 0x03 },				/* 0x00-0x1F contrast ? */
/* LOGTAG */ { NULL, 0x143a, 0xF9 },				/* 0x00-0x0F - gain */
/* LOGTAG */ { NULL, 0x0509, 0x38 },				/* R */
/* LOGTAG */ { NULL, 0x050a, 0x38 },				/* G */
/* LOGTAG */ { NULL, 0x050b, 0x38 },				/* B */
/* LOGTAG */ { NULL, 0x050c, 0x2A },
/* LOGTAG */ { NULL, 0x050d, 0x01 },


/* LOGTAG */ { NULL, 0x1431, 0x00 },				/* 0x00-0x07 ??? */
/* LOGTAG */ { NULL, 0x1433, 0x34 },	/* 160x120 */		/* 0x00-0x01 night filter */
/* LOGTAG */ { NULL, 0x1438, 0x18 },	/* 640x480 */
// 18 bayes
// 10 compressed?

/* LOGTAG */ { NULL, 0x1439, 0x00 },
// antiflimmer??  0xa2 ger perfekt bild mot monitor

/* LOGTAG */ { NULL, 0x143b, 0x05 },
/* LOGTAG */ { NULL, 0x143c, 0x00 },				/* 0x00-0x01 - ??? */


// shutter time 0x0000-0x03FF
// low value  give good picures on moving objects (but requires much light)
// high value gives good picures in darkness (but tends to be overexposed)
/* LOGTAG */ { NULL, 0x143e, 0x01 },
/* LOGTAG */ { NULL, 0x143d, 0x00 },

/* LOGTAG */ { NULL, 0x1442, 0xe2 },
// write: 1x1x xxxx
// read:  1x1x xxxx
//        bit 5 == button pressed and hold if 0
// write 0xe2,0xea

// 0x144a
// 0x00 init
// bit 7 == button has been pressed, but not handled

// interrupt
//if(urb->iso_frame_desc[i].status == 0x80) {
//if(urb->iso_frame_desc[i].status == 0x88) {

/* LOGTAG */ { NULL, 0x1500, 0xd0 },
/* LOGTAG */ { NULL, 0x1500, 0xd0 },
/* LOGTAG */ { NULL, 0x1500, 0x50 },	/* 0x00 - 0xFF  0x80 == compr ? */

/* LOGTAG */ { NULL, 0x1501, 0xaf },
// high val-> ljus area blir morkare.
// low val -> ljus area blir ljusare.
/* LOGTAG */ { NULL, 0x1502, 0xc2 },
// high val-> ljus area blir morkare.
// low val -> ljus area blir ljusare.
/* LOGTAG */ { NULL, 0x1503, 0x45 },
// high val-> ljus area blir morkare.
// low val -> ljus area blir ljusare.

/* LOGTAG */ { NULL, 0x1505, 0x02 },
// 2  : 324x248  80352 bytes
// 7  : 248x162  40176 bytes
// c+f: 162*124  20088 bytes

/* LOGTAG */ { NULL, 0x150e, 0x8e },
/* LOGTAG */ { NULL, 0x150f, 0x37 },
/* LOGTAG */ { NULL, 0x15c0, 0x00 },
/* LOGTAG */ { NULL, 0x15c1, ISOC_PACKET_SIZE },	/* 160x120 */ /* ISOC_PACKET_SIZE */
/* LOGTAG */ { NULL, 0x15c3, 0x08 },				/* 0x04/0x14 ... test pictures ??? */


/* LOGTAG */ { NULL, 0x143f, 0x01 },	/* commit settings */

	};

	unsigned int cols = 416;
	unsigned int rows = 320;
	int i,j;

	if (qcdebug&QC_DEBUGLOGIC) PDEBUG("vv6450_init(quickcam=%p)",qc);
	if (PARANOID && !qc) { PDEBUG("qc==NULL"); return -EINVAL; }

	for (i=0; i<SIZE(stv_init); i++) {
		if (stv_init[i].data==NULL) {
		  if(stv_init[i].len & 0xFF00) {
		    //STV_SETW_CHECK(stv_init[i].start, stv_init[i].len);
		    if ((r = qc_stv_setw(qc,(stv_init[i].start),(stv_init[i].len)))<0)
		    {
		      PDEBUG("qc_stv_setw: error");
		    }
		  } else {
		    STV_SET_CHECK(stv_init[i].start, stv_init[i].len);
		  }
		} else {
			for (j=0; j<stv_init[i].len; j++) {
				STV_SET_CHECK(stv_init[i].start+j, stv_init[i].data[j]);
			}
		}
	}

	
	sd->width  = 324;  // This is what is setup in the init-string
	sd->height = 248;
	
	sd->maxwidth  = MAX_FRAME_WIDTH;
	sd->maxheight = MAX_FRAME_HEIGHT;

	//PDEBUG("Setup maxwidth=%d,%d\n", sd->maxwidth, sd->maxheight);

	if (!sd->compress) {
		/* Disable compression */
#if 0
		STV_SET_CHECK(0x1443, sd->subsample ? 0x00 : 0x10);	/* Scan rate: Larger -> slower */
		STV_SETW_CHECK(0x15C1, 1023);				/* ISO-Size */
		STV_SET_CHECK(0x15C3, 1);				/* Y control */
#endif

		if (qc->settings.subsample) {
			//FIXME:subsampling (still) doesn't work yet
			cols=250;
			rows=160;
			sd->width  = 180;
			sd->height = 148;
#if 0
			I2C_SET_CHECK(VV6450_SETUP0, BIT(7)|BIT(6)|BIT(1)|BIT(0));	/* Subsampled timing mode */
#endif
			if (qcdebug&QC_DEBUGLOGIC) PDEBUG("WARNING: subsample\n");
		}
	}
	return r;
fail:
	//PDEBUG("Error setup");
	return r;
}

static int vv6450_set_exposure(struct quickcam *qc, unsigned int val)
{
	struct qc_sensor_data *sd = &qc->sensor_data;
	int r = 0;
	int a = (val >> 12) & 0x0F;
#if 1
	int oldval = sd->exposure;
	if (val == oldval) return 0;
	sd->exposure = val;
	if ((oldval>>12) == a) return 0;
#else
	if((sd->exposure>>12) == a) return 0;
	sd->exposure = val;
#endif
	if(a >= 14) a = 14;
	if (qcdebug&QC_DEBUGADAPTATION) PDEBUG("vv6450_set_exposure %d (%d)",sd->exposure, a);

        r = qc_i2c_wait(qc);
	if(r) goto fail;
	STV_SET_CHECK(0x143A, 0xF0 | a);
        r = qc_i2c_wait(qc);
	if(r) goto fail;
	STV_SET_CHECK(0x143F, 0x01); // commit settings
	//qc_frame_flush(qc);
	return r;
 fail:
	//PDEBUG("Error: vv6450_set_exposure");
	return r;
} 

int toggle = 0;
int toggle_val[4] = {0x00, 0x15, 0x2A, 0x3F};

static int vv6450_set_gains(struct quickcam *qc, u16 hue, u16 sat, u16 val)
{
	static const unsigned int min_gain = 8;
	struct qc_sensor_data *sd = &qc->sensor_data;
	unsigned int rgain, bgain, ggain;
	int r = 0;
	qc_hsv2rgb(hue, sat, val, &rgain, &bgain, &ggain);
	rgain >>= 12;					/* After this the values are 0..15 */
	ggain >>= 12;
	bgain >>= 12;

	/* Do not allow very small values, they cause bad (low-contrast) image */
	rgain = MAX(rgain, min_gain);
	ggain = MAX(ggain, min_gain);
	bgain = MAX(bgain, min_gain);
	//PDEBUG("debug: vv6450_set_gains hue=%d sat=%d val=%d  rgain=%02d ggain=%02d bgain=%02d", hue, sat, val, rgain, ggain, bgain);

	if (rgain==sd->rgain && ggain==sd->ggain && bgain==sd->bgain) {
	  return 0;
	}
	sd->rgain = rgain;
	sd->ggain = ggain;
	sd->bgain = bgain;

	//#define RAISE_GAIN
#ifdef RAISE_GAIN
	// don't know if there are any other bit to raise gain...
	if (rgain > 15) rgain = rgain/2 | 0x80;
	else rgain += 0x30;
	if (ggain > 15) ggain = ggain/2 | 0x80;
	else ggain += 0x30;
	if (bgain > 15) bgain = bgain/2 | 0x80;
	else bgain += 0x30;
#else
	rgain |= 0x30;
	ggain |= 0x30;
	bgain |= 0x30;
#endif

	if (qcdebug&QC_DEBUGADAPTATION) PDEBUG("vv6450_set_gains hue=%d sat=%d val=%d  rgain=%02X(%02X) ggain=%02X(%02X) bgain=%02X(%02X)", hue, sat, val, rgain, sd->rgain, ggain, sd->ggain, bgain, sd->bgain);

	r = qc_i2c_wait(qc);
	if(r) goto fail;
	STV_SET_CHECK(0x0509, rgain);
	r = qc_i2c_wait(qc);
	if(r) goto fail;
	STV_SET_CHECK(0x050A, ggain);
	r = qc_i2c_wait(qc);
	if(r) goto fail;
	STV_SET_CHECK(0x050B, bgain);
	r = qc_i2c_wait(qc);
	if(r) goto fail;

#if 0
	// a stupid test...
	val = toggle_val[toggle++ % 4];
	STV_SET_CHECK(0x050C, val);  // 0, 15, 2A, 3F   have no idea about this
#else
	STV_SET_CHECK(0x050C, 0x2A);  // 0, 15, 2A, 3F   have no idea about this
#endif
	r = qc_i2c_wait(qc);
	if(r) goto fail;
	STV_SET_CHECK(0x050D, 0x01);
#if 1
        r = qc_i2c_wait(qc);
	if(r) goto fail;
	STV_SET_CHECK(0x143F, 0x01); // commit settings
#endif
	return r;
fail:
	if (qcdebug&QC_DEBUGADAPTATION) PDEBUG("vv6450_set_gains: error");
	return r;
}

/* {{{ [fold] vv6450_set_levels() */
static int vv6450_set_levels(struct quickcam *qc, unsigned int exp, unsigned int gain, unsigned int hue, unsigned int sat)
{
//set_levels(qc, qc->vpic.brightness, qc->vpic.contrast, qc->vpic.hue, qc->vpic.colour)
//set_target(qc, qc->vpic.brightness)
        int r = 0;

	if ((r = vv6450_set_exposure(qc, exp))<0) goto fail;
        r = vv6450_set_gains(qc, hue, sat, gain);
	if(r) goto fail;
	return r;
fail:
	PDEBUG("vv6450_set_levels: error");
	return r;
}
/* }}} */
/* {{{ [fold] vv6450_set_shutter: Set shutter value for sensor, val=0..65535 */
static int vv6450_set_shutter(struct quickcam *qc, unsigned int val)
{
	struct qc_sensor_data *sd = &qc->sensor_data;
	int r;
	int oldval;

	//PDEBUG("vv6450_set_shutter: val=%d", val);

	if (val == sd->shutter) return 0;
	oldval = sd->shutter;
	sd->shutter = val;
#define SHUTTER_SHIFT 8

	if ((val>>SHUTTER_SHIFT) == (oldval>>SHUTTER_SHIFT)) return 0;
	
	/* rescale the value if greater than 0xC000 */
	if(val > 0xC000) {
	  val = 0xC000 + (val&0x3FFF)*8;
	}
	/*
	  0xZZZZ -> 03 FF  (max value, but never used)
	  0xFFFF -> 02 B0  (extreme light and slow frame rate)
	  0xC100 -> 00 C8
	  0xC000 -> 00 C0
	  0x8000 -> 00 80  (normal value)
	  0x0000 -> 00 00  (extreme dark and fast frame rate)
	*/
	//PDEBUG("vv6450_set_shutter: val=%d (%04X)", val, (val >> SHUTTER_SHIFT));

        r = qc_i2c_wait(qc);
	if(r) goto fail;
	STV_SET_CHECK(0x143d, (val >> SHUTTER_SHIFT) & 0xff);
        r = qc_i2c_wait(qc);
	if(r) goto fail;
	STV_SET_CHECK(0x143e, (val >> (SHUTTER_SHIFT+8)) & 0x03);
	r = qc_i2c_wait(qc);
	if(r) goto fail;
	STV_SET_CHECK(0x143F, 0x01);
	return r;
fail:
	PDEBUG("error set_shutter");
	return r;
}
/* }}} */
/* {{{ [fold] vv6450_set_target: Set target brightness for sensor autoexposure, val=0..65535 */
static int vv6450_set_target(struct quickcam *qc, unsigned int val)
{
	int r = 0;

	//PDEBUG("vv6450_set_target: val=%d", val);

#ifdef WHITENESS_IS_SHUTTER
	if (val == qc->vpic.whiteness) return 0;
	qc->vpic.whiteness = val;
#else
	if (val == qc->vpic.brightness) return 0;
	qc->vpic.brightness = val;
#endif

	//PDEBUG("vv6450_set_target: call set_shutter (%d) %d", val, qc->settings.shutteradapt);

	if(!qc->settings.shutteradapt) {
	  r = vv6450_set_shutter(qc, val);
	}
	if(r) goto fail;
	return r;
fail:
	PDEBUG("error set_target");
	return r;
}
/* }}} */

const struct qc_sensor qc_sensor_vv6450 = {
	name:		"VV6450",
	manufacturer:	"ST Microelectronics",
	init:		vv6450_init,
	start:		vv6450_start,
	stop:		vv6450_stop,
	set_size:	vv6450_set_size,
	set_levels:	vv6450_set_levels,
	set_target:	vv6450_set_target,
	set_shutter:	vv6450_set_shutter,
	autoexposure:	FALSE,
#if 1
        adapt_gainlow:  40000,
        adapt_gainhigh: 65535,
#else
	adapt_gainlow:	1000,
        adapt_gainhigh: 65535,
#endif
	/* Information needed to access the sensor via I2C */
	reg23: 		-1, // not used
	i2c_addr: 	(unsigned char)-1, // not used
	/* Identification information used for auto-detection */
	id_reg:		VV6450_DEVICEH,
	/*
	  0x08F0 & 0x08F5 is known to be Messenger
	*/
	id:		0x08,
	length_id:	2,
};

