diff options
author | Paul Mackerras <paulus@samba.org> | 2008-01-31 11:25:51 +1100 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2008-01-31 11:25:51 +1100 |
commit | bd45ac0c5daae35e7c71138172e63df5cf644cf6 (patch) | |
tree | 5eb5a599bf6a9d7a8a34e802db932aa9e9555de4 /drivers/media/video/pvrusb2 | |
parent | 4eece4ccf997c0e6d8fdad3d842e37b16b8d705f (diff) | |
parent | 5bdeae46be6dfe9efa44a548bd622af325f4bdb4 (diff) |
Merge branch 'linux-2.6'
Diffstat (limited to 'drivers/media/video/pvrusb2')
22 files changed, 1615 insertions, 942 deletions
diff --git a/drivers/media/video/pvrusb2/Kconfig b/drivers/media/video/pvrusb2/Kconfig index d0c2cd78543..6fc1b8be1a1 100644 --- a/drivers/media/video/pvrusb2/Kconfig +++ b/drivers/media/video/pvrusb2/Kconfig @@ -5,6 +5,10 @@ config VIDEO_PVRUSB2 select VIDEO_TUNER select VIDEO_TVEEPROM select VIDEO_CX2341X + select VIDEO_SAA711X + select VIDEO_CX25840 + select VIDEO_MSP3400 + select VIDEO_WM8775 ---help--- This is a video4linux driver for Conexant 23416 based usb2 personal video recorder devices. @@ -12,32 +16,29 @@ config VIDEO_PVRUSB2 To compile this driver as a module, choose M here: the module will be called pvrusb2 -config VIDEO_PVRUSB2_29XXX - bool "Hauppauge WinTV-PVR USB2 support for 29xxx model series" +config VIDEO_PVRUSB2_ONAIR_CREATOR + bool "pvrusb2 driver support for OnAir Creator model" depends on VIDEO_PVRUSB2 && EXPERIMENTAL select VIDEO_SAA711X - select VIDEO_MSP3400 + select VIDEO_CS53L32A ---help--- - This option enables support for WinTV-PVR USB2 devices whose - model number is of the form "29xxx" (leading prefix of "29" - followed by 3 digits). - To see if you may need this option, examine the white - sticker on the underside of your device. + + This option enables support for the OnAir Creator USB tuner + device. This is a hybrid device, however currently only + analog mode is supported. If you are in doubt, say Y. -config VIDEO_PVRUSB2_24XXX - bool "Hauppauge WinTV-PVR USB2 support for 24xxx model series" +config VIDEO_PVRUSB2_ONAIR_USB2 + bool "pvrusb2 driver support for OnAir USB2 model" depends on VIDEO_PVRUSB2 && EXPERIMENTAL - select VIDEO_CX25840 - select VIDEO_WM8775 + select VIDEO_SAA711X + select VIDEO_CS53L32A ---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. + + This option enables support for the OnAir USB2 tuner device + (also known as the Sasem tuner). This is a hybrid device, + however currently only analog mode is supported. If you are in doubt, say Y. diff --git a/drivers/media/video/pvrusb2/Makefile b/drivers/media/video/pvrusb2/Makefile index 69b3e43cd0e..47284e55864 100644 --- a/drivers/media/video/pvrusb2/Makefile +++ b/drivers/media/video/pvrusb2/Makefile @@ -6,7 +6,7 @@ pvrusb2-objs := pvrusb2-i2c-core.o pvrusb2-i2c-cmd-v4l2.o \ pvrusb2-encoder.o pvrusb2-video-v4l.o \ pvrusb2-eeprom.o pvrusb2-tuner.o \ pvrusb2-main.o pvrusb2-hdw.o pvrusb2-v4l2.o \ - pvrusb2-ctrl.o pvrusb2-std.o \ + pvrusb2-ctrl.o pvrusb2-std.o pvrusb2-devattr.o \ pvrusb2-context.o pvrusb2-io.o pvrusb2-ioread.o \ pvrusb2-cx2584x-v4l.o pvrusb2-wm8775.o \ $(obj-pvrusb2-sysfs-y) $(obj-pvrusb2-debugifc-y) diff --git a/drivers/media/video/pvrusb2/pvrusb2-audio.c b/drivers/media/video/pvrusb2/pvrusb2-audio.c index 379645e481c..9a7c8e9c3e8 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-audio.c +++ b/drivers/media/video/pvrusb2/pvrusb2-audio.c @@ -35,34 +35,58 @@ struct pvr2_msp3400_handler { }; + +struct routing_scheme { + const int *def; + unsigned int cnt; +}; + +static const int routing_scheme0[] = { + [PVR2_CVAL_INPUT_TV] = MSP_INPUT_DEFAULT, + [PVR2_CVAL_INPUT_RADIO] = MSP_INPUT(MSP_IN_SCART2, + MSP_IN_TUNER1, + MSP_DSP_IN_SCART, + MSP_DSP_IN_SCART), + [PVR2_CVAL_INPUT_COMPOSITE] = MSP_INPUT(MSP_IN_SCART1, + MSP_IN_TUNER1, + MSP_DSP_IN_SCART, + MSP_DSP_IN_SCART), + [PVR2_CVAL_INPUT_SVIDEO] = MSP_INPUT(MSP_IN_SCART1, + MSP_IN_TUNER1, + MSP_DSP_IN_SCART, + MSP_DSP_IN_SCART), +}; + +static const struct routing_scheme routing_schemes[] = { + [PVR2_ROUTING_SCHEME_HAUPPAUGE] = { + .def = routing_scheme0, + .cnt = ARRAY_SIZE(routing_scheme0), + }, +}; + /* This function selects the correct audio input source */ static void set_stereo(struct pvr2_msp3400_handler *ctxt) { struct pvr2_hdw *hdw = ctxt->hdw; struct v4l2_routing route; + const struct routing_scheme *sp; + unsigned int sid = hdw->hdw_desc->signal_routing_scheme; pvr2_trace(PVR2_TRACE_CHIPS,"i2c msp3400 v4l2 set_stereo"); - route.input = MSP_INPUT_DEFAULT; - route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1); - switch (hdw->input_val) { - case PVR2_CVAL_INPUT_TV: - break; - case PVR2_CVAL_INPUT_RADIO: - /* Assume that msp34xx also handle FM decoding, in which case - 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_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_SCART1, MSP_IN_TUNER1, - MSP_DSP_IN_SCART, MSP_DSP_IN_SCART); - break; + if ((sid < ARRAY_SIZE(routing_schemes)) && + ((sp = routing_schemes + sid) != 0) && + (hdw->input_val >= 0) && + (hdw->input_val < sp->cnt)) { + route.input = sp->def[hdw->input_val]; + } else { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "*** WARNING *** i2c msp3400 v4l2 set_stereo:" + " Invalid routing scheme (%u) and/or input (%d)", + sid,hdw->input_val); + return; } + route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1); pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUDIO_ROUTING,&route); } diff --git a/drivers/media/video/pvrusb2/pvrusb2-context.c b/drivers/media/video/pvrusb2/pvrusb2-context.c index 22719ba861a..9d94aed2e12 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-context.c +++ b/drivers/media/video/pvrusb2/pvrusb2-context.c @@ -31,52 +31,32 @@ static void pvr2_context_destroy(struct pvr2_context *mp) { - if (mp->hdw) pvr2_hdw_destroy(mp->hdw); pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr_main id=%p",mp); - if (mp->workqueue) { - flush_workqueue(mp->workqueue); - destroy_workqueue(mp->workqueue); - } + if (mp->hdw) pvr2_hdw_destroy(mp->hdw); kfree(mp); } -static void pvr2_context_trigger_poll(struct pvr2_context *mp) -{ - queue_work(mp->workqueue,&mp->workpoll); -} - - -static void pvr2_context_poll(struct work_struct *work) -{ - struct pvr2_context *mp = - container_of(work, struct pvr2_context, workpoll); - pvr2_context_enter(mp); do { - pvr2_hdw_poll(mp->hdw); - } while (0); pvr2_context_exit(mp); -} - - -static void pvr2_context_setup(struct work_struct *work) +static void pvr2_context_state_check(struct pvr2_context *mp) { - struct pvr2_context *mp = - container_of(work, struct pvr2_context, workinit); + if (mp->init_flag) return; + + switch (pvr2_hdw_get_state(mp->hdw)) { + case PVR2_STATE_WARM: break; + case PVR2_STATE_ERROR: break; + case PVR2_STATE_READY: break; + case PVR2_STATE_RUN: break; + default: return; + } pvr2_context_enter(mp); do { - if (!pvr2_hdw_dev_ok(mp->hdw)) break; - pvr2_hdw_setup(mp->hdw); - pvr2_hdw_setup_poll_trigger( - mp->hdw, - (void (*)(void *))pvr2_context_trigger_poll, - mp); - if (!pvr2_hdw_dev_ok(mp->hdw)) break; - if (!pvr2_hdw_init_ok(mp->hdw)) break; + mp->init_flag = !0; mp->video_stream.stream = pvr2_hdw_get_video_stream(mp->hdw); if (mp->setup_func) { mp->setup_func(mp); } } while (0); pvr2_context_exit(mp); -} + } struct pvr2_context *pvr2_context_create( @@ -96,11 +76,10 @@ struct pvr2_context *pvr2_context_create( mp = NULL; goto done; } - - mp->workqueue = create_singlethread_workqueue("pvrusb2"); - INIT_WORK(&mp->workinit, pvr2_context_setup); - INIT_WORK(&mp->workpoll, pvr2_context_poll); - queue_work(mp->workqueue,&mp->workinit); + pvr2_hdw_set_state_callback(mp->hdw, + (void (*)(void *))pvr2_context_state_check, + mp); + pvr2_context_state_check(mp); done: return mp; } diff --git a/drivers/media/video/pvrusb2/pvrusb2-context.h b/drivers/media/video/pvrusb2/pvrusb2-context.h index 6327fa1f7e4..a04187a9322 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-context.h +++ b/drivers/media/video/pvrusb2/pvrusb2-context.h @@ -45,14 +45,11 @@ struct pvr2_context { struct pvr2_context_stream video_stream; struct mutex mutex; int disconnect_flag; + int init_flag; /* Called after pvr2_context initialization is complete */ void (*setup_func)(struct pvr2_context *); - /* Work queue overhead for out-of-line processing */ - struct workqueue_struct *workqueue; - struct work_struct workinit; - struct work_struct workpoll; }; struct pvr2_channel { diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c index e8a9252c7df..ffdc45c324e 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c +++ b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c @@ -49,34 +49,89 @@ struct pvr2_v4l_cx2584x { }; +struct routing_scheme_item { + int vid; + int aud; +}; + +struct routing_scheme { + const struct routing_scheme_item *def; + unsigned int cnt; +}; + +static const struct routing_scheme_item routing_scheme0[] = { + [PVR2_CVAL_INPUT_TV] = { + .vid = CX25840_COMPOSITE7, + .aud = CX25840_AUDIO8, + }, + [PVR2_CVAL_INPUT_RADIO] = { /* Treat the same as composite */ + .vid = CX25840_COMPOSITE3, + .aud = CX25840_AUDIO_SERIAL, + }, + [PVR2_CVAL_INPUT_COMPOSITE] = { + .vid = CX25840_COMPOSITE3, + .aud = CX25840_AUDIO_SERIAL, + }, + [PVR2_CVAL_INPUT_SVIDEO] = { + .vid = CX25840_SVIDEO1, + .aud = CX25840_AUDIO_SERIAL, + }, +}; + +/* Specific to gotview device */ +static const struct routing_scheme_item routing_schemegv[] = { + [PVR2_CVAL_INPUT_TV] = { + .vid = CX25840_COMPOSITE2, + .aud = CX25840_AUDIO5, + }, + [PVR2_CVAL_INPUT_RADIO] = { /* Treat the same as composite */ + .vid = CX25840_COMPOSITE1, + .aud = CX25840_AUDIO_SERIAL, + }, + [PVR2_CVAL_INPUT_COMPOSITE] = { + .vid = CX25840_COMPOSITE1, + .aud = CX25840_AUDIO_SERIAL, + }, + [PVR2_CVAL_INPUT_SVIDEO] = { + .vid = (CX25840_SVIDEO_LUMA3|CX25840_SVIDEO_CHROMA4), + .aud = CX25840_AUDIO_SERIAL, + }, +}; + +static const struct routing_scheme routing_schemes[] = { + [PVR2_ROUTING_SCHEME_HAUPPAUGE] = { + .def = routing_scheme0, + .cnt = ARRAY_SIZE(routing_scheme0), + }, + [PVR2_ROUTING_SCHEME_GOTVIEW] = { + .def = routing_schemegv, + .cnt = ARRAY_SIZE(routing_schemegv), + }, +}; + 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; + const struct routing_scheme *sp; + unsigned int sid = hdw->hdw_desc->signal_routing_scheme; 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_RADIO: // Treat same as composite - 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; - default: - // Just set it to be composite input for now... - vid_input = CX25840_COMPOSITE3; - aud_input = CX25840_AUDIO_SERIAL; - break; + if ((sid < ARRAY_SIZE(routing_schemes)) && + ((sp = routing_schemes + sid) != 0) && + (hdw->input_val >= 0) && + (hdw->input_val < sp->cnt)) { + vid_input = sp->def[hdw->input_val].vid; + aud_input = sp->def[hdw->input_val].aud; + } else { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "*** WARNING *** i2c cx2584x set_input:" + " Invalid routing scheme (%u) and/or input (%d)", + sid,hdw->input_val); + return; } pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x set_input vid=0x%x aud=0x%x", @@ -140,7 +195,7 @@ static const struct pvr2_v4l_cx2584x_ops decoder_ops[] = { static void decoder_detach(struct pvr2_v4l_cx2584x *ctxt) { ctxt->client->handler = NULL; - ctxt->hdw->decoder_ctrl = NULL; + pvr2_hdw_set_decoder(ctxt->hdw,NULL); kfree(ctxt); } @@ -241,7 +296,7 @@ int pvr2_i2c_cx2584x_v4l_setup(struct pvr2_hdw *hdw, ctxt->client = cp; ctxt->hdw = hdw; ctxt->stale_mask = (1 << ARRAY_SIZE(decoder_ops)) - 1; - hdw->decoder_ctrl = &ctxt->ctrl; + pvr2_hdw_set_decoder(hdw,&ctxt->ctrl); cp->handler = &ctxt->handler; { /* diff --git a/drivers/media/video/pvrusb2/pvrusb2-debug.h b/drivers/media/video/pvrusb2/pvrusb2-debug.h index da6441b88f3..fca49d8a931 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-debug.h +++ b/drivers/media/video/pvrusb2/pvrusb2-debug.h @@ -34,25 +34,26 @@ extern int pvrusb2_debug; #define PVR2_TRACE_INIT (1 << 5) /* misc initialization steps */ #define PVR2_TRACE_START_STOP (1 << 6) /* Streaming start / stop */ #define PVR2_TRACE_CTL (1 << 7) /* commit of control changes */ -#define PVR2_TRACE_DEBUG (1 << 8) /* Temporary debug code */ -#define PVR2_TRACE_EEPROM (1 << 9) /* eeprom parsing / report */ -#define PVR2_TRACE_STRUCT (1 << 10) /* internal struct creation */ -#define PVR2_TRACE_OPEN_CLOSE (1 << 11) /* application open / close */ -#define PVR2_TRACE_CREG (1 << 12) /* Main critical region entry / exit */ -#define PVR2_TRACE_SYSFS (1 << 13) /* Sysfs driven I/O */ -#define PVR2_TRACE_FIRMWARE (1 << 14) /* firmware upload actions */ -#define PVR2_TRACE_CHIPS (1 << 15) /* chip broadcast operation */ -#define PVR2_TRACE_I2C (1 << 16) /* I2C related stuff */ -#define PVR2_TRACE_I2C_CMD (1 << 17) /* Software commands to I2C modules */ -#define PVR2_TRACE_I2C_CORE (1 << 18) /* I2C core debugging */ -#define PVR2_TRACE_I2C_TRAF (1 << 19) /* I2C traffic through the adapter */ -#define PVR2_TRACE_V4LIOCTL (1 << 20) /* v4l ioctl details */ -#define PVR2_TRACE_ENCODER (1 << 21) /* mpeg2 encoder operation */ -#define PVR2_TRACE_BUF_POOL (1 << 22) /* Track buffer pool management */ -#define PVR2_TRACE_BUF_FLOW (1 << 23) /* Track buffer flow in system */ -#define PVR2_TRACE_DATA_FLOW (1 << 24) /* Track data flow */ -#define PVR2_TRACE_DEBUGIFC (1 << 25) /* Debug interface actions */ -#define PVR2_TRACE_GPIO (1 << 26) /* GPIO state bit changes */ +#define PVR2_TRACE_STATE (1 << 8) /* Device state changes */ +#define PVR2_TRACE_STBITS (1 << 9) /* Individual bit state changes */ +#define PVR2_TRACE_EEPROM (1 << 10) /* eeprom parsing / report */ +#define PVR2_TRACE_STRUCT (1 << 11) /* internal struct creation */ +#define PVR2_TRACE_OPEN_CLOSE (1 << 12) /* application open / close */ +#define PVR2_TRACE_CREG (1 << 13) /* Main critical region entry / exit */ +#define PVR2_TRACE_SYSFS (1 << 14) /* Sysfs driven I/O */ +#define PVR2_TRACE_FIRMWARE (1 << 15) /* firmware upload actions */ +#define PVR2_TRACE_CHIPS (1 << 16) /* chip broadcast operation */ +#define PVR2_TRACE_I2C (1 << 17) /* I2C related stuff */ +#define PVR2_TRACE_I2C_CMD (1 << 18) /* Software commands to I2C modules */ +#define PVR2_TRACE_I2C_CORE (1 << 19) /* I2C core debugging */ +#define PVR2_TRACE_I2C_TRAF (1 << 20) /* I2C traffic through the adapter */ +#define PVR2_TRACE_V4LIOCTL (1 << 21) /* v4l ioctl details */ +#define PVR2_TRACE_ENCODER (1 << 22) /* mpeg2 encoder operation */ +#define PVR2_TRACE_BUF_POOL (1 << 23) /* Track buffer pool management */ +#define PVR2_TRACE_BUF_FLOW (1 << 24) /* Track buffer flow in system */ +#define PVR2_TRACE_DATA_FLOW (1 << 25) /* Track data flow */ +#define PVR2_TRACE_DEBUGIFC (1 << 26) /* Debug interface actions */ +#define PVR2_TRACE_GPIO (1 << 27) /* GPIO state bit changes */ #endif /* __PVRUSB2_HDW_INTERNAL_H */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c index 6f135f4a249..b0687430fdd 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c +++ b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c @@ -31,14 +31,6 @@ struct debugifc_mask_item { unsigned long msk; }; -static struct debugifc_mask_item mask_items[] = { - {"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)}, -}; - static unsigned int debugifc_count_whitespace(const char *buf, unsigned int count) @@ -148,134 +140,14 @@ static int debugifc_match_keyword(const char *buf,unsigned int count, } -static unsigned long debugifc_find_mask(const char *buf,unsigned int count) -{ - struct debugifc_mask_item *mip; - unsigned int idx; - for (idx = 0; idx < ARRAY_SIZE(mask_items); idx++) { - mip = mask_items + idx; - if (debugifc_match_keyword(buf,count,mip->name)) { - return mip->msk; - } - } - return 0; -} - - -static int debugifc_print_mask(char *buf,unsigned int sz, - unsigned long msk,unsigned long val) -{ - struct debugifc_mask_item *mip; - unsigned int idx; - int bcnt = 0; - int ccnt; - for (idx = 0; idx < ARRAY_SIZE(mask_items); idx++) { - mip = mask_items + idx; - if (!(mip->msk & msk)) continue; - ccnt = scnprintf(buf,sz,"%s%c%s", - (bcnt ? " " : ""), - ((mip->msk & val) ? '+' : '-'), - mip->name); - sz -= ccnt; - buf += ccnt; - bcnt += ccnt; - } - return bcnt; -} - -static unsigned int debugifc_parse_subsys_mask(const char *buf, - unsigned int count, - unsigned long *mskPtr, - unsigned long *valPtr) -{ - const char *wptr; - unsigned int consume_cnt = 0; - unsigned int scnt; - unsigned int wlen; - int mode; - unsigned long m1,msk,val; - - msk = 0; - val = 0; - - while (count) { - scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); - if (!scnt) break; - consume_cnt += scnt; count -= scnt; buf += scnt; - if (!wptr) break; - - mode = 0; - if (wlen) switch (wptr[0]) { - case '+': - wptr++; - wlen--; - break; - case '-': - mode = 1; - wptr++; - wlen--; - break; - } - if (!wlen) continue; - m1 = debugifc_find_mask(wptr,wlen); - if (!m1) break; - msk |= m1; - if (!mode) val |= m1; - } - *mskPtr = msk; - *valPtr = val; - return consume_cnt; -} - - int pvr2_debugifc_print_info(struct pvr2_hdw *hdw,char *buf,unsigned int acnt) { int bcnt = 0; int ccnt; - struct pvr2_hdw_debug_info dbg; - - pvr2_hdw_get_debug_info(hdw,&dbg); - - ccnt = scnprintf(buf,acnt,"big lock %s; ctl lock %s", - (dbg.big_lock_held ? "held" : "free"), - (dbg.ctl_lock_held ? "held" : "free")); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - if (dbg.ctl_lock_held) { - ccnt = scnprintf(buf,acnt,"; cmd_state=%d cmd_code=%d" - " cmd_wlen=%d cmd_rlen=%d" - " wpend=%d rpend=%d tmout=%d rstatus=%d" - " wstatus=%d", - dbg.cmd_debug_state,dbg.cmd_code, - dbg.cmd_debug_write_len, - dbg.cmd_debug_read_len, - dbg.cmd_debug_write_pend, - dbg.cmd_debug_read_pend, - dbg.cmd_debug_timeout, - dbg.cmd_debug_rstatus, - dbg.cmd_debug_wstatus); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - } - ccnt = scnprintf(buf,acnt,"\n"); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = scnprintf( - buf,acnt,"driver flags: %s %s %s\n", - (dbg.flag_init_ok ? "initialized" : "uninitialized"), - (dbg.flag_ok ? "ok" : "fail"), - (dbg.flag_disconnected ? "disconnected" : "connected")); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = scnprintf(buf,acnt,"Subsystems enabled / configured: "); + ccnt = scnprintf(buf,acnt,"Driver state info:\n"); bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = debugifc_print_mask(buf,acnt,dbg.subsys_flags,dbg.subsys_flags); + ccnt = pvr2_hdw_state_report(hdw,buf,acnt); bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = scnprintf(buf,acnt,"\n"); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = scnprintf(buf,acnt,"Subsystems disabled / unconfigured: "); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = debugifc_print_mask(buf,acnt,~dbg.subsys_flags,dbg.subsys_flags); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = scnprintf(buf,acnt,"\n"); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = scnprintf(buf,acnt,"Attached I2C modules:\n"); bcnt += ccnt; acnt -= ccnt; buf += ccnt; ccnt = pvr2_i2c_report(hdw,buf,acnt); @@ -290,7 +162,6 @@ int pvr2_debugifc_print_status(struct pvr2_hdw *hdw, { int bcnt = 0; int ccnt; - unsigned long msk; int ret; u32 gpio_dir,gpio_in,gpio_out; @@ -311,28 +182,6 @@ int pvr2_debugifc_print_status(struct pvr2_hdw *hdw, pvr2_hdw_get_streaming(hdw) ? "on" : "off"); bcnt += ccnt; acnt -= ccnt; buf += ccnt; - msk = pvr2_hdw_subsys_get(hdw); - ccnt = scnprintf(buf,acnt,"Subsystems enabled / configured: "); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = debugifc_print_mask(buf,acnt,msk,msk); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = scnprintf(buf,acnt,"\n"); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = scnprintf(buf,acnt,"Subsystems disabled / unconfigured: "); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = debugifc_print_mask(buf,acnt,~msk,msk); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = scnprintf(buf,acnt,"\n"); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - - msk = pvr2_hdw_subsys_stream_get(hdw); - ccnt = scnprintf(buf,acnt,"Subsystems stopped on stream shutdown: "); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = debugifc_print_mask(buf,acnt,msk,msk); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = scnprintf(buf,acnt,"\n"); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - return bcnt; } @@ -369,28 +218,10 @@ static int pvr2_debugifc_do1cmd(struct pvr2_hdw *hdw,const char *buf, return pvr2_upload_firmware2(hdw); } else if (debugifc_match_keyword(wptr,wlen,"decoder")) { return pvr2_hdw_cmd_decoder_reset(hdw); + } else if (debugifc_match_keyword(wptr,wlen,"worker")) { + return pvr2_hdw_untrip(hdw); } return -EINVAL; - } else if (debugifc_match_keyword(wptr,wlen,"subsys_flags")) { - unsigned long msk = 0; - unsigned long val = 0; - if (debugifc_parse_subsys_mask(buf,count,&msk,&val) != count) { - pvr2_trace(PVR2_TRACE_DEBUGIFC, - "debugifc parse error on subsys mask"); - return -EINVAL; - } - pvr2_hdw_subsys_bit_chg(hdw,msk,val); - return 0; - } else if (debugifc_match_keyword(wptr,wlen,"stream_flags")) { - unsigned long msk = 0; - unsigned long val = 0; - if (debugifc_parse_subsys_mask(buf,count,&msk,&val) != count) { - pvr2_trace(PVR2_TRACE_DEBUGIFC, - "debugifc parse error on stream mask"); - return -EINVAL; - } - pvr2_hdw_subsys_stream_bit_chg(hdw,msk,val); - return 0; } else if (debugifc_match_keyword(wptr,wlen,"cpufw")) { scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); if (!scnt) return -EINVAL; diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/drivers/media/video/pvrusb2/pvrusb2-devattr.c new file mode 100644 index 00000000000..4df6d6d936f --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.c @@ -0,0 +1,217 @@ +/* + * + * $Id$ + * + * Copyright (C) 2007 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 + * + */ + +/* + +This source file should encompass ALL per-device type information for the +driver. To define a new device, add elements to the pvr2_device_table and +pvr2_device_desc structures. + +*/ + +#include "pvrusb2-devattr.h" +#include <linux/usb.h> +/* This is needed in order to pull in tuner type ids... */ +#include <linux/i2c.h> +#include <media/tuner.h> + + + +/*------------------------------------------------------------------------*/ +/* Hauppauge PVR-USB2 Model 29xxx */ + +static const char *pvr2_client_29xxx[] = { + "msp3400", + "saa7115", + "tuner", +}; + +static const char *pvr2_fw1_names_29xxx[] = { + "v4l-pvrusb2-29xxx-01.fw", +}; + +static const struct pvr2_device_desc pvr2_device_29xxx = { + .description = "WinTV PVR USB2 Model Category 29xxxx", + .shortname = "29xxx", + .client_modules.lst = pvr2_client_29xxx, + .client_modules.cnt = ARRAY_SIZE(pvr2_client_29xxx), + .fx2_firmware.lst = pvr2_fw1_names_29xxx, + .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_29xxx), + .flag_has_hauppauge_rom = !0, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, +}; + + + +/*------------------------------------------------------------------------*/ +/* Hauppauge PVR-USB2 Model 24xxx */ + +static const char *pvr2_client_24xxx[] = { + "cx25840", + "tuner", + "wm8775", +}; + +static const char *pvr2_fw1_names_24xxx[] = { + "v4l-pvrusb2-24xxx-01.fw", +}; + +static const struct pvr2_device_desc pvr2_device_24xxx = { + .description = "WinTV PVR USB2 Model Category 24xxxx", + .shortname = "24xxx", + .client_modules.lst = pvr2_client_24xxx, + .client_modules.cnt = ARRAY_SIZE(pvr2_client_24xxx), + .fx2_firmware.lst = pvr2_fw1_names_24xxx, + .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_24xxx), + .flag_has_cx25840 = !0, + .flag_has_wm8775 = !0, + .flag_has_hauppauge_rom = !0, + .flag_has_hauppauge_custom_ir = !0, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, +}; + + + +/*------------------------------------------------------------------------*/ +/* GOTVIEW USB2.0 DVD2 */ + +static const char *pvr2_client_gotview_2[] = { + "cx25840", + "tuner", +}; + +static const struct pvr2_device_desc pvr2_device_gotview_2 = { + .description = "Gotview USB 2.0 DVD 2", + .shortname = "gv2", + .client_modules.lst = pvr2_client_gotview_2, + .client_modules.cnt = ARRAY_SIZE(pvr2_client_gotview_2), + .flag_has_cx25840 = !0, + .default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_GOTVIEW, +}; + + + +#ifdef CONFIG_VIDEO_PVRUSB2_ONAIR_CREATOR +/*------------------------------------------------------------------------*/ +/* OnAir Creator */ + +static const char *pvr2_client_onair_creator[] = { + "saa7115", + "tuner", + "cs53l32a", +}; + +static const struct pvr2_device_desc pvr2_device_onair_creator = { + .description = "OnAir Creator Hybrid USB tuner", + .shortname = "oac", + .client_modules.lst = pvr2_client_onair_creator, + .client_modules.cnt = ARRAY_SIZE(pvr2_client_onair_creator), + .default_tuner_type = TUNER_LG_TDVS_H06XF, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, +}; +#endif + + + +#ifdef CONFIG_VIDEO_PVRUSB2_ONAIR_USB2 +/*------------------------------------------------------------------------*/ +/* OnAir USB 2.0 */ + +static const char *pvr2_client_onair_usb2[] = { + "saa7115", + "tuner", + "cs53l32a", +}; + +static const struct pvr2_device_desc pvr2_device_onair_usb2 = { + .description = "OnAir USB2 Hybrid USB tuner", + .shortname = "oa2", + .client_modules.lst = pvr2_client_onair_usb2, + .client_modules.cnt = ARRAY_SIZE(pvr2_client_onair_usb2), + .default_tuner_type = TUNER_PHILIPS_ATSC, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, +}; +#endif + + + +/*------------------------------------------------------------------------*/ +/* Hauppauge PVR-USB2 Model 75xxx */ + +static const char *pvr2_client_75xxx[] = { + "cx25840", + "tuner", +}; + +static const char *pvr2_fw1_names_75xxx[] = { + "v4l-pvrusb2-73xxx-01.fw", +}; + +static const struct pvr2_device_desc pvr2_device_75xxx = { + .description = "WinTV PVR USB2 Model Category 75xxxx", + .shortname = "75xxx", + .client_modules.lst = pvr2_client_75xxx, + .client_modules.cnt = ARRAY_SIZE(pvr2_client_75xxx), + .fx2_firmware.lst = pvr2_fw1_names_75xxx, + .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_75xxx), + .flag_has_cx25840 = !0, + .flag_has_hauppauge_rom = !0, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, + .default_std_mask = V4L2_STD_NTSC_M, +}; + + + +/*------------------------------------------------------------------------*/ + +struct usb_device_id pvr2_device_table[] = { + { USB_DEVICE(0x2040, 0x2900), + .driver_info = (kernel_ulong_t)&pvr2_device_29xxx}, + { USB_DEVICE(0x2040, 0x2400), + .driver_info = (kernel_ulong_t)&pvr2_device_24xxx}, + { USB_DEVICE(0x1164, 0x0622), + .driver_info = (kernel_ulong_t)&pvr2_device_gotview_2}, +#ifdef CONFIG_VIDEO_PVRUSB2_ONAIR_CREATOR + { USB_DEVICE(0x11ba, 0x1003), + .driver_info = (kernel_ulong_t)&pvr2_device_onair_creator}, +#endif +#ifdef CONFIG_VIDEO_PVRUSB2_ONAIR_USB2 + { USB_DEVICE(0x11ba, 0x1001), + .driver_info = (kernel_ulong_t)&pvr2_device_onair_usb2}, +#endif + { USB_DEVICE(0x2040, 0x7500), + .driver_info = (kernel_ulong_t)&pvr2_device_75xxx}, + { } +}; + +MODULE_DEVICE_TABLE(usb, pvr2_device_table); + + +/* + 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/drivers/media/video/pvrusb2/pvrusb2-devattr.h b/drivers/media/video/pvrusb2/pvrusb2-devattr.h new file mode 100644 index 00000000000..64b467f0637 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.h @@ -0,0 +1,119 @@ +/* + * + * $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_DEVATTR_H +#define __PVRUSB2_DEVATTR_H + +#include <linux/mod_devicetable.h> +#include <linux/videodev2.h> + +/* + + This header defines structures used to describe attributes of a device. + +*/ + + +struct pvr2_string_table { + const char **lst; + unsigned int cnt; +}; + +#define PVR2_ROUTING_SCHEME_HAUPPAUGE 0 +#define PVR2_ROUTING_SCHEME_GOTVIEW 1 + +/* This describes a particular hardware type (except for the USB device ID + which must live in a separate structure due to environmental + constraints). See the top of pvrusb2-hdw.c for where this is + instantiated. */ +struct pvr2_device_desc { + /* Single line text description of hardware */ + const char *description; + + /* Single token identifier for hardware */ + const char *shortname; + + /* List of additional client modules we need to load */ + struct pvr2_string_table client_modules; + + /* List of FX2 firmware file names we should search; if empty then + FX2 firmware check / load is skipped and we assume the device + was initialized from internal ROM. */ + struct pvr2_string_table fx2_firmware; + + /* Signal routing scheme used by device, contains one of + PVR2_ROUTING_SCHEME_XXX. Schemes have to be defined as we + encounter them. This is an arbitrary integer scheme id; its + meaning is contained entirely within the driver and is + interpreted by logic which must send commands to the chip-level + drivers (search for things which touch this field). */ + unsigned int signal_routing_scheme; + + /* V4L tuner type ID to use with this device (only used if the + driver could not discover the type any other way). */ + int default_tuner_type; + + /* Initial standard bits to use for this device, if not zero. + Anything set here is also implied as an available standard. + Note: This is ignored if overridden on the module load line via + the video_std module option. */ + v4l2_std_id default_std_mask; + + /* If set, we don't bother trying to load cx23416 firmware. */ + char flag_skip_cx23416_firmware; + + /* Device has a hauppauge eeprom which we can interrogate. */ + char flag_has_hauppauge_rom; + + /* Device does not require a powerup command to be issued. */ + char flag_no_powerup; + + /* Device has a cx25840 - this enables special additional logic to + handle it. */ + char flag_has_cx25840; + + /* Device has a wm8775 - this enables special additional logic to + ensure that it is found. */ + char flag_has_wm8775; + + /* Device has IR hardware that can be faked into looking like a + normal Hauppauge i2c IR receiver. This is currently very + specific to the 24xxx device, where Hauppauge had replaced their + 'standard' I2C IR receiver with a bunch of FPGA logic controlled + directly via the FX2. Turning this on tells the pvrusb2 driver + to virtualize the presence of the non-existant IR receiver chip and + implement the virtual receiver in terms of appropriate FX2 + commands. */ + char flag_has_hauppauge_custom_ir; +}; + +extern struct usb_device_id pvr2_device_table[]; + +#endif /* __PVRUSB2_HDW_INTERNAL_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/drivers/media/video/pvrusb2/pvrusb2-eeprom.c b/drivers/media/video/pvrusb2/pvrusb2-eeprom.c index 45cbca0143c..5ef005947b0 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-eeprom.c +++ b/drivers/media/video/pvrusb2/pvrusb2-eeprom.c @@ -144,6 +144,7 @@ int pvr2_eeprom_analyze(struct pvr2_hdw *hdw) trace_eeprom("serial_number=%d",tvdata.serial_number); trace_eeprom("rev_str=%s",tvdata.rev_str); hdw->tuner_type = tvdata.tuner_type; + hdw->tuner_updated = !0; hdw->serial_number = tvdata.serial_number; hdw->std_mask_eeprom = tvdata.tuner_formats; diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/drivers/media/video/pvrusb2/pvrusb2-encoder.c index 205087a3e13..64062879981 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-encoder.c +++ b/drivers/media/video/pvrusb2/pvrusb2-encoder.c @@ -140,7 +140,7 @@ static int pvr2_encoder_read_words(struct pvr2_hdw *hdw, cx2341x.ko to write to our encoder (by handing it a pointer to this function). For earlier kernels this doesn't really matter. */ static int pvr2_encoder_cmd(void *ctxt, - int cmd, + u32 cmd, int arg_cnt_send, int arg_cnt_recv, u32 *argp) @@ -209,7 +209,7 @@ static int pvr2_encoder_cmd(void *ctxt, LOCK_TAKE(hdw->ctl_lock); do { - if (!hdw->flag_encoder_ok) { + if (!hdw->state_encoder_ok) { ret = -EIO; break; } @@ -278,12 +278,15 @@ static int pvr2_encoder_cmd(void *ctxt, ret = -EBUSY; } if (ret) { - hdw->flag_encoder_ok = 0; + hdw->state_encoder_ok = 0; + pvr2_trace(PVR2_TRACE_STBITS, + "State bit %s <-- %s", + "state_encoder_ok", + (hdw->state_encoder_ok ? "true" : "false")); pvr2_trace( PVR2_TRACE_ERROR_LEGS, "Giving up on command." - " It is likely that" - " this is a bad idea..."); + " This is normally recovered by the driver."); break; } wrData[0] = 0x7; @@ -366,13 +369,13 @@ static int pvr2_encoder_prep_config(struct pvr2_hdw *hdw) /* This ENC_MISC(3,encMisc3Arg) command is critical - without it there will eventually be video corruption. Also, the - 29xxx case is strange - the Windows driver is passing 1 - regardless of device type but if we have 1 for 29xxx device - the video turns sluggish. */ - switch (hdw->hdw_type) { - case PVR2_HDW_TYPE_24XXX: encMisc3Arg = 1; break; - case PVR2_HDW_TYPE_29XXX: encMisc3Arg = 0; break; - default: break; + saa7115 case is strange - the Windows driver is passing 1 + regardless of device type but if we have 1 for saa7115 + devices the video turns sluggish. */ + if (hdw->hdw_desc->flag_has_cx25840) { + encMisc3Arg = 1; + } else { + encMisc3Arg = 0; } ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 3, encMisc3Arg,0,0); @@ -394,6 +397,24 @@ static int pvr2_encoder_prep_config(struct pvr2_hdw *hdw) return ret; } +int pvr2_encoder_adjust(struct pvr2_hdw *hdw) +{ + int ret; + ret = cx2341x_update(hdw,pvr2_encoder_cmd, + (hdw->enc_cur_valid ? &hdw->enc_cur_state : NULL), + &hdw->enc_ctl_state); + if (ret) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Error from cx2341x module code=%d",ret); + } else { + memcpy(&hdw->enc_cur_state,&hdw->enc_ctl_state, + sizeof(struct cx2341x_mpeg_params)); + hdw->enc_cur_valid = !0; + } + return ret; +} + + int pvr2_encoder_configure(struct pvr2_hdw *hdw) { int ret; @@ -412,7 +433,7 @@ int pvr2_encoder_configure(struct pvr2_hdw *hdw) /* saa7115: 0xf0 */ val = 0xf0; - if (hdw->hdw_type == PVR2_HDW_TYPE_24XXX) { + if (hdw->hdw_desc->flag_has_cx25840) { /* ivtv cx25840: 0x140 */ val = 0x140; } @@ -436,18 +457,10 @@ int pvr2_encoder_configure(struct pvr2_hdw *hdw) return ret; } - ret = cx2341x_update(hdw,pvr2_encoder_cmd, - (hdw->enc_cur_valid ? &hdw->enc_cur_state : NULL), - &hdw->enc_ctl_state); - if (ret) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Error from cx2341x module code=%d",ret); - return ret; - } - - ret = 0; + ret = pvr2_encoder_adjust(hdw); + if (ret) return ret; - if (!ret) ret = pvr2_encoder_vcmd( + ret = pvr2_encoder_vcmd( hdw, CX2341X_ENC_INITIALIZE_INPUT, 0); if (ret) { @@ -456,10 +469,6 @@ int pvr2_encoder_configure(struct pvr2_hdw *hdw) return ret; } - hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG); - memcpy(&hdw->enc_cur_state,&hdw->enc_ctl_state, - sizeof(struct cx2341x_mpeg_params)); - hdw->enc_cur_valid = !0; return 0; } @@ -478,7 +487,7 @@ int pvr2_encoder_start(struct pvr2_hdw *hdw) pvr2_encoder_vcmd(hdw,CX2341X_ENC_MUTE_VIDEO,1, hdw->input_val == PVR2_CVAL_INPUT_RADIO ? 1 : 0); - switch (hdw->config) { + switch (hdw->active_stream_type) { case pvr2_config_vbi: status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, 0x01,0x14); @@ -492,9 +501,6 @@ int pvr2_encoder_start(struct pvr2_hdw *hdw) 0,0x13); break; } - if (!status) { - hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_RUN); - } return status; } @@ -505,7 +511,7 @@ int pvr2_encoder_stop(struct pvr2_hdw *hdw) /* mask all interrupts */ pvr2_write_register(hdw, 0x0048, 0xffffffff); - switch (hdw->config) { + switch (hdw->active_stream_type) { case pvr2_config_vbi: status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, 0x01,0x01,0x14); @@ -526,9 +532,6 @@ int pvr2_encoder_stop(struct pvr2_hdw *hdw) pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000401); pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000000); - if (!status) { - hdw->subsys_enabled_mask &= ~(1<<PVR2_SUBSYS_B_ENC_RUN); - } return status; } diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.h b/drivers/media/video/pvrusb2/pvrusb2-encoder.h index 01b5a0b89c0..54caf2e3c42 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-encoder.h +++ b/drivers/media/video/pvrusb2/pvrusb2-encoder.h @@ -25,6 +25,7 @@ struct pvr2_hdw; +int pvr2_encoder_adjust(struct pvr2_hdw *); int pvr2_encoder_configure(struct pvr2_hdw *); int pvr2_encoder_start(struct pvr2_hdw *); int pvr2_encoder_stop(struct pvr2_hdw *); diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index f873994b088..d7a216b41b7 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -35,10 +35,12 @@ #include <linux/videodev2.h> #include <linux/i2c.h> +#include <linux/workqueue.h> #include <linux/mutex.h> #include "pvrusb2-hdw.h" #include "pvrusb2-io.h" #include <media/cx2341x.h> +#include "pvrusb2-devattr.h" /* Legal values for PVR2_CID_HSM */ #define PVR2_CVAL_HSM_FAIL 0 @@ -161,10 +163,6 @@ struct pvr2_decoder_ctrl { #define FW1_STATE_RELOAD 3 #define FW1_STATE_OK 4 -/* Known major hardware variants, keyed from device ID */ -#define PVR2_HDW_TYPE_29XXX 0 -#define PVR2_HDW_TYPE_24XXX 1 - typedef int (*pvr2_i2c_func)(struct pvr2_hdw *,u8,u8 *,u16,u8 *, u16); #define PVR2_I2C_FUNC_CNT 128 @@ -176,8 +174,15 @@ struct pvr2_hdw { struct usb_device *usb_dev; struct usb_interface *usb_intf; - /* Device type, one of PVR2_HDW_TYPE_xxxxx */ - unsigned int hdw_type; + /* Device description, anything that must adjust behavior based on + device specific info will use information held here. */ + const struct pvr2_device_desc *hdw_desc; + + /* Kernel worker thread handling */ + struct workqueue_struct *workqueue; + struct work_struct workpoll; /* Update driver state */ + struct work_struct worki2csync; /* Update i2c clients */ + struct work_struct workinit; /* Driver initialization sequence */ /* Video spigot */ struct pvr2_stream *vid_stream; @@ -186,9 +191,6 @@ struct pvr2_hdw { struct mutex big_lock_mutex; int big_lock_held; /* For debugging */ - void (*poll_trigger_func)(void *); - void *poll_trigger_data; - char name[32]; /* I2C stuff */ @@ -215,9 +217,9 @@ struct pvr2_hdw { struct urb *ctl_read_urb; unsigned char *ctl_write_buffer; unsigned char *ctl_read_buffer; - volatile int ctl_write_pend_flag; - volatile int ctl_read_pend_flag; - volatile int ctl_timeout_flag; + int ctl_write_pend_flag; + int ctl_read_pend_flag; + int ctl_timeout_flag; struct completion ctl_done; unsigned char cmd_buffer[PVR2_CTL_BUFFSIZE]; int cmd_debug_state; // Low level command debugging info @@ -225,14 +227,48 @@ struct pvr2_hdw { unsigned int cmd_debug_write_len; // unsigned int cmd_debug_read_len; // + /* Bits of state that describe what is going on with various parts + of the driver. */ + int state_encoder_ok; /* Encoder is operational */ + int state_encoder_run; /* Encoder is running */ + int state_encoder_config; /* Encoder is configured */ + int state_encoder_waitok; /* Encoder pre-wait done */ + int state_decoder_run; /* Decoder is running */ + int state_usbstream_run; /* FX2 is streaming */ + int state_decoder_quiescent; /* Decoder idle for > 50msec */ + int state_pipeline_config; /* Pipeline is configured */ + int state_pipeline_req; /* Somebody wants to stream */ + int state_pipeline_pause; /* Pipeline must be paused */ + int state_pipeline_idle; /* Pipeline not running */ + + /* This is the master state of the driver. It is the combined + result of other bits of state. Examining this will indicate the + overall state of the driver. Values here are one of + PVR2_STATE_xxxx */ + unsigned int master_state; + + /* True if states must be re-evaluated */ + int state_stale; + + void (*state_func)(void *); + void *state_data; + + /* Timer for measuring decoder settling time */ + struct timer_list quiescent_timer; + + /* Timer for measuring encoder pre-wait time */ + struct timer_list encoder_wait_timer; + + /* Place to block while waiting for state changes */ + wait_queue_head_t state_wait_data; + + int flag_ok; /* device in known good state */ int flag_disconnected; /* flag_ok == 0 due to disconnect */ int flag_init_ok; /* true if structure is fully initialized */ - int flag_streaming_enabled; /* true if streaming should be on */ int fw1_state; /* current situation with fw1 */ - int flag_encoder_ok; /* True if encoder is healthy */ - - int flag_decoder_is_tuned; + int flag_decoder_missed;/* We've noticed missing decoder */ + int flag_tripped; /* Indicates overall failure to start */ struct pvr2_decoder_ctrl *decoder_ctrl; @@ -241,12 +277,6 @@ struct pvr2_hdw { unsigned int fw_size; int fw_cpu_flag; /* True if we are dealing with the CPU */ - // Which subsystem pieces have been enabled / configured - unsigned long subsys_enabled_mask; - - // Which subsystems are manipulated to enable streaming - unsigned long subsys_stream_mask; - // True if there is a request to trigger logging of state in each // module. int log_requested; @@ -296,13 +326,16 @@ struct pvr2_hdw { /* Location of eeprom or a negative number if none */ int eeprom_addr; - enum pvr2_config config; + enum pvr2_config active_stream_type; + enum pvr2_config desired_stream_type; /* Control state needed for cx2341x module */ struct cx2341x_mpeg_params enc_cur_state; struct cx2341x_mpeg_params enc_ctl_state; /* True if an encoder attribute has changed */ int enc_stale; + /* True if an unsafe encoder attribute has changed */ + int enc_unsafe_stale; /* True if enc_cur_state is valid */ int enc_cur_valid; @@ -332,6 +365,7 @@ struct pvr2_hdw { /* This function gets the current frequency */ unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *); +void pvr2_hdw_set_decoder(struct pvr2_hdw *,struct pvr2_decoder_ctrl *); #endif /* __PVRUSB2_HDW_INTERNAL_H */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 402c5948825..41ae980405e 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -41,47 +41,6 @@ #define TV_MIN_FREQ 55250000L #define TV_MAX_FREQ 850000000L -struct usb_device_id pvr2_device_table[] = { - [PVR2_HDW_TYPE_29XXX] = { USB_DEVICE(0x2040, 0x2900) }, - [PVR2_HDW_TYPE_24XXX] = { USB_DEVICE(0x2040, 0x2400) }, - { } -}; - -MODULE_DEVICE_TABLE(usb, pvr2_device_table); - -static const char *pvr2_device_names[] = { - [PVR2_HDW_TYPE_29XXX] = "WinTV PVR USB2 Model Category 29xxxx", - [PVR2_HDW_TYPE_24XXX] = "WinTV PVR USB2 Model Category 24xxxx", -}; - -struct pvr2_string_table { - const char **lst; - unsigned int cnt; -}; - -// Names of other client modules to request for 24xxx model hardware -static const char *pvr2_client_24xxx[] = { - "cx25840", - "tuner", - "wm8775", -}; - -// Names of other client modules to request for 29xxx model hardware -static const char *pvr2_client_29xxx[] = { - "msp3400", - "saa7115", - "tuner", -}; - -static struct pvr2_string_table pvr2_client_lists[] = { - [PVR2_HDW_TYPE_29XXX] = { - pvr2_client_29xxx, ARRAY_SIZE(pvr2_client_29xxx) - }, - [PVR2_HDW_TYPE_24XXX] = { - pvr2_client_24xxx, ARRAY_SIZE(pvr2_client_24xxx) - }, -}; - static struct pvr2_hdw *unit_pointers[PVR_NUM] = {[ 0 ... PVR_NUM-1 ] = NULL}; static DEFINE_MUTEX(pvr2_unit_mtx); @@ -246,32 +205,46 @@ static const char *control_values_hsm[] = { }; -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 const char *pvr2_state_names[] = { + [PVR2_STATE_NONE] = "none", + [PVR2_STATE_DEAD] = "dead", + [PVR2_STATE_COLD] = "cold", + [PVR2_STATE_WARM] = "warm", + [PVR2_STATE_ERROR] = "error", + [PVR2_STATE_READY] = "ready", + [PVR2_STATE_RUN] = "run", }; + +static void pvr2_hdw_state_sched(struct pvr2_hdw *); +static int pvr2_hdw_state_eval(struct pvr2_hdw *); static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *,unsigned long); +static void pvr2_hdw_worker_i2c(struct work_struct *work); +static void pvr2_hdw_worker_poll(struct work_struct *work); +static void pvr2_hdw_worker_init(struct work_struct *work); +static int pvr2_hdw_wait(struct pvr2_hdw *,int state); +static int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *); +static void pvr2_hdw_state_log_state(struct pvr2_hdw *); static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl); -static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw); +static int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw); static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw); static void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw); static void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw); -static void pvr2_hdw_render_useless_unlocked(struct pvr2_hdw *hdw); -static void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw, - unsigned long msk, - unsigned long val); -static void pvr2_hdw_subsys_stream_bit_chg_no_lock(struct pvr2_hdw *hdw, - unsigned long msk, - unsigned long val); +static void pvr2_hdw_quiescent_timeout(unsigned long); +static void pvr2_hdw_encoder_wait_timeout(unsigned long); static int pvr2_send_request_ex(struct pvr2_hdw *hdw, unsigned int timeout,int probe_fl, void *write_data,unsigned int write_len, void *read_data,unsigned int read_len); + +static void trace_stbit(const char *name,int val) +{ + pvr2_trace(PVR2_TRACE_STBITS, + "State bit %s <-- %s", + name,(val ? "true" : "false")); +} + static int ctrl_channelfreq_get(struct pvr2_ctrl *cptr,int *vp) { struct pvr2_hdw *hdw = cptr->hdw; @@ -380,8 +353,8 @@ static int ctrl_vres_max_get(struct pvr2_ctrl *cptr,int *vp) static int ctrl_vres_min_get(struct pvr2_ctrl *cptr,int *vp) { - /* Actual minimum depends on device type. */ - if (cptr->hdw->hdw_type == PVR2_HDW_TYPE_24XXX) { + /* Actual minimum depends on device digitizer type. */ + if (cptr->hdw->hdw_desc->flag_has_cx25840) { *vp = 75; } else { *vp = 17; @@ -480,6 +453,7 @@ static int ctrl_cx2341x_is_dirty(struct pvr2_ctrl *cptr) static void ctrl_cx2341x_clear_dirty(struct pvr2_ctrl *cptr) { cptr->hdw->enc_stale = 0; + cptr->hdw->enc_unsafe_stale = 0; } static int ctrl_cx2341x_get(struct pvr2_ctrl *cptr,int *vp) @@ -502,6 +476,7 @@ static int ctrl_cx2341x_get(struct pvr2_ctrl *cptr,int *vp) static int ctrl_cx2341x_set(struct pvr2_ctrl *cptr,int m,int v) { int ret; + struct pvr2_hdw *hdw = cptr->hdw; struct v4l2_ext_controls cs; struct v4l2_ext_control c1; memset(&cs,0,sizeof(cs)); @@ -510,10 +485,22 @@ static int ctrl_cx2341x_set(struct pvr2_ctrl *cptr,int m,int v) cs.count = 1; c1.id = cptr->info->v4l_id; c1.value = v; - ret = cx2341x_ext_ctrls(&cptr->hdw->enc_ctl_state, 0, &cs, + ret = cx2341x_ext_ctrls(&hdw->enc_ctl_state, + hdw->state_encoder_run, &cs, VIDIOC_S_EXT_CTRLS); + if (ret == -EBUSY) { + /* Oops. cx2341x is telling us it's not safe to change + this control while we're capturing. Make a note of this + fact so that the pipeline will be stopped the next time + controls are committed. Then go on ahead and store this + change anyway. */ + ret = cx2341x_ext_ctrls(&hdw->enc_ctl_state, + 0, &cs, + VIDIOC_S_EXT_CTRLS); + if (!ret) hdw->enc_unsafe_stale = !0; + } if (ret) return ret; - cptr->hdw->enc_stale = !0; + hdw->enc_stale = !0; return 0; } @@ -544,7 +531,13 @@ static unsigned int ctrl_cx2341x_getv4lflags(struct pvr2_ctrl *cptr) static int ctrl_streamingenabled_get(struct pvr2_ctrl *cptr,int *vp) { - *vp = cptr->hdw->flag_streaming_enabled; + *vp = cptr->hdw->state_pipeline_req; + return 0; +} + +static int ctrl_masterstate_get(struct pvr2_ctrl *cptr,int *vp) +{ + *vp = cptr->hdw->master_state; return 0; } @@ -657,29 +650,6 @@ static int ctrl_audio_modes_present_get(struct pvr2_ctrl *cptr,int *vp) 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) { @@ -915,6 +885,11 @@ static const struct pvr2_ctl_info control_defs[] = { .get_value = ctrl_hsm_get, DEFENUM(control_values_hsm), },{ + .desc = "Master State", + .name = "master_state", + .get_value = ctrl_masterstate_get, + DEFENUM(pvr2_state_names), + },{ .desc = "Signal Present", .name = "signal_present", .get_value = ctrl_signal_get, @@ -955,20 +930,6 @@ static const struct pvr2_ctl_info control_defs[] = { .sym_to_val = ctrl_std_sym_to_val, .type = pvr2_ctl_bitmask, },{ - .desc = "Subsystem enabled mask", - .name = "debug_subsys_mask", - .skip_init = !0, - .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, @@ -1129,25 +1090,13 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw) unsigned int pipe; int ret; u16 address; - static const char *fw_files_29xxx[] = { - "v4l-pvrusb2-29xxx-01.fw", - }; - static const char *fw_files_24xxx[] = { - "v4l-pvrusb2-24xxx-01.fw", - }; - static const struct pvr2_string_table fw_file_defs[] = { - [PVR2_HDW_TYPE_29XXX] = { - fw_files_29xxx, ARRAY_SIZE(fw_files_29xxx) - }, - [PVR2_HDW_TYPE_24XXX] = { - fw_files_24xxx, ARRAY_SIZE(fw_files_24xxx) - }, - }; - if ((hdw->hdw_type >= ARRAY_SIZE(fw_file_defs)) || - (!fw_file_defs[hdw->hdw_type].lst)) { + if (!hdw->hdw_desc->fx2_firmware.cnt) { hdw->fw1_state = FW1_STATE_OK; - return 0; + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Connected device type defines" + " no firmware to upload; ignoring firmware"); + return -ENOTTY; } hdw->fw1_state = FW1_STATE_FAILED; // default result @@ -1155,8 +1104,8 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw) trace_firmware("pvr2_upload_firmware1"); ret = pvr2_locate_firmware(hdw,&fw_entry,"fx2 controller", - fw_file_defs[hdw->hdw_type].cnt, - fw_file_defs[hdw->hdw_type].lst); + hdw->hdw_desc->fx2_firmware.cnt, + hdw->hdw_desc->fx2_firmware.lst); if (ret < 0) { if (ret == -ENOENT) hdw->fw1_state = FW1_STATE_MISSING; return ret; @@ -1231,8 +1180,7 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) CX2341X_FIRM_ENC_FILENAME, }; - if ((hdw->hdw_type != PVR2_HDW_TYPE_29XXX) && - (hdw->hdw_type != PVR2_HDW_TYPE_24XXX)) { + if (hdw->hdw_desc->flag_skip_cx23416_firmware) { return 0; } @@ -1248,8 +1196,6 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) time we configure the encoder, then we'll fully configure it. */ hdw->enc_cur_valid = 0; - hdw->flag_encoder_ok = 0; - /* First prepare firmware loading */ ret |= pvr2_write_register(hdw, 0x0048, 0xffffffff); /*interrupt mask*/ ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000088); /*gpio dir*/ @@ -1347,293 +1293,129 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) if (ret) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "firmware2 upload post-proc failure"); - } else { - hdw->flag_encoder_ok = !0; - hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_FIRMWARE); } return ret; } -#define FIRMWARE_RECOVERY_BITS \ - ((1<<PVR2_SUBSYS_B_ENC_CFG) | \ - (1<<PVR2_SUBSYS_B_ENC_RUN) | \ - (1<<PVR2_SUBSYS_B_ENC_FIRMWARE) | \ - (1<<PVR2_SUBSYS_B_USBSTREAM_RUN)) - -/* - - This single function is key to pretty much everything. The pvrusb2 - device can logically be viewed as a series of subsystems which can be - stopped / started or unconfigured / configured. To get things streaming, - one must configure everything and start everything, but there may be - various reasons over time to deconfigure something or stop something. - This function handles all of this activity. Everything EVERYWHERE that - must affect a subsystem eventually comes here to do the work. - - The current state of all subsystems is represented by a single bit mask, - known as subsys_enabled_mask. The bit positions are defined by the - PVR2_SUBSYS_xxxx macros, with one subsystem per bit position. At any - time the set of configured or active subsystems can be queried just by - looking at that mask. To change bits in that mask, this function here - must be called. The "msk" argument indicates which bit positions to - change, and the "val" argument defines the new values for the positions - defined by "msk". - - There is a priority ordering of starting / stopping things, and for - multiple requested changes, this function implements that ordering. - (Thus we will act on a request to load encoder firmware before we - configure the encoder.) In addition to priority ordering, there is a - recovery strategy implemented here. If a particular step fails and we - detect that failure, this function will clear the affected subsystem bits - and restart. Thus we have a means for recovering from a dead encoder: - Clear all bits that correspond to subsystems that we need to restart / - reconfigure and start over. - -*/ -static void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw, - unsigned long msk, - unsigned long val) -{ - unsigned long nmsk; - unsigned long vmsk; - int ret; - unsigned int tryCount = 0; - - if (!hdw->flag_ok) return; - - msk &= PVR2_SUBSYS_ALL; - nmsk = (hdw->subsys_enabled_mask & ~msk) | (val & msk); - nmsk &= PVR2_SUBSYS_ALL; - - for (;;) { - tryCount++; - if (!((nmsk ^ hdw->subsys_enabled_mask) & - PVR2_SUBSYS_ALL)) break; - if (tryCount > 4) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Too many retries when configuring device;" - " giving up"); - pvr2_hdw_render_useless(hdw); - break; - } - if (tryCount > 1) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Retrying device reconfiguration"); - } - pvr2_trace(PVR2_TRACE_INIT, - "subsys mask changing 0x%lx:0x%lx" - " from 0x%lx to 0x%lx", - msk,val,hdw->subsys_enabled_mask,nmsk); - - vmsk = (nmsk ^ hdw->subsys_enabled_mask) & - hdw->subsys_enabled_mask; - if (vmsk) { - if (vmsk & (1<<PVR2_SUBSYS_B_ENC_RUN)) { - pvr2_trace(PVR2_TRACE_CTL, - "/*---TRACE_CTL----*/" - " pvr2_encoder_stop"); - ret = pvr2_encoder_stop(hdw); - if (ret) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Error recovery initiated"); - hdw->subsys_enabled_mask &= - ~FIRMWARE_RECOVERY_BITS; - continue; - } - } - 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 & (1<<PVR2_SUBSYS_B_DIGITIZER_RUN)) { - pvr2_trace(PVR2_TRACE_CTL, - "/*---TRACE_CTL----*/" - " decoder disable"); - if (hdw->decoder_ctrl) { - hdw->decoder_ctrl->enable( - hdw->decoder_ctrl->ctxt,0); - } else { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "WARNING:" - " No decoder present"); - } - hdw->subsys_enabled_mask &= - ~(1<<PVR2_SUBSYS_B_DIGITIZER_RUN); - } - if (vmsk & PVR2_SUBSYS_CFG_ALL) { - hdw->subsys_enabled_mask &= - ~(vmsk & PVR2_SUBSYS_CFG_ALL); - } - } - vmsk = (nmsk ^ hdw->subsys_enabled_mask) & nmsk; - if (vmsk) { - if (vmsk & (1<<PVR2_SUBSYS_B_ENC_FIRMWARE)) { - pvr2_trace(PVR2_TRACE_CTL, - "/*---TRACE_CTL----*/" - " pvr2_upload_firmware2"); - ret = pvr2_upload_firmware2(hdw); - if (ret) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Failure uploading encoder" - " firmware"); - pvr2_hdw_render_useless(hdw); - break; - } - } - if (vmsk & (1<<PVR2_SUBSYS_B_ENC_CFG)) { - pvr2_trace(PVR2_TRACE_CTL, - "/*---TRACE_CTL----*/" - " pvr2_encoder_configure"); - ret = pvr2_encoder_configure(hdw); - if (ret) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Error recovery initiated"); - hdw->subsys_enabled_mask &= - ~FIRMWARE_RECOVERY_BITS; - continue; - } - } - if (vmsk & (1<<PVR2_SUBSYS_B_DIGITIZER_RUN)) { - pvr2_trace(PVR2_TRACE_CTL, - "/*---TRACE_CTL----*/" - " decoder enable"); - if (hdw->decoder_ctrl) { - hdw->decoder_ctrl->enable( - hdw->decoder_ctrl->ctxt,!0); - } else { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "WARNING:" - " No decoder present"); - } - hdw->subsys_enabled_mask |= - (1<<PVR2_SUBSYS_B_DIGITIZER_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 & (1<<PVR2_SUBSYS_B_ENC_RUN)) { - pvr2_trace(PVR2_TRACE_CTL, - "/*---TRACE_CTL----*/" - " pvr2_encoder_start"); - ret = pvr2_encoder_start(hdw); - if (ret) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Error recovery initiated"); - hdw->subsys_enabled_mask &= - ~FIRMWARE_RECOVERY_BITS; - continue; - } - } - } +static const char *pvr2_get_state_name(unsigned int st) +{ + if (st < ARRAY_SIZE(pvr2_state_names)) { + return pvr2_state_names[st]; } + return "???"; } - -void pvr2_hdw_subsys_bit_chg(struct pvr2_hdw *hdw, - unsigned long msk,unsigned long val) +static int pvr2_decoder_enable(struct pvr2_hdw *hdw,int enablefl) { - LOCK_TAKE(hdw->big_lock); do { - pvr2_hdw_subsys_bit_chg_no_lock(hdw,msk,val); - } while (0); LOCK_GIVE(hdw->big_lock); + if (!hdw->decoder_ctrl) { + if (!hdw->flag_decoder_missed) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "WARNING: No decoder present"); + hdw->flag_decoder_missed = !0; + trace_stbit("flag_decoder_missed", + hdw->flag_decoder_missed); + } + return -EIO; + } + hdw->decoder_ctrl->enable(hdw->decoder_ctrl->ctxt,enablefl); + return 0; } -unsigned long pvr2_hdw_subsys_get(struct pvr2_hdw *hdw) +void pvr2_hdw_set_decoder(struct pvr2_hdw *hdw,struct pvr2_decoder_ctrl *ptr) { - return hdw->subsys_enabled_mask; + if (hdw->decoder_ctrl == ptr) return; + hdw->decoder_ctrl = ptr; + if (hdw->decoder_ctrl && hdw->flag_decoder_missed) { + hdw->flag_decoder_missed = 0; + trace_stbit("flag_decoder_missed", + hdw->flag_decoder_missed); + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Decoder has appeared"); + pvr2_hdw_state_sched(hdw); + } } -unsigned long pvr2_hdw_subsys_stream_get(struct pvr2_hdw *hdw) +int pvr2_hdw_get_state(struct pvr2_hdw *hdw) { - return hdw->subsys_stream_mask; + return hdw->master_state; } -static void pvr2_hdw_subsys_stream_bit_chg_no_lock(struct pvr2_hdw *hdw, - unsigned long msk, - unsigned long val) +static int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *hdw) { - unsigned long val2; - msk &= PVR2_SUBSYS_ALL; - val2 = ((hdw->subsys_stream_mask & ~msk) | (val & msk)); - pvr2_trace(PVR2_TRACE_INIT, - "stream mask changing 0x%lx:0x%lx from 0x%lx to 0x%lx", - msk,val,hdw->subsys_stream_mask,val2); - hdw->subsys_stream_mask = val2; + if (!hdw->flag_tripped) return 0; + hdw->flag_tripped = 0; + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Clearing driver error statuss"); + return !0; } -void pvr2_hdw_subsys_stream_bit_chg(struct pvr2_hdw *hdw, - unsigned long msk, - unsigned long val) +int pvr2_hdw_untrip(struct pvr2_hdw *hdw) { + int fl; LOCK_TAKE(hdw->big_lock); do { - pvr2_hdw_subsys_stream_bit_chg_no_lock(hdw,msk,val); + fl = pvr2_hdw_untrip_unlocked(hdw); } while (0); LOCK_GIVE(hdw->big_lock); + if (fl) pvr2_hdw_state_sched(hdw); + return 0; } -static int pvr2_hdw_set_streaming_no_lock(struct pvr2_hdw *hdw,int enableFl) +const char *pvr2_hdw_get_state_name(unsigned int id) { - if ((!enableFl) == !(hdw->flag_streaming_enabled)) return 0; - if (enableFl) { - pvr2_trace(PVR2_TRACE_START_STOP, - "/*--TRACE_STREAM--*/ enable"); - pvr2_hdw_subsys_bit_chg_no_lock(hdw,~0,~0); - } else { - pvr2_trace(PVR2_TRACE_START_STOP, - "/*--TRACE_STREAM--*/ disable"); - pvr2_hdw_subsys_bit_chg_no_lock(hdw,hdw->subsys_stream_mask,0); - } - if (!hdw->flag_ok) return -EIO; - hdw->flag_streaming_enabled = enableFl != 0; - return 0; + if (id >= ARRAY_SIZE(pvr2_state_names)) return NULL; + return pvr2_state_names[id]; } int pvr2_hdw_get_streaming(struct pvr2_hdw *hdw) { - return hdw->flag_streaming_enabled != 0; + return hdw->state_pipeline_req != 0; } int pvr2_hdw_set_streaming(struct pvr2_hdw *hdw,int enable_flag) { - int ret; + int ret,st; LOCK_TAKE(hdw->big_lock); do { - ret = pvr2_hdw_set_streaming_no_lock(hdw,enable_flag); + pvr2_hdw_untrip_unlocked(hdw); + if ((!enable_flag) != !(hdw->state_pipeline_req)) { + hdw->state_pipeline_req = enable_flag != 0; + pvr2_trace(PVR2_TRACE_START_STOP, + "/*--TRACE_STREAM--*/ %s", + enable_flag ? "enable" : "disable"); + } + pvr2_hdw_state_sched(hdw); } while (0); LOCK_GIVE(hdw->big_lock); - return ret; -} - - -static int pvr2_hdw_set_stream_type_no_lock(struct pvr2_hdw *hdw, - enum pvr2_config config) -{ - unsigned long sm = hdw->subsys_enabled_mask; - if (!hdw->flag_ok) return -EIO; - pvr2_hdw_subsys_bit_chg_no_lock(hdw,hdw->subsys_stream_mask,0); - hdw->config = config; - pvr2_hdw_subsys_bit_chg_no_lock(hdw,~0,sm); + if ((ret = pvr2_hdw_wait(hdw,0)) < 0) return ret; + if (enable_flag) { + while ((st = hdw->master_state) != PVR2_STATE_RUN) { + if (st != PVR2_STATE_READY) return -EIO; + if ((ret = pvr2_hdw_wait(hdw,st)) < 0) return ret; + } + } return 0; } int pvr2_hdw_set_stream_type(struct pvr2_hdw *hdw,enum pvr2_config config) { - int ret; - if (!hdw->flag_ok) return -EIO; + int fl; LOCK_TAKE(hdw->big_lock); - ret = pvr2_hdw_set_stream_type_no_lock(hdw,config); + if ((fl = (hdw->desired_stream_type != config)) != 0) { + hdw->desired_stream_type = config; + hdw->state_pipeline_config = 0; + trace_stbit("state_pipeline_config", + hdw->state_pipeline_config); + pvr2_hdw_state_sched(hdw); + } LOCK_GIVE(hdw->big_lock); - return ret; + if (fl) return 0; + return pvr2_hdw_wait(hdw,0); } @@ -1646,6 +1428,7 @@ static int get_default_tuner_type(struct pvr2_hdw *hdw) } if (tp < 0) return -EINVAL; hdw->tuner_type = tp; + hdw->tuner_updated = !0; return 0; } @@ -1656,8 +1439,9 @@ static v4l2_std_id get_default_standard(struct pvr2_hdw *hdw) int tp = 0; if ((unit_number >= 0) && (unit_number < PVR_NUM)) { tp = video_std[unit_number]; + if (tp) return tp; } - return tp; + return 0; } @@ -1731,7 +1515,7 @@ const static struct pvr2_std_hack std_eeprom_maps[] = { }, { /* PAL(D/D1/K) */ .pat = V4L2_STD_DK, - .std = V4L2_STD_PAL_D/V4L2_STD_PAL_D1|V4L2_STD_PAL_K, + .std = V4L2_STD_PAL_D|V4L2_STD_PAL_D1|V4L2_STD_PAL_K, }, }; @@ -1739,18 +1523,20 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw) { char buf[40]; unsigned int bcnt; - v4l2_std_id std1,std2; + v4l2_std_id std1,std2,std3; std1 = get_default_standard(hdw); + std3 = std1 ? 0 : hdw->hdw_desc->default_std_mask; bcnt = pvr2_std_id_to_str(buf,sizeof(buf),hdw->std_mask_eeprom); pvr2_trace(PVR2_TRACE_STD, - "Supported video standard(s) reported by eeprom: %.*s", + "Supported video standard(s) reported available" + " in hardware: %.*s", bcnt,buf); hdw->std_mask_avail = hdw->std_mask_eeprom; - std2 = std1 & ~hdw->std_mask_avail; + std2 = (std1|std3) & ~hdw->std_mask_avail; if (std2) { bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std2); pvr2_trace(PVR2_TRACE_STD, @@ -1772,6 +1558,16 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw) pvr2_hdw_internal_find_stdenum(hdw); return; } + if (std3) { + bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std3); + pvr2_trace(PVR2_TRACE_STD, + "Initial video standard" + " (determined by device type): %.*s",bcnt,buf); + hdw->std_mask_cur = std3; + hdw->std_dirty = !0; + pvr2_hdw_internal_find_stdenum(hdw); + return; + } { unsigned int idx; @@ -1816,8 +1612,7 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) unsigned int idx; struct pvr2_ctrl *cptr; int reloadFl = 0; - if ((hdw->hdw_type == PVR2_HDW_TYPE_29XXX) || - (hdw->hdw_type == PVR2_HDW_TYPE_24XXX)) { + if (hdw->hdw_desc->fx2_firmware.cnt) { if (!reloadFl) { reloadFl = (hdw->usb_intf->cur_altsetting->desc.bNumEndpoints @@ -1853,25 +1648,13 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) } if (!pvr2_hdw_dev_ok(hdw)) return; - if (hdw->hdw_type < ARRAY_SIZE(pvr2_client_lists)) { - for (idx = 0; - idx < pvr2_client_lists[hdw->hdw_type].cnt; - idx++) { - request_module( - pvr2_client_lists[hdw->hdw_type].lst[idx]); - } + for (idx = 0; idx < hdw->hdw_desc->client_modules.cnt; idx++) { + request_module(hdw->hdw_desc->client_modules.lst[idx]); } - if ((hdw->hdw_type == PVR2_HDW_TYPE_29XXX) || - (hdw->hdw_type == PVR2_HDW_TYPE_24XXX)) { + if (!hdw->hdw_desc->flag_no_powerup) { pvr2_hdw_cmd_powerup(hdw); if (!pvr2_hdw_dev_ok(hdw)) return; - - if (pvr2_upload_firmware2(hdw)){ - pvr2_trace(PVR2_TRACE_ERROR_LEGS,"device unstable!!"); - pvr2_hdw_render_useless(hdw); - return; - } } // This step MUST happen after the earlier powerup step. @@ -1899,15 +1682,22 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) // 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; - if (ret < 0) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Unable to determine location of eeprom, skipping"); - } else { - hdw->eeprom_addr = ret; - pvr2_eeprom_analyze(hdw); + if (hdw->hdw_desc->flag_has_hauppauge_rom) { + ret = pvr2_hdw_get_eeprom_addr(hdw); if (!pvr2_hdw_dev_ok(hdw)) return; + if (ret < 0) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Unable to determine location of eeprom," + " skipping"); + } else { + hdw->eeprom_addr = ret; + pvr2_eeprom_analyze(hdw); + if (!pvr2_hdw_dev_ok(hdw)) return; + } + } else { + hdw->tuner_type = hdw->hdw_desc->default_tuner_type; + hdw->tuner_updated = !0; + hdw->std_mask_eeprom = V4L2_STD_ALL; } pvr2_hdw_setup_std(hdw); @@ -1918,14 +1708,12 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) hdw->tuner_type); } - hdw->tuner_updated = !0; pvr2_i2c_core_check_stale(hdw); hdw->tuner_updated = 0; if (!pvr2_hdw_dev_ok(hdw)) return; - pvr2_hdw_commit_ctl_internal(hdw); - if (!pvr2_hdw_dev_ok(hdw)) return; + pvr2_hdw_commit_setup(hdw); hdw->vid_stream = pvr2_stream_create(); if (!pvr2_hdw_dev_ok(hdw)) return; @@ -1945,25 +1733,25 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) if (!pvr2_hdw_dev_ok(hdw)) return; - /* Make sure everything is up to date */ - pvr2_i2c_core_sync(hdw); - - if (!pvr2_hdw_dev_ok(hdw)) return; - hdw->flag_init_ok = !0; + + pvr2_hdw_state_sched(hdw); } -int pvr2_hdw_setup(struct pvr2_hdw *hdw) +/* Set up the structure and attempt to put the device into a usable state. + This can be a time-consuming operation, which is why it is not done + internally as part of the create() step. */ +static void pvr2_hdw_setup(struct pvr2_hdw *hdw) { pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_setup(hdw=%p) begin",hdw); - LOCK_TAKE(hdw->big_lock); do { + do { pvr2_hdw_setup_low(hdw); pvr2_trace(PVR2_TRACE_INIT, "pvr2_hdw_setup(hdw=%p) done, ok=%d init_ok=%d", - hdw,hdw->flag_ok,hdw->flag_init_ok); + hdw,pvr2_hdw_dev_ok(hdw),hdw->flag_init_ok); if (pvr2_hdw_dev_ok(hdw)) { - if (pvr2_hdw_init_ok(hdw)) { + if (hdw->flag_init_ok) { pvr2_trace( PVR2_TRACE_INFO, "Device initialization" @@ -2013,9 +1801,8 @@ int pvr2_hdw_setup(struct pvr2_hdw *hdw) " the pvrusb2 device" " in order to recover."); } - } while (0); LOCK_GIVE(hdw->big_lock); + } while (0); pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_setup(hdw=%p) end",hdw); - return hdw->flag_init_ok; } @@ -2026,24 +1813,32 @@ 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; + const struct pvr2_device_desc *hdw_desc; __u8 ifnum; struct v4l2_queryctrl qctrl; struct pvr2_ctl_info *ciptr; - hdw_type = devid - pvr2_device_table; - if (hdw_type >= ARRAY_SIZE(pvr2_device_names)) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Bogus device type of %u reported",hdw_type); - return NULL; - } + hdw_desc = (const struct pvr2_device_desc *)(devid->driver_info); hdw = kzalloc(sizeof(*hdw),GFP_KERNEL); pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_create: hdw=%p, type \"%s\"", - hdw,pvr2_device_names[hdw_type]); + hdw,hdw_desc->description); if (!hdw) goto fail; + + init_timer(&hdw->quiescent_timer); + hdw->quiescent_timer.data = (unsigned long)hdw; + hdw->quiescent_timer.function = pvr2_hdw_quiescent_timeout; + + init_timer(&hdw->encoder_wait_timer); + hdw->encoder_wait_timer.data = (unsigned long)hdw; + hdw->encoder_wait_timer.function = pvr2_hdw_encoder_wait_timeout; + + hdw->master_state = PVR2_STATE_DEAD; + + init_waitqueue_head(&hdw->state_wait_data); + hdw->tuner_signal_stale = !0; cx2341x_fill_defaults(&hdw->enc_ctl_state); @@ -2052,7 +1847,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, hdw->controls = kzalloc(sizeof(struct pvr2_ctrl) * hdw->control_cnt, GFP_KERNEL); if (!hdw->controls) goto fail; - hdw->hdw_type = hdw_type; + hdw->hdw_desc = hdw_desc; for (idx = 0; idx < hdw->control_cnt; idx++) { cptr = hdw->controls + idx; cptr->hdw = hdw; @@ -2184,18 +1979,16 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, if (cnt1 >= sizeof(hdw->name)) cnt1 = sizeof(hdw->name)-1; hdw->name[cnt1] = 0; + hdw->workqueue = create_singlethread_workqueue(hdw->name); + INIT_WORK(&hdw->workpoll,pvr2_hdw_worker_poll); + INIT_WORK(&hdw->worki2csync,pvr2_hdw_worker_i2c); + INIT_WORK(&hdw->workinit,pvr2_hdw_worker_init); + pvr2_trace(PVR2_TRACE_INIT,"Driver unit number is %d, name is %s", hdw->unit_number,hdw->name); hdw->tuner_type = -1; hdw->flag_ok = !0; - /* 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 |= (1<<PVR2_SUBSYS_B_ENC_CFG); - - pvr2_trace(PVR2_TRACE_INIT,"subsys_stream_mask: 0x%lx", - hdw->subsys_stream_mask); hdw->usb_intf = intf; hdw->usb_dev = interface_to_usbdev(intf); @@ -2211,15 +2004,25 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, mutex_init(&hdw->ctl_lock_mutex); mutex_init(&hdw->big_lock_mutex); + queue_work(hdw->workqueue,&hdw->workinit); return hdw; fail: if (hdw) { + del_timer_sync(&hdw->quiescent_timer); + del_timer_sync(&hdw->encoder_wait_timer); + if (hdw->workqueue) { + flush_workqueue(hdw->workqueue); + destroy_workqueue(hdw->workqueue); + hdw->workqueue = NULL; + } usb_free_urb(hdw->ctl_read_urb); usb_free_urb(hdw->ctl_write_urb); kfree(hdw->ctl_read_buffer); kfree(hdw->ctl_write_buffer); kfree(hdw->controls); kfree(hdw->mpeg_ctrl_info); + kfree(hdw->std_defs); + kfree(hdw->std_enum_names); kfree(hdw); } return NULL; @@ -2250,10 +2053,10 @@ static void pvr2_hdw_remove_usb_stuff(struct pvr2_hdw *hdw) kfree(hdw->ctl_write_buffer); hdw->ctl_write_buffer = NULL; } - pvr2_hdw_render_useless_unlocked(hdw); hdw->flag_disconnected = !0; hdw->usb_dev = NULL; hdw->usb_intf = NULL; + pvr2_hdw_render_useless(hdw); } @@ -2262,6 +2065,13 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw) { if (!hdw) return; pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_destroy: hdw=%p",hdw); + del_timer_sync(&hdw->quiescent_timer); + del_timer_sync(&hdw->encoder_wait_timer); + if (hdw->workqueue) { + flush_workqueue(hdw->workqueue); + destroy_workqueue(hdw->workqueue); + hdw->workqueue = NULL; + } if (hdw->fw_buffer) { kfree(hdw->fw_buffer); hdw->fw_buffer = NULL; @@ -2290,12 +2100,6 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw) } -int pvr2_hdw_init_ok(struct pvr2_hdw *hdw) -{ - return hdw->flag_init_ok; -} - - int pvr2_hdw_dev_ok(struct pvr2_hdw *hdw) { return (hdw && hdw->flag_ok); @@ -2473,17 +2277,11 @@ static const char *get_ctrl_typename(enum pvr2_ctl_type tp) } -/* Commit all control changes made up to this point. Subsystems can be - indirectly affected by these changes. For a given set of things being - committed, we'll clear the affected subsystem bits and then once we're - done committing everything we'll make a request to restore the subsystem - state(s) back to their previous value before this function was called. - Thus we can automatically reconfigure affected pieces of the driver as - controls are changed. */ -static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) +/* Figure out if we need to commit control changes. If so, mark internal + state flags to indicate this fact and return true. Otherwise do nothing + else and return false. */ +static int pvr2_hdw_commit_setup(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; @@ -2518,6 +2316,25 @@ static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) return 0; } + hdw->state_pipeline_config = 0; + trace_stbit("state_pipeline_config",hdw->state_pipeline_config); + pvr2_hdw_state_sched(hdw); + + return !0; +} + + +/* Perform all operations needed to commit all control changes. This must + be performed in synchronization with the pipeline state and is thus + expected to be called as part of the driver's worker thread. Return + true if commit successful, otherwise return false to indicate that + commit isn't possible at this time. */ +static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw) +{ + unsigned int idx; + struct pvr2_ctrl *cptr; + int disruptive_change; + /* When video standard changes, reset the hres and vres values - but if the user has pending changes there, then let the changes take priority. */ @@ -2536,24 +2353,26 @@ static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) } } - if (hdw->std_dirty || - hdw->enc_stale || - hdw->srate_dirty || - hdw->res_ver_dirty || - hdw->res_hor_dirty || - 0) { - /* If any of this changes, then the encoder needs to be - reconfigured, and we need to reset the stream. */ - stale_subsys_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG); - } - - if (hdw->input_dirty) { - /* pk: If input changes to or from radio, then the encoder - needs to be restarted (for ENC_MUTE_VIDEO to work) */ - stale_subsys_mask |= (1<<PVR2_SUBSYS_B_ENC_RUN); + /* If any of the below has changed, then we can't do the update + while the pipeline is running. Pipeline must be paused first + and decoder -> encoder connection be made quiescent before we + can proceed. */ + disruptive_change = + (hdw->std_dirty || + hdw->enc_unsafe_stale || + hdw->srate_dirty || + hdw->res_ver_dirty || + hdw->res_hor_dirty || + hdw->input_dirty || + (hdw->active_stream_type != hdw->desired_stream_type)); + if (disruptive_change && !hdw->state_pipeline_idle) { + /* Pipeline is not idle; we can't proceed. Arrange to + cause pipeline to stop so that we can try this again + later.... */ + hdw->state_pipeline_pause = !0; + return 0; } - if (hdw->srate_dirty) { /* Write new sample rate into control structure since * the master copy is stale. We must track srate @@ -2582,51 +2401,88 @@ static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) cptr->info->clear_dirty(cptr); } + if (hdw->active_stream_type != hdw->desired_stream_type) { + /* Handle any side effects of stream config here */ + hdw->active_stream_type = hdw->desired_stream_type; + } + /* Now execute i2c core update */ pvr2_i2c_core_sync(hdw); - pvr2_hdw_subsys_bit_chg_no_lock(hdw,stale_subsys_mask,0); - pvr2_hdw_subsys_bit_chg_no_lock(hdw,~0,saved_subsys_mask); + if (hdw->state_encoder_run) { + /* If encoder isn't running, then this will get worked out + later when we start the encoder. */ + if (pvr2_encoder_adjust(hdw) < 0) return !0; + } - return 0; + hdw->state_pipeline_config = !0; + trace_stbit("state_pipeline_config",hdw->state_pipeline_config); + return !0; } int pvr2_hdw_commit_ctl(struct pvr2_hdw *hdw) { + int fl; + LOCK_TAKE(hdw->big_lock); + fl = pvr2_hdw_commit_setup(hdw); + LOCK_GIVE(hdw->big_lock); + if (!fl) return 0; + return pvr2_hdw_wait(hdw,0); +} + + +static void pvr2_hdw_worker_i2c(struct work_struct *work) +{ + struct pvr2_hdw *hdw = container_of(work,struct pvr2_hdw,worki2csync); LOCK_TAKE(hdw->big_lock); do { - pvr2_hdw_commit_ctl_internal(hdw); + pvr2_i2c_core_sync(hdw); } while (0); LOCK_GIVE(hdw->big_lock); - return 0; } -void pvr2_hdw_poll(struct pvr2_hdw *hdw) +static void pvr2_hdw_worker_poll(struct work_struct *work) { + int fl = 0; + struct pvr2_hdw *hdw = container_of(work,struct pvr2_hdw,workpoll); LOCK_TAKE(hdw->big_lock); do { - pvr2_i2c_core_sync(hdw); + fl = pvr2_hdw_state_eval(hdw); } while (0); LOCK_GIVE(hdw->big_lock); + if (fl && hdw->state_func) { + hdw->state_func(hdw->state_data); + } } -void pvr2_hdw_setup_poll_trigger(struct pvr2_hdw *hdw, - void (*func)(void *), - void *data) +static void pvr2_hdw_worker_init(struct work_struct *work) { + struct pvr2_hdw *hdw = container_of(work,struct pvr2_hdw,workinit); LOCK_TAKE(hdw->big_lock); do { - hdw->poll_trigger_func = func; - hdw->poll_trigger_data = data; + pvr2_hdw_setup(hdw); } while (0); LOCK_GIVE(hdw->big_lock); } -void pvr2_hdw_poll_trigger_unlocked(struct pvr2_hdw *hdw) +static int pvr2_hdw_wait(struct pvr2_hdw *hdw,int state) { - if (hdw->poll_trigger_func) { - hdw->poll_trigger_func(hdw->poll_trigger_data); - } + return wait_event_interruptible( + hdw->state_wait_data, + (hdw->state_stale == 0) && + (!state || (hdw->master_state != state))); } + +void pvr2_hdw_set_state_callback(struct pvr2_hdw *hdw, + void (*callback_func)(void *), + void *callback_data) +{ + LOCK_TAKE(hdw->big_lock); do { + hdw->state_data = callback_data; + hdw->state_func = callback_func; + } while (0); LOCK_GIVE(hdw->big_lock); +} + + /* Return name for this driver instance */ const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *hdw) { @@ -2634,6 +2490,18 @@ const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *hdw) } +const char *pvr2_hdw_get_desc(struct pvr2_hdw *hdw) +{ + return hdw->hdw_desc->description; +} + + +const char *pvr2_hdw_get_type(struct pvr2_hdw *hdw) +{ + return hdw->hdw_desc->shortname; +} + + int pvr2_hdw_is_hsm(struct pvr2_hdw *hdw) { int result; @@ -2689,6 +2557,7 @@ void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw) pvr2_i2c_core_sync(hdw); pvr2_trace(PVR2_TRACE_INFO,"cx2341x config:"); cx2341x_log_status(&hdw->enc_ctl_state, "pvrusb2"); + pvr2_hdw_state_log_state(hdw); printk(KERN_INFO "pvrusb2: ================== END STATUS CARD #%d ==================\n", nr); } while (0); LOCK_GIVE(hdw->big_lock); } @@ -2959,7 +2828,7 @@ static int pvr2_send_request_ex(struct pvr2_hdw *hdw, " without lock!!"); return -EDEADLK; } - if ((!hdw->flag_ok) && !probe_fl) { + if (!hdw->flag_ok && !probe_fl) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "Attempted to execute control transfer" " when device not ok"); @@ -3167,7 +3036,7 @@ static int pvr2_send_request_ex(struct pvr2_hdw *hdw, hdw->cmd_debug_state = 0; if ((status < 0) && (!probe_fl)) { - pvr2_hdw_render_useless_unlocked(hdw); + pvr2_hdw_render_useless(hdw); } return status; } @@ -3227,24 +3096,17 @@ static int pvr2_read_register(struct pvr2_hdw *hdw, u16 reg, u32 *data) } -static void pvr2_hdw_render_useless_unlocked(struct pvr2_hdw *hdw) +void pvr2_hdw_render_useless(struct pvr2_hdw *hdw) { if (!hdw->flag_ok) return; - pvr2_trace(PVR2_TRACE_INIT,"render_useless"); - hdw->flag_ok = 0; + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Device being rendered inoperable"); if (hdw->vid_stream) { pvr2_stream_setup(hdw->vid_stream,NULL,0,0); } - hdw->flag_streaming_enabled = 0; - hdw->subsys_enabled_mask = 0; -} - - -void pvr2_hdw_render_useless(struct pvr2_hdw *hdw) -{ - LOCK_TAKE(hdw->ctl_lock); - pvr2_hdw_render_useless_unlocked(hdw); - LOCK_GIVE(hdw->ctl_lock); + hdw->flag_ok = 0; + trace_stbit("flag_ok",hdw->flag_ok); + pvr2_hdw_state_sched(hdw); } @@ -3299,7 +3161,6 @@ int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *hdw) int status; LOCK_TAKE(hdw->ctl_lock); do { pvr2_trace(PVR2_TRACE_INIT,"Requesting uproc hard reset"); - hdw->flag_ok = !0; hdw->cmd_buffer[0] = FX2CMD_DEEP_RESET; status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0); } while (0); LOCK_GIVE(hdw->ctl_lock); @@ -3349,26 +3210,473 @@ static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl) (runFl ? FX2CMD_STREAMING_ON : FX2CMD_STREAMING_OFF); status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0); } while (0); LOCK_GIVE(hdw->ctl_lock); - if (!status) { - hdw->subsys_enabled_mask = - ((hdw->subsys_enabled_mask & - ~(1<<PVR2_SUBSYS_B_USBSTREAM_RUN)) | - (runFl ? (1<<PVR2_SUBSYS_B_USBSTREAM_RUN) : 0)); - } return status; } -void pvr2_hdw_get_debug_info(const struct pvr2_hdw *hdw, - struct pvr2_hdw_debug_info *ptr) +/* Evaluate whether or not state_encoder_ok can change */ +static int state_eval_encoder_ok(struct pvr2_hdw *hdw) +{ + if (hdw->state_encoder_ok) return 0; + if (hdw->flag_tripped) return 0; + if (hdw->state_encoder_run) return 0; + if (hdw->state_encoder_config) return 0; + if (hdw->state_decoder_run) return 0; + if (hdw->state_usbstream_run) return 0; + if (pvr2_upload_firmware2(hdw) < 0) { + hdw->flag_tripped = !0; + trace_stbit("flag_tripped",hdw->flag_tripped); + return !0; + } + hdw->state_encoder_ok = !0; + trace_stbit("state_encoder_ok",hdw->state_encoder_ok); + return !0; +} + + +/* Evaluate whether or not state_encoder_config can change */ +static int state_eval_encoder_config(struct pvr2_hdw *hdw) +{ + if (hdw->state_encoder_config) { + if (hdw->state_encoder_ok) { + if (hdw->state_pipeline_req && + !hdw->state_pipeline_pause) return 0; + } + hdw->state_encoder_config = 0; + hdw->state_encoder_waitok = 0; + trace_stbit("state_encoder_waitok",hdw->state_encoder_waitok); + /* paranoia - solve race if timer just completed */ + del_timer_sync(&hdw->encoder_wait_timer); + } else { + if (!hdw->state_encoder_ok || + !hdw->state_pipeline_idle || + hdw->state_pipeline_pause || + !hdw->state_pipeline_req || + !hdw->state_pipeline_config) { + /* We must reset the enforced wait interval if + anything has happened that might have disturbed + the encoder. This should be a rare case. */ + if (timer_pending(&hdw->encoder_wait_timer)) { + del_timer_sync(&hdw->encoder_wait_timer); + } + if (hdw->state_encoder_waitok) { + /* Must clear the state - therefore we did + something to a state bit and must also + return true. */ + hdw->state_encoder_waitok = 0; + trace_stbit("state_encoder_waitok", + hdw->state_encoder_waitok); + return !0; + } + return 0; + } + if (!hdw->state_encoder_waitok) { + if (!timer_pending(&hdw->encoder_wait_timer)) { + /* waitok flag wasn't set and timer isn't + running. Check flag once more to avoid + a race then start the timer. This is + the point when we measure out a minimal + quiet interval before doing something to + the encoder. */ + if (!hdw->state_encoder_waitok) { + hdw->encoder_wait_timer.expires = + jiffies + (HZ*50/1000); + add_timer(&hdw->encoder_wait_timer); + } + } + /* We can't continue until we know we have been + quiet for the interval measured by this + timer. */ + return 0; + } + pvr2_encoder_configure(hdw); + if (hdw->state_encoder_ok) hdw->state_encoder_config = !0; + } + trace_stbit("state_encoder_config",hdw->state_encoder_config); + return !0; +} + + +/* Evaluate whether or not state_encoder_run can change */ +static int state_eval_encoder_run(struct pvr2_hdw *hdw) +{ + if (hdw->state_encoder_run) { + if (hdw->state_encoder_ok) { + if (hdw->state_decoder_run) return 0; + if (pvr2_encoder_stop(hdw) < 0) return !0; + } + hdw->state_encoder_run = 0; + } else { + if (!hdw->state_encoder_ok) return 0; + if (!hdw->state_decoder_run) return 0; + if (pvr2_encoder_start(hdw) < 0) return !0; + hdw->state_encoder_run = !0; + } + trace_stbit("state_encoder_run",hdw->state_encoder_run); + return !0; +} + + +/* Timeout function for quiescent timer. */ +static void pvr2_hdw_quiescent_timeout(unsigned long data) +{ + struct pvr2_hdw *hdw = (struct pvr2_hdw *)data; + hdw->state_decoder_quiescent = !0; + trace_stbit("state_decoder_quiescent",hdw->state_decoder_quiescent); + hdw->state_stale = !0; + queue_work(hdw->workqueue,&hdw->workpoll); +} + + +/* Timeout function for encoder wait timer. */ +static void pvr2_hdw_encoder_wait_timeout(unsigned long data) +{ + struct pvr2_hdw *hdw = (struct pvr2_hdw *)data; + hdw->state_encoder_waitok = !0; + trace_stbit("state_encoder_waitok",hdw->state_encoder_waitok); + hdw->state_stale = !0; + queue_work(hdw->workqueue,&hdw->workpoll); +} + + +/* Evaluate whether or not state_decoder_run can change */ +static int state_eval_decoder_run(struct pvr2_hdw *hdw) +{ + if (hdw->state_decoder_run) { + if (hdw->state_encoder_ok) { + if (hdw->state_pipeline_req && + !hdw->state_pipeline_pause) return 0; + } + if (!hdw->flag_decoder_missed) { + pvr2_decoder_enable(hdw,0); + } + hdw->state_decoder_quiescent = 0; + hdw->state_decoder_run = 0; + /* paranoia - solve race if timer just completed */ + del_timer_sync(&hdw->quiescent_timer); + } else { + if (!hdw->state_decoder_quiescent) { + if (!timer_pending(&hdw->quiescent_timer)) { + /* We don't do something about the + quiescent timer until right here because + we also want to catch cases where the + decoder was already not running (like + after initialization) as opposed to + knowing that we had just stopped it. + The second flag check is here to cover a + race - the timer could have run and set + this flag just after the previous check + but before we did the pending check. */ + if (!hdw->state_decoder_quiescent) { + hdw->quiescent_timer.expires = + jiffies + (HZ*50/1000); + add_timer(&hdw->quiescent_timer); + } + } + /* Don't allow decoder to start again until it has + been quiesced first. This little detail should + hopefully further stabilize the encoder. */ + return 0; + } + if (!hdw->state_pipeline_req || + hdw->state_pipeline_pause || + !hdw->state_pipeline_config || + !hdw->state_encoder_config || + !hdw->state_encoder_ok) return 0; + del_timer_sync(&hdw->quiescent_timer); + if (hdw->flag_decoder_missed) return 0; + if (pvr2_decoder_enable(hdw,!0) < 0) return 0; + hdw->state_decoder_quiescent = 0; + hdw->state_decoder_run = !0; + } + trace_stbit("state_decoder_quiescent",hdw->state_decoder_quiescent); + trace_stbit("state_decoder_run",hdw->state_decoder_run); + return !0; +} + + +/* Evaluate whether or not state_usbstream_run can change */ +static int state_eval_usbstream_run(struct pvr2_hdw *hdw) +{ + if (hdw->state_usbstream_run) { + if (hdw->state_encoder_ok) { + if (hdw->state_encoder_run) return 0; + } + pvr2_hdw_cmd_usbstream(hdw,0); + hdw->state_usbstream_run = 0; + } else { + if (!hdw->state_encoder_ok || + !hdw->state_encoder_run || + !hdw->state_pipeline_req || + hdw->state_pipeline_pause) return 0; + if (pvr2_hdw_cmd_usbstream(hdw,!0) < 0) return 0; + hdw->state_usbstream_run = !0; + } + trace_stbit("state_usbstream_run",hdw->state_usbstream_run); + return !0; +} + + +/* Attempt to configure pipeline, if needed */ +static int state_eval_pipeline_config(struct pvr2_hdw *hdw) +{ + if (hdw->state_pipeline_config || + hdw->state_pipeline_pause) return 0; + pvr2_hdw_commit_execute(hdw); + return !0; +} + + +/* Update pipeline idle and pipeline pause tracking states based on other + inputs. This must be called whenever the other relevant inputs have + changed. */ +static int state_update_pipeline_state(struct pvr2_hdw *hdw) +{ + unsigned int st; + int updatedFl = 0; + /* Update pipeline state */ + st = !(hdw->state_encoder_run || + hdw->state_decoder_run || + hdw->state_usbstream_run || + (!hdw->state_decoder_quiescent)); + if (!st != !hdw->state_pipeline_idle) { + hdw->state_pipeline_idle = st; + updatedFl = !0; + } + if (hdw->state_pipeline_idle && hdw->state_pipeline_pause) { + hdw->state_pipeline_pause = 0; + updatedFl = !0; + } + return updatedFl; +} + + +typedef int (*state_eval_func)(struct pvr2_hdw *); + +/* Set of functions to be run to evaluate various states in the driver. */ +const static state_eval_func eval_funcs[] = { + state_eval_pipeline_config, + state_eval_encoder_ok, + state_eval_encoder_config, + state_eval_decoder_run, + state_eval_encoder_run, + state_eval_usbstream_run, +}; + + +/* Process various states and return true if we did anything interesting. */ +static int pvr2_hdw_state_update(struct pvr2_hdw *hdw) +{ + unsigned int i; + int state_updated = 0; + int check_flag; + + if (!hdw->state_stale) return 0; + if ((hdw->fw1_state != FW1_STATE_OK) || + !hdw->flag_ok) { + hdw->state_stale = 0; + return !0; + } + /* This loop is the heart of the entire driver. It keeps trying to + evaluate various bits of driver state until nothing changes for + one full iteration. Each "bit of state" tracks some global + aspect of the driver, e.g. whether decoder should run, if + pipeline is configured, usb streaming is on, etc. We separately + evaluate each of those questions based on other driver state to + arrive at the correct running configuration. */ + do { + check_flag = 0; + state_update_pipeline_state(hdw); + /* Iterate over each bit of state */ + for (i = 0; (i<ARRAY_SIZE(eval_funcs)) && hdw->flag_ok; i++) { + if ((*eval_funcs[i])(hdw)) { + check_flag = !0; + state_updated = !0; + state_update_pipeline_state(hdw); + } + } + } while (check_flag && hdw->flag_ok); + hdw->state_stale = 0; + trace_stbit("state_stale",hdw->state_stale); + return state_updated; +} + + +static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which, + char *buf,unsigned int acnt) +{ + switch (which) { + case 0: + return scnprintf( + buf,acnt, + "driver:%s%s%s%s%s", + (hdw->flag_ok ? " <ok>" : " <fail>"), + (hdw->flag_init_ok ? " <init>" : " <uninitialized>"), + (hdw->flag_disconnected ? " <disconnected>" : + " <connected>"), + (hdw->flag_tripped ? " <tripped>" : ""), + (hdw->flag_decoder_missed ? " <no decoder>" : "")); + case 1: + return scnprintf( + buf,acnt, + "pipeline:%s%s%s%s", + (hdw->state_pipeline_idle ? " <idle>" : ""), + (hdw->state_pipeline_config ? + " <configok>" : " <stale>"), + (hdw->state_pipeline_req ? " <req>" : ""), + (hdw->state_pipeline_pause ? " <pause>" : "")); + case 2: + return scnprintf( + buf,acnt, + "worker:%s%s%s%s%s%s", + (hdw->state_decoder_run ? + " <decode:run>" : + (hdw->state_decoder_quiescent ? + "" : " <decode:stop>")), + (hdw->state_decoder_quiescent ? + " <decode:quiescent>" : ""), + (hdw->state_encoder_ok ? + "" : " <encode:init>"), + (hdw->state_encoder_run ? + " <encode:run>" : " <encode:stop>"), + (hdw->state_encoder_config ? + " <encode:configok>" : + (hdw->state_encoder_waitok ? + "" : " <encode:wait>")), + (hdw->state_usbstream_run ? + " <usb:run>" : " <usb:stop>")); + break; + case 3: + return scnprintf( + buf,acnt, + "state: %s", + pvr2_get_state_name(hdw->master_state)); + break; + default: break; + } + return 0; +} + + +unsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw, + char *buf,unsigned int acnt) +{ + unsigned int bcnt,ccnt,idx; + bcnt = 0; + LOCK_TAKE(hdw->big_lock); + for (idx = 0; ; idx++) { + ccnt = pvr2_hdw_report_unlocked(hdw,idx,buf,acnt); + if (!ccnt) break; + bcnt += ccnt; acnt -= ccnt; buf += ccnt; + if (!acnt) break; + buf[0] = '\n'; ccnt = 1; + bcnt += ccnt; acnt -= ccnt; buf += ccnt; + } + LOCK_GIVE(hdw->big_lock); + return bcnt; +} + + +static void pvr2_hdw_state_log_state(struct pvr2_hdw *hdw) +{ + char buf[128]; + unsigned int idx,ccnt; + + for (idx = 0; ; idx++) { + ccnt = pvr2_hdw_report_unlocked(hdw,idx,buf,sizeof(buf)); + if (!ccnt) break; + printk(KERN_INFO "%s %.*s\n",hdw->name,ccnt,buf); + } +} + + +/* Evaluate and update the driver's current state, taking various actions + as appropriate for the update. */ +static int pvr2_hdw_state_eval(struct pvr2_hdw *hdw) +{ + unsigned int st; + int state_updated = 0; + int callback_flag = 0; + + pvr2_trace(PVR2_TRACE_STBITS, + "Drive state check START"); + if (pvrusb2_debug & PVR2_TRACE_STBITS) { + pvr2_hdw_state_log_state(hdw); + } + + /* Process all state and get back over disposition */ + state_updated = pvr2_hdw_state_update(hdw); + + /* Update master state based upon all other states. */ + if (!hdw->flag_ok) { + st = PVR2_STATE_DEAD; + } else if (hdw->fw1_state != FW1_STATE_OK) { + st = PVR2_STATE_COLD; + } else if (!hdw->state_encoder_ok) { + st = PVR2_STATE_WARM; + } else if (hdw->flag_tripped || hdw->flag_decoder_missed) { + st = PVR2_STATE_ERROR; + } else if (hdw->state_encoder_run && + hdw->state_decoder_run && + hdw->state_usbstream_run) { + st = PVR2_STATE_RUN; + } else { + st = PVR2_STATE_READY; + } + if (hdw->master_state != st) { + pvr2_trace(PVR2_TRACE_STATE, + "Device state change from %s to %s", + pvr2_get_state_name(hdw->master_state), + pvr2_get_state_name(st)); + hdw->master_state = st; + state_updated = !0; + callback_flag = !0; + } + if (state_updated) { + /* Trigger anyone waiting on any state changes here. */ + wake_up(&hdw->state_wait_data); + } + + if (pvrusb2_debug & PVR2_TRACE_STBITS) { + pvr2_hdw_state_log_state(hdw); + } + pvr2_trace(PVR2_TRACE_STBITS, + "Drive state check DONE callback=%d",callback_flag); + + return callback_flag; +} + + +/* Cause kernel thread to check / update driver state */ +static void pvr2_hdw_state_sched(struct pvr2_hdw *hdw) +{ + if (hdw->state_stale) return; + hdw->state_stale = !0; + trace_stbit("state_stale",hdw->state_stale); + queue_work(hdw->workqueue,&hdw->workpoll); +} + + +void pvr2_hdw_get_debug_info_unlocked(const struct pvr2_hdw *hdw, + struct pvr2_hdw_debug_info *ptr) { ptr->big_lock_held = hdw->big_lock_held; ptr->ctl_lock_held = hdw->ctl_lock_held; - ptr->flag_ok = hdw->flag_ok; ptr->flag_disconnected = hdw->flag_disconnected; ptr->flag_init_ok = hdw->flag_init_ok; - ptr->flag_streaming_enabled = hdw->flag_streaming_enabled; - ptr->subsys_flags = hdw->subsys_enabled_mask; + ptr->flag_ok = hdw->flag_ok; + ptr->fw1_state = hdw->fw1_state; + ptr->flag_decoder_missed = hdw->flag_decoder_missed; + ptr->flag_tripped = hdw->flag_tripped; + ptr->state_encoder_ok = hdw->state_encoder_ok; + ptr->state_encoder_run = hdw->state_encoder_run; + ptr->state_decoder_run = hdw->state_decoder_run; + ptr->state_usbstream_run = hdw->state_usbstream_run; + ptr->state_decoder_quiescent = hdw->state_decoder_quiescent; + ptr->state_pipeline_config = hdw->state_pipeline_config; + ptr->state_pipeline_req = hdw->state_pipeline_req; + ptr->state_pipeline_pause = hdw->state_pipeline_pause; + ptr->state_pipeline_idle = hdw->state_pipeline_idle; ptr->cmd_debug_state = hdw->cmd_debug_state; ptr->cmd_code = hdw->cmd_debug_code; ptr->cmd_debug_write_len = hdw->cmd_debug_write_len; @@ -3381,6 +3689,15 @@ void pvr2_hdw_get_debug_info(const struct pvr2_hdw *hdw, } +void pvr2_hdw_get_debug_info_locked(struct pvr2_hdw *hdw, + struct pvr2_hdw_debug_info *ptr) +{ + LOCK_TAKE(hdw->ctl_lock); do { + pvr2_hdw_get_debug_info_unlocked(hdw,ptr); + } while(0); LOCK_GIVE(hdw->ctl_lock); +} + + int pvr2_hdw_gpio_get_dir(struct pvr2_hdw *hdw,u32 *dp) { return pvr2_read_register(hdw,PVR2_GPIO_DIR,dp); diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h index e2f9d5e4cb6..3ad7a13d6c3 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -44,27 +44,6 @@ #define PVR2_CVAL_INPUT_COMPOSITE 2 #define PVR2_CVAL_INPUT_RADIO 3 -/* 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_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 ( \ - (1 << PVR2_SUBSYS_B_ENC_FIRMWARE) | \ - (1 << PVR2_SUBSYS_B_ENC_CFG) ) -#define PVR2_SUBSYS_RUN_ALL ( \ - (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 ) - enum pvr2_config { pvr2_config_empty, /* No configuration */ pvr2_config_mpeg, /* Encoded / compressed video */ @@ -79,8 +58,41 @@ enum pvr2_v4l_type { pvr2_v4l_type_radio, }; +/* Major states that we can be in: + * + * DEAD - Device is in an unusable state and cannot be recovered. This + * can happen if we completely lose the ability to communicate with it + * (but it might still on the bus). In this state there's nothing we can + * do; it must be replugged in order to recover. + * + * COLD - Device is in an unusuable state, needs microcontroller firmware. + * + * WARM - We can communicate with the device and the proper + * microcontroller firmware is running, but other device initialization is + * still needed (e.g. encoder firmware). + * + * ERROR - A problem prevents capture operation (e.g. encoder firmware + * missing). + * + * READY - Device is operational, but not streaming. + * + * RUN - Device is streaming. + * + */ +#define PVR2_STATE_NONE 0 +#define PVR2_STATE_DEAD 1 +#define PVR2_STATE_COLD 2 +#define PVR2_STATE_WARM 3 +#define PVR2_STATE_ERROR 4 +#define PVR2_STATE_READY 5 +#define PVR2_STATE_RUN 6 + +/* Translate configuration enum to a string label */ const char *pvr2_config_get_name(enum pvr2_config); +/* Translate a master state enum to a string label */ +const char *pvr2_hdw_get_state_name(unsigned int); + struct pvr2_hdw; /* Create and return a structure for interacting with the underlying @@ -88,28 +100,13 @@ struct pvr2_hdw; struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, const struct usb_device_id *devid); -/* Poll for background activity (if any) */ -void pvr2_hdw_poll(struct pvr2_hdw *); - -/* Trigger a poll to take place later at a convenient time */ -void pvr2_hdw_poll_trigger_unlocked(struct pvr2_hdw *); - -/* Register a callback used to trigger a future poll */ -void pvr2_hdw_setup_poll_trigger(struct pvr2_hdw *, - void (*func)(void *), - void *data); - /* Destroy hardware interaction structure */ void pvr2_hdw_destroy(struct pvr2_hdw *); -/* Set up the structure and attempt to put the device into a usable state. - This can be a time-consuming operation, which is why it is not done - internally as part of the create() step. Return value is exactly the - same as pvr2_hdw_init_ok(). */ -int pvr2_hdw_setup(struct pvr2_hdw *); - -/* Initialization succeeded */ -int pvr2_hdw_init_ok(struct pvr2_hdw *); +/* Register a function to be called whenever the master state changes. */ +void pvr2_hdw_set_state_callback(struct pvr2_hdw *, + void (*callback_func)(void *), + void *callback_data); /* Return true if in the ready (normal) state */ int pvr2_hdw_dev_ok(struct pvr2_hdw *); @@ -161,12 +158,21 @@ int pvr2_hdw_get_tuner_status(struct pvr2_hdw *,struct v4l2_tuner *); /* Query device and see if it thinks it is on a high-speed USB link */ int pvr2_hdw_is_hsm(struct pvr2_hdw *); +/* Return a string token representative of the hardware type */ +const char *pvr2_hdw_get_type(struct pvr2_hdw *); + +/* Return a single line description of the hardware type */ +const char *pvr2_hdw_get_desc(struct pvr2_hdw *); + /* Turn streaming on/off */ int pvr2_hdw_set_streaming(struct pvr2_hdw *,int); /* Find out if streaming is on */ int pvr2_hdw_get_streaming(struct pvr2_hdw *); +/* Retrieve driver overall state */ +int pvr2_hdw_get_state(struct pvr2_hdw *); + /* Configure the type of stream to generate */ int pvr2_hdw_set_stream_type(struct pvr2_hdw *, enum pvr2_config); @@ -177,26 +183,6 @@ struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *); 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. */ -void pvr2_hdw_subsys_bit_chg(struct pvr2_hdw *hdw, - unsigned long msk,unsigned long val); - -/* Retrieve mask indicating which pieces of hardware are currently enabled - / configured. */ -unsigned long pvr2_hdw_subsys_get(struct pvr2_hdw *); - -/* Adjust mask of what get shut down when streaming is stopped. This is a - debugging aid. */ -void pvr2_hdw_subsys_stream_bit_chg(struct pvr2_hdw *hdw, - unsigned long msk,unsigned long val); - -/* Retrieve mask indicating which pieces of hardware are disabled when - streaming is turned off. */ -unsigned long pvr2_hdw_subsys_stream_get(struct pvr2_hdw *); - - /* Enable / disable retrieval of CPU firmware or prom contents. This must be enabled before pvr2_hdw_cpufw_get() will function. Note that doing this may prevent the device from running (and leaving this mode may @@ -253,6 +239,9 @@ void pvr2_hdw_cpureset_assert(struct pvr2_hdw *,int); /* Execute a USB-commanded device reset */ void pvr2_hdw_device_reset(struct pvr2_hdw *); +/* Reset worker's error trapping circuit breaker */ +int pvr2_hdw_untrip(struct pvr2_hdw *); + /* Execute hard reset command (after this point it's likely that the encoder will have to be reconfigured). This also clears the "useless" state. */ @@ -275,11 +264,21 @@ int pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val); struct pvr2_hdw_debug_info { int big_lock_held; int ctl_lock_held; - int flag_ok; int flag_disconnected; int flag_init_ok; - int flag_streaming_enabled; - unsigned long subsys_flags; + int flag_ok; + int fw1_state; + int flag_decoder_missed; + int flag_tripped; + int state_encoder_ok; + int state_encoder_run; + int state_decoder_run; + int state_usbstream_run; + int state_decoder_quiescent; + int state_pipeline_config; + int state_pipeline_req; + int state_pipeline_pause; + int state_pipeline_idle; int cmd_debug_state; int cmd_debug_write_len; int cmd_debug_read_len; @@ -295,8 +294,20 @@ struct pvr2_hdw_debug_info { diagnosing lockups. Note that this operation is completed without any kind of locking and so it is not atomic and may yield inconsistent results. This is *purely* a debugging aid. */ -void pvr2_hdw_get_debug_info(const struct pvr2_hdw *hdw, - struct pvr2_hdw_debug_info *); +void pvr2_hdw_get_debug_info_unlocked(const struct pvr2_hdw *hdw, + struct pvr2_hdw_debug_info *); + +/* Intrusively retrieve internal state info - this is useful for + diagnosing overall driver state. This operation synchronizes against + the overall driver mutex - so if there are locking problems this will + likely hang! This is *purely* a debugging aid. */ +void pvr2_hdw_get_debug_info_locked(struct pvr2_hdw *hdw, + struct pvr2_hdw_debug_info *); + +/* Report out several lines of text that describes driver internal state. + Results are written into the passed-in buffer. */ +unsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw, + char *buf_ptr,unsigned int buf_size); /* Cause modules to log their state once */ void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw); @@ -306,9 +317,6 @@ void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw); a debugging aid. */ int pvr2_upload_firmware2(struct pvr2_hdw *hdw); -/* List of device types that we can match */ -extern struct usb_device_id pvr2_device_table[]; - #endif /* __PVRUSB2_HDW_H */ /* diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index c817c864e6a..62867fa3517 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -895,7 +895,7 @@ static int pvr2_i2c_attach_inform(struct i2c_client *client) list_add_tail(&cp->list,&hdw->i2c_clients); hdw->i2c_pend_types |= PVR2_I2C_PEND_DETECT; } while (0); mutex_unlock(&hdw->i2c_list_lock); - if (fl) pvr2_hdw_poll_trigger_unlocked(hdw); + if (fl) queue_work(hdw->workqueue,&hdw->worki2csync); return 0; } @@ -980,14 +980,16 @@ void pvr2_i2c_core_init(struct pvr2_hdw *hdw) printk(KERN_INFO "%s: IR disabled\n",hdw->name); hdw->i2c_func[0x18] = i2c_black_hole; } else if (ir_mode[hdw->unit_number] == 1) { - if (hdw->hdw_type == PVR2_HDW_TYPE_24XXX) { + if (hdw->hdw_desc->flag_has_hauppauge_custom_ir) { hdw->i2c_func[0x18] = i2c_24xxx_ir; } } - if (hdw->hdw_type == PVR2_HDW_TYPE_24XXX) { - hdw->i2c_func[0x1b] = i2c_hack_wm8775; + if (hdw->hdw_desc->flag_has_cx25840) { hdw->i2c_func[0x44] = i2c_hack_cx25840; } + if (hdw->hdw_desc->flag_has_wm8775) { + hdw->i2c_func[0x1b] = i2c_hack_wm8775; + } // Configure the adapter and set up everything else related to it. memcpy(&hdw->i2c_adap,&pvr2_i2c_adap_template,sizeof(hdw->i2c_adap)); diff --git a/drivers/media/video/pvrusb2/pvrusb2-main.c b/drivers/media/video/pvrusb2/pvrusb2-main.c index 11b3b2e84b9..b63b2265503 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-main.c +++ b/drivers/media/video/pvrusb2/pvrusb2-main.c @@ -28,6 +28,7 @@ #include <linux/videodev2.h> #include "pvrusb2-hdw.h" +#include "pvrusb2-devattr.h" #include "pvrusb2-context.h" #include "pvrusb2-debug.h" #include "pvrusb2-v4l2.h" @@ -148,11 +149,6 @@ static void __exit pvr_exit(void) module_init(pvr_init); module_exit(pvr_exit); -/* Mike Isely <mcisely@pobox.com> 11-Mar-2006: See pvrusb2-hdw.c for - MODULE_DEVICE_TABLE(). We have to declare that attribute there - because that's where the device table actually is now and it seems - that certain gcc configurations get angry if MODULE_DEVICE_TABLE() - is used on what ends up being an external symbol. */ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/pvrusb2/pvrusb2-std.c b/drivers/media/video/pvrusb2/pvrusb2-std.c index 63e55bb59fc..da309288daa 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-std.c +++ b/drivers/media/video/pvrusb2/pvrusb2-std.c @@ -50,6 +50,10 @@ struct std_name { V4L2_STD_NTSC_M_KR| \ V4L2_STD_NTSC_443) +#define CSTD_ATSC \ + (V4L2_STD_ATSC_8_VSB| \ + V4L2_STD_ATSC_16_VSB) + #define CSTD_SECAM \ (V4L2_STD_SECAM_B| \ V4L2_STD_SECAM_D| \ @@ -82,6 +86,7 @@ static const struct std_name std_groups[] = { {"PAL",CSTD_PAL}, {"NTSC",CSTD_NTSC}, {"SECAM",CSTD_SECAM}, + {"ATSC",CSTD_ATSC}, }; /* Mapping of standard bits to modulation system */ @@ -104,6 +109,8 @@ static const struct std_name std_items[] = { {"N",TSTD_N}, {"Nc",TSTD_Nc}, {"60",TSTD_60}, + {"8VSB",V4L2_STD_ATSC_8_VSB}, + {"16VSB",V4L2_STD_ATSC_16_VSB}, }; diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c index 3c57a7d8200..7a1cd878e31 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c +++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c @@ -43,10 +43,14 @@ struct pvr2_sysfs { struct device_attribute attr_v4l_radio_minor_number; struct device_attribute attr_unit_number; struct device_attribute attr_bus_info; + struct device_attribute attr_hdw_name; + struct device_attribute attr_hdw_desc; int v4l_minor_number_created_ok; int v4l_radio_minor_number_created_ok; int unit_number_created_ok; int bus_info_created_ok; + int hdw_name_created_ok; + int hdw_desc_created_ok; }; #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC @@ -712,6 +716,14 @@ static void class_dev_destroy(struct pvr2_sysfs *sfp) pvr2_sysfs_tear_down_debugifc(sfp); #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ pvr2_sysfs_tear_down_controls(sfp); + if (sfp->hdw_desc_created_ok) { + device_remove_file(sfp->class_dev, + &sfp->attr_hdw_desc); + } + if (sfp->hdw_name_created_ok) { + device_remove_file(sfp->class_dev, + &sfp->attr_hdw_name); + } if (sfp->bus_info_created_ok) { device_remove_file(sfp->class_dev, &sfp->attr_bus_info); @@ -758,6 +770,28 @@ static ssize_t bus_info_show(struct device *class_dev, } +static ssize_t hdw_name_show(struct device *class_dev, + struct device_attribute *attr, char *buf) +{ + struct pvr2_sysfs *sfp; + sfp = (struct pvr2_sysfs *)class_dev->driver_data; + if (!sfp) return -EINVAL; + return scnprintf(buf,PAGE_SIZE,"%s\n", + pvr2_hdw_get_type(sfp->channel.hdw)); +} + + +static ssize_t hdw_desc_show(struct device *class_dev, + struct device_attribute *attr, char *buf) +{ + struct pvr2_sysfs *sfp; + sfp = (struct pvr2_sysfs *)class_dev->driver_data; + if (!sfp) return -EINVAL; + return scnprintf(buf,PAGE_SIZE,"%s\n", + pvr2_hdw_get_desc(sfp->channel.hdw)); +} + + static ssize_t v4l_radio_minor_number_show(struct device *class_dev, struct device_attribute *attr, char *buf) @@ -871,6 +905,32 @@ static void class_dev_create(struct pvr2_sysfs *sfp, sfp->bus_info_created_ok = !0; } + sfp->attr_hdw_name.attr.name = "device_hardware_type"; + sfp->attr_hdw_name.attr.mode = S_IRUGO; + sfp->attr_hdw_name.show = hdw_name_show; + sfp->attr_hdw_name.store = NULL; + ret = device_create_file(sfp->class_dev, + &sfp->attr_hdw_name); + if (ret < 0) { + printk(KERN_WARNING "%s: device_create_file error: %d\n", + __FUNCTION__, ret); + } else { + sfp->hdw_name_created_ok = !0; + } + + sfp->attr_hdw_desc.attr.name = "device_hardware_description"; + sfp->attr_hdw_desc.attr.mode = S_IRUGO; + sfp->attr_hdw_desc.show = hdw_desc_show; + sfp->attr_hdw_desc.store = NULL; + ret = device_create_file(sfp->class_dev, + &sfp->attr_hdw_desc); + if (ret < 0) { + printk(KERN_WARNING "%s: device_create_file error: %d\n", + __FUNCTION__, ret); + } else { + sfp->hdw_desc_created_ok = !0; + } + pvr2_sysfs_add_controls(sfp); #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC pvr2_sysfs_add_debugifc(sfp); diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 7a596ea7cfe..8f0587ebd4b 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -205,6 +205,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability)); strlcpy(cap->bus_info,pvr2_hdw_get_bus_info(hdw), sizeof(cap->bus_info)); + strlcpy(cap->card,pvr2_hdw_get_desc(hdw),sizeof(cap->card)); ret = 0; break; @@ -1015,10 +1016,8 @@ static int pvr2_v4l2_iosetup(struct pvr2_v4l2_fh *fh) sp = fh->dev_info->stream->stream; pvr2_stream_set_callback(sp,(pvr2_stream_callback)pvr2_v4l2_notify,fh); pvr2_hdw_set_stream_type(hdw,fh->dev_info->config); - pvr2_hdw_set_streaming(hdw,!0); - ret = pvr2_ioread_set_enabled(fh->rhp,!0); - - return ret; + if ((ret = pvr2_hdw_set_streaming(hdw,!0)) < 0) return ret; + return pvr2_ioread_set_enabled(fh->rhp,!0); } diff --git a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c index 61efa6f0220..7c47345501b 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c +++ b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c @@ -49,29 +49,50 @@ struct pvr2_v4l_decoder { }; +struct routing_scheme { + const int *def; + unsigned int cnt; +}; + + +static const int routing_scheme0[] = { + [PVR2_CVAL_INPUT_TV] = SAA7115_COMPOSITE4, + /* In radio mode, we mute the video, but point at one + spot just to stay consistent */ + [PVR2_CVAL_INPUT_RADIO] = SAA7115_COMPOSITE5, + [PVR2_CVAL_INPUT_COMPOSITE] = SAA7115_COMPOSITE5, + [PVR2_CVAL_INPUT_SVIDEO] = SAA7115_SVIDEO2, +}; + +static const struct routing_scheme routing_schemes[] = { + [PVR2_ROUTING_SCHEME_HAUPPAUGE] = { + .def = routing_scheme0, + .cnt = ARRAY_SIZE(routing_scheme0), + }, +}; + static void set_input(struct pvr2_v4l_decoder *ctxt) { struct pvr2_hdw *hdw = ctxt->hdw; struct v4l2_routing route; + const struct routing_scheme *sp; + unsigned int sid = hdw->hdw_desc->signal_routing_scheme; 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; - case PVR2_CVAL_INPUT_COMPOSITE: - route.input = SAA7115_COMPOSITE5; - break; - case PVR2_CVAL_INPUT_SVIDEO: - route.input = SAA7115_SVIDEO2; - break; - case PVR2_CVAL_INPUT_RADIO: - // In radio mode, we mute the video, but point at one - // spot just to stay consistent - route.input = SAA7115_COMPOSITE5; - default: + + if ((sid < ARRAY_SIZE(routing_schemes)) && + ((sp = routing_schemes + sid) != 0) && + (hdw->input_val >= 0) && + (hdw->input_val < sp->cnt)) { + route.input = sp->def[hdw->input_val]; + } else { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "*** WARNING *** i2c v4l2 set_input:" + " Invalid routing scheme (%u) and/or input (%d)", + sid,hdw->input_val); return; } + route.output = 0; pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_VIDEO_ROUTING,&route); } @@ -129,7 +150,7 @@ static const struct pvr2_v4l_decoder_ops decoder_ops[] = { static void decoder_detach(struct pvr2_v4l_decoder *ctxt) { ctxt->client->handler = NULL; - ctxt->hdw->decoder_ctrl = NULL; + pvr2_hdw_set_decoder(ctxt->hdw,NULL); kfree(ctxt); } @@ -217,7 +238,7 @@ int pvr2_i2c_decoder_v4l_setup(struct pvr2_hdw *hdw, ctxt->client = cp; ctxt->hdw = hdw; ctxt->stale_mask = (1 << ARRAY_SIZE(decoder_ops)) - 1; - hdw->decoder_ctrl = &ctxt->ctrl; + pvr2_hdw_set_decoder(hdw,&ctxt->ctrl); cp->handler = &ctxt->handler; pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x saa711x V4L2 handler set up", cp->client->addr); |