📄 pvrusb2-hdw.c
字号:
/* * * $Id$ * * Copyright (C) 2005 Mike Isely <isely@pobox.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */#include <linux/errno.h>#include <linux/string.h>#include <linux/slab.h>#include <linux/firmware.h>#include <linux/videodev2.h>#include <media/v4l2-common.h>#include <asm/semaphore.h>#include "pvrusb2.h"#include "pvrusb2-std.h"#include "pvrusb2-util.h"#include "pvrusb2-hdw.h"#include "pvrusb2-i2c-core.h"#include "pvrusb2-tuner.h"#include "pvrusb2-eeprom.h"#include "pvrusb2-hdw-internal.h"#include "pvrusb2-encoder.h"#include "pvrusb2-debug.h"#include "pvrusb2-fx2-cmd.h"#define TV_MIN_FREQ 55250000L#define TV_MAX_FREQ 850000000Lstruct 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 hardwarestatic const char *pvr2_client_24xxx[] = { "cx25840", "tuner", "wm8775",};// Names of other client modules to request for 29xxx model hardwarestatic 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);static int ctlchg = 0;static int initusbreset = 1;static int procreload = 0;static int tuner[PVR_NUM] = { [0 ... PVR_NUM-1] = -1 };static int tolerance[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 };static int video_std[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 };static int init_pause_msec = 0;module_param(ctlchg, int, S_IRUGO|S_IWUSR);MODULE_PARM_DESC(ctlchg, "0=optimize ctl change 1=always accept new ctl value");module_param(init_pause_msec, int, S_IRUGO|S_IWUSR);MODULE_PARM_DESC(init_pause_msec, "hardware initialization settling delay");module_param(initusbreset, int, S_IRUGO|S_IWUSR);MODULE_PARM_DESC(initusbreset, "Do USB reset device on probe");module_param(procreload, int, S_IRUGO|S_IWUSR);MODULE_PARM_DESC(procreload, "Attempt init failure recovery with firmware reload");module_param_array(tuner, int, NULL, 0444);MODULE_PARM_DESC(tuner,"specify installed tuner type");module_param_array(video_std, int, NULL, 0444);MODULE_PARM_DESC(video_std,"specify initial video standard");module_param_array(tolerance, int, NULL, 0444);MODULE_PARM_DESC(tolerance,"specify stream error tolerance");#define PVR2_CTL_WRITE_ENDPOINT 0x01#define PVR2_CTL_READ_ENDPOINT 0x81#define PVR2_GPIO_IN 0x9008#define PVR2_GPIO_OUT 0x900c#define PVR2_GPIO_DIR 0x9020#define trace_firmware(...) pvr2_trace(PVR2_TRACE_FIRMWARE,__VA_ARGS__)#define PVR2_FIRMWARE_ENDPOINT 0x02/* size of a firmware chunk */#define FIRMWARE_CHUNK_SIZE 0x2000/* Define the list of additional controls we'll dynamically construct based on query of the cx2341x module. */struct pvr2_mpeg_ids { const char *strid; int id;};static const struct pvr2_mpeg_ids mpeg_ids[] = { { .strid = "audio_layer", .id = V4L2_CID_MPEG_AUDIO_ENCODING, },{ .strid = "audio_bitrate", .id = V4L2_CID_MPEG_AUDIO_L2_BITRATE, },{ /* Already using audio_mode elsewhere :-( */ .strid = "mpeg_audio_mode", .id = V4L2_CID_MPEG_AUDIO_MODE, },{ .strid = "mpeg_audio_mode_extension", .id = V4L2_CID_MPEG_AUDIO_MODE_EXTENSION, },{ .strid = "audio_emphasis", .id = V4L2_CID_MPEG_AUDIO_EMPHASIS, },{ .strid = "audio_crc", .id = V4L2_CID_MPEG_AUDIO_CRC, },{ .strid = "video_aspect", .id = V4L2_CID_MPEG_VIDEO_ASPECT, },{ .strid = "video_b_frames", .id = V4L2_CID_MPEG_VIDEO_B_FRAMES, },{ .strid = "video_gop_size", .id = V4L2_CID_MPEG_VIDEO_GOP_SIZE, },{ .strid = "video_gop_closure", .id = V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, },{ .strid = "video_bitrate_mode", .id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE, },{ .strid = "video_bitrate", .id = V4L2_CID_MPEG_VIDEO_BITRATE, },{ .strid = "video_bitrate_peak", .id = V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, },{ .strid = "video_temporal_decimation", .id = V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION, },{ .strid = "stream_type", .id = V4L2_CID_MPEG_STREAM_TYPE, },{ .strid = "video_spatial_filter_mode", .id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE, },{ .strid = "video_spatial_filter", .id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER, },{ .strid = "video_luma_spatial_filter_type", .id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE, },{ .strid = "video_chroma_spatial_filter_type", .id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE, },{ .strid = "video_temporal_filter_mode", .id = V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE, },{ .strid = "video_temporal_filter", .id = V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER, },{ .strid = "video_median_filter_type", .id = V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE, },{ .strid = "video_luma_median_filter_top", .id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP, },{ .strid = "video_luma_median_filter_bottom", .id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM, },{ .strid = "video_chroma_median_filter_top", .id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP, },{ .strid = "video_chroma_median_filter_bottom", .id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM, }};#define MPEGDEF_COUNT ARRAY_SIZE(mpeg_ids)static const char *control_values_srate[] = { [V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100] = "44.1 kHz", [V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000] = "48 kHz", [V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000] = "32 kHz",};static const char *control_values_input[] = { [PVR2_CVAL_INPUT_TV] = "television", /*xawtv needs this name*/ [PVR2_CVAL_INPUT_RADIO] = "radio", [PVR2_CVAL_INPUT_SVIDEO] = "s-video", [PVR2_CVAL_INPUT_COMPOSITE] = "composite",};static const char *control_values_audiomode[] = { [V4L2_TUNER_MODE_MONO] = "Mono", [V4L2_TUNER_MODE_STEREO] = "Stereo", [V4L2_TUNER_MODE_LANG1] = "Lang1", [V4L2_TUNER_MODE_LANG2] = "Lang2", [V4L2_TUNER_MODE_LANG1_LANG2] = "Lang1+Lang2",};static const char *control_values_hsm[] = { [PVR2_CVAL_HSM_FAIL] = "Fail", [PVR2_CVAL_HSM_HIGH] = "High", [PVR2_CVAL_HSM_FULL] = "Full",};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 void pvr2_hdw_set_cur_freq(struct pvr2_hdw *,unsigned long);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_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 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 int ctrl_channelfreq_get(struct pvr2_ctrl *cptr,int *vp){ struct pvr2_hdw *hdw = cptr->hdw; if ((hdw->freqProgSlot > 0) && (hdw->freqProgSlot <= FREQTABLE_SIZE)) { *vp = hdw->freqTable[hdw->freqProgSlot-1]; } else { *vp = 0; } return 0;}static int ctrl_channelfreq_set(struct pvr2_ctrl *cptr,int m,int v){ struct pvr2_hdw *hdw = cptr->hdw; unsigned int slotId = hdw->freqProgSlot; if ((slotId > 0) && (slotId <= FREQTABLE_SIZE)) { hdw->freqTable[slotId-1] = v; /* Handle side effects correctly - if we're tuned to this slot, then forgot the slot id relation since the stored frequency has been changed. */ if (hdw->freqSelector) { if (hdw->freqSlotRadio == slotId) { hdw->freqSlotRadio = 0; } } else { if (hdw->freqSlotTelevision == slotId) { hdw->freqSlotTelevision = 0; } } } return 0;}static int ctrl_channelprog_get(struct pvr2_ctrl *cptr,int *vp){ *vp = cptr->hdw->freqProgSlot; return 0;}static int ctrl_channelprog_set(struct pvr2_ctrl *cptr,int m,int v){ struct pvr2_hdw *hdw = cptr->hdw; if ((v >= 0) && (v <= FREQTABLE_SIZE)) { hdw->freqProgSlot = v; } return 0;}static int ctrl_channel_get(struct pvr2_ctrl *cptr,int *vp){ struct pvr2_hdw *hdw = cptr->hdw; *vp = hdw->freqSelector ? hdw->freqSlotRadio : hdw->freqSlotTelevision; return 0;}static int ctrl_channel_set(struct pvr2_ctrl *cptr,int m,int slotId){ unsigned freq = 0; struct pvr2_hdw *hdw = cptr->hdw; if ((slotId < 0) || (slotId > FREQTABLE_SIZE)) return 0; if (slotId > 0) { freq = hdw->freqTable[slotId-1]; if (!freq) return 0; pvr2_hdw_set_cur_freq(hdw,freq); } if (hdw->freqSelector) { hdw->freqSlotRadio = slotId; } else { hdw->freqSlotTelevision = slotId; } return 0;}static int ctrl_freq_get(struct pvr2_ctrl *cptr,int *vp){ *vp = pvr2_hdw_get_cur_freq(cptr->hdw); return 0;}static int ctrl_freq_is_dirty(struct pvr2_ctrl *cptr){ return cptr->hdw->freqDirty != 0;}static void ctrl_freq_clear_dirty(struct pvr2_ctrl *cptr){ cptr->hdw->freqDirty = 0;}static int ctrl_freq_set(struct pvr2_ctrl *cptr,int m,int v){ pvr2_hdw_set_cur_freq(cptr->hdw,v); return 0;}static int ctrl_vres_max_get(struct pvr2_ctrl *cptr,int *vp){ /* Actual maximum depends on the video standard in effect. */ if (cptr->hdw->std_mask_cur & V4L2_STD_525_60) { *vp = 480; } else { *vp = 576; } return 0;}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) { *vp = 75; } else { *vp = 17; } return 0;}static int ctrl_get_input(struct pvr2_ctrl *cptr,int *vp){ *vp = cptr->hdw->input_val; return 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->freqSelector = 1; hdw->freqDirty = !0; } return 0;}static int ctrl_isdirty_input(struct pvr2_ctrl *cptr){ return cptr->hdw->input_dirty != 0;}static void ctrl_cleardirty_input(struct pvr2_ctrl *cptr){ cptr->hdw->input_dirty = 0;}static int ctrl_freq_max_get(struct pvr2_ctrl *cptr, int *vp){ unsigned long fv; struct pvr2_hdw *hdw = cptr->hdw; if (hdw->tuner_signal_stale) { pvr2_i2c_core_status_poll(hdw); } fv = hdw->tuner_signal_info.rangehigh; if (!fv) { /* Safety fallback */ *vp = TV_MAX_FREQ; return 0; } if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) { fv = (fv * 125) / 2; } else { fv = fv * 62500; } *vp = fv; return 0;}static int ctrl_freq_min_get(struct pvr2_ctrl *cptr, int *vp){ unsigned long fv; struct pvr2_hdw *hdw = cptr->hdw; if (hdw->tuner_signal_stale) { pvr2_i2c_core_status_poll(hdw); } fv = hdw->tuner_signal_info.rangelow; if (!fv) { /* Safety fallback */ *vp = TV_MIN_FREQ; return 0; } if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) { fv = (fv * 125) / 2; } else { fv = fv * 62500; } *vp = fv; return 0;}static int ctrl_cx2341x_is_dirty(struct pvr2_ctrl *cptr){ return cptr->hdw->enc_stale != 0;}static void ctrl_cx2341x_clear_dirty(struct pvr2_ctrl *cptr){ cptr->hdw->enc_stale = 0;}static int ctrl_cx2341x_get(struct pvr2_ctrl *cptr,int *vp)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -