/*************************************************************************** * Video4Linux driver for W996[87]CF JPEG USB Dual Mode Camera Chip. * * * * Copyright (C) 2002-2004 by Luca Risolia * * * * - Memory management code from bttv driver by Ralph Metzler, * * Marcus Metzler and Gerd Knorr. * * - I2C interface to kernel, high-level image sensor control routines and * * some symbolic names from OV511 driver by Mark W. McClelland. * * - Low-level I2C fast write function by Piotr Czerczak. * * - Low-level I2C read function by Frederic Jouault. * * * * 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 "w9968cf.h" #include "w9968cf_decoder.h" /**************************************************************************** * Module macros and parameters * ****************************************************************************/ MODULE_DEVICE_TABLE(usb, winbond_id_table); MODULE_AUTHOR(W9968CF_MODULE_AUTHOR" "W9968CF_AUTHOR_EMAIL); MODULE_DESCRIPTION(W9968CF_MODULE_NAME); MODULE_VERSION(W9968CF_MODULE_VERSION); MODULE_LICENSE(W9968CF_MODULE_LICENSE); MODULE_SUPPORTED_DEVICE("Video"); static int ovmod_load = W9968CF_OVMOD_LOAD; static int vppmod_load = W9968CF_VPPMOD_LOAD; static unsigned short simcams = W9968CF_SIMCAMS; static short video_nr[]={[0 ... W9968CF_MAX_DEVICES-1] = -1}; /*-1=first free*/ static unsigned int packet_size[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_PACKET_SIZE}; static unsigned short max_buffers[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BUFFERS}; static int double_buffer[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_DOUBLE_BUFFER}; static int clamping[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CLAMPING}; static unsigned short filter_type[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_FILTER_TYPE}; static int largeview[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_LARGEVIEW}; static unsigned short decompression[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_DECOMPRESSION}; static int upscaling[]= {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_UPSCALING}; static unsigned short force_palette[] = {[0 ... W9968CF_MAX_DEVICES-1] = 0}; static int force_rgb[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_FORCE_RGB}; static int autobright[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_AUTOBRIGHT}; static int autoexp[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_AUTOEXP}; static unsigned short lightfreq[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_LIGHTFREQ}; static int bandingfilter[] = {[0 ... W9968CF_MAX_DEVICES-1]= W9968CF_BANDINGFILTER}; static short clockdiv[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CLOCKDIV}; static int backlight[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BACKLIGHT}; static int mirror[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_MIRROR}; static int monochrome[] = {[0 ... W9968CF_MAX_DEVICES-1]=W9968CF_MONOCHROME}; static unsigned int brightness[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_BRIGHTNESS}; static unsigned int hue[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_HUE}; static unsigned int colour[]={[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_COLOUR}; static unsigned int contrast[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_CONTRAST}; static unsigned int whiteness[] = {[0 ... W9968CF_MAX_DEVICES-1] = W9968CF_WHITENESS}; #ifdef W9968CF_DEBUG static unsigned short debug = W9968CF_DEBUG_LEVEL; static int specific_debug = W9968CF_SPECIFIC_DEBUG; #endif static unsigned int param_nv[24]; /* number of values per parameter */ #ifdef CONFIG_KMOD module_param(ovmod_load, bool, 0644); module_param(vppmod_load, bool, 0444); #endif module_param(simcams, ushort, 0644); module_param_array(video_nr, short, ¶m_nv[0], 0444); module_param_array(packet_size, uint, ¶m_nv[1], 0444); module_param_array(max_buffers, ushort, ¶m_nv[2], 0444); module_param_array(double_buffer, bool, ¶m_nv[3], 0444); module_param_array(clamping, bool, ¶m_nv[4], 0444); module_param_array(filter_type, ushort, ¶m_nv[5], 0444); module_param_array(largeview, bool, ¶m_nv[6], 0444); module_param_array(decompression, ushort, ¶m_nv[7], 0444); module_param_array(upscaling, bool, ¶m_nv[8], 0444); module_param_array(force_palette, ushort, ¶m_nv[9], 0444); module_param_array(force_rgb, ushort, ¶m_nv[10], 0444); module_param_array(autobright, bool, ¶m_nv[11], 0444); module_param_array(autoexp, bool, ¶m_nv[12], 0444); module_param_array(lightfreq, ushort, ¶m_nv[13], 0444); module_param_array(bandingfilter, bool, ¶m_nv[14], 0444); module_param_array(clockdiv, short, ¶m_nv[15], 0444); module_param_array(backlight, bool, ¶m_nv[16], 0444); module_param_array(mirror, bool, ¶m_nv[17], 0444); module_param_array(monochrome, bool, ¶m_nv[18], 0444); module_param_array(brightness, uint, ¶m_nv[19], 0444); module_param_array(hue, uint, ¶m_nv[20], 0444); module_param_array(colour, uint, ¶m_nv[21], 0444); module_param_array(contrast, uint, ¶m_nv[22], 0444); module_param_array(whiteness, uint, ¶m_nv[23], 0444); #ifdef W9968CF_DEBUG module_param(debug, ushort, 0644); module_param(specific_debug, bool, 0644); #endif #ifdef CONFIG_KMOD MODULE_PARM_DESC(ovmod_load, "\n<0|1> Automatic 'ovcamchip' module loading." "\n0 disabled, 1 enabled." "\nIf enabled,'insmod' searches for the required 'ovcamchip'" "\nmodule in the system, according to its configuration, and" "\nattempts to load that module automatically. This action is" "\nperformed once as soon as the 'w9968cf' module is loaded" "\ninto memory." "\nDefault value is "__MODULE_STRING(W9968CF_OVMOD_LOAD)"." "\n"); MODULE_PARM_DESC(vppmod_load, "\n<0|1> Automatic 'w9968cf-vpp' module loading." "\n0 disabled, 1 enabled." "\nIf enabled, every time an application attempts to open a" "\ncamera, 'insmod' searches for the video post-processing" "\nmodule in the system and loads it automatically (if" "\npresent). The optional 'w9968cf-vpp' module adds extra" "\n image manipulation functions to the 'w9968cf' module,like" "\nsoftware up-scaling,colour conversions and video decoding" "\nfor very high frame rates." "\nDefault value is "__MODULE_STRING(W9968CF_VPPMOD_LOAD)"." "\n"); #endif MODULE_PARM_DESC(simcams, "\n Number of cameras allowed to stream simultaneously." "\nn may vary from 0 to " __MODULE_STRING(W9968CF_MAX_DEVICES)"." "\nDefault value is "__MODULE_STRING(W9968CF_SIMCAMS)"." "\n"); MODULE_PARM_DESC(video_nr, "\n<-1|n[,...]> Specify V4L minor mode number." "\n -1 = use next available (default)" "\n n = use minor number n (integer >= 0)" "\nYou can specify up to "__MODULE_STRING(W9968CF_MAX_DEVICES) " cameras this way." "\nFor example:" "\nvideo_nr=-1,2,-1 would assign minor number 2 to" "\nthe second camera and use auto for the first" "\none and for every other camera." "\n"); MODULE_PARM_DESC(packet_size, "\n Specify the maximum data payload" "\nsize in bytes for alternate settings, for each device." "\nn is scaled between 63 and 1023 " "(default is "__MODULE_STRING(W9968CF_PACKET_SIZE)")." "\n"); MODULE_PARM_DESC(max_buffers, "\n For advanced users." "\nSpecify the maximum number of video frame buffers" "\nto allocate for each device, from 2 to " __MODULE_STRING(W9968CF_MAX_BUFFERS) ". (default is "__MODULE_STRING(W9968CF_BUFFERS)")." "\n"); MODULE_PARM_DESC(double_buffer, "\n<0|1[,...]> " "Hardware double buffering: 0 disabled, 1 enabled." "\nIt should be enabled if you want smooth video output: if" "\nyou obtain out of sync. video, disable it, or try to" "\ndecrease the 'clockdiv' module parameter value." "\nDefault value is "__MODULE_STRING(W9968CF_DOUBLE_BUFFER) " for every device." "\n"); MODULE_PARM_DESC(clamping, "\n<0|1[,...]> Video data clamping: 0 disabled, 1 enabled." "\nDefault value is "__MODULE_STRING(W9968CF_CLAMPING) " for every device." "\n"); MODULE_PARM_DESC(filter_type, "\n<0|1|2[,...]> Video filter type." "\n0 none, 1 (1-2-1) 3-tap filter, " "2 (2-3-6-3-2) 5-tap filter." "\nDefault value is "__MODULE_STRING(W9968CF_FILTER_TYPE) " for every device." "\nThe filter is used to reduce noise and aliasing artifacts" "\nproduced by the CCD or CMOS image sensor, and the scaling" " process." "\n"); MODULE_PARM_DESC(largeview, "\n<0|1[,...]> Large view: 0 disabled, 1 enabled." "\nDefault value is "__MODULE_STRING(W9968CF_LARGEVIEW) " for every device." "\n"); MODULE_PARM_DESC(upscaling, "\n<0|1[,...]> Software scaling (for non-compressed video):" "\n0 disabled, 1 enabled." "\nDisable it if you have a slow CPU or you don't have" " enough memory." "\nDefault value is "__MODULE_STRING(W9968CF_UPSCALING) " for every device." "\nIf 'w9968cf-vpp' is not present, this parameter is" " set to 0." "\n"); MODULE_PARM_DESC(decompression, "\n<0|1|2[,...]> Software video decompression:" "\n- 0 disables decompression (doesn't allow formats needing" " decompression)" "\n- 1 forces decompression (allows formats needing" " decompression only);" "\n- 2 allows any permitted formats." "\nFormats supporting compressed video are YUV422P and" " YUV420P/YUV420 " "\nin any resolutions where both width and height are " "a multiple of 16." "\nDefault value is "__MODULE_STRING(W9968CF_DECOMPRESSION) " for every device." "\nIf 'w9968cf-vpp' is not present, forcing decompression is " "\nnot allowed; in this case this parameter is set to 2." "\n"); MODULE_PARM_DESC(force_palette, "\n<0" "|" __MODULE_STRING(VIDEO_PALETTE_UYVY) "|" __MODULE_STRING(VIDEO_PALETTE_YUV420) "|" __MODULE_STRING(VIDEO_PALETTE_YUV422P) "|" __MODULE_STRING(VIDEO_PALETTE_YUV420P) "|" __MODULE_STRING(VIDEO_PALETTE_YUYV) "|" __MODULE_STRING(VIDEO_PALETTE_YUV422) "|" __MODULE_STRING(VIDEO_PALETTE_GREY) "|" __MODULE_STRING(VIDEO_PALETTE_RGB555) "|" __MODULE_STRING(VIDEO_PALETTE_RGB565) "|" __MODULE_STRING(VIDEO_PALETTE_RGB24) "|" __MODULE_STRING(VIDEO_PALETTE_RGB32) "[,...]>" " Force picture palette." "\nIn order:" "\n- 0 allows any of the following formats:" "\n- UYVY 16 bpp - Original video, compression disabled" "\n- YUV420 12 bpp - Original video, compression enabled" "\n- YUV422P 16 bpp - Original video, compression enabled" "\n- YUV420P 12 bpp - Original video, compression enabled" "\n- YUVY 16 bpp - Software conversion from UYVY" "\n- YUV422 16 bpp - Software conversion from UYVY" "\n- GREY 8 bpp - Software conversion from UYVY" "\n- RGB555 16 bpp - Software conversion from UYVY" "\n- RGB565 16 bpp - Software conversion from UYVY" "\n- RGB24 24 bpp - Software conversion from UYVY" "\n- RGB32 32 bpp - Software conversion from UYVY" "\nWhen not 0, this parameter will override 'decompression'." "\nDefault value is 0 for every device." "\nInitial palette is " __MODULE_STRING(W9968CF_PALETTE_DECOMP_ON)"." "\nIf 'w9968cf-vpp' is not present, this parameter is" " set to 9 (UYVY)." "\n"); MODULE_PARM_DESC(force_rgb, "\n<0|1[,...]> Read RGB video data instead of BGR:" "\n 1 = use RGB component ordering." "\n 0 = use BGR component ordering." "\nThis parameter has effect when using RGBX palettes only." "\nDefault value is "__MODULE_STRING(W9968CF_FORCE_RGB) " for every device." "\n"); MODULE_PARM_DESC(autobright, "\n<0|1[,...]> Image sensor automatically changes brightness:" "\n 0 = no, 1 = yes" "\nDefault value is "__MODULE_STRING(W9968CF_AUTOBRIGHT) " for every device." "\n"); MODULE_PARM_DESC(autoexp, "\n<0|1[,...]> Image sensor automatically changes exposure:" "\n 0 = no, 1 = yes" "\nDefault value is "__MODULE_STRING(W9968CF_AUTOEXP) " for every device." "\n"); MODULE_PARM_DESC(lightfreq, "\n<50|60[,...]> Light frequency in Hz:" "\n 50 for European and Asian lighting," " 60 for American lighting." "\nDefault value is "__MODULE_STRING(W9968CF_LIGHTFREQ) " for every device." "\n"); MODULE_PARM_DESC(bandingfilter, "\n<0|1[,...]> Banding filter to reduce effects of" " fluorescent lighting:" "\n 0 disabled, 1 enabled." "\nThis filter tries to reduce the pattern of horizontal" "\nlight/dark bands caused by some (usually fluorescent)" " lighting." "\nDefault value is "__MODULE_STRING(W9968CF_BANDINGFILTER) " for every device." "\n"); MODULE_PARM_DESC(clockdiv, "\n<-1|n[,...]> " "Force pixel clock divisor to a specific value (for experts):" "\n n may vary from 0 to 127." "\n -1 for automatic value." "\nSee also the 'double_buffer' module parameter." "\nDefault value is "__MODULE_STRING(W9968CF_CLOCKDIV) " for every device." "\n"); MODULE_PARM_DESC(backlight, "\n<0|1[,...]> Objects are lit from behind:" "\n 0 = no, 1 = yes" "\nDefault value is "__MODULE_STRING(W9968CF_BACKLIGHT) " for every device." "\n"); MODULE_PARM_DESC(mirror, "\n<0|1[,...]> Reverse image horizontally:" "\n 0 = no, 1 = yes" "\nDefault value is "__MODULE_STRING(W9968CF_MIRROR) " for every device." "\n"); MODULE_PARM_DESC(monochrome, "\n<0|1[,...]> Use image sensor as monochrome sensor:" "\n 0 = no, 1 = yes" "\nNot all the sensors support monochrome color." "\nDefault value is "__MODULE_STRING(W9968CF_MONOCHROME) " for every device." "\n"); MODULE_PARM_DESC(brightness, "\n Set picture brightness (0-65535)." "\nDefault value is "__MODULE_STRING(W9968CF_BRIGHTNESS) " for every device." "\nThis parameter has no effect if 'autobright' is enabled." "\n"); MODULE_PARM_DESC(hue, "\n Set picture hue (0-65535)." "\nDefault value is "__MODULE_STRING(W9968CF_HUE) " for every device." "\n"); MODULE_PARM_DESC(colour, "\n Set picture saturation (0-65535)." "\nDefault value is "__MODULE_STRING(W9968CF_COLOUR) " for every device." "\n"); MODULE_PARM_DESC(contrast, "\n Set picture contrast (0-65535)." "\nDefault value is "__MODULE_STRING(W9968CF_CONTRAST) " for every device." "\n"); MODULE_PARM_DESC(whiteness, "\n Set picture whiteness (0-65535)." "\nDefault value is "__MODULE_STRING(W9968CF_WHITENESS) " for every device." "\n"); #ifdef W9968CF_DEBUG MODULE_PARM_DESC(debug, "\n Debugging information level, from 0 to 6:" "\n0 = none (use carefully)" "\n1 = critical errors" "\n2 = significant informations" "\n3 = configuration or general messages" "\n4 = warnings" "\n5 = called functions" "\n6 = function internals" "\nLevel 5 and 6 are useful for testing only, when only " "one device is used." "\nDefault value is "__MODULE_STRING(W9968CF_DEBUG_LEVEL)"." "\n"); MODULE_PARM_DESC(specific_debug, "\n<0|1> Enable or disable specific debugging messages:" "\n0 = print messages concerning every level" " <= 'debug' level." "\n1 = print messages concerning the level" " indicated by 'debug'." "\nDefault value is " __MODULE_STRING(W9968CF_SPECIFIC_DEBUG)"." "\n"); #endif /* W9968CF_DEBUG */ /**************************************************************************** * Some prototypes * ****************************************************************************/ /* Video4linux interface */ static struct file_operations w9968cf_fops; static int w9968cf_open(struct inode*, struct file*); static int w9968cf_release(struct inode*, struct file*); static int w9968cf_mmap(struct file*, struct vm_area_struct*); static int w9968cf_ioctl(struct inode*, struct file*, unsigned, unsigned long); static ssize_t w9968cf_read(struct file*, char __user *, size_t, loff_t*); static int w9968cf_v4l_ioctl(struct inode*, struct file*, unsigned int, void __user *); /* USB-specific */ static int w9968cf_start_transfer(struct w9968cf_device*); static int w9968cf_stop_transfer(struct w9968cf_device*); static int w9968cf_write_reg(struct w9968cf_device*, u16 value, u16 index); static int w9968cf_read_reg(struct w9968cf_device*, u16 index); static int w9968cf_write_fsb(struct w9968cf_device*, u16* data); static int w9968cf_write_sb(struct w9968cf_device*, u16 value); static int w9968cf_read_sb(struct w9968cf_device*); static int w9968cf_upload_quantizationtables(struct w9968cf_device*); static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs); /* Low-level I2C (SMBus) I/O */ static int w9968cf_smbus_start(struct w9968cf_device*); static int w9968cf_smbus_stop(struct w9968cf_device*); static int w9968cf_smbus_write_byte(struct w9968cf_device*, u8 v); static int w9968cf_smbus_read_byte(struct w9968cf_device*, u8* v); static int w9968cf_smbus_write_ack(struct w9968cf_device*); static int w9968cf_smbus_read_ack(struct w9968cf_device*); static int w9968cf_smbus_refresh_bus(struct w9968cf_device*); static int w9968cf_i2c_adap_read_byte(struct w9968cf_device* cam, u16 address, u8* value); static int w9968cf_i2c_adap_read_byte_data(struct w9968cf_device*, u16 address, u8 subaddress, u8* value); static int w9968cf_i2c_adap_write_byte(struct w9968cf_device*, u16 address, u8 subaddress); static int w9968cf_i2c_adap_fastwrite_byte_data(struct w9968cf_device*, u16 address, u8 subaddress, u8 value); /* I2C interface to kernel */ static int w9968cf_i2c_init(struct w9968cf_device*); static int w9968cf_i2c_smbus_xfer(struct i2c_adapter*, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data*); static u32 w9968cf_i2c_func(struct i2c_adapter*); static int w9968cf_i2c_attach_inform(struct i2c_client*); static int w9968cf_i2c_detach_inform(struct i2c_client*); static int w9968cf_i2c_control(struct i2c_adapter*, unsigned int cmd, unsigned long arg); /* Memory management */ static void* rvmalloc(unsigned long size); static void rvfree(void *mem, unsigned long size); static void w9968cf_deallocate_memory(struct w9968cf_device*); static int w9968cf_allocate_memory(struct w9968cf_device*); /* High-level image sensor control functions */ static int w9968cf_sensor_set_control(struct w9968cf_device*,int cid,int val); static int w9968cf_sensor_get_control(struct w9968cf_device*,int cid,int *val); static int w9968cf_sensor_cmd(struct w9968cf_device*, unsigned int cmd, void *arg); static int w9968cf_sensor_init(struct w9968cf_device*); static int w9968cf_sensor_update_settings(struct w9968cf_device*); static int w9968cf_sensor_get_picture(struct w9968cf_device*); static int w9968cf_sensor_update_picture(struct w9968cf_device*, struct video_picture pict); /* Other helper functions */ static void w9968cf_configure_camera(struct w9968cf_device*,struct usb_device*, enum w9968cf_model_id, const unsigned short dev_nr); static void w9968cf_adjust_configuration(struct w9968cf_device*); static int w9968cf_turn_on_led(struct w9968cf_device*); static int w9968cf_init_chip(struct w9968cf_device*); static inline u16 w9968cf_valid_palette(u16 palette); static inline u16 w9968cf_valid_depth(u16 palette); static inline u8 w9968cf_need_decompression(u16 palette); static int w9968cf_set_picture(struct w9968cf_device*, struct video_picture); static int w9968cf_set_window(struct w9968cf_device*, struct video_window); static int w9968cf_postprocess_frame(struct w9968cf_device*, struct w9968cf_frame_t*); static int w9968cf_adjust_window_size(struct w9968cf_device*, u16* w, u16* h); static void w9968cf_init_framelist(struct w9968cf_device*); static void w9968cf_push_frame(struct w9968cf_device*, u8 f_num); static void w9968cf_pop_frame(struct w9968cf_device*,struct w9968cf_frame_t**); static void w9968cf_release_resources(struct w9968cf_device*); /* Intermodule communication */ static int w9968cf_vppmod_detect(struct w9968cf_device*); static void w9968cf_vppmod_release(struct w9968cf_device*); /**************************************************************************** * Symbolic names * ****************************************************************************/ /* Used to represent a list of values and their respective symbolic names */ struct w9968cf_symbolic_list { const int num; const char *name; }; /*-------------------------------------------------------------------------- Returns the name of the matching element in the symbolic_list array. The end of the list must be marked with an element that has a NULL name. --------------------------------------------------------------------------*/ static inline const char * symbolic(struct w9968cf_symbolic_list list[], const int num) { int i; for (i = 0; list[i].name != NULL; i++) if (list[i].num == num) return (list[i].name); return "Unknown"; } static struct w9968cf_symbolic_list camlist[] = { { W9968CF_MOD_GENERIC, "W996[87]CF JPEG USB Dual Mode Camera" }, { W9968CF_MOD_CLVBWGP, "Creative Labs Video Blaster WebCam Go Plus" }, /* Other cameras (having the same descriptors as Generic W996[87]CF) */ { W9968CF_MOD_ADPVDMA, "Aroma Digi Pen VGA Dual Mode ADG-5000" }, { W9986CF_MOD_AAU, "AVerMedia AVerTV USB" }, { W9968CF_MOD_CLVBWG, "Creative Labs Video Blaster WebCam Go" }, { W9968CF_MOD_LL, "Lebon LDC-035A" }, { W9968CF_MOD_EEEMC, "Ezonics EZ-802 EZMega Cam" }, { W9968CF_MOD_OOE, "OmniVision OV8610-EDE" }, { W9968CF_MOD_ODPVDMPC, "OPCOM Digi Pen VGA Dual Mode Pen Camera" }, { W9968CF_MOD_PDPII, "Pretec Digi Pen-II" }, { W9968CF_MOD_PDP480, "Pretec DigiPen-480" }, { -1, NULL } }; static struct w9968cf_symbolic_list senlist[] = { { CC_OV76BE, "OV76BE" }, { CC_OV7610, "OV7610" }, { CC_OV7620, "OV7620" }, { CC_OV7620AE, "OV7620AE" }, { CC_OV6620, "OV6620" }, { CC_OV6630, "OV6630" }, { CC_OV6630AE, "OV6630AE" }, { CC_OV6630AF, "OV6630AF" }, { -1, NULL } }; /* Video4Linux1 palettes */ static struct w9968cf_symbolic_list v4l1_plist[] = { { VIDEO_PALETTE_GREY, "GREY" }, { VIDEO_PALETTE_HI240, "HI240" }, { VIDEO_PALETTE_RGB565, "RGB565" }, { VIDEO_PALETTE_RGB24, "RGB24" }, { VIDEO_PALETTE_RGB32, "RGB32" }, { VIDEO_PALETTE_RGB555, "RGB555" }, { VIDEO_PALETTE_YUV422, "YUV422" }, { VIDEO_PALETTE_YUYV, "YUYV" }, { VIDEO_PALETTE_UYVY, "UYVY" }, { VIDEO_PALETTE_YUV420, "YUV420" }, { VIDEO_PALETTE_YUV411, "YUV411" }, { VIDEO_PALETTE_RAW, "RAW" }, { VIDEO_PALETTE_YUV422P, "YUV422P" }, { VIDEO_PALETTE_YUV411P, "YUV411P" }, { VIDEO_PALETTE_YUV420P, "YUV420P" }, { VIDEO_PALETTE_YUV410P, "YUV410P" }, { -1, NULL } }; /* Decoder error codes: */ static struct w9968cf_symbolic_list decoder_errlist[] = { { W9968CF_DEC_ERR_CORRUPTED_DATA, "Corrupted data" }, { W9968CF_DEC_ERR_BUF_OVERFLOW, "Buffer overflow" }, { W9968CF_DEC_ERR_NO_SOI, "SOI marker not found" }, { W9968CF_DEC_ERR_NO_SOF0, "SOF0 marker not found" }, { W9968CF_DEC_ERR_NO_SOS, "SOS marker not found" }, { W9968CF_DEC_ERR_NO_EOI, "EOI marker not found" }, { -1, NULL } }; /* URB error codes: */ static struct w9968cf_symbolic_list urb_errlist[] = { { -ENOMEM, "No memory for allocation of internal structures" }, { -ENOSPC, "The host controller's bandwidth is already consumed" }, { -ENOENT, "URB was canceled by unlink_urb" }, { -EXDEV, "ISO transfer only partially completed" }, { -EAGAIN, "Too match scheduled for the future" }, { -ENXIO, "URB already queued" }, { -EFBIG, "Too much ISO frames requested" }, { -ENOSR, "Buffer error (overrun)" }, { -EPIPE, "Specified endpoint is stalled (device not responding)"}, { -EOVERFLOW, "Babble (bad cable?)" }, { -EPROTO, "Bit-stuff error (bad cable?)" }, { -EILSEQ, "CRC/Timeout" }, { -ETIMEDOUT, "NAK (device does not respond)" }, { -1, NULL } }; /**************************************************************************** * Memory management functions * ****************************************************************************/ static void* rvmalloc(unsigned long size) { void* mem; unsigned long adr; size = PAGE_ALIGN(size); mem = vmalloc_32(size); if (!mem) return NULL; memset(mem, 0, size); /* Clear the ram out, no junk to the user */ adr = (unsigned long) mem; while (size > 0) { SetPageReserved(vmalloc_to_page((void *)adr)); adr += PAGE_SIZE; size -= PAGE_SIZE; } return mem; } static void rvfree(void* mem, unsigned long size) { unsigned long adr; if (!mem) return; adr = (unsigned long) mem; while ((long) size > 0) { ClearPageReserved(vmalloc_to_page((void *)adr)); adr += PAGE_SIZE; size -= PAGE_SIZE; } vfree(mem); } /*-------------------------------------------------------------------------- Deallocate previously allocated memory. --------------------------------------------------------------------------*/ static void w9968cf_deallocate_memory(struct w9968cf_device* cam) { u8 i; /* Free the isochronous transfer buffers */ for (i = 0; i < W9968CF_URBS; i++) { kfree(cam->transfer_buffer[i]); cam->transfer_buffer[i] = NULL; } /* Free temporary frame buffer */ if (cam->frame_tmp.buffer) { rvfree(cam->frame_tmp.buffer, cam->frame_tmp.size); cam->frame_tmp.buffer = NULL; } /* Free helper buffer */ if (cam->frame_vpp.buffer) { rvfree(cam->frame_vpp.buffer, cam->frame_vpp.size); cam->frame_vpp.buffer = NULL; } /* Free video frame buffers */ if (cam->frame[0].buffer) { rvfree(cam->frame[0].buffer, cam->nbuffers*cam->frame[0].size); cam->frame[0].buffer = NULL; } cam->nbuffers = 0; DBG(5, "Memory successfully deallocated") } /*-------------------------------------------------------------------------- Allocate memory buffers for USB transfers and video frames. This function is called by open() only. Return 0 on success, a negative number otherwise. --------------------------------------------------------------------------*/ static int w9968cf_allocate_memory(struct w9968cf_device* cam) { const u16 p_size = wMaxPacketSize[cam->altsetting-1]; void* buff = NULL; unsigned long hw_bufsize, vpp_bufsize; u8 i, bpp; /* NOTE: Deallocation is done elsewhere in case of error */ /* Calculate the max amount of raw data per frame from the device */ hw_bufsize = cam->maxwidth*cam->maxheight*2; /* Calculate the max buf. size needed for post-processing routines */ bpp = (w9968cf_vpp) ? 4 : 2; if (cam->upscaling) vpp_bufsize = max(W9968CF_MAX_WIDTH*W9968CF_MAX_HEIGHT*bpp, cam->maxwidth*cam->maxheight*bpp); else vpp_bufsize = cam->maxwidth*cam->maxheight*bpp; /* Allocate memory for the isochronous transfer buffers */ for (i = 0; i < W9968CF_URBS; i++) { if (!(cam->transfer_buffer[i] = kmalloc(W9968CF_ISO_PACKETS*p_size, GFP_KERNEL))) { DBG(1, "Couldn't allocate memory for the isochronous " "transfer buffers (%u bytes)", p_size * W9968CF_ISO_PACKETS) return -ENOMEM; } memset(cam->transfer_buffer[i], 0, W9968CF_ISO_PACKETS*p_size); } /* Allocate memory for the temporary frame buffer */ if (!(cam->frame_tmp.buffer = rvmalloc(hw_bufsize))) { DBG(1, "Couldn't allocate memory for the temporary " "video frame buffer (%lu bytes)", hw_bufsize) return -ENOMEM; } cam->frame_tmp.size = hw_bufsize; cam->frame_tmp.number = -1; /* Allocate memory for the helper buffer */ if (w9968cf_vpp) { if (!(cam->frame_vpp.buffer = rvmalloc(vpp_bufsize))) { DBG(1, "Couldn't allocate memory for the helper buffer" " (%lu bytes)", vpp_bufsize) return -ENOMEM; } cam->frame_vpp.size = vpp_bufsize; } else cam->frame_vpp.buffer = NULL; /* Allocate memory for video frame buffers */ cam->nbuffers = cam->max_buffers; while (cam->nbuffers >= 2) { if ((buff = rvmalloc(cam->nbuffers * vpp_bufsize))) break; else cam->nbuffers--; } if (!buff) { DBG(1, "Couldn't allocate memory for the video frame buffers") cam->nbuffers = 0; return -ENOMEM; } if (cam->nbuffers != cam->max_buffers) DBG(2, "Couldn't allocate memory for %u video frame buffers. " "Only memory for %u buffers has been allocated", cam->max_buffers, cam->nbuffers) for (i = 0; i < cam->nbuffers; i++) { cam->frame[i].buffer = buff + i*vpp_bufsize; cam->frame[i].size = vpp_bufsize; cam->frame[i].number = i; /* Circular list */ if (i != cam->nbuffers-1) cam->frame[i].next = &cam->frame[i+1]; else cam->frame[i].next = &cam->frame[0]; cam->frame[i].status = F_UNUSED; } DBG(5, "Memory successfully allocated") return 0; } /**************************************************************************** * USB-specific functions * ****************************************************************************/ /*-------------------------------------------------------------------------- This is an handler function which is called after the URBs are completed. It collects multiple data packets coming from the camera by putting them into frame buffers: one or more zero data length data packets are used to mark the end of a video frame; the first non-zero data packet is the start of the next video frame; if an error is encountered in a packet, the entire video frame is discarded and grabbed again. If there are no requested frames in the FIFO list, packets are collected into a temporary buffer. --------------------------------------------------------------------------*/ static void w9968cf_urb_complete(struct urb *urb, struct pt_regs *regs) { struct w9968cf_device* cam = (struct w9968cf_device*)urb->context; struct w9968cf_frame_t** f; unsigned int len, status; void* pos; u8 i; int err = 0; if ((!cam->streaming) || cam->disconnected) { DBG(4, "Got interrupt, but not streaming") return; } /* "(*f)" will be used instead of "cam->frame_current" */ f = &cam->frame_current; /* If a frame has been requested and we are grabbing into the temporary frame, we'll switch to that requested frame */ if ((*f) == &cam->frame_tmp && *cam->requested_frame) { if (cam->frame_tmp.status == F_GRABBING) { w9968cf_pop_frame(cam, &cam->frame_current); (*f)->status = F_GRABBING; (*f)->length = cam->frame_tmp.length; memcpy((*f)->buffer, cam->frame_tmp.buffer, (*f)->length); DBG(6, "Switched from temp. frame to frame #%d", (*f)->number) } } for (i = 0; i < urb->number_of_packets; i++) { len = urb->iso_frame_desc[i].actual_length; status = urb->iso_frame_desc[i].status; pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer; if (status && len != 0) { DBG(4, "URB failed, error in data packet " "(error #%u, %s)", status, symbolic(urb_errlist, status)) (*f)->status = F_ERROR; continue; } if (len) { /* start of frame */ if ((*f)->status == F_UNUSED) { (*f)->status = F_GRABBING; (*f)->length = 0; } /* Buffer overflows shouldn't happen, however...*/ if ((*f)->length + len > (*f)->size) { DBG(4, "Buffer overflow: bad data packets") (*f)->status = F_ERROR; } if ((*f)->status == F_GRABBING) { memcpy((*f)->buffer + (*f)->length, pos, len); (*f)->length += len; } } else if ((*f)->status == F_GRABBING) { /* end of frame */ DBG(6, "Frame #%d successfully grabbed", (*f)->number) if (cam->vpp_flag & VPP_DECOMPRESSION) { err = w9968cf_vpp->check_headers((*f)->buffer, (*f)->length); if (err) { DBG(4, "Skip corrupted frame: %s", symbolic(decoder_errlist, err)) (*f)->status = F_UNUSED; continue; /* grab this frame again */ } } (*f)->status = F_READY; (*f)->queued = 0; /* Take a pointer to the new frame from the FIFO list. If the list is empty,we'll use the temporary frame*/ if (*cam->requested_frame) w9968cf_pop_frame(cam, &cam->frame_current); else { cam->frame_current = &cam->frame_tmp; (*f)->status = F_UNUSED; } } else if ((*f)->status == F_ERROR) (*f)->status = F_UNUSED; /* grab it again */ PDBGG("Frame length %lu | pack.#%u | pack.len. %u | state %d", (unsigned long)(*f)->length, i, len, (*f)->status) } /* end for */ /* Resubmit this URB */ urb->dev = cam->usbdev; urb->status = 0; spin_lock(&cam->urb_lock); if (cam->streaming) if ((err = usb_submit_urb(urb, GFP_ATOMIC))) { cam->misconfigured = 1; DBG(1, "Couldn't resubmit the URB: error %d, %s", err, symbolic(urb_errlist, err)) } spin_unlock(&cam->urb_lock); /* Wake up the user process */ wake_up_interruptible(&cam->wait_queue); } /*--------------------------------------------------------------------------- Setup the URB structures for the isochronous transfer. Submit the URBs so that the data transfer begins. Return 0 on success, a negative number otherwise. ---------------------------------------------------------------------------*/ static int w9968cf_start_transfer(struct w9968cf_device* cam) { struct usb_device *udev = cam->usbdev; struct urb* urb; const u16 p_size = wMaxPacketSize[cam->altsetting-1]; u16 w, h, d; int vidcapt; u32 t_size; int err = 0; s8 i, j; for (i = 0; i < W9968CF_URBS; i++) { urb = usb_alloc_urb(W9968CF_ISO_PACKETS, GFP_KERNEL); cam->urb[i] = urb; if (!urb) { for (j = 0; j < i; j++) usb_free_urb(cam->urb[j]); DBG(1, "Couldn't allocate the URB structures") return -ENOMEM; } urb->dev = udev; urb->context = (void*)cam; urb->pipe = usb_rcvisocpipe(udev, 1); urb->transfer_flags = URB_ISO_ASAP; urb->number_of_packets = W9968CF_ISO_PACKETS; urb->complete = w9968cf_urb_complete; urb->transfer_buffer = cam->transfer_buffer[i]; urb->transfer_buffer_length = p_size*W9968CF_ISO_PACKETS; urb->interval = 1; for (j = 0; j < W9968CF_ISO_PACKETS; j++) { urb->iso_frame_desc[j].offset = p_size*j; urb->iso_frame_desc[j].length = p_size; } } /* Transfer size per frame, in WORD ! */ d = cam->hw_depth; w = cam->hw_width; h = cam->hw_height; t_size = (w*h*d)/16; err = w9968cf_write_reg(cam, 0xbf17, 0x00); /* reset everything */ err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* normal operation */ /* Transfer size */ err += w9968cf_write_reg(cam, t_size & 0xffff, 0x3d); /* low bits */ err += w9968cf_write_reg(cam, t_size >> 16, 0x3e); /* high bits */ if (cam->vpp_flag & VPP_DECOMPRESSION) err += w9968cf_upload_quantizationtables(cam); vidcapt = w9968cf_read_reg(cam, 0x16); /* read picture settings */ err += w9968cf_write_reg(cam, vidcapt|0x8000, 0x16); /* capt. enable */ err += usb_set_interface(udev, 0, cam->altsetting); err += w9968cf_write_reg(cam, 0x8a05, 0x3c); /* USB FIFO enable */ if (err || (vidcapt < 0)) { for (i = 0; i < W9968CF_URBS; i++) usb_free_urb(cam->urb[i]); DBG(1, "Couldn't tell the camera to start the data transfer") return err; } w9968cf_init_framelist(cam); /* Begin to grab into the temporary buffer */ cam->frame_tmp.status = F_UNUSED; cam->frame_tmp.queued = 0; cam->frame_current = &cam->frame_tmp; if (!(cam->vpp_flag & VPP_DECOMPRESSION)) DBG(5, "Isochronous transfer size: %lu bytes/frame", (unsigned long)t_size*2) DBG(5, "Starting the isochronous transfer...") cam->streaming = 1; /* Submit the URBs */ for (i = 0; i < W9968CF_URBS; i++) { err = usb_submit_urb(cam->urb[i], GFP_KERNEL); if (err) { cam->streaming = 0; for (j = i-1; j >= 0; j--) { usb_kill_urb(cam->urb[j]); usb_free_urb(cam->urb[j]); } DBG(1, "Couldn't send a transfer request to the " "USB core (error #%d, %s)", err, symbolic(urb_errlist, err)) return err; } } return 0; } /*-------------------------------------------------------------------------- Stop the isochronous transfer and set alternate setting to 0 (0Mb/s). Return 0 on success, a negative number otherwise. --------------------------------------------------------------------------*/ static int w9968cf_stop_transfer(struct w9968cf_device* cam) { struct usb_device *udev = cam->usbdev; unsigned long lock_flags; int err = 0; s8 i; if (!cam->streaming) return 0; /* This avoids race conditions with usb_submit_urb() in the URB completition handler */ spin_lock_irqsave(&cam->urb_lock, lock_flags); cam->streaming = 0; spin_unlock_irqrestore(&cam->urb_lock, lock_flags); for (i = W9968CF_URBS-1; i >= 0; i--) if (cam->urb[i]) { usb_kill_urb(cam->urb[i]); usb_free_urb(cam->urb[i]); cam->urb[i] = NULL; } if (cam->disconnected) goto exit; err = w9968cf_write_reg(cam, 0x0a05, 0x3c); /* stop USB transfer */ err += usb_set_interface(udev, 0, 0); /* 0 Mb/s */ err += w9968cf_write_reg(cam, 0x0000, 0x39); /* disable JPEG encoder */ err += w9968cf_write_reg(cam, 0x0000, 0x16); /* stop video capture */ if (err) { DBG(2, "Failed to tell the camera to stop the isochronous " "transfer. However this is not a critical error.") return -EIO; } exit: DBG(5, "Isochronous transfer stopped") return 0; } /*-------------------------------------------------------------------------- Write a W9968CF register. Return 0 on success, -1 otherwise. --------------------------------------------------------------------------*/ static int w9968cf_write_reg(struct w9968cf_device* cam, u16 value, u16 index) { struct usb_device* udev = cam->usbdev; int res; res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0, USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, value, index, NULL, 0, W9968CF_USB_CTRL_TIMEOUT); if (res < 0) DBG(4, "Failed to write a register " "(value 0x%04X, index 0x%02X, error #%d, %s)", value, index, res, symbolic(urb_errlist, res)) return (res >= 0) ? 0 : -1; } /*-------------------------------------------------------------------------- Read a W9968CF register. Return the register value on success, -1 otherwise. --------------------------------------------------------------------------*/ static int w9968cf_read_reg(struct w9968cf_device* cam, u16 index) { struct usb_device* udev = cam->usbdev; u16* buff = cam->control_buffer; int res; res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 1, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, index, buff, 2, W9968CF_USB_CTRL_TIMEOUT); if (res < 0) DBG(4, "Failed to read a register " "(index 0x%02X, error #%d, %s)", index, res, symbolic(urb_errlist, res)) return (res >= 0) ? (int)(*buff) : -1; } /*-------------------------------------------------------------------------- Write 64-bit data to the fast serial bus registers. Return 0 on success, -1 otherwise. --------------------------------------------------------------------------*/ static int w9968cf_write_fsb(struct w9968cf_device* cam, u16* data) { struct usb_device* udev = cam->usbdev; u16 value; int res; value = *data++; res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0, USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, value, 0x06, data, 6, W9968CF_USB_CTRL_TIMEOUT); if (res < 0) DBG(4, "Failed to write the FSB registers " "(error #%d, %s)", res, symbolic(urb_errlist, res)) return (res >= 0) ? 0 : -1; } /*-------------------------------------------------------------------------- Write data to the serial bus control register. Return 0 on success, a negative number otherwise. --------------------------------------------------------------------------*/ static int w9968cf_write_sb(struct w9968cf_device* cam, u16 value) { int err = 0; err = w9968cf_write_reg(cam, value, 0x01); udelay(W9968CF_I2C_BUS_DELAY); return err; } /*-------------------------------------------------------------------------- Read data from the serial bus control register. Return 0 on success, a negative number otherwise. --------------------------------------------------------------------------*/ static int w9968cf_read_sb(struct w9968cf_device* cam) { int v = 0; v = w9968cf_read_reg(cam, 0x01); udelay(W9968CF_I2C_BUS_DELAY); return v; } /*-------------------------------------------------------------------------- Upload quantization tables for the JPEG compression. This function is called by w9968cf_start_transfer(). Return 0 on success, a negative number otherwise. --------------------------------------------------------------------------*/ static int w9968cf_upload_quantizationtables(struct w9968cf_device* cam) { u16 a, b; int err = 0, i, j; err += w9968cf_write_reg(cam, 0x0010, 0x39); /* JPEG clock enable */ for (i = 0, j = 0; i < 32; i++, j += 2) { a = Y_QUANTABLE[j] | ((unsigned)(Y_QUANTABLE[j+1]) << 8); b = UV_QUANTABLE[j] | ((unsigned)(UV_QUANTABLE[j+1]) << 8); err += w9968cf_write_reg(cam, a, 0x40+i); err += w9968cf_write_reg(cam, b, 0x60+i); } err += w9968cf_write_reg(cam, 0x0012, 0x39); /* JPEG encoder enable */ return err; } /**************************************************************************** * Low-level I2C I/O functions. * * The adapter supports the following I2C transfer functions: * * i2c_adap_fastwrite_byte_data() (at 400 kHz bit frequency only) * * i2c_adap_read_byte_data() * * i2c_adap_read_byte() * ****************************************************************************/ static int w9968cf_smbus_start(struct w9968cf_device* cam) { int err = 0; err += w9968cf_write_sb(cam, 0x0011); /* SDE=1, SDA=0, SCL=1 */ err += w9968cf_write_sb(cam, 0x0010); /* SDE=1, SDA=0, SCL=0 */ return err; } static int w9968cf_smbus_stop(struct w9968cf_device* cam) { int err = 0; err += w9968cf_write_sb(cam, 0x0011); /* SDE=1, SDA=0, SCL=1 */ err += w9968cf_write_sb(cam, 0x0013); /* SDE=1, SDA=1, SCL=1 */ return err; } static int w9968cf_smbus_write_byte(struct w9968cf_device* cam, u8 v) { u8 bit; int err = 0, sda; for (bit = 0 ; bit < 8 ; bit++) { sda = (v & 0x80) ? 2 : 0; v <<= 1; /* SDE=1, SDA=sda, SCL=0 */ err += w9968cf_write_sb(cam, 0x10 | sda); /* SDE=1, SDA=sda, SCL=1 */ err += w9968cf_write_sb(cam, 0x11 | sda); /* SDE=1, SDA=sda, SCL=0 */ err += w9968cf_write_sb(cam, 0x10 | sda); } return err; } static int w9968cf_smbus_read_byte(struct w9968cf_device* cam, u8* v) { u8 bit; int err = 0; *v = 0; for (bit = 0 ; bit < 8 ; bit++) { *v <<= 1; err += w9968cf_write_sb(cam, 0x0013); *v |= (w9968cf_read_sb(cam) & 0x0008) ? 1 : 0; err += w9968cf_write_sb(cam, 0x0012); } return err; } static int w9968cf_smbus_write_ack(struct w9968cf_device* cam) { int err = 0; err += w9968cf_write_sb(cam, 0x0010); /* SDE=1, SDA=0, SCL=0 */ err += w9968cf_write_sb(cam, 0x0011); /* SDE=1, SDA=0, SCL=1 */ err += w9968cf_write_sb(cam, 0x0010); /* SDE=1, SDA=0, SCL=0 */ return err; } static int w9968cf_smbus_read_ack(struct w9968cf_device* cam) { int err = 0, sda; err += w9968cf_write_sb(cam, 0x0013); /* SDE=1, SDA=1, SCL=1 */ sda = (w9968cf_read_sb(cam) & 0x08) ? 1 : 0; /* sda = SDA */ err += w9968cf_write_sb(cam, 0x0012); /* SDE=1, SDA=1, SCL=0 */ if (sda < 0) err += sda; if (sda == 1) { DBG(6, "Couldn't receive the ACK") err += -1; } return err; } /* This seems to refresh the communication through the serial bus */ static int w9968cf_smbus_refresh_bus(struct w9968cf_device* cam) { int err = 0, j; for (j = 1; j <= 10; j++) { err = w9968cf_write_reg(cam, 0x0020, 0x01); err += w9968cf_write_reg(cam, 0x0000, 0x01); if (err) break; } return err; } /* SMBus protocol: S Addr Wr [A] Subaddr [A] Value [A] P */ static int w9968cf_i2c_adap_fastwrite_byte_data(struct w9968cf_device* cam, u16 address, u8 subaddress,u8 value) { u16* data = cam->data_buffer; int err = 0; err += w9968cf_smbus_refresh_bus(cam); /* Enable SBUS outputs */ err += w9968cf_write_sb(cam, 0x0020); data[0] = 0x082f | ((address & 0x80) ? 0x1500 : 0x0); data[0] |= (address & 0x40) ? 0x4000 : 0x0; data[1] = 0x2082 | ((address & 0x40) ? 0x0005 : 0x0); data[1] |= (address & 0x20) ? 0x0150 : 0x0; data[1] |= (address & 0x10) ? 0x5400 : 0x0; data[2] = 0x8208 | ((address & 0x08) ? 0x0015 : 0x0); data[2] |= (address & 0x04) ? 0x0540 : 0x0; data[2] |= (address & 0x02) ? 0x5000 : 0x0; data[3] = 0x1d20 | ((address & 0x02) ? 0x0001 : 0x0); data[3] |= (address & 0x01) ? 0x0054 : 0x0; err += w9968cf_write_fsb(cam, data); data[0] = 0x8208 | ((subaddress & 0x80) ? 0x0015 : 0x0); data[0] |= (subaddress & 0x40) ? 0x0540 : 0x0; data[0] |= (subaddress & 0x20) ? 0x5000 : 0x0; data[1] = 0x0820 | ((subaddress & 0x20) ? 0x0001 : 0x0); data[1] |= (subaddress & 0x10) ? 0x0054 : 0x0; data[1] |= (subaddress & 0x08) ? 0x1500 : 0x0; data[1] |= (subaddress & 0x04) ? 0x4000 : 0x0; data[2] = 0x2082 | ((subaddress & 0x04) ? 0x0005 : 0x0); data[2] |= (subaddress & 0x02) ? 0x0150 : 0x0; data[2] |= (subaddress & 0x01) ? 0x5400 : 0x0; data[3] = 0x001d; err += w9968cf_write_fsb(cam, data); data[0] = 0x8208 | ((value & 0x80) ? 0x0015 : 0x0); data[0] |= (value & 0x40) ? 0x0540 : 0x0; data[0] |= (value & 0x20) ? 0x5000 : 0x0; data[1] = 0x0820 | ((value & 0x20) ? 0x0001 : 0x0); data[1] |= (value & 0x10) ? 0x0054 : 0x0; data[1] |= (value & 0x08) ? 0x1500 : 0x0; data[1] |= (value & 0x04) ? 0x4000 : 0x0; data[2] = 0x2082 | ((value & 0x04) ? 0x0005 : 0x0); data[2] |= (value & 0x02) ? 0x0150 : 0x0; data[2] |= (value & 0x01) ? 0x5400 : 0x0; data[3] = 0xfe1d; err += w9968cf_write_fsb(cam, data); /* Disable SBUS outputs */ err += w9968cf_write_sb(cam, 0x0000); if (!err) DBG(5, "I2C write byte data done, addr.0x%04X, subaddr.0x%02X " "value 0x%02X", address, subaddress, value) else DBG(5, "I2C write byte data failed, addr.0x%04X, " "subaddr.0x%02X, value 0x%02X", address, subaddress, value) return err; } /* SMBus protocol: S Addr Wr [A] Subaddr [A] P S Addr+1 Rd [A] [Value] NA P */ static int w9968cf_i2c_adap_read_byte_data(struct w9968cf_device* cam, u16 address, u8 subaddress, u8* value) { int err = 0; /* Serial data enable */ err += w9968cf_write_sb(cam, 0x0013); /* don't change ! */ err += w9968cf_smbus_start(cam); err += w9968cf_smbus_write_byte(cam, address); err += w9968cf_smbus_read_ack(cam); err += w9968cf_smbus_write_byte(cam, subaddress); err += w9968cf_smbus_read_ack(cam); err += w9968cf_smbus_stop(cam); err += w9968cf_smbus_start(cam); err += w9968cf_smbus_write_byte(cam, address + 1); err += w9968cf_smbus_read_ack(cam); err += w9968cf_smbus_read_byte(cam, value); err += w9968cf_smbus_write_ack(cam); err += w9968cf_smbus_stop(cam); /* Serial data disable */ err += w9968cf_write_sb(cam, 0x0000); if (!err) DBG(5, "I2C read byte data done, addr.0x%04X, " "subaddr.0x%02X, value 0x%02X", address, subaddress, *value) else DBG(5, "I2C read byte data failed, addr.0x%04X, " "subaddr.0x%02X, wrong value 0x%02X", address, subaddress, *value) return err; } /* SMBus protocol: S Addr+1 Rd [A] [Value] NA P */ static int w9968cf_i2c_adap_read_byte(struct w9968cf_device* cam, u16 address, u8* value) { int err = 0; /* Serial data enable */ err += w9968cf_write_sb(cam, 0x0013); err += w9968cf_smbus_start(cam); err += w9968cf_smbus_write_byte(cam, address + 1); err += w9968cf_smbus_read_ack(cam); err += w9968cf_smbus_read_byte(cam, value); err += w9968cf_smbus_write_ack(cam); err += w9968cf_smbus_stop(cam); /* Serial data disable */ err += w9968cf_write_sb(cam, 0x0000); if (!err) DBG(5, "I2C read byte done, addr.0x%04X, " "value 0x%02X", address, *value) else DBG(5, "I2C read byte failed, addr.0x%04X, " "wrong value 0x%02X", address, *value) return err; } /* SMBus protocol: S Addr Wr [A] Value [A] P */ static int w9968cf_i2c_adap_write_byte(struct w9968cf_device* cam, u16 address, u8 value) { DBG(4, "i2c_write_byte() is an unsupported transfer mode") return -EINVAL; } /**************************************************************************** * I2C interface to kernel * ****************************************************************************/ static int w9968cf_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data) { struct w9968cf_device* cam = i2c_get_adapdata(adapter); u8 i; int err = 0; switch (addr) { case OV6xx0_SID: case OV7xx0_SID: break; default: DBG(4, "Rejected slave ID 0x%04X", addr) return -EINVAL; } if (size == I2C_SMBUS_BYTE) { /* Why addr <<= 1? See OVXXX0_SID defines in ovcamchip.h */ addr <<= 1; if (read_write == I2C_SMBUS_WRITE) err = w9968cf_i2c_adap_write_byte(cam, addr, command); else if (read_write == I2C_SMBUS_READ) err = w9968cf_i2c_adap_read_byte(cam,addr,&data->byte); } else if (size == I2C_SMBUS_BYTE_DATA) { addr <<= 1; if (read_write == I2C_SMBUS_WRITE) err = w9968cf_i2c_adap_fastwrite_byte_data(cam, addr, command, data->byte); else if (read_write == I2C_SMBUS_READ) { for (i = 1; i <= W9968CF_I2C_RW_RETRIES; i++) { err = w9968cf_i2c_adap_read_byte_data(cam,addr, command, &data->byte); if (err) { if (w9968cf_smbus_refresh_bus(cam)) { err = -EIO; break; } } else break; } } else return -EINVAL; } else { DBG(4, "Unsupported I2C transfer mode (%d)", size) return -EINVAL; } return err; } static u32 w9968cf_i2c_func(struct i2c_adapter* adap) { return I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_READ_BYTE_DATA | I2C_FUNC_SMBUS_WRITE_BYTE_DATA; } static int w9968cf_i2c_attach_inform(struct i2c_client* client) { struct w9968cf_device* cam = i2c_get_adapdata(client->adapter); int id = client->driver->id, err = 0; if (id == I2C_DRIVERID_OVCAMCHIP) { cam->sensor_client = client; err = w9968cf_sensor_init(cam); if (err) { cam->sensor_client = NULL; return err; } } else { DBG(4, "Rejected client [%s] with driver [%s]", client->name, client->driver->name) return -EINVAL; } DBG(5, "I2C attach client [%s] with driver [%s]", client->name, client->driver->name) return 0; } static int w9968cf_i2c_detach_inform(struct i2c_client* client) { struct w9968cf_device* cam = i2c_get_adapdata(client->adapter); if (cam->sensor_client == client) cam->sensor_client = NULL; DBG(5, "I2C detach client [%s]", client->name) return 0; } static int w9968cf_i2c_control(struct i2c_adapter* adapter, unsigned int cmd, unsigned long arg) { return 0; } static int w9968cf_i2c_init(struct w9968cf_device* cam) { int err = 0; static struct i2c_algorithm algo = { .smbus_xfer = w9968cf_i2c_smbus_xfer, .algo_control = w9968cf_i2c_control, .functionality = w9968cf_i2c_func, }; static struct i2c_adapter adap = { .id = I2C_HW_SMBUS_W9968CF, .class = I2C_CLASS_CAM_DIGITAL, .owner = THIS_MODULE, .client_register = w9968cf_i2c_attach_inform, .client_unregister = w9968cf_i2c_detach_inform, .algo = &algo, }; memcpy(&cam->i2c_adapter, &adap, sizeof(struct i2c_adapter)); strcpy(cam->i2c_adapter.name, "w9968cf"); i2c_set_adapdata(&cam->i2c_adapter, cam); DBG(6, "Registering I2C adapter with kernel...") err = i2c_add_adapter(&cam->i2c_adapter); if (err) DBG(1, "Failed to register the I2C adapter") else DBG(5, "I2C adapter registered") return err; } /**************************************************************************** * Helper functions * ****************************************************************************/ /*-------------------------------------------------------------------------- Turn on the LED on some webcams. A beep should be heard too. Return 0 on success, a negative number otherwise. --------------------------------------------------------------------------*/ static int w9968cf_turn_on_led(struct w9968cf_device* cam) { int err = 0; err += w9968cf_write_reg(cam, 0xff00, 0x00); /* power-down */ err += w9968cf_write_reg(cam, 0xbf17, 0x00); /* reset everything */ err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* normal operation */ err += w9968cf_write_reg(cam, 0x0010, 0x01); /* serial bus, SDS high */ err += w9968cf_write_reg(cam, 0x0000, 0x01); /* serial bus, SDS low */ err += w9968cf_write_reg(cam, 0x0010, 0x01); /* ..high 'beep-beep' */ if (err) DBG(2, "Couldn't turn on the LED") DBG(5, "LED turned on") return err; } /*-------------------------------------------------------------------------- Write some registers for the device initialization. This function is called once on open(). Return 0 on success, a negative number otherwise. --------------------------------------------------------------------------*/ static int w9968cf_init_chip(struct w9968cf_device* cam) { unsigned long hw_bufsize = cam->maxwidth*cam->maxheight*2, y0 = 0x0000, u0 = y0 + hw_bufsize/2, v0 = u0 + hw_bufsize/4, y1 = v0 + hw_bufsize/4, u1 = y1 + hw_bufsize/2, v1 = u1 + hw_bufsize/4; int err = 0; err += w9968cf_write_reg(cam, 0xff00, 0x00); /* power off */ err += w9968cf_write_reg(cam, 0xbf10, 0x00); /* power on */ err += w9968cf_write_reg(cam, 0x405d, 0x03); /* DRAM timings */ err += w9968cf_write_reg(cam, 0x0030, 0x04); /* SDRAM timings */ err += w9968cf_write_reg(cam, y0 & 0xffff, 0x20); /* Y buf.0, low */ err += w9968cf_write_reg(cam, y0 >> 16, 0x21); /* Y buf.0, high */ err += w9968cf_write_reg(cam, u0 & 0xffff, 0x24); /* U buf.0, low */ err += w9968cf_write_reg(cam, u0 >> 16, 0x25); /* U buf.0, high */ err += w9968cf_write_reg(cam, v0 & 0xffff, 0x28); /* V buf.0, low */ err += w9968cf_write_reg(cam, v0 >> 16, 0x29); /* V buf.0, high */ err += w9968cf_write_reg(cam, y1 & 0xffff, 0x22); /* Y buf.1, low */ err += w9968cf_write_reg(cam, y1 >> 16, 0x23); /* Y buf.1, high */ err += w9968cf_write_reg(cam, u1 & 0xffff, 0x26); /* U buf.1, low */ err += w9968cf_write_reg(cam, u1 >> 16, 0x27); /* U buf.1, high */ err += w9968cf_write_reg(cam, v1 & 0xffff, 0x2a); /* V buf.1, low */ err += w9968cf_write_reg(cam, v1 >> 16, 0x2b); /* V buf.1, high */ err += w9968cf_write_reg(cam, y1 & 0xffff, 0x32); /* JPEG buf 0 low */ err += w9968cf_write_reg(cam, y1 >> 16, 0x33); /* JPEG buf 0 high */ err += w9968cf_write_reg(cam, y1 & 0xffff, 0x34); /* JPEG buf 1 low */ err += w9968cf_write_reg(cam, y1 >> 16, 0x35); /* JPEG bug 1 high */ err += w9968cf_write_reg(cam, 0x0000, 0x36);/* JPEG restart interval */ err += w9968cf_write_reg(cam, 0x0804, 0x37);/*JPEG VLE FIFO threshold*/ err += w9968cf_write_reg(cam, 0x0000, 0x38);/* disable hw up-scaling */ err += w9968cf_write_reg(cam, 0x0000, 0x3f); /* JPEG/MCTL test data */ err += w9968cf_set_picture(cam, cam->picture); /* this before */ err += w9968cf_set_window(cam, cam->window); if (err) DBG(1, "Chip initialization failed") else DBG(5, "Chip successfully initialized") return err; } /*-------------------------------------------------------------------------- Return non-zero if the palette is supported, 0 otherwise. --------------------------------------------------------------------------*/ static inline u16 w9968cf_valid_palette(u16 palette) { u8 i = 0; while (w9968cf_formatlist[i].palette != 0) { if (palette == w9968cf_formatlist[i].palette) return palette; i++; } return 0; } /*-------------------------------------------------------------------------- Return the depth corresponding to the given palette. Palette _must_ be supported ! --------------------------------------------------------------------------*/ static inline u16 w9968cf_valid_depth(u16 palette) { u8 i=0; while (w9968cf_formatlist[i].palette != palette) i++; return w9968cf_formatlist[i].depth; } /*-------------------------------------------------------------------------- Return non-zero if the format requires decompression, 0 otherwise. --------------------------------------------------------------------------*/ static inline u8 w9968cf_need_decompression(u16 palette) { u8 i = 0; while (w9968cf_formatlist[i].palette != 0) { if (palette == w9968cf_formatlist[i].palette) return w9968cf_formatlist[i].compression; i++; } return 0; } /*-------------------------------------------------------------------------- Change the picture settings of the camera. Return 0 on success, a negative number otherwise. --------------------------------------------------------------------------*/ static int w9968cf_set_picture(struct w9968cf_device* cam, struct video_picture pict) { u16 fmt, hw_depth, hw_palette, reg_v = 0x0000; int err = 0; /* Make sure we are using a valid depth */ pict.depth = w9968cf_valid_depth(pict.palette); fmt = pict.palette; hw_depth = pict.depth; /* depth used by the winbond chip */ hw_palette = pict.palette; /* palette used by the winbond chip */ /* VS & HS polarities */ reg_v = (cam->vs_polarity << 12) | (cam->hs_polarity << 11); switch (fmt) { case VIDEO_PALETTE_UYVY: reg_v |= 0x0000; cam->vpp_flag = VPP_NONE; break; case VIDEO_PALETTE_YUV422P: reg_v |= 0x0002; cam->vpp_flag = VPP_DECOMPRESSION; break; case VIDEO_PALETTE_YUV420: case VIDEO_PALETTE_YUV420P: reg_v |= 0x0003; cam->vpp_flag = VPP_DECOMPRESSION; break; case VIDEO_PALETTE_YUYV: case VIDEO_PALETTE_YUV422: reg_v |= 0x0000; cam->vpp_flag = VPP_SWAP_YUV_BYTES; hw_palette = VIDEO_PALETTE_UYVY; break; /* Original video is used instead of RGBX palettes. Software conversion later. */ case VIDEO_PALETTE_GREY: case VIDEO_PALETTE_RGB555: case VIDEO_PALETTE_RGB565: case VIDEO_PALETTE_RGB24: case VIDEO_PALETTE_RGB32: reg_v |= 0x0000; /* UYVY 16 bit is used */ hw_depth = 16; hw_palette = VIDEO_PALETTE_UYVY; cam->vpp_flag = VPP_UYVY_TO_RGBX; break; } /* NOTE: due to memory issues, it is better to disable the hardware double buffering during compression */ if (cam->double_buffer && !(cam->vpp_flag & VPP_DECOMPRESSION)) reg_v |= 0x0080; if (cam->clamping) reg_v |= 0x0020; if (cam->filter_type == 1) reg_v |= 0x0008; else if (cam->filter_type == 2) reg_v |= 0x000c; if ((err = w9968cf_write_reg(cam, reg_v, 0x16))) goto error; if ((err = w9968cf_sensor_update_picture(cam, pict))) goto error; /* If all went well, update the device data structure */ memcpy(&cam->picture, &pict, sizeof(pict)); cam->hw_depth