/*************************************************************************** * Video4Linux2 driver for Pixart PAC207BCA Camera bridge and sensor * * * * Copyright (C) 2008 by Hans de Goede * * PAC207BCA code derived from the gspca Pixart PAC207BCA library: * * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li * * Copyright (C) 2005 Bertrik.Sikken * * Copyleft (C) 2005 Michel Xhaard mxhaard@magic.fr * * * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usbvideo2.h" /*****************************************************************************/ static __devinitdata struct usb_device_id pac207_id_table[] = { { USB_DEVICE(0x041E, 0x4028) }, /* Creative VistaPlus */ { USB_DEVICE(0x093a, 0x2460) }, /* Qtec Wb100 */ { USB_DEVICE(0x093a, 0x2463) }, /* Philips SPC220NC */ { USB_DEVICE(0x093a, 0x2468) }, /* Generic PAC 207 */ { USB_DEVICE(0x093a, 0x2470) }, /* Genius GF112 */ { USB_DEVICE(0x093a, 0x2471) }, /* Genius GF111 */ { USB_DEVICE(0x093a, 0x2472) }, /* Genius GF111 */ { 0 } }; /*****************************************************************************/ MODULE_DEVICE_TABLE(usb, pac207_id_table); MODULE_AUTHOR("Hans de Goede "); MODULE_DESCRIPTION("Pixart PAC207BCA Camera Driver"); MODULE_LICENSE("GPL"); /*****************************************************************************/ #define PAC207_NAME "pac207" #define PAC207_BRIGHTNESS_MIN 0 #define PAC207_BRIGHTNESS_MAX 255 #define PAC207_BRIGHTNESS_DEFAULT 4 /* power on default: 4 */ #define PAC207_EXPOSURE_MIN 4 #define PAC207_EXPOSURE_MAX 26 #define PAC207_EXPOSURE_DEFAULT 4 /* power on default: 3 ?? */ #define PAC207_EXPOSURE_KNEE 15 #define PAC207_GAIN_MIN 0 #define PAC207_GAIN_MAX 31 #define PAC207_GAIN_DEFAULT 9 /* power on default: 9 */ #define PAC207_GAIN_KNEE 20 #define PAC207_AUTOGAIN_DEADZONE 10 /* We calculating the autogain at the end of the transfer of a frame, at this moment a frame with the old settings is being transmitted, and a frame is being captured with the old settings. So if we adjust the autogain we must ignore atleast the 2 next frames for the new settings to come into effect before doing any other adjustments */ #define PAC207_AUTOGAIN_IGNORE_FRAMES 3 #define PAC207_DEFAULT_READBUFFERS 3 /*****************************************************************************/ enum pac207_line_state { LINE_HEADER1, LINE_HEADER2, LINE_UNCOMPRESSED, LINE_COMPRESSED, }; struct pac207_decompress_table_t { u8 is_abs; u8 len; s8 val; }; struct pac207_decoder_state { u16 line_read; u16 line_marker; u8 line_state; u8 header_read; u8 remaining_bits; s8 no_remaining_bits; u8 get_abs; u8 discard_byte; }; struct pac207_data { struct pac207_decoder_state decoder_state; u8 mode; u8 brightness; u8 exposure; u8 autogain; u8 gain; u8 sof_read; u8 autogain_ignore_frames; atomic_t avg_lum; }; static const char pac207_sof_marker[5] = { 0xFF, 0xFF, 0x00, 0xFF, 0x96 }; static const u8 pac207_sensor_init[][8] = { {0x10, 0x12, 0x0d, 0x12, 0x0c, 0x01, 0x29, 0xf0}, /* 2 */ /* {0x10, 0x24, 0x06, 0x12, 0x0c, 0x01, 0x29, 0xf0}, ** 2 increase the times exposure decrease frame rate */ {0x00, 0x64, 0x64, 0x64, 0x04, 0x10, 0xF0, 0x30}, /* a reg_10 digital gain Red Green Blue Ggain */ {0x00, 0x00, 0x00, 0x70, 0xA0, 0xF8, 0x00, 0x00}, /* 12 */ {0x00, 0x00, 0x32, 0x00, 0x96, 0x00, 0xA2, 0x02}, /* 40 */ {0x32, 0x00, 0x96, 0x00, 0xA2, 0x02, 0xAF, 0x00}, /* 42 reg_66 rate control */ }; static const u8 PacReg72[] = { 0x00, 0x00, 0x36, 0x00 }; /* 48 reg_72 Rate Control end BalSize_4a =0x36 */ static const struct v4l2_pix_format pac207_pix_fmt[2] = { { .width = 352, .height = 288, .pixelformat = V4L2_PIX_FMT_SBGGR8, .field = V4L2_FIELD_NONE, .bytesperline = 0, .sizeimage = 352 * 288, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 8, }, { .width = 176, .height = 144, .pixelformat = V4L2_PIX_FMT_SBGGR8, .field = V4L2_FIELD_NONE, .bytesperline = 0, .sizeimage = 176 * 144, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 8, } }; static struct pac207_decompress_table_t pac207_decompress_table[256]; /*****************************************************************************/ int pac207_write_regs(struct usbvideo2_device* cam, u16 index, const u8 *buffer, u16 length) { struct usb_device* udev = cam->usbdev; int err = 0; u8 *kbuffer; kbuffer = (u8 *) kmalloc(length, GFP_KERNEL); if (!kbuffer) { DBG(1, "Not enough memory"); return -ENOMEM; } memcpy(kbuffer, buffer, length); err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x01, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0x00, index, kbuffer, length, USBVIDEO2_CTRL_TIMEOUT); if (err < 0) DBG(1, "Failed to write registers to index 0x%04X, error %d)", index, err); kfree(kbuffer); return err; } int pac207_write_reg(struct usbvideo2_device* cam, u16 index, u16 value) { struct usb_device* udev = cam->usbdev; int err; err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, value, index, NULL, 0, USBVIDEO2_CTRL_TIMEOUT); if (err && err != -ENODEV) DBG(1, "Failed to write a register (index 0x%04X, " "value 0x%02X, error %d)",index, value, err); return err; } int pac207_read_reg(struct usbvideo2_device* cam, u16 index) { struct usb_device* udev = cam->usbdev; u8* buff = cam->control_buffer; int res; res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0x00, index, buff, 1, USBVIDEO2_CTRL_TIMEOUT); if (res < 0) DBG(1, "Failed to read a register (index 0x%04X, error %d)", index, res); return (res >= 0) ? (int)(*buff) : res; } /*****************************************************************************/ /* auto gain and exposure algorithm based on the knee algorithm described here: http://ytse.tricolour.net/docs/LowLightOptimization.html */ static void pac207_do_auto_gain(struct usbvideo2_device* cam) { int i, steps, desired_avg_lum; struct pac207_data *data = cam->cam_data; int orig_gain = data->gain; int orig_exposure = data->exposure; int avg_lum = atomic_read(&data->avg_lum); if (!data->autogain || avg_lum == -1) return; if (data->autogain_ignore_frames > 0) { data->autogain_ignore_frames--; return; } /* correct desired lumination for the configured brightness */ desired_avg_lum = 100 + data->brightness / 2; /* If we are of a multiple of deadzone, do multiple step to reach the desired lumination fast (with the risc of a slight overshoot */ steps = abs(desired_avg_lum - avg_lum) / PAC207_AUTOGAIN_DEADZONE; for (i = 0; i < steps; i++) { if (avg_lum > desired_avg_lum) { if (data->gain > PAC207_GAIN_KNEE) { data->gain--; } else if (data->exposure > PAC207_EXPOSURE_KNEE) { data->exposure--; } else if (data->gain > PAC207_GAIN_DEFAULT) { data->gain--; } else if (data->exposure > PAC207_EXPOSURE_MIN) { data->exposure--; } else if (data->gain > PAC207_GAIN_MIN) { data->gain--; } else break; } else { if (data->gain < PAC207_GAIN_DEFAULT) { data->gain++; } else if (data->exposure < PAC207_EXPOSURE_KNEE) { data->exposure++; } else if (data->gain < PAC207_GAIN_KNEE) { data->gain++; } else if (data->exposure < PAC207_EXPOSURE_MAX) { data->exposure++; } else if (data->gain < PAC207_GAIN_MAX) { data->gain++; } else break; } } if (data->exposure != orig_exposure || data->gain != orig_gain) { if (data->exposure != orig_exposure) pac207_write_reg(cam, 0x0002, data->exposure); else pac207_write_reg(cam, 0x000e, data->gain); pac207_write_reg(cam, 0x13, 0x01); /* load registers to sen. */ pac207_write_reg(cam, 0x1c, 0x01); /* not documented */ data->autogain_ignore_frames = PAC207_AUTOGAIN_IGNORE_FRAMES; } } /*****************************************************************************/ static u8 *pac207_find_sof(struct usbvideo2_device* cam, u8 *m, unsigned int len, int repeat) { struct pac207_data *data = cam->cam_data; unsigned int i; /* Search for the SOF marker (fixed part) in the header */ for (i = 0; i < len; i++) { if (m[i] == pac207_sof_marker[data->sof_read]) { data->sof_read++; if (data->sof_read == sizeof(pac207_sof_marker)) { DBG(3, "SOF found, bytes to analyze: %u. Frame" "starts at byte #%u", len, i + 1); data->sof_read = 0; return m + i + 1; } } else data->sof_read = 0; } return NULL; } #define CLIP(color) (unsigned char)(((color)>0xFF)?0xff:(((color)<0)?0:(color))) /* Note len is deliberately signed here, where it is unsigned everywhere else as it gets compared with the signed bitpos and can even become negative! */ static int pac207_decompress_row(struct usbvideo2_device* cam, struct usbvideo2_frame_t* f, u8 *cdata, int len) { struct pac207_data *data = cam->cam_data; const struct v4l2_pix_format *pix_format = &pac207_pix_fmt[data->mode]; struct pac207_decoder_state *decoder_state = &data->decoder_state; u8 *outp = f->bufmem + f->buf.bytesused; int val, bitlen, bitpos = -decoder_state->no_remaining_bits; u8 code; /* first two pixels are stored as raw 8-bit */ while (decoder_state->line_read < 2) { *outp++ = *cdata++; decoder_state->line_read++; len--; if (len == 0) goto decompress_exit; } while (decoder_state->line_read < pix_format->width) { if (bitpos < 0) { code = decoder_state->remaining_bits << (8 + bitpos) | cdata[0] >> -bitpos; } else { u8 *addr = cdata + bitpos / 8; code = addr[0] << (bitpos & 7) | addr[1] >> (8 - (bitpos & 7)); } bitlen = decoder_state->get_abs ? 6 : pac207_decompress_table[code].len; /* Stop decompressing if we're out of input data */ if ((bitpos + bitlen) > (len * 8)) break; if (decoder_state->get_abs) { *outp++ = code & 0xFC; decoder_state->line_read++; decoder_state->get_abs = 0; } else { if (pac207_decompress_table[code].is_abs) decoder_state->get_abs = 1; else { /* relative to left pixel */ val = outp[-2] + pac207_decompress_table[code].val; *outp++ = CLIP(val); decoder_state->line_read++; } } bitpos += bitlen; } if (decoder_state->line_read == pix_format->width) { /* completely decompressed line, round pos to nearest word */ len -= 2 * ((bitpos + 15) / 16); if (len < 0) { decoder_state->discard_byte = 1; len = 0; } } else { decoder_state->remaining_bits = cdata[bitpos/8]; decoder_state->no_remaining_bits = (8 - bitpos) & 7; len = 0; } decompress_exit: f->buf.bytesused = outp - (u8 *)f->bufmem; return len; } static void pac207_decode_line_init(struct usbvideo2_device* cam) { struct pac207_data *data = cam->cam_data; struct pac207_decoder_state *decoder_state = &data->decoder_state; decoder_state->line_read = 0; decoder_state->line_state = LINE_HEADER1; decoder_state->no_remaining_bits = 0; decoder_state->get_abs = 0; } static void pac207_decode_frame_init(struct usbvideo2_device* cam) { struct pac207_data *data = cam->cam_data; struct pac207_decoder_state *decoder_state = &data->decoder_state; decoder_state->header_read = 0; decoder_state->discard_byte = 0; pac207_decode_line_init(cam); } static int pac207_decode(struct usbvideo2_device *cam, struct usbvideo2_frame_t *f, u8 *data, unsigned int len) { struct pac207_data *cam_data = cam->cam_data; struct pac207_decoder_state *decoder_state = &cam_data->decoder_state; const struct v4l2_pix_format *pix_format = &pac207_pix_fmt[cam_data->mode]; unsigned int needed = 0; /* first 11 bytes after sof marker: frame header */ if (decoder_state->header_read < 11 ) { /* get average lumination from frame header (byte 5) */ if (decoder_state->header_read < 5 ) { needed = 5 - decoder_state->header_read; if (len >= needed) atomic_set(&cam_data->avg_lum, data[needed-1]); } /* skip the rest of the header */ needed = 11 - decoder_state->header_read; if (len <= needed) { decoder_state->header_read += len; return 0; } data += needed; len -= needed; decoder_state->header_read = 11; } while (len) { if (decoder_state->discard_byte) { data++; len--; decoder_state->discard_byte = 0; continue; } switch (decoder_state->line_state) { case LINE_HEADER1: decoder_state->line_marker = data[0] << 8; decoder_state->line_state = LINE_HEADER2; needed = 1; break; case LINE_HEADER2: decoder_state->line_marker |= data[0]; switch (decoder_state->line_marker) { case 0x0FF0: decoder_state->line_state = LINE_UNCOMPRESSED; break; case 0x1EE1: decoder_state->line_state = LINE_COMPRESSED; break; default: DBG(3, "Error unknown line-header %04X", (int)decoder_state->line_marker); f->state = F_ERROR; return 0; } needed = 1; break; case LINE_UNCOMPRESSED: needed = pix_format->width - decoder_state->line_read; if (needed > len) needed = len; memcpy(f->bufmem + f->buf.bytesused, data, needed); f->buf.bytesused += needed; decoder_state->line_read += needed; break; case LINE_COMPRESSED: needed = len - pac207_decompress_row(cam, f, data, len); break; } data += needed; len -= needed; if (decoder_state->line_read == pix_format->width) { if (f->buf.bytesused == cam->imagesize) { /* eureka we've got a frame */ f->state = F_DONE; return 1; } pac207_decode_line_init(cam); } } return 0; } /*****************************************************************************/ static int pac207_start_transfer(struct usbvideo2_device* cam) { u8 mode; struct pac207_data *data = cam->cam_data; pac207_write_reg(cam, 0x0f, 0x10); /* Power control (Bit 6-0) */ pac207_write_regs(cam, 0x0002, pac207_sensor_init[0], 8); pac207_write_regs(cam, 0x000a, pac207_sensor_init[1], 8); pac207_write_regs(cam, 0x0012, pac207_sensor_init[2], 8); pac207_write_regs(cam, 0x0040, pac207_sensor_init[3], 8); pac207_write_regs(cam, 0x0042, pac207_sensor_init[4], 8); pac207_write_regs(cam, 0x0048, PacReg72, 4); pac207_write_reg(cam, 0x4a, 0x88); /* Compression Balance size */ pac207_write_reg(cam, 0x4b, 0x00); /* Sram test value */ pac207_write_reg(cam, 0x08, data->brightness); pac207_write_reg(cam, 0x0e, data->gain); /* PGA global gain (Bit 4-0) */ pac207_write_reg(cam, 0x02, data->exposure); /* PXCK = 12MHz /n */ mode = 0x02; /* Image Format (Bit 0), LED (1), Compr. test mode (2) */ if (data->mode) { /* 176x144 */ mode |= 0x01; DBG(3, "pac207_start mode 176x144"); } else /* 352x288 */ DBG(3, "pac207_start mode 352x288"); pac207_write_reg(cam, 0x41, mode); pac207_write_reg(cam, 0x13, 0x01); /* load registers to sensor (Bit 0, auto clear) */ pac207_write_reg(cam, 0x1c, 0x01); /* not documented */ udelay(1000); /* taken from gspca */ pac207_write_reg(cam, 0x40, 0x01); /* Start ISO pipe */ data->sof_read = 0; data->autogain_ignore_frames = 0; atomic_set (&data->avg_lum, -1); return 0; } static void pac207_stop_transfer_pre_kill_urbs(struct usbvideo2_device* cam) { pac207_write_reg(cam, 0x40, 0x00); /* Stop ISO pipe */ pac207_write_reg(cam, 0x41, 0x00); /* Turn of LED */ pac207_write_reg(cam, 0x0f, 0x00); /* Power Control */ } /*****************************************************************************/ static int pac207_open(struct usbvideo2_device* cam) { struct pac207_data *data = cam->cam_data; data->mode = 0; cam->imagesize = 352 * 288; data->brightness = PAC207_BRIGHTNESS_DEFAULT; data->exposure = PAC207_EXPOSURE_DEFAULT; data->gain = PAC207_GAIN_DEFAULT; data->autogain = 1; return 0; } static int pac207_vidioc_query_ctrl(struct file *file, void *fh, struct v4l2_queryctrl *a) { const struct v4l2_queryctrl qctl[] = { { .id = V4L2_CID_BRIGHTNESS, .type = V4L2_CTRL_TYPE_INTEGER, .name = "brightness", .minimum = PAC207_BRIGHTNESS_MIN, .maximum = PAC207_BRIGHTNESS_MAX, .step = 1, .default_value = PAC207_BRIGHTNESS_DEFAULT, .flags = 0, }, { .id = V4L2_CID_EXPOSURE, .type = V4L2_CTRL_TYPE_INTEGER, .name = "exposure", .minimum = PAC207_EXPOSURE_MIN, .maximum = PAC207_EXPOSURE_MAX, .step = 1, .default_value = PAC207_EXPOSURE_DEFAULT, .flags = 0, }, { .id = V4L2_CID_AUTOGAIN, .type = V4L2_CTRL_TYPE_BOOLEAN, .name = "autogain", .minimum = 0, .maximum = 1, .step = 1, .default_value = 1, .flags = 0, }, { .id = V4L2_CID_GAIN, .type = V4L2_CTRL_TYPE_INTEGER, .name = "gain", .minimum = PAC207_GAIN_MIN, .maximum = PAC207_GAIN_MAX, .step = 1, .default_value = PAC207_GAIN_DEFAULT, .flags = 0, } }; int i; for (i = 0; i < ARRAY_SIZE(qctl); i++) if (qctl[i].id == a->id) { memcpy(a, &qctl[i], sizeof(qctl[0])); return 0; } return -EINVAL; } static int pac207_vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *a) { struct usbvideo2_device* cam = fh; struct pac207_data *data = cam->cam_data; switch (a->id) { case V4L2_CID_BRIGHTNESS: a->value = data->brightness; break; case V4L2_CID_EXPOSURE: a->value = data->exposure; break; case V4L2_CID_AUTOGAIN: a->value = data->autogain; break; case V4L2_CID_GAIN: a->value = data->gain; break; default: return -EINVAL; } return 0; } static int pac207_vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a) { struct usbvideo2_device* cam = fh; struct pac207_data *data = cam->cam_data; int new_value = a->value; int err; if ((err = pac207_vidioc_g_ctrl(file, fh, a))) return err; if (a->value == new_value) return 0; /* don't allow mucking with gain / exposure when using autogain */ if (data->autogain && (a->id == V4L2_CID_GAIN || a->id == V4L2_CID_EXPOSURE)) return -EINVAL; switch (a->id) { case V4L2_CID_BRIGHTNESS: data->brightness = new_value; pac207_write_reg(cam, 0x0008, data->brightness); /* give brightness change time to take effect before doing autogain based on the new brightness */ data->autogain_ignore_frames = PAC207_AUTOGAIN_IGNORE_FRAMES; break; case V4L2_CID_EXPOSURE: data->exposure = new_value; pac207_write_reg(cam, 0x0002, data->exposure); break; case V4L2_CID_AUTOGAIN: data->autogain = new_value; /* when switching to autogain set defaults to make sure we are on a valid point of the autogain gain / exposure knee graph, and give this change time to take effect before doing autogain. */ if (data->autogain) { data->exposure = PAC207_EXPOSURE_DEFAULT; data->gain = PAC207_GAIN_DEFAULT; data->autogain_ignore_frames = PAC207_AUTOGAIN_IGNORE_FRAMES; pac207_write_reg(cam, 0x0002, data->exposure); pac207_write_reg(cam, 0x000e, data->gain); } break; case V4L2_CID_GAIN: data->gain = new_value; pac207_write_reg(cam, 0x000e, data->gain); break; /* no default needed already checked in pac207_vidioc_g_ctrl */ } pac207_write_reg(cam, 0x13, 0x01); /* load registers to sensor */ pac207_write_reg(cam, 0x1c, 0x01); /* not documented */ return 0; } static int pac207_vidioc_enum_fmt_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f) { if (f->index != 0) return -EINVAL; f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; f->flags = 0; strcpy(f->description, "bayer rgb"); f->pixelformat = V4L2_PIX_FMT_SBGGR8; memset(&f->reserved, 0, sizeof(f->reserved)); return 0; } static int pac207_vidioc_g_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) { struct usbvideo2_device* cam = fh; struct pac207_data *data = cam->cam_data; memcpy(&(f->fmt.pix), &pac207_pix_fmt[data->mode], sizeof(pac207_pix_fmt[0])); return 0; } static int pac207_vidioc_try_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) { if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_SBGGR8) return -EINVAL; if (f->fmt.pix.width >= 352 && f->fmt.pix.height >= 288) memcpy(&(f->fmt.pix), &pac207_pix_fmt[0], sizeof(pac207_pix_fmt[0])); else memcpy(&(f->fmt.pix), &pac207_pix_fmt[1], sizeof(pac207_pix_fmt[0])); return 0; } static int pac207_vidioc_s_fmt_cap(struct file *file, void *fh, struct v4l2_format *f) { struct usbvideo2_device* cam = fh; struct pac207_data *data = cam->cam_data; if (f->fmt.pix.width == pac207_pix_fmt[0].width) { data->mode = 0; cam->imagesize = 352 * 288; } else { data->mode = 1; cam->imagesize = 176 * 144; } return 0; } /*****************************************************************************/ static int pac207_probe(struct usbvideo2_device* cam, const struct usb_device_id* id) { u8 idreg[2]; idreg[0] = pac207_read_reg(cam, 0x0000); idreg[1] = pac207_read_reg(cam, 0x0001); idreg[0] = ((idreg[0] & 0x0F) << 4) | ((idreg[1] & 0xf0) >> 4); idreg[1] = idreg[1] & 0x0f; DBG(2, "Pixart Sensor ID 0x%02X Chips ID 0x%02X", idreg[0], idreg[1]); if (idreg[0] != 0x27) { DBG(1, "Error invalid sensor ID!"); return -ENODEV; } pac207_write_reg(cam, 0x41, 0x00); /* 00 Bit_0=Image Format, Bit_1=LED, Bit_2=Compression test mode enable */ pac207_write_reg(cam, 0x0f, 0x00); /* Power Control */ pac207_write_reg(cam, 0x11, 0x30); /* Analog Bias */ DBG(2, "Pixart PAC207BCA Image Processor and Control Chip detected " "(vid/pid 0x%04X:0x%04X)",id->idVendor, id->idProduct); cam->imagesize = 352 * 288; strcpy(cam->driver, PAC207_NAME); strcpy(cam->name, "Pixart PAC207BCA USB Camera"); cam->sof_marker_size = sizeof(pac207_sof_marker); cam->endpoint_address = 0x85; return 0; } const struct usbvideo2_cam_funcs pac207_funcs = { .owner = THIS_MODULE, .probe = pac207_probe, .find_sof = pac207_find_sof, .decode_frame_init = pac207_decode_frame_init, .decode_frame_data = pac207_decode, .start_transfer = pac207_start_transfer, .stop_transfer_pre_kill_urbs = pac207_stop_transfer_pre_kill_urbs, .open = pac207_open, .frame_dequeued = pac207_do_auto_gain, .vidioc_query_ctrl = pac207_vidioc_query_ctrl, .vidioc_s_ctrl = pac207_vidioc_s_ctrl, .vidioc_g_ctrl = pac207_vidioc_g_ctrl, .vidioc_enum_fmt_cap = pac207_vidioc_enum_fmt_cap, .vidioc_g_fmt_cap = pac207_vidioc_g_fmt_cap, .vidioc_try_fmt_cap = pac207_vidioc_try_fmt_cap, .vidioc_s_fmt_cap = pac207_vidioc_s_fmt_cap, }; static int pac207_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) { return usbvideo2_probe(intf, id, &pac207_funcs, sizeof(struct pac207_data)); } static struct usb_driver pac207_usb_driver = { .name = PAC207_NAME, .id_table = pac207_id_table, .probe = pac207_usb_probe, .disconnect = usbvideo2_disconnect, }; /*****************************************************************************/ static void pac207_init_decompress_table(void) { int i; u8 is_abs, len; s8 val; for (i = 0; i < 256; i++) { is_abs = 0; val = 0; len = 0; if ((i & 0xC0) == 0) { /* code 00 */ val = 0; len = 2; } else if ((i & 0xC0) == 0x40) { /* code 01 */ val = -5; len = 2; } else if ((i & 0xC0) == 0x80) { /* code 10 */ val = +5; len = 2; } else if ((i & 0xF0) == 0xC0) { /* code 1100 */ val = -10; len = 4; } else if ((i & 0xF0) == 0xD0) { /* code 1101 */ val = +10; len = 4; } else if ((i & 0xF8) == 0xE0) { /* code 11100 */ val = -15; len = 5; } else if ((i & 0xF8) == 0xE8) { /* code 11101 */ val = +15; len = 5; } else if ((i & 0xFC) == 0xF0) { /* code 111100 */ val = -20; len = 6; } else if ((i & 0xFC) == 0xF4) { /* code 111101 */ val = +20; len = 6; } else if ((i & 0xF8) == 0xF8) { /* code 11111xxxxxx */ is_abs = 1; val = 0; len = 5; } pac207_decompress_table[i].is_abs = is_abs; pac207_decompress_table[i].val = val; pac207_decompress_table[i].len = len; } } /*****************************************************************************/ static int __init pac207_module_init(void) { int err = 0; pac207_init_decompress_table(); if ((err = usb_register(&pac207_usb_driver))) printk(KERN_ERR PAC207_NAME ": usb_register() failed, error: %d", err); return err; } static void __exit pac207_module_exit(void) { usb_deregister(&pac207_usb_driver); } module_init(pac207_module_init); module_exit(pac207_module_exit);