From ff699e6bd02eb1c6d02c7c2b576c2ee6caab201c Mon Sep 17 00:00:00 2001 From: Douglas Schilling Landgraf Date: Tue, 22 Apr 2008 14:41:48 -0300 Subject: V4L/DVB (7094): static memory - Static memory is always initialized with 0. - Replaced in some cases C99 comments for /* */ Signed-off-by: Douglas Schilling Landgraf Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 2404053a4d8..9199b5defb6 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -43,13 +43,13 @@ static struct pvr2_hdw *unit_pointers[PVR_NUM] = {[ 0 ... PVR_NUM-1 ] = NULL}; static DEFINE_MUTEX(pvr2_unit_mtx); -static int ctlchg = 0; +static int ctlchg; static int initusbreset = 1; -static int procreload = 0; +static int procreload; static int tuner[PVR_NUM] = { [0 ... PVR_NUM-1] = -1 }; static int tolerance[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 }; static int video_std[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 }; -static int init_pause_msec = 0; +static int init_pause_msec; module_param(ctlchg, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(ctlchg, "0=optimize ctl change 1=always accept new ctl value"); -- cgit v1.2.3 From 29bf5b1d754a9a64f68c37938e1a0b7b63b724ba Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Tue, 22 Apr 2008 14:45:37 -0300 Subject: V4L/DVB (7297): pvrusb2: Dynamically control range of input selections This follows from defining the available inputs as device attributes. This change causes the driver to adjust its list of inputs based on those attributes. Now, for example, the FM radio will appear as a choice only if the hardware supports an FM radio. Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 9199b5defb6..66945f7d221 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -182,6 +182,7 @@ static const char *control_values_srate[] = { static const char *control_values_input[] = { [PVR2_CVAL_INPUT_TV] = "television", /*xawtv needs this name*/ + [PVR2_CVAL_INPUT_DTV] = "dtv", [PVR2_CVAL_INPUT_RADIO] = "radio", [PVR2_CVAL_INPUT_SVIDEO] = "s-video", [PVR2_CVAL_INPUT_COMPOSITE] = "composite", @@ -367,6 +368,27 @@ static int ctrl_get_input(struct pvr2_ctrl *cptr,int *vp) return 0; } +static int ctrl_check_input(struct pvr2_ctrl *cptr,int v) +{ + struct pvr2_hdw *hdw = cptr->hdw; + const struct pvr2_device_desc *dsc = hdw->hdw_desc; + + switch (v) { + case PVR2_CVAL_INPUT_TV: + return dsc->flag_has_analogtuner != 0; + case PVR2_CVAL_INPUT_DTV: + return dsc->flag_has_digitaltuner != 0; + case PVR2_CVAL_INPUT_SVIDEO: + return dsc->flag_has_svideo != 0; + case PVR2_CVAL_INPUT_COMPOSITE: + return dsc->flag_has_composite != 0; + case PVR2_CVAL_INPUT_RADIO: + return dsc->flag_has_fmradio != 0; + default: + return 0; + } +} + static int ctrl_set_input(struct pvr2_ctrl *cptr,int m,int v) { struct pvr2_hdw *hdw = cptr->hdw; @@ -382,7 +404,8 @@ static int ctrl_set_input(struct pvr2_ctrl *cptr,int m,int v) if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { hdw->freqSelector = 0; hdw->freqDirty = !0; - } else if (hdw->input_val == PVR2_CVAL_INPUT_TV) { + } else if ((hdw->input_val == PVR2_CVAL_INPUT_TV) || + (hdw->input_val == PVR2_CVAL_INPUT_DTV)) { hdw->freqSelector = 1; hdw->freqDirty = !0; } @@ -803,6 +826,7 @@ static const struct pvr2_ctl_info control_defs[] = { .name = "input", .internal_id = PVR2_CID_INPUT, .default_value = PVR2_CVAL_INPUT_TV, + .check_value = ctrl_check_input, DEFREF(input), DEFENUM(control_values_input), },{ -- cgit v1.2.3 From 7fb20fa38caaf5c9d1b1d60b181c99ca30122520 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Tue, 22 Apr 2008 14:45:37 -0300 Subject: V4L/DVB (7299): pvrusb2: Improve logic which handles input choice availability Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 35 +++++++++++++++---------------- 1 file changed, 17 insertions(+), 18 deletions(-) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 66945f7d221..94cb0a76e77 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -370,23 +370,7 @@ static int ctrl_get_input(struct pvr2_ctrl *cptr,int *vp) static int ctrl_check_input(struct pvr2_ctrl *cptr,int v) { - struct pvr2_hdw *hdw = cptr->hdw; - const struct pvr2_device_desc *dsc = hdw->hdw_desc; - - switch (v) { - case PVR2_CVAL_INPUT_TV: - return dsc->flag_has_analogtuner != 0; - case PVR2_CVAL_INPUT_DTV: - return dsc->flag_has_digitaltuner != 0; - case PVR2_CVAL_INPUT_SVIDEO: - return dsc->flag_has_svideo != 0; - case PVR2_CVAL_INPUT_COMPOSITE: - return dsc->flag_has_composite != 0; - case PVR2_CVAL_INPUT_RADIO: - return dsc->flag_has_fmradio != 0; - default: - return 0; - } + return ((1 << v) & cptr->hdw->input_avail_mask) != 0; } static int ctrl_set_input(struct pvr2_ctrl *cptr,int m,int v) @@ -1834,7 +1818,7 @@ static void pvr2_hdw_setup(struct pvr2_hdw *hdw) struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, const struct usb_device_id *devid) { - unsigned int idx,cnt1,cnt2; + unsigned int idx,cnt1,cnt2,m; struct pvr2_hdw *hdw; int valid_std_mask; struct pvr2_ctrl *cptr; @@ -1865,6 +1849,15 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, hdw->tuner_signal_stale = !0; cx2341x_fill_defaults(&hdw->enc_ctl_state); + /* Calculate which inputs are OK */ + m = 0; + if (hdw_desc->flag_has_analogtuner) m |= 1 << PVR2_CVAL_INPUT_TV; + if (hdw_desc->flag_has_digitaltuner) m |= 1 << PVR2_CVAL_INPUT_DTV; + if (hdw_desc->flag_has_svideo) m |= 1 << PVR2_CVAL_INPUT_SVIDEO; + if (hdw_desc->flag_has_composite) m |= 1 << PVR2_CVAL_INPUT_COMPOSITE; + if (hdw_desc->flag_has_fmradio) m |= 1 << PVR2_CVAL_INPUT_RADIO; + hdw->input_avail_mask = m; + hdw->control_cnt = CTRLDEF_COUNT; hdw->control_cnt += MPEGDEF_COUNT; hdw->controls = kzalloc(sizeof(struct pvr2_ctrl) * hdw->control_cnt, @@ -3780,6 +3773,12 @@ int pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val) } +unsigned int pvr2_hdw_get_input_available(struct pvr2_hdw *hdw) +{ + return hdw->input_avail_mask; +} + + /* Find I2C address of eeprom */ static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw) { -- cgit v1.2.3 From dbc40a0e582a88d2561d13d1fea4f3496bff9650 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Tue, 22 Apr 2008 14:45:39 -0300 Subject: V4L/DVB (7303): pvrusb2: Ensure that default input selection is actually valid Previously the pvrusb2 driver just started with the default input to be "television". But if the device doesn't support an analog tuner then this default must be different. New logic here selects a reasonable default based on the actual valid set of available inputs. Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 94cb0a76e77..ae36948c6c1 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1875,6 +1875,15 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, cptr = hdw->controls + idx; cptr->info = control_defs+idx; } + + /* Ensure that default input choice is a valid one. */ + m = hdw->input_avail_mask; + if (m) for (idx = 0; idx < (sizeof(m) << 3); idx++) { + if (!((1 << idx) & m)) continue; + hdw->input_val = idx; + break; + } + /* Define and configure additional controls from cx2341x module. */ hdw->mpeg_ctrl_info = kzalloc( sizeof(*(hdw->mpeg_ctrl_info)) * MPEGDEF_COUNT, GFP_KERNEL); -- cgit v1.2.3 From e1edb19a001b25c0ce2e52a669cba6b6eb31883c Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 22 Apr 2008 14:45:39 -0300 Subject: V4L/DVB (7304): pvrusb2: add function pvr2_hdw_cmd_powerdown Call pvr2_hdw_cmd_powerdown to power down the device Signed-off-by: Michael Krufky Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index ae36948c6c1..9408e8db98e 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -3193,17 +3193,32 @@ int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *hdw) } -int pvr2_hdw_cmd_powerup(struct pvr2_hdw *hdw) +static int pvr2_hdw_cmd_power_ctrl(struct pvr2_hdw *hdw, int onoff) { int status; LOCK_TAKE(hdw->ctl_lock); do { - pvr2_trace(PVR2_TRACE_INIT,"Requesting powerup"); - hdw->cmd_buffer[0] = FX2CMD_POWER_ON; - status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0); + if (onoff) { + pvr2_trace(PVR2_TRACE_INIT, "Requesting powerup"); + hdw->cmd_buffer[0] = FX2CMD_POWER_ON; + } else { + pvr2_trace(PVR2_TRACE_INIT, "Requesting powerdown"); + hdw->cmd_buffer[0] = FX2CMD_POWER_OFF; + } + status = pvr2_send_request(hdw, hdw->cmd_buffer, 1, NULL, 0); } while (0); LOCK_GIVE(hdw->ctl_lock); return status; } +int pvr2_hdw_cmd_powerup(struct pvr2_hdw *hdw) +{ + return pvr2_hdw_cmd_power_ctrl(hdw, 1); +} + +int pvr2_hdw_cmd_powerdown(struct pvr2_hdw *hdw) +{ + return pvr2_hdw_cmd_power_ctrl(hdw, 0); +} + int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw) { -- cgit v1.2.3 From 84147f3dd9187cd0c9810801be1282419a8ea00a Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Tue, 22 Apr 2008 14:45:40 -0300 Subject: V4L/DVB (7307): pvrusb2: New functions for additional FX2 digital-related commands This code is actually part of a larger set from Mike Krufky , to support ATSC streaming from within the pvrusb2 driver. More to come... Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 55 +++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 9408e8db98e..8eabb67e589 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -3241,6 +3241,61 @@ int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw) } +int pvr2_hdw_cmd_hcw_demod_reset(struct pvr2_hdw *hdw, int onoff) +{ + int status; + + LOCK_TAKE(hdw->ctl_lock); do { + pvr2_trace(PVR2_TRACE_INIT, "Issuing fe demod wake command"); + hdw->flag_ok = !0; + hdw->cmd_buffer[0] = FX2CMD_HCW_DEMOD_RESETIN; + hdw->cmd_buffer[1] = onoff; + status = pvr2_send_request(hdw, hdw->cmd_buffer, 2, NULL, 0); + } while (0); LOCK_GIVE(hdw->ctl_lock); + + return status; +} + +int pvr2_hdw_cmd_hcw_usbstream_dvb(struct pvr2_hdw *hdw, int onoff) +{ + int status; + LOCK_TAKE(hdw->ctl_lock); do { + hdw->cmd_buffer[0] = + (onoff ? FX2CMD_HCW_DTV_STREAMING_ON : + FX2CMD_HCW_DTV_STREAMING_OFF); + status = pvr2_send_request(hdw, hdw->cmd_buffer, 1, NULL, 0); + } while (0); LOCK_GIVE(hdw->ctl_lock); + return status; +} + +int pvr2_hdw_cmd_onair_fe_power_ctrl(struct pvr2_hdw *hdw, int onoff) +{ + int status; + + LOCK_TAKE(hdw->ctl_lock); do { + pvr2_trace(PVR2_TRACE_INIT, "Issuing fe power command to CPLD"); + hdw->flag_ok = !0; + hdw->cmd_buffer[0] = + (onoff ? FX2CMD_ONAIR_DTV_POWER_ON : + FX2CMD_ONAIR_DTV_POWER_OFF); + status = pvr2_send_request(hdw, hdw->cmd_buffer, 1, NULL, 0); + } while (0); LOCK_GIVE(hdw->ctl_lock); + + return status; +} + +int pvr2_hdw_cmd_onair_digital_path_ctrl(struct pvr2_hdw *hdw, int onoff) +{ + int status; + LOCK_TAKE(hdw->ctl_lock); do { + hdw->cmd_buffer[0] = + (onoff ? FX2CMD_ONAIR_DTV_STREAMING_ON : + FX2CMD_ONAIR_DTV_STREAMING_OFF); + status = pvr2_send_request(hdw, hdw->cmd_buffer, 1, NULL, 0); + } while (0); LOCK_GIVE(hdw->ctl_lock); + return status; +} + /* Stop / start video stream transport */ static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl) { -- cgit v1.2.3 From e8f5bacfcf2ba9a98674f3cd51b63020920e16aa Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Tue, 22 Apr 2008 14:45:40 -0300 Subject: V4L/DVB (7308): pvrusb2: Define digital control scheme device attributes Unlike analog control, control of the digital side is not nearly as uniform among different devices. So we have to specify the correct digital control scheme as a new device attribute. Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 8eabb67e589..7016d3eda32 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1852,7 +1852,9 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, /* Calculate which inputs are OK */ m = 0; if (hdw_desc->flag_has_analogtuner) m |= 1 << PVR2_CVAL_INPUT_TV; - if (hdw_desc->flag_has_digitaltuner) m |= 1 << PVR2_CVAL_INPUT_DTV; + if (hdw_desc->digital_control_scheme != PVR2_DIGITAL_SCHEME_NONE) { + m |= 1 << PVR2_CVAL_INPUT_DTV; + } if (hdw_desc->flag_has_svideo) m |= 1 << PVR2_CVAL_INPUT_SVIDEO; if (hdw_desc->flag_has_composite) m |= 1 << PVR2_CVAL_INPUT_COMPOSITE; if (hdw_desc->flag_has_fmradio) m |= 1 << PVR2_CVAL_INPUT_RADIO; -- cgit v1.2.3 From 62433e312076d4ff4f2df357b2a6fac29974344a Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Tue, 22 Apr 2008 14:45:40 -0300 Subject: V4L/DVB (7309): pvrusb2: Enhance core logic to also control digital streaming This is a major pvrusb2 change. The driver core has an algorithm that is used to cleanly sequence the changes needed to enable / disable video streaming. The algorithm had originally been written for analog streaming, but when in digital mode the pipeline is considerably Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 196 ++++++++++++++++++++++++------ 1 file changed, 157 insertions(+), 39 deletions(-) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 7016d3eda32..0941f0b434b 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1860,6 +1860,14 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, if (hdw_desc->flag_has_fmradio) m |= 1 << PVR2_CVAL_INPUT_RADIO; hdw->input_avail_mask = m; + /* If not a hybrid device, pathway_state never changes. So + initialize it here to what it should forever be. */ + if (!(hdw->input_avail_mask & (1 << PVR2_CVAL_INPUT_DTV))) { + hdw->pathway_state = PVR2_PATHWAY_ANALOG; + } else if (!(hdw->input_avail_mask & (1 << PVR2_CVAL_INPUT_TV))) { + hdw->pathway_state = PVR2_PATHWAY_DIGITAL; + } + hdw->control_cnt = CTRLDEF_COUNT; hdw->control_cnt += MPEGDEF_COUNT; hdw->controls = kzalloc(sizeof(struct pvr2_ctrl) * hdw->control_cnt, @@ -2380,6 +2388,17 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw) } } + if (hdw->input_dirty && + (((hdw->input_val == PVR2_CVAL_INPUT_DTV) ? + PVR2_PATHWAY_DIGITAL : PVR2_PATHWAY_ANALOG) != + hdw->pathway_state)) { + /* Change of mode being asked for... */ + hdw->state_pathway_ok = 0; + } + if (!hdw->state_pathway_ok) { + /* Can't commit anything until pathway is ok. */ + return 0; + } /* 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 @@ -2436,9 +2455,11 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw) /* Now execute i2c core update */ pvr2_i2c_core_sync(hdw); - if (hdw->state_encoder_run) { - /* If encoder isn't running, then this will get worked out - later when we start the encoder. */ + if ((hdw->pathway_state == PVR2_PATHWAY_ANALOG) && + hdw->state_encoder_run) { + /* If encoder isn't running or it can't be touched, then + this will get worked out later when we start the + encoder. */ if (pvr2_encoder_adjust(hdw) < 0) return !0; } @@ -3243,12 +3264,14 @@ int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw) } -int pvr2_hdw_cmd_hcw_demod_reset(struct pvr2_hdw *hdw, int onoff) +static int pvr2_hdw_cmd_hcw_demod_reset(struct pvr2_hdw *hdw, int onoff) { int status; LOCK_TAKE(hdw->ctl_lock); do { - pvr2_trace(PVR2_TRACE_INIT, "Issuing fe demod wake command"); + pvr2_trace(PVR2_TRACE_INIT, + "Issuing fe demod wake command (%s)", + (onoff ? "on" : "off")); hdw->flag_ok = !0; hdw->cmd_buffer[0] = FX2CMD_HCW_DEMOD_RESETIN; hdw->cmd_buffer[1] = onoff; @@ -3258,24 +3281,15 @@ int pvr2_hdw_cmd_hcw_demod_reset(struct pvr2_hdw *hdw, int onoff) return status; } -int pvr2_hdw_cmd_hcw_usbstream_dvb(struct pvr2_hdw *hdw, int onoff) -{ - int status; - LOCK_TAKE(hdw->ctl_lock); do { - hdw->cmd_buffer[0] = - (onoff ? FX2CMD_HCW_DTV_STREAMING_ON : - FX2CMD_HCW_DTV_STREAMING_OFF); - status = pvr2_send_request(hdw, hdw->cmd_buffer, 1, NULL, 0); - } while (0); LOCK_GIVE(hdw->ctl_lock); - return status; -} -int pvr2_hdw_cmd_onair_fe_power_ctrl(struct pvr2_hdw *hdw, int onoff) +static int pvr2_hdw_cmd_onair_fe_power_ctrl(struct pvr2_hdw *hdw, int onoff) { int status; LOCK_TAKE(hdw->ctl_lock); do { - pvr2_trace(PVR2_TRACE_INIT, "Issuing fe power command to CPLD"); + pvr2_trace(PVR2_TRACE_INIT, + "Issuing fe power command to CPLD (%s)", + (onoff ? "on" : "off")); hdw->flag_ok = !0; hdw->cmd_buffer[0] = (onoff ? FX2CMD_ONAIR_DTV_POWER_ON : @@ -3286,10 +3300,15 @@ int pvr2_hdw_cmd_onair_fe_power_ctrl(struct pvr2_hdw *hdw, int onoff) return status; } -int pvr2_hdw_cmd_onair_digital_path_ctrl(struct pvr2_hdw *hdw, int onoff) + +static int pvr2_hdw_cmd_onair_digital_path_ctrl(struct pvr2_hdw *hdw, + int onoff) { int status; LOCK_TAKE(hdw->ctl_lock); do { + pvr2_trace(PVR2_TRACE_INIT, + "Issuing onair digital setup command (%s)", + (onoff ? "on" : "off")); hdw->cmd_buffer[0] = (onoff ? FX2CMD_ONAIR_DTV_STREAMING_ON : FX2CMD_ONAIR_DTV_STREAMING_OFF); @@ -3298,19 +3317,85 @@ int pvr2_hdw_cmd_onair_digital_path_ctrl(struct pvr2_hdw *hdw, int onoff) return status; } + +static void pvr2_hdw_cmd_modeswitch(struct pvr2_hdw *hdw,int digitalFl) +{ + int cmode; + /* Compare digital/analog desired setting with current setting. If + they don't match, fix it... */ + cmode = (digitalFl ? PVR2_PATHWAY_DIGITAL : PVR2_PATHWAY_ANALOG); + if (cmode == hdw->pathway_state) { + /* They match; nothing to do */ + return; + } + + switch (hdw->hdw_desc->digital_control_scheme) { + case PVR2_DIGITAL_SCHEME_HAUPPAUGE: + pvr2_hdw_cmd_hcw_demod_reset(hdw,digitalFl); + if (cmode == PVR2_PATHWAY_ANALOG) { + /* If moving to analog mode, also force the decoder + to reset. If no decoder is attached, then it's + ok to ignore this because if/when the decoder + attaches, it will reset itself at that time. */ + pvr2_hdw_cmd_decoder_reset(hdw); + } + break; + case PVR2_DIGITAL_SCHEME_ONAIR: + /* Supposedly we should always have the power on whether in + digital or analog mode. But for now do what appears to + work... */ + if (digitalFl) pvr2_hdw_cmd_onair_fe_power_ctrl(hdw,!0); + pvr2_hdw_cmd_onair_digital_path_ctrl(hdw,digitalFl); + if (!digitalFl) pvr2_hdw_cmd_onair_fe_power_ctrl(hdw,0); + break; + default: break; + } + + hdw->pathway_state = cmode; +} + + /* Stop / start video stream transport */ static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl) { - int status; + int status,cc; + if ((hdw->pathway_state == PVR2_PATHWAY_DIGITAL) && + hdw->hdw_desc->digital_control_scheme == + PVR2_DIGITAL_SCHEME_HAUPPAUGE) { + cc = (runFl ? + FX2CMD_HCW_DTV_STREAMING_ON : + FX2CMD_HCW_DTV_STREAMING_OFF); + } else { + cc = (runFl ? + FX2CMD_STREAMING_ON : + FX2CMD_STREAMING_OFF); + } + LOCK_TAKE(hdw->ctl_lock); do { - hdw->cmd_buffer[0] = - (runFl ? FX2CMD_STREAMING_ON : FX2CMD_STREAMING_OFF); + hdw->cmd_buffer[0] = cc; status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0); } while (0); LOCK_GIVE(hdw->ctl_lock); return status; } +/* Evaluate whether or not state_pathway_ok can change */ +static int state_eval_pathway_ok(struct pvr2_hdw *hdw) +{ + if (hdw->state_pathway_ok) { + /* Nothing to do if pathway is already ok */ + return 0; + } + if (!hdw->state_pipeline_idle) { + /* Not allowed to change anything if pipeline is not idle */ + return 0; + } + pvr2_hdw_cmd_modeswitch(hdw,hdw->input_val == PVR2_CVAL_INPUT_DTV); + hdw->state_pathway_ok = !0; + return !0; +} + + /* Evaluate whether or not state_encoder_ok can change */ static int state_eval_encoder_ok(struct pvr2_hdw *hdw) { @@ -3320,6 +3405,7 @@ static int state_eval_encoder_ok(struct pvr2_hdw *hdw) if (hdw->state_encoder_config) return 0; if (hdw->state_decoder_run) return 0; if (hdw->state_usbstream_run) return 0; + if (hdw->pathway_state != PVR2_PATHWAY_ANALOG) return 0; if (pvr2_upload_firmware2(hdw) < 0) { hdw->flag_tripped = !0; trace_stbit("flag_tripped",hdw->flag_tripped); @@ -3345,7 +3431,9 @@ static int state_eval_encoder_config(struct pvr2_hdw *hdw) /* paranoia - solve race if timer just completed */ del_timer_sync(&hdw->encoder_wait_timer); } else { - if (!hdw->state_encoder_ok || + if (!hdw->state_pathway_ok || + (hdw->pathway_state != PVR2_PATHWAY_ANALOG) || + !hdw->state_encoder_ok || !hdw->state_pipeline_idle || hdw->state_pipeline_pause || !hdw->state_pipeline_req || @@ -3399,13 +3487,16 @@ 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 (hdw->state_decoder_run && + hdw->state_pathway_ok) 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 (!hdw->state_pathway_ok) return 0; + if (hdw->pathway_state != PVR2_PATHWAY_ANALOG) return 0; if (pvr2_encoder_start(hdw) < 0) return !0; hdw->state_encoder_run = !0; } @@ -3442,7 +3533,8 @@ 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; + !hdw->state_pipeline_pause && + hdw->state_pathway_ok) return 0; } if (!hdw->flag_decoder_missed) { pvr2_decoder_enable(hdw,0); @@ -3475,7 +3567,9 @@ static int state_eval_decoder_run(struct pvr2_hdw *hdw) hopefully further stabilize the encoder. */ return 0; } - if (!hdw->state_pipeline_req || + if (!hdw->state_pathway_ok || + (hdw->pathway_state != PVR2_PATHWAY_ANALOG) || + !hdw->state_pipeline_req || hdw->state_pipeline_pause || !hdw->state_pipeline_config || !hdw->state_encoder_config || @@ -3496,16 +3590,25 @@ static int state_eval_decoder_run(struct pvr2_hdw *hdw) 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; + if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) { + if (hdw->state_encoder_ok && + hdw->state_encoder_run && + hdw->state_pathway_ok) return 0; + } else { + if (hdw->state_pipeline_req && + !hdw->state_pipeline_pause && + hdw->state_pathway_ok) 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 (!hdw->state_pipeline_req || + hdw->state_pipeline_pause || + !hdw->state_pathway_ok) return 0; + if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) { + if (!hdw->state_encoder_ok || + !hdw->state_encoder_run) return 0; + } if (pvr2_hdw_cmd_usbstream(hdw,!0) < 0) return 0; hdw->state_usbstream_run = !0; } @@ -3552,6 +3655,7 @@ 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_pathway_ok, state_eval_pipeline_config, state_eval_encoder_ok, state_eval_encoder_config, @@ -3599,6 +3703,16 @@ static int pvr2_hdw_state_update(struct pvr2_hdw *hdw) } +static const char *pvr2_pathway_state_name(int id) +{ + switch (id) { + case PVR2_PATHWAY_ANALOG: return "analog"; + case PVR2_PATHWAY_DIGITAL: return "digital"; + default: return "unknown"; + } +} + + static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which, char *buf,unsigned int acnt) { @@ -3606,13 +3720,15 @@ static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which, case 0: return scnprintf( buf,acnt, - "driver:%s%s%s%s%s", + "driver:%s%s%s%s%s", (hdw->flag_ok ? " " : " "), (hdw->flag_init_ok ? " " : " "), (hdw->flag_disconnected ? " " : " "), (hdw->flag_tripped ? " " : ""), - (hdw->flag_decoder_missed ? " " : "")); + (hdw->flag_decoder_missed ? " " : ""), + pvr2_pathway_state_name(hdw->pathway_state)); + case 1: return scnprintf( buf,acnt, @@ -3625,7 +3741,7 @@ static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which, case 2: return scnprintf( buf,acnt, - "worker:%s%s%s%s%s%s", + "worker:%s%s%s%s%s%s%s", (hdw->state_decoder_run ? " " : (hdw->state_decoder_quiescent ? @@ -3641,7 +3757,9 @@ static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which, (hdw->state_encoder_waitok ? "" : " ")), (hdw->state_usbstream_run ? - " " : " ")); + " " : " "), + (hdw->state_pathway_ok ? + "" : "")); break; case 3: return scnprintf( @@ -3713,9 +3831,9 @@ static int pvr2_hdw_state_eval(struct pvr2_hdw *hdw) 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) { + } else if (hdw->state_usbstream_run && + ((hdw->pathway_state != PVR2_PATHWAY_ANALOG) || + (hdw->state_encoder_run && hdw->state_decoder_run))) { st = PVR2_STATE_RUN; } else { st = PVR2_STATE_READY; -- cgit v1.2.3 From e9db1ff23507d3e430db2bd130bd7861baa8c87e Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Tue, 22 Apr 2008 14:45:41 -0300 Subject: V4L/DVB (7310): pvrusb2: trace print cosmetic cleanup / improvements Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 0941f0b434b..85e61d4b644 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -2394,6 +2394,7 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw) hdw->pathway_state)) { /* Change of mode being asked for... */ hdw->state_pathway_ok = 0; + trace_stbit("state_pathway_ok",hdw->state_pathway_ok); } if (!hdw->state_pathway_ok) { /* Can't commit anything until pathway is ok. */ @@ -3392,6 +3393,7 @@ static int state_eval_pathway_ok(struct pvr2_hdw *hdw) } pvr2_hdw_cmd_modeswitch(hdw,hdw->input_val == PVR2_CVAL_INPUT_DTV); hdw->state_pathway_ok = !0; + trace_stbit("state_pathway_ok",hdw->state_pathway_ok); return !0; } @@ -3720,7 +3722,7 @@ static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which, case 0: return scnprintf( buf,acnt, - "driver:%s%s%s%s%s", + "driver:%s%s%s%s%s ", (hdw->flag_ok ? " " : " "), (hdw->flag_init_ok ? " " : " "), (hdw->flag_disconnected ? " " : @@ -3759,7 +3761,7 @@ static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which, (hdw->state_usbstream_run ? " " : " "), (hdw->state_pathway_ok ? - "" : "")); + " " : "")); break; case 3: return scnprintf( -- cgit v1.2.3 From 1b9c18c54d68cc22f090948fc47890c56d22153d Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Tue, 22 Apr 2008 14:45:41 -0300 Subject: V4L/DVB (7311): pvrusb2: Allow digital streaming without encoder firmware The encoder is not a part of the pipeline when in digital mode, so streaming is OK in this case even when the encoder's firmware is not loaded. Modify the driver core handling of this scenario to permit streaming. Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 85e61d4b644..a85ffdaadcc 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -3352,6 +3352,7 @@ static void pvr2_hdw_cmd_modeswitch(struct pvr2_hdw *hdw,int digitalFl) default: break; } + pvr2_hdw_untrip_unlocked(hdw); hdw->pathway_state = cmode; } @@ -3814,6 +3815,7 @@ static int pvr2_hdw_state_eval(struct pvr2_hdw *hdw) unsigned int st; int state_updated = 0; int callback_flag = 0; + int analog_mode; pvr2_trace(PVR2_TRACE_STBITS, "Drive state check START"); @@ -3824,17 +3826,20 @@ static int pvr2_hdw_state_eval(struct pvr2_hdw *hdw) /* Process all state and get back over disposition */ state_updated = pvr2_hdw_state_update(hdw); + analog_mode = (hdw->pathway_state != PVR2_PATHWAY_DIGITAL); + /* 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) { + } else if (analog_mode && !hdw->state_encoder_ok) { st = PVR2_STATE_WARM; - } else if (hdw->flag_tripped || hdw->flag_decoder_missed) { + } else if (hdw->flag_tripped || + (analog_mode && hdw->flag_decoder_missed)) { st = PVR2_STATE_ERROR; } else if (hdw->state_usbstream_run && - ((hdw->pathway_state != PVR2_PATHWAY_ANALOG) || + (!analog_mode || (hdw->state_encoder_run && hdw->state_decoder_run))) { st = PVR2_STATE_RUN; } else { -- cgit v1.2.3 From c55a97d7538d5f3abbee5486e37e56e896478fbd Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Tue, 22 Apr 2008 14:45:41 -0300 Subject: V4L/DVB (7312): pvrusb2: Indicate streaming status via LED Most of this originates from Michael Krufky ; these changes move LED control into separate functions. This is the first step in new work to make LED control a device-specific attribute. Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index a85ffdaadcc..9b1e22f2e55 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -3357,6 +3357,27 @@ static void pvr2_hdw_cmd_modeswitch(struct pvr2_hdw *hdw,int digitalFl) } +/* Toggle LED */ +int pvr2_led_ctrl(struct pvr2_hdw *hdw, int onoff) +{ + /* change some GPIO data + * + * note: bit d7 of dir appears to control the LED, + * so we shut it off here. + * + * FIXME: is this device-specific? + */ + if (onoff) + pvr2_hdw_gpio_chg_dir(hdw, 0xffffffff, 0x00000481); + else + pvr2_hdw_gpio_chg_dir(hdw, 0xffffffff, 0x00000401); + + pvr2_hdw_gpio_chg_out(hdw, 0xffffffff, 0x00000000); + + return 0; +} + + /* Stop / start video stream transport */ static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl) { -- cgit v1.2.3 From 40381cb02fb7fc0b46c55e3a71325b5d930580fa Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Tue, 22 Apr 2008 14:45:42 -0300 Subject: V4L/DVB (7313): pvrusb2: Make LED control into a device-specific attribute The pvrusb2 driver has used hardcoded logic to control the LED on the device. However this is really Hauppauge-specific behavior. This change defines a new device attribute for LED control and sets things up appropriately for Hauppauge devices. Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 39 +++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 7 deletions(-) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 9b1e22f2e55..c51c5cef82d 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -3357,24 +3357,48 @@ static void pvr2_hdw_cmd_modeswitch(struct pvr2_hdw *hdw,int digitalFl) } -/* Toggle LED */ -int pvr2_led_ctrl(struct pvr2_hdw *hdw, int onoff) +void pvr2_led_ctrl_hauppauge(struct pvr2_hdw *hdw, int onoff) { /* change some GPIO data * * note: bit d7 of dir appears to control the LED, * so we shut it off here. * - * FIXME: is this device-specific? */ - if (onoff) + if (onoff) { pvr2_hdw_gpio_chg_dir(hdw, 0xffffffff, 0x00000481); - else + } else { pvr2_hdw_gpio_chg_dir(hdw, 0xffffffff, 0x00000401); - + } pvr2_hdw_gpio_chg_out(hdw, 0xffffffff, 0x00000000); +} - return 0; + +typedef void (*led_method_func)(struct pvr2_hdw *,int); + +static led_method_func led_methods[] = { + [PVR2_LED_SCHEME_HAUPPAUGE] = pvr2_led_ctrl_hauppauge, +}; + + +/* Toggle LED */ +static void pvr2_led_ctrl(struct pvr2_hdw *hdw,int onoff) +{ + unsigned int scheme_id; + led_method_func fp; + + if ((!onoff) == (!hdw->led_on)) return; + + hdw->led_on = onoff != 0; + + scheme_id = hdw->hdw_desc->led_scheme; + if (scheme_id < ARRAY_SIZE(led_methods)) { + fp = led_methods[scheme_id]; + } else { + fp = NULL; + } + + if (fp) (*fp)(hdw,onoff); } @@ -3871,6 +3895,7 @@ static int pvr2_hdw_state_eval(struct pvr2_hdw *hdw) "Device state change from %s to %s", pvr2_get_state_name(hdw->master_state), pvr2_get_state_name(st)); + pvr2_led_ctrl(hdw,st == PVR2_STATE_RUN); hdw->master_state = st; state_updated = !0; callback_flag = !0; -- cgit v1.2.3 From c4a8828ddbf5fb445d2679ab006d5743540fc41a Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Tue, 22 Apr 2008 14:45:44 -0300 Subject: V4L/DVB (7319): pvrusb2: Close potential race condition during initialization There is a callback that is issued to into pvr2_context from pvr2_hdw after initialization is done. There was a probability that this callback could get missed. Fixed. Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index c51c5cef82d..f2d2677936b 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1813,8 +1813,23 @@ static void pvr2_hdw_setup(struct pvr2_hdw *hdw) } -/* Create and return a structure for interacting with the underlying - hardware */ +/* Perform second stage initialization. Set callback pointer first so that + we can avoid a possible initialization race (if the kernel thread runs + before the callback has been set). */ +void pvr2_hdw_initialize(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); + queue_work(hdw->workqueue,&hdw->workinit); +} + + +/* Create, set up, and return a structure for interacting with the + underlying hardware. */ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, const struct usb_device_id *devid) { @@ -2039,7 +2054,6 @@ 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) { @@ -2521,17 +2535,6 @@ static int pvr2_hdw_wait(struct pvr2_hdw *hdw,int 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) { -- cgit v1.2.3 From 8f59100a42576c49e2170e9dc04f8b7ac922a74d Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Tue, 22 Apr 2008 14:45:45 -0300 Subject: V4L/DVB (7320): pvrusb2: Eliminate timer race during tear-down The pvrusb2 tear-down logic was clearing two timers before stopping its internal work queue. That left a tiny window open where the work queue might run after the timers are stopped, possibly starting them again. This could lead to dangling pointers and an oops. Solution: Kill the work queue first, then delete the timers. Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index f2d2677936b..1dff2d04a5d 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -2114,13 +2114,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; } + del_timer_sync(&hdw->quiescent_timer); + del_timer_sync(&hdw->encoder_wait_timer); if (hdw->fw_buffer) { kfree(hdw->fw_buffer); hdw->fw_buffer = NULL; -- cgit v1.2.3 From 794b16072e00d0a40a8c773dd4319fb1e460a632 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Tue, 22 Apr 2008 14:45:45 -0300 Subject: V4L/DVB (7321): pvrusb2: Rework context handling and initialization This change significantly rearranges pvr2_context level initialization and operation: 1. A new kernel thread is set up for management of the context. 2. Destruction of the pvr2_context instance is moved into the kernel thread. No other context is able to remove the instance; doing this simplifies lock handling. 3. The callback into pvrusb2-main, which is used to trigger initialization of each interface, is now issued from this kernel thread. Previously it had been indirectly issued out of the work queue thread in pvr2_hdw, which led to deadlock issues if the interface needed to change a control setting (which in turn requires dispatch of another work queue entry). 4. Callbacks into the interfaces (via the pvr2_channel structure) are now issued strictly from this thread. The net result of this is that such callback functions can now also safely operate driver controls without deadlocking the work queue. (At the moment this is not actually a problem, but I'm anticipating issues with this in the future). 5. There is no longer any need for anyone to enter / exit the pvr2_context structure. Implementation of the kernel thread here allows this all to be internal now, simplifying other logic. 6. A very very longstanding issue involving a mutex deadlock between the pvrusb2 driver and v4l should now be solved. The deadlock involved the pvr2_context mutex and a globals-protecting mutex in v4l. During initialization the driver would take the pvr2_context mutex first then the v4l2 interface would register with v4l and implicitly take the v4l mutex. Later when v4l would call back into the driver, the two mutexes could possibly be taken in the opposite order, a situation that can lead to deadlock. In practice this really wasn't an issue unless a v4l app tried to start VERY early after the driver appeared. However it still needed to be solved, and with the use of the kernel thread relieving need for pvr2_context mutex, the problem should be finally solved. Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 1dff2d04a5d..7e7c570cf83 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -221,7 +221,6 @@ 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 *); @@ -1816,15 +1815,16 @@ static void pvr2_hdw_setup(struct pvr2_hdw *hdw) /* Perform second stage initialization. Set callback pointer first so that we can avoid a possible initialization race (if the kernel thread runs before the callback has been set). */ -void pvr2_hdw_initialize(struct pvr2_hdw *hdw, - void (*callback_func)(void *), - void *callback_data) +int pvr2_hdw_initialize(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); - queue_work(hdw->workqueue,&hdw->workinit); + pvr2_hdw_setup(hdw); + return hdw->flag_init_ok; } @@ -2032,7 +2032,6 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, 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); @@ -2517,15 +2516,6 @@ static void pvr2_hdw_worker_poll(struct work_struct *work) } -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 { - pvr2_hdw_setup(hdw); - } while (0); LOCK_GIVE(hdw->big_lock); -} - - static int pvr2_hdw_wait(struct pvr2_hdw *hdw,int state) { return wait_event_interruptible( -- cgit v1.2.3 From ebff033039b654b7b5493499babe22c7c1b0d36e Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Tue, 22 Apr 2008 14:45:45 -0300 Subject: V4L/DVB (7322): pvrusb2: Fix storage-class as per C99 spec The C99 specification states in section 6.11.5: The placement of a storage-class specifier other than at the beginning of the declaration specifiers in a declaration is an obsolescent feature. Signed-off-by: Tobias Klauser Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 7e7c570cf83..8bdb807b19b 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1502,7 +1502,7 @@ struct pvr2_std_hack { default - which can always be overridden explicitly - and if the user has otherwise named a default then that default will always be used in place of this table. */ -const static struct pvr2_std_hack std_eeprom_maps[] = { +static const struct pvr2_std_hack std_eeprom_maps[] = { { /* PAL(B/G) */ .pat = V4L2_STD_B|V4L2_STD_GH, .std = V4L2_STD_PAL_B|V4L2_STD_PAL_B1|V4L2_STD_PAL_G, @@ -3695,7 +3695,7 @@ static int state_update_pipeline_state(struct pvr2_hdw *hdw) 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[] = { +static const state_eval_func eval_funcs[] = { state_eval_pathway_ok, state_eval_pipeline_config, state_eval_encoder_ok, -- cgit v1.2.3 From 98e184d978c9fdd294c169e468ab4a3e51a8be2d Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Fri, 28 Mar 2008 05:28:20 -0300 Subject: V4L/DVB (7695): pvrusb2: Make associativity of == and && explicit (cosmetic) Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 8bdb807b19b..6a820b713fe 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -3400,8 +3400,8 @@ static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl) { int status,cc; if ((hdw->pathway_state == PVR2_PATHWAY_DIGITAL) && - hdw->hdw_desc->digital_control_scheme == - PVR2_DIGITAL_SCHEME_HAUPPAUGE) { + (hdw->hdw_desc->digital_control_scheme == + PVR2_DIGITAL_SCHEME_HAUPPAUGE)) { cc = (runFl ? FX2CMD_HCW_DTV_STREAMING_ON : FX2CMD_HCW_DTV_STREAMING_OFF); -- cgit v1.2.3 From 38d9a2cf2db30b37dde81ec5f8b4de0fd9843ccc Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Fri, 28 Mar 2008 05:30:48 -0300 Subject: V4L/DVB (7696): pvrusb2: state control tweak Don't trigger a pathway state change if it's already been triggered (eliminates some wasted processing and some debug output noise) Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 6a820b713fe..307a38d91c8 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -2401,7 +2401,7 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw) } } - if (hdw->input_dirty && + if (hdw->input_dirty && hdw->state_pathway_ok && (((hdw->input_val == PVR2_CVAL_INPUT_DTV) ? PVR2_PATHWAY_DIGITAL : PVR2_PATHWAY_ANALOG) != hdw->pathway_state)) { -- cgit v1.2.3 From b9a37d9124dc834f2448558dbbe766eee077b954 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Fri, 28 Mar 2008 05:31:40 -0300 Subject: V4L/DVB (7697): pvrusb2: Fix misleading bit of debug output (trivial) Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 307a38d91c8..4329e4366f9 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -3796,7 +3796,7 @@ static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which, (hdw->state_encoder_config ? " " : (hdw->state_encoder_waitok ? - "" : " ")), + "" : " ")), (hdw->state_usbstream_run ? " " : " "), (hdw->state_pathway_ok ? -- cgit v1.2.3 From be9cbb7c559eddea19604abafb89faf9c8666715 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Fri, 28 Mar 2008 05:32:23 -0300 Subject: V4L/DVB (7698): pvrusb2: Remove never-reached break statements (trivial) Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 4329e4366f9..16c7df9c093 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -3801,13 +3801,11 @@ static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which, " " : " "), (hdw->state_pathway_ok ? " " : "")); - break; case 3: return scnprintf( buf,acnt, "state: %s", pvr2_get_state_name(hdw->master_state)); - break; default: break; } return 0; -- cgit v1.2.3 From ad0992e97c5416e431e19dcfd4f6c84448dc1bc2 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Fri, 28 Mar 2008 05:34:45 -0300 Subject: V4L/DVB (7699): pvrusb2: Implement statistics for USB I/O performance / tracking Implement a mechanism in the pvrusb2 driver for gathering statistics on the stream buffering, including bytes transferred, buffers handled, buffers in flight, etc. This is useful for debugging certain classes of streaming issues and for determining if the buffer pool size is generally correct for the driver. Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 16c7df9c093..a26b5251ec1 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -3806,6 +3806,24 @@ static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which, buf,acnt, "state: %s", pvr2_get_state_name(hdw->master_state)); + case 4: { + struct pvr2_stream_stats stats; + if (!hdw->vid_stream) break; + pvr2_stream_get_stats(hdw->vid_stream, + &stats, + 0); + return scnprintf( + buf,acnt, + "Bytes streamed=%u" + " URBs: queued=%u idle=%u ready=%u" + " processed=%u failed=%u", + stats.bytes_processed, + stats.buffers_in_queue, + stats.buffers_in_idle, + stats.buffers_in_ready, + stats.buffers_processed, + stats.buffers_failed); + } default: break; } return 0; -- cgit v1.2.3 From 1c9d10d4d2e791a9eacd9f919dec78c5bc6737e8 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Fri, 28 Mar 2008 05:38:54 -0300 Subject: V4L/DVB (7701): pvrusb2: Centralize handling of simple FX2 commands Numerous places in the driver need to issue simple commands to the FX2 microcontroller (e.g. only 1 or 2 bytes, no reply needed). Previously each place that did this, had to take lock, set up a central buffer, and call the function to perform the handshake. This change puts these steps into a single spot. This also has the effect of removing the need to mess with the control lock from numerous places in the code. Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 198 +++++++++++++++++------------- 1 file changed, 116 insertions(+), 82 deletions(-) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index a26b5251ec1..063aed0067c 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -216,6 +216,39 @@ static const char *pvr2_state_names[] = { }; +struct pvr2_fx2cmd_desc { + unsigned char id; + unsigned char *desc; +}; + +static const struct pvr2_fx2cmd_desc pvr2_fx2cmd_desc[] = { + {FX2CMD_MEM_WRITE_DWORD, "write encoder dword"}, + {FX2CMD_MEM_READ_DWORD, "read encoder dword"}, + {FX2CMD_MEM_READ_64BYTES, "read encoder 64bytes"}, + {FX2CMD_REG_WRITE, "write encoder register"}, + {FX2CMD_REG_READ, "read encoder register"}, + {FX2CMD_MEMSEL, "encoder memsel"}, + {FX2CMD_I2C_WRITE, "i2c write"}, + {FX2CMD_I2C_READ, "i2c read"}, + {FX2CMD_GET_USB_SPEED, "get USB speed"}, + {FX2CMD_STREAMING_ON, "stream on"}, + {FX2CMD_STREAMING_OFF, "stream off"}, + {FX2CMD_FWPOST1, "fwpost1"}, + {FX2CMD_POWER_OFF, "power off"}, + {FX2CMD_POWER_ON, "power on"}, + {FX2CMD_DEEP_RESET, "deep reset"}, + {FX2CMD_GET_EEPROM_ADDR, "get rom addr"}, + {FX2CMD_GET_IR_CODE, "get IR code"}, + {FX2CMD_HCW_DEMOD_RESETIN, "hcw demod resetin"}, + {FX2CMD_HCW_DTV_STREAMING_ON, "hcw dtv stream on"}, + {FX2CMD_HCW_DTV_STREAMING_OFF, "hcw dtv stream off"}, + {FX2CMD_ONAIR_DTV_STREAMING_ON, "onair dtv stream on"}, + {FX2CMD_ONAIR_DTV_STREAMING_OFF, "onair dtv stream off"}, + {FX2CMD_ONAIR_DTV_POWER_ON, "onair dtv power on"}, + {FX2CMD_ONAIR_DTV_POWER_OFF, "onair dtv power off"}, +}; + + 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); @@ -231,6 +264,7 @@ 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_quiescent_timeout(unsigned long); static void pvr2_hdw_encoder_wait_timeout(unsigned long); +static int pvr2_issue_simple_cmd(struct pvr2_hdw *,u32); static int pvr2_send_request_ex(struct pvr2_hdw *hdw, unsigned int timeout,int probe_fl, void *write_data,unsigned int write_len, @@ -1219,13 +1253,8 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) ret |= pvr2_write_register(hdw, 0xaa04, 0x00057810); /*unknown*/ ret |= pvr2_write_register(hdw, 0xaa10, 0x00148500); /*unknown*/ ret |= pvr2_write_register(hdw, 0xaa18, 0x00840000); /*unknown*/ - LOCK_TAKE(hdw->ctl_lock); do { - hdw->cmd_buffer[0] = FX2CMD_FWPOST1; - ret |= pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0); - hdw->cmd_buffer[0] = FX2CMD_MEMSEL; - hdw->cmd_buffer[1] = 0; - ret |= pvr2_send_request(hdw,hdw->cmd_buffer,2,NULL,0); - } while (0); LOCK_GIVE(hdw->ctl_lock); + ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_FWPOST1); + ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_MEMSEL | (1 << 8) | (0 << 16)); if (ret) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, @@ -1290,11 +1319,7 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) ret |= pvr2_write_register(hdw, 0x9054, 0xffffffff); /*reset hw blocks*/ ret |= pvr2_write_register(hdw, 0x9058, 0xffffffe8); /*VPU ctrl*/ - LOCK_TAKE(hdw->ctl_lock); do { - hdw->cmd_buffer[0] = FX2CMD_MEMSEL; - hdw->cmd_buffer[1] = 0; - ret |= pvr2_send_request(hdw,hdw->cmd_buffer,2,NULL,0); - } while (0); LOCK_GIVE(hdw->ctl_lock); + ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_MEMSEL | (1 << 8) | (0 << 16)); if (ret) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, @@ -3093,6 +3118,67 @@ int pvr2_send_request(struct pvr2_hdw *hdw, read_data,read_len); } + +static int pvr2_issue_simple_cmd(struct pvr2_hdw *hdw,u32 cmdcode) +{ + int ret; + unsigned int cnt = 1; + unsigned int args = 0; + LOCK_TAKE(hdw->ctl_lock); + hdw->cmd_buffer[0] = cmdcode & 0xffu; + args = (cmdcode >> 8) & 0xffu; + args = (args > 2) ? 2 : args; + if (args) { + cnt += args; + hdw->cmd_buffer[1] = (cmdcode >> 16) & 0xffu; + if (args > 1) { + hdw->cmd_buffer[2] = (cmdcode >> 24) & 0xffu; + } + } + if (pvrusb2_debug & PVR2_TRACE_INIT) { + unsigned int idx; + unsigned int ccnt,bcnt; + char tbuf[50]; + cmdcode &= 0xffu; + bcnt = 0; + ccnt = scnprintf(tbuf+bcnt, + sizeof(tbuf)-bcnt, + "Sending FX2 command 0x%x",cmdcode); + bcnt += ccnt; + for (idx = 0; idx < ARRAY_SIZE(pvr2_fx2cmd_desc); idx++) { + if (pvr2_fx2cmd_desc[idx].id == cmdcode) { + ccnt = scnprintf(tbuf+bcnt, + sizeof(tbuf)-bcnt, + " \"%s\"", + pvr2_fx2cmd_desc[idx].desc); + bcnt += ccnt; + break; + } + } + if (args) { + ccnt = scnprintf(tbuf+bcnt, + sizeof(tbuf)-bcnt, + " (%u",hdw->cmd_buffer[1]); + bcnt += ccnt; + if (args > 1) { + ccnt = scnprintf(tbuf+bcnt, + sizeof(tbuf)-bcnt, + ",%u",hdw->cmd_buffer[2]); + bcnt += ccnt; + } + ccnt = scnprintf(tbuf+bcnt, + sizeof(tbuf)-bcnt, + ")"); + bcnt += ccnt; + } + pvr2_trace(PVR2_TRACE_INIT,"%.*s",bcnt,tbuf); + } + ret = pvr2_send_request(hdw,hdw->cmd_buffer,cnt,NULL,0); + LOCK_GIVE(hdw->ctl_lock); + return ret; +} + + int pvr2_write_register(struct pvr2_hdw *hdw, u16 reg, u32 data) { int ret; @@ -3200,40 +3286,19 @@ void pvr2_hdw_cpureset_assert(struct pvr2_hdw *hdw,int val) 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->cmd_buffer[0] = FX2CMD_DEEP_RESET; - status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0); - } while (0); LOCK_GIVE(hdw->ctl_lock); - return status; + return pvr2_issue_simple_cmd(hdw,FX2CMD_DEEP_RESET); } -static int pvr2_hdw_cmd_power_ctrl(struct pvr2_hdw *hdw, int onoff) -{ - int status; - LOCK_TAKE(hdw->ctl_lock); do { - if (onoff) { - pvr2_trace(PVR2_TRACE_INIT, "Requesting powerup"); - hdw->cmd_buffer[0] = FX2CMD_POWER_ON; - } else { - pvr2_trace(PVR2_TRACE_INIT, "Requesting powerdown"); - hdw->cmd_buffer[0] = FX2CMD_POWER_OFF; - } - status = pvr2_send_request(hdw, hdw->cmd_buffer, 1, NULL, 0); - } while (0); LOCK_GIVE(hdw->ctl_lock); - return status; -} - int pvr2_hdw_cmd_powerup(struct pvr2_hdw *hdw) { - return pvr2_hdw_cmd_power_ctrl(hdw, 1); + return pvr2_issue_simple_cmd(hdw,FX2CMD_POWER_ON); } + int pvr2_hdw_cmd_powerdown(struct pvr2_hdw *hdw) { - return pvr2_hdw_cmd_power_ctrl(hdw, 0); + return pvr2_issue_simple_cmd(hdw,FX2CMD_POWER_OFF); } @@ -3260,55 +3325,29 @@ int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw) static int pvr2_hdw_cmd_hcw_demod_reset(struct pvr2_hdw *hdw, int onoff) { - int status; - - LOCK_TAKE(hdw->ctl_lock); do { - pvr2_trace(PVR2_TRACE_INIT, - "Issuing fe demod wake command (%s)", - (onoff ? "on" : "off")); - hdw->flag_ok = !0; - hdw->cmd_buffer[0] = FX2CMD_HCW_DEMOD_RESETIN; - hdw->cmd_buffer[1] = onoff; - status = pvr2_send_request(hdw, hdw->cmd_buffer, 2, NULL, 0); - } while (0); LOCK_GIVE(hdw->ctl_lock); - - return status; + hdw->flag_ok = !0; + return pvr2_issue_simple_cmd(hdw, + FX2CMD_HCW_DEMOD_RESETIN | + (1 << 8) | + ((onoff ? 1 : 0) << 16)); } static int pvr2_hdw_cmd_onair_fe_power_ctrl(struct pvr2_hdw *hdw, int onoff) { - int status; - - LOCK_TAKE(hdw->ctl_lock); do { - pvr2_trace(PVR2_TRACE_INIT, - "Issuing fe power command to CPLD (%s)", - (onoff ? "on" : "off")); - hdw->flag_ok = !0; - hdw->cmd_buffer[0] = - (onoff ? FX2CMD_ONAIR_DTV_POWER_ON : - FX2CMD_ONAIR_DTV_POWER_OFF); - status = pvr2_send_request(hdw, hdw->cmd_buffer, 1, NULL, 0); - } while (0); LOCK_GIVE(hdw->ctl_lock); - - return status; + hdw->flag_ok = !0; + return pvr2_issue_simple_cmd(hdw,(onoff ? + FX2CMD_ONAIR_DTV_POWER_ON : + FX2CMD_ONAIR_DTV_POWER_OFF)); } static int pvr2_hdw_cmd_onair_digital_path_ctrl(struct pvr2_hdw *hdw, int onoff) { - int status; - LOCK_TAKE(hdw->ctl_lock); do { - pvr2_trace(PVR2_TRACE_INIT, - "Issuing onair digital setup command (%s)", - (onoff ? "on" : "off")); - hdw->cmd_buffer[0] = - (onoff ? FX2CMD_ONAIR_DTV_STREAMING_ON : - FX2CMD_ONAIR_DTV_STREAMING_OFF); - status = pvr2_send_request(hdw, hdw->cmd_buffer, 1, NULL, 0); - } while (0); LOCK_GIVE(hdw->ctl_lock); - return status; + return pvr2_issue_simple_cmd(hdw,(onoff ? + FX2CMD_ONAIR_DTV_STREAMING_ON : + FX2CMD_ONAIR_DTV_STREAMING_OFF)); } @@ -3398,7 +3437,7 @@ static void pvr2_led_ctrl(struct pvr2_hdw *hdw,int onoff) /* Stop / start video stream transport */ static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl) { - int status,cc; + int cc; if ((hdw->pathway_state == PVR2_PATHWAY_DIGITAL) && (hdw->hdw_desc->digital_control_scheme == PVR2_DIGITAL_SCHEME_HAUPPAUGE)) { @@ -3410,12 +3449,7 @@ static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl) FX2CMD_STREAMING_ON : FX2CMD_STREAMING_OFF); } - - LOCK_TAKE(hdw->ctl_lock); do { - hdw->cmd_buffer[0] = cc; - status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0); - } while (0); LOCK_GIVE(hdw->ctl_lock); - return status; + return pvr2_issue_simple_cmd(hdw,cc); } -- cgit v1.2.3 From bb0c2fe0c423d6501395e626501dd8747823cafb Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Fri, 28 Mar 2008 05:41:19 -0300 Subject: V4L/DVB (7702): pvrusb2: Rework USB streaming start/stop execution The commands to start / stop USB streaming for an analog device are fairly standard, owing to the fact that all supported devices apparently started from the same common reference design. However with digital mode, the commands seem to vary by vendor. This change makes that variance more explicit. It also cleans up a related problem for OnAir devices which prevented digital mode from working at all. Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 52 ++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 15 deletions(-) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 063aed0067c..b711328400e 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -3377,9 +3377,7 @@ static void pvr2_hdw_cmd_modeswitch(struct pvr2_hdw *hdw,int digitalFl) /* Supposedly we should always have the power on whether in digital or analog mode. But for now do what appears to work... */ - if (digitalFl) pvr2_hdw_cmd_onair_fe_power_ctrl(hdw,!0); - pvr2_hdw_cmd_onair_digital_path_ctrl(hdw,digitalFl); - if (!digitalFl) pvr2_hdw_cmd_onair_fe_power_ctrl(hdw,0); + pvr2_hdw_cmd_onair_fe_power_ctrl(hdw,digitalFl); break; default: break; } @@ -3437,19 +3435,43 @@ static void pvr2_led_ctrl(struct pvr2_hdw *hdw,int onoff) /* Stop / start video stream transport */ static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl) { - int cc; - if ((hdw->pathway_state == PVR2_PATHWAY_DIGITAL) && - (hdw->hdw_desc->digital_control_scheme == - PVR2_DIGITAL_SCHEME_HAUPPAUGE)) { - cc = (runFl ? - FX2CMD_HCW_DTV_STREAMING_ON : - FX2CMD_HCW_DTV_STREAMING_OFF); - } else { - cc = (runFl ? - FX2CMD_STREAMING_ON : - FX2CMD_STREAMING_OFF); + int ret; + + /* If we're in analog mode, then just issue the usual analog + command. */ + if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) { + return pvr2_issue_simple_cmd(hdw, + (runFl ? + FX2CMD_STREAMING_ON : + FX2CMD_STREAMING_OFF)); + /*Note: Not reached */ + } + + if (hdw->pathway_state != PVR2_PATHWAY_DIGITAL) { + /* Whoops, we don't know what mode we're in... */ + return -EINVAL; + } + + /* To get here we have to be in digital mode. The mechanism here + is unfortunately different for different vendors. So we switch + on the device's digital scheme attribute in order to figure out + what to do. */ + switch (hdw->hdw_desc->digital_control_scheme) { + case PVR2_DIGITAL_SCHEME_HAUPPAUGE: + return pvr2_issue_simple_cmd(hdw, + (runFl ? + FX2CMD_HCW_DTV_STREAMING_ON : + FX2CMD_HCW_DTV_STREAMING_OFF)); + case PVR2_DIGITAL_SCHEME_ONAIR: + ret = pvr2_issue_simple_cmd(hdw, + (runFl ? + FX2CMD_STREAMING_ON : + FX2CMD_STREAMING_OFF)); + if (ret) return ret; + return pvr2_hdw_cmd_onair_digital_path_ctrl(hdw,runFl); + default: + return -EINVAL; } - return pvr2_issue_simple_cmd(hdw,cc); } -- cgit v1.2.3 From 694dca2b80d5e17f5edddacf4fcdacc1091623f2 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Fri, 28 Mar 2008 05:42:10 -0300 Subject: V4L/DVB (7703): pvrusb2: Fix minor problem involving ARRAY_SIZE confusion Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index b711328400e..195e974f5eb 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -216,12 +216,12 @@ static const char *pvr2_state_names[] = { }; -struct pvr2_fx2cmd_desc { +struct pvr2_fx2cmd_descdef { unsigned char id; unsigned char *desc; }; -static const struct pvr2_fx2cmd_desc pvr2_fx2cmd_desc[] = { +static const struct pvr2_fx2cmd_descdef pvr2_fx2cmd_desc[] = { {FX2CMD_MEM_WRITE_DWORD, "write encoder dword"}, {FX2CMD_MEM_READ_DWORD, "read encoder dword"}, {FX2CMD_MEM_READ_64BYTES, "read encoder 64bytes"}, -- cgit v1.2.3 From 72998b71096e364002269a8cacc0524937d479c6 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Thu, 3 Apr 2008 04:51:19 -0300 Subject: V4L/DVB (7709): pvrusb2: New device attribute for encoder usage in digital mode Some tuners seem to not work in digital mode unless the encoder is healthy. Implement a device attribute to represent this flag and modify the core state machines to enforce this requirement. Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 33 ++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 195e974f5eb..539fe17105e 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -3502,7 +3502,12 @@ static int state_eval_encoder_ok(struct pvr2_hdw *hdw) if (hdw->state_encoder_config) return 0; if (hdw->state_decoder_run) return 0; if (hdw->state_usbstream_run) return 0; - if (hdw->pathway_state != PVR2_PATHWAY_ANALOG) return 0; + if (hdw->pathway_state == PVR2_PATHWAY_DIGITAL) { + if (!hdw->hdw_desc->flag_digital_requires_cx23416) return 0; + } else if (hdw->pathway_state != PVR2_PATHWAY_ANALOG) { + return 0; + } + if (pvr2_upload_firmware2(hdw) < 0) { hdw->flag_tripped = !0; trace_stbit("flag_tripped",hdw->flag_tripped); @@ -3687,14 +3692,19 @@ static int state_eval_decoder_run(struct pvr2_hdw *hdw) static int state_eval_usbstream_run(struct pvr2_hdw *hdw) { if (hdw->state_usbstream_run) { + int fl = !0; if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) { - if (hdw->state_encoder_ok && - hdw->state_encoder_run && - hdw->state_pathway_ok) return 0; - } else { - if (hdw->state_pipeline_req && - !hdw->state_pipeline_pause && - hdw->state_pathway_ok) return 0; + fl = (hdw->state_encoder_ok && + hdw->state_encoder_run); + } else if ((hdw->pathway_state == PVR2_PATHWAY_DIGITAL) && + (hdw->hdw_desc->flag_digital_requires_cx23416)) { + fl = hdw->state_encoder_ok; + } + if (fl && + hdw->state_pipeline_req && + !hdw->state_pipeline_pause && + hdw->state_pathway_ok) { + return 0; } pvr2_hdw_cmd_usbstream(hdw,0); hdw->state_usbstream_run = 0; @@ -3705,6 +3715,9 @@ static int state_eval_usbstream_run(struct pvr2_hdw *hdw) if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) { if (!hdw->state_encoder_ok || !hdw->state_encoder_run) return 0; + } else if ((hdw->pathway_state == PVR2_PATHWAY_DIGITAL) && + (hdw->hdw_desc->flag_digital_requires_cx23416)) { + if (!hdw->state_encoder_ok) return 0; } if (pvr2_hdw_cmd_usbstream(hdw,!0) < 0) return 0; hdw->state_usbstream_run = !0; @@ -3943,7 +3956,9 @@ static int pvr2_hdw_state_eval(struct pvr2_hdw *hdw) st = PVR2_STATE_DEAD; } else if (hdw->fw1_state != FW1_STATE_OK) { st = PVR2_STATE_COLD; - } else if (analog_mode && !hdw->state_encoder_ok) { + } else if ((analog_mode || + hdw->hdw_desc->flag_digital_requires_cx23416) && + !hdw->state_encoder_ok) { st = PVR2_STATE_WARM; } else if (hdw->flag_tripped || (analog_mode && hdw->flag_decoder_missed)) { -- cgit v1.2.3 From d913d6303072ca194919d851e6743ad8c3a7563d Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sun, 6 Apr 2008 04:04:35 -0300 Subject: V4L/DVB (7710): pvrusb2: Implement critical digital streaming quirk for onair devices Implement timed measurement of encoder operation for the first time it is run. This allows the driver to note when the encoder has been run successfully for at least 1/4 second. On top of that implement various bits to ensure that the encoder has been run once before digital streaming for OnAir devices. This is done via several core state machine tweaks. Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 150 ++++++++++++++++++++++++++++-- 1 file changed, 143 insertions(+), 7 deletions(-) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 539fe17105e..4f6bb58ca5f 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -264,6 +264,7 @@ 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_quiescent_timeout(unsigned long); static void pvr2_hdw_encoder_wait_timeout(unsigned long); +static void pvr2_hdw_encoder_run_timeout(unsigned long); static int pvr2_issue_simple_cmd(struct pvr2_hdw *,u32); static int pvr2_send_request_ex(struct pvr2_hdw *hdw, unsigned int timeout,int probe_fl, @@ -1236,6 +1237,14 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) time we configure the encoder, then we'll fully configure it. */ hdw->enc_cur_valid = 0; + /* Encoder is about to be reset so note that as far as we're + concerned now, the encoder has never been run. */ + del_timer_sync(&hdw->encoder_run_timer); + if (hdw->state_encoder_runok) { + hdw->state_encoder_runok = 0; + trace_stbit("state_encoder_runok",hdw->state_encoder_runok); + } + /* First prepare firmware loading */ ret |= pvr2_write_register(hdw, 0x0048, 0xffffffff); /*interrupt mask*/ ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000088); /*gpio dir*/ @@ -1882,6 +1891,10 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, hdw->encoder_wait_timer.data = (unsigned long)hdw; hdw->encoder_wait_timer.function = pvr2_hdw_encoder_wait_timeout; + init_timer(&hdw->encoder_run_timer); + hdw->encoder_run_timer.data = (unsigned long)hdw; + hdw->encoder_run_timer.function = pvr2_hdw_encoder_run_timeout; + hdw->master_state = PVR2_STATE_DEAD; init_waitqueue_head(&hdw->state_wait_data); @@ -2082,6 +2095,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, fail: if (hdw) { del_timer_sync(&hdw->quiescent_timer); + del_timer_sync(&hdw->encoder_run_timer); del_timer_sync(&hdw->encoder_wait_timer); if (hdw->workqueue) { flush_workqueue(hdw->workqueue); @@ -2144,6 +2158,7 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw) hdw->workqueue = NULL; } del_timer_sync(&hdw->quiescent_timer); + del_timer_sync(&hdw->encoder_run_timer); del_timer_sync(&hdw->encoder_wait_timer); if (hdw->fw_buffer) { kfree(hdw->fw_buffer); @@ -3584,23 +3599,116 @@ static int state_eval_encoder_config(struct pvr2_hdw *hdw) } +/* Return true if the encoder should not be running. */ +static int state_check_disable_encoder_run(struct pvr2_hdw *hdw) +{ + if (!hdw->state_encoder_ok) { + /* Encoder isn't healthy at the moment, so stop it. */ + return !0; + } + if (!hdw->state_pathway_ok) { + /* Mode is not understood at the moment (i.e. it wants to + change), so encoder must be stopped. */ + return !0; + } + + switch (hdw->pathway_state) { + case PVR2_PATHWAY_ANALOG: + if (!hdw->state_decoder_run) { + /* We're in analog mode and the decoder is not + running; thus the encoder should be stopped as + well. */ + return !0; + } + break; + case PVR2_PATHWAY_DIGITAL: + if (hdw->state_encoder_runok) { + /* This is a funny case. We're in digital mode so + really the encoder should be stopped. However + if it really is running, only kill it after + runok has been set. This gives a chance for the + onair quirk to function (encoder must run + briefly first, at least once, before onair + digital streaming can work). */ + return !0; + } + break; + default: + /* Unknown mode; so encoder should be stopped. */ + return !0; + } + + /* If we get here, we haven't found a reason to stop the + encoder. */ + return 0; +} + + +/* Return true if the encoder should be running. */ +static int state_check_enable_encoder_run(struct pvr2_hdw *hdw) +{ + if (!hdw->state_encoder_ok) { + /* Don't run the encoder if it isn't healthy... */ + return 0; + } + if (!hdw->state_pathway_ok) { + /* Don't run the encoder if we don't (yet) know what mode + we need to be in... */ + return 0; + } + + switch (hdw->pathway_state) { + case PVR2_PATHWAY_ANALOG: + if (hdw->state_decoder_run) { + /* In analog mode, if the decoder is running, then + run the encoder. */ + return !0; + } + break; + case PVR2_PATHWAY_DIGITAL: + if ((hdw->hdw_desc->digital_control_scheme == + PVR2_DIGITAL_SCHEME_ONAIR) && + !hdw->state_encoder_runok) { + /* This is a quirk. OnAir hardware won't stream + digital until the encoder has been run at least + once, for a minimal period of time (empiricially + measured to be 1/4 second). So if we're on + OnAir hardware and the encoder has never been + run at all, then start the encoder. Normal + state machine logic in the driver will + automatically handle the remaining bits. */ + return !0; + } + break; + default: + /* For completeness (unknown mode; encoder won't run ever) */ + break; + } + /* If we get here, then we haven't found any reason to run the + encoder, so don't run it. */ + 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 (!state_check_disable_encoder_run(hdw)) return 0; if (hdw->state_encoder_ok) { - if (hdw->state_decoder_run && - hdw->state_pathway_ok) return 0; + del_timer_sync(&hdw->encoder_run_timer); 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 (!hdw->state_pathway_ok) return 0; - if (hdw->pathway_state != PVR2_PATHWAY_ANALOG) return 0; + if (!state_check_enable_encoder_run(hdw)) return 0; if (pvr2_encoder_start(hdw) < 0) return !0; hdw->state_encoder_run = !0; + if (!hdw->state_encoder_runok) { + hdw->encoder_run_timer.expires = + jiffies + (HZ*250/1000); + add_timer(&hdw->encoder_run_timer); + } } trace_stbit("state_encoder_run",hdw->state_encoder_run); return !0; @@ -3629,6 +3737,19 @@ static void pvr2_hdw_encoder_wait_timeout(unsigned long data) } +/* Timeout function for encoder run timer. */ +static void pvr2_hdw_encoder_run_timeout(unsigned long data) +{ + struct pvr2_hdw *hdw = (struct pvr2_hdw *)data; + if (!hdw->state_encoder_runok) { + hdw->state_encoder_runok = !0; + trace_stbit("state_encoder_runok",hdw->state_encoder_runok); + 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) { @@ -3718,6 +3839,16 @@ static int state_eval_usbstream_run(struct pvr2_hdw *hdw) } else if ((hdw->pathway_state == PVR2_PATHWAY_DIGITAL) && (hdw->hdw_desc->flag_digital_requires_cx23416)) { if (!hdw->state_encoder_ok) return 0; + if (hdw->state_encoder_run) return 0; + if (hdw->hdw_desc->digital_control_scheme == + PVR2_DIGITAL_SCHEME_ONAIR) { + /* OnAir digital receivers won't stream + unless the analog encoder has run first. + Why? I have no idea. But don't even + try until we know the analog side is + known to have run. */ + if (!hdw->state_encoder_runok) return 0; + } } if (pvr2_hdw_cmd_usbstream(hdw,!0) < 0) return 0; hdw->state_usbstream_run = !0; @@ -3861,7 +3992,12 @@ static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which, (hdw->state_encoder_ok ? "" : " "), (hdw->state_encoder_run ? - " " : " "), + (hdw->state_encoder_runok ? + " " : + " ") : + (hdw->state_encoder_runok ? + " " : + " ")), (hdw->state_encoder_config ? " " : (hdw->state_encoder_waitok ? -- cgit v1.2.3 From 97f26ff6049a7fff5460cebe392ad1d699dc434c Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Mon, 7 Apr 2008 02:22:43 -0300 Subject: V4L/DVB (7712): pvrusb2: Close connect/disconnect race If a disconnect happens before initialization is completed, the pvrusb2 driver can accidentally touch dangling pointers. The whole initialization function must be protected by the big_lock, and once inside that lock, the initialization function should abort if it is discovered that a disconnect has already taken place. Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 4f6bb58ca5f..f907a56c587 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1854,10 +1854,19 @@ int pvr2_hdw_initialize(struct pvr2_hdw *hdw, void *callback_data) { LOCK_TAKE(hdw->big_lock); do { + if (hdw->flag_disconnected) { + /* Handle a race here: If we're already + disconnected by this point, then give up. If we + get past this then we'll remain connected for + the duration of initialization since the entire + initialization sequence is now protected by the + big_lock. */ + break; + } hdw->state_data = callback_data; hdw->state_func = callback_func; + pvr2_hdw_setup(hdw); } while (0); LOCK_GIVE(hdw->big_lock); - pvr2_hdw_setup(hdw); return hdw->flag_init_ok; } -- cgit v1.2.3 From f55a871241899ea2ecc85670d7c9a83e4de29637 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 18 Apr 2008 05:38:56 -0300 Subject: V4L/DVB (7716): pvrusb2: clean up global functions This patch contains the following cleanups: - make the following needlessly global function static: - pvr2_hdw_set_cur_freq() - #if 0 the following unused global functions: - pvr2_hdw_get_state_name() - pvr2_hdw_get_debug_info_unlocked() - pvr2_hdw_get_debug_info_locked() Signed-off-by: Adrian Bunk Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 48 +------------------------------ 1 file changed, 1 insertion(+), 47 deletions(-) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index f907a56c587..63d0af759ed 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1024,7 +1024,7 @@ unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *hdw) /* Set the currently tuned frequency and account for all possible driver-core side effects of this action. */ -void pvr2_hdw_set_cur_freq(struct pvr2_hdw *hdw,unsigned long val) +static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *hdw,unsigned long val) { if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { if (hdw->freqSelector) { @@ -1405,11 +1405,6 @@ int pvr2_hdw_untrip(struct pvr2_hdw *hdw) } -const char *pvr2_hdw_get_state_name(unsigned int id) -{ - if (id >= ARRAY_SIZE(pvr2_state_names)) return NULL; - return pvr2_state_names[id]; -} int pvr2_hdw_get_streaming(struct pvr2_hdw *hdw) @@ -4150,47 +4145,6 @@ static void pvr2_hdw_state_sched(struct pvr2_hdw *hdw) } -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_disconnected = hdw->flag_disconnected; - ptr->flag_init_ok = hdw->flag_init_ok; - 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; - ptr->cmd_debug_read_len = hdw->cmd_debug_read_len; - ptr->cmd_debug_timeout = hdw->ctl_timeout_flag; - ptr->cmd_debug_write_pend = hdw->ctl_write_pend_flag; - ptr->cmd_debug_read_pend = hdw->ctl_read_pend_flag; - ptr->cmd_debug_rstatus = hdw->ctl_read_urb->status; - ptr->cmd_debug_wstatus = hdw->ctl_read_urb->status; -} - - -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); -- cgit v1.2.3 From 1cb03b76d09d20accfa5c1664c16ba6566f539a0 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Mon, 21 Apr 2008 03:47:43 -0300 Subject: V4L/DVB (7719): pvrusb2: Implement input selection enforcement In the pvrusb2 driver, different interfaces (e.g. V4L, DVB) have Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 132 +++++++++++++++++++++++++----- 1 file changed, 112 insertions(+), 20 deletions(-) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 63d0af759ed..72e9056557b 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -249,6 +249,7 @@ static const struct pvr2_fx2cmd_descdef pvr2_fx2cmd_desc[] = { }; +static int pvr2_hdw_set_input(struct pvr2_hdw *hdw,int v); 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); @@ -404,30 +405,12 @@ static int ctrl_get_input(struct pvr2_ctrl *cptr,int *vp) static int ctrl_check_input(struct pvr2_ctrl *cptr,int v) { - return ((1 << v) & cptr->hdw->input_avail_mask) != 0; + return ((1 << v) & cptr->hdw->input_allowed_mask) != 0; } static int ctrl_set_input(struct pvr2_ctrl *cptr,int m,int v) { - struct pvr2_hdw *hdw = cptr->hdw; - - if (hdw->input_val != v) { - hdw->input_val = v; - hdw->input_dirty = !0; - } - - /* Handle side effects - if we switch to a mode that needs the RF - tuner, then select the right frequency choice as well and mark - it dirty. */ - if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { - hdw->freqSelector = 0; - hdw->freqDirty = !0; - } else if ((hdw->input_val == PVR2_CVAL_INPUT_TV) || - (hdw->input_val == PVR2_CVAL_INPUT_DTV)) { - hdw->freqSelector = 1; - hdw->freqDirty = !0; - } - return 0; + return pvr2_hdw_set_input(cptr->hdw,v); } static int ctrl_isdirty_input(struct pvr2_ctrl *cptr) @@ -1916,6 +1899,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, if (hdw_desc->flag_has_composite) m |= 1 << PVR2_CVAL_INPUT_COMPOSITE; if (hdw_desc->flag_has_fmradio) m |= 1 << PVR2_CVAL_INPUT_RADIO; hdw->input_avail_mask = m; + hdw->input_allowed_mask = hdw->input_avail_mask; /* If not a hybrid device, pathway_state never changes. So initialize it here to what it should forever be. */ @@ -3948,6 +3932,24 @@ static int pvr2_hdw_state_update(struct pvr2_hdw *hdw) } +static unsigned int print_input_mask(unsigned int msk, + char *buf,unsigned int acnt) +{ + unsigned int idx,ccnt; + unsigned int tcnt = 0; + for (idx = 0; idx < ARRAY_SIZE(control_values_input); idx++) { + if (!((1 << idx) & msk)) continue; + ccnt = scnprintf(buf+tcnt, + acnt-tcnt, + "%s%s", + (tcnt ? ", " : ""), + control_values_input[idx]); + tcnt += ccnt; + } + return tcnt; +} + + static const char *pvr2_pathway_state_name(int id) { switch (id) { @@ -4016,6 +4018,28 @@ static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which, "state: %s", pvr2_get_state_name(hdw->master_state)); case 4: { + unsigned int tcnt = 0; + unsigned int ccnt; + + ccnt = scnprintf(buf, + acnt, + "Hardware supported inputs: "); + tcnt += ccnt; + tcnt += print_input_mask(hdw->input_avail_mask, + buf+tcnt, + acnt-tcnt); + if (hdw->input_avail_mask != hdw->input_allowed_mask) { + ccnt = scnprintf(buf+tcnt, + acnt-tcnt, + "; allowed inputs: "); + tcnt += ccnt; + tcnt += print_input_mask(hdw->input_allowed_mask, + buf+tcnt, + acnt-tcnt); + } + return tcnt; + } + case 5: { struct pvr2_stream_stats stats; if (!hdw->vid_stream) break; pvr2_stream_get_stats(hdw->vid_stream, @@ -4210,6 +4234,74 @@ unsigned int pvr2_hdw_get_input_available(struct pvr2_hdw *hdw) } +unsigned int pvr2_hdw_get_input_allowed(struct pvr2_hdw *hdw) +{ + return hdw->input_allowed_mask; +} + + +static int pvr2_hdw_set_input(struct pvr2_hdw *hdw,int v) +{ + if (hdw->input_val != v) { + hdw->input_val = v; + hdw->input_dirty = !0; + } + + /* Handle side effects - if we switch to a mode that needs the RF + tuner, then select the right frequency choice as well and mark + it dirty. */ + if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { + hdw->freqSelector = 0; + hdw->freqDirty = !0; + } else if ((hdw->input_val == PVR2_CVAL_INPUT_TV) || + (hdw->input_val == PVR2_CVAL_INPUT_DTV)) { + hdw->freqSelector = 1; + hdw->freqDirty = !0; + } + return 0; +} + + +int pvr2_hdw_set_input_allowed(struct pvr2_hdw *hdw, + unsigned int change_mask, + unsigned int change_val) +{ + int ret = 0; + unsigned int nv,m,idx; + LOCK_TAKE(hdw->big_lock); + do { + nv = hdw->input_allowed_mask & ~change_mask; + nv |= (change_val & change_mask); + nv &= hdw->input_avail_mask; + if (!nv) { + /* No legal modes left; return error instead. */ + ret = -EPERM; + break; + } + hdw->input_allowed_mask = nv; + if ((1 << hdw->input_val) & hdw->input_allowed_mask) { + /* Current mode is still in the allowed mask, so + we're done. */ + break; + } + /* Select and switch to a mode that is still in the allowed + mask */ + if (!hdw->input_allowed_mask) { + /* Nothing legal; give up */ + break; + } + m = hdw->input_allowed_mask; + for (idx = 0; idx < (sizeof(m) << 3); idx++) { + if (!((1 << idx) & m)) continue; + pvr2_hdw_set_input(hdw,idx); + break; + } + } while (0); + LOCK_GIVE(hdw->big_lock); + return ret; +} + + /* Find I2C address of eeprom */ static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw) { -- cgit v1.2.3 From 17a7b6642da13f789471895677c98736ac85f43a Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Mon, 21 Apr 2008 03:48:41 -0300 Subject: V4L/DVB (7720): pvrusb2: Fix bad error code on cx23416 firmware load failure Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 72e9056557b..a3e40adc2c1 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1265,7 +1265,7 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) " must be a multiple of %zu bytes", fw_files[fwidx],sizeof(u32)); release_firmware(fw_entry); - return -1; + return -EINVAL; } fw_ptr = kmalloc(FIRMWARE_CHUNK_SIZE, GFP_KERNEL); -- cgit v1.2.3 From 21684ba921d7758dc9264e0de64475b8a636ee95 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Mon, 21 Apr 2008 03:49:33 -0300 Subject: V4L/DVB (7721): pvrusb2: Restructure cx23416 firmware loading to have a common exit point Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index a3e40adc2c1..19de395a5fc 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1252,7 +1252,7 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) pvr2_trace(PVR2_TRACE_ERROR_LEGS, "firmware2 upload prep failed, ret=%d",ret); release_firmware(fw_entry); - return ret; + goto done; } /* Now send firmware */ @@ -1265,7 +1265,8 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) " must be a multiple of %zu bytes", fw_files[fwidx],sizeof(u32)); release_firmware(fw_entry); - return -EINVAL; + ret = -EINVAL; + goto done; } fw_ptr = kmalloc(FIRMWARE_CHUNK_SIZE, GFP_KERNEL); @@ -1273,7 +1274,8 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) release_firmware(fw_entry); pvr2_trace(PVR2_TRACE_ERROR_LEGS, "failed to allocate memory for firmware2 upload"); - return -ENOMEM; + ret = -ENOMEM; + goto done; } pipe = usb_sndbulkpipe(hdw->usb_dev, PVR2_FIRMWARE_ENDPOINT); @@ -1304,7 +1306,7 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) if (ret) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "firmware2 upload transfer failure"); - return ret; + goto done; } /* Finish upload */ @@ -1317,6 +1319,8 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) pvr2_trace(PVR2_TRACE_ERROR_LEGS, "firmware2 upload post-proc failure"); } + + done: return ret; } -- cgit v1.2.3 From 1df59f0b908bfcdc35d1ea2319290ece272bf576 Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Mon, 21 Apr 2008 03:50:39 -0300 Subject: V4L/DVB (7722): pvrusb2: Implement FM radio support for Gotview USB2.0 DVD 2 Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'drivers/media/video/pvrusb2/pvrusb2-hdw.c') diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 19de395a5fc..0a868888f38 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1321,6 +1321,12 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) } done: + if (hdw->hdw_desc->signal_routing_scheme == + PVR2_ROUTING_SCHEME_GOTVIEW) { + /* Ensure that GPIO 11 is set to output for GOTVIEW + hardware. */ + pvr2_hdw_gpio_chg_dir(hdw,(1 << 11),~0); + } return ret; } @@ -1735,6 +1741,13 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) if (!pvr2_hdw_dev_ok(hdw)) return; + if (hdw->hdw_desc->signal_routing_scheme == + PVR2_ROUTING_SCHEME_GOTVIEW) { + /* Ensure that GPIO 11 is set to output for GOTVIEW + hardware. */ + pvr2_hdw_gpio_chg_dir(hdw,(1 << 11),~0); + } + pvr2_hdw_commit_setup(hdw); hdw->vid_stream = pvr2_stream_create(); @@ -2498,6 +2511,20 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw) hdw->active_stream_type = hdw->desired_stream_type; } + if (hdw->hdw_desc->signal_routing_scheme == + PVR2_ROUTING_SCHEME_GOTVIEW) { + u32 b; + /* Handle GOTVIEW audio switching */ + pvr2_hdw_gpio_get_out(hdw,&b); + if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { + /* Set GPIO 11 */ + pvr2_hdw_gpio_chg_out(hdw,(1 << 11),~0); + } else { + /* Clear GPIO 11 */ + pvr2_hdw_gpio_chg_out(hdw,(1 << 11),0); + } + } + /* Now execute i2c core update */ pvr2_i2c_core_sync(hdw); -- cgit v1.2.3