This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / drivers / media / video / pwc / pwc-dec23.c
diff --git a/drivers/media/video/pwc/pwc-dec23.c b/drivers/media/video/pwc/pwc-dec23.c
new file mode 100644 (file)
index 0000000..9e2d91f
--- /dev/null
@@ -0,0 +1,941 @@
+/* Linux driver for Philips webcam
+   Decompression for chipset version 2 et 3
+   (C) 2004-2006  Luc Saillard (luc@saillard.org)
+
+   NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
+   driver and thus may have bugs that are not present in the original version.
+   Please send bug reports and support requests to <luc@saillard.org>.
+   The decompression routines have been implemented by reverse-engineering the
+   Nemosoft binary pwcx module. Caveat emptor.
+
+   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 "pwc-timon.h"
+#include "pwc-kiara.h"
+#include "pwc-dec23.h"
+#include <media/pwc-ioctl.h>
+
+#include <linux/string.h>
+
+/*
+ * USE_LOOKUP_TABLE_TO_CLAMP
+ *   0: use a C version of this tests:  {  a<0?0:(a>255?255:a) }
+ *   1: use a faster lookup table for cpu with a big cache (intel)
+ */
+#define USE_LOOKUP_TABLE_TO_CLAMP      1
+/*
+ * UNROLL_LOOP_FOR_COPYING_BLOCK
+ *   0: use a loop for a smaller code (but little slower)
+ *   1: when unrolling the loop, gcc produces some faster code (perhaps only
+ *   valid for intel processor class). Activating this option, automaticaly
+ *   activate USE_LOOKUP_TABLE_TO_CLAMP
+ */
+#define UNROLL_LOOP_FOR_COPY           1
+#if UNROLL_LOOP_FOR_COPY
+# undef USE_LOOKUP_TABLE_TO_CLAMP
+# define USE_LOOKUP_TABLE_TO_CLAMP 1
+#endif
+
+/*
+ * ENABLE_BAYER_DECODER
+ *   0: bayer decoder is not build (save some space)
+ *   1: bayer decoder is build and can be used
+ */
+#define ENABLE_BAYER_DECODER 0
+
+static void build_subblock_pattern(struct pwc_dec23_private *pdec)
+{
+       static const unsigned int initial_values[12] = {
+               -0x526500, -0x221200, 0x221200, 0x526500,
+                          -0x3de200, 0x3de200,
+               -0x6db480, -0x2d5d00, 0x2d5d00, 0x6db480,
+                          -0x12c200, 0x12c200
+
+       };
+       static const unsigned int values_derivated[12] = {
+               0xa4ca, 0x4424, -0x4424, -0xa4ca,
+                       0x7bc4, -0x7bc4,
+               0xdb69, 0x5aba, -0x5aba, -0xdb69,
+                       0x2584, -0x2584
+       };
+       unsigned int temp_values[12];
+       int i, j;
+
+       memcpy(temp_values, initial_values, sizeof(initial_values));
+       for (i = 0; i < 256; i++) {
+               for (j = 0; j < 12; j++) {
+                       pdec->table_subblock[i][j] = temp_values[j];
+                       temp_values[j] += values_derivated[j];
+               }
+       }
+}
+
+static void build_bit_powermask_table(struct pwc_dec23_private *pdec)
+{
+       unsigned char *p;
+       unsigned int bit, byte, mask, val;
+       unsigned int bitpower = 1;
+
+       for (bit = 0; bit < 8; bit++) {
+               mask = bitpower - 1;
+               p = pdec->table_bitpowermask[bit];
+               for (byte = 0; byte < 256; byte++) {
+                       val = (byte & mask);
+                       if (byte & bitpower)
+                               val = -val;
+                       *p++ = val;
+               }
+               bitpower<<=1;
+       }
+}
+
+
+static void build_table_color(const unsigned int romtable[16][8],
+                             unsigned char p0004[16][1024],
+                             unsigned char p8004[16][256])
+{
+       int compression_mode, j, k, bit, pw;
+       unsigned char *p0, *p8;
+       const unsigned int *r;
+
+       /* We have 16 compressions tables */
+       for (compression_mode = 0; compression_mode < 16; compression_mode++) {
+               p0 = p0004[compression_mode];
+               p8 = p8004[compression_mode];
+               r  = romtable[compression_mode];
+
+               for (j = 0; j < 8; j++, r++, p0 += 128) {
+
+                       for (k = 0; k < 16; k++) {
+                               if (k == 0)
+                                       bit = 1;
+                               else if (k >= 1 && k < 3)
+                                       bit = (r[0] >> 15) & 7;
+                               else if (k >= 3 && k < 6)
+                                       bit = (r[0] >> 12) & 7;
+                               else if (k >= 6 && k < 10)
+                                       bit = (r[0] >> 9) & 7;
+                               else if (k >= 10 && k < 13)
+                                       bit = (r[0] >> 6) & 7;
+                               else if (k >= 13 && k < 15)
+                                       bit = (r[0] >> 3) & 7;
+                               else
+                                       bit = (r[0]) & 7;
+                               if (k == 0)
+                                       *p8++ = 8;
+                               else
+                                       *p8++ = j - bit;
+                               *p8++ = bit;
+
+                               pw = 1 << bit;
+                               p0[k + 0x00] = (1 * pw) + 0x80;
+                               p0[k + 0x10] = (2 * pw) + 0x80;
+                               p0[k + 0x20] = (3 * pw) + 0x80;
+                               p0[k + 0x30] = (4 * pw) + 0x80;
+                               p0[k + 0x40] = (-1 * pw) + 0x80;
+                               p0[k + 0x50] = (-2 * pw) + 0x80;
+                               p0[k + 0x60] = (-3 * pw) + 0x80;
+                               p0[k + 0x70] = (-4 * pw) + 0x80;
+                       }       /* end of for (k=0; k<16; k++, p8++) */
+               }       /* end of for (j=0; j<8; j++ , table++) */
+       } /* end of foreach compression_mode */
+}
+
+/*
+ *
+ */
+static void fill_table_dc00_d800(struct pwc_dec23_private *pdec)
+{
+#define SCALEBITS 15
+#define ONE_HALF  (1UL << (SCALEBITS - 1))
+       int i;
+       unsigned int offset1 = ONE_HALF;
+       unsigned int offset2 = 0x0000;
+
+       for (i=0; i<256; i++) {
+               pdec->table_dc00[i] = offset1 & ~(ONE_HALF);
+               pdec->table_d800[i] = offset2;
+
+               offset1 += 0x7bc4;
+               offset2 += 0x7bc4;
+       }
+}
+
+/*
+ * To decode the stream:
+ *   if look_bits(2) == 0:     # op == 2 in the lookup table
+ *      skip_bits(2)
+ *      end of the stream
+ *   elif look_bits(3) == 7:   # op == 1 in the lookup table
+ *      skip_bits(3)
+ *      yyyy = get_bits(4)
+ *      xxxx = get_bits(8)
+ *   else:                     # op == 0 in the lookup table
+ *      skip_bits(x)
+ *
+ * For speedup processing, we build a lookup table and we takes the first 6 bits.
+ *
+ * struct {
+ *   unsigned char op;     // operation to execute
+ *   unsigned char bits;    // bits use to perform operation
+ *   unsigned char offset1; // offset to add to access in the table_0004 % 16
+ *   unsigned char offset2; // offset to add to access in the table_0004
+ * }
+ *
+ * How to build this table ?
+ *   op == 2 when (i%4)==0
+ *   op == 1 when (i%8)==7
+ *   op == 0 otherwise
+ *
+ */
+static const unsigned char hash_table_ops[64*4] = {
+       0x02, 0x00, 0x00, 0x00,
+       0x00, 0x03, 0x01, 0x00,
+       0x00, 0x04, 0x01, 0x10,
+       0x00, 0x06, 0x01, 0x30,
+       0x02, 0x00, 0x00, 0x00,
+       0x00, 0x03, 0x01, 0x40,
+       0x00, 0x05, 0x01, 0x20,
+       0x01, 0x00, 0x00, 0x00,
+       0x02, 0x00, 0x00, 0x00,
+       0x00, 0x03, 0x01, 0x00,
+       0x00, 0x04, 0x01, 0x50,
+       0x00, 0x05, 0x02, 0x00,
+       0x02, 0x00, 0x00, 0x00,
+       0x00, 0x03, 0x01, 0x40,
+       0x00, 0x05, 0x03, 0x00,
+       0x01, 0x00, 0x00, 0x00,
+       0x02, 0x00, 0x00, 0x00,
+       0x00, 0x03, 0x01, 0x00,
+       0x00, 0x04, 0x01, 0x10,
+       0x00, 0x06, 0x02, 0x10,
+       0x02, 0x00, 0x00, 0x00,
+       0x00, 0x03, 0x01, 0x40,
+       0x00, 0x05, 0x01, 0x60,
+       0x01, 0x00, 0x00, 0x00,
+       0x02, 0x00, 0x00, 0x00,
+       0x00, 0x03, 0x01, 0x00,
+       0x00, 0x04, 0x01, 0x50,
+       0x00, 0x05, 0x02, 0x40,
+       0x02, 0x00, 0x00, 0x00,
+       0x00, 0x03, 0x01, 0x40,
+       0x00, 0x05, 0x03, 0x40,
+       0x01, 0x00, 0x00, 0x00,
+       0x02, 0x00, 0x00, 0x00,
+       0x00, 0x03, 0x01, 0x00,
+       0x00, 0x04, 0x01, 0x10,
+       0x00, 0x06, 0x01, 0x70,
+       0x02, 0x00, 0x00, 0x00,
+       0x00, 0x03, 0x01, 0x40,
+       0x00, 0x05, 0x01, 0x20,
+       0x01, 0x00, 0x00, 0x00,
+       0x02, 0x00, 0x00, 0x00,
+       0x00, 0x03, 0x01, 0x00,
+       0x00, 0x04, 0x01, 0x50,
+       0x00, 0x05, 0x02, 0x00,
+       0x02, 0x00, 0x00, 0x00,
+       0x00, 0x03, 0x01, 0x40,
+       0x00, 0x05, 0x03, 0x00,
+       0x01, 0x00, 0x00, 0x00,
+       0x02, 0x00, 0x00, 0x00,
+       0x00, 0x03, 0x01, 0x00,
+       0x00, 0x04, 0x01, 0x10,
+       0x00, 0x06, 0x02, 0x50,
+       0x02, 0x00, 0x00, 0x00,
+       0x00, 0x03, 0x01, 0x40,
+       0x00, 0x05, 0x01, 0x60,
+       0x01, 0x00, 0x00, 0x00,
+       0x02, 0x00, 0x00, 0x00,
+       0x00, 0x03, 0x01, 0x00,
+       0x00, 0x04, 0x01, 0x50,
+       0x00, 0x05, 0x02, 0x40,
+       0x02, 0x00, 0x00, 0x00,
+       0x00, 0x03, 0x01, 0x40,
+       0x00, 0x05, 0x03, 0x40,
+       0x01, 0x00, 0x00, 0x00
+};
+
+/*
+ *
+ */
+static const unsigned int MulIdx[16][16] = {
+       {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,},
+       {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3,},
+       {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3,},
+       {4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4,},
+       {6, 7, 8, 9, 7, 10, 11, 8, 8, 11, 10, 7, 9, 8, 7, 6,},
+       {4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 4,},
+       {1, 3, 0, 2, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3, 0, 2,},
+       {0, 3, 3, 0, 1, 2, 2, 1, 2, 1, 1, 2, 3, 0, 0, 3,},
+       {0, 1, 2, 3, 3, 2, 1, 0, 3, 2, 1, 0, 0, 1, 2, 3,},
+       {1, 1, 1, 1, 3, 3, 3, 3, 0, 0, 0, 0, 2, 2, 2, 2,},
+       {7, 10, 11, 8, 9, 8, 7, 6, 6, 7, 8, 9, 8, 11, 10, 7,},
+       {4, 5, 5, 4, 5, 4, 4, 5, 5, 4, 4, 5, 4, 5, 5, 4,},
+       {7, 9, 6, 8, 10, 8, 7, 11, 11, 7, 8, 10, 8, 6, 9, 7,},
+       {1, 3, 0, 2, 2, 0, 3, 1, 2, 0, 3, 1, 1, 3, 0, 2,},
+       {1, 2, 2, 1, 3, 0, 0, 3, 0, 3, 3, 0, 2, 1, 1, 2,},
+       {10, 8, 7, 11, 8, 6, 9, 7, 7, 9, 6, 8, 11, 7, 8, 10}
+};
+
+#if USE_LOOKUP_TABLE_TO_CLAMP
+#define MAX_OUTER_CROP_VALUE   (512)
+static unsigned char pwc_crop_table[256 + 2*MAX_OUTER_CROP_VALUE];
+#define CLAMP(x) (pwc_crop_table[MAX_OUTER_CROP_VALUE+(x)])
+#else
+#define CLAMP(x) ((x)>255?255:((x)<0?0:x))
+#endif
+
+
+/* If the type or the command change, we rebuild the lookup table */
+int pwc_dec23_init(struct pwc_device *pwc, int type, unsigned char *cmd)
+{
+       int flags, version, shift, i;
+       struct pwc_dec23_private *pdec;
+
+       if (pwc->decompress_data == NULL) {
+               pdec = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL);
+               if (pdec == NULL)
+                       return -ENOMEM;
+               pwc->decompress_data = pdec;
+       }
+       pdec = pwc->decompress_data;
+
+       if (DEVICE_USE_CODEC3(type)) {
+               flags = cmd[2] & 0x18;
+               if (flags == 8)
+                       pdec->nbits = 7;        /* More bits, mean more bits to encode the stream, but better quality */
+               else if (flags == 0x10)
+                       pdec->nbits = 8;
+               else
+                       pdec->nbits = 6;
+
+               version = cmd[2] >> 5;
+               build_table_color(KiaraRomTable[version][0], pdec->table_0004_pass1, pdec->table_8004_pass1);
+               build_table_color(KiaraRomTable[version][1], pdec->table_0004_pass2, pdec->table_8004_pass2);
+
+       } else {
+
+               flags = cmd[2] & 6;
+               if (flags == 2)
+                       pdec->nbits = 7;
+               else if (flags == 4)
+                       pdec->nbits = 8;
+               else
+                       pdec->nbits = 6;
+
+               version = cmd[2] >> 3;
+               build_table_color(TimonRomTable[version][0], pdec->table_0004_pass1, pdec->table_8004_pass1);
+               build_table_color(TimonRomTable[version][1], pdec->table_0004_pass2, pdec->table_8004_pass2);
+       }
+
+       /* Informations can be coded on a variable number of bits but never less than 8 */
+       shift = 8 - pdec->nbits;
+       pdec->scalebits = SCALEBITS - shift;
+       pdec->nbitsmask = 0xFF >> shift;
+
+       fill_table_dc00_d800(pdec);
+       build_subblock_pattern(pdec);
+       build_bit_powermask_table(pdec);
+
+#if USE_LOOKUP_TABLE_TO_CLAMP
+       /* Build the static table to clamp value [0-255] */
+       for (i=0;i<MAX_OUTER_CROP_VALUE;i++)
+               pwc_crop_table[i] = 0;
+       for (i=0; i<256; i++)
+               pwc_crop_table[MAX_OUTER_CROP_VALUE+i] = i;
+       for (i=0; i<MAX_OUTER_CROP_VALUE; i++)
+               pwc_crop_table[MAX_OUTER_CROP_VALUE+256+i] = 255;
+#endif
+
+       return 0;
+}
+
+/*
+ * Copy the 4x4 image block to Y plane buffer
+ */
+static void copy_image_block_Y(const int *src, unsigned char *dst, unsigned int bytes_per_line, unsigned int scalebits)
+{
+#if UNROLL_LOOP_FOR_COPY
+       const unsigned char *cm = pwc_crop_table+MAX_OUTER_CROP_VALUE;
+       const int *c = src;
+       unsigned char *d = dst;
+
+       *d++ = cm[c[0] >> scalebits];
+       *d++ = cm[c[1] >> scalebits];
+       *d++ = cm[c[2] >> scalebits];
+       *d++ = cm[c[3] >> scalebits];
+
+       d = dst + bytes_per_line;
+       *d++ = cm[c[4] >> scalebits];
+       *d++ = cm[c[5] >> scalebits];
+       *d++ = cm[c[6] >> scalebits];
+       *d++ = cm[c[7] >> scalebits];
+
+       d = dst + bytes_per_line*2;
+       *d++ = cm[c[8] >> scalebits];
+       *d++ = cm[c[9] >> scalebits];
+       *d++ = cm[c[10] >> scalebits];
+       *d++ = cm[c[11] >> scalebits];
+
+       d = dst + bytes_per_line*3;
+       *d++ = cm[c[12] >> scalebits];
+       *d++ = cm[c[13] >> scalebits];
+       *d++ = cm[c[14] >> scalebits];
+       *d++ = cm[c[15] >> scalebits];
+#else
+       int i;
+       const int *c = src;
+       unsigned char *d = dst;
+       for (i = 0; i < 4; i++, c++)
+               *d++ = CLAMP((*c) >> scalebits);
+
+       d = dst + bytes_per_line;
+       for (i = 0; i < 4; i++, c++)
+               *d++ = CLAMP((*c) >> scalebits);
+
+       d = dst + bytes_per_line*2;
+       for (i = 0; i < 4; i++, c++)
+               *d++ = CLAMP((*c) >> scalebits);
+
+       d = dst + bytes_per_line*3;
+       for (i = 0; i < 4; i++, c++)
+               *d++ = CLAMP((*c) >> scalebits);
+#endif
+}
+
+/*
+ * Copy the 4x4 image block to a CrCb plane buffer
+ *
+ */
+static void copy_image_block_CrCb(const int *src, unsigned char *dst, unsigned int bytes_per_line, unsigned int scalebits)
+{
+#if UNROLL_LOOP_FOR_COPY
+       /* Unroll all loops */
+       const unsigned char *cm = pwc_crop_table+MAX_OUTER_CROP_VALUE;
+       const int *c = src;
+       unsigned char *d = dst;
+
+       *d++ = cm[c[0] >> scalebits];
+       *d++ = cm[c[4] >> scalebits];
+       *d++ = cm[c[1] >> scalebits];
+       *d++ = cm[c[5] >> scalebits];
+       *d++ = cm[c[2] >> scalebits];
+       *d++ = cm[c[6] >> scalebits];
+       *d++ = cm[c[3] >> scalebits];
+       *d++ = cm[c[7] >> scalebits];
+
+       d = dst + bytes_per_line;
+       *d++ = cm[c[12] >> scalebits];
+       *d++ = cm[c[8] >> scalebits];
+       *d++ = cm[c[13] >> scalebits];
+       *d++ = cm[c[9] >> scalebits];
+       *d++ = cm[c[14] >> scalebits];
+       *d++ = cm[c[10] >> scalebits];
+       *d++ = cm[c[15] >> scalebits];
+       *d++ = cm[c[11] >> scalebits];
+#else
+       int i;
+       const int *c1 = src;
+       const int *c2 = src + 4;
+       unsigned char *d = dst;
+
+       for (i = 0; i < 4; i++, c1++, c2++) {
+               *d++ = CLAMP((*c1) >> scalebits);
+               *d++ = CLAMP((*c2) >> scalebits);
+       }
+       c1 = src + 12;
+       d = dst + bytes_per_line;
+       for (i = 0; i < 4; i++, c1++, c2++) {
+               *d++ = CLAMP((*c1) >> scalebits);
+               *d++ = CLAMP((*c2) >> scalebits);
+       }
+#endif
+}
+
+#if ENABLE_BAYER_DECODER
+/*
+ * Format: 8x2 pixels
+ *   . G . G . G . G . G . G . G
+ *   . . . . . . . . . . . . . .
+ *   . G . G . G . G . G . G . G
+ *   . . . . . . . . . . . . . .
+ *   or
+ *   . . . . . . . . . . . . . .
+ *   G . G . G . G . G . G . G .
+ *   . . . . . . . . . . . . . .
+ *   G . G . G . G . G . G . G .
+*/
+static void copy_image_block_Green(const int *src, unsigned char *dst, unsigned int bytes_per_line, unsigned int scalebits)
+{
+#if UNROLL_LOOP_FOR_COPY
+       /* Unroll all loops */
+       const unsigned char *cm = pwc_crop_table+MAX_OUTER_CROP_VALUE;
+       unsigned char *d = dst;
+       const int *c = src;
+
+       d[0] = cm[c[0] >> scalebits];
+       d[2] = cm[c[1] >> scalebits];
+       d[4] = cm[c[2] >> scalebits];
+       d[6] = cm[c[3] >> scalebits];
+       d[8] = cm[c[4] >> scalebits];
+       d[10] = cm[c[5] >> scalebits];
+       d[12] = cm[c[6] >> scalebits];
+       d[14] = cm[c[7] >> scalebits];
+
+       d = dst + bytes_per_line;
+       d[0] = cm[c[8] >> scalebits];
+       d[2] = cm[c[9] >> scalebits];
+       d[4] = cm[c[10] >> scalebits];
+       d[6] = cm[c[11] >> scalebits];
+       d[8] = cm[c[12] >> scalebits];
+       d[10] = cm[c[13] >> scalebits];
+       d[12] = cm[c[14] >> scalebits];
+       d[14] = cm[c[15] >> scalebits];
+#else
+       int i;
+       unsigned char *d;
+       const int *c = src;
+
+       d = dst;
+       for (i = 0; i < 8; i++, c++)
+               d[i*2] = CLAMP((*c) >> scalebits);
+
+       d = dst + bytes_per_line;
+       for (i = 0; i < 8; i++, c++)
+               d[i*2] = CLAMP((*c) >> scalebits);
+#endif
+}
+#endif
+
+#if ENABLE_BAYER_DECODER
+/*
+ * Format: 4x4 pixels
+ *   R . R . R . R
+ *   . B . B . B .
+ *   R . R . R . R
+ *   . B . B . B .
+ */
+static void copy_image_block_RedBlue(const int *src, unsigned char *dst, unsigned int bytes_per_line, unsigned int scalebits)
+{
+#if UNROLL_LOOP_FOR_COPY
+       /* Unroll all loops */
+       const unsigned char *cm = pwc_crop_table+MAX_OUTER_CROP_VALUE;
+       unsigned char *d = dst;
+       const int *c = src;
+
+       d[0] = cm[c[0] >> scalebits];
+       d[2] = cm[c[1] >> scalebits];
+       d[4] = cm[c[2] >> scalebits];
+       d[6] = cm[c[3] >> scalebits];
+
+       d = dst + bytes_per_line;
+       d[1] = cm[c[4] >> scalebits];
+       d[3] = cm[c[5] >> scalebits];
+       d[5] = cm[c[6] >> scalebits];
+       d[7] = cm[c[7] >> scalebits];
+
+       d = dst + bytes_per_line*2;
+       d[0] = cm[c[8] >> scalebits];
+       d[2] = cm[c[9] >> scalebits];
+       d[4] = cm[c[10] >> scalebits];
+       d[6] = cm[c[11] >> scalebits];
+
+       d = dst + bytes_per_line*3;
+       d[1] = cm[c[12] >> scalebits];
+       d[3] = cm[c[13] >> scalebits];
+       d[5] = cm[c[14] >> scalebits];
+       d[7] = cm[c[15] >> scalebits];
+#else
+       int i;
+       unsigned char *d;
+       const int *c = src;
+
+       d = dst;
+       for (i = 0; i < 4; i++, c++)
+               d[i*2] = CLAMP((*c) >> scalebits);
+
+       d = dst + bytes_per_line;
+       for (i = 0; i < 4; i++, c++)
+               d[i*2+1] = CLAMP((*c) >> scalebits);
+
+       d = dst + bytes_per_line*2;
+       for (i = 0; i < 4; i++, c++)
+               d[i*2] = CLAMP((*c) >> scalebits);
+
+       d = dst + bytes_per_line*3;
+       for (i = 0; i < 4; i++, c++)
+               d[i*2+1] = CLAMP((*c) >> scalebits);
+#endif
+}
+#endif
+
+/*
+ * To manage the stream, we keep bits in a 32 bits register.
+ * fill_nbits(n): fill the reservoir with at least n bits
+ * skip_bits(n): discard n bits from the reservoir
+ * get_bits(n): fill the reservoir, returns the first n bits and discard the
+ *              bits from the reservoir.
+ * __get_nbits(n): faster version of get_bits(n), but asumes that the reservoir
+ *                 contains at least n bits. bits returned is discarded.
+ */
+#define fill_nbits(pdec, nbits_wanted) do { \
+   while (pdec->nbits_in_reservoir<(nbits_wanted)) \
+    { \
+      pdec->reservoir |= (*(pdec->stream)++) << (pdec->nbits_in_reservoir); \
+      pdec->nbits_in_reservoir += 8; \
+    } \
+}  while(0);
+
+#define skip_nbits(pdec, nbits_to_skip) do { \
+   pdec->reservoir >>= (nbits_to_skip); \
+   pdec->nbits_in_reservoir -= (nbits_to_skip); \
+}  while(0);
+
+#define get_nbits(pdec, nbits_wanted, result) do { \
+   fill_nbits(pdec, nbits_wanted); \
+   result = (pdec->reservoir) & ((1U<<(nbits_wanted))-1); \
+   skip_nbits(pdec, nbits_wanted); \
+}  while(0);
+
+#define __get_nbits(pdec, nbits_wanted, result) do { \
+   result = (pdec->reservoir) & ((1U<<(nbits_wanted))-1); \
+   skip_nbits(pdec, nbits_wanted); \
+}  while(0);
+
+#define look_nbits(pdec, nbits_wanted) \
+   ((pdec->reservoir) & ((1U<<(nbits_wanted))-1))
+
+/*
+ * Decode a 4x4 pixel block
+ */
+static void decode_block(struct pwc_dec23_private *pdec,
+                        const unsigned char *ptable0004,
+                        const unsigned char *ptable8004)
+{
+       unsigned int primary_color;
+       unsigned int channel_v, offset1, op;
+       int i;
+
+       fill_nbits(pdec, 16);
+       __get_nbits(pdec, pdec->nbits, primary_color);
+
+       if (look_nbits(pdec,2) == 0) {
+               skip_nbits(pdec, 2);
+               /* Very simple, the color is the same for all pixels of the square */
+               for (i = 0; i < 16; i++)
+                       pdec->temp_colors[i] = pdec->table_dc00[primary_color];
+
+               return;
+       }
+
+       /* This block is encoded with small pattern */
+       for (i = 0; i < 16; i++)
+               pdec->temp_colors[i] = pdec->table_d800[primary_color];
+
+       __get_nbits(pdec, 3, channel_v);
+       channel_v = ((channel_v & 1) << 2) | (channel_v & 2) | ((channel_v & 4) >> 2);
+
+       ptable0004 += (channel_v * 128);
+       ptable8004 += (channel_v * 32);
+
+       offset1 = 0;
+       do
+       {
+               unsigned int htable_idx, rows = 0;
+               const unsigned int *block;
+
+               /* [  zzzz y x x ]
+                *     xx == 00 :=> end of the block def, remove the two bits from the stream
+                *    yxx == 111
+                *    yxx == any other value
+                *
+                */
+               fill_nbits(pdec, 16);
+               htable_idx = look_nbits(pdec, 6);
+               op = hash_table_ops[htable_idx * 4];
+
+               if (op == 2) {
+                       skip_nbits(pdec, 2);
+
+               } else if (op == 1) {
+                       /* 15bits [ xxxx xxxx yyyy 111 ]
+                        * yyy => offset in the table8004
+                        * xxx => offset in the tabled004 (tree)
+                        */
+                       unsigned int mask, shift;
+                       unsigned int nbits, col1;
+                       unsigned int yyyy;
+
+                       skip_nbits(pdec, 3);
+                       /* offset1 += yyyy */
+                       __get_nbits(pdec, 4, yyyy);
+                       offset1 += 1 + yyyy;
+                       offset1 &= 0x0F;
+                       nbits = ptable8004[offset1 * 2];
+
+                       /* col1 = xxxx xxxx */
+                       __get_nbits(pdec, nbits+1, col1);
+
+                       /* Bit mask table */
+                       mask = pdec->table_bitpowermask[nbits][col1];
+                       shift = ptable8004[offset1 * 2 + 1];
+                       rows = ((mask << shift) + 0x80) & 0xFF;
+
+                       block = pdec->table_subblock[rows];
+                       for (i = 0; i < 16; i++)
+                               pdec->temp_colors[i] += block[MulIdx[offset1][i]];
+
+               } else {
+                       /* op == 0
+                        * offset1 is coded on 3 bits
+                        */
+                       unsigned int shift;
+
+                       offset1 += hash_table_ops [htable_idx * 4 + 2];
+                       offset1 &= 0x0F;
+
+                       rows = ptable0004[offset1 + hash_table_ops [htable_idx * 4 + 3]];
+                       block = pdec->table_subblock[rows];
+                       for (i = 0; i < 16; i++)
+                               pdec->temp_colors[i] += block[MulIdx[offset1][i]];
+
+                       shift = hash_table_ops[htable_idx * 4 + 1];
+                       skip_nbits(pdec, shift);
+               }
+
+       } while (op != 2);
+
+}
+
+static void DecompressBand23(struct pwc_dec23_private *pdec,
+                            const unsigned char *rawyuv,
+                            unsigned char *planar_y,
+                            unsigned char *planar_u,
+                            unsigned char *planar_v,
+                            unsigned int   compressed_image_width,
+                            unsigned int   real_image_width)
+{
+       int compression_index, nblocks;
+       const unsigned char *ptable0004;
+       const unsigned char *ptable8004;
+
+       pdec->reservoir = 0;
+       pdec->nbits_in_reservoir = 0;
+       pdec->stream = rawyuv + 1;      /* The first byte of the stream is skipped */
+
+       get_nbits(pdec, 4, compression_index);
+
+       /* pass 1: uncompress Y component */
+       nblocks = compressed_image_width / 4;
+
+       ptable0004 = pdec->table_0004_pass1[compression_index];
+       ptable8004 = pdec->table_8004_pass1[compression_index];
+
+       /* Each block decode a square of 4x4 */
+       while (nblocks) {
+               decode_block(pdec, ptable0004, ptable8004);
+               copy_image_block_Y(pdec->temp_colors, planar_y, real_image_width, pdec->scalebits);
+               planar_y += 4;
+               nblocks--;
+       }
+
+       /* pass 2: uncompress UV component */
+       nblocks = compressed_image_width / 8;
+
+       ptable0004 = pdec->table_0004_pass2[compression_index];
+       ptable8004 = pdec->table_8004_pass2[compression_index];
+
+       /* Each block decode a square of 4x4 */
+       while (nblocks) {
+               decode_block(pdec, ptable0004, ptable8004);
+               copy_image_block_CrCb(pdec->temp_colors, planar_u, real_image_width/2, pdec->scalebits);
+
+               decode_block(pdec, ptable0004, ptable8004);
+               copy_image_block_CrCb(pdec->temp_colors, planar_v, real_image_width/2, pdec->scalebits);
+
+               planar_v += 8;
+               planar_u += 8;
+               nblocks -= 2;
+       }
+
+}
+
+#if ENABLE_BAYER_DECODER
+/*
+ * Size need to be a multiple of 8 in width
+ *
+ * Return a block of four line encoded like this:
+ *
+ *   G R G R G R G R G R G R G R G R
+ *   B G B G B G B G B G B G B G B G
+ *   G R G R G R G R G R G R G R G R
+ *   B G B G B G B G B G B G B G B G
+ *
+ */
+static void DecompressBandBayer(struct pwc_dec23_private *pdec,
+                               const unsigned char *rawyuv,
+                               unsigned char *rgbbayer,
+                               unsigned int   compressed_image_width,
+                               unsigned int   real_image_width)
+{
+       int compression_index, nblocks;
+       const unsigned char *ptable0004;
+       const unsigned char *ptable8004;
+       unsigned char *dest;
+
+       pdec->reservoir = 0;
+       pdec->nbits_in_reservoir = 0;
+       pdec->stream = rawyuv + 1;      /* The first byte of the stream is skipped */
+
+       get_nbits(pdec, 4, compression_index);
+
+       /* pass 1: uncompress RB component */
+       nblocks = compressed_image_width / 4;
+
+       ptable0004 = pdec->table_0004_pass1[compression_index];
+       ptable8004 = pdec->table_8004_pass1[compression_index];
+       dest = rgbbayer;
+
+       /* Each block decode a square of 4x4 */
+       while (nblocks) {
+               decode_block(pdec, ptable0004, ptable8004);
+               copy_image_block_RedBlue(pdec->temp_colors, rgbbayer, real_image_width, pdec->scalebits);
+               dest += 8;
+               nblocks--;
+       }
+
+       /* pass 2: uncompress G component */
+       nblocks = compressed_image_width / 8;
+
+       ptable0004 = pdec->table_0004_pass2[compression_index];
+       ptable8004 = pdec->table_8004_pass2[compression_index];
+
+       /* Each block decode a square of 4x4 */
+       while (nblocks) {
+               decode_block(pdec, ptable0004, ptable8004);
+               copy_image_block_Green(pdec->temp_colors, rgbbayer+1, real_image_width, pdec->scalebits);
+
+               decode_block(pdec, ptable0004, ptable8004);
+               copy_image_block_Green(pdec->temp_colors, rgbbayer+real_image_width, real_image_width, pdec->scalebits);
+
+               rgbbayer += 16;
+               nblocks -= 2;
+       }
+}
+#endif
+
+
+/**
+ *
+ * Uncompress a pwc23 buffer.
+ *
+ * pwc.view: size of the image wanted
+ * pwc.image: size of the image returned by the camera
+ * pwc.offset: (x,y) to displayer image in the view
+ *
+ * src: raw data
+ * dst: image output
+ * flags: PWCX_FLAG_PLANAR or PWCX_FLAG_BAYER
+ */
+void pwc_dec23_decompress(const struct pwc_device *pwc,
+                         const void *src,
+                         void *dst,
+                         int flags)
+{
+       int bandlines_left, stride, bytes_per_block;
+
+       bandlines_left = pwc->image.y / 4;
+       bytes_per_block = pwc->view.x * 4;
+
+       if (flags & PWCX_FLAG_BAYER) {
+#if ENABLE_BAYER_DECODER
+               /* RGB Bayer format */
+               unsigned char *rgbout;
+
+               stride = pwc->view.x * pwc->offset.y;
+               rgbout = dst + stride + pwc->offset.x;
+
+
+               while (bandlines_left--) {
+
+                       DecompressBandBayer(pwc->decompress_data,
+                                           src,
+                                           rgbout,
+                                           pwc->image.x, pwc->view.x);
+
+                       src += pwc->vbandlength;
+                       rgbout += bytes_per_block;
+
+               }
+#else
+               memset(dst, 0, pwc->view.x * pwc->view.y);
+#endif
+
+       } else {
+               /* YUV420P image format */
+               unsigned char *pout_planar_y;
+               unsigned char *pout_planar_u;
+               unsigned char *pout_planar_v;
+               unsigned int   plane_size;
+
+               plane_size = pwc->view.x * pwc->view.y;
+
+               /* offset in Y plane */
+               stride = pwc->view.x * pwc->offset.y;
+               pout_planar_y = dst + stride + pwc->offset.x;
+
+               /* offsets in U/V planes */
+               stride = (pwc->view.x * pwc->offset.y) / 4 + pwc->offset.x / 2;
+               pout_planar_u = dst + plane_size + stride;
+               pout_planar_v = dst + plane_size + plane_size / 4 + stride;
+
+               while (bandlines_left--) {
+
+                       DecompressBand23(pwc->decompress_data,
+                                        src,
+                                        pout_planar_y, pout_planar_u, pout_planar_v,
+                                        pwc->image.x, pwc->view.x);
+                       src += pwc->vbandlength;
+                       pout_planar_y += bytes_per_block;
+                       pout_planar_u += pwc->view.x;
+                       pout_planar_v += pwc->view.x;
+
+               }
+
+       }
+
+}
+
+void pwc_dec23_exit(void)
+{
+       /* Do nothing */
+
+}
+
+/**
+ * Allocate a private structure used by lookup table.
+ * You must call kfree() to free the memory allocated.
+ */
+int pwc_dec23_alloc(struct pwc_device *pwc)
+{
+       pwc->decompress_data = kmalloc(sizeof(struct pwc_dec23_private), GFP_KERNEL);
+       if (pwc->decompress_data == NULL)
+               return -ENOMEM;
+       return 0;
+}
+
+/* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */