pvrusb2-hdw.c
来自「trident tm5600的linux驱动」· C语言 代码 · 共 2,389 行 · 第 1/5 页
C
2,389 行
/* * * * 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 "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"#include "compat.h"#define TV_MIN_FREQ 55250000L#define TV_MAX_FREQ 850000000L/* This defines a minimum interval that the decoder must remain quiet before we are allowed to start it running. */#define TIME_MSEC_DECODER_WAIT 50/* This defines a minimum interval that the encoder must remain quiet before we are allowed to configure it. I had this originally set to 50msec, but Martin Dauskardt <martin.dauskardt@gmx.de> reports that things work better when it's set to 100msec. */#define TIME_MSEC_ENCODER_WAIT 100/* This defines the minimum interval that the encoder must successfully run before we consider that the encoder has run at least once since its firmware has been loaded. This measurement is in important for cases where we can't do something until we know that the encoder has been run at least once. */#define TIME_MSEC_ENCODER_OK 250static struct pvr2_hdw *unit_pointers[PVR_NUM] = {[ 0 ... PVR_NUM-1 ] = NULL};static DEFINE_MUTEX(pvr2_unit_mtx);static int ctlchg;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;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(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");/* US Broadcast channel 7 (175.25 MHz) */static int default_tv_freq = 175250000L;/* 104.3 MHz, a usable FM station for my area */static int default_radio_freq = 104300000L;module_param_named(tv_freq, default_tv_freq, int, 0444);MODULE_PARM_DESC(tv_freq, "specify initial television frequency");module_param_named(radio_freq, default_radio_freq, int, 0444);MODULE_PARM_DESC(radio_freq, "specify initial radio frequency");#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_DTV] = "dtv", [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 *pvr2_state_names[] = { [PVR2_STATE_NONE] = "none", [PVR2_STATE_DEAD] = "dead", [PVR2_STATE_COLD] = "cold", [PVR2_STATE_WARM] = "warm", [PVR2_STATE_ERROR] = "error", [PVR2_STATE_READY] = "ready", [PVR2_STATE_RUN] = "run",};struct pvr2_fx2cmd_descdef { unsigned char id; unsigned char *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_HCW_ZILOG_RESET, "zilog IR reset control"}, {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 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);static void pvr2_hdw_worker_i2c(struct work_struct *work);static void pvr2_hdw_worker_poll(struct work_struct *work);static int pvr2_hdw_wait(struct pvr2_hdw *,int state);static int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *);static void pvr2_hdw_state_log_state(struct pvr2_hdw *);static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl);static int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw);static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw);static void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw);static void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw);static void pvr2_hdw_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, void *write_data,unsigned int write_len, void *read_data,unsigned int read_len);static int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw);static void trace_stbit(const char *name,int val){ pvr2_trace(PVR2_TRACE_STBITS, "State bit %s <-- %s", name,(val ? "true" : "false"));}static int ctrl_channelfreq_get(struct pvr2_ctrl *cptr,int *vp){ struct pvr2_hdw *hdw = cptr->hdw; 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_cropl_min_get(struct pvr2_ctrl *cptr, int *left){ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; int stat = pvr2_hdw_check_cropcap(cptr->hdw); if (stat != 0) { return stat; } *left = cap->bounds.left; return 0;}static int ctrl_cropl_max_get(struct pvr2_ctrl *cptr, int *left){ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; int stat = pvr2_hdw_check_cropcap(cptr->hdw); if (stat != 0) { return stat; } *left = cap->bounds.left; if (cap->bounds.width > cptr->hdw->cropw_val) { *left += cap->bounds.width - cptr->hdw->cropw_val; } return 0;}static int ctrl_cropt_min_get(struct pvr2_ctrl *cptr, int *top){ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; int stat = pvr2_hdw_check_cropcap(cptr->hdw); if (stat != 0) { return stat; } *top = cap->bounds.top; return 0;}static int ctrl_cropt_max_get(struct pvr2_ctrl *cptr, int *top){ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; int stat = pvr2_hdw_check_cropcap(cptr->hdw); if (stat != 0) { return stat; } *top = cap->bounds.top; if (cap->bounds.height > cptr->hdw->croph_val) { *top += cap->bounds.height - cptr->hdw->croph_val; } return 0;}static int ctrl_cropw_max_get(struct pvr2_ctrl *cptr, int *val){ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; int stat = pvr2_hdw_check_cropcap(cptr->hdw); if (stat != 0) { return stat; } *val = 0; if (cap->bounds.width > cptr->hdw->cropl_val) { *val = cap->bounds.width - cptr->hdw->cropl_val; } return 0;}static int ctrl_croph_max_get(struct pvr2_ctrl *cptr, int *val){ struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info; int stat = pvr2_hdw_check_cropcap(cptr->hdw); if (stat != 0) { return stat; } *val = 0; if (cap->bounds.height > cptr->hdw->cropt_val) { *val = cap->bounds.height - cptr->hdw->cropt_val; }
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?