Index: gspca.h
===================================================================
--- gspca.h	(revision 2)
+++ gspca.h	(revision 5)
@@ -33,8 +33,8 @@
 #define SPCA50X_ENDPOINT_ADDRESS 1	/* Isoc endpoint number */
 #define PAC207_ENDPOINT_ADDRESS 5	/* Isoc endpoint number */
 /* only 2 or 4 frames are allowed here !!! */
-#define SPCA50X_NUMFRAMES 2
-#define SPCA50X_NUMSBUF 4	/* 2 is problematic on some systems */
+#define SPCA50X_NUMFRAMES 4
+#define SPCA50X_NUMSBUF 8	/* 2 is problematic on some systems */
 
 #define VENDOR_SONIX 0x0c45
 #define VENDOR_ETOMS 0x102c
@@ -152,7 +152,7 @@
 #define SPCA50X_I2C_STATUS 0x3
 /* Brightness autoadjustment parameters*/
 #define NSTABLE_MAX 4
-#define NUNSTABLE_MAX 600
+#define NUNSTABLE_MAX 200
 #define MIN_BRIGHTNESS 10
 /* Camera type jpeg yuvy yyuv yuyv grey gbrg*/
 enum {
@@ -426,6 +426,9 @@
 /* Statistics variables */
 	spinlock_t v4l_lock;	/* lock to protect shared data between isoc and process context */
 	int avg_lum;		//The average luminance (if available from theframe header)
+    __u8 lum_level; //Luminance level for brightness autoadjustment
+    uint nstable; // the stable condition counter
+    uint nunstable; // the unstable position counter
 	int avg_bg, avg_rg;	//The average B-G and R-G for white balancing 
 	struct semaphore lock;
 	int user;		/* user count for exclusive use */
Index: gspca_core.c
===================================================================
--- gspca_core.c	(revision 2)
+++ gspca_core.c	(revision 5)
@@ -97,6 +97,8 @@
 #endif
 /* Light frequency banding filter 50HZ/60HZ/NoFliker */
 static int lightfreq = 50;
+/* Parameter defines the average luminance that should be kept */
+static int lum_level = 0x20;
 #ifdef GSPCA_ENABLE_REGISTERPLAY
 static int RegAddress = 0;
 static int RegValue = 0;
@@ -117,6 +119,7 @@
 #endif
 module_param(usbgrabber, int, 0444);
 module_param(lightfreq, int, 0644);
+module_param(lum_level, int, 0664);
 #ifdef GSPCA_ENABLE_REGISTERPLAY
 module_param(RegAddress, int, 0644);
 module_param(RegValue, int, 0644);
@@ -139,6 +142,8 @@
 #endif				/* SPCA50X_ENABLE_COMPRESSION */
 MODULE_PARM_DESC(usbgrabber, "Is a usb grabber 0x0733:0x0430 ? (default 1) ");
 MODULE_PARM_DESC(lightfreq,  "Light frequency banding filter. Set to 50 or 60 Hz, or zero to NoFliker (default=50)");
+MODULE_PARM_DESC(lum_level, 
+	"Luminance level for brightness autoadjustment (default 32)");
 #ifdef GSPCA_ENABLE_REGISTERPLAY
 MODULE_PARM_DESC(RegAddress, "Register Address of PAC207");
 MODULE_PARM_DESC(RegValue, "Register Value for PAC207");
@@ -1776,6 +1781,7 @@
 	int i;
 /* configure the frame detector with default parameters */
 	memset(&spca50x->pictsetting, 0, sizeof (struct pictparam));
+	spca50x->lum_level = lum_level;
 	spca50x->pictsetting.change = 0x01;
 	spca50x->pictsetting.force_rgb = force_rgb;
 	spca50x->pictsetting.gamma = gamma;
@@ -2141,7 +2147,7 @@
 			spca50x->funct.set_contrast(spca50x);
 			spca50x->funct.set_colors(spca50x);
 			spca50x->hue = p->hue;
-			spca50x->whiteness = p->whiteness;
+////			spca50x->whiteness = p->whiteness;
 			return 0;
 		}
 	case VIDIOCGCAPTURE:
@@ -2640,10 +2646,11 @@
 	struct usb_spca50x *spca50x = cd_to_spca50x(cd);
 	struct pictparam *gcorrect = &spca50x->pictsetting;
 	return snprintf(buf, 128,
-			"force_rgb=%d, gamma=%d, OffRed=%d, OffBlue=%d, OffGreen=%d, GRed=%d, GBlue=%d, GGreen= %d \n",
+			"force_rgb=%d, gamma=%d, OffRed=%d, OffBlue=%d, OffGreen=%d, GRed=%d, GBlue=%d, GGreen= %d \nwhiteness= %d, brightness= %d\n",
 			gcorrect->force_rgb, gcorrect->gamma, gcorrect->OffRed,
 			gcorrect->OffBlue, gcorrect->OffGreen, gcorrect->GRed,
-			gcorrect->GBlue, gcorrect->GGreen);
+			gcorrect->GBlue, gcorrect->GGreen,
+			spca50x->whiteness, spca50x->brightness >> 8);
 }
 
 static CLASS_DEVICE_ATTR(pictsetting, S_IRUGO, show_pictsetting, NULL);
Index: Sunplus/spca501.dat
===================================================================
--- Sunplus/spca501.dat	(revision 2)
+++ Sunplus/spca501.dat	(revision 5)
@@ -31,15 +31,15 @@
     {0x0, 0x02, 0x0F},		//A32
     {0x0, 0x00, 0x10},		//A33
 #else
-    {0x1, 0x2a, 0x08},		//A11 0x31
-    {0x1, 0xf8, 0x09},		//A12 f8
-    {0x1, 0xf8, 0x0A},		//A13 f8
-    {0x1, 0xf8, 0x0B},		//A21 f8
-    {0x1, 0x14, 0x0C},		//A22 0x14
-    {0x1, 0xf8, 0x0D},		//A23 f8
-    {0x1, 0xf8, 0x0E},		//A31 f8
-    {0x1, 0xf8, 0x0F},		//A32 f8
-    {0x1, 0x20, 0x10},		//A33 0x20
+	{ 0x1, 0x31, 0x08 }, //A11
+	{ 0x1, 0x00, 0x09 }, //A12
+	{ 0x1, 0x00, 0x0A }, //A13
+	{ 0x1, 0x00, 0x0B }, //A21
+	{ 0x1, 0x14, 0x0C }, //A22
+	{ 0x1, 0x00, 0x0D }, //A23
+	{ 0x1, 0x00, 0x0E }, //A31
+	{ 0x1, 0x00, 0x0F }, //A32
+	{ 0x1, 0x1E, 0x10 }, //A33
 #endif
     {0x1, 0x00, 0x11},		// R offset
     {0x1, 0x00, 0x12},		// G offset
Index: Sunplus/spca501_init.h
===================================================================
--- Sunplus/spca501_init.h	(revision 2)
+++ Sunplus/spca501_init.h	(revision 5)
@@ -71,6 +71,7 @@
 static void spca501_setAutobright(struct usb_spca50x *spca50x){}
 static void spca501_setquality(struct usb_spca50x *spca50x){}
 /*****************************************************************/
+static void spca501_bottom_half(struct usb_spca50x* spca50x);
 static struct cam_operation fspca501 = {
  	.initialize = spca501_init,
 	.configure = spca501_config,
@@ -87,21 +88,31 @@
 	.set_quality = spca501_setquality,
 	.cam_shutdown = spca501_shutdown,
 	.sof_detect = spca501_sofdetect,
+	.set_autobright = spca501_bottom_half,
  };
+
 static void spca501_shutdown(struct usb_spca50x *spca50x)
 {
 // This maybe reset or power control
 	    spca50x_reg_write(spca50x->dev, SPCA501_REG_CTLRL, 0x5, 0x0);
 }
+
 static __u16 spca501_getbrightness(struct usb_spca50x *spca50x)
 {
 __u16 brightness;
- brightness =
-		spca50x_reg_read(spca50x->dev,SPCA501_REG_CCDSP , 0x11, 2) & 0xFF;
-	    brightness <<= 8;
-	    spca50x->brightness = brightness;
+	brightness = spca50x_reg_read(spca50x->dev, 0x0, 0x00, 2) & 0xFF;
+	brightness -= 125;
+	brightness <<= 1;
+	spca50x->brightness = brightness << 8;
   return spca50x->brightness;
 }
+static void spca501_setbrightness(struct usb_spca50x *spca50x)
+{
+	__u8 brightness = spca50x->brightness >> 8;
+	spca50x_reg_write(spca50x->dev, 0x0, 0x00, 
+			  (brightness >> 1) + 125);
+}
+
 static __u16 spca501_getcontrast(struct usb_spca50x *spca50x)
 {
 #if 0
@@ -116,18 +127,10 @@
 						     1) & 0xFF);
 #endif
 spca50x->contrast = 0xaa01;						     
-PDEBUG(0, "SPCA501 Getcontrast %d",spca50x->contrast);
+	PDEBUG(1, "SPCA501 Getcontrast %d",spca50x->contrast);
 return spca50x->contrast;
 }
-static void spca501_setbrightness(struct usb_spca50x *spca50x)
-{
-	spca50x_reg_write(spca50x->dev, SPCA501_REG_CCDSP, 0x11,
-			      spca50x->brightness >> 9);
-	spca50x_reg_write(spca50x->dev, SPCA501_REG_CCDSP, 0x12,
-			      spca50x->brightness >> 9);
-	spca50x_reg_write(spca50x->dev, SPCA501_REG_CCDSP, 0x13,
-			      spca50x->brightness >> 9);
-}
+
 static void spca501_setcontrast(struct usb_spca50x *spca50x)
 {
 spca50x_reg_write(spca50x->dev,0x00, 0x00,
@@ -135,6 +138,133 @@
 spca50x_reg_write(spca50x->dev,0x00, 0x01,
 			      spca50x->contrast & 0xff);			      
 }
+
+
+/*
+ * Function reads the "whiteness parameter" from the camera
+ * Actually "whiteness" is a parameter that close to brighness
+ * but currently ununderstanded
+ * Receives the pointer to the description structure
+ * returns the value of whiteness
+ */
+static inline __u16 spca501_getwhiteness(struct usb_spca50x *spca50x)
+{
+	__u16 whiteness = 0; //the "whiteness" parameter
+	__u16 br = 0; //the brightness value
+	int i;   // just an index variable
+	__u16 mask; //the bit mask to set bit in p.contrast
+	__u8 br_table[] = { 128, 64, 32, 16, 0 }; //the brightness table
+
+	/* Getting "whiteness" (strange parameters in TG 0x0D */
+	br = spca50x_reg_read(spca50x->dev, 0x0, 0x0d, 2);
+	for (i = 0, mask = 1; br_table[i]; i++, mask <<= 1)
+	{
+		if (br & br_table[i])
+			whiteness |= mask;
+	}
+	return whiteness;
+}
+
+/*
+ * Function sets the "whiteness parameter" from the camera
+ * Actually "whiteness" is a parameter that close to brighness
+ * but currently ununderstanded
+ * Receives the pointer to the description structure 
+ * and whiteness value
+ */
+static inline void spca501_setwhiteness(struct usb_spca50x *spca50x, 
+					__u8 whiteness)
+{
+	__u8 br_out = 0; // contrast output value
+	int i;   // just an index variable
+	__u16 mask; //the bit mask to set bit in contrast
+	__u8 br_table[] = { 128, 64, 32, 16, 0 }; //the brightness table
+
+
+	PDEBUG(3, "Setting whiteness %d", whiteness);
+	for (i = 0, mask = 1; br_table[i]; i++, mask <<= 1)
+	{
+		if (whiteness & mask)
+			br_out |= br_table[i];
+	}
+	PDEBUG(3, "Writing whiteness %d", br_out);
+	spca50x_reg_write(spca50x->dev, 0x0, 0x0d, br_out);
+}
+
+/*
+ * Get average luminance
+ */
+static inline __u8 get_avg_lum(struct usb_spca50x *spca50x)
+{
+	__u8 luminance; //The average luminance from camera
+	switch(spca50x->bridge)
+	{
+	case BRIDGE_SPCA501:
+		luminance = spca50x_reg_read(spca50x->dev, SPCA501_REG_CCDSP, 
+					     0x26, 2) >> 8;
+		break;
+	default:
+		luminance = 0;
+		break;
+	}
+	return luminance;
+}
+
+
+/*
+ * Get average R-G
+ */
+static inline __u8 get_avg_RG(struct usb_spca50x *spca50x)
+{
+	return spca50x_reg_read(spca50x->dev, SPCA501_REG_CCDSP, 
+				0x30, 2) >> 8;
+}
+
+
+/*
+ * Get average B-G
+ */
+static inline __u8 get_avg_BG(struct usb_spca50x *spca50x)
+{
+	return spca50x_reg_read(spca50x->dev, SPCA501_REG_CCDSP, 
+				0x2f, 2) >> 8;
+}
+
+
+/*
+ * Get red offset for white balance
+ */
+static inline __u8 get_red_off(struct usb_spca50x *spca50x)
+{
+	return spca50x_reg_read(spca50x->dev, SPCA501_REG_CCDSP, 
+				0x13, 2) & 0xFF;
+}
+
+/*
+ * Get blue offset for white balance
+ */
+static inline __u8 get_blue_off(struct usb_spca50x *spca50x)
+{
+	return spca50x_reg_read(spca50x->dev, SPCA501_REG_CCDSP, 
+				0x11, 2) & 0xFF;
+}
+
+/*
+ * Set red offset for white balance
+ */
+static inline void set_red_off(struct usb_spca50x *spca50x, __u8 off)
+{
+	spca50x_reg_write(spca50x->dev, SPCA501_REG_CCDSP, 0x13, off);
+}
+
+/*
+ * Set blue offset for white balance
+ */
+static inline void set_blue_off(struct usb_spca50x *spca50x, __u8 off)
+{
+	spca50x_reg_write(spca50x->dev, SPCA501_REG_CCDSP, 0x11, off);
+}
+
 static int spca501_init(struct usb_spca50x *spca50x)
 {
 	    PDEBUG(2, "Initializing SPCA501 started");
@@ -154,17 +284,19 @@
 		/* Generic 501 open data */
 		spca50x_write_vector(spca50x, spca501_open_data);
 	    }
-#ifdef SPCA50X_ENABLE_EXPERIMENTAL
-	    spca50x->a_blue = spca50x_reg_read(spca50x->dev,
+	spca501_getbrightness(spca50x);
+	spca501_getcontrast(spca50x);
+	spca50x->whiteness = 0;
+	spca501_setwhiteness(spca50x, spca50x->whiteness);
+	spca50x->pictsetting.OffBlue = spca50x_reg_read(spca50x->dev,
 					       SPCA501_REG_CCDSP,
 					       SPCA501_A11, 2) & 0xFF;
-	    spca50x->a_green =
+	spca50x->pictsetting.OffGreen =
 		spca50x_reg_read(spca50x->dev, SPCA501_REG_CCDSP,
 				 SPCA501_A21, 2) & 0xFF;
-	    spca50x->a_red =
+	spca50x->pictsetting.OffRed =
 		spca50x_reg_read(spca50x->dev, SPCA501_REG_CCDSP,
 				 SPCA501_A31, 2) & 0xFF;
-#endif				/* SPCA50X_ENABLE_EXPERIMENTAL */
 	    PDEBUG(2, "Initializing SPCA501 finished");
 return 0;
 }
@@ -194,7 +326,8 @@
 }
 static void spca501_stopN(struct usb_spca50x *spca50x)
 {
-	    /* Disable ISO packet machine CTRL reg=2, index=1 bitmask=0x0 (bit ordinal 1) */
+	/* Disable ISO packet machine CTRL reg=2, i
+	   ndex=1 bitmask=0x0 (bit ordinal 1) */
 	    spca50x_reg_write(spca50x->dev, SPCA501_REG_CTLRL,
 			      (__u16) 0x1, (__u16) 0x0);
 }
@@ -288,7 +421,8 @@
              spca50x_reg_write(spca50x->dev,SPCA501_REG_CCDSP, 0x0c,
 			           spca50x->colour >> 10);
 }
-static int spca501_sofdetect(struct usb_spca50x *spca50x,struct spca50x_frame *frame, unsigned char *cdata,int *iPix, int seqnum,int *datalength)
+static int spca501_sofdetect(struct usb_spca50x *spca50x,struct spca50x_frame *frame, 
+			     unsigned char *cdata,int *iPix, int seqnum,int *datalength)
 {
 	switch (cdata[0]){
 	   case 0:
@@ -303,7 +437,143 @@
 	   	return seqnum+1 ;
 	   }
 }
+/*
+ * Function tries to control brightness
+ * Receives the pointer to camera descriptor and 
+ * target average luminance
+ * param: spca50x structure that describes the camera device
+ */
+static void spca501_bottom_half(struct usb_spca50x* spca50x)
+{
+	__u8 cur_lum = 0; //The current luminance
+	__s16 brightness = 0;
+	char br_set = 0; //If the brightness should be set
+	__u8 lum = spca50x->lum_level; //The desired luminance level
+	__u8 delta = lum >> 3; //overshooting that we shouldn't care
+	__u8 whiteness = 0; //whiteness level
+	char wh_set = 0; //If whiteness must be set
+	__s8 rg; // red/green balance
+	__s8 rg_off; // red/green offset
 
+	__s8 bg; // red/green balance
+	__s8 bg_off; // red/green offset
+
+	if (in_interrupt())
+	{
+		PDEBUG(2, "In intr");
+		return;
+	}
+	spca50x->lum_level = lum_level;
+	/*
+	 * This algorithm may be dangerous. We MUST be sure that the 
+	 * !spca50x->streaming will be set to 0 BEFORE kfree(spca50x)
+	 */
+	PDEBUG(2, "BH enter");
+	if (!spca50x->streaming) //No isoc
+		return;
+	if (spca50x_reg_read(spca50x->dev, SPCA501_REG_CCDSP, 0x26, 2) < 0)
+	{
+		PDEBUG(2, "BH error");
+		return;
+	}
+	
+	cur_lum = get_avg_lum(spca50x);
+	brightness = spca50x->brightness >> 8;
+	PDEBUG(2, "BH half, lum = %d, brightness=%d, whiteness=%d, nstable=%d, nunstable=%d",
+	       cur_lum, brightness, spca50x->whiteness, spca50x->nstable, spca50x->nunstable);
+	if (cur_lum < (lum - delta))
+	{
+		if (brightness < 255)
+		{
+			if (lum - cur_lum > delta * 2)
+				brightness += 10;
+			else
+				brightness++;
+			br_set = 1;
+		}
+		if (brightness >= 255) 
+		{
+			whiteness = spca50x->whiteness;
+			if (whiteness < 15)
+			{   
+				whiteness++;
+				wh_set = 1;
+			}
+		}
+	}
+	if (cur_lum > (lum + delta) && brightness >= 0)
+	{
+		if (cur_lum - lum > delta * 2)
+			brightness -= 10;
+		else
+			brightness--;
+		if (brightness < 0)
+			brightness = 0;
+		br_set = 1;
+	}
+	if ((spca50x->nunstable == NUNSTABLE_MAX && 
+	    brightness < MIN_BRIGHTNESS) || brightness < 1)
+	{
+		whiteness = spca50x->whiteness;
+		if (whiteness > 0)
+		{   
+			whiteness--;
+			wh_set = 1;
+		}
+	}        
+
+	if (wh_set)
+	{
+		spca50x->whiteness = whiteness;
+		spca501_setwhiteness(spca50x, whiteness);
+		spca50x->nstable = 0;
+		spca50x->nunstable = 0;
+		spca50x->brightness = 128 << 8;
+		return; //if we have set whiteness, we needn't care about brightness
+	}
+	if (br_set)
+	{
+		spca50x->brightness = brightness << 8;
+		spca501_setbrightness(spca50x);
+		if (spca50x->nunstable < NUNSTABLE_MAX)
+			spca50x->nunstable++;
+		spca50x->nstable = 0;
+	}
+	else
+	{
+		if (spca50x->nstable < NSTABLE_MAX)
+			spca50x->nstable++;
+		else
+			spca50x->nunstable = 0;
+	}
+	rg = (__s8)get_avg_RG(spca50x);
+	if (rg >= 2 || rg <= -2)
+	{
+		rg_off = (__u8)((spca50x->pictsetting.OffRed >> 8) & 0xFF);
+		if (rg > 0)
+			rg_off--;
+		else
+			rg_off++;
+		set_red_off(spca50x, (__u8)rg_off);
+		spca50x->pictsetting.OffRed = rg_off;
+		spca50x->pictsetting.OffRed <<= 8;
+	}
+
+	bg = (__s8)get_avg_BG(spca50x);
+	if (bg >= 2 || bg <= -2)
+	{
+		bg_off = (__u8)((spca50x->pictsetting.OffBlue >> 8) & 0xFF);
+		if (bg > 0)
+			bg_off--;
+		else
+			bg_off++;
+		set_blue_off(spca50x, (__u8)bg_off);
+		spca50x->pictsetting.OffBlue = bg_off;
+		spca50x->pictsetting.OffBlue <<= 8;
+	}
+	PDEBUG(2, "BH exit");
+}
+
 			/* SPCA501_INIT_H */
 #endif
 //eof
