diff options
author | rmcc@localhost.localdomain <rmcc@localhost.localdomain> | 2006-05-18 16:06:06 +0100 |
---|---|---|
committer | rmcc@localhost.localdomain <rmcc@localhost.localdomain> | 2006-05-18 16:06:06 +0100 |
commit | aad2e973e44590acd4c1b55dd32eadad842e7fdb (patch) | |
tree | de152190bd763eb849cf2952cfb468169f3881ba /linux/drivers/media/video/pvrusb2 | |
parent | f637ab4e875bd28b68bbd4d776c2071683050b5c (diff) | |
parent | 122bd90fa6f72e1366146662e7fb9a6581aab68e (diff) | |
download | mediapointer-dvb-s2-aad2e973e44590acd4c1b55dd32eadad842e7fdb.tar.gz mediapointer-dvb-s2-aad2e973e44590acd4c1b55dd32eadad842e7fdb.tar.bz2 |
merge: from master
From: Ricardo Cerqueira <v4l@cerqueira.org>
merging master changes
Signed-off-by: Ricardo Cerqueira <v4l@cerqueira.org>
Diffstat (limited to 'linux/drivers/media/video/pvrusb2')
32 files changed, 3620 insertions, 1554 deletions
diff --git a/linux/drivers/media/video/pvrusb2/Kconfig b/linux/drivers/media/video/pvrusb2/Kconfig index 9824ad267..207efa6c0 100644 --- a/linux/drivers/media/video/pvrusb2/Kconfig +++ b/linux/drivers/media/video/pvrusb2/Kconfig @@ -1,6 +1,6 @@ config VIDEO_PVRUSB2 tristate "Hauppauge WinTV-PVR USB2 support" - depends on VIDEO_DEV && USB && I2C && EXPERIMENTAL + depends on VIDEO_V4L1 && USB && I2C && EXPERIMENTAL select FW_LOADER select VIDEO_TUNER select VIDEO_TVEEPROM @@ -12,3 +12,50 @@ config VIDEO_PVRUSB2 To compile this driver as a module, choose M here: the module will be called pvrusb2 + +config VIDEO_PVRUSB2_24XXX + bool "Hauppauge WinTV-PVR USB2 support for 24xxx model series" + depends on VIDEO_PVRUSB2 && EXPERIMENTAL + select VIDEO_CX25840 + select VIDEO_WM8775 + ---help--- + This option enables inclusion of additional logic to operate + newer WinTV-PVR USB2 devices whose model number is of the + form "24xxx" (leading prefix of "24" followed by 3 digits). + To see if you may need this option, examine the white + sticker on the underside of your device. Enabling this + option will not harm support for older devices, however it + is a separate option because of the experimental nature of + this new feature. + + If you are in doubt, say N. + + Note: This feature is _very_ experimental. You have been + warned. + +config VIDEO_PVRUSB2_SYSFS + bool "pvrusb2 sysfs support" + default y + depends on VIDEO_PVRUSB2 && SYSFS && EXPERIMENTAL + ---help--- + This option enables the operation of a sysfs based + interface for query and control of the pvrusb2 driver. + + This is not generally needed for v4l applications, + although certain applications are optimized to take + advantage of this feature. + + If you are in doubt, say Y. + + Note: This feature is experimental and subject to change. + +config VIDEO_PVRUSB2_DEBUGIFC + bool "pvrusb2 debug interface" + depends on VIDEO_PVRUSB2_SYSFS + ---help--- + This option enables the inclusion of a debug interface + in the pvrusb2 driver, hosted through sysfs. + + You do not need to select this option unless you plan + on debugging the driver or performing a manual firmware + extraction. diff --git a/linux/drivers/media/video/pvrusb2/Makefile b/linux/drivers/media/video/pvrusb2/Makefile index c83742fb1..fed603ad0 100644 --- a/linux/drivers/media/video/pvrusb2/Makefile +++ b/linux/drivers/media/video/pvrusb2/Makefile @@ -1,11 +1,18 @@ +obj-pvrusb2-sysfs-$(CONFIG_VIDEO_PVRUSB2_SYSFS) := pvrusb2-sysfs.o +obj-pvrusb2-debugifc-$(CONFIG_VIDEO_PVRUSB2_DEBUGIFC) := pvrusb2-debugifc.o + +obj-pvrusb2-24xxx-$(CONFIG_VIDEO_PVRUSB2_24XXX) := \ + pvrusb2-cx2584x-v4l.o \ + pvrusb2-wm8775.o + pvrusb2-objs := pvrusb2-i2c-core.o pvrusb2-i2c-cmd-v4l2.o \ pvrusb2-audio.o pvrusb2-i2c-chips-v4l2.o \ pvrusb2-encoder.o pvrusb2-video-v4l.o \ pvrusb2-eeprom.o pvrusb2-tuner.o pvrusb2-demod.o \ pvrusb2-main.o pvrusb2-hdw.o pvrusb2-v4l2.o \ - pvrusb2-sysfs.o pvrusb2-context.o pvrusb2-io.o \ - pvrusb2-ioread.o pvrusb2-debugifc.o + pvrusb2-ctrl.o pvrusb2-std.o \ + pvrusb2-context.o pvrusb2-io.o pvrusb2-ioread.o \ + $(obj-pvrusb2-24xxx-y) \ + $(obj-pvrusb2-sysfs-y) $(obj-pvrusb2-debugifc-y) obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2.o - -EXTRA_CFLAGS += -I$(src)/.. diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c index 79395d540..30e3a38f6 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c @@ -24,7 +24,7 @@ #include "pvrusb2-audio.h" #include "pvrusb2-hdw-internal.h" #include "pvrusb2-debug.h" -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <media/msp3400.h> #include <media/v4l2-common.h> @@ -37,26 +37,6 @@ struct pvr2_msp3400_handler { }; -static int xlat_audiomode_to_v4l2(int id) -{ - switch (id) { - case PVR2_CVAL_AUDIOMODE_MONO: - return V4L2_TUNER_MODE_MONO; - case PVR2_CVAL_AUDIOMODE_STEREO: - return V4L2_TUNER_MODE_STEREO; - case PVR2_CVAL_AUDIOMODE_SAP: - return V4L2_TUNER_MODE_SAP; - case PVR2_CVAL_AUDIOMODE_LANG1: - return V4L2_TUNER_MODE_LANG1; - case PVR2_CVAL_AUDIOMODE_LANG2: - return V4L2_TUNER_MODE_LANG2; - case PVR2_CVAL_AUDIOMODE_LANG1_LANG2: - return V4L2_TUNER_MODE_LANG1_LANG2; - } - return V4L2_TUNER_MODE_STEREO; -} - - /* This function selects the correct audio input source */ static void set_stereo(struct pvr2_msp3400_handler *ctxt) { @@ -65,17 +45,16 @@ static void set_stereo(struct pvr2_msp3400_handler *ctxt) pvr2_trace(PVR2_TRACE_CHIPS,"i2c msp3400 v4l2 set_stereo"); - if (hdw->controls[PVR2_CID_INPUT].value == PVR2_CVAL_INPUT_TV) { + if (hdw->input_val == PVR2_CVAL_INPUT_TV) { struct v4l2_tuner vt; memset(&vt,0,sizeof(vt)); - vt.audmode = xlat_audiomode_to_v4l2( - hdw->controls[PVR2_CID_AUDIOMODE].value); + vt.audmode = hdw->audiomode_val; pvr2_i2c_client_cmd(ctxt->client,VIDIOC_S_TUNER,&vt); } route.input = MSP_INPUT_DEFAULT; - route.output = MSP_OUTPUT(MSP_OUT_SCART1_DA); - switch (hdw->controls[PVR2_CID_INPUT].value) { + route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1); + switch (hdw->input_val) { case PVR2_CVAL_INPUT_TV: break; case PVR2_CVAL_INPUT_RADIO: @@ -83,14 +62,14 @@ static void set_stereo(struct pvr2_msp3400_handler *ctxt) we're still using the tuner. */ /* HV: actually it is more likely to be the SCART2 input if the ivtv experience is any indication. */ - route.input = MSP_INPUT(MSP_IN_SCART_2, MSP_IN_TUNER_1, - MSP_DSP_OUT_SCART, MSP_DSP_OUT_SCART); + route.input = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1, + MSP_DSP_IN_SCART, MSP_DSP_IN_SCART); break; case PVR2_CVAL_INPUT_SVIDEO: case PVR2_CVAL_INPUT_COMPOSITE: /* SCART 1 input */ - route.input = MSP_INPUT(MSP_IN_SCART_1, MSP_IN_TUNER_1, - MSP_DSP_OUT_SCART, MSP_DSP_OUT_SCART); + route.input = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, + MSP_DSP_IN_SCART, MSP_DSP_IN_SCART); break; } pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUDIO_ROUTING,&route); @@ -100,8 +79,8 @@ static void set_stereo(struct pvr2_msp3400_handler *ctxt) static int check_stereo(struct pvr2_msp3400_handler *ctxt) { struct pvr2_hdw *hdw = ctxt->hdw; - return ((hdw->controls[PVR2_CID_INPUT].dirty != 0)|| - (hdw->controls[PVR2_CID_AUDIOMODE].dirty != 0)); + return (hdw->input_dirty || + hdw->audiomode_dirty); } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-context.c b/linux/drivers/media/video/pvrusb2/pvrusb2-context.c index f3fd61f21..40dc59871 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-context.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-context.c @@ -47,11 +47,9 @@ static void pvr2_context_trigger_poll(struct pvr2_context *mp) static void pvr2_context_poll(struct pvr2_context *mp) { - pvr2_trace(PVR2_TRACE_DEBUG,"pvr2_context_poll BEGIN"); pvr2_context_enter(mp); do { pvr2_hdw_poll(mp->hdw); } while (0); pvr2_context_exit(mp); - pvr2_trace(PVR2_TRACE_DEBUG,"pvr2_context_poll END"); } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-context.h b/linux/drivers/media/video/pvrusb2/pvrusb2-context.h index 910086ba2..69c1f3a92 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-context.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-context.h @@ -22,8 +22,7 @@ #include "compat.h" #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -#include <asm/atomic.h> -#include <asm/mutex.h> +#include <linux/mutex.h> #else #include <asm/semaphore.h> #endif diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c b/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c new file mode 100644 index 000000000..e55f43e44 --- /dev/null +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c @@ -0,0 +1,549 @@ +/* + * + * $Id$ + * + * Copyright (C) 2005 Mike Isely <isely@pobox.com> + * + * 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 + * + * 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 "pvrusb2-ctrl.h" +#include "pvrusb2-hdw-internal.h" +#include "compat.h" +#include <linux/errno.h> +#include <linux/string.h> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) +#include <linux/mutex.h> +#else +#include <asm/semaphore.h> +#endif + + +/* Set the given control. */ +int pvr2_ctrl_set_value(struct pvr2_ctrl *cptr,int val) +{ + return pvr2_ctrl_set_mask_value(cptr,~0,val); +} + + +/* Set/clear specific bits of the given control. */ +int pvr2_ctrl_set_mask_value(struct pvr2_ctrl *cptr,int mask,int val) +{ + int ret = 0; + if (!cptr) return -EINVAL; + LOCK_TAKE(cptr->hdw->big_lock); do { + if (cptr->info->set_value != 0) { + if (cptr->info->type == pvr2_ctl_bitmask) { + mask &= cptr->info->def.type_bitmask.valid_bits; + } else if (cptr->info->type == pvr2_ctl_int) { + if (val < cptr->info->def.type_int.min_value) { + break; + } + if (val > cptr->info->def.type_int.max_value) { + break; + } + } else if (cptr->info->type == pvr2_ctl_enum) { + if (val >= cptr->info->def.type_enum.count) { + break; + } + } + ret = cptr->info->set_value(cptr,mask,val); + } else { + ret = -EPERM; + } + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; +} + + +/* Get the current value of the given control. */ +int pvr2_ctrl_get_value(struct pvr2_ctrl *cptr,int *valptr) +{ + int ret = 0; + if (!cptr) return -EINVAL; + LOCK_TAKE(cptr->hdw->big_lock); do { + ret = cptr->info->get_value(cptr,valptr); + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; +} + + +/* Retrieve control's type */ +enum pvr2_ctl_type pvr2_ctrl_get_type(struct pvr2_ctrl *cptr) +{ + if (!cptr) return pvr2_ctl_int; + return cptr->info->type; +} + + +/* Retrieve control's maximum value (int type) */ +int pvr2_ctrl_get_max(struct pvr2_ctrl *cptr) +{ + int ret = 0; + if (!cptr) return 0; + LOCK_TAKE(cptr->hdw->big_lock); do { + if (cptr->info->type == pvr2_ctl_int) { + ret = cptr->info->def.type_int.max_value; + } + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; +} + + +/* Retrieve control's minimum value (int type) */ +int pvr2_ctrl_get_min(struct pvr2_ctrl *cptr) +{ + int ret = 0; + if (!cptr) return 0; + LOCK_TAKE(cptr->hdw->big_lock); do { + if (cptr->info->type == pvr2_ctl_int) { + ret = cptr->info->def.type_int.min_value; + } + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; +} + + +/* Retrieve control's default value (any type) */ +int pvr2_ctrl_get_def(struct pvr2_ctrl *cptr) +{ + int ret = 0; + if (!cptr) return 0; + LOCK_TAKE(cptr->hdw->big_lock); do { + if (cptr->info->type == pvr2_ctl_int) { + ret = cptr->info->default_value; + } + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; +} + + +/* Retrieve control's enumeration count (enum only) */ +int pvr2_ctrl_get_cnt(struct pvr2_ctrl *cptr) +{ + int ret = 0; + if (!cptr) return 0; + LOCK_TAKE(cptr->hdw->big_lock); do { + if (cptr->info->type == pvr2_ctl_enum) { + ret = cptr->info->def.type_enum.count; + } + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; +} + + +/* Retrieve control's valid mask bits (bit mask only) */ +int pvr2_ctrl_get_mask(struct pvr2_ctrl *cptr) +{ + int ret = 0; + if (!cptr) return 0; + LOCK_TAKE(cptr->hdw->big_lock); do { + if (cptr->info->type == pvr2_ctl_bitmask) { + ret = cptr->info->def.type_bitmask.valid_bits; + } + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; +} + + +/* Retrieve the control's name */ +const char *pvr2_ctrl_get_name(struct pvr2_ctrl *cptr) +{ + if (!cptr) return 0; + return cptr->info->name; +} + + +/* Retrieve the control's desc */ +const char *pvr2_ctrl_get_desc(struct pvr2_ctrl *cptr) +{ + if (!cptr) return 0; + return cptr->info->desc; +} + + +/* Retrieve a control enumeration or bit mask value */ +int pvr2_ctrl_get_valname(struct pvr2_ctrl *cptr,int val, + char *bptr,unsigned int bmax, + unsigned int *blen) +{ + int ret = -EINVAL; + if (!cptr) return 0; + *blen = 0; + LOCK_TAKE(cptr->hdw->big_lock); do { + if (cptr->info->type == pvr2_ctl_enum) { + const char **names; + names = cptr->info->def.type_enum.value_names; + if ((val >= 0) && + (val < cptr->info->def.type_enum.count)) { + if (names[val]) { + *blen = scnprintf( + bptr,bmax,"%s", + names[val]); + } else { + *blen = 0; + } + ret = 0; + } + } else if (cptr->info->type == pvr2_ctl_bitmask) { + const char **names; + unsigned int idx; + int msk; + names = cptr->info->def.type_bitmask.bit_names; + val &= cptr->info->def.type_bitmask.valid_bits; + for (idx = 0, msk = 1; val; idx++, msk <<= 1) { + if (val & msk) { + *blen = scnprintf(bptr,bmax,"%s", + names[idx]); + ret = 0; + break; + } + } + } + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; +} + + +/* Return true if control is writable */ +int pvr2_ctrl_is_writable(struct pvr2_ctrl *cptr) +{ + if (!cptr) return 0; + return cptr->info->set_value != 0; +} + + +/* Return true if control has custom symbolic representation */ +int pvr2_ctrl_has_custom_symbols(struct pvr2_ctrl *cptr) +{ + if (!cptr) return 0; + if (!cptr->info->val_to_sym) return 0; + if (!cptr->info->sym_to_val) return 0; + return !0; +} + + +/* Convert a given mask/val to a custom symbolic value */ +int pvr2_ctrl_custom_value_to_sym(struct pvr2_ctrl *cptr, + int mask,int val, + char *buf,unsigned int maxlen, + unsigned int *len) +{ + if (!cptr) return -EINVAL; + if (!cptr->info->val_to_sym) return -EINVAL; + return cptr->info->val_to_sym(cptr,mask,val,buf,maxlen,len); +} + + +/* Convert a symbolic value to a mask/value pair */ +int pvr2_ctrl_custom_sym_to_value(struct pvr2_ctrl *cptr, + const char *buf,unsigned int len, + int *maskptr,int *valptr) +{ + if (!cptr) return -EINVAL; + if (!cptr->info->sym_to_val) return -EINVAL; + return cptr->info->sym_to_val(cptr,buf,len,maskptr,valptr); +} + + +static unsigned int gen_bitmask_string(int msk,int val,int msk_only, + const char **names, + char *ptr,unsigned int len) +{ + unsigned int idx; + long sm,um; + int spcFl; + unsigned int uc,cnt; + const char *idStr; + + spcFl = 0; + uc = 0; + um = 0; + for (idx = 0, sm = 1; msk; idx++, sm <<= 1) { + if (sm & msk) { + msk &= ~sm; + idStr = names[idx]; + if (idStr) { + cnt = scnprintf(ptr,len,"%s%s%s", + (spcFl ? " " : ""), + (msk_only ? "" : + ((val & sm) ? "+" : "-")), + idStr); + ptr += cnt; len -= cnt; uc += cnt; + spcFl = !0; + } else { + um |= sm; + } + } + } + if (um) { + if (msk_only) { + cnt = scnprintf(ptr,len,"%s0x%lx", + (spcFl ? " " : ""), + um); + ptr += cnt; len -= cnt; uc += cnt; + spcFl = !0; + } else if (um & val) { + cnt = scnprintf(ptr,len,"%s+0x%lx", + (spcFl ? " " : ""), + um & val); + ptr += cnt; len -= cnt; uc += cnt; + spcFl = !0; + } else if (um & ~val) { + cnt = scnprintf(ptr,len,"%s+0x%lx", + (spcFl ? " " : ""), + um & ~val); + ptr += cnt; len -= cnt; uc += cnt; + spcFl = !0; + } + } + return uc; +} + + +static int parse_token(const char *ptr,unsigned int len, + int *valptr, + const char **names,unsigned int namecnt) +{ + char buf[33]; + unsigned int slen; + unsigned int idx; + int negfl; + char *p2; + *valptr = 0; + if (!names) namecnt = 0; + for (idx = 0; idx < namecnt; idx++) { + if (!names[idx]) continue; + slen = strlen(names[idx]); + if (slen != len) continue; + if (memcmp(names[idx],ptr,slen)) continue; + *valptr = idx; + return 0; + } + negfl = 0; + if ((*ptr == '-') || (*ptr == '+')) { + negfl = (*ptr == '-'); + ptr++; len--; + } + if (len >= sizeof(buf)) return -EINVAL; + memcpy(buf,ptr,len); + buf[len] = 0; + *valptr = simple_strtol(buf,&p2,0); + if (negfl) *valptr = -(*valptr); + if (*p2) return -EINVAL; + return 0; +} + + +static int parse_mtoken(const char *ptr,unsigned int len, + int *valptr, + const char **names,int valid_bits) +{ + char buf[33]; + unsigned int slen; + unsigned int idx; + char *p2; + int msk; + *valptr = 0; + for (idx = 0, msk = 1; valid_bits; idx++, msk <<= 1) { + if (!msk & valid_bits) continue; + valid_bits &= ~msk; + if (!names[idx]) continue; + slen = strlen(names[idx]); + if (slen != len) continue; + if (memcmp(names[idx],ptr,slen)) continue; + *valptr = msk; + return 0; + } + if (len >= sizeof(buf)) return -EINVAL; + memcpy(buf,ptr,len); + buf[len] = 0; + *valptr = simple_strtol(buf,&p2,0); + if (*p2) return -EINVAL; + return 0; +} + + +static int parse_tlist(const char *ptr,unsigned int len, + int *maskptr,int *valptr, + const char **names,int valid_bits) +{ + unsigned int cnt; + int mask,val,kv,mode,ret; + mask = 0; + val = 0; + ret = 0; + while (len) { + cnt = 0; + while ((cnt < len) && + ((ptr[cnt] <= 32) || + (ptr[cnt] >= 127))) cnt++; + ptr += cnt; + len -= cnt; + mode = 0; + if ((*ptr == '-') || (*ptr == '+')) { + mode = (*ptr == '-') ? -1 : 1; + ptr++; + len--; + } + cnt = 0; + while (cnt < len) { + if (ptr[cnt] <= 32) break; + if (ptr[cnt] >= 127) break; + cnt++; + } + if (!cnt) break; + if (parse_mtoken(ptr,cnt,&kv,names,valid_bits)) { + ret = -EINVAL; + break; + } + ptr += cnt; + len -= cnt; + switch (mode) { + case 0: + mask = valid_bits; + val |= kv; + break; + case -1: + mask |= kv; + val &= ~kv; + break; + case 1: + mask |= kv; + val |= kv; + break; + default: + break; + } + } + *maskptr = mask; + *valptr = val; + return ret; +} + + +/* Convert a symbolic value to a mask/value pair */ +int pvr2_ctrl_sym_to_value(struct pvr2_ctrl *cptr, + const char *ptr,unsigned int len, + int *maskptr,int *valptr) +{ + int ret = -EINVAL; + unsigned int cnt; + + *maskptr = 0; + *valptr = 0; + + cnt = 0; + while ((cnt < len) && ((ptr[cnt] <= 32) || (ptr[cnt] >= 127))) cnt++; + len -= cnt; ptr += cnt; + cnt = 0; + while ((cnt < len) && ((ptr[len-(cnt+1)] <= 32) || + (ptr[len-(cnt+1)] >= 127))) cnt++; + len -= cnt; + + if (!len) return -EINVAL; + + LOCK_TAKE(cptr->hdw->big_lock); do { + if (cptr->info->type == pvr2_ctl_int) { + ret = parse_token(ptr,len,valptr,0,0); + if ((ret == 0) && + ((*valptr < cptr->info->def.type_int.min_value) || + (*valptr > cptr->info->def.type_int.max_value))) { + ret = -EINVAL; + } + if (maskptr) *maskptr = ~0; + } else if (cptr->info->type == pvr2_ctl_enum) { + ret = parse_token( + ptr,len,valptr, + cptr->info->def.type_enum.value_names, + cptr->info->def.type_enum.count); + if ((ret == 0) && + ((*valptr < 0) || + (*valptr >= cptr->info->def.type_enum.count))) { + ret = -EINVAL; + } + if (maskptr) *maskptr = ~0; + } else if (cptr->info->type == pvr2_ctl_bitmask) { + ret = parse_tlist( + ptr,len,maskptr,valptr, + cptr->info->def.type_bitmask.bit_names, + cptr->info->def.type_bitmask.valid_bits); + } + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; +} + + +/* Convert a given mask/val to a symbolic value */ +int pvr2_ctrl_value_to_sym_internal(struct pvr2_ctrl *cptr, + int mask,int val, + char *buf,unsigned int maxlen, + unsigned int *len) +{ + int ret = -EINVAL; + + *len = 0; + if (cptr->info->type == pvr2_ctl_int) { + *len = scnprintf(buf,maxlen,"%d",val); + ret = 0; + } else if (cptr->info->type == pvr2_ctl_enum) { + const char **names; + names = cptr->info->def.type_enum.value_names; + if ((val >= 0) && + (val < cptr->info->def.type_enum.count)) { + if (names[val]) { + *len = scnprintf( + buf,maxlen,"%s", + names[val]); + } else { + *len = 0; + } + ret = 0; + } + } else if (cptr->info->type == pvr2_ctl_bitmask) { + *len = gen_bitmask_string( + val & mask & cptr->info->def.type_bitmask.valid_bits, + ~0,!0, + cptr->info->def.type_bitmask.bit_names, + buf,maxlen); + } + return ret; +} + + +/* Convert a given mask/val to a symbolic value */ +int pvr2_ctrl_value_to_sym(struct pvr2_ctrl *cptr, + int mask,int val, + char *buf,unsigned int maxlen, + unsigned int *len) +{ + int ret; + LOCK_TAKE(cptr->hdw->big_lock); do { + ret = pvr2_ctrl_value_to_sym_internal(cptr,mask,val, + buf,maxlen,len); + } while(0); LOCK_GIVE(cptr->hdw->big_lock); + return ret; +} + + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.h b/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.h new file mode 100644 index 000000000..9d74151a3 --- /dev/null +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.h @@ -0,0 +1,115 @@ +/* + * + * $Id$ + * + * Copyright (C) 2005 Mike Isely <isely@pobox.com> + * + * 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 + * + * 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 + * + */ +#ifndef __PVRUSB2_CTRL_H +#define __PVRUSB2_CTRL_H + +struct pvr2_ctrl; + +enum pvr2_ctl_type { + pvr2_ctl_int = 0, + pvr2_ctl_enum = 1, + pvr2_ctl_bitmask = 2, +}; + + +/* Set the given control. */ +int pvr2_ctrl_set_value(struct pvr2_ctrl *,int val); + +/* Set/clear specific bits of the given control. */ +int pvr2_ctrl_set_mask_value(struct pvr2_ctrl *,int mask,int val); + +/* Get the current value of the given control. */ +int pvr2_ctrl_get_value(struct pvr2_ctrl *,int *valptr); + +/* Retrieve control's type */ +enum pvr2_ctl_type pvr2_ctrl_get_type(struct pvr2_ctrl *); + +/* Retrieve control's maximum value (int type) */ +int pvr2_ctrl_get_max(struct pvr2_ctrl *); + +/* Retrieve control's minimum value (int type) */ +int pvr2_ctrl_get_min(struct pvr2_ctrl *); + +/* Retrieve control's default value (any type) */ +int pvr2_ctrl_get_def(struct pvr2_ctrl *); + +/* Retrieve control's enumeration count (enum only) */ +int pvr2_ctrl_get_cnt(struct pvr2_ctrl *); + +/* Retrieve control's valid mask bits (bit mask only) */ +int pvr2_ctrl_get_mask(struct pvr2_ctrl *); + +/* Retrieve the control's name */ +const char *pvr2_ctrl_get_name(struct pvr2_ctrl *); + +/* Retrieve the control's desc */ +const char *pvr2_ctrl_get_desc(struct pvr2_ctrl *); + +/* Retrieve a control enumeration or bit mask value */ +int pvr2_ctrl_get_valname(struct pvr2_ctrl *,int,char *,unsigned int, + unsigned int *); + +/* Return true if control is writable */ +int pvr2_ctrl_is_writable(struct pvr2_ctrl *); + +/* Return true if control has custom symbolic representation */ +int pvr2_ctrl_has_custom_symbols(struct pvr2_ctrl *); + +/* Convert a given mask/val to a custom symbolic value */ +int pvr2_ctrl_custom_value_to_sym(struct pvr2_ctrl *, + int mask,int val, + char *buf,unsigned int maxlen, + unsigned int *len); + +/* Convert a symbolic value to a mask/value pair */ +int pvr2_ctrl_custom_sym_to_value(struct pvr2_ctrl *, + const char *buf,unsigned int len, + int *maskptr,int *valptr); + +/* Convert a given mask/val to a symbolic value */ +int pvr2_ctrl_value_to_sym(struct pvr2_ctrl *, + int mask,int val, + char *buf,unsigned int maxlen, + unsigned int *len); + +/* Convert a symbolic value to a mask/value pair */ +int pvr2_ctrl_sym_to_value(struct pvr2_ctrl *, + const char *buf,unsigned int len, + int *maskptr,int *valptr); + +/* Convert a given mask/val to a symbolic value - must already be + inside of critical region. */ +int pvr2_ctrl_value_to_sym_internal(struct pvr2_ctrl *, + int mask,int val, + char *buf,unsigned int maxlen, + unsigned int *len); + +#endif /* __PVRUSB2_CTRL_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c new file mode 100644 index 000000000..47e7f5dbd --- /dev/null +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c @@ -0,0 +1,276 @@ +/* + * + * $Id$ + * + * Copyright (C) 2005 Mike Isely <isely@pobox.com> + * Copyright (C) 2004 Aurelien Alleaume <slts@free.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 + * + * 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 + * + */ + +/* + + This source file is specifically designed to interface with the + cx2584x, in kernels 2.6.16 or newer. + +*/ + +#include "pvrusb2-cx2584x-v4l.h" +#include "pvrusb2-video-v4l.h" +#include "pvrusb2-i2c-cmd-v4l2.h" + + +#include "pvrusb2-hdw-internal.h" +#include "pvrusb2-debug.h" +#include <media/cx25840.h> +#include <linux/videodev2.h> +#include <media/v4l2-common.h> +#include <linux/errno.h> +#include <linux/slab.h> + +struct pvr2_v4l_cx2584x { + struct pvr2_i2c_handler handler; + struct pvr2_decoder_ctrl ctrl; + struct pvr2_i2c_client *client; + struct pvr2_hdw *hdw; + unsigned long stale_mask; +}; + + +static void set_input(struct pvr2_v4l_cx2584x *ctxt) +{ + struct pvr2_hdw *hdw = ctxt->hdw; + struct v4l2_routing route; + enum cx25840_video_input vid_input; + enum cx25840_audio_input aud_input; + + memset(&route,0,sizeof(route)); + + switch(hdw->input_val) { + case PVR2_CVAL_INPUT_TV: + vid_input = CX25840_COMPOSITE7; + aud_input = CX25840_AUDIO8; + break; + case PVR2_CVAL_INPUT_COMPOSITE: + vid_input = CX25840_COMPOSITE3; + aud_input = CX25840_AUDIO_SERIAL; + break; + case PVR2_CVAL_INPUT_SVIDEO: + vid_input = CX25840_SVIDEO1; + aud_input = CX25840_AUDIO_SERIAL; + break; + case PVR2_CVAL_INPUT_RADIO: + default: + // Just set it to be composite input for now... + vid_input = CX25840_COMPOSITE3; + aud_input = CX25840_AUDIO_SERIAL; + break; + } + + pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x set_input vid=0x%x aud=0x%x", + vid_input,aud_input); + route.input = (u32)vid_input; + pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_VIDEO_ROUTING,&route); + route.input = (u32)aud_input; + pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUDIO_ROUTING,&route); +} + + +static int check_input(struct pvr2_v4l_cx2584x *ctxt) +{ + struct pvr2_hdw *hdw = ctxt->hdw; + return hdw->input_dirty != 0; +} + + +static void set_audio(struct pvr2_v4l_cx2584x *ctxt) +{ + u32 val; + struct pvr2_hdw *hdw = ctxt->hdw; + + pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x set_audio %d", + hdw->srate_val); + switch (hdw->srate_val) { + default: + case PVR2_CVAL_SRATE_48: + val = 48000; + break; + case PVR2_CVAL_SRATE_44_1: + val = 44100; + break; + } + pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_AUDIO_CLOCK_FREQ,&val); +} + + +static int check_audio(struct pvr2_v4l_cx2584x *ctxt) +{ + struct pvr2_hdw *hdw = ctxt->hdw; + return hdw->srate_dirty != 0; +} + + +struct pvr2_v4l_cx2584x_ops { + void (*update)(struct pvr2_v4l_cx2584x *); + int (*check)(struct pvr2_v4l_cx2584x *); +}; + + +static const struct pvr2_v4l_cx2584x_ops decoder_ops[] = { + { .update = set_input, .check = check_input}, + { .update = set_audio, .check = check_audio}, +}; + + +static void decoder_detach(struct pvr2_v4l_cx2584x *ctxt) +{ + ctxt->client->handler = 0; + ctxt->hdw->decoder_ctrl = 0; + kfree(ctxt); +} + + +static int decoder_check(struct pvr2_v4l_cx2584x *ctxt) +{ + unsigned long msk; + unsigned int idx; + + for (idx = 0; idx < sizeof(decoder_ops)/sizeof(decoder_ops[0]); + idx++) { + msk = 1 << idx; + if (ctxt->stale_mask & msk) continue; + if (decoder_ops[idx].check(ctxt)) { + ctxt->stale_mask |= msk; + } + } + return ctxt->stale_mask != 0; +} + + +static void decoder_update(struct pvr2_v4l_cx2584x *ctxt) +{ + unsigned long msk; + unsigned int idx; + + for (idx = 0; idx < sizeof(decoder_ops)/sizeof(decoder_ops[0]); + idx++) { + msk = 1 << idx; + if (!(ctxt->stale_mask & msk)) continue; + ctxt->stale_mask &= ~msk; + decoder_ops[idx].update(ctxt); + } +} + + +static void decoder_enable(struct pvr2_v4l_cx2584x *ctxt,int fl) +{ + pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx25840 decoder_enable(%d)",fl); + pvr2_v4l2_cmd_stream(ctxt->client,fl); +} + + +static int decoder_detect(struct pvr2_i2c_client *cp) +{ + int ret; + /* Attempt to query the decoder - let's see if it will answer */ + struct v4l2_queryctrl qc; + + memset(&qc,0,sizeof(qc)); + + qc.id = V4L2_CID_BRIGHTNESS; + + ret = pvr2_i2c_client_cmd(cp,VIDIOC_QUERYCTRL,&qc); + return ret == 0; /* Return true if it answered */ +} + + +static int decoder_is_tuned(struct pvr2_v4l_cx2584x *ctxt) +{ + struct v4l2_tuner vt; + int ret; + + memset(&vt,0,sizeof(vt)); + ret = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_G_TUNER,&vt); + if (ret < 0) return -EINVAL; + return vt.signal ? 1 : 0; +} + + +static unsigned int decoder_describe(struct pvr2_v4l_cx2584x *ctxt, + char *buf,unsigned int cnt) +{ + return scnprintf(buf,cnt,"handler: pvrusb2-cx2584x-v4l"); +} + + +static void decoder_reset(struct pvr2_v4l_cx2584x *ctxt) +{ + int ret; + ret = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_RESET,0); + pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx25840 decoder_reset (ret=%d)",ret); +} + + +const static struct pvr2_i2c_handler_functions hfuncs = { + .detach = (void (*)(void *))decoder_detach, + .check = (int (*)(void *))decoder_check, + .update = (void (*)(void *))decoder_update, + .describe = (unsigned int (*)(void *,char *,unsigned int))decoder_describe, +}; + + +int pvr2_i2c_cx2584x_v4l_setup(struct pvr2_hdw *hdw, + struct pvr2_i2c_client *cp) +{ + struct pvr2_v4l_cx2584x *ctxt; + + if (hdw->decoder_ctrl) return 0; + if (cp->handler) return 0; + if (!decoder_detect(cp)) return 0; + + ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL); + if (!ctxt) return 0; + memset(ctxt,0,sizeof(*ctxt)); + + ctxt->handler.func_data = ctxt; + ctxt->handler.func_table = &hfuncs; + ctxt->ctrl.ctxt = ctxt; + ctxt->ctrl.detach = (void (*)(void *))decoder_detach; + ctxt->ctrl.enable = (void (*)(void *,int))decoder_enable; + ctxt->ctrl.tuned = (int (*)(void *))decoder_is_tuned; + ctxt->ctrl.force_reset = (void (*)(void*))decoder_reset; + ctxt->client = cp; + ctxt->hdw = hdw; + ctxt->stale_mask = (1 << (sizeof(decoder_ops)/ + sizeof(decoder_ops[0]))) - 1; + hdw->decoder_ctrl = &ctxt->ctrl; + cp->handler = &ctxt->handler; + pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x cx2584x V4L2 handler set up", + cp->client->addr); + return !0; +} + + + + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 70 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h new file mode 100644 index 000000000..5dea8d7b3 --- /dev/null +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h @@ -0,0 +1,54 @@ +/* + * + * $Id$ + * + * Copyright (C) 2005 Mike Isely <isely@pobox.com> + * Copyright (C) 2004 Aurelien Alleaume <slts@free.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 + * + * 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 + * + */ + +#ifndef __PVRUSB2_CX2584X_V4L_H +#define __PVRUSB2_CX2584X_V4L_H + +/* + + This module connects the pvrusb2 driver to the I2C chip level + driver which handles combined device audio & video processing. + This interface is used internally by the driver; higher level code + should only interact through the interface provided by + pvrusb2-hdw.h. + +*/ + +#include "compat.h" + + +#include "pvrusb2-i2c-core.h" + +int pvr2_i2c_cx2584x_v4l_setup(struct pvr2_hdw *,struct pvr2_i2c_client *); + + +#endif /* __PVRUSB2_CX2584X_V4L_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 70 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c index bcfe468eb..586900e36 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c @@ -32,11 +32,11 @@ struct debugifc_mask_item { }; static struct debugifc_mask_item mask_items[] = { - {"ENC_FIRMWARE",PVR2_SUBSYS_ENC_FIRMWARE}, - {"ENC_CFG",PVR2_SUBSYS_ENC_CFG}, - {"DIG_RUN",PVR2_SUBSYS_DIGITIZER_RUN}, - {"USB_RUN",PVR2_SUBSYS_USBSTREAM_RUN}, - {"ENC_RUN",PVR2_SUBSYS_ENC_RUN}, + {"ENC_FIRMWARE",(1<<PVR2_SUBSYS_B_ENC_FIRMWARE)}, + {"ENC_CFG",(1<<PVR2_SUBSYS_B_ENC_CFG)}, + {"DIG_RUN",(1<<PVR2_SUBSYS_B_DIGITIZER_RUN)}, + {"USB_RUN",(1<<PVR2_SUBSYS_B_USBSTREAM_RUN)}, + {"ENC_RUN",(1<<PVR2_SUBSYS_B_ENC_RUN)}, }; @@ -362,11 +362,13 @@ int pvr2_debugifc_do1cmd(struct pvr2_hdw *hdw,const char *buf, } else if (debugifc_match_keyword(wptr,wlen,"bus")) { pvr2_hdw_device_reset(hdw); } else if (debugifc_match_keyword(wptr,wlen,"soft")) { - return pvr2_hdw_cmd_soft_reset(hdw); + return pvr2_hdw_cmd_powerup(hdw); } else if (debugifc_match_keyword(wptr,wlen,"deep")) { return pvr2_hdw_cmd_deep_reset(hdw); } else if (debugifc_match_keyword(wptr,wlen,"firmware")) { return pvr2_upload_firmware2(hdw); + } else if (debugifc_match_keyword(wptr,wlen,"decoder")) { + return pvr2_hdw_cmd_decoder_reset(hdw); } return -EINVAL; } else if (debugifc_match_keyword(wptr,wlen,"subsys_flags")) { diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-demod.c b/linux/drivers/media/video/pvrusb2/pvrusb2-demod.c index dca787dfa..f082ecee2 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-demod.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-demod.c @@ -26,7 +26,7 @@ #include "pvrusb2-hdw-internal.h" #include "pvrusb2-debug.h" #include "compat.h" -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <media/tuner.h> #include <media/v4l2-common.h> diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c b/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c index 60ee45ca2..4003149a1 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.c @@ -27,56 +27,7 @@ #define trace_eeprom(...) pvr2_trace(PVR2_TRACE_EEPROM,__VA_ARGS__) -/* - - isely@pobox.com 16-Oct-2005 - There are two method by which we can - theoretically retrieve information from the device's eeprom: - - Method #1: We expect tveeprom to attach to our I2C adapter as a - client, in which case we send it a command to tell us what it - knows about the device. This is the "indirect" method. - - Method #2: We retrieve the eeprom contents ourselves and call into - tveeprom_hauppauge_analog() to parse the data and tell us what - it knows about the device. This is the "direct" method. - - Unfortunately it isn't perfectly clear which method is the best. - They each have pros and cons: - - #1 is simpler & more portable and has an API which is more stable. - - #1 doesn't provide as much information as #2 does. For example, we - can't retrieve the device's serial number with method #1. - - #1 requires that tveeprom.ko autonomously detect the eeprom chip on - its own; we can't help it out here. Worse still, it seems that - the eeprom in some PVR USB2 devices (like mine) can't be detected - correctly (I don't see an ack on a zero length write which is - what the I2C core attempts). - - #2 uses an unstable API. Current the ivtv implementation of #2 uses - a completely different tveeprom struct than the v4l - implementation of #2. This causes a usability nightmare. - - Since I can't decide, both methods are implemented below. Method #2 - (direct) is the default choice, but if you want to try method #1, - then define PVR2_EEPROM_INDIRECT and cross your fingers... - If you use method #1, please be aware that you won't have a serial - number for the device and thus the sysfs interface may be a little - different. In addition, if tveeprom.ko fails to detect the eeprom - you may have to force it using standard i2c module options (try - force=-1,80). FINALLY (and this may foreclose this option for you - completely), the PVR USB2 eeprom seems to have valid data only in - the upper 128 bytes - the lower 128 bytes causes tveeprom.ko to - abort. In method #2 we only read the upper 128 bytes... - - */ - - - - -/* Stuff common to direct approach of operation tveeprom */ /* @@ -121,7 +72,7 @@ static u8 *pvr2_eeprom_fetch(struct pvr2_hdw *hdw) /* FX2 documentation states that a 16bit-addressed eeprom is expected if the I2C address is an odd number (yeah, this is - strange bit it's what they do) */ + strange but it's what they do) */ mode16 = (addr & 1); eepromSize = (mode16 ? 4096 : 256); trace_eeprom("Examining %d byte eeprom at location 0x%x" @@ -132,7 +83,7 @@ static u8 *pvr2_eeprom_fetch(struct pvr2_hdw *hdw) msg[0].flags = 0; msg[0].len = mode16 ? 2 : 1; msg[0].buf = iadd; - msg[1].addr = hdw->eeprom_addr; + msg[1].addr = addr; msg[1].flags = I2C_M_RD; /* We have to do the actual eeprom data fetch ourselves, because @@ -165,15 +116,7 @@ static u8 *pvr2_eeprom_fetch(struct pvr2_hdw *hdw) } -/*VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV*/ -/* BEGIN DIRECT METHOD, V4L ONLY */ - - -/* Directly call eeprom analysis function within tveeprom. This - version directly assumes it is talking to the V4L version of - tveeprom.ko and does not attempt anything ugly to maintain - backwards compatibility. */ - +/* Directly call eeprom analysis function within tveeprom. */ int pvr2_eeprom_analyze(struct pvr2_hdw *hdw) { u8 *eeprom; @@ -204,66 +147,13 @@ int pvr2_eeprom_analyze(struct pvr2_hdw *hdw) trace_eeprom("rev_str=%s",tvdata.rev_str); hdw->tuner_type = tvdata.tuner_type; hdw->serial_number = tvdata.serial_number; - hdw->video_standards = tvdata.tuner_formats; + hdw->std_mask_eeprom = tvdata.tuner_formats; kfree(eeprom); return 0; } - - -/* END DIRECT METHOD, V4L ONLY */ -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - - - - - - -static v4l2_std_id std_choices[] = { - [PVR2_CVAL_VIDEOSTANDARD_NTSC_M] = V4L2_STD_NTSC_M, - [PVR2_CVAL_VIDEOSTANDARD_PAL_BG] = V4L2_STD_PAL_BG, - [PVR2_CVAL_VIDEOSTANDARD_PAL_I] = V4L2_STD_PAL_I, - [PVR2_CVAL_VIDEOSTANDARD_PAL_DK] = V4L2_STD_PAL_DK, - [PVR2_CVAL_VIDEOSTANDARD_SECAM_L] = V4L2_STD_SECAM_L, - [PVR2_CVAL_VIDEOSTANDARD_PAL_M] = V4L2_STD_PAL_M, -}; - -void pvr2_eeprom_set_default_standard(struct pvr2_hdw *hdw) -{ - int vstd_value = 0; - int vstd_found = 0; - unsigned int idx; - v4l2_std_id vs = (v4l2_std_id)hdw->video_standards; - - for (idx = 0; idx < sizeof(std_choices)/sizeof(std_choices[0]); - idx++) { - if (!(vs & std_choices[idx])) continue; - trace_eeprom("Detected video standard %s (from eeprom)", - pvr2_hdw_get_ctl_value_name( - hdw,PVR2_CID_VIDEOSTANDARD,idx)); - if (vstd_found) continue; - vstd_value = idx; - vstd_found = !0; - } - - if (!vstd_found) { - trace_eeprom("eeprom unable to recognize" - " a known video standard"); - return; - } - - trace_eeprom("Setting initial video standard to %s" - " (detected from eeprom)", - pvr2_hdw_get_ctl_value_name(hdw, - PVR2_CID_VIDEOSTANDARD, - vstd_value)); - pvr2_hdw_set_ctl_value_internal(hdw,PVR2_CID_VIDEOSTANDARD,vstd_value); -} - - /* Stuff for Emacs to see, in order to encourage consistent editing style: *** Local Variables: *** diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.h b/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.h index 061cecd91..84242975d 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-eeprom.h @@ -26,7 +26,6 @@ struct pvr2_hdw; int pvr2_eeprom_analyze(struct pvr2_hdw *); -void pvr2_eeprom_set_default_standard(struct pvr2_hdw *); #endif /* __PVRUSB2_EEPROM_H */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c index 97931d3c4..6c66e258f 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c @@ -22,7 +22,7 @@ #include <linux/device.h> // for linux/firmware.h #include <linux/firmware.h> -#include "cx2341x.h" +#include <media/cx2341x.h> #include "pvrusb2-util.h" #include "pvrusb2-encoder.h" #include "pvrusb2-hdw-internal.h" @@ -268,50 +268,62 @@ static int pvr2_write_encoder_vcmd (struct pvr2_hdw *hdw, u8 cmd, int pvr2_encoder_configure(struct pvr2_hdw *hdw) { int ret = 0, audio, i; - int vd_std = hdw->controls[PVR2_CID_VIDEOSTANDARD].value; - int height = hdw->controls[PVR2_CID_VRES].value; - int width = hdw->controls[PVR2_CID_HRES].value; - int height_full = !hdw->controls[PVR2_CID_INTERLACE].value; + v4l2_std_id vd_std = hdw->std_mask_cur; + int height = hdw->res_ver_val; + int width = hdw->res_hor_val; + int height_full = !hdw->interlace_val; int is_30fps, is_ntsc; - switch (vd_std) { - case PVR2_CVAL_VIDEOSTANDARD_NTSC_M: + if (vd_std & V4L2_STD_NTSC) { is_ntsc=1; is_30fps=1; - break; - case PVR2_CVAL_VIDEOSTANDARD_PAL_M: + } else if (vd_std & V4L2_STD_PAL_M) { is_ntsc=0; is_30fps=1; - break; - default: + } else { is_ntsc=0; is_30fps=0; - break; } pvr2_trace(PVR2_TRACE_ENCODER,"pvr2_encoder_configure"); - /* set stream output port. */ + /* set stream output port. Some notes here: The ivtv-derived + encoder documentation says that this command only gets a + single argument. However the Windows driver for the model + 29xxx series hardware has been sending 0x01 as a second + argument, while the Windows driver for the model 24xxx + series hardware has been sending 0x02 as a second argument. + Confusing matters further are the observations that 0x01 + for that second argument simply won't work on the 24xxx + hardware, while 0x02 will work on the 29xxx - except that + when we use 0x02 then xawtv breaks due to a loss of + synchronization with the mpeg packet headers. While xawtv + should be fixed to let it resync better (I did try to + contact Gerd about this but he has not answered), it has + also been determined that sending 0x00 as this mystery + second argument seems to work on both hardware models AND + xawtv works again. So we're going to send 0x00. */ ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_OUTPUT_PORT, 2, - 0x01, 0x01); + 0x01, 0x00); /* set the Program Index Information. We want I,P,B frames (max 400) */ ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_PGM_INDEX_INFO, 2, 0x07, 0x0190); - /* NOTE : windows driver sends some 0xdc */ - - /* Mike Isely <isely@pobox.com> 19-Jun-2005 I've confirmed - that the Windows driver seems to issue these commands, but - right now I have no idea what these do (and neither does - the ivtv driver). But, if I leave them in, then mplayer - goes nuts with xrun errors. So for now we don't do this. - It sure would be nice to know what these are for. */ -#if 0 - ret |= pvr2_write_encoder_vcmd(hdw, 0xdc, 1, 5); - ret |= pvr2_write_encoder_vcmd(hdw, 0xdc, 2, 3, 1); - ret |= pvr2_write_encoder_vcmd(hdw, 0xdc, 1, 8); + /* NOTE : windows driver sends these */ + /* Mike Isely <isely@pobox.com> 7-Mar-2006 The windows driver + sends the following commands but if we do the same then + many apps are no longer able to read the video stream. + Leaving these out seems to do no harm at all, so they're + commented out for that reason. */ +#ifdef notdef + ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 5,0,0,0); + ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 3,1,0,0); + ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 8,0,0,0); + ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 4,1,0,0); + ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 0,3,0,0); + ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_MISC,4,15,0,0,0); #endif /* Strange compared to ivtv data. */ @@ -373,9 +385,9 @@ int pvr2_encoder_configure(struct pvr2_hdw *hdw) /* set video bitrate */ ret |= pvr2_write_encoder_vcmd( hdw, CX2341X_ENC_SET_BIT_RATE, 3, - (hdw->controls[PVR2_CID_VBR].value ? 1 : 0), - hdw->controls[PVR2_CID_AVERAGEVIDEOBITRATE].value, - (u32) (hdw->controls[PVR2_CID_PEAKVIDEOBITRATE].value) / 400); + (hdw->vbr_val ? 1 : 0), + hdw->videobitrate_val, + hdw->videopeak_val / 400); /* setup GOP structure (GOP size = 0f or 0c, 3-1 = 2 B-frames) */ ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_GOP_PROPERTIES, 2, is_30fps ? 0x0f : 0x0c, 0x03); @@ -387,13 +399,11 @@ int pvr2_encoder_configure(struct pvr2_hdw *hdw) ret |= pvr2_write_encoder_vcmd(hdw,CX2341X_ENC_SET_GOP_CLOSURE,1,0); /* set audio stream properties 0x40b9? 0100 0000 1011 1001 */ - audio = (pvr_tbl_audiobitrate[hdw->controls[ - PVR2_CID_AUDIOBITRATE].value] | - pvr_tbl_srate[hdw->controls[PVR2_CID_SRATE].value] | - hdw->controls[PVR2_CID_AUDIOLAYER].value << 2 | - (hdw->controls[PVR2_CID_AUDIOCRC].value ? 1 << 14 : 0) | - pvr_tbl_emphasis[hdw->controls[ - PVR2_CID_AUDIOEMPHASIS].value]); + audio = (pvr_tbl_audiobitrate[hdw->audiobitrate_val] | + pvr_tbl_srate[hdw->srate_val] | + hdw->audiolayer_val << 2 | + (hdw->audiocrc_val ? 1 << 14 : 0) | + pvr_tbl_emphasis[hdw->audioemphasis_val]); ret |= pvr2_write_encoder_vcmd(hdw,CX2341X_ENC_SET_AUDIO_PROPERTIES,1, audio); @@ -419,7 +429,7 @@ int pvr2_encoder_configure(struct pvr2_hdw *hdw) ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_INITIALIZE_INPUT, 0); if (!ret) { - hdw->subsys_enabled_mask |= PVR2_SUBSYS_ENC_CFG; + hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG); } return ret; @@ -447,7 +457,7 @@ int pvr2_encoder_start(struct pvr2_hdw *hdw) 0,0x13); } if (!status) { - hdw->subsys_enabled_mask |= PVR2_SUBSYS_ENC_RUN; + hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_RUN); } return status; } @@ -477,7 +487,7 @@ int pvr2_encoder_stop(struct pvr2_hdw *hdw) pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000000); if (!status) { - hdw->subsys_enabled_mask &= ~PVR2_SUBSYS_ENC_RUN; + hdw->subsys_enabled_mask &= ~(1<<PVR2_SUBSYS_B_ENC_RUN); } return status; } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index 1b161773b..0d527abb5 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -34,17 +34,48 @@ */ #include "compat.h" +#include <linux/config.h> #include <linux/videodev2.h> #include <linux/i2c.h> #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -#include <asm/atomic.h> -#include <asm/mutex.h> +#include <linux/mutex.h> #else #include <asm/semaphore.h> #endif #include "pvrusb2-hdw.h" #include "pvrusb2-io.h" +/* Legal values for the SRATE state variable */ +#define PVR2_CVAL_SRATE_48 0 +#define PVR2_CVAL_SRATE_44_1 1 + +/* Legal values for the AUDIOBITRATE state variable */ +#define PVR2_CVAL_AUDIOBITRATE_384 0 +#define PVR2_CVAL_AUDIOBITRATE_320 1 +#define PVR2_CVAL_AUDIOBITRATE_256 2 +#define PVR2_CVAL_AUDIOBITRATE_224 3 +#define PVR2_CVAL_AUDIOBITRATE_192 4 +#define PVR2_CVAL_AUDIOBITRATE_160 5 +#define PVR2_CVAL_AUDIOBITRATE_128 6 +#define PVR2_CVAL_AUDIOBITRATE_112 7 +#define PVR2_CVAL_AUDIOBITRATE_96 8 +#define PVR2_CVAL_AUDIOBITRATE_80 9 +#define PVR2_CVAL_AUDIOBITRATE_64 10 +#define PVR2_CVAL_AUDIOBITRATE_56 11 +#define PVR2_CVAL_AUDIOBITRATE_48 12 +#define PVR2_CVAL_AUDIOBITRATE_32 13 +#define PVR2_CVAL_AUDIOBITRATE_VBR 14 + +/* Legal values for the AUDIOEMPHASIS state variable */ +#define PVR2_CVAL_AUDIOEMPHASIS_NONE 0 +#define PVR2_CVAL_AUDIOEMPHASIS_50_15 1 +#define PVR2_CVAL_AUDIOEMPHASIS_CCITT 2 + +/* Legal values for PVR2_CID_HSM */ +#define PVR2_CVAL_HSM_FAIL 0 +#define PVR2_CVAL_HSM_FULL 1 +#define PVR2_CVAL_HSM_HIGH 2 + #define PVR2_VID_ENDPOINT 0x84 #define PVR2_UNK_ENDPOINT 0x86 /* maybe raw yuv ? */ #define PVR2_VBI_ENDPOINT 0x88 @@ -58,11 +89,72 @@ struct pvr2_decoder; -struct pvr2_ctl_state { - int value; - int dirty; +typedef int (*pvr2_ctlf_is_dirty)(struct pvr2_ctrl *); +typedef void (*pvr2_ctlf_clear_dirty)(struct pvr2_ctrl *); +typedef int (*pvr2_ctlf_get_value)(struct pvr2_ctrl *,int *); +typedef int (*pvr2_ctlf_set_value)(struct pvr2_ctrl *,int msk,int val); +typedef int (*pvr2_ctlf_val_to_sym)(struct pvr2_ctrl *,int msk,int val, + char *,unsigned int,unsigned int *); +typedef int (*pvr2_ctlf_sym_to_val)(struct pvr2_ctrl *, + const char *,unsigned int, + int *mskp,int *valp); + +/* This structure describes a specific control. A table of these is set up + in pvrusb2-hdw.c. */ +struct pvr2_ctl_info { + /* Control's name suitable for use as an identifier */ + const char *name; + + /* Short description of control */ + const char *desc; + + /* Control's implementation */ + pvr2_ctlf_get_value get_value; /* Get its value */ + pvr2_ctlf_set_value set_value; /* Set its value */ + pvr2_ctlf_val_to_sym val_to_sym; /* Custom convert value->symbol */ + pvr2_ctlf_sym_to_val sym_to_val; /* Custom convert symbol->value */ + pvr2_ctlf_is_dirty is_dirty; /* Return true if dirty */ + pvr2_ctlf_clear_dirty clear_dirty; /* Clear dirty state */ + + /* Control's type (int, enum, bitmask) */ + enum pvr2_ctl_type type; + + /* Associated V4L control ID, if any */ + int v4l_id; + + /* Associated driver internal ID, if any */ + int internal_id; + + /* Don't implicitly initialize this control's value */ + int skip_init; + + /* Starting value for this control */ + int default_value; + + /* Type-specific control information */ + union { + struct { /* Integer control */ + long min_value; /* lower limit */ + long max_value; /* upper limit */ + } type_int; + struct { /* enumerated control */ + unsigned int count; /* enum value count */ + const char **value_names; /* symbol names */ + } type_enum; + struct { /* bitmask control */ + unsigned int valid_bits; /* bits in use */ + const char **bit_names; /* symbol name/bit */ + } type_bitmask; + } def; }; + +struct pvr2_ctrl { + const struct pvr2_ctl_info *info; + struct pvr2_hdw *hdw; +}; + + struct pvr2_audio_stat { void *ctxt; void (*detach)(void *); @@ -74,6 +166,7 @@ struct pvr2_decoder_ctrl { void (*detach)(void *); void (*enable)(void *,int); int (*tuned)(void *); + void (*force_reset)(void *); }; #define PVR2_I2C_PEND_DETECT 0x01 /* Need to detect a client type */ @@ -95,7 +188,12 @@ struct pvr2_decoder_ctrl { /* Known major hardware variants, keyed from device ID */ #define PVR2_HDW_TYPE_29XXX 0 +#ifdef CONFIG_VIDEO_PVRUSB2_24XXX #define PVR2_HDW_TYPE_24XXX 1 +#endif + +typedef int (*pvr2_i2c_func)(struct pvr2_hdw *,u8,u8 *,u16,u8 *, u16); +#define PVR2_I2C_FUNC_CNT 128 /* This structure contains all state data directly needed to manipulate the hardware (as opposed to complying with a kernel @@ -127,6 +225,8 @@ struct pvr2_hdw { /* I2C stuff */ struct i2c_adapter i2c_adap; struct i2c_algorithm i2c_algo; + pvr2_i2c_func i2c_func[PVR2_I2C_FUNC_CNT]; + int i2c_cx25840_hack_state; int i2c_linked; unsigned int i2c_pend_types; /* Which types of update are needed */ unsigned long i2c_pend_mask; /* Change bits we need to scan */ @@ -141,6 +241,8 @@ struct pvr2_hdw { /* Frequency table */ unsigned int freqTable[FREQTABLE_SIZE]; + unsigned int freqProgSlot; + unsigned int freqSlot; /* Stuff for handling low level control interaction with device */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) @@ -190,7 +292,25 @@ struct pvr2_hdw { /* Tuner / frequency control stuff */ unsigned int tuner_type; int tuner_updated; - unsigned long video_standards; + unsigned int freqVal; + int freqDirty; + + /* Video standard handling */ + v4l2_std_id std_mask_eeprom; // Hardware supported selections + v4l2_std_id std_mask_avail; // Which standards we may select from + v4l2_std_id std_mask_cur; // Currently selected standard(s) + unsigned int std_enum_cnt; // # of enumerated standards + int std_enum_cur; // selected standard enumeration value + int std_dirty; // True if std_mask_cur has changed + struct pvr2_ctl_info std_info_enum; + struct pvr2_ctl_info std_info_avail; + struct pvr2_ctl_info std_info_cur; + struct v4l2_standard *std_defs; + const char **std_enum_names; + + // Generated string names, one per actual V4L2 standard + const char *std_mask_ptrs[32]; + char std_mask_names[32][10]; int unit_number; /* ID for driver instance */ unsigned long serial_number; /* ID for hardware itself */ @@ -209,14 +329,51 @@ struct pvr2_hdw { int flag_bilingual; struct pvr2_audio_stat *audio_stat; - /* Every last bit of controllable state */ - struct pvr2_ctl_state controls[PVR2_CID_COUNT]; + /* Control state */ +#define VCREATE_DATA(lab) int lab##_val; int lab##_dirty + VCREATE_DATA(brightness); + VCREATE_DATA(contrast); + VCREATE_DATA(saturation); + VCREATE_DATA(hue); + VCREATE_DATA(volume); + VCREATE_DATA(balance); + VCREATE_DATA(bass); + VCREATE_DATA(treble); + VCREATE_DATA(mute); + VCREATE_DATA(srate); + VCREATE_DATA(audiobitrate); + VCREATE_DATA(audiocrc); + VCREATE_DATA(audioemphasis); + VCREATE_DATA(vbr); + VCREATE_DATA(videobitrate); + VCREATE_DATA(videopeak); + VCREATE_DATA(input); + VCREATE_DATA(audiomode); + VCREATE_DATA(res_hor); + VCREATE_DATA(res_ver); + VCREATE_DATA(interlace); + VCREATE_DATA(audiolayer); +#undef VCREATE_DATA + + struct pvr2_ctrl *controls; }; -int pvr2_hdw_set_ctl_value_internal(struct pvr2_hdw *hdw, - unsigned int ctl_id,int value); int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw); +unsigned int pvr2_hdw_get_signal_status_internal(struct pvr2_hdw *); + +void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw, + unsigned long msk,unsigned long val); +void pvr2_hdw_subsys_stream_bit_chg_no_lock(struct pvr2_hdw *hdw, + unsigned long msk, + unsigned long val); + +void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw); +void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw); + +int pvr2_i2c_basic_op(struct pvr2_hdw *,u8 i2c_addr, + u8 *wdata,u16 wlen, + u8 *rdata,u16 rlen); #endif /* __PVRUSB2_HDW_INTERNAL_H */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 539456b44..333419d6f 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -25,7 +25,9 @@ #include <linux/slab.h> #include <linux/firmware.h> #include <asm/semaphore.h> +#include <media/cx2341x.h> #include "pvrusb2.h" +#include "pvrusb2-std.h" #include "pvrusb2-util.h" #include "pvrusb2-hdw.h" #include "pvrusb2-i2c-core.h" @@ -37,7 +39,9 @@ struct usb_device_id pvr2_device_table[] = { [PVR2_HDW_TYPE_29XXX] = { USB_DEVICE(0x2040, 0x2900) }, +#ifdef CONFIG_VIDEO_PVRUSB2_24XXX [PVR2_HDW_TYPE_24XXX] = { USB_DEVICE(0x2040, 0x2400) }, +#endif { } }; @@ -45,7 +49,45 @@ MODULE_DEVICE_TABLE(usb, pvr2_device_table); static const char *pvr2_device_names[] = { [PVR2_HDW_TYPE_29XXX] = "WinTV PVR USB2 Model Category 29xxxx", +#ifdef CONFIG_VIDEO_PVRUSB2_24XXX [PVR2_HDW_TYPE_24XXX] = "WinTV PVR USB2 Model Category 24xxxx", +#endif +}; + +struct pvr2_string_table { + const char **lst; + unsigned int cnt; +}; + +#ifdef CONFIG_VIDEO_PVRUSB2_24XXX +// Names of other client modules to request for 24xxx model hardware +static const char *pvr2_client_24xxx[] = { + "cx25840", + "tuner", + "tda9887", + "wm8775", +}; +#endif + +// Names of other client modules to request for 29xxx model hardware +static const char *pvr2_client_29xxx[] = { + "msp3400", + "saa7115", + "tuner", + "tda9887", +}; + +static struct pvr2_string_table pvr2_client_lists[] = { + [PVR2_HDW_TYPE_29XXX] = { + pvr2_client_29xxx, + sizeof(pvr2_client_29xxx)/sizeof(pvr2_client_29xxx[0]), + }, +#ifdef CONFIG_VIDEO_PVRUSB2_24XXX + [PVR2_HDW_TYPE_24XXX] = { + pvr2_client_24xxx, + sizeof(pvr2_client_24xxx)/sizeof(pvr2_client_24xxx[0]), + }, +#endif }; static struct pvr2_hdw *unit_pointers[PVR_NUM] = {[ 0 ... PVR_NUM-1 ] = 0}; @@ -56,6 +98,7 @@ static int initusbreset = 1; static int procreload = 0; static int tuner[PVR_NUM] = { [0 ... PVR_NUM-1] = -1 }; static int tolerance[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 }; +static int video_std[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 }; static int init_pause_msec = 0; module_param(ctlchg, int, S_IRUGO|S_IWUSR); @@ -69,6 +112,8 @@ MODULE_PARM_DESC(procreload, "Attempt init failure recovery with firmware reload"); module_param_array(tuner, int, NULL, 0444); MODULE_PARM_DESC(tuner,"specify installed tuner type"); +module_param_array(video_std, int, NULL, 0444); +MODULE_PARM_DESC(video_std,"specify initial video standard"); module_param_array(tolerance, int, NULL, 0444); MODULE_PARM_DESC(tolerance,"specify stream error tolerance"); @@ -86,22 +131,6 @@ MODULE_PARM_DESC(tolerance,"specify stream error tolerance"); /* size of a firmware chunk */ #define FIRMWARE_CHUNK_SIZE 0x2000 -typedef int (*pvr2_ctl_set_func)(struct pvr2_hdw *,int ctl_id,int val); -typedef int (*pvr2_ctl_get_func)(struct pvr2_hdw *,int ctl_id); - -struct pvr2_ctl_def { - const char *name; - pvr2_ctl_set_func set_func; - pvr2_ctl_get_func get_func; - int max_value; - int min_value; - int skip_init; - int default_value; - const char **value_defs_ptr; - unsigned int value_defs_count; -}; - - static const char *control_values_srate[] = { [PVR2_CVAL_SRATE_48] = "48KHz", [PVR2_CVAL_SRATE_44_1] = "44.1KHz", @@ -134,16 +163,6 @@ static const char *control_values_audioemphasis[] = { }; -static const char *control_values_videostandard[] = { - [PVR2_CVAL_VIDEOSTANDARD_NTSC_M] = "NTSC-M", - [PVR2_CVAL_VIDEOSTANDARD_SECAM_L] = "SECAM-L", - [PVR2_CVAL_VIDEOSTANDARD_PAL_BG] = "PAL-BG", - [PVR2_CVAL_VIDEOSTANDARD_PAL_I] = "PAL-I", - [PVR2_CVAL_VIDEOSTANDARD_PAL_DK] = "PAL-DK", - [PVR2_CVAL_VIDEOSTANDARD_PAL_M] = "PAL-M", -}; - - static const char *control_values_input[] = { [PVR2_CVAL_INPUT_TV] = "television", /*xawtv needs this name*/ [PVR2_CVAL_INPUT_RADIO] = "radio", @@ -153,12 +172,11 @@ static const char *control_values_input[] = { static const char *control_values_audiomode[] = { - [PVR2_CVAL_AUDIOMODE_MONO] = "Mono", - [PVR2_CVAL_AUDIOMODE_STEREO] = "Stereo", - [PVR2_CVAL_AUDIOMODE_SAP] = "SAP", - [PVR2_CVAL_AUDIOMODE_LANG1] = "Lang1", - [PVR2_CVAL_AUDIOMODE_LANG2] = "Lang2", - [PVR2_CVAL_AUDIOMODE_LANG1_LANG2] = "Lang1+Lang2", + [V4L2_TUNER_MODE_MONO] = "Mono", + [V4L2_TUNER_MODE_STEREO] = "Stereo", + [V4L2_TUNER_MODE_LANG1] = "Lang1", + [V4L2_TUNER_MODE_LANG2] = "Lang2", + [V4L2_TUNER_MODE_LANG1_LANG2] = "Lang1+Lang2", }; @@ -169,231 +187,551 @@ static const char *control_values_hsm[] = { }; -#define VDEF(x) \ - .value_defs_ptr = x, \ - .value_defs_count = (sizeof(x)/sizeof(x[0])) +static const char *control_values_subsystem[] = { + [PVR2_SUBSYS_B_ENC_FIRMWARE] = "enc_firmware", + [PVR2_SUBSYS_B_ENC_CFG] = "enc_config", + [PVR2_SUBSYS_B_DIGITIZER_RUN] = "digitizer_run", + [PVR2_SUBSYS_B_USBSTREAM_RUN] = "usbstream_run", + [PVR2_SUBSYS_B_ENC_RUN] = "enc_run", +}; + + +static int ctrl_channelfreq_get(struct pvr2_ctrl *cptr,int *vp) +{ + struct pvr2_hdw *hdw = cptr->hdw; + if ((hdw->freqProgSlot > 0) && (hdw->freqProgSlot <= FREQTABLE_SIZE)) { + *vp = hdw->freqTable[hdw->freqProgSlot-1]; + } else { + *vp = 0; + } + return 0; +} + +static int ctrl_channelfreq_set(struct pvr2_ctrl *cptr,int m,int v) +{ + struct pvr2_hdw *hdw = cptr->hdw; + if ((hdw->freqProgSlot > 0) && (hdw->freqProgSlot <= FREQTABLE_SIZE)) { + hdw->freqTable[hdw->freqProgSlot-1] = v; + } + return 0; +} + +static int ctrl_channelprog_get(struct pvr2_ctrl *cptr,int *vp) +{ + *vp = cptr->hdw->freqProgSlot; + return 0; +} + +static int ctrl_channelprog_set(struct pvr2_ctrl *cptr,int m,int v) +{ + struct pvr2_hdw *hdw = cptr->hdw; + if ((v >= 0) && (v <= FREQTABLE_SIZE)) { + hdw->freqProgSlot = v; + } + return 0; +} + +static int ctrl_channel_get(struct pvr2_ctrl *cptr,int *vp) +{ + *vp = cptr->hdw->freqSlot; + return 0; +} + +static int ctrl_channel_set(struct pvr2_ctrl *cptr,int m,int v) +{ + unsigned freq = 0; + struct pvr2_hdw *hdw = cptr->hdw; + hdw->freqSlot = v; + if ((hdw->freqSlot > 0) && (hdw->freqSlot <= FREQTABLE_SIZE)) { + freq = hdw->freqTable[hdw->freqSlot-1]; + } + if (freq && (freq != hdw->freqVal)) { + hdw->freqVal = freq; + hdw->freqDirty = !0; + } + return 0; +} + +static int ctrl_freq_get(struct pvr2_ctrl *cptr,int *vp) +{ + *vp = cptr->hdw->freqVal; + return 0; +} + +static int ctrl_freq_is_dirty(struct pvr2_ctrl *cptr) +{ + return cptr->hdw->freqDirty != 0; +} + +static void ctrl_freq_clear_dirty(struct pvr2_ctrl *cptr) +{ + cptr->hdw->freqDirty = 0; +} + +static int ctrl_freq_set(struct pvr2_ctrl *cptr,int m,int v) +{ + struct pvr2_hdw *hdw = cptr->hdw; + hdw->freqVal = v; + hdw->freqDirty = !0; + hdw->freqSlot = 0; + return 0; +} + +static int ctrl_streamingenabled_get(struct pvr2_ctrl *cptr,int *vp) +{ + *vp = cptr->hdw->flag_streaming_enabled; + return 0; +} + +static int ctrl_hsm_get(struct pvr2_ctrl *cptr,int *vp) +{ + int result = pvr2_hdw_is_hsm(cptr->hdw); + *vp = PVR2_CVAL_HSM_FULL; + if (result < 0) *vp = PVR2_CVAL_HSM_FAIL; + if (result) *vp = PVR2_CVAL_HSM_HIGH; + return 0; +} + +static int ctrl_stdavail_get(struct pvr2_ctrl *cptr,int *vp) +{ + *vp = cptr->hdw->std_mask_avail; + return 0; +} + +static int ctrl_stdavail_set(struct pvr2_ctrl *cptr,int m,int v) +{ + struct pvr2_hdw *hdw = cptr->hdw; + v4l2_std_id ns; + ns = hdw->std_mask_avail; + ns = (ns & ~m) | (v & m); + if (ns == hdw->std_mask_avail) return 0; + hdw->std_mask_avail = ns; + pvr2_hdw_internal_set_std_avail(hdw); + pvr2_hdw_internal_find_stdenum(hdw); + return 0; +} + +static int ctrl_std_val_to_sym(struct pvr2_ctrl *cptr,int msk,int val, + char *bufPtr,unsigned int bufSize, + unsigned int *len) +{ + *len = pvr2_std_id_to_str(bufPtr,bufSize,msk & val); + return 0; +} + +static int ctrl_std_sym_to_val(struct pvr2_ctrl *cptr, + const char *bufPtr,unsigned int bufSize, + int *mskp,int *valp) +{ + int ret; + v4l2_std_id id; + ret = pvr2_std_str_to_id(&id,bufPtr,bufSize); + if (ret < 0) return ret; + if (mskp) *mskp = id; + if (valp) *valp = id; + return 0; +} + +static int ctrl_stdcur_get(struct pvr2_ctrl *cptr,int *vp) +{ + *vp = cptr->hdw->std_mask_cur; + return 0; +} + +static int ctrl_stdcur_set(struct pvr2_ctrl *cptr,int m,int v) +{ + struct pvr2_hdw *hdw = cptr->hdw; + v4l2_std_id ns; + ns = hdw->std_mask_cur; + ns = (ns & ~m) | (v & m); + if (ns == hdw->std_mask_cur) return 0; + hdw->std_mask_cur = ns; + hdw->std_dirty = !0; + pvr2_hdw_internal_find_stdenum(hdw); + return 0; +} + +static int ctrl_stdcur_is_dirty(struct pvr2_ctrl *cptr) +{ + return cptr->hdw->std_dirty != 0; +} + +static void ctrl_stdcur_clear_dirty(struct pvr2_ctrl *cptr) +{ + cptr->hdw->std_dirty = 0; +} + +static int ctrl_signal_get(struct pvr2_ctrl *cptr,int *vp) +{ + *vp = ((pvr2_hdw_get_signal_status_internal(cptr->hdw) & + PVR2_SIGNAL_OK) ? 1 : 0); + return 0; +} + +static int ctrl_subsys_get(struct pvr2_ctrl *cptr,int *vp) +{ + *vp = cptr->hdw->subsys_enabled_mask; + return 0; +} + +static int ctrl_subsys_set(struct pvr2_ctrl *cptr,int m,int v) +{ + pvr2_hdw_subsys_bit_chg_no_lock(cptr->hdw,m,v); + return 0; +} + +static int ctrl_subsys_stream_get(struct pvr2_ctrl *cptr,int *vp) +{ + *vp = cptr->hdw->subsys_stream_mask; + return 0; +} + +static int ctrl_subsys_stream_set(struct pvr2_ctrl *cptr,int m,int v) +{ + pvr2_hdw_subsys_stream_bit_chg_no_lock(cptr->hdw,m,v); + return 0; +} + +static int ctrl_stdenumcur_set(struct pvr2_ctrl *cptr,int m,int v) +{ + struct pvr2_hdw *hdw = cptr->hdw; + if (v < 0) return -EINVAL; + if (v > hdw->std_enum_cnt) return -EINVAL; + hdw->std_enum_cur = v; + if (!v) return 0; + v--; + if (hdw->std_mask_cur == hdw->std_defs[v].id) return 0; + hdw->std_mask_cur = hdw->std_defs[v].id; + hdw->std_dirty = !0; + return 0; +} + + +static int ctrl_stdenumcur_get(struct pvr2_ctrl *cptr,int *vp) +{ + *vp = cptr->hdw->std_enum_cur; + return 0; +} -static int pvr2_ctl_set_chanprog_id(struct pvr2_hdw *hdw,int ctl_id,int value); -static int pvr2_ctl_get_chanprog_id(struct pvr2_hdw *hdw,int ctl_id); -static int pvr2_ctl_get_signal(struct pvr2_hdw *hdw,int ctl_id); -static int pvr2_ctl_get_streaming(struct pvr2_hdw *hdw,int ctl_id); -static int pvr2_ctl_get_hsm(struct pvr2_hdw *hdw,int ctl_id); -static int pvr2_ctl_get_subsys_mask(struct pvr2_hdw *hdw,int ctl_id); -static int pvr2_ctl_set_subsys_mask(struct pvr2_hdw *hdw,int ctl_id,int val); -static int pvr2_ctl_get_subsys_stream_mask(struct pvr2_hdw *hdw,int ctl_id); -static int pvr2_ctl_set_subsys_stream_mask(struct pvr2_hdw *hdw,int ctl_id, - int val); -static struct pvr2_ctl_def control_defs[PVR2_CID_COUNT] = +static int ctrl_stdenumcur_is_dirty(struct pvr2_ctrl *cptr) { - [PVR2_CID_BRIGHTNESS] = { - .name = "Brightness", - .min_value = 0, - .max_value = 255, + return cptr->hdw->std_dirty != 0; +} + + +static void ctrl_stdenumcur_clear_dirty(struct pvr2_ctrl *cptr) +{ + cptr->hdw->std_dirty = 0; +} + + +#define DEFINT(vmin,vmax) \ + .type = pvr2_ctl_int, \ + .def.type_int.min_value = vmin, \ + .def.type_int.max_value = vmax + +#define DEFENUM(tab) \ + .type = pvr2_ctl_enum, \ + .def.type_enum.count = (sizeof(tab)/sizeof((tab)[0])), \ + .def.type_enum.value_names = tab + +#define DEFMASK(msk,tab) \ + .type = pvr2_ctl_bitmask, \ + .def.type_bitmask.valid_bits = msk, \ + .def.type_bitmask.bit_names = tab + +#define DEFREF(vname) \ + .set_value = ctrl_set_##vname, \ + .get_value = ctrl_get_##vname, \ + .is_dirty = ctrl_isdirty_##vname, \ + .clear_dirty = ctrl_cleardirty_##vname + + +#define VCREATE_FUNCS(vname) \ +static int ctrl_get_##vname(struct pvr2_ctrl *cptr,int *vp) \ +{*vp = cptr->hdw->vname##_val; return 0;} \ +static int ctrl_set_##vname(struct pvr2_ctrl *cptr,int m,int v) \ +{cptr->hdw->vname##_val = v; cptr->hdw->vname##_dirty = !0; return 0;} \ +static int ctrl_isdirty_##vname(struct pvr2_ctrl *cptr) \ +{return cptr->hdw->vname##_dirty != 0;} \ +static void ctrl_cleardirty_##vname(struct pvr2_ctrl *cptr) \ +{cptr->hdw->vname##_dirty = 0;} + +VCREATE_FUNCS(brightness) +VCREATE_FUNCS(contrast) +VCREATE_FUNCS(saturation) +VCREATE_FUNCS(hue) +VCREATE_FUNCS(volume) +VCREATE_FUNCS(balance) +VCREATE_FUNCS(bass) +VCREATE_FUNCS(treble) +VCREATE_FUNCS(mute) +VCREATE_FUNCS(srate) +VCREATE_FUNCS(audiobitrate) +VCREATE_FUNCS(audiocrc) +VCREATE_FUNCS(audioemphasis) +VCREATE_FUNCS(vbr) +VCREATE_FUNCS(videobitrate) +VCREATE_FUNCS(videopeak) +VCREATE_FUNCS(input) +VCREATE_FUNCS(audiomode) +VCREATE_FUNCS(res_hor) +VCREATE_FUNCS(res_ver) +VCREATE_FUNCS(interlace) +VCREATE_FUNCS(audiolayer) + +#define MIN_FREQ 55250000L +#define MAX_FREQ 850000000L + +/* Table definition of all controls which can be manipulated */ +static const struct pvr2_ctl_info control_defs[] = { + { + .v4l_id = V4L2_CID_BRIGHTNESS, + .desc = "Brightness", + .name = "brightness", .default_value = 128, - }, - [PVR2_CID_CONTRAST] = { - .name = "Contrast", - .min_value = 0, - .max_value = 127, + DEFREF(brightness), + DEFINT(0,255), + },{ + .v4l_id = V4L2_CID_CONTRAST, + .desc = "Contrast", + .name = "contrast", .default_value = 68, - }, - [PVR2_CID_SATURATION] = { - .name = "Saturation", - .min_value = 0, - .max_value = 127, + DEFREF(contrast), + DEFINT(0,127), + },{ + .v4l_id = V4L2_CID_SATURATION, + .desc = "Saturation", + .name = "saturation", .default_value = 64, - }, - [PVR2_CID_HUE] = { - .name = "Hue", - .min_value = -128, - .max_value = 127, + DEFREF(saturation), + DEFINT(0,127), + },{ + .v4l_id = V4L2_CID_HUE, + .desc = "Hue", + .name = "hue", .default_value = 0, - }, - [PVR2_CID_VOLUME] = { - .name = "Volume", - .min_value = 0, - .max_value = 65535, + DEFREF(hue), + DEFINT(-128,127), + },{ + .v4l_id = V4L2_CID_AUDIO_VOLUME, + .desc = "Volume", + .name = "volume", .default_value = 65535, - }, - [PVR2_CID_BALANCE] = { - .name = "Balance", - .min_value = -32768, - .max_value = 32767, + DEFREF(volume), + DEFINT(0,65535), + },{ + .v4l_id = V4L2_CID_AUDIO_BALANCE, + .desc = "Balance", + .name = "balance", .default_value = 0, - }, - [PVR2_CID_BASS] = { - .name = "Bass", - .min_value = -32768, - .max_value = 32767, + DEFREF(balance), + DEFINT(-32768,32767), + },{ + .v4l_id = V4L2_CID_AUDIO_BASS, + .desc = "Bass", + .name = "bass", .default_value = 0, - }, - [PVR2_CID_TREBLE] = { - .name = "Treble", - .min_value = -32768, - .max_value = 32767, + DEFREF(bass), + DEFINT(-32768,32767), + },{ + .v4l_id = V4L2_CID_AUDIO_TREBLE, + .desc = "Treble", + .name = "treble", .default_value = 0, - }, - [PVR2_CID_MUTE] = { - .name = "Mute", - .min_value = 0, - .max_value = 1, + DEFREF(treble), + DEFINT(-32768,32767), + },{ + .v4l_id = V4L2_CID_AUDIO_MUTE, + .desc = "Mute", + .name = "mute", .default_value = 0, - }, - [PVR2_CID_SRATE] = { - .name = "Sample rate", - .min_value = PVR2_CVAL_SRATE_MIN, - .max_value = PVR2_CVAL_SRATE_MAX, + DEFREF(mute), + DEFINT(0,1), + },{ + .v4l_id = V4L2_CID_PVR_SRATE, + .desc = "Sample rate", + .name = "srate", .default_value = PVR2_CVAL_SRATE_48, - VDEF(control_values_srate), - }, - [PVR2_CID_AUDIOBITRATE] = { - .name = "Audio Bitrate", - .min_value = PVR2_CVAL_AUDIOBITRATE_MIN, - .max_value = PVR2_CVAL_AUDIOBITRATE_MAX, + DEFREF(srate), + DEFENUM(control_values_srate), + },{ + .v4l_id = V4L2_CID_PVR_AUDIOBITRATE, + .desc = "Audio Bitrate", + .name = "audio_bitrate", .default_value = PVR2_CVAL_AUDIOBITRATE_224, - VDEF(control_values_audiobitrate), - }, - [PVR2_CID_AUDIOCRC] = { - .name = "Audio CRC", - .min_value = 0, - .max_value = 1, + DEFREF(audiobitrate), + DEFENUM(control_values_audiobitrate), + },{ + .v4l_id = V4L2_CID_PVR_AUDIOCRC, + .desc = "Audio CRC", + .name = "audio_crc", .default_value = 1, - }, - [PVR2_CID_AUDIOEMPHASIS] = { - .name = "Audio Emphasis", - .min_value = PVR2_CVAL_AUDIOEMPHASIS_MIN, - .max_value = PVR2_CVAL_AUDIOEMPHASIS_MAX, + DEFREF(audiocrc), + DEFINT(0,1), + },{ + .desc = "Audio Layer", + .name = "audio_layer", + .default_value = 2, + DEFREF(audiolayer), + DEFINT(0,3), + },{ + .v4l_id = V4L2_CID_PVR_AUDIOEMPHASIS, + .desc = "Audio Emphasis", + .name = "audio_emphasis", .default_value = PVR2_CVAL_AUDIOEMPHASIS_NONE, - VDEF(control_values_audioemphasis), - }, - [PVR2_CID_VBR] = { - .name = "Variable video bitrate", - .min_value = 0, - .max_value = 1, + DEFREF(audioemphasis), + DEFENUM(control_values_audioemphasis), + },{ + .desc = "Interlace mode", + .name = "interlace", + .internal_id = PVR2_CID_INTERLACE, .default_value = 0, - }, - [PVR2_CID_AVERAGEVIDEOBITRATE] = { - .name = "Average video bitrate", - .min_value = 1, - .max_value = 20000000, + DEFREF(interlace), + DEFINT(0,1), + },{ + .v4l_id = V4L2_CID_PVR_VBR, + .desc = "Variable video bitrate", + .name = "vbr", + .default_value = 0, + DEFREF(vbr), + DEFINT(0,1), + },{ + .v4l_id = V4L2_CID_PVR_VIDEOBITRATE, + .desc = "Average video bitrate", + .name = "video_average_bitrate", .default_value = 6000000, - }, - [PVR2_CID_PEAKVIDEOBITRATE] = { - .name = "Peak video bitrate", - .min_value = 1, - .max_value = 20000000, + DEFREF(videobitrate), + DEFINT(500000,20000000), + },{ + .v4l_id = V4L2_CID_PVR_VIDEOPEAK, + .desc = "Peak video bitrate", + .name = "video_peak_bitrate", .default_value = 6000000, - }, - [PVR2_CID_VIDEOSTANDARD] = { - .name = "Video Standard", - .min_value = PVR2_CVAL_VIDEOSTANDARD_MIN, - .max_value = PVR2_CVAL_VIDEOSTANDARD_MAX, - .default_value = PVR2_CVAL_VIDEOSTANDARD_NTSC_M, - VDEF(control_values_videostandard), - }, - [PVR2_CID_INPUT] = { - .name = "Video Source", - .min_value = PVR2_CVAL_INPUT_MIN, - .max_value = PVR2_CVAL_INPUT_MAX, + DEFREF(videopeak), + DEFINT(500000,20000000), + },{ + .desc = "Video Source", + .name = "input", + .internal_id = PVR2_CID_INPUT, .default_value = PVR2_CVAL_INPUT_TV, - VDEF(control_values_input), - }, - [PVR2_CID_AUDIOMODE] = { - .name = "Audio Mode", - .min_value = PVR2_CVAL_AUDIOMODE_MIN, - .max_value = PVR2_CVAL_AUDIOMODE_MAX, - .default_value = PVR2_CVAL_AUDIOMODE_STEREO, - VDEF(control_values_audiomode), - }, - [PVR2_CID_FREQUENCY] = { - .name = "Tuner Frequency (Hz)", - .min_value = 55250000L, - .max_value = 850000000L, + DEFREF(input), + DEFENUM(control_values_input), + },{ + .desc = "Audio Mode", + .name = "audio_mode", + .internal_id = PVR2_CID_AUDIOMODE, + .default_value = V4L2_TUNER_MODE_STEREO, + DEFREF(audiomode), + DEFENUM(control_values_audiomode), + },{ + .desc = "Tuner Frequency (Hz)", + .name = "frequency", + .internal_id = PVR2_CID_FREQUENCY, .default_value = 175250000L, - }, - [PVR2_CID_HRES] = { - .name = "Horizontal capture resolution", - .min_value = 320, - .max_value = 720, + .set_value = ctrl_freq_set, + .get_value = ctrl_freq_get, + .is_dirty = ctrl_freq_is_dirty, + .clear_dirty = ctrl_freq_clear_dirty, + DEFINT(MIN_FREQ,MAX_FREQ), + },{ + .desc = "Channel", + .name = "channel", + .set_value = ctrl_channel_set, + .get_value = ctrl_channel_get, + DEFINT(0,FREQTABLE_SIZE), + },{ + .desc = "Channel Program Frequency", + .name = "freq_table_value", + .set_value = ctrl_channelfreq_set, + .get_value = ctrl_channelfreq_get, + DEFINT(MIN_FREQ,MAX_FREQ), + },{ + .desc = "Channel Program ID", + .name = "freq_table_channel", + .set_value = ctrl_channelprog_set, + .get_value = ctrl_channelprog_get, + DEFINT(0,FREQTABLE_SIZE), + },{ + .desc = "Horizontal capture resolution", + .name = "resolution_hor", + .internal_id = PVR2_CID_HRES, .default_value = 720, - }, - [PVR2_CID_VRES] = { - .name = "Vertical capture resolution", - .min_value = 200, - .max_value = 625, + DEFREF(res_hor), + DEFINT(320,720), + },{ + .desc = "Vertical capture resolution", + .name = "resolution_ver", + .internal_id = PVR2_CID_VRES, .default_value = 480, - }, - [PVR2_CID_INTERLACE] = { - .name = "Interlace mode", - .min_value = 0, - .max_value = 1, - .default_value = 0, - }, - [PVR2_CID_AUDIOLAYER] = { - .name = "Audio Layer", - .min_value = 0, /* This is all a wild guess */ - .max_value = 3, - .default_value = 2, /* Appears to be all that is supported */ - }, - [PVR2_CID_CHANNEL] = { - .name = "Channel", - .min_value = 0, - .max_value = FREQTABLE_SIZE, - .default_value = 0, - }, - [PVR2_CID_CHANPROG_ID] = { - .name = "Channel Program ID", - .min_value = 0, - .max_value = FREQTABLE_SIZE, - .default_value = 0, - }, - [PVR2_CID_CHANPROG_FREQ] = { - .name = "Channel Program Frequency", - .min_value = 55250000L, - .max_value = 850000000L, + DEFREF(res_ver), + DEFINT(200,625), + },{ + .desc = "Streaming Enabled", + .name = "streaming_enabled", + .get_value = ctrl_streamingenabled_get, + DEFINT(0,1), + },{ + .desc = "USB Speed", + .name = "usb_speed", + .get_value = ctrl_hsm_get, + DEFENUM(control_values_hsm), + },{ + .desc = "Signal Present", + .name = "signal_present", + .get_value = ctrl_signal_get, + DEFINT(0,1), + },{ + .desc = "Video Standards Available Mask", + .name = "video_standard_mask_available", + .internal_id = PVR2_CID_STDAVAIL, .skip_init = !0, - .set_func = pvr2_ctl_set_chanprog_id, - .get_func = pvr2_ctl_get_chanprog_id, - }, - [PVR2_CID_SIGNAL_PRESENT] = { - .name = "Signal Present", - .min_value = 0, - .max_value = 1, - .get_func = pvr2_ctl_get_signal, - }, - [PVR2_CID_STREAMING_ENABLED] = { - .name = "Streaming Enabled", - .min_value = 0, - .max_value = 1, - .get_func = pvr2_ctl_get_streaming, - }, - [PVR2_CID_HSM] = { - .name = "USB Speed", - .min_value = PVR2_CVAL_HSM_MIN, - .max_value = PVR2_CVAL_HSM_MAX, - .get_func = pvr2_ctl_get_hsm, - VDEF(control_values_hsm), - }, - [PVR2_CID_SUBSYS_MASK] = { - .name = "Subsystem enabled mask", - .min_value = 0, - .max_value = 0x7fffffff, + .get_value = ctrl_stdavail_get, + .set_value = ctrl_stdavail_set, + .val_to_sym = ctrl_std_val_to_sym, + .sym_to_val = ctrl_std_sym_to_val, + .type = pvr2_ctl_bitmask, + },{ + .desc = "Video Standards In Use Mask", + .name = "video_standard_mask_active", + .internal_id = PVR2_CID_STDCUR, .skip_init = !0, - .get_func = pvr2_ctl_get_subsys_mask, - .set_func = pvr2_ctl_set_subsys_mask, - }, - [PVR2_CID_SUBSYS_STREAM_MASK] = { - .name = "Subsystem stream mask", - .min_value = 0, - .max_value = 0x7fffffff, + .get_value = ctrl_stdcur_get, + .set_value = ctrl_stdcur_set, + .is_dirty = ctrl_stdcur_is_dirty, + .clear_dirty = ctrl_stdcur_clear_dirty, + .val_to_sym = ctrl_std_val_to_sym, + .sym_to_val = ctrl_std_sym_to_val, + .type = pvr2_ctl_bitmask, + },{ + .desc = "Subsystem enabled mask", + .name = "debug_subsys_mask", .skip_init = !0, - .get_func = pvr2_ctl_get_subsys_stream_mask, - .set_func = pvr2_ctl_set_subsys_stream_mask, - }, + .get_value = ctrl_subsys_get, + .set_value = ctrl_subsys_set, + DEFMASK(PVR2_SUBSYS_ALL,control_values_subsystem), + },{ + .desc = "Subsystem stream mask", + .name = "debug_subsys_stream_mask", + .skip_init = !0, + .get_value = ctrl_subsys_stream_get, + .set_value = ctrl_subsys_stream_set, + DEFMASK(PVR2_SUBSYS_ALL,control_values_subsystem), + },{ + .desc = "Video Standard Name", + .name = "video_standard", + .internal_id = PVR2_CID_STDENUM, + .skip_init = !0, + .get_value = ctrl_stdenumcur_get, + .set_value = ctrl_stdenumcur_set, + .is_dirty = ctrl_stdenumcur_is_dirty, + .clear_dirty = ctrl_stdenumcur_clear_dirty, + .type = pvr2_ctl_enum, + } }; -#undef VDEF +#define CTRL_COUNT (sizeof(control_defs)/sizeof(control_defs[0])) const char *pvr2_config_get_name(enum pvr2_config cfg) @@ -511,21 +849,22 @@ int pvr2_upload_firmware1(struct pvr2_hdw *hdw) static const char *fw_files_29xxx[] = { "v4l-pvrusb2-29xxx-01.fw", }; +#ifdef CONFIG_VIDEO_PVRUSB2_24XXX static const char *fw_files_24xxx[] = { "v4l-pvrusb2-24xxx-01.fw", }; - static const struct { - const char **lst; - unsigned int cnt; - } fw_file_defs[] = { +#endif + static const struct pvr2_string_table fw_file_defs[] = { [PVR2_HDW_TYPE_29XXX] = { fw_files_29xxx, sizeof(fw_files_29xxx)/sizeof(fw_files_29xxx[0]), }, +#ifdef CONFIG_VIDEO_PVRUSB2_24XXX [PVR2_HDW_TYPE_24XXX] = { fw_files_24xxx, sizeof(fw_files_24xxx)/sizeof(fw_files_24xxx[0]), }, +#endif }; hdw->fw1_state = FW1_STATE_FAILED; // default result @@ -605,7 +944,7 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) int ret = 0; int fwidx; static const char *fw_files[] = { - "v4l-cx2341x-enc.fw", + CX2341X_FIRM_ENC_FILENAME, }; trace_firmware("pvr2_upload_firmware2"); @@ -618,7 +957,6 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) ret = 0; /* First prepare firmware loading */ - ret |= pvr2_hdw_cmd_soft_reset(hdw); ret |= pvr2_write_register(hdw, 0x0048, 0xffffffff); /*interrupt mask*/ ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000088); /*gpio dir*/ ret |= pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000008); /*gpio output state*/ @@ -704,17 +1042,17 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) pvr2_trace(PVR2_TRACE_ERROR_LEGS, "firmware2 upload post-proc failure"); } else { - hdw->subsys_enabled_mask |= PVR2_SUBSYS_ENC_FIRMWARE; + hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_FIRMWARE); } return ret; } #define FIRMWARE_RECOVERY_BITS \ - (PVR2_SUBSYS_ENC_CFG | \ - PVR2_SUBSYS_ENC_RUN | \ - PVR2_SUBSYS_ENC_FIRMWARE | \ - PVR2_SUBSYS_USBSTREAM_RUN) + ((1<<PVR2_SUBSYS_B_ENC_CFG) | \ + (1<<PVR2_SUBSYS_B_ENC_RUN) | \ + (1<<PVR2_SUBSYS_B_ENC_FIRMWARE) | \ + (1<<PVR2_SUBSYS_B_USBSTREAM_RUN)) /* @@ -782,7 +1120,7 @@ void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw, vmsk = (nmsk ^ hdw->subsys_enabled_mask) & hdw->subsys_enabled_mask; if (vmsk) { - if (vmsk & PVR2_SUBSYS_ENC_RUN) { + if (vmsk & (1<<PVR2_SUBSYS_B_ENC_RUN)) { pvr2_trace(PVR2_TRACE_CTL, "/*---TRACE_CTL----*/" " pvr2_encoder_stop"); @@ -795,13 +1133,13 @@ void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw, continue; } } - if (vmsk & PVR2_SUBSYS_USBSTREAM_RUN) { + if (vmsk & (1<<PVR2_SUBSYS_B_USBSTREAM_RUN)) { pvr2_trace(PVR2_TRACE_CTL, "/*---TRACE_CTL----*/" " pvr2_hdw_cmd_usbstream(0)"); pvr2_hdw_cmd_usbstream(hdw,0); } - if (vmsk & PVR2_SUBSYS_DIGITIZER_RUN) { + if (vmsk & (1<<PVR2_SUBSYS_B_DIGITIZER_RUN)) { pvr2_trace(PVR2_TRACE_CTL, "/*---TRACE_CTL----*/" " decoder disable"); @@ -814,7 +1152,7 @@ void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw, " No decoder present"); } hdw->subsys_enabled_mask &= - ~PVR2_SUBSYS_DIGITIZER_RUN; + ~(1<<PVR2_SUBSYS_B_DIGITIZER_RUN); } if (vmsk & PVR2_SUBSYS_CFG_ALL) { hdw->subsys_enabled_mask &= @@ -823,7 +1161,7 @@ void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw, } vmsk = (nmsk ^ hdw->subsys_enabled_mask) & nmsk; if (vmsk) { - if (vmsk & PVR2_SUBSYS_ENC_FIRMWARE) { + if (vmsk & (1<<PVR2_SUBSYS_B_ENC_FIRMWARE)) { pvr2_trace(PVR2_TRACE_CTL, "/*---TRACE_CTL----*/" " pvr2_upload_firmware2"); @@ -836,7 +1174,7 @@ void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw, break; } } - if (vmsk & PVR2_SUBSYS_ENC_CFG) { + if (vmsk & (1<<PVR2_SUBSYS_B_ENC_CFG)) { pvr2_trace(PVR2_TRACE_CTL, "/*---TRACE_CTL----*/" " pvr2_encoder_configure"); @@ -849,7 +1187,7 @@ void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw, continue; } } - if (vmsk & PVR2_SUBSYS_DIGITIZER_RUN) { + if (vmsk & (1<<PVR2_SUBSYS_B_DIGITIZER_RUN)) { pvr2_trace(PVR2_TRACE_CTL, "/*---TRACE_CTL----*/" " decoder enable"); @@ -862,15 +1200,15 @@ void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw, " No decoder present"); } hdw->subsys_enabled_mask |= - PVR2_SUBSYS_DIGITIZER_RUN; + (1<<PVR2_SUBSYS_B_DIGITIZER_RUN); } - if (vmsk & PVR2_SUBSYS_USBSTREAM_RUN) { + if (vmsk & (1<<PVR2_SUBSYS_B_USBSTREAM_RUN)) { pvr2_trace(PVR2_TRACE_CTL, "/*---TRACE_CTL----*/" " pvr2_hdw_cmd_usbstream(1)"); pvr2_hdw_cmd_usbstream(hdw,!0); } - if (vmsk & PVR2_SUBSYS_ENC_RUN) { + if (vmsk & (1<<PVR2_SUBSYS_B_ENC_RUN)) { pvr2_trace(PVR2_TRACE_CTL, "/*---TRACE_CTL----*/" " pvr2_encoder_start"); @@ -945,12 +1283,6 @@ void pvr2_hdw_subsys_stream_bit_chg(struct pvr2_hdw *hdw, } -static int pvr2_ctl_get_streaming(struct pvr2_hdw *hdw,int ctl_id) -{ - return hdw->flag_streaming_enabled != 0; -} - - int pvr2_hdw_set_streaming_no_lock(struct pvr2_hdw *hdw,int enableFl) { if ((!enableFl) == !(hdw->flag_streaming_enabled)) return 0; @@ -1021,6 +1353,17 @@ static int get_default_tuner_type(struct pvr2_hdw *hdw) } +static v4l2_std_id get_default_standard(struct pvr2_hdw *hdw) +{ + int unit_number = hdw->unit_number; + int tp = 0; + if ((unit_number >= 0) && (unit_number < PVR_NUM)) { + tp = video_std[unit_number]; + } + return tp; +} + + static unsigned int get_default_error_tolerance(struct pvr2_hdw *hdw) { int unit_number = hdw->unit_number; @@ -1057,11 +1400,65 @@ static int pvr2_hdw_check_firmware(struct pvr2_hdw *hdw) return result == 0; } +static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw) +{ + char buf[40]; + unsigned int bcnt; + v4l2_std_id std1,std2; + + std1 = get_default_standard(hdw); + + bcnt = pvr2_std_id_to_str(buf,sizeof(buf),hdw->std_mask_eeprom); + pvr2_trace(PVR2_TRACE_INIT, + "Supported video standard(s) reported by eeprom: %.*s", + bcnt,buf); + + hdw->std_mask_avail = hdw->std_mask_eeprom; + + std2 = std1 & ~hdw->std_mask_avail; + if (std2) { + bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std2); + pvr2_trace(PVR2_TRACE_INIT, + "Expanding supported video standards" + " to include: %.*s", + bcnt,buf); + hdw->std_mask_avail |= std2; + } + + pvr2_hdw_internal_set_std_avail(hdw); + + if (std1) { + bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std1); + pvr2_trace(PVR2_TRACE_INIT, + "Initial video standard forced to %.*s", + bcnt,buf); + hdw->std_mask_cur = std1; + hdw->std_dirty = !0; + pvr2_hdw_internal_find_stdenum(hdw); + return; + } + + if (hdw->std_enum_cnt > 1) { + // Autoselect the first listed standard + hdw->std_enum_cur = 1; + hdw->std_mask_cur = hdw->std_defs[hdw->std_enum_cur-1].id; + hdw->std_dirty = !0; + pvr2_trace(PVR2_TRACE_INIT, + "Initial video standard auto-selected to %s", + hdw->std_defs[hdw->std_enum_cur-1].name); + return; + } + + pvr2_trace(PVR2_TRACE_EEPROM, + "Unable to select a viable initial video standard"); +} + static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) { int ret; unsigned int idx; + struct pvr2_ctrl *cptr; int reloadFl = 0; if (!reloadFl) { reloadFl = (hdw->usb_intf->cur_altsetting->desc.bNumEndpoints @@ -1094,7 +1491,11 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) } if (!pvr2_hdw_dev_ok(hdw)) return; - pvr2_i2c_core_init(hdw); + for (idx = 0; idx < pvr2_client_lists[hdw->hdw_type].cnt; idx++) { + request_module(pvr2_client_lists[hdw->hdw_type].lst[idx]); + } + + pvr2_hdw_cmd_powerup(hdw); if (!pvr2_hdw_dev_ok(hdw)) return; if (pvr2_upload_firmware2(hdw)){ @@ -1103,14 +1504,20 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) return; } - for (idx = 0; idx < PVR2_CID_COUNT; idx++) { - if (control_defs[idx].skip_init) continue; - pvr2_hdw_set_ctl_value_internal( - hdw,idx,control_defs[idx].default_value); + // This step MUST happen after the earlier powerup step. + pvr2_i2c_core_init(hdw); + if (!pvr2_hdw_dev_ok(hdw)) return; + + for (idx = 0; idx < CTRL_COUNT; idx++) { + cptr = hdw->controls + idx; + if (cptr->info->skip_init) continue; + if (!cptr->info->set_value) continue; + cptr->info->set_value(cptr,~0,cptr->info->default_value); } - pvr2_reset_ctl_endpoints(hdw); - if (!pvr2_hdw_dev_ok(hdw)) return; + // Do not use pvr2_reset_ctl_endpoints() here. It is not + // thread-safe against the normal pvr2_send_request() mechanism. + // (We should make it thread safe). ret = pvr2_hdw_get_eeprom_addr(hdw); if (!pvr2_hdw_dev_ok(hdw)) return; @@ -1123,6 +1530,8 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) if (!pvr2_hdw_dev_ok(hdw)) return; } + pvr2_hdw_setup_std(hdw); + if (!get_default_tuner_type(hdw)) { pvr2_trace(PVR2_TRACE_INIT, "pvr2_hdw_setup: Tuner type overridden to %d", @@ -1135,9 +1544,6 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) if (!pvr2_hdw_dev_ok(hdw)) return; - pvr2_eeprom_set_default_standard(hdw); - if (!pvr2_hdw_dev_ok(hdw)) return; - pvr2_hdw_commit_ctl_internal(hdw); if (!pvr2_hdw_dev_ok(hdw)) return; @@ -1241,6 +1647,8 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, unsigned int idx,cnt1,cnt2; struct pvr2_hdw *hdw; unsigned int hdw_type; + int valid_std_mask; + struct pvr2_ctrl *cptr; __u8 ifnum; hdw_type = devid - pvr2_device_table; @@ -1256,8 +1664,62 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, hdw,pvr2_device_names[hdw_type]); if (!hdw) goto fail; memset(hdw,0,sizeof(*hdw)); + + hdw->controls = kmalloc(sizeof(struct pvr2_ctrl) * CTRL_COUNT, + GFP_KERNEL); + if (!hdw->controls) goto fail; + memset(hdw->controls,0,sizeof(struct pvr2_ctrl) * CTRL_COUNT); hdw->hdw_type = hdw_type; + for (idx = 0; idx < 32; idx++) { + hdw->std_mask_ptrs[idx] = hdw->std_mask_names[idx]; + } + + for (idx = 0; idx < CTRL_COUNT; idx++) { + cptr = hdw->controls + idx; + cptr->hdw = hdw; + cptr->info = control_defs+idx; + } + + // Initialize video standard enum dynamic control + cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDENUM); + if (cptr) { + memcpy(&hdw->std_info_enum,cptr->info, + sizeof(hdw->std_info_enum)); + cptr->info = &hdw->std_info_enum; + + } + // Initialize control data regarding video standard masks + valid_std_mask = pvr2_std_get_usable(); + for (idx = 0; idx < 32; idx++) { + if (!(valid_std_mask & (1 << idx))) continue; + cnt1 = pvr2_std_id_to_str( + hdw->std_mask_names[idx], + sizeof(hdw->std_mask_names[idx])-1, + 1 << idx); + hdw->std_mask_names[idx][cnt1] = 0; + } + cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDAVAIL); + if (cptr) { + memcpy(&hdw->std_info_avail,cptr->info, + sizeof(hdw->std_info_avail)); + cptr->info = &hdw->std_info_avail; + hdw->std_info_avail.def.type_bitmask.bit_names = + hdw->std_mask_ptrs; + hdw->std_info_avail.def.type_bitmask.valid_bits = + valid_std_mask; + } + cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR); + if (cptr) { + memcpy(&hdw->std_info_cur,cptr->info, + sizeof(hdw->std_info_cur)); + cptr->info = &hdw->std_info_cur; + hdw->std_info_cur.def.type_bitmask.bit_names = + hdw->std_mask_ptrs; + hdw->std_info_avail.def.type_bitmask.valid_bits = + valid_std_mask; + } + hdw->eeprom_addr = -1; hdw->unit_number = -1; hdw->v4l_minor_number = -1; @@ -1298,7 +1760,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, /* Initialize the mask of subsystems that we will shut down when we stop streaming. */ hdw->subsys_stream_mask = PVR2_SUBSYS_RUN_ALL; - hdw->subsys_stream_mask |= PVR2_SUBSYS_ENC_CFG; + hdw->subsys_stream_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG); pvr2_trace(PVR2_TRACE_INIT,"subsys_stream_mask: 0x%lx", hdw->subsys_stream_mask); @@ -1319,6 +1781,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, if (hdw->ctl_write_urb) usb_free_urb(hdw->ctl_write_urb); if (hdw->ctl_read_buffer) kfree(hdw->ctl_read_buffer); if (hdw->ctl_write_buffer) kfree(hdw->ctl_write_buffer); + if (hdw->controls) kfree(hdw->controls); kfree(hdw); } return 0; @@ -1383,6 +1846,9 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw) unit_pointers[hdw->unit_number] = 0; } } while (0); up(&pvr2_unit_sem); + kfree(hdw->controls); + if (hdw->std_defs) kfree(hdw->std_defs); + if (hdw->std_enum_names) kfree(hdw->std_enum_names); kfree(hdw); } @@ -1411,133 +1877,137 @@ void pvr2_hdw_disconnect(struct pvr2_hdw *hdw) } -static int pvr2_ctl_set_chanprog_id(struct pvr2_hdw *hdw,int ctl_id,int value) +// Attempt to autoselect an appropriate value for std_enum_cur given +// whatever is currently in std_mask_cur +void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw) { - /* This is a special case; the value to store is to an array, and - the element to select is determined by PVR_CID_CHANPROG_ID. */ - int id = hdw->controls[PVR2_CID_CHANPROG_ID].value; - if ((id < 1) || (id > FREQTABLE_SIZE)) return 0; - hdw->freqTable[id-1] = value; - if (hdw->controls[PVR2_CID_CHANNEL].value == id) { - /* If the current channel happens to be the slot we just - set, then act like the current channel just got changed - so we'll update that too. */ - hdw->controls[PVR2_CID_CHANNEL].dirty = !0; + unsigned int idx; + for (idx = 1; idx < hdw->std_enum_cnt; idx++) { + if (hdw->std_defs[idx-1].id == hdw->std_mask_cur) { + hdw->std_enum_cur = idx; + return; + } } - return 0; + hdw->std_enum_cur = 0; } -static int pvr2_ctl_get_chanprog_id(struct pvr2_hdw *hdw,int ctl_id) +// Calculate correct set of enumerated standards based on currently known +// set of available standards bits. +void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw) { - /* This is a special case; the value to return is from an array, - and the element to select is determined by - PVR_CID_CHANPROG_ID. */ - int id = hdw->controls[PVR2_CID_CHANPROG_ID].value; - if ((id < 1) || (id > FREQTABLE_SIZE)) return 0; - return hdw->freqTable[id-1]; -} + struct v4l2_standard *newstd; + unsigned int std_cnt; + unsigned int idx; + newstd = pvr2_std_create_enum(&std_cnt,hdw->std_mask_avail); -/* Retrieve current value for a given control */ -int pvr2_hdw_get_ctl_value(struct pvr2_hdw *hdw,unsigned int ctl_id) -{ - int ret = 0; + if (hdw->std_defs) { + kfree(hdw->std_defs); + hdw->std_defs = 0; + } + hdw->std_enum_cnt = 0; + if (hdw->std_enum_names) { + kfree(hdw->std_enum_names); + hdw->std_enum_names = 0; + } - if (ctl_id >= PVR2_CID_COUNT) return 0; - LOCK_TAKE(hdw->big_lock); do { - if (control_defs[ctl_id].get_func) { - ret = control_defs[ctl_id].get_func(hdw,ctl_id); - break; - } - ret = hdw->controls[ctl_id].value; - } while(0); LOCK_GIVE(hdw->big_lock); - return ret; + if (!std_cnt) { + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "WARNING: Failed to identify any viable standards"); + } + hdw->std_enum_names = kmalloc(sizeof(char *)*(std_cnt+1),GFP_KERNEL); + hdw->std_enum_names[0] = "none"; + for (idx = 0; idx < std_cnt; idx++) { + hdw->std_enum_names[idx+1] = + newstd[idx].name; + } + // Set up the dynamic control for this standard + hdw->std_info_enum.def.type_enum.value_names = hdw->std_enum_names; + hdw->std_info_enum.def.type_enum.count = std_cnt+1; + hdw->std_defs = newstd; + hdw->std_enum_cnt = std_cnt+1; + hdw->std_enum_cur = 0; + hdw->std_info_cur.def.type_bitmask.valid_bits = hdw->std_mask_avail; } -/* Return true if control is writable */ -int pvr2_hdw_get_ctl_rw(struct pvr2_hdw *hdw,unsigned int ctl_id) +int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw, + struct v4l2_standard *std, + unsigned int idx) { - if (ctl_id >= PVR2_CID_COUNT) return 0; - if (control_defs[ctl_id].get_func && !control_defs[ctl_id].set_func) { - return 0; - } - return !0; + int ret = -EINVAL; + if (!idx) return ret; + LOCK_TAKE(hdw->big_lock); do { + if (idx >= hdw->std_enum_cnt) break; + idx--; + memcpy(std,hdw->std_defs+idx,sizeof(*std)); + ret = 0; + } while (0); LOCK_GIVE(hdw->big_lock); + return ret; } -/* Retrieve legal minimum value for a given control */ -int pvr2_hdw_get_ctl_min_value(struct pvr2_hdw *hdw,unsigned int ctl_id) +/* Get the number of defined controls */ +unsigned int pvr2_hdw_get_ctrl_count(struct pvr2_hdw *hdw) { - if (ctl_id >= PVR2_CID_COUNT) return 0; - return control_defs[ctl_id].min_value; + return CTRL_COUNT; } -/* Retrieve legal maximum value for a given control */ -int pvr2_hdw_get_ctl_max_value(struct pvr2_hdw *hdw,unsigned int ctl_id) +/* Retrieve a control handle given its index (0..count-1) */ +struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_index(struct pvr2_hdw *hdw, + unsigned int idx) { - if (ctl_id >= PVR2_CID_COUNT) return 0; - return control_defs[ctl_id].max_value; + if (idx >= CTRL_COUNT) return 0; + return hdw->controls + idx; } -/* Set current value for given control - normally this is just stored and - the hardware isn't updated until the commit function is called. */ -int pvr2_hdw_set_ctl_value_internal(struct pvr2_hdw *hdw, - unsigned int ctl_id,int value) +/* Retrieve a control handle given its index (0..count-1) */ +struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_id(struct pvr2_hdw *hdw, + unsigned int ctl_id) { - if (ctl_id >= PVR2_CID_COUNT) return -EINVAL; - if (value < control_defs[ctl_id].min_value) return -EINVAL; - if (value > control_defs[ctl_id].max_value) return -EINVAL; - if (control_defs[ctl_id].set_func) { - return control_defs[ctl_id].set_func(hdw,ctl_id,value); - } else if (control_defs[ctl_id].get_func) { - /* If there's no "set" function yet there is still a "get" - function, then treat this as a read-only value. */ - return -EINVAL; - } - if ((hdw->controls[ctl_id].value != value) || (ctlchg != 0)) { - hdw->controls[ctl_id].value = value; - hdw->controls[ctl_id].dirty = !0; + struct pvr2_ctrl *cptr; + unsigned int idx; + int i; + + /* This could be made a lot more efficient, but for now... */ + for (idx = 0; idx < CTRL_COUNT; idx++) { + cptr = hdw->controls + idx; + i = cptr->info->internal_id; + if (i && (i == ctl_id)) return cptr; } return 0; } -/* Set current value for given control - this is just stored; the hardware - isn't updated until the commit function is called. */ -int pvr2_hdw_set_ctl_value(struct pvr2_hdw *hdw,unsigned int ctl_id,int value) +/* Given an ID, retrieve the control structure associated with it. */ +struct pvr2_ctrl *pvr2_hdw_get_ctrl_v4l(struct pvr2_hdw *hdw,unsigned int ctl_id) { - int ret; - LOCK_TAKE(hdw->big_lock); do { - ret = pvr2_hdw_set_ctl_value_internal(hdw,ctl_id,value); - } while (0); LOCK_GIVE(hdw->big_lock); - return ret; -} - + struct pvr2_ctrl *cptr; + unsigned int idx; + int i; -/* Retrieve string name for a given control value (returns a null pointer - for any invalid combinations). */ -const char *pvr2_hdw_get_ctl_value_name(struct pvr2_hdw *hdw, - unsigned int ctl_id, - int value) -{ - struct pvr2_ctl_def *cdef; - if (ctl_id >= PVR2_CID_COUNT) return 0; - cdef = control_defs + ctl_id; - if (! cdef->value_defs_ptr) return 0; - if (value >= cdef->value_defs_count) return 0; - return cdef->value_defs_ptr[value]; + /* This could be made a lot more efficient, but for now... */ + for (idx = 0; idx < CTRL_COUNT; idx++) { + cptr = hdw->controls + idx; + i = cptr->info->v4l_id; + if (i && (i == ctl_id)) return cptr; + } + return 0; } -/* Retrieve string name for given control */ -const char *pvr2_hdw_get_ctl_name(struct pvr2_hdw *hdw,unsigned int ctl_id) +static const char *get_ctrl_typename(enum pvr2_ctl_type tp) { - if (ctl_id >= PVR2_CID_COUNT) return 0; - return control_defs[ctl_id].name; + switch (tp) { + case pvr2_ctl_int: return "integer"; + case pvr2_ctl_enum: return "enum"; + case pvr2_ctl_bitmask: return "bitmask"; + } + return ""; } @@ -1553,61 +2023,33 @@ int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) unsigned long saved_subsys_mask = hdw->subsys_enabled_mask; unsigned long stale_subsys_mask = 0; unsigned int idx; + struct pvr2_ctrl *cptr; int value; - const char *ctl_name; - const char *ctl_value; int commit_flag = 0; + char buf[100]; + unsigned int bcnt,ccnt; - /* Let's see if the channel changed and we have to update the - frequency because of it. This setup means one can tune the - receiver either by just setting the channel (using the frequency - table), or by directly programming the frequency. How do we - resolve the obvious conflict here? The direct frequency takes - priority; if directly set then we commit that value and force - the channel to zero which is interpreted to mean "none". If on - the other hand we see that the channel has been set and it's a - legal value, then we copy that into the frequency. The metaphor - here is similar to when you tune your digital radio: You an - either set a frequency directly or punch up a pre-programmed - station. Either way a frequency is set, and if you do use a - preset, then the radio also shows you which preset it is - until - you override that by directly entering a new frequency. */ - if (hdw->controls[PVR2_CID_FREQUENCY].dirty) { - /* Frequency has been directly set, so clear out the - channel. */ - hdw->controls[PVR2_CID_CHANNEL].value = 0; - } else if (hdw->controls[PVR2_CID_CHANNEL].dirty) { - int id = hdw->controls[PVR2_CID_CHANNEL].value; - if ((id > 0) && (id <= FREQTABLE_SIZE)) { - if (hdw->controls[PVR2_CID_FREQUENCY].value != - hdw->freqTable[id-1]) { - hdw->controls[PVR2_CID_FREQUENCY].value = - hdw->freqTable[id-1]; - hdw->controls[PVR2_CID_FREQUENCY].dirty = !0; - } - } - } - - for (idx = 0; idx < PVR2_CID_COUNT; idx++) { - if (!hdw->controls[idx].dirty) continue; + for (idx = 0; idx < CTRL_COUNT; idx++) { + cptr = hdw->controls + idx; + if (cptr->info->is_dirty == 0) continue; + if (!cptr->info->is_dirty(cptr)) continue; if (!commit_flag) { commit_flag = !0; } - value = hdw->controls[idx].value; - ctl_name = control_defs[idx].name; - if (control_defs[idx].value_defs_ptr) { - if (value < control_defs[idx].value_defs_count) { - ctl_value = - control_defs[idx].value_defs_ptr[value]; - } else { - ctl_value = "<out of range>"; - } - } else { - ctl_value = "<integer>"; - } + + bcnt = scnprintf(buf,sizeof(buf),"\"%s\" <-- ", + cptr->info->name); + value = 0; + cptr->info->get_value(cptr,&value); + pvr2_ctrl_value_to_sym_internal(cptr,~0,value, + buf+bcnt, + sizeof(buf)-bcnt,&ccnt); + bcnt += ccnt; + bcnt += scnprintf(buf+bcnt,sizeof(buf)-bcnt," <%s>", + get_ctrl_typename(cptr->info->type)); pvr2_trace(PVR2_TRACE_CTL, - "/*--TRACE_COMMIT--*/ \"%s\" <-- %d (%s)", - ctl_name,value,ctl_value); + "/*--TRACE_COMMIT--*/ %.*s", + bcnt,buf); } if (!commit_flag) { @@ -1618,43 +2060,40 @@ int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) /* When video standard changes, reset the hres and vres values - but if the user has pending changes there, then let the changes take priority. */ - if (hdw->controls[PVR2_CID_VIDEOSTANDARD].dirty) { + if (hdw->std_dirty) { /* Rewrite the vertical resolution to be appropriate to the video standard that has been selected. */ - int nvres = hdw->controls[PVR2_CID_VRES].value; - switch (hdw->controls[PVR2_CID_VIDEOSTANDARD].value) { - case PVR2_CVAL_VIDEOSTANDARD_NTSC_M: - case PVR2_CVAL_VIDEOSTANDARD_PAL_M: + int nvres; + if (hdw->std_mask_cur & V4L2_STD_525_60) { nvres = 480; - break; - default: + } else { nvres = 576; } - if (nvres != hdw->controls[PVR2_CID_VRES].value) { - hdw->controls[PVR2_CID_VRES].value = nvres; - hdw->controls[PVR2_CID_VRES].dirty = !0; + if (nvres != hdw->res_ver_val) { + hdw->res_ver_val = nvres; + hdw->res_ver_dirty = !0; } - if (!hdw->controls[PVR2_CID_INTERLACE].value) { - hdw->controls[PVR2_CID_INTERLACE].value = 0; - hdw->controls[PVR2_CID_INTERLACE].dirty = !0; + if (!hdw->interlace_val) { + hdw->interlace_val = 0; + hdw->interlace_dirty = !0; } } - if (hdw->controls[PVR2_CID_VIDEOSTANDARD].dirty || - hdw->controls[PVR2_CID_VRES].dirty || - hdw->controls[PVR2_CID_HRES].dirty || - hdw->controls[PVR2_CID_INTERLACE].dirty || - hdw->controls[PVR2_CID_VBR].dirty || - hdw->controls[PVR2_CID_AVERAGEVIDEOBITRATE].dirty || - hdw->controls[PVR2_CID_PEAKVIDEOBITRATE].dirty || - hdw->controls[PVR2_CID_AUDIOBITRATE].dirty || - hdw->controls[PVR2_CID_SRATE].dirty || - hdw->controls[PVR2_CID_AUDIOLAYER].dirty || - hdw->controls[PVR2_CID_AUDIOCRC].dirty || - hdw->controls[PVR2_CID_AUDIOEMPHASIS].dirty) { + if (hdw->std_dirty || + hdw->res_ver_dirty || + hdw->res_hor_dirty || + hdw->interlace_dirty || + hdw->vbr_dirty || + hdw->videobitrate_dirty || + hdw->videopeak_dirty || + hdw->audiobitrate_dirty || + hdw->srate_dirty || + hdw->audiolayer_dirty || + hdw->audiocrc_dirty || + hdw->audioemphasis_dirty) { /* If any of this changes, then the encoder needs to be reconfigured, and we need to reset the stream. */ - stale_subsys_mask |= PVR2_SUBSYS_ENC_CFG; + stale_subsys_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG); stale_subsys_mask |= hdw->subsys_stream_mask; } @@ -1664,8 +2103,10 @@ int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) the client drivers in order to keep everything in sync */ pvr2_i2c_core_check_stale(hdw); - for (idx = 0; idx < PVR2_CID_COUNT; idx++) { - hdw->controls[idx].dirty = 0; + for (idx = 0; idx < CTRL_COUNT; idx++) { + cptr = hdw->controls + idx; + if (!cptr->info->clear_dirty) continue; + cptr->info->clear_dirty(cptr); } /* Now execute i2c core update */ @@ -1722,14 +2163,6 @@ void pvr2_hdw_poll_trigger(struct pvr2_hdw *hdw) } -/* Find out how many controls there are. Legal ids are numbered from 1 - through this value. */ -unsigned int pvr2_hdw_get_ctl_count(struct pvr2_hdw *hdw) -{ - return PVR2_CID_COUNT; -} - - /* Return name for this driver instance */ const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *hdw) { @@ -1741,7 +2174,7 @@ const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *hdw) unsigned int pvr2_hdw_get_signal_status_internal(struct pvr2_hdw *hdw) { unsigned int msk = 0; - switch (hdw->controls[PVR2_CID_INPUT].value) { + switch (hdw->input_val) { case PVR2_CVAL_INPUT_TV: case PVR2_CVAL_INPUT_RADIO: if (hdw->decoder_ctrl && @@ -1765,42 +2198,6 @@ unsigned int pvr2_hdw_get_signal_status_internal(struct pvr2_hdw *hdw) } -static int pvr2_ctl_get_subsys_mask(struct pvr2_hdw *hdw,int ctl_id) -{ - return hdw->subsys_enabled_mask; -} - - -static int pvr2_ctl_set_subsys_mask(struct pvr2_hdw *hdw,int ctl_id,int val) -{ - pvr2_hdw_subsys_bit_chg_no_lock(hdw,~0,val); - return 0; -} - - -static int pvr2_ctl_get_subsys_stream_mask(struct pvr2_hdw *hdw,int ctl_id) -{ - return hdw->subsys_stream_mask; -} - - -static int pvr2_ctl_set_subsys_stream_mask(struct pvr2_hdw *hdw,int ctl_id, - int val) -{ - pvr2_hdw_subsys_stream_bit_chg_no_lock(hdw,~0,val); - return 0; -} - - -static int pvr2_ctl_get_hsm(struct pvr2_hdw *hdw,int ctl_id) -{ - int result = pvr2_hdw_is_hsm(hdw); - if (result < 0) return PVR2_CVAL_HSM_FAIL; - if (result) return PVR2_CVAL_HSM_HIGH; - return PVR2_CVAL_HSM_FULL; -} - - int pvr2_hdw_is_hsm(struct pvr2_hdw *hdw) { int result; @@ -1816,13 +2213,6 @@ int pvr2_hdw_is_hsm(struct pvr2_hdw *hdw) } -static int pvr2_ctl_get_signal(struct pvr2_hdw *hdw,int ctl_id) -{ - return ((pvr2_hdw_get_signal_status_internal(hdw) & PVR2_SIGNAL_OK) ? - 1 : 0); -} - - /* Return bit mask indicating signal status */ unsigned int pvr2_hdw_get_signal_status(struct pvr2_hdw *hdw) { @@ -2062,6 +2452,28 @@ int pvr2_send_request_ex(struct pvr2_hdw *hdw, return -EINVAL; } +#if 0 + printk(KERN_INFO "pvrusb2: REQUEST BEGIN writeCnt=%u readCnt=%u", + write_len,read_len); + if (probe_fl) { + printk(" <probe>"); + } + for (idx = 0; idx < write_len; idx++) { + if (idx > 5) { + printk(" ..."); + break; + } + if (idx) { + printk(" "); + } else { + printk(" ["); + } + printk("%02x",((unsigned char *)write_data)[idx]); + } + if (write_len) printk("]"); + printk("\n"); +#endif + hdw->cmd_debug_state = 1; if (write_len) { hdw->cmd_debug_code = ((unsigned char *)write_data)[0]; @@ -2231,6 +2643,26 @@ int pvr2_send_request_ex(struct pvr2_hdw *hdw, } done: +#if 0 + printk(KERN_INFO "pvrusb2: REQUEST END status=%d",status); + if (status >= 0) { + for (idx = 0; idx < read_len; idx++) { + if (idx > 5) { + printk(" ..."); + break; + } + if (idx) { + printk(" "); + } else { + printk(" ["); + } + printk("%02x",((unsigned char *)read_data)[idx]); + } + if (read_len) printk("]"); + } + printk("\n"); +#endif + hdw->cmd_debug_state = 0; if ((status < 0) && (!probe_fl)) { pvr2_hdw_render_useless_unlocked(hdw); @@ -2406,11 +2838,11 @@ int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *hdw) } -int pvr2_hdw_cmd_soft_reset(struct pvr2_hdw *hdw) +int pvr2_hdw_cmd_powerup(struct pvr2_hdw *hdw) { int status; LOCK_TAKE(hdw->ctl_lock); do { - pvr2_trace(PVR2_TRACE_INIT,"Requesting uproc soft reset"); + pvr2_trace(PVR2_TRACE_INIT,"Requesting powerup"); hdw->cmd_buffer[0] = 0xde; status = pvr2_send_request(hdw,hdw->cmd_buffer,1,0,0); } while (0); LOCK_GIVE(hdw->ctl_lock); @@ -2418,6 +2850,27 @@ int pvr2_hdw_cmd_soft_reset(struct pvr2_hdw *hdw) } +int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw) +{ + if (!hdw->decoder_ctrl) { + pvr2_trace(PVR2_TRACE_INIT, + "Unable to reset decoder: nothing attached"); + return -ENOTTY; + } + + if (!hdw->decoder_ctrl->force_reset) { + pvr2_trace(PVR2_TRACE_INIT, + "Unable to reset decoder: not implemented"); + return -ENOTTY; + } + + pvr2_trace(PVR2_TRACE_INIT, + "Requesting decoder reset"); + hdw->decoder_ctrl->force_reset(hdw->decoder_ctrl->ctxt); + return 0; +} + + int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl) { int status; @@ -2428,8 +2881,8 @@ int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl) if (!status) { hdw->subsys_enabled_mask = ((hdw->subsys_enabled_mask & - ~PVR2_SUBSYS_USBSTREAM_RUN) | - (runFl ? PVR2_SUBSYS_USBSTREAM_RUN : 0)); + ~(1<<PVR2_SUBSYS_B_USBSTREAM_RUN)) | + (runFl ? (1<<PVR2_SUBSYS_B_USBSTREAM_RUN) : 0)); } return status; } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h index 085afe566..ba70f5d48 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -21,137 +21,64 @@ #ifndef __PVRUSB2_HDW_H #define __PVRUSB2_HDW_H +#include "compat.h" #include <linux/usb.h> +#include <linux/videodev2.h> #include "pvrusb2-io.h" - -/* Definition of state variables that we can inspect & change. Numbers are - assigned from zero counting up with no gaps. */ -#define PVR2_CID_BRIGHTNESS 0 -#define PVR2_CID_CONTRAST 1 -#define PVR2_CID_SATURATION 2 -#define PVR2_CID_HUE 3 -#define PVR2_CID_VOLUME 4 -#define PVR2_CID_BALANCE 5 -#define PVR2_CID_BASS 6 -#define PVR2_CID_TREBLE 7 -#define PVR2_CID_MUTE 8 -#define PVR2_CID_SRATE 9 -#define PVR2_CID_AUDIOBITRATE 10 -#define PVR2_CID_AUDIOCRC 11 -#define PVR2_CID_AUDIOEMPHASIS 12 -#define PVR2_CID_VBR 13 -#define PVR2_CID_AVERAGEVIDEOBITRATE 14 -#define PVR2_CID_PEAKVIDEOBITRATE 15 -#define PVR2_CID_VIDEOSTANDARD 16 -#define PVR2_CID_INPUT 17 -#define PVR2_CID_AUDIOMODE 18 -#define PVR2_CID_FREQUENCY 19 // Units of Hz -#define PVR2_CID_HRES 20 -#define PVR2_CID_VRES 21 -#define PVR2_CID_INTERLACE 22 -#define PVR2_CID_AUDIOLAYER 23 -#define PVR2_CID_CHANNEL 24 -#define PVR2_CID_CHANPROG_ID 25 -#define PVR2_CID_CHANPROG_FREQ 26 -#define PVR2_CID_SIGNAL_PRESENT 27 -#define PVR2_CID_STREAMING_ENABLED 28 -#define PVR2_CID_HSM 29 -#define PVR2_CID_SUBSYS_MASK 30 -#define PVR2_CID_SUBSYS_STREAM_MASK 31 - -/* Number of state variables */ -#define PVR2_CID_COUNT 32 - -/* Legal values for the SRATE state variable */ -#define PVR2_CVAL_SRATE_48 0 -#define PVR2_CVAL_SRATE_44_1 1 -#define PVR2_CVAL_SRATE_MIN PVR2_CVAL_SRATE_48 -#define PVR2_CVAL_SRATE_MAX PVR2_CVAL_SRATE_44_1 - -/* Legal values for the AUDIOBITRATE state variable */ -#define PVR2_CVAL_AUDIOBITRATE_384 0 -#define PVR2_CVAL_AUDIOBITRATE_320 1 -#define PVR2_CVAL_AUDIOBITRATE_256 2 -#define PVR2_CVAL_AUDIOBITRATE_224 3 -#define PVR2_CVAL_AUDIOBITRATE_192 4 -#define PVR2_CVAL_AUDIOBITRATE_160 5 -#define PVR2_CVAL_AUDIOBITRATE_128 6 -#define PVR2_CVAL_AUDIOBITRATE_112 7 -#define PVR2_CVAL_AUDIOBITRATE_96 8 -#define PVR2_CVAL_AUDIOBITRATE_80 9 -#define PVR2_CVAL_AUDIOBITRATE_64 10 -#define PVR2_CVAL_AUDIOBITRATE_56 11 -#define PVR2_CVAL_AUDIOBITRATE_48 12 -#define PVR2_CVAL_AUDIOBITRATE_32 13 -#define PVR2_CVAL_AUDIOBITRATE_VBR 14 -#define PVR2_CVAL_AUDIOBITRATE_MIN PVR2_CVAL_AUDIOBITRATE_384 -#define PVR2_CVAL_AUDIOBITRATE_MAX PVR2_CVAL_AUDIOBITRATE_VBR - -/* Legal values for the AUDIOEMPHASIS state variable */ -#define PVR2_CVAL_AUDIOEMPHASIS_NONE 0 -#define PVR2_CVAL_AUDIOEMPHASIS_50_15 1 -#define PVR2_CVAL_AUDIOEMPHASIS_CCITT 2 -#define PVR2_CVAL_AUDIOEMPHASIS_MIN PVR2_CVAL_AUDIOEMPHASIS_NONE -#define PVR2_CVAL_AUDIOEMPHASIS_MAX PVR2_CVAL_AUDIOEMPHASIS_CCITT - -/* Legal values for the VIDEOSTANDARD state variable */ -#define PVR2_CVAL_VIDEOSTANDARD_NTSC_M 0 -#define PVR2_CVAL_VIDEOSTANDARD_PAL_BG 1 -#define PVR2_CVAL_VIDEOSTANDARD_PAL_I 2 -#define PVR2_CVAL_VIDEOSTANDARD_PAL_DK 3 -#define PVR2_CVAL_VIDEOSTANDARD_PAL_M 4 -#define PVR2_CVAL_VIDEOSTANDARD_SECAM_L 5 - -#define PVR2_CVAL_VIDEOSTANDARD_MIN PVR2_CVAL_VIDEOSTANDARD_NTSC_M -#define PVR2_CVAL_VIDEOSTANDARD_MAX PVR2_CVAL_VIDEOSTANDARD_SECAM_L +#include "pvrusb2-ctrl.h" + +/* Private V4L2-compatible controls available in this driver, look these up + with pvr2_hdw_get_ctrl_v4l(). */ +#define V4L2_CID_PVR_SRATE (V4L2_CID_PRIVATE_BASE) +#define V4L2_CID_PVR_AUDIOBITRATE (V4L2_CID_PRIVATE_BASE+1) +#define V4L2_CID_PVR_AUDIOCRC (V4L2_CID_PRIVATE_BASE+2) +#define V4L2_CID_PVR_AUDIOEMPHASIS (V4L2_CID_PRIVATE_BASE+3) +#define V4L2_CID_PVR_VBR (V4L2_CID_PRIVATE_BASE+4) +#define V4L2_CID_PVR_VIDEOBITRATE (V4L2_CID_PRIVATE_BASE+5) +#define V4L2_CID_PVR_VIDEOPEAK (V4L2_CID_PRIVATE_BASE+6) +#define V4L2_CID_PVR_VIDEOSTANDARD (V4L2_CID_PRIVATE_BASE+7) + +/* Private internal control ids, look these up with + pvr2_hdw_get_ctrl_by_id() - these are NOT visible in V4L */ +#define PVR2_CID_STDENUM 1 +#define PVR2_CID_STDCUR 2 +#define PVR2_CID_STDAVAIL 3 +#define PVR2_CID_INPUT 4 +#define PVR2_CID_AUDIOMODE 5 +#define PVR2_CID_FREQUENCY 6 +#define PVR2_CID_HRES 7 +#define PVR2_CID_VRES 8 +#define PVR2_CID_INTERLACE 9 /* Legal values for the INPUT state variable */ #define PVR2_CVAL_INPUT_TV 0 #define PVR2_CVAL_INPUT_SVIDEO 1 #define PVR2_CVAL_INPUT_COMPOSITE 2 #define PVR2_CVAL_INPUT_RADIO 3 -#define PVR2_CVAL_INPUT_MIN PVR2_CVAL_INPUT_TV -#define PVR2_CVAL_INPUT_MAX PVR2_CVAL_INPUT_RADIO - -/* Legal values for the AUDIOMODE state variable */ -#define PVR2_CVAL_AUDIOMODE_MONO 0 -#define PVR2_CVAL_AUDIOMODE_STEREO 1 -#define PVR2_CVAL_AUDIOMODE_SAP 2 -#define PVR2_CVAL_AUDIOMODE_LANG1 3 -#define PVR2_CVAL_AUDIOMODE_LANG2 4 -#define PVR2_CVAL_AUDIOMODE_LANG1_LANG2 5 -#define PVR2_CVAL_AUDIOMODE_MIN PVR2_CVAL_AUDIOMODE_MONO -#define PVR2_CVAL_AUDIOMODE_MAX PVR2_CVAL_AUDIOMODE_LANG1_LANG2 /* Values that pvr2_hdw_get_signal_status() returns */ #define PVR2_SIGNAL_OK 0x0001 #define PVR2_SIGNAL_STEREO 0x0002 #define PVR2_SIGNAL_SAP 0x0004 -/* Legal values for PVR2_CID_HSM */ -#define PVR2_CVAL_HSM_FAIL 0 -#define PVR2_CVAL_HSM_FULL 1 -#define PVR2_CVAL_HSM_HIGH 2 -#define PVR2_CVAL_HSM_MIN PVR2_CVAL_HSM_FAIL -#define PVR2_CVAL_HSM_MAX PVR2_CVAL_HSM_HIGH /* Subsystem definitions - these are various pieces that can be independently stopped / started. Usually you don't want to mess with this directly (let the driver handle things itself), but it is useful for debugging. */ -#define PVR2_SUBSYS_ENC_FIRMWARE (1 << 0) -#define PVR2_SUBSYS_ENC_CFG (1 << 1) -#define PVR2_SUBSYS_DIGITIZER_RUN (1 << 2) -#define PVR2_SUBSYS_USBSTREAM_RUN (1 << 3) -#define PVR2_SUBSYS_ENC_RUN (1 << 4) +#define PVR2_SUBSYS_B_ENC_FIRMWARE 0 +#define PVR2_SUBSYS_B_ENC_CFG 1 +#define PVR2_SUBSYS_B_DIGITIZER_RUN 2 +#define PVR2_SUBSYS_B_USBSTREAM_RUN 3 +#define PVR2_SUBSYS_B_ENC_RUN 4 #define PVR2_SUBSYS_CFG_ALL ( \ - PVR2_SUBSYS_ENC_FIRMWARE | \ - PVR2_SUBSYS_ENC_CFG ) + (1 << PVR2_SUBSYS_B_ENC_FIRMWARE) | \ + (1 << PVR2_SUBSYS_B_ENC_CFG) ) #define PVR2_SUBSYS_RUN_ALL ( \ - PVR2_SUBSYS_DIGITIZER_RUN | \ - PVR2_SUBSYS_USBSTREAM_RUN | \ - PVR2_SUBSYS_ENC_RUN ) + (1 << PVR2_SUBSYS_B_DIGITIZER_RUN) | \ + (1 << PVR2_SUBSYS_B_USBSTREAM_RUN) | \ + (1 << PVR2_SUBSYS_B_ENC_RUN) ) #define PVR2_SUBSYS_ALL ( \ PVR2_SUBSYS_CFG_ALL | \ PVR2_SUBSYS_RUN_ALL ) @@ -215,37 +142,21 @@ unsigned long pvr2_hdw_get_sn(struct pvr2_hdw *); /* Called when hardware has been unplugged */ void pvr2_hdw_disconnect(struct pvr2_hdw *); -/* Retrieve current value for a given control */ -int pvr2_hdw_get_ctl_value(struct pvr2_hdw *,unsigned int ctl_id); - -/* Return true if control is writable */ -int pvr2_hdw_get_ctl_rw(struct pvr2_hdw *,unsigned int ctl_id); - -/* Retrieve legal minimum value for a given control */ -int pvr2_hdw_get_ctl_min_value(struct pvr2_hdw *,unsigned int ctl_id); - -/* Retrieve legal maximum value for a given control */ -int pvr2_hdw_get_ctl_max_value(struct pvr2_hdw *,unsigned int ctl_id); +/* Get the number of defined controls */ +unsigned int pvr2_hdw_get_ctrl_count(struct pvr2_hdw *); -/* Set current value for given control - this is just stored; the hardware - isn't updated until the commit function is called. */ -int pvr2_hdw_set_ctl_value(struct pvr2_hdw *,unsigned int ctl_id,int value); +/* Retrieve a control handle given its index (0..count-1) */ +struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_index(struct pvr2_hdw *,unsigned int); -/* Retrieve string name for given control */ -const char *pvr2_hdw_get_ctl_name(struct pvr2_hdw *,unsigned int ctl_id); +/* Retrieve a control handle given its internal ID (if any) */ +struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_id(struct pvr2_hdw *,unsigned int); -/* Retrieve string name for a given control value (returns a null pointer - for any invalid combinations). */ -const char *pvr2_hdw_get_ctl_value_name(struct pvr2_hdw *, - unsigned int ctl_id,int value); +/* Retrieve a control handle given its V4L ID (if any) */ +struct pvr2_ctrl *pvr2_hdw_get_ctrl_v4l(struct pvr2_hdw *,unsigned int ctl_id); /* Commit all control changes made up to this point */ int pvr2_hdw_commit_ctl(struct pvr2_hdw *); -/* Find out how many controls there are. Legal ids are numbered from 0 - through this value - 1. */ -unsigned int pvr2_hdw_get_ctl_count(struct pvr2_hdw *); - /* Return name for this driver instance */ const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *); @@ -267,6 +178,10 @@ int pvr2_hdw_set_stream_type(struct pvr2_hdw *, enum pvr2_config); /* Get handle to video output stream */ struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *); +/* Emit a video standard struct */ +int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw,struct v4l2_standard *std, + unsigned int idx); + /* Enable / disable various pieces of hardware. Items to change are identified by bit positions within msk, and new state for each item is identified by corresponding bit positions within val. */ @@ -359,7 +274,10 @@ void pvr2_hdw_device_reset(struct pvr2_hdw *); int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *); /* Execute simple reset command */ -int pvr2_hdw_cmd_soft_reset(struct pvr2_hdw *); +int pvr2_hdw_cmd_powerup(struct pvr2_hdw *); + +/* Order decoder to reset */ +int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *); /* Stop / start video stream transport */ int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c index 2d97653d5..89d0da053 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c @@ -28,6 +28,10 @@ #include "pvrusb2-tuner.h" #include "pvrusb2-demod.h" #include "pvrusb2-video-v4l.h" +#ifdef CONFIG_VIDEO_PVRUSB2_24XXX +#include "pvrusb2-cx2584x-v4l.h" +#include "pvrusb2-wm8775.h" +#endif #define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__) @@ -69,18 +73,28 @@ void pvr2_i2c_probe(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) return; } } +#ifdef CONFIG_VIDEO_PVRUSB2_24XXX + if (id == I2C_DRIVERID_CX25840) { + if (pvr2_i2c_cx2584x_v4l_setup(hdw,cp)) { + return; + } + } + if (id == I2C_DRIVERID_WM8775) { + if (pvr2_i2c_wm8775_setup(hdw,cp)) { + return; + } + } +#endif if (id == I2C_DRIVERID_SAA711X) { if (pvr2_i2c_decoder_v4l_setup(hdw,cp)) { return; } } -#ifdef PVR2_ENABLE_DRIVERID_TDA9887 if (id == I2C_DRIVERID_TDA9887) { if (pvr2_i2c_demod_setup(hdw,cp)) { return; } } -#endif } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c index 7d137a25f..9f81aff2b 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c @@ -23,44 +23,23 @@ #include "pvrusb2-i2c-cmd-v4l2.h" #include "pvrusb2-hdw-internal.h" #include "pvrusb2-debug.h" -#include <linux/videodev.h> +#include <linux/videodev2.h> static void set_standard(struct pvr2_hdw *hdw) { - int cvstd = hdw->controls[PVR2_CID_VIDEOSTANDARD].value; v4l2_std_id vs; + vs = hdw->std_mask_cur; pvr2_trace(PVR2_TRACE_CHIPS, - "i2c v4l2 set_standard(%d)",cvstd); - - switch (cvstd) { - default: - case PVR2_CVAL_VIDEOSTANDARD_NTSC_M: - vs = V4L2_STD_NTSC_M; - break; - case PVR2_CVAL_VIDEOSTANDARD_SECAM_L: - vs = V4L2_STD_SECAM; - break; - case PVR2_CVAL_VIDEOSTANDARD_PAL_BG: - vs = V4L2_STD_PAL_BG; - break; - case PVR2_CVAL_VIDEOSTANDARD_PAL_I: - vs = V4L2_STD_PAL_I; - break; - case PVR2_CVAL_VIDEOSTANDARD_PAL_DK: - vs = V4L2_STD_PAL_DK; - break; - case PVR2_CVAL_VIDEOSTANDARD_PAL_M: - vs = V4L2_STD_PAL_M; - break; - } + "i2c v4l2 set_standard(0x%llx)",(__u64)vs); + pvr2_i2c_core_cmd(hdw,VIDIOC_S_STD,&vs); } static int check_standard(struct pvr2_hdw *hdw) { - return hdw->controls[PVR2_CID_VIDEOSTANDARD].dirty != 0; + return hdw->std_dirty != 0; } @@ -76,32 +55,30 @@ static void set_bcsh(struct pvr2_hdw *hdw) struct v4l2_control ctrl; pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_bcsh" " b=%d c=%d s=%d h=%d", - hdw->controls[PVR2_CID_BRIGHTNESS].value, - hdw->controls[PVR2_CID_CONTRAST].value, - hdw->controls[PVR2_CID_SATURATION].value, - hdw->controls[PVR2_CID_HUE].value); + hdw->brightness_val,hdw->contrast_val, + hdw->saturation_val,hdw->hue_val); memset(&ctrl,0,sizeof(ctrl)); ctrl.id = V4L2_CID_BRIGHTNESS; - ctrl.value = hdw->controls[PVR2_CID_BRIGHTNESS].value; + ctrl.value = hdw->brightness_val; pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); ctrl.id = V4L2_CID_CONTRAST; - ctrl.value = hdw->controls[PVR2_CID_CONTRAST].value; + ctrl.value = hdw->contrast_val; pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); ctrl.id = V4L2_CID_SATURATION; - ctrl.value = hdw->controls[PVR2_CID_SATURATION].value; + ctrl.value = hdw->saturation_val; pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); ctrl.id = V4L2_CID_HUE; - ctrl.value = hdw->controls[PVR2_CID_HUE].value; + ctrl.value = hdw->hue_val; pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); } static int check_bcsh(struct pvr2_hdw *hdw) { - return (hdw->controls[PVR2_CID_BRIGHTNESS].dirty || - hdw->controls[PVR2_CID_CONTRAST].dirty || - hdw->controls[PVR2_CID_SATURATION].dirty || - hdw->controls[PVR2_CID_HUE].dirty); + return (hdw->brightness_dirty || + hdw->contrast_dirty || + hdw->saturation_dirty || + hdw->hue_dirty); } @@ -118,37 +95,37 @@ static void set_volume(struct pvr2_hdw *hdw) pvr2_trace(PVR2_TRACE_CHIPS, "i2c v4l2 set_volume" "(vol=%d bal=%d bas=%d treb=%d mute=%d)", - hdw->controls[PVR2_CID_VOLUME].value, - hdw->controls[PVR2_CID_BALANCE].value, - hdw->controls[PVR2_CID_BASS].value, - hdw->controls[PVR2_CID_TREBLE].value, - hdw->controls[PVR2_CID_MUTE].value); + hdw->volume_val, + hdw->balance_val, + hdw->bass_val, + hdw->treble_val, + hdw->mute_val); memset(&ctrl,0,sizeof(ctrl)); ctrl.id = V4L2_CID_AUDIO_MUTE; - ctrl.value = hdw->controls[PVR2_CID_MUTE].value ? 1 : 0; + ctrl.value = hdw->mute_val ? 1 : 0; pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); ctrl.id = V4L2_CID_AUDIO_VOLUME; - ctrl.value = hdw->controls[PVR2_CID_VOLUME].value; + ctrl.value = hdw->volume_val; pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); ctrl.id = V4L2_CID_AUDIO_BALANCE; - ctrl.value = hdw->controls[PVR2_CID_BALANCE].value; + ctrl.value = hdw->balance_val; pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); ctrl.id = V4L2_CID_AUDIO_BASS; - ctrl.value = hdw->controls[PVR2_CID_BASS].value; + ctrl.value = hdw->bass_val; pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); ctrl.id = V4L2_CID_AUDIO_TREBLE; - ctrl.value = hdw->controls[PVR2_CID_TREBLE].value; + ctrl.value = hdw->treble_val; pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); } static int check_volume(struct pvr2_hdw *hdw) { - return (hdw->controls[PVR2_CID_VOLUME].dirty || - hdw->controls[PVR2_CID_BALANCE].dirty || - hdw->controls[PVR2_CID_BASS].dirty || - hdw->controls[PVR2_CID_TREBLE].dirty || - hdw->controls[PVR2_CID_MUTE].dirty); + return (hdw->volume_dirty || + hdw->balance_dirty || + hdw->bass_dirty || + hdw->treble_dirty || + hdw->mute_dirty); } @@ -163,7 +140,7 @@ static void set_frequency(struct pvr2_hdw *hdw) { unsigned long fv; struct v4l2_frequency freq; - fv = hdw->controls[PVR2_CID_FREQUENCY].value; + fv = hdw->freqVal; pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_freq(%lu)",fv); memset(&freq,0,sizeof(freq)); freq.frequency = fv / 62500; @@ -175,7 +152,7 @@ static void set_frequency(struct pvr2_hdw *hdw) static int check_frequency(struct pvr2_hdw *hdw) { - return hdw->controls[PVR2_CID_FREQUENCY].dirty != 0; + return hdw->freqDirty != 0; } @@ -193,8 +170,8 @@ static void set_size(struct pvr2_hdw *hdw) memset(&fmt,0,sizeof(fmt)); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fmt.fmt.pix.width = hdw->controls[PVR2_CID_HRES].value; - fmt.fmt.pix.height = hdw->controls[PVR2_CID_VRES].value; + fmt.fmt.pix.width = hdw->res_hor_val; + fmt.fmt.pix.height = hdw->res_ver_val; pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_size(%dx%d)", fmt.fmt.pix.width,fmt.fmt.pix.height); @@ -205,8 +182,7 @@ static void set_size(struct pvr2_hdw *hdw) static int check_size(struct pvr2_hdw *hdw) { - return (hdw->controls[PVR2_CID_HRES].dirty || - hdw->controls[PVR2_CID_VRES].dirty); + return (hdw->res_hor_dirty || hdw->res_ver_dirty); } @@ -238,6 +214,13 @@ const struct pvr2_i2c_op pvr2_i2c_op_v4l2_log = { }; +void pvr2_v4l2_cmd_stream(struct pvr2_i2c_client *cp,int fl) +{ + pvr2_i2c_client_cmd(cp, + (fl ? VIDIOC_STREAMON : VIDIOC_STREAMOFF),0); +} + + /* Stuff for Emacs to see, in order to encourage consistent editing style: *** Local Variables: *** diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h index fa0a3af7e..1666a3287 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h @@ -33,6 +33,8 @@ extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_frequency; extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_size; extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_log; +void pvr2_v4l2_cmd_stream(struct pvr2_i2c_client *,int); + #endif /* __PVRUSB2_CMD_V4L2_H */ /* diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index 8ec637e5e..4f1f4b000 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -56,7 +56,7 @@ static int pvr2_i2c_write(struct pvr2_hdw *hdw, /* Context */ "Killing an I2C write to %u that is too large" " (desired=%u limit=%u)", i2c_addr, - length,(sizeof(hdw->cmd_buffer) - 3)); + length,(unsigned int)(sizeof(hdw->cmd_buffer) - 3)); return -ENOTSUPP; } @@ -88,7 +88,7 @@ static int pvr2_i2c_write(struct pvr2_hdw *hdw, /* Context */ } } #if 0 - trace_i2c("i2c_write(%d) len=%d ret=%d stat=%d",i2c_addr,length,ret, + trace_i2c("i2c_write(0x%x) len=%d ret=%d stat=%d",i2c_addr,length,ret, hdw->cmd_buffer[0]); #endif @@ -112,8 +112,22 @@ static int pvr2_i2c_read(struct pvr2_hdw *hdw, /* Context */ #endif if (!data) dlen = 0; - if (dlen > (sizeof(hdw->cmd_buffer) - 4)) return -ENOTSUPP; - if (res && (rlen > (sizeof(hdw->cmd_buffer) - 1))) return -ENOTSUPP; + if (dlen > (sizeof(hdw->cmd_buffer) - 4)) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Killing an I2C read to %u that has wlen too large" + " (desired=%u limit=%u)", + i2c_addr, + dlen,(unsigned int)(sizeof(hdw->cmd_buffer) - 4)); + return -ENOTSUPP; + } + if (res && (rlen > (sizeof(hdw->cmd_buffer) - 1))) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Killing an I2C read to %u that has rlen too large" + " (desired=%u limit=%u)", + i2c_addr, + rlen,(unsigned int)(sizeof(hdw->cmd_buffer) - 1)); + return -ENOTSUPP; + } LOCK_TAKE(hdw->ctl_lock); @@ -146,7 +160,7 @@ static int pvr2_i2c_read(struct pvr2_hdw *hdw, /* Context */ } #if 0 - trace_i2c("i2c_read(%d) wlen=%d rlen=%d ret=%d stat=%d", + trace_i2c("i2c_read(0x%x) wlen=%d rlen=%d ret=%d stat=%d", i2c_addr,dlen,rlen,ret,hdw->cmd_buffer[0]); #endif /* Copy back the result */ @@ -164,6 +178,130 @@ static int pvr2_i2c_read(struct pvr2_hdw *hdw, /* Context */ return ret; } +/* This is the common low level entry point for doing I2C operations to the + hardware. */ +int pvr2_i2c_basic_op(struct pvr2_hdw *hdw, + u8 i2c_addr, + u8 *wdata, + u16 wlen, + u8 *rdata, + u16 rlen) +{ + if (!rdata) rlen = 0; + if (!wdata) wlen = 0; + if (rlen || !wlen) { + return pvr2_i2c_read(hdw,i2c_addr,wdata,wlen,rdata,rlen); + } else { + return pvr2_i2c_write(hdw,i2c_addr,wdata,wlen); + } +} + +#ifdef CONFIG_VIDEO_PVRUSB2_24XXX + +/* This is a special entry point that is entered if an I2C operation is + attempted to a wm8775 chip on model 24xxx hardware. Autodetect of this + part doesn't work, but we know it is really there. So let's look for + the autodetect attempt and just return success if we see that. */ +static int i2c_hack_wm8775(struct pvr2_hdw *hdw, + u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen) +{ + if (!(rlen || wlen)) { + // This is a probe attempt. Just let it succeed. + return 0; + } + return pvr2_i2c_basic_op(hdw,i2c_addr,wdata,wlen,rdata,rlen); +} + +/* This is a special entry point that is entered if an I2C operation is + attempted to a cx25840 chip on model 24xxx hardware. This chip can + sometimes wedge itself. Worse still, when this happens msp3400 can + falsely detect this part and then the system gets hosed up after msp3400 + gets confused and dies. What we want to do here is try to keep msp3400 + away and also try to notice if the chip is wedged and send a warning to + the system log. */ +static int i2c_hack_cx25840(struct pvr2_hdw *hdw, + u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen) +{ + int ret; + unsigned int subaddr; + u8 wbuf[2]; + int state = hdw->i2c_cx25840_hack_state; + + if (!(rlen || wlen)) { + // Probe attempt - always just succeed and don't bother the + // hardware (this helps to make the state machine further + // down somewhat easier). + return 0; + } + + if (state == 3) { + return pvr2_i2c_basic_op(hdw,i2c_addr,wdata,wlen,rdata,rlen); + } + + /* We're looking for the exact pattern where the revision register + is being read. The cx25840 module will always look at the + revision register first. Any other pattern of access therefore + has to be a probe attempt from somebody else so we'll reject it. + Normally we could just let each client just probe the part + anyway, but when the cx25840 is wedged, msp3400 will get a false + positive and that just screws things up... */ + + if (wlen == 0) { + switch (state) { + case 1: subaddr = 0x0100; break; + case 2: subaddr = 0x0101; break; + default: goto fail; + } + } else if (wlen == 2) { + subaddr = (wdata[0] << 8) | wdata[1]; + switch (subaddr) { + case 0x0100: state = 1; break; + case 0x0101: state = 2; break; + default: goto fail; + } + } else { + goto fail; + } + if (!rlen) goto success; + state = 0; + if (rlen != 1) goto fail; + + /* If we get to here then we have a legitimate read for one of the + two revision bytes, so pass it through. */ + wbuf[0] = subaddr >> 8; + wbuf[1] = subaddr; + ret = pvr2_i2c_basic_op(hdw,i2c_addr,wbuf,2,rdata,rlen); + + if ((ret != 0) || (*rdata == 0x04) || (*rdata == 0x0a)) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "WARNING: Detected a wedged cx25840 chip;" + " the device will not work."); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "WARNING: Try power cycling the pvrusb2 device."); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "WARNING: Disabling further access to the device" + " to prevent other foul-ups."); + // This blocks all further communication with the part. + hdw->i2c_func[0x44] = 0; + pvr2_hdw_render_useless(hdw); + goto fail; + } + + /* Success! */ + pvr2_trace(PVR2_TRACE_CHIPS,"cx25840 appears to be OK."); + state = 3; + + success: + hdw->i2c_cx25840_hack_state = state; + return 0; + + fail: + hdw->i2c_cx25840_hack_state = state; + return -EIO; +} + +#endif /* CONFIG_VIDEO_PVRUSB2_24XXX */ + /* This is a very, very limited I2C adapter implementation. We can only support what we actually know will work on the device... */ static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap, @@ -171,12 +309,24 @@ static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap, int num) { int ret = -ENOTSUPP; + pvr2_i2c_func funcp = 0; struct pvr2_hdw *hdw = (struct pvr2_hdw *)(i2c_adap->algo_data); + if (!num) { + ret = -EINVAL; + goto done; + } if ((msgs[0].flags & I2C_M_NOSTART)) { trace_i2c("i2c refusing I2C_M_NOSTART"); goto done; } + if (msgs[0].addr < PVR2_I2C_FUNC_CNT) { + funcp = hdw->i2c_func[msgs[0].addr]; + } + if (!funcp) { + ret = -EIO; + goto done; + } if (num == 1) { if (msgs[0].flags & I2C_M_RD) { @@ -184,8 +334,7 @@ static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap, u16 tcnt,bcnt,offs; if (!msgs[0].len) { /* Length == 0 read. This is a probe. */ - if (pvr2_i2c_read(hdw,msgs[0].addr, - 0,0,0,0)) { + if (funcp(hdw,msgs[0].addr,0,0,0,0)) { ret = -EIO; goto done; } @@ -202,9 +351,8 @@ static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap, if (bcnt > sizeof(hdw->cmd_buffer)-1) { bcnt = sizeof(hdw->cmd_buffer)-1; } - if (pvr2_i2c_read(hdw,msgs[0].addr, - 0,0, - msgs[0].buf+offs,bcnt)) { + if (funcp(hdw,msgs[0].addr,0,0, + msgs[0].buf+offs,bcnt)) { ret = -EIO; goto done; } @@ -216,13 +364,19 @@ static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap, } else { /* Simple write */ ret = 1; - if (pvr2_i2c_write(hdw,msgs[0].addr, - msgs[0].buf,msgs[0].len)) { + if (funcp(hdw,msgs[0].addr, + msgs[0].buf,msgs[0].len,0,0)) { ret = -EIO; } goto done; } } else if (num == 2) { + if (msgs[0].addr != msgs[1].addr) { + trace_i2c("i2c refusing 2 phase transfer with" + " conflicting target addresses"); + ret = -ENOTSUPP; + goto done; + } if ((!((msgs[0].flags & I2C_M_RD))) && (msgs[1].flags & I2C_M_RD)) { u16 tcnt,bcnt,wcnt,offs; @@ -238,9 +392,9 @@ static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap, if (bcnt > sizeof(hdw->cmd_buffer)-1) { bcnt = sizeof(hdw->cmd_buffer)-1; } - if (pvr2_i2c_read(hdw,msgs[0].addr, - msgs[0].buf,wcnt, - msgs[1].buf+offs,bcnt)) { + if (funcp(hdw,msgs[0].addr, + msgs[0].buf,wcnt, + msgs[1].buf+offs,bcnt)) { ret = -EIO; goto done; } @@ -262,18 +416,29 @@ static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap, done: if (pvrusb2_debug & PVR2_TRACE_I2C_TRAF) { - unsigned int idx; + unsigned int idx,offs,cnt; for (idx = 0; idx < num; idx++) { + cnt = msgs[idx].len; printk(KERN_INFO "pvrusb2 i2c xfer %u/%u:" " addr=0x%x len=%d %s%s", idx+1,num, msgs[idx].addr, - msgs[idx].len, + cnt, (msgs[idx].flags & I2C_M_RD ? "read" : "write"), (msgs[idx].flags & I2C_M_NOSTART ? " nostart" : "")); + if ((ret > 0) || !(msgs[idx].flags & I2C_M_RD)) { + if (cnt > 8) cnt = 8; + printk(" ["); + for (offs = 0; offs < (cnt>8?8:cnt); offs++) { + if (offs) printk(" "); + printk("%02x",msgs[idx].buf[offs]); + } + if (offs < cnt) printk(" ..."); + printk("]"); + } if (idx+1 == num) { printk(" result=%d",ret); } @@ -296,7 +461,7 @@ static int pvr2_i2c_control(struct i2c_adapter *adapter, static u32 pvr2_i2c_functionality(struct i2c_adapter *adap) { - return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C; + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE_DATA; } static int pvr2_i2c_core_singleton(struct i2c_client *cp, @@ -322,15 +487,26 @@ static int pvr2_i2c_core_singleton(struct i2c_client *cp, int pvr2_i2c_client_cmd(struct pvr2_i2c_client *cp,unsigned int cmd,void *arg) { + int stat; + if (pvrusb2_debug & PVR2_TRACE_I2C_CMD) { + char buf[100]; + unsigned int cnt; + cnt = pvr2_i2c_client_describe(cp,PVR2_I2C_DETAIL_DEBUG, + buf,sizeof(buf)); + pvr2_trace(PVR2_TRACE_I2C_CMD, + "i2c COMMAND (code=%u 0x%x) to %.*s", + cmd,cmd,cnt,buf); + } + stat = pvr2_i2c_core_singleton(cp->client,cmd,arg); if (pvrusb2_debug & PVR2_TRACE_I2C_CMD) { char buf[100]; unsigned int cnt; cnt = pvr2_i2c_client_describe(cp,PVR2_I2C_DETAIL_DEBUG, buf,sizeof(buf)); pvr2_trace(PVR2_TRACE_I2C_CMD, - "i2c COMMAND to %.*s",cnt,buf); + "i2c COMMAND to %.*s (ret=%d)",cnt,buf,stat); } - return pvr2_i2c_core_singleton(cp->client,cmd,arg); + return stat; } int pvr2_i2c_core_cmd(struct pvr2_hdw *hdw,unsigned int cmd,void *arg) @@ -735,6 +911,24 @@ static void do_i2c_scan(struct pvr2_hdw *hdw) void pvr2_i2c_core_init(struct pvr2_hdw *hdw) { + unsigned int idx; + + // The default action for all possible I2C addresses is just to do + // the transfer normally. + for (idx = 0; idx < PVR2_I2C_FUNC_CNT; idx++) { + hdw->i2c_func[idx] = pvr2_i2c_basic_op; + } + +#ifdef CONFIG_VIDEO_PVRUSB2_24XXX + // If however we're dealing with new hardware, insert some hacks in + // the I2C transfer stack to let things work better. + if (hdw->hdw_type == PVR2_HDW_TYPE_24XXX) { + hdw->i2c_func[0x1b] = i2c_hack_wm8775; + hdw->i2c_func[0x44] = i2c_hack_cx25840; + } +#endif + + // Configure the adapter and set up everything else related to it. memcpy(&hdw->i2c_adap,&pvr2_i2c_adap_template,sizeof(hdw->i2c_adap)); memcpy(&hdw->i2c_algo,&pvr2_i2c_algo_template,sizeof(hdw->i2c_algo)); strlcpy(hdw->i2c_adap.name,hdw->name,sizeof(hdw->i2c_adap.name)); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-io.c b/linux/drivers/media/video/pvrusb2/pvrusb2-io.c index a6bc3befe..cb0ca6376 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-io.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-io.c @@ -26,8 +26,7 @@ #include <linux/string.h> #include <linux/slab.h> #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -#include <asm/atomic.h> -#include <asm/mutex.h> +#include <linux/mutex.h> #else #include <asm/semaphore.h> #endif diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-ioread.c b/linux/drivers/media/video/pvrusb2/pvrusb2-ioread.c index f81a0c225..555dbbca8 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-ioread.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-ioread.c @@ -26,8 +26,7 @@ #include <linux/string.h> #include <linux/slab.h> #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -#include <asm/atomic.h> -#include <asm/mutex.h> +#include <linux/mutex.h> #else #include <asm/semaphore.h> #endif diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-main.c b/linux/drivers/media/video/pvrusb2/pvrusb2-main.c index c554671af..245f7a36d 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-main.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-main.c @@ -29,13 +29,15 @@ #include <linux/moduleparam.h> #include <linux/smp_lock.h> #include <linux/usb.h> -#include <linux/videodev.h> +#include <linux/videodev2.h> #include "pvrusb2-hdw.h" #include "pvrusb2-context.h" #include "pvrusb2-debug.h" #include "pvrusb2-v4l2.h" +#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS #include "pvrusb2-sysfs.h" +#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ #define DRIVER_AUTHOR "Mike Isely <isely@pobox.com>" #define DRIVER_DESC "Hauppauge WinTV-PVR-USB2 MPEG2 Encoder/Tuner" @@ -60,13 +62,17 @@ int pvrusb2_debug = DEFAULT_DEBUG_MASK; module_param_named(debug,pvrusb2_debug,int,S_IRUGO|S_IWUSR); MODULE_PARM_DESC(debug, "Debug trace mask"); +#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS static struct pvr2_sysfs_class *class_ptr = 0; +#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ static void pvr_setup_attach(struct pvr2_context *pvr) { /* Create association with v4l layer */ pvr2_v4l2_create(pvr); +#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS pvr2_sysfs_create(pvr,class_ptr); +#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ } static int pvr_probe(struct usb_interface *intf, @@ -128,15 +134,9 @@ static int __init pvr_init(void) pvr2_trace(PVR2_TRACE_INIT,"pvr_init"); - /* Auto-load various support modules (with which we may - indirectly interact) */ - request_module("tuner"); - request_module("tveeprom"); - request_module("msp3400"); - request_module("saa7115"); - request_module("tda9887"); - +#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS class_ptr = pvr2_sysfs_class_create(); +#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ ret = usb_register(&pvr_driver); @@ -153,7 +153,9 @@ static void __exit pvr_exit(void) pvr2_trace(PVR2_TRACE_INIT,"pvr_exit"); +#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS pvr2_sysfs_class_destroy(class_ptr); +#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */ usb_deregister(&pvr_driver); } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-std.c b/linux/drivers/media/video/pvrusb2/pvrusb2-std.c new file mode 100644 index 000000000..64ba223c2 --- /dev/null +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-std.c @@ -0,0 +1,406 @@ +/* + * + * $Id$ + * + * Copyright (C) 2005 Mike Isely <isely@pobox.com> + * + * 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 + * + * 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 "pvrusb2-std.h" +#include "pvrusb2-debug.h" + +struct std_name { + const char *name; + v4l2_std_id id; +}; + + +#define CSTD_PAL \ + (V4L2_STD_PAL_B| \ + V4L2_STD_PAL_B1| \ + V4L2_STD_PAL_G| \ + V4L2_STD_PAL_H| \ + V4L2_STD_PAL_I| \ + V4L2_STD_PAL_D| \ + V4L2_STD_PAL_D1| \ + V4L2_STD_PAL_K| \ + V4L2_STD_PAL_M| \ + V4L2_STD_PAL_N| \ + V4L2_STD_PAL_Nc| \ + V4L2_STD_PAL_60) + +#define CSTD_NTSC \ + (V4L2_STD_NTSC_M| \ + V4L2_STD_NTSC_M_JP| \ + V4L2_STD_NTSC_M_KR| \ + V4L2_STD_NTSC_443) + +#define CSTD_SECAM \ + (V4L2_STD_SECAM_B| \ + V4L2_STD_SECAM_D| \ + V4L2_STD_SECAM_G| \ + V4L2_STD_SECAM_H| \ + V4L2_STD_SECAM_K| \ + V4L2_STD_SECAM_K1| \ + V4L2_STD_SECAM_L| \ + V4L2_STD_SECAM_LC) + +#define TSTD_B (V4L2_STD_PAL_B|V4L2_STD_SECAM_B) +#define TSTD_B1 (V4L2_STD_PAL_B1) +#define TSTD_D (V4L2_STD_PAL_D|V4L2_STD_SECAM_D) +#define TSTD_D1 (V4L2_STD_PAL_D1) +#define TSTD_G (V4L2_STD_PAL_G|V4L2_STD_SECAM_G) +#define TSTD_H (V4L2_STD_PAL_H|V4L2_STD_SECAM_H) +#define TSTD_I (V4L2_STD_PAL_I) +#define TSTD_K (V4L2_STD_PAL_K|V4L2_STD_SECAM_K) +#define TSTD_K1 (V4L2_STD_SECAM_K1) +#define TSTD_L (V4L2_STD_SECAM_L) +#define TSTD_M (V4L2_STD_PAL_M|V4L2_STD_NTSC_M) +#define TSTD_N (V4L2_STD_PAL_N) +#define TSTD_Nc (V4L2_STD_PAL_Nc) +#define TSTD_60 (V4L2_STD_PAL_60) + +#define CSTD_ALL (CSTD_PAL|CSTD_NTSC|CSTD_SECAM) + +/* Mapping of standard bits to color system */ +const static struct std_name std_groups[] = { + {"PAL",CSTD_PAL}, + {"NTSC",CSTD_NTSC}, + {"SECAM",CSTD_SECAM}, +}; + +/* Mapping of standard bits to modulation system */ +const static struct std_name std_items[] = { + {"B",TSTD_B}, + {"B1",TSTD_B1}, + {"D",TSTD_D}, + {"D1",TSTD_D1}, + {"G",TSTD_G}, + {"H",TSTD_H}, + {"I",TSTD_I}, + {"K",TSTD_K}, + {"K1",TSTD_K1}, + {"L",TSTD_L}, + {"LC",V4L2_STD_SECAM_LC}, + {"M",TSTD_M}, + {"Mj",V4L2_STD_NTSC_M_JP}, + {"443",V4L2_STD_NTSC_443}, + {"Mk",V4L2_STD_NTSC_M_KR}, + {"N",TSTD_N}, + {"Nc",TSTD_Nc}, + {"60",TSTD_60}, +}; + + +// Search an array of std_name structures and return a pointer to the +// element with the matching name. +static const struct std_name *find_std_name(const struct std_name *arrPtr, + unsigned int arrSize, + const char *bufPtr, + unsigned int bufSize) +{ + unsigned int idx; + const struct std_name *p; + for (idx = 0; idx < arrSize; idx++) { + p = arrPtr + idx; + if (strlen(p->name) != bufSize) continue; + if (!memcmp(bufPtr,p->name,bufSize)) return p; + } + return 0; +} + + +int pvr2_std_str_to_id(v4l2_std_id *idPtr,const char *bufPtr, + unsigned int bufSize) +{ + v4l2_std_id id = 0; + v4l2_std_id cmsk = 0; + v4l2_std_id t; + int mMode = 0; + unsigned int cnt; + char ch; + const struct std_name *sp; + + while (bufSize) { + if (!mMode) { + cnt = 0; + while ((cnt < bufSize) && (bufPtr[cnt] != '-')) cnt++; + if (cnt >= bufSize) return 0; // No more characters + sp = find_std_name( + std_groups, + sizeof(std_groups)/sizeof(std_groups[0]), + bufPtr,cnt); + if (!sp) return 0; // Illegal color system name + cnt++; + bufPtr += cnt; + bufSize -= cnt; + mMode = !0; + cmsk = sp->id; + continue; + } + cnt = 0; + while (cnt < bufSize) { + ch = bufPtr[cnt]; + if (ch == ';') { + mMode = 0; + break; + } + if (ch == '/') break; + cnt++; + } + sp = find_std_name(std_items, + sizeof(std_items)/sizeof(std_items[0]), + bufPtr,cnt); + if (!sp) return 0; // Illegal modulation system ID + t = sp->id & cmsk; + if (!t) return 0; // Specific color + modulation system illegal + id |= t; + if (cnt < bufSize) cnt++; + bufPtr += cnt; + bufSize -= cnt; + } + + if (idPtr) *idPtr = id; + return !0; +} + + +unsigned int pvr2_std_id_to_str(char *bufPtr, unsigned int bufSize, + v4l2_std_id id) +{ + unsigned int idx1,idx2; + const struct std_name *ip,*gp; + int gfl,cfl; + unsigned int c1,c2; + cfl = 0; + c1 = 0; + for (idx1 = 0; + idx1 < sizeof(std_groups)/sizeof(std_groups[0]); + idx1++) { + gp = std_groups + idx1; + gfl = 0; + for (idx2 = 0; + idx2 < sizeof(std_items)/sizeof(std_items[0]); + idx2++) { + ip = std_items + idx2; + if (!(gp->id & ip->id & id)) continue; + if (!gfl) { + if (cfl) { + c2 = scnprintf(bufPtr,bufSize,";"); + c1 += c2; + bufSize -= c2; + bufPtr += c2; + } + cfl = !0; + c2 = scnprintf(bufPtr,bufSize, + "%s-",gp->name); + gfl = !0; + } else { + c2 = scnprintf(bufPtr,bufSize,"/"); + } + c1 += c2; + bufSize -= c2; + bufPtr += c2; + c2 = scnprintf(bufPtr,bufSize, + ip->name); + c1 += c2; + bufSize -= c2; + bufPtr += c2; + } + } + return c1; +} + + +// Template data for possible enumerated video standards. Here we group +// standards which share common frame rates and resolution. +static struct v4l2_standard generic_standards[] = { + { + .id = (TSTD_B|TSTD_B1| + TSTD_D|TSTD_D1| + TSTD_G| + TSTD_H| + TSTD_I| + TSTD_K|TSTD_K1| + TSTD_L| + V4L2_STD_SECAM_LC | + TSTD_N|TSTD_Nc), + .frameperiod = + { + .numerator = 1, + .denominator= 25 + }, + .framelines = 625, + .reserved = {0,0,0,0} + }, { + .id = (TSTD_M| + V4L2_STD_NTSC_M_JP| + V4L2_STD_NTSC_M_KR), + .frameperiod = + { + .numerator = 1001, + .denominator= 30000 + }, + .framelines = 525, + .reserved = {0,0,0,0} + }, { // This is a total wild guess + .id = (TSTD_60), + .frameperiod = + { + .numerator = 1001, + .denominator= 30000 + }, + .framelines = 525, + .reserved = {0,0,0,0} + }, { // This is total wild guess + .id = V4L2_STD_NTSC_443, + .frameperiod = + { + .numerator = 1001, + .denominator= 30000 + }, + .framelines = 525, + .reserved = {0,0,0,0} + } +}; + +#define generic_standards_cnt (sizeof(generic_standards)/sizeof(generic_standards[0])) + +static struct v4l2_standard *match_std(v4l2_std_id id) +{ + unsigned int idx; + for (idx = 0; idx < generic_standards_cnt; idx++) { + if (generic_standards[idx].id & id) { + return generic_standards + idx; + } + } + return 0; +} + +static int pvr2_std_fill(struct v4l2_standard *std,v4l2_std_id id) +{ + struct v4l2_standard *template; + int idx; + unsigned int bcnt; + template = match_std(id); + if (!template) return 0; + idx = std->index; + memcpy(std,template,sizeof(*template)); + std->index = idx; + std->id = id; + bcnt = pvr2_std_id_to_str(std->name,sizeof(std->name)-1,id); + std->name[bcnt] = 0; + pvr2_trace(PVR2_TRACE_INIT,"Set up standard idx=%u name=%s", + std->index,std->name); + return !0; +} + +/* These are special cases of combined standards that we should enumerate + separately if the component pieces are present. */ +static v4l2_std_id std_mixes[] = { + V4L2_STD_PAL_B | V4L2_STD_PAL_G, + V4L2_STD_PAL_D | V4L2_STD_PAL_K, + V4L2_STD_SECAM_B | V4L2_STD_SECAM_G, + V4L2_STD_SECAM_D | V4L2_STD_SECAM_K, +}; + +struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, + v4l2_std_id id) +{ + unsigned int std_cnt = 0; + unsigned int idx,bcnt,idx2; + v4l2_std_id idmsk,cmsk,fmsk; + struct v4l2_standard *stddefs; + + if (pvrusb2_debug & PVR2_TRACE_INIT) { + char buf[50]; + bcnt = pvr2_std_id_to_str(buf,sizeof(buf),id); + pvr2_trace( + PVR2_TRACE_INIT,"Mapping standards mask=0x%x (%.*s)", + (int)id,bcnt,buf); + } + + *countptr = 0; + std_cnt = 0; + fmsk = 0; + for (idmsk = 1, cmsk = id; cmsk; idmsk <<= 1) { + if (!(idmsk & cmsk)) continue; + cmsk &= ~idmsk; + if (match_std(idmsk)) { + std_cnt++; + continue; + } + fmsk |= idmsk; + } + + for (idx2 = 0; idx2 < sizeof(std_mixes)/sizeof(std_mixes[0]); idx2++) { + if ((id & std_mixes[idx2]) == std_mixes[idx2]) std_cnt++; + } + + if (fmsk) { + char buf[50]; + bcnt = pvr2_std_id_to_str(buf,sizeof(buf),fmsk); + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "WARNING:" + " Failed to classify the following standard(s): %.*s", + bcnt,buf); + } + + pvr2_trace(PVR2_TRACE_INIT,"Setting up %u unique standard(s)", + std_cnt); + if (!std_cnt) return 0; // paranoia + + stddefs = kmalloc(sizeof(struct v4l2_standard) * std_cnt, + GFP_KERNEL); + memset(stddefs,0,sizeof(struct v4l2_standard) * std_cnt); + for (idx = 0; idx < std_cnt; idx++) stddefs[idx].index = idx; + + idx = 0; + + /* Enumerate potential special cases */ + for (idx2 = 0; ((idx2 < sizeof(std_mixes)/sizeof(std_mixes[0])) && + (idx < std_cnt)); idx2++) { + if (!(id & std_mixes[idx2])) continue; + if (pvr2_std_fill(stddefs+idx,std_mixes[idx2])) idx++; + } + /* Now enumerate individual pieces */ + for (idmsk = 1, cmsk = id; cmsk && (idx < std_cnt); idmsk <<= 1) { + if (!(idmsk & cmsk)) continue; + cmsk &= ~idmsk; + if (!pvr2_std_fill(stddefs+idx,idmsk)) continue; + idx++; + } + + *countptr = std_cnt; + return stddefs; +} + +v4l2_std_id pvr2_std_get_usable(void) +{ + return CSTD_ALL; +} + + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-std.h b/linux/drivers/media/video/pvrusb2/pvrusb2-std.h new file mode 100644 index 000000000..dc9ef5bb3 --- /dev/null +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-std.h @@ -0,0 +1,61 @@ +/* + * + * $Id$ + * + * Copyright (C) 2005 Mike Isely <isely@pobox.com> + * + * 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 + * + * 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 + * + */ +#ifndef __PVRUSB2_STD_H +#define __PVRUSB2_STD_H + +#include "compat.h" +#include <linux/videodev2.h> + +// Convert string describing one or more video standards into a mask of V4L +// standard bits. Return true if conversion succeeds otherwise return +// false. String is expected to be of the form: C1-x/y;C2-a/b where C1 and +// C2 are color system names (e.g. "PAL", "NTSC") and x, y, a, and b are +// modulation schemes (e.g. "M", "B", "G", etc). +int pvr2_std_str_to_id(v4l2_std_id *idPtr,const char *bufPtr, + unsigned int bufSize); + +// Convert any arbitrary set of video standard bits into an unambiguous +// readable string. Return value is the number of bytes consumed in the +// buffer. The formatted string is of a form that can be parsed by our +// sibling std_std_to_id() function. +unsigned int pvr2_std_id_to_str(char *bufPtr, unsigned int bufSize, + v4l2_std_id id); + +// Create an array of suitable v4l2_standard structures given a bit mask of +// video standards to support. The array is allocated from the heap, and +// the number of elements is returned in the first argument. +struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, + v4l2_std_id id); + +// Return mask of which video standard bits are valid +v4l2_std_id pvr2_std_get_usable(void); + +#endif /* __PVRUSB2_STD_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c index a9710b52c..075882877 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c @@ -19,6 +19,7 @@ * */ +#include <linux/config.h> #include <linux/string.h> #include <linux/slab.h> #include <asm/semaphore.h> @@ -26,49 +27,18 @@ #include "pvrusb2-sysfs.h" #include "pvrusb2-hdw.h" #include "pvrusb2-debug.h" +#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC #include "pvrusb2-debugifc.h" +#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ #define pvr2_sysfs_trace(...) pvr2_trace(PVR2_TRACE_SYSFS,__VA_ARGS__) -static char *item_names[PVR2_CID_COUNT] = { - [PVR2_CID_BRIGHTNESS] = "ctl_brightness", - [PVR2_CID_CONTRAST] = "ctl_contrast", - [PVR2_CID_SATURATION] = "ctl_saturation", - [PVR2_CID_HUE] = "ctl_hue", - [PVR2_CID_VOLUME] = "ctl_volume", - [PVR2_CID_BALANCE] = "ctl_balance", - [PVR2_CID_BASS] = "ctl_bass", - [PVR2_CID_TREBLE] = "ctl_treble", - [PVR2_CID_MUTE] = "ctl_mute", - [PVR2_CID_SRATE] = "ctl_srate", - [PVR2_CID_AUDIOBITRATE] = "ctl_audio_bitrate", - [PVR2_CID_AUDIOCRC] = "ctl_audio_crc", - [PVR2_CID_AUDIOEMPHASIS] = "ctl_audio_emphasis", - [PVR2_CID_VBR] = "ctl_vbr", - [PVR2_CID_AVERAGEVIDEOBITRATE] = "ctl_video_average_bitrate", - [PVR2_CID_PEAKVIDEOBITRATE] = "ctl_video_peak_bitrate", - [PVR2_CID_VIDEOSTANDARD] = "ctl_video_standard", - [PVR2_CID_INPUT] = "ctl_input", - [PVR2_CID_AUDIOMODE] = "ctl_audio_mode", - [PVR2_CID_FREQUENCY] = "ctl_frequency", - [PVR2_CID_HRES] = "ctl_resolution_hor", - [PVR2_CID_VRES] = "ctl_resolution_ver", - [PVR2_CID_INTERLACE] = "ctl_interlace", - [PVR2_CID_AUDIOLAYER] = "ctl_audio_layer", - [PVR2_CID_CHANNEL] = "ctl_channel", - [PVR2_CID_CHANPROG_ID] = "ctl_freq_table_channel", - [PVR2_CID_CHANPROG_FREQ] = "ctl_freq_table_value", - [PVR2_CID_SIGNAL_PRESENT] = "ctl_signal_present", - [PVR2_CID_STREAMING_ENABLED] = "ctl_streaming_enabled", - [PVR2_CID_HSM] = "ctl_usb_speed", - [PVR2_CID_SUBSYS_MASK] = "ctl_debug_subsys_mask", - [PVR2_CID_SUBSYS_STREAM_MASK] = "ctl_debug_subsys_stream_mask", -}; - struct pvr2_sysfs { struct pvr2_channel channel; struct class_device *class_dev; +#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC struct pvr2_sysfs_debugifc *debugifc; +#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ struct pvr2_sysfs_ctl_item *item_first; struct pvr2_sysfs_ctl_item *item_last; struct sysfs_ops kops; @@ -77,22 +47,27 @@ struct pvr2_sysfs { struct class_device_attribute attr_unit_number; }; +#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC struct pvr2_sysfs_debugifc { struct class_device_attribute attr_debugcmd; struct class_device_attribute attr_debuginfo; }; +#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ struct pvr2_sysfs_ctl_item { struct class_device_attribute attr_name; struct class_device_attribute attr_min; struct class_device_attribute attr_max; struct class_device_attribute attr_enum; + struct class_device_attribute attr_bits; struct class_device_attribute attr_val; - int attr_id; + struct class_device_attribute attr_custom; + struct pvr2_ctrl *cptr; struct pvr2_sysfs *chptr; struct pvr2_sysfs_ctl_item *item_next; - struct attribute *attr_gen[5]; + struct attribute *attr_gen[6]; struct attribute_group grp; + char name[80]; }; struct pvr2_sysfs_class { @@ -101,13 +76,16 @@ struct pvr2_sysfs_class { static ssize_t show_name(int id,struct class_device *class_dev,char *buf) { + struct pvr2_ctrl *cptr; struct pvr2_sysfs *sfp; const char *name; sfp = (struct pvr2_sysfs *)class_dev->class_data; if (!sfp) return -EINVAL; - name = pvr2_hdw_get_ctl_name(sfp->channel.hdw,id); + cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); + if (!cptr) return -EINVAL; + name = pvr2_ctrl_get_desc(cptr); pvr2_sysfs_trace("pvr2_sysfs(%p) show_name(cid=%d) is %s",sfp,id,name); if (!name) return -EINVAL; @@ -117,217 +95,173 @@ static ssize_t show_name(int id,struct class_device *class_dev,char *buf) static ssize_t show_min(int id,struct class_device *class_dev,char *buf) { + struct pvr2_ctrl *cptr; struct pvr2_sysfs *sfp; - int val; + long val; sfp = (struct pvr2_sysfs *)class_dev->class_data; if (!sfp) return -EINVAL; - val = pvr2_hdw_get_ctl_min_value(sfp->channel.hdw,id); + cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); + if (!cptr) return -EINVAL; + val = pvr2_ctrl_get_min(cptr); - pvr2_sysfs_trace("pvr2_sysfs(%p) show_min(cid=%d) is %d",sfp,id,val); + pvr2_sysfs_trace("pvr2_sysfs(%p) show_min(cid=%d) is %ld",sfp,id,val); - return scnprintf(buf,PAGE_SIZE,"%d\n",val); + return scnprintf(buf,PAGE_SIZE,"%ld\n",val); } static ssize_t show_max(int id,struct class_device *class_dev,char *buf) { + struct pvr2_ctrl *cptr; struct pvr2_sysfs *sfp; - int val; + long val; sfp = (struct pvr2_sysfs *)class_dev->class_data; if (!sfp) return -EINVAL; - val = pvr2_hdw_get_ctl_max_value(sfp->channel.hdw,id); + cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); + if (!cptr) return -EINVAL; + val = pvr2_ctrl_get_max(cptr); - pvr2_sysfs_trace("pvr2_sysfs(%p) show_max(cid=%d) is %d",sfp,id,val); + pvr2_sysfs_trace("pvr2_sysfs(%p) show_max(cid=%d) is %ld",sfp,id,val); - return scnprintf(buf,PAGE_SIZE,"%d\n",val); + return scnprintf(buf,PAGE_SIZE,"%ld\n",val); } -static ssize_t show_val_int(int id,struct class_device *class_dev,char *buf) +static ssize_t show_val_norm(int id,struct class_device *class_dev,char *buf) { + struct pvr2_ctrl *cptr; struct pvr2_sysfs *sfp; - int val; + int val,ret; + unsigned int cnt = 0; sfp = (struct pvr2_sysfs *)class_dev->class_data; if (!sfp) return -EINVAL; - val = pvr2_hdw_get_ctl_value(sfp->channel.hdw,id); + cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); + if (!cptr) return -EINVAL; - pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_int(cid=%d) is %d", - sfp,id,val); + ret = pvr2_ctrl_get_value(cptr,&val); + if (ret < 0) return ret; - return scnprintf(buf,PAGE_SIZE,"%d\n",val); + ret = pvr2_ctrl_value_to_sym(cptr,~0,val, + buf,PAGE_SIZE-1,&cnt); + + pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_norm(cid=%d) is %.*s (%d)", + sfp,id,cnt,buf,val); + buf[cnt] = '\n'; + return cnt+1; } -static ssize_t show_val_enum(int id,struct class_device *class_dev,char *buf) +static ssize_t show_val_custom(int id,struct class_device *class_dev,char *buf) { + struct pvr2_ctrl *cptr; struct pvr2_sysfs *sfp; - int val; - const char *name; + int val,ret; + unsigned int cnt = 0; sfp = (struct pvr2_sysfs *)class_dev->class_data; if (!sfp) return -EINVAL; - val = pvr2_hdw_get_ctl_value(sfp->channel.hdw,id); + cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); + if (!cptr) return -EINVAL; - name = pvr2_hdw_get_ctl_value_name(sfp->channel.hdw,id,val); + ret = pvr2_ctrl_get_value(cptr,&val); + if (ret < 0) return ret; - pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_enum(cid=%d) is %s (%d)", - sfp,id,name,val); + ret = pvr2_ctrl_custom_value_to_sym(cptr,~0,val, + buf,PAGE_SIZE-1,&cnt); - return scnprintf(buf,PAGE_SIZE,"%s\n",name); + pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_custom(cid=%d) is %.*s (%d)", + sfp,id,cnt,buf,val); + buf[cnt] = '\n'; + return cnt+1; } static ssize_t show_enum(int id,struct class_device *class_dev,char *buf) { + struct pvr2_ctrl *cptr; struct pvr2_sysfs *sfp; - int minval,maxval,val; - const char *name; - ssize_t cnt = 0; + long val; + unsigned int bcnt,ccnt,ecnt; sfp = (struct pvr2_sysfs *)class_dev->class_data; if (!sfp) return -EINVAL; - minval = pvr2_hdw_get_ctl_min_value(sfp->channel.hdw,id); - maxval = pvr2_hdw_get_ctl_max_value(sfp->channel.hdw,id); - for (val = minval; val <= maxval; val++) { - name = pvr2_hdw_get_ctl_value_name(sfp->channel.hdw,id,val); - cnt += scnprintf(buf+cnt,PAGE_SIZE-cnt,"%s\n",name); + cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); + if (!cptr) return -EINVAL; + ecnt = pvr2_ctrl_get_cnt(cptr); + bcnt = 0; + for (val = 0; val < ecnt; val++) { + pvr2_ctrl_get_valname(cptr,val,buf+bcnt,PAGE_SIZE-bcnt,&ccnt); + bcnt += ccnt; + if (bcnt >= PAGE_SIZE) break; + buf[bcnt] = '\n'; + bcnt++; } pvr2_sysfs_trace("pvr2_sysfs(%p) show_enum(cid=%d)",sfp,id); - return cnt; + return bcnt; } -static int store_val_any(int id,struct pvr2_sysfs *sfp, - const char *buf,unsigned int count) +static ssize_t show_bits(int id,struct class_device *class_dev,char *buf) { - int val,minval,maxval; - int ch,ret; - const char *nv; - unsigned int nl; - int negfl; - - /* Trim leading / trailing whitespace */ - while (count) { - ch = buf[0]; - if ((ch > 32) && (ch < 127)) break; - buf++; - count--; - } - while (count) { - ch = buf[count-1]; - if ((ch > 32) && (ch < 127)) break; - count--; - } - - /* Is this an enum? Look for a string value */ - minval = pvr2_hdw_get_ctl_min_value(sfp->channel.hdw,id); - maxval = pvr2_hdw_get_ctl_max_value(sfp->channel.hdw,id); - for (val = minval; val <= maxval; val++) { - nv = pvr2_hdw_get_ctl_value_name(sfp->channel.hdw,id,val); - if ((!nv) && (val == minval)) break; /* Not an enum */ - pvr2_sysfs_trace("pvr2_sysfs(%p) trying ctl_id %d val %d", - sfp,id,val); - if (!nv) { - pvr2_sysfs_trace("pvr2_sysfs(%p) no pointer",sfp); - continue; - } - nl = strlen(nv); - if (nl != count) { - pvr2_sysfs_trace("pvr2_sysfs(%p) count mismatch" - " %d != %d", - sfp,count,nl); - continue; - } - if (memcmp(buf,nv,nl)) { - pvr2_sysfs_trace( - "pvr2_sysfs(%p) name mismatch" - " >>%.*s<< != >>%.*s<<", - sfp,nl,buf,nl,nv); - continue; - } - pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_any(cid=%d)" - " is enum %s", - sfp,id,nv); - ret = pvr2_hdw_set_ctl_value(sfp->channel.hdw,id,val); - pvr2_hdw_commit_ctl(sfp->channel.hdw); - return 0; - } - if (val > minval) { - pvr2_sysfs_trace( - "pvr2_sysfs(%p) store_val_any(cid=%d)" - " unmatched enum >>%.*s<<", - sfp,id,count,buf); - } + struct pvr2_ctrl *cptr; + struct pvr2_sysfs *sfp; + int valid_bits,msk; + unsigned int bcnt,ccnt; - /* Try to parse as a number */ - negfl = 0; - val = 0; - if (count) { - if (buf[0] == '-') { - negfl = !0; - buf++; - count--; - } else if (buf[0] == '+') { - buf++; - count--; - } - } - while (count) { - ch = buf[0]; - if ((ch < '0') || (ch > '9')) break; - val = val * 10; - val += (ch - '0'); - buf++; - count--; - } - if (!count) { - if (negfl) val = -val; - pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_any(cid=%d)" - " int is %d", - sfp,id,val); - - ret = pvr2_hdw_set_ctl_value(sfp->channel.hdw,id,val); - pvr2_hdw_commit_ctl(sfp->channel.hdw); - return ret; + sfp = (struct pvr2_sysfs *)class_dev->class_data; + if (!sfp) return -EINVAL; + cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); + if (!cptr) return -EINVAL; + valid_bits = pvr2_ctrl_get_mask(cptr); + bcnt = 0; + for (msk = 1; valid_bits; msk <<= 1) { + if (!(msk & valid_bits)) continue; + valid_bits &= ~msk; + pvr2_ctrl_get_valname(cptr,msk,buf+bcnt,PAGE_SIZE-bcnt,&ccnt); + bcnt += ccnt; + if (bcnt >= PAGE_SIZE) break; + buf[bcnt] = '\n'; + bcnt++; } - - return -EINVAL; + pvr2_sysfs_trace("pvr2_sysfs(%p) show_bits(cid=%d)",sfp,id); + return bcnt; } -static int store_val_multi(int id,struct pvr2_sysfs *sfp, - const char *buf,unsigned int count) +static int store_val_any(int id,int customfl,struct pvr2_sysfs *sfp, + const char *buf,unsigned int count) { - unsigned int count2; + struct pvr2_ctrl *cptr; int ret; + int mask,val; - while (count) { - count2 = 0; - while ((count2 < count) && (buf[count2] != '\n')) count2++; - ret = store_val_any(id,sfp,buf,count2); - if (ret < 0) return ret; - if (count2 < count) count2++; - buf += count2; - count -= count2; + cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id); + if (customfl) { + ret = pvr2_ctrl_custom_sym_to_value(cptr,buf,count,&mask,&val); + } else { + ret = pvr2_ctrl_sym_to_value(cptr,buf,count,&mask,&val); } - return 0; + if (ret < 0) return ret; + ret = pvr2_ctrl_set_mask_value(cptr,mask,val); + pvr2_hdw_commit_ctl(sfp->channel.hdw); + return ret; } -static ssize_t store_val_int(int id,struct class_device *class_dev, +static ssize_t store_val_norm(int id,struct class_device *class_dev, const char *buf,size_t count) { struct pvr2_sysfs *sfp; int ret; sfp = (struct pvr2_sysfs *)class_dev->class_data; - ret = store_val_multi(id,sfp,buf,count); + ret = store_val_any(id,0,sfp,buf,count); if (!ret) ret = count; return ret; } -static ssize_t store_val_enum(int id,struct class_device *class_dev, - const char *buf,size_t count) +static ssize_t store_val_custom(int id,struct class_device *class_dev, + const char *buf,size_t count) { struct pvr2_sysfs *sfp; int ret; sfp = (struct pvr2_sysfs *)class_dev->class_data; - ret = store_val_multi(id,sfp,buf,count); + ret = store_val_any(id,1,sfp,buf,count); if (!ret) ret = count; return ret; } @@ -357,57 +291,61 @@ static ssize_t sf_name##_##ctl_id(struct class_device *class_dev,const char *buf CREATE_SHOW_INSTANCE(show_name,ctl_id) \ CREATE_SHOW_INSTANCE(show_min,ctl_id) \ CREATE_SHOW_INSTANCE(show_max,ctl_id) \ -CREATE_SHOW_INSTANCE(show_val_int,ctl_id) \ -CREATE_SHOW_INSTANCE(show_val_enum,ctl_id) \ +CREATE_SHOW_INSTANCE(show_val_norm,ctl_id) \ +CREATE_SHOW_INSTANCE(show_val_custom,ctl_id) \ CREATE_SHOW_INSTANCE(show_enum,ctl_id) \ -CREATE_STORE_INSTANCE(store_val_int,ctl_id) \ -CREATE_STORE_INSTANCE(store_val_enum,ctl_id) +CREATE_SHOW_INSTANCE(show_bits,ctl_id) \ +CREATE_STORE_INSTANCE(store_val_norm,ctl_id) \ +CREATE_STORE_INSTANCE(store_val_custom,ctl_id) \ CREATE_BATCH(0) - CREATE_BATCH(1) - CREATE_BATCH(2) - CREATE_BATCH(3) - CREATE_BATCH(4) - CREATE_BATCH(5) - CREATE_BATCH(6) - CREATE_BATCH(7) - CREATE_BATCH(8) - CREATE_BATCH(9) - CREATE_BATCH(10) - CREATE_BATCH(11) - CREATE_BATCH(12) - CREATE_BATCH(13) - CREATE_BATCH(14) - CREATE_BATCH(15) - CREATE_BATCH(16) - CREATE_BATCH(17) - CREATE_BATCH(18) - CREATE_BATCH(19) - CREATE_BATCH(20) - CREATE_BATCH(21) - CREATE_BATCH(22) - CREATE_BATCH(23) - CREATE_BATCH(24) - CREATE_BATCH(25) - CREATE_BATCH(26) - CREATE_BATCH(27) - CREATE_BATCH(28) - CREATE_BATCH(29) - CREATE_BATCH(30) - CREATE_BATCH(31) - - struct pvr2_sysfs_func_set { - ssize_t (*show_name)(struct class_device *,char *); - ssize_t (*show_min)(struct class_device *,char *); - ssize_t (*show_max)(struct class_device *,char *); - ssize_t (*show_enum)(struct class_device *,char *); - ssize_t (*show_val_int)(struct class_device *,char *); - ssize_t (*show_val_enum)(struct class_device *,char *); - ssize_t (*store_val_int)(struct class_device *, - const char *,size_t); - ssize_t (*store_val_enum)(struct class_device *, - const char *,size_t); - }; +CREATE_BATCH(1) +CREATE_BATCH(2) +CREATE_BATCH(3) +CREATE_BATCH(4) +CREATE_BATCH(5) +CREATE_BATCH(6) +CREATE_BATCH(7) +CREATE_BATCH(8) +CREATE_BATCH(9) +CREATE_BATCH(10) +CREATE_BATCH(11) +CREATE_BATCH(12) +CREATE_BATCH(13) +CREATE_BATCH(14) +CREATE_BATCH(15) +CREATE_BATCH(16) +CREATE_BATCH(17) +CREATE_BATCH(18) +CREATE_BATCH(19) +CREATE_BATCH(20) +CREATE_BATCH(21) +CREATE_BATCH(22) +CREATE_BATCH(23) +CREATE_BATCH(24) +CREATE_BATCH(25) +CREATE_BATCH(26) +CREATE_BATCH(27) +CREATE_BATCH(28) +CREATE_BATCH(29) +CREATE_BATCH(30) +CREATE_BATCH(31) +CREATE_BATCH(32) +CREATE_BATCH(33) + +struct pvr2_sysfs_func_set { + ssize_t (*show_name)(struct class_device *,char *); + ssize_t (*show_min)(struct class_device *,char *); + ssize_t (*show_max)(struct class_device *,char *); + ssize_t (*show_enum)(struct class_device *,char *); + ssize_t (*show_bits)(struct class_device *,char *); + ssize_t (*show_val_norm)(struct class_device *,char *); + ssize_t (*store_val_norm)(struct class_device *, + const char *,size_t); + ssize_t (*show_val_custom)(struct class_device *,char *); + ssize_t (*store_val_custom)(struct class_device *, + const char *,size_t); +}; #define INIT_BATCH(ctl_id) \ [ctl_id] = { \ @@ -415,10 +353,11 @@ CREATE_BATCH(0) .show_min = show_min_##ctl_id, \ .show_max = show_max_##ctl_id, \ .show_enum = show_enum_##ctl_id, \ - .show_val_int = show_val_int_##ctl_id, \ - .show_val_enum = show_val_enum_##ctl_id, \ - .store_val_int = store_val_int_##ctl_id, \ - .store_val_enum = store_val_enum_##ctl_id, \ + .show_bits = show_bits_##ctl_id, \ + .show_val_norm = show_val_norm_##ctl_id, \ + .store_val_norm = store_val_norm_##ctl_id, \ + .show_val_custom = show_val_custom_##ctl_id, \ + .store_val_custom = store_val_custom_##ctl_id, \ } \ static struct pvr2_sysfs_func_set funcs[] = { @@ -454,6 +393,8 @@ static struct pvr2_sysfs_func_set funcs[] = { INIT_BATCH(29), INIT_BATCH(30), INIT_BATCH(31), + INIT_BATCH(32), + INIT_BATCH(33), }; @@ -461,19 +402,23 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) { struct pvr2_sysfs_ctl_item *cip; struct pvr2_sysfs_func_set *fp; + struct pvr2_ctrl *cptr; + unsigned int cnt,acnt; if ((ctl_id < 0) || (ctl_id >= (sizeof(funcs)/sizeof(funcs[0])))) { return; } fp = funcs + ctl_id; + cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,ctl_id); + if (!cptr) return; cip = kmalloc(sizeof(*cip),GFP_KERNEL); if (!cip) return; memset(cip,0,sizeof(*cip)); pvr2_sysfs_trace("Creating pvr2_sysfs_ctl_item id=%p",cip); - cip->attr_id = ctl_id; + cip->cptr = cptr; cip->chptr = sfp; cip->item_next = 0; @@ -503,38 +448,62 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) cip->attr_val.attr.name = "cur_val"; cip->attr_val.attr.mode = S_IRUGO; + cip->attr_custom.attr.owner = THIS_MODULE; + cip->attr_custom.attr.name = "custom_val"; + cip->attr_custom.attr.mode = S_IRUGO; + cip->attr_enum.attr.owner = THIS_MODULE; cip->attr_enum.attr.name = "enum_val"; cip->attr_enum.attr.mode = S_IRUGO; cip->attr_enum.show = fp->show_enum; - if (pvr2_hdw_get_ctl_rw(sfp->channel.hdw,ctl_id)) { + cip->attr_bits.attr.owner = THIS_MODULE; + cip->attr_bits.attr.name = "bit_val"; + cip->attr_bits.attr.mode = S_IRUGO; + cip->attr_bits.show = fp->show_bits; + + if (pvr2_ctrl_is_writable(cptr)) { cip->attr_val.attr.mode |= S_IWUSR|S_IWGRP; + cip->attr_custom.attr.mode |= S_IWUSR|S_IWGRP; } - cip->attr_gen[0] = &cip->attr_name.attr; - cip->attr_gen[1] = &cip->attr_val.attr; - if (pvr2_hdw_get_ctl_value_name( - sfp->channel.hdw,ctl_id, - pvr2_hdw_get_ctl_min_value(sfp->channel.hdw,ctl_id))) { + acnt = 0; + cip->attr_gen[acnt++] = &cip->attr_name.attr; + cip->attr_gen[acnt++] = &cip->attr_val.attr; + cip->attr_val.show = fp->show_val_norm; + cip->attr_val.store = fp->store_val_norm; + if (pvr2_ctrl_has_custom_symbols(cptr)) { + cip->attr_gen[acnt++] = &cip->attr_custom.attr; + cip->attr_custom.show = fp->show_val_custom; + cip->attr_custom.store = fp->store_val_custom; + } + switch (pvr2_ctrl_get_type(cptr)) { + case pvr2_ctl_enum: // Control is an enumeration - cip->attr_gen[2] = &cip->attr_enum.attr; - cip->attr_val.show = fp->show_val_enum; - cip->attr_val.store = fp->store_val_enum; - } else { + cip->attr_gen[acnt++] = &cip->attr_enum.attr; + break; + case pvr2_ctl_int: // Control is an integer - cip->attr_val.show = fp->show_val_int; - cip->attr_val.store = fp->store_val_int; - cip->attr_gen[2] = &cip->attr_min.attr; - cip->attr_gen[3] = &cip->attr_max.attr; + cip->attr_gen[acnt++] = &cip->attr_min.attr; + cip->attr_gen[acnt++] = &cip->attr_max.attr; + break; + case pvr2_ctl_bitmask: + // Control is an bitmask + cip->attr_gen[acnt++] = &cip->attr_bits.attr; + break; + default: break; } - cip->grp.name = item_names[ctl_id]; + cnt = scnprintf(cip->name,sizeof(cip->name)-1,"ctl_%s", + pvr2_ctrl_get_name(cptr)); + cip->name[cnt] = 0; + cip->grp.name = cip->name; cip->grp.attrs = cip->attr_gen; sysfs_create_group(&sfp->class_dev->kobj,&cip->grp); } +#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC static ssize_t debuginfo_show(struct class_device *,char *); static ssize_t debugcmd_show(struct class_device *,char *); static ssize_t debugcmd_store(struct class_device *,const char *,size_t count); @@ -569,16 +538,15 @@ static void pvr2_sysfs_tear_down_debugifc(struct pvr2_sysfs *sfp) kfree(sfp->debugifc); sfp->debugifc = 0; } +#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ static void pvr2_sysfs_add_controls(struct pvr2_sysfs *sfp) { - unsigned int ctl_id; - - for (ctl_id = 0; - ctl_id < (sizeof(item_names)/sizeof(item_names[0])); ctl_id++) { - if (!item_names[ctl_id]) continue; - pvr2_sysfs_add_control(sfp,ctl_id); + unsigned int idx,cnt; + cnt = pvr2_hdw_get_ctrl_count(sfp->channel.hdw); + for (idx = 0; idx < cnt; idx++) { + pvr2_sysfs_add_control(sfp,idx); } } @@ -614,7 +582,9 @@ static void pvr2_sysfs_release(struct class_device *class_dev) static void class_dev_destroy(struct pvr2_sysfs *sfp) { if (!sfp->class_dev) return; +#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC pvr2_sysfs_tear_down_debugifc(sfp); +#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ pvr2_sysfs_tear_down_controls(sfp); class_device_remove_file(sfp->class_dev,&sfp->attr_v4l_minor_number); class_device_remove_file(sfp->class_dev,&sfp->attr_unit_number); @@ -690,7 +660,9 @@ static void class_dev_create(struct pvr2_sysfs *sfp, class_device_create_file(sfp->class_dev,&sfp->attr_unit_number); pvr2_sysfs_add_controls(sfp); +#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC pvr2_sysfs_add_debugifc(sfp); +#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ } @@ -761,6 +733,7 @@ void pvr2_sysfs_class_destroy(struct pvr2_sysfs_class *clp) } +#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC static ssize_t debuginfo_show(struct class_device *class_dev,char *buf) { struct pvr2_sysfs *sfp; @@ -793,6 +766,7 @@ static ssize_t debugcmd_store(struct class_device *class_dev, if (ret < 0) return ret; return count; } +#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ /* diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-tuner.c b/linux/drivers/media/video/pvrusb2/pvrusb2-tuner.c index f829c0acc..8bf7cc142 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-tuner.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-tuner.c @@ -26,7 +26,7 @@ #include "pvrusb2-hdw-internal.h" #include "pvrusb2-debug.h" #include "compat.h" -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <media/tuner.h> #include <media/v4l2-common.h> diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index dd207fae4..f4284f927 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -30,17 +30,9 @@ #include "pvrusb2-debug.h" #include "pvrusb2-v4l2.h" #include "pvrusb2-ioread.h" -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <media/v4l2-common.h> - -#define PVR_WIDTH_DVD 720 -#define PVR_WIDTH_SVCD 480 -#define PVR_WIDTH_VCD 352 - -#define PVR_HEIGHT_PAL 480 -#define PVR_HEIGHT_NTSC 576 - struct pvr2_v4l2_dev; struct pvr2_v4l2_fh; struct pvr2_v4l2; @@ -80,22 +72,6 @@ static int video_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1}; module_param_array(video_nr, int, NULL, 0444); MODULE_PARM_DESC(video_nr, "Offset for device's minor"); -#define V4L2_CID_PVR_SRATE (V4L2_CID_PRIVATE_BASE) -#define V4L2_CID_PVR_AUDIOBITRATE (V4L2_CID_PRIVATE_BASE+1) -#define V4L2_CID_PVR_AUDIOCRC (V4L2_CID_PRIVATE_BASE+2) -#define V4L2_CID_PVR_AUDIOEMPHASIS (V4L2_CID_PRIVATE_BASE+3) -#define V4L2_CID_PVR_VBR (V4L2_CID_PRIVATE_BASE+4) -#define V4L2_CID_PVR_VIDEOBITRATE (V4L2_CID_PRIVATE_BASE+5) -#define V4L2_CID_PVR_VIDEOPEAK (V4L2_CID_PRIVATE_BASE+6) -#define V4L2_CID_PVR_VIDEOSTANDARD (V4L2_CID_PRIVATE_BASE+7) -#define V4L2_CID_PVR_INPUT (V4L2_CID_PRIVATE_BASE+8) -#define V4L2_CID_PVR_AUDIOMODE (V4L2_CID_PRIVATE_BASE+9) -#define V4L2_CID_PVR_FREQUENCY (V4L2_CID_PRIVATE_BASE+10) -#define V4L2_CID_PVR_HRES (V4L2_CID_PRIVATE_BASE+11) -#define V4L2_CID_PVR_VRES (V4L2_CID_PRIVATE_BASE+12) - -#define V4L2_CID_PVR_MAX (V4L2_CID_PRIVATE_BASE+12) - struct v4l2_capability pvr_capability ={ .driver = "pvrusb2", .card = "Hauppauge WinTV pvr-usb2", @@ -141,69 +117,6 @@ static struct v4l2_tuner pvr_v4l2_tuners[]= { #endif }; -struct v4l2_standard pvr_standards[] = { - [PVR2_CVAL_VIDEOSTANDARD_PAL_BG] = { - .id = V4L2_STD_PAL_BG, - .frameperiod = - { - .numerator = 1, - .denominator= 25 - }, - .framelines = 625, - .reserved = {0,0,0,0} - }, - [PVR2_CVAL_VIDEOSTANDARD_PAL_I] = { - .id = V4L2_STD_PAL_I, - .frameperiod = - { - .numerator = 1, - .denominator= 25 - }, - .framelines = 625, - .reserved = {0,0,0,0} - }, - [PVR2_CVAL_VIDEOSTANDARD_PAL_DK] = { - .id = V4L2_STD_PAL_DK, - .frameperiod = - { - .numerator = 1, - .denominator= 25 - }, - .framelines = 625, - .reserved = {0,0,0,0} - }, - [PVR2_CVAL_VIDEOSTANDARD_SECAM_L] = { - .id = V4L2_STD_SECAM, - .frameperiod = - { - .numerator = 1, - .denominator= 25 - }, - .framelines = 625, - .reserved = {0,0,0,0} - }, - [PVR2_CVAL_VIDEOSTANDARD_NTSC_M] = { - .id = V4L2_STD_NTSC_M, - .frameperiod = - { - .numerator = 1001, - .denominator= 30000 - }, - .framelines = 525, - .reserved = {0,0,0,0} - }, - [PVR2_CVAL_VIDEOSTANDARD_PAL_M] = { - .id = V4L2_STD_PAL_M, - .frameperiod = - { - .numerator = 1001, - .denominator= 30000 - }, - .framelines = 525, - .reserved = {0,0,0,0} - } -}; - struct v4l2_fmtdesc pvr_fmtdesc [] = { { .index = 0, @@ -257,84 +170,6 @@ struct v4l2_format pvr_format [] = { } }; -static int cnv_cid_v4l2_pvr2(int id) -{ - switch (id) { - case V4L2_CID_BRIGHTNESS: - return PVR2_CID_BRIGHTNESS; - case V4L2_CID_SATURATION: - return PVR2_CID_SATURATION; - case V4L2_CID_CONTRAST: - return PVR2_CID_CONTRAST; - case V4L2_CID_HUE: - return PVR2_CID_HUE; - case V4L2_CID_AUDIO_VOLUME: - return PVR2_CID_VOLUME; - case V4L2_CID_AUDIO_BALANCE: - return PVR2_CID_BALANCE; - case V4L2_CID_AUDIO_BASS: - return PVR2_CID_BASS; - case V4L2_CID_AUDIO_TREBLE: - return PVR2_CID_TREBLE; - case V4L2_CID_AUDIO_MUTE: - return PVR2_CID_MUTE; - case V4L2_CID_PVR_SRATE: - return PVR2_CID_SRATE; - case V4L2_CID_PVR_AUDIOBITRATE: - return PVR2_CID_AUDIOBITRATE; - case V4L2_CID_PVR_AUDIOCRC: - return PVR2_CID_AUDIOCRC; - case V4L2_CID_PVR_AUDIOEMPHASIS: - return PVR2_CID_AUDIOEMPHASIS; - case V4L2_CID_PVR_VBR: - return PVR2_CID_VBR; - case V4L2_CID_PVR_VIDEOBITRATE: - return PVR2_CID_AVERAGEVIDEOBITRATE; - case V4L2_CID_PVR_VIDEOPEAK: - return PVR2_CID_PEAKVIDEOBITRATE; - case V4L2_CID_PVR_INPUT: - return PVR2_CID_INPUT; - case V4L2_CID_PVR_AUDIOMODE: - return PVR2_CID_AUDIOMODE; - case V4L2_CID_PVR_FREQUENCY: - return PVR2_CID_FREQUENCY; - case V4L2_CID_PVR_HRES: - return PVR2_CID_HRES; - case V4L2_CID_PVR_VRES: - return PVR2_CID_VRES; - } - return -1; -} - -#if 0 -static int cnv_cid_pvr2_v4l2(int id) -{ - switch (id) { - case PVR2_CID_BRIGHTNESS: - return V4L2_CID_BRIGHTNESS; - case PVR2_CID_SATURATION: - return V4L2_CID_SATURATION; - case PVR2_CID_CONTRAST: - return V4L2_CID_CONTRAST; - case PVR2_CID_HUE: - return V4L2_CID_HUE; - case PVR2_CID_VOLUME: - return V4L2_CID_AUDIO_VOLUME; - case PVR2_CID_BALANCE: - return V4L2_CID_AUDIO_BALANCE; - case PVR2_CID_BASS: - return V4L2_CID_AUDIO_BASS; - case PVR2_CID_TREBLE: - return V4L2_CID_AUDIO_TREBLE; - case PVR2_CID_MUTE: - return V4L2_CID_AUDIO_MUTE; - - return id + V4L2_CID_PRIVATE_BASE; - } - return -1; -} -#endif - /* * pvr_ioctl() * @@ -402,92 +237,41 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_ENUMSTD: { - struct v4l2_standard *vs = (struct v4l2_standard *)arg; int idx = vs->index; - - if ((vs->index < PVR2_CVAL_VIDEOSTANDARD_MIN) || - (vs->index > PVR2_CVAL_VIDEOSTANDARD_MAX)) { - break; - } - - memcpy(vs, &pvr_standards[idx], sizeof(struct v4l2_standard)); - vs->index = idx; - strlcpy(vs->name, - pvr2_hdw_get_ctl_value_name(hdw, - PVR2_CID_VIDEOSTANDARD, - idx), - sizeof(vs->name)); - - ret = 0; + ret = pvr2_hdw_get_stdenum_value(hdw,vs,idx+1); break; } case VIDIOC_G_STD: { - v4l2_std_id *vs = (v4l2_std_id *)arg; - - switch (pvr2_hdw_get_ctl_value(hdw,PVR2_CID_VIDEOSTANDARD)) { - default: - case PVR2_CVAL_VIDEOSTANDARD_NTSC_M: - *vs = V4L2_STD_NTSC_M; - break; - case PVR2_CVAL_VIDEOSTANDARD_PAL_M: - *vs = V4L2_STD_PAL_M; - break; - case PVR2_CVAL_VIDEOSTANDARD_SECAM_L: - *vs = V4L2_STD_SECAM; - break; - case PVR2_CVAL_VIDEOSTANDARD_PAL_BG: - *vs = V4L2_STD_PAL_BG; - break; - case PVR2_CVAL_VIDEOSTANDARD_PAL_I: - *vs = V4L2_STD_PAL_I; - break; - case PVR2_CVAL_VIDEOSTANDARD_PAL_DK: - *vs = V4L2_STD_PAL_DK; - break; - } - ret = 0; + int val = 0; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR),&val); + *(v4l2_std_id *)arg = val; break; } case VIDIOC_S_STD: { - v4l2_std_id *vs = (v4l2_std_id *)arg; - int val = PVR2_CVAL_VIDEOSTANDARD_NTSC_M; - - if (*vs & V4L2_STD_NTSC_M){ - val = PVR2_CVAL_VIDEOSTANDARD_NTSC_M; - } else if (*vs & V4L2_STD_PAL_BG){ - val = PVR2_CVAL_VIDEOSTANDARD_PAL_BG; - } else if (*vs & V4L2_STD_PAL_I){ - val = PVR2_CVAL_VIDEOSTANDARD_PAL_I; - } else if (*vs & V4L2_STD_PAL_DK){ - val = PVR2_CVAL_VIDEOSTANDARD_PAL_DK; - } else if (*vs & V4L2_STD_SECAM){ - val = PVR2_CVAL_VIDEOSTANDARD_SECAM_L; - } else if (*vs & V4L2_STD_PAL_M){ - val = PVR2_CVAL_VIDEOSTANDARD_PAL_M; - } - - pvr2_hdw_set_ctl_value(hdw,PVR2_CID_VIDEOSTANDARD,val); - + ret = pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR), + *(v4l2_std_id *)arg); break; } case VIDIOC_ENUMINPUT: { + struct pvr2_ctrl *cptr; struct v4l2_input *vi = (struct v4l2_input *)arg; struct v4l2_input tmp; + unsigned int cnt; - if ((vi->index > PVR2_CVAL_INPUT_MAX) || - (vi->index < PVR2_CVAL_INPUT_MIN)) { - break; - } + cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); memset(&tmp,0,sizeof(tmp)); tmp.index = vi->index; + ret = 0; switch (vi->index) { case PVR2_CVAL_INPUT_TV: case PVR2_CVAL_INPUT_RADIO: @@ -497,12 +281,16 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case PVR2_CVAL_INPUT_COMPOSITE: tmp.type = V4L2_INPUT_TYPE_CAMERA; break; + default: + ret = -EINVAL; + break; } + if (ret < 0) break; - strlcpy(tmp.name, - pvr2_hdw_get_ctl_value_name(hdw,PVR2_CID_INPUT, - vi->index), - sizeof(tmp.name)); + cnt = 0; + pvr2_ctrl_get_valname(cptr,vi->index, + tmp.name,sizeof(tmp.name)-1,&cnt); + tmp.name[cnt] = 0; /* Don't bother with audioset, since this driver currently always switches the audio whenever the video is @@ -522,19 +310,22 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_G_INPUT: { + struct pvr2_ctrl *cptr; struct v4l2_input *vi = (struct v4l2_input *)arg; - vi->index = pvr2_hdw_get_ctl_value(hdw,PVR2_CID_INPUT); - ret = 0; + int val; + cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); + val = 0; + ret = pvr2_ctrl_get_value(cptr,&val); + vi->index = val; break; } case VIDIOC_S_INPUT: { struct v4l2_input *vi = (struct v4l2_input *)arg; - ret = 0; - if (pvr2_hdw_set_ctl_value(hdw,PVR2_CID_INPUT,vi->index)) { - ret = -EINVAL; - } + ret = pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT), + vi->index); break; } @@ -559,6 +350,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, { struct v4l2_tuner *vt = (struct v4l2_tuner *)arg; unsigned int status_mask; + int val; if (vt->index !=0) break; status_mask = pvr2_hdw_get_signal_status(hdw); @@ -580,83 +372,44 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, vt->signal = 65535; } - switch (pvr2_hdw_get_ctl_value(hdw,PVR2_CID_AUDIOMODE)) { - case PVR2_CVAL_AUDIOMODE_MONO: - vt->audmode = V4L2_TUNER_MODE_MONO; - break; - case PVR2_CVAL_AUDIOMODE_STEREO: - vt->audmode = V4L2_TUNER_MODE_STEREO; - break; - case PVR2_CVAL_AUDIOMODE_LANG1: - vt->audmode = V4L2_TUNER_MODE_LANG1; - break; - case PVR2_CVAL_AUDIOMODE_LANG2: - vt->audmode = V4L2_TUNER_MODE_LANG2; - break; - case PVR2_CVAL_AUDIOMODE_LANG1_LANG2: - vt->audmode = V4L2_TUNER_MODE_LANG1_LANG2; - break; - case PVR2_CVAL_AUDIOMODE_SAP: - vt->audmode = V4L2_TUNER_MODE_SAP; - break; - } - - ret = 0; + val = 0; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_AUDIOMODE), + &val); + vt->audmode = val; break; } case VIDIOC_S_TUNER: { struct v4l2_tuner *vt=(struct v4l2_tuner *)arg; - int val = PVR2_CVAL_AUDIOMODE_STEREO; if (vt->index != 0) break; - switch (vt->audmode) { - case V4L2_TUNER_MODE_MONO: - val = PVR2_CVAL_AUDIOMODE_MONO; - break; - case V4L2_TUNER_MODE_STEREO: - val = PVR2_CVAL_AUDIOMODE_STEREO; - break; - case V4L2_TUNER_MODE_LANG1: - val = PVR2_CVAL_AUDIOMODE_LANG1; - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - val = PVR2_CVAL_AUDIOMODE_LANG1_LANG2; - break; - case V4L2_TUNER_MODE_SAP: // Also LANG2 - val = PVR2_CVAL_AUDIOMODE_SAP; - break; - } - - pvr2_hdw_set_ctl_value(hdw,PVR2_CID_AUDIOMODE,val); - ret = 0; + ret = pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_AUDIOMODE), + vt->audmode); } case VIDIOC_S_FREQUENCY: { const struct v4l2_frequency *vf = (struct v4l2_frequency *)arg; - - pvr2_hdw_set_ctl_value(hdw,PVR2_CID_FREQUENCY, - vf->frequency * 62500); - - ret = 0; + ret = pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY), + vf->frequency * 62500); break; } case VIDIOC_G_FREQUENCY: { struct v4l2_frequency *vf = (struct v4l2_frequency *)arg; - int val; - - val = pvr2_hdw_get_ctl_value(hdw,PVR2_CID_FREQUENCY); - + int val = 0; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY), + &val); val /= 62500; vf->frequency = val; - - ret = 0; break; } @@ -676,18 +429,27 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_G_FMT: { struct v4l2_format *vf = (struct v4l2_format *)arg; - + int val; switch(vf->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: memcpy(vf, &pvr_format[PVR_FORMAT_PIX], sizeof(struct v4l2_format)); - vf->fmt.pix.width = - pvr2_hdw_get_ctl_value(hdw,PVR2_CID_HRES); - if (pvr2_hdw_get_ctl_value(hdw,PVR2_CID_INTERLACE)) { - vf->fmt.pix.width /= 2; - } - vf->fmt.pix.height = - pvr2_hdw_get_ctl_value(hdw,PVR2_CID_VRES); + val = 0; + pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_HRES), + &val); + vf->fmt.pix.width = val; + val = 0; + pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, + PVR2_CID_INTERLACE), + &val); + if (val) vf->fmt.pix.width /= 2; + val = 0; + pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_VRES), + &val); + vf->fmt.pix.height = val; ret = 0; break; case V4L2_BUF_TYPE_VBI_CAPTURE: @@ -713,16 +475,14 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, int w = vf->fmt.pix.width; int vd_std, hf, hh; - vd_std = pvr2_hdw_get_ctl_value(hdw, - PVR2_CID_VIDEOSTANDARD); - switch (vd_std) { - case PVR2_CVAL_VIDEOSTANDARD_NTSC_M: - case PVR2_CVAL_VIDEOSTANDARD_PAL_M: + vd_std = 0; + pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR), + &vd_std); + if (vd_std & V4L2_STD_525_60) { hf=480; - break; - default: + } else { hf=576; - break; } hh = (int) (hf / 2); @@ -733,16 +493,19 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, vf->fmt.pix.width &= 0xff0; vf->fmt.pix.height = (h > hh) ? hf : hh; - if (cmd == VIDIOC_S_FMT){ - pvr2_hdw_set_ctl_value( - hdw,PVR2_CID_HRES, + if (cmd == VIDIOC_S_FMT) { + pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw, + PVR2_CID_HRES), vf->fmt.pix.width); - pvr2_hdw_set_ctl_value( - hdw,PVR2_CID_VRES, + pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw, + PVR2_CID_VRES), vf->fmt.pix.height); - pvr2_hdw_set_ctl_value( - hdw,PVR2_CID_INTERLACE, - (vf->fmt.pix.height != hf)); + pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id( + hdw,PVR2_CID_INTERLACE), + vf->fmt.pix.height != hf); } } break; case V4L2_BUF_TYPE_VBI_CAPTURE: @@ -772,82 +535,75 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_QUERYCTRL: { + struct pvr2_ctrl *cptr; struct v4l2_queryctrl *vc = (struct v4l2_queryctrl *)arg; - int pvr2_id = cnv_cid_v4l2_pvr2(vc->id); - if (pvr2_id < 0) { + ret = 0; + cptr = pvr2_hdw_get_ctrl_v4l(hdw,vc->id); + if (!cptr) { ret = -EINVAL; break; } - if (pvr2_hdw_get_ctl_value_name(hdw,pvr2_id,0)) { + strlcpy(vc->name,pvr2_ctrl_get_name(cptr),sizeof(vc->name)); + vc->default_value = pvr2_ctrl_get_def(cptr); + switch (pvr2_ctrl_get_type(cptr)) { + case pvr2_ctl_enum: vc->type = V4L2_CTRL_TYPE_MENU; - } else { + vc->minimum = 0; + vc->maximum = pvr2_ctrl_get_cnt(cptr) - 1; + vc->step = 1; + break; + case pvr2_ctl_int: vc->type = V4L2_CTRL_TYPE_INTEGER; + vc->minimum = pvr2_ctrl_get_min(cptr); + vc->maximum = pvr2_ctrl_get_max(cptr); + vc->step = 1; + break; + default: + ret = -EINVAL; + break; } - strlcpy(vc->name,pvr2_hdw_get_ctl_name(hdw,pvr2_id), - sizeof(vc->name)); - vc->minimum = pvr2_hdw_get_ctl_min_value(hdw,pvr2_id); - vc->maximum = pvr2_hdw_get_ctl_max_value(hdw,pvr2_id); - vc->step = 1; - ret = 0; break; } case VIDIOC_QUERYMENU: { struct v4l2_querymenu *vm = (struct v4l2_querymenu *)arg; - int pvr2_id = cnv_cid_v4l2_pvr2(vm->id); - const char *value_name; - if (pvr2_id < 0) { - ret = -EINVAL; - break; - } - - value_name = pvr2_hdw_get_ctl_value_name(hdw,pvr2_id, - vm->index); - if (value_name) { - strlcpy(vm->name,value_name,sizeof(vm->name)); - ret = 0; - } else { - ret = -EINVAL; - } - + unsigned int cnt = 0; + ret = pvr2_ctrl_get_valname(pvr2_hdw_get_ctrl_v4l(hdw,vm->id), + vm->index, + vm->name,sizeof(vm->name)-1, + &cnt); + vm->name[cnt] = 0; break; } case VIDIOC_G_CTRL: { struct v4l2_control *vc = (struct v4l2_control *)arg; - int pvr2_id; - - pvr2_id = cnv_cid_v4l2_pvr2(vc->id); - if (pvr2_id < 0) { - ret = -EINVAL; - break; - } - ret = 0; - vc->value = pvr2_hdw_get_ctl_value(hdw,pvr2_id); + int val = 0; + ret = pvr2_ctrl_get_value(pvr2_hdw_get_ctrl_v4l(hdw,vc->id), + &val); + vc->value = val; break; } case VIDIOC_S_CTRL: { struct v4l2_control *vc = (struct v4l2_control *)arg; - int pvr2_id; - - pvr2_id = cnv_cid_v4l2_pvr2(vc->id); - if (pvr2_id < 0) { - ret = -EINVAL; - break; - } - - ret = pvr2_hdw_set_ctl_value(hdw,pvr2_id,vc->value); + ret = pvr2_ctrl_set_value(pvr2_hdw_get_ctrl_v4l(hdw,vc->id), + vc->value); break; } case VIDIOC_LOG_STATUS: { + int nr = pvr2_hdw_get_unit_number(hdw); + + printk(KERN_INFO "pvrusb2: ================= START STATUS CARD #%d =================\n", nr); pvr2_hdw_trigger_module_log(hdw); + printk(KERN_INFO "pvrusb2: ================== END STATUS CARD #%d ==================\n", nr); + ret = 0; break; } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c b/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c index 97c3e2c38..4127c82f7 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c @@ -29,11 +29,12 @@ */ #include "pvrusb2-video-v4l.h" +#include "pvrusb2-i2c-cmd-v4l2.h" #include "pvrusb2-hdw-internal.h" #include "pvrusb2-debug.h" -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <media/v4l2-common.h> #include <media/saa7115.h> #include <linux/errno.h> @@ -53,9 +54,8 @@ static void set_input(struct pvr2_v4l_decoder *ctxt) struct pvr2_hdw *hdw = ctxt->hdw; struct v4l2_routing route; - pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_input(%d)", - hdw->controls[PVR2_CID_INPUT].value); - switch(hdw->controls[PVR2_CID_INPUT].value){ + pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_input(%d)",hdw->input_val); + switch(hdw->input_val) { case PVR2_CVAL_INPUT_TV: route.input = SAA7115_COMPOSITE4; break; @@ -78,7 +78,7 @@ static void set_input(struct pvr2_v4l_decoder *ctxt) static int check_input(struct pvr2_v4l_decoder *ctxt) { struct pvr2_hdw *hdw = ctxt->hdw; - return hdw->controls[PVR2_CID_INPUT].dirty != 0; + return hdw->input_dirty != 0; } @@ -88,8 +88,8 @@ static void set_audio(struct pvr2_v4l_decoder *ctxt) struct pvr2_hdw *hdw = ctxt->hdw; pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_audio %d", - hdw->controls[PVR2_CID_SRATE].value); - switch (hdw->controls[PVR2_CID_SRATE].value) { + hdw->srate_val); + switch (hdw->srate_val) { default: case PVR2_CVAL_SRATE_48: val = 48000; @@ -105,7 +105,7 @@ static void set_audio(struct pvr2_v4l_decoder *ctxt) static int check_audio(struct pvr2_v4l_decoder *ctxt) { struct pvr2_hdw *hdw = ctxt->hdw; - return hdw->controls[PVR2_CID_SRATE].dirty != 0; + return hdw->srate_dirty != 0; } @@ -176,8 +176,7 @@ static int decoder_detect(struct pvr2_i2c_client *cp) static void decoder_enable(struct pvr2_v4l_decoder *ctxt,int fl) { pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 decoder_enable(%d)",fl); - pvr2_i2c_client_cmd(ctxt->client, - (fl ? VIDIOC_STREAMON : VIDIOC_STREAMOFF),0); + pvr2_v4l2_cmd_stream(ctxt->client,fl); } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c b/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c new file mode 100644 index 000000000..fcad346e3 --- /dev/null +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c @@ -0,0 +1,170 @@ +/* + * + * $Id$ + * + * Copyright (C) 2005 Mike Isely <isely@pobox.com> + * Copyright (C) 2004 Aurelien Alleaume <slts@free.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 + * + * 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 + * + */ + +/* + + This source file is specifically designed to interface with the + wm8775. + +*/ + +#include "pvrusb2-wm8775.h" +#include "pvrusb2-i2c-cmd-v4l2.h" + + +#include "pvrusb2-hdw-internal.h" +#include "pvrusb2-debug.h" +#include <linux/videodev2.h> +#include <media/v4l2-common.h> +#include <linux/errno.h> +#include <linux/slab.h> + +struct pvr2_v4l_wm8775 { + struct pvr2_i2c_handler handler; + struct pvr2_i2c_client *client; + struct pvr2_hdw *hdw; + unsigned long stale_mask; +}; + + +static void set_input(struct pvr2_v4l_wm8775 *ctxt) +{ + struct v4l2_routing route; + struct pvr2_hdw *hdw = ctxt->hdw; + int msk = 0; + + memset(&route,0,sizeof(route)); + + pvr2_trace(PVR2_TRACE_CHIPS,"i2c wm8775 set_input(val=%d msk=0x%x)", + hdw->input_val,msk); + + // Always point to input #1 no matter what + route.input = 2; + pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUDIO_ROUTING,&route); +} + +static int check_input(struct pvr2_v4l_wm8775 *ctxt) +{ + struct pvr2_hdw *hdw = ctxt->hdw; + return hdw->input_dirty != 0; +} + + +struct pvr2_v4l_wm8775_ops { + void (*update)(struct pvr2_v4l_wm8775 *); + int (*check)(struct pvr2_v4l_wm8775 *); +}; + + +static const struct pvr2_v4l_wm8775_ops wm8775_ops[] = { + { .update = set_input, .check = check_input}, +}; + + +static unsigned int wm8775_describe(struct pvr2_v4l_wm8775 *ctxt, + char *buf,unsigned int cnt) +{ + return scnprintf(buf,cnt,"handler: pvrusb2-wm8775"); +} + + +static void wm8775_detach(struct pvr2_v4l_wm8775 *ctxt) +{ + ctxt->client->handler = 0; + kfree(ctxt); +} + + +static int wm8775_check(struct pvr2_v4l_wm8775 *ctxt) +{ + unsigned long msk; + unsigned int idx; + + for (idx = 0; idx < sizeof(wm8775_ops)/sizeof(wm8775_ops[0]); + idx++) { + msk = 1 << idx; + if (ctxt->stale_mask & msk) continue; + if (wm8775_ops[idx].check(ctxt)) { + ctxt->stale_mask |= msk; + } + } + return ctxt->stale_mask != 0; +} + + +static void wm8775_update(struct pvr2_v4l_wm8775 *ctxt) +{ + unsigned long msk; + unsigned int idx; + + for (idx = 0; idx < sizeof(wm8775_ops)/sizeof(wm8775_ops[0]); + idx++) { + msk = 1 << idx; + if (!(ctxt->stale_mask & msk)) continue; + ctxt->stale_mask &= ~msk; + wm8775_ops[idx].update(ctxt); + } +} + + +const static struct pvr2_i2c_handler_functions hfuncs = { + .detach = (void (*)(void *))wm8775_detach, + .check = (int (*)(void *))wm8775_check, + .update = (void (*)(void *))wm8775_update, + .describe = (unsigned int (*)(void *,char *,unsigned int))wm8775_describe, +}; + + +int pvr2_i2c_wm8775_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) +{ + struct pvr2_v4l_wm8775 *ctxt; + + if (cp->handler) return 0; + + ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL); + if (!ctxt) return 0; + memset(ctxt,0,sizeof(*ctxt)); + + ctxt->handler.func_data = ctxt; + ctxt->handler.func_table = &hfuncs; + ctxt->client = cp; + ctxt->hdw = hdw; + ctxt->stale_mask = (1 << (sizeof(wm8775_ops)/ + sizeof(wm8775_ops[0]))) - 1; + cp->handler = &ctxt->handler; + pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x wm8775 V4L2 handler set up", + cp->client->addr); + return !0; +} + + + + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 70 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.h b/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.h new file mode 100644 index 000000000..15ee1e215 --- /dev/null +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.h @@ -0,0 +1,54 @@ +/* + * + * $Id$ + * + * Copyright (C) 2005 Mike Isely <isely@pobox.com> + * Copyright (C) 2004 Aurelien Alleaume <slts@free.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 + * + * 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 + * + */ + +#ifndef __PVRUSB2_WM8775_H +#define __PVRUSB2_WM8775_H + +/* + + This module connects the pvrusb2 driver to the I2C chip level + driver which performs analog -> digital audio conversion for + external audio inputs. This interface is used internally by the + driver; higher level code should only interact through the + interface provided by pvrusb2-hdw.h. + +*/ + +#include "compat.h" + + +#include "pvrusb2-i2c-core.h" + +int pvr2_i2c_wm8775_setup(struct pvr2_hdw *,struct pvr2_i2c_client *); + + +#endif /* __PVRUSB2_WM8775_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 70 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ |