5145 lines
		
	
	
		
			138 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			5145 lines
		
	
	
		
			138 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0-only
 | |
| /*
 | |
|  *
 | |
|  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
 | |
|  */
 | |
| 
 | |
| #include <linux/errno.h>
 | |
| #include <linux/string.h>
 | |
| #include <linux/slab.h>
 | |
| #include <linux/module.h>
 | |
| #include <linux/firmware.h>
 | |
| #include <linux/videodev2.h>
 | |
| #include <media/v4l2-common.h>
 | |
| #include <media/tuner.h>
 | |
| #include "pvrusb2.h"
 | |
| #include "pvrusb2-std.h"
 | |
| #include "pvrusb2-util.h"
 | |
| #include "pvrusb2-hdw.h"
 | |
| #include "pvrusb2-i2c-core.h"
 | |
| #include "pvrusb2-eeprom.h"
 | |
| #include "pvrusb2-hdw-internal.h"
 | |
| #include "pvrusb2-encoder.h"
 | |
| #include "pvrusb2-debug.h"
 | |
| #include "pvrusb2-fx2-cmd.h"
 | |
| #include "pvrusb2-wm8775.h"
 | |
| #include "pvrusb2-video-v4l.h"
 | |
| #include "pvrusb2-cx2584x-v4l.h"
 | |
| #include "pvrusb2-cs53l32a.h"
 | |
| #include "pvrusb2-audio.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 decoder must be allowed to run
 | |
|    before we can safely begin using its streaming output. */
 | |
| #define TIME_MSEC_DECODER_STABILIZATION_WAIT 300
 | |
| 
 | |
| /* This defines a minimum interval that the encoder must remain quiet
 | |
|    before we are allowed to configure it. */
 | |
| #define TIME_MSEC_ENCODER_WAIT 50
 | |
| 
 | |
| /* 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 250
 | |
| 
 | |
| static 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 3 (61.25 MHz), to help with testing */
 | |
| static int default_tv_freq    = 61250000L;
 | |
| /* 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
 | |
| 
 | |
| typedef void (*pvr2_subdev_update_func)(struct pvr2_hdw *,
 | |
| 					struct v4l2_subdev *);
 | |
| 
 | |
| static const pvr2_subdev_update_func pvr2_module_update_functions[] = {
 | |
| 	[PVR2_CLIENT_ID_WM8775] = pvr2_wm8775_subdev_update,
 | |
| 	[PVR2_CLIENT_ID_SAA7115] = pvr2_saa7115_subdev_update,
 | |
| 	[PVR2_CLIENT_ID_MSP3400] = pvr2_msp3400_subdev_update,
 | |
| 	[PVR2_CLIENT_ID_CX25840] = pvr2_cx25840_subdev_update,
 | |
| 	[PVR2_CLIENT_ID_CS53L32A] = pvr2_cs53l32a_subdev_update,
 | |
| };
 | |
| 
 | |
| static const char *module_names[] = {
 | |
| 	[PVR2_CLIENT_ID_MSP3400] = "msp3400",
 | |
| 	[PVR2_CLIENT_ID_CX25840] = "cx25840",
 | |
| 	[PVR2_CLIENT_ID_SAA7115] = "saa7115",
 | |
| 	[PVR2_CLIENT_ID_TUNER] = "tuner",
 | |
| 	[PVR2_CLIENT_ID_DEMOD] = "tuner",
 | |
| 	[PVR2_CLIENT_ID_CS53L32A] = "cs53l32a",
 | |
| 	[PVR2_CLIENT_ID_WM8775] = "wm8775",
 | |
| };
 | |
| 
 | |
| 
 | |
| static const unsigned char *module_i2c_addresses[] = {
 | |
| 	[PVR2_CLIENT_ID_TUNER] = "\x60\x61\x62\x63",
 | |
| 	[PVR2_CLIENT_ID_DEMOD] = "\x43",
 | |
| 	[PVR2_CLIENT_ID_MSP3400] = "\x40",
 | |
| 	[PVR2_CLIENT_ID_SAA7115] = "\x21",
 | |
| 	[PVR2_CLIENT_ID_WM8775] = "\x1b",
 | |
| 	[PVR2_CLIENT_ID_CX25840] = "\x44",
 | |
| 	[PVR2_CLIENT_ID_CS53L32A] = "\x11",
 | |
| };
 | |
| 
 | |
| 
 | |
| static const char *ir_scheme_names[] = {
 | |
| 	[PVR2_IR_SCHEME_NONE] = "none",
 | |
| 	[PVR2_IR_SCHEME_29XXX] = "29xxx",
 | |
| 	[PVR2_IR_SCHEME_24XXX] = "24xxx (29xxx emulation)",
 | |
| 	[PVR2_IR_SCHEME_24XXX_MCE] = "24xxx (MCE device)",
 | |
| 	[PVR2_IR_SCHEME_ZILOG] = "Zilog",
 | |
| };
 | |
| 
 | |
| 
 | |
| /* 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"},
 | |
| 	{FX2CMD_HCW_DEMOD_RESET_PIN, "hcw demod reset pin"},
 | |
| 	{FX2CMD_HCW_MAKO_SLEEP_PIN, "hcw mako sleep pin"},
 | |
| };
 | |
| 
 | |
| 
 | |
| 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_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_quiescent_timeout(struct timer_list *);
 | |
| static void pvr2_hdw_decoder_stabilization_timeout(struct timer_list *);
 | |
| static void pvr2_hdw_encoder_wait_timeout(struct timer_list *);
 | |
| static void pvr2_hdw_encoder_run_timeout(struct timer_list *);
 | |
| 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 v4l2_std_id pvr2_hdw_get_detected_std(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 *width)
 | |
| {
 | |
| 	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
 | |
| 	int stat, bleftend, cleft;
 | |
| 
 | |
| 	stat = pvr2_hdw_check_cropcap(cptr->hdw);
 | |
| 	if (stat != 0) {
 | |
| 		return stat;
 | |
| 	}
 | |
| 	bleftend = cap->bounds.left+cap->bounds.width;
 | |
| 	cleft = cptr->hdw->cropl_val;
 | |
| 
 | |
| 	*width = cleft < bleftend ? bleftend-cleft : 0;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ctrl_croph_max_get(struct pvr2_ctrl *cptr, int *height)
 | |
| {
 | |
| 	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
 | |
| 	int stat, btopend, ctop;
 | |
| 
 | |
| 	stat = pvr2_hdw_check_cropcap(cptr->hdw);
 | |
| 	if (stat != 0) {
 | |
| 		return stat;
 | |
| 	}
 | |
| 	btopend = cap->bounds.top+cap->bounds.height;
 | |
| 	ctop = cptr->hdw->cropt_val;
 | |
| 
 | |
| 	*height = ctop < btopend ? btopend-ctop : 0;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ctrl_get_cropcapbl(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 = cap->bounds.left;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ctrl_get_cropcapbt(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 = cap->bounds.top;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ctrl_get_cropcapbw(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 = cap->bounds.width;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ctrl_get_cropcapbh(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 = cap->bounds.height;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ctrl_get_cropcapdl(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 = cap->defrect.left;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ctrl_get_cropcapdt(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 = cap->defrect.top;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ctrl_get_cropcapdw(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 = cap->defrect.width;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ctrl_get_cropcapdh(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 = cap->defrect.height;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ctrl_get_cropcappan(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 = cap->pixelaspect.numerator;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ctrl_get_cropcappad(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 = cap->pixelaspect.denominator;
 | |
| 	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 digitizer type. */
 | |
| 	if (cptr->hdw->hdw_desc->flag_has_cx25840) {
 | |
| 		*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_check_input(struct pvr2_ctrl *cptr,int v)
 | |
| {
 | |
| 	if (v < 0 || v > PVR2_CVAL_INPUT_MAX)
 | |
| 		return 0;
 | |
| 	return ((1UL << v) & cptr->hdw->input_allowed_mask) != 0;
 | |
| }
 | |
| 
 | |
| static int ctrl_set_input(struct pvr2_ctrl *cptr,int m,int v)
 | |
| {
 | |
| 	return pvr2_hdw_set_input(cptr->hdw,v);
 | |
| }
 | |
| 
 | |
| 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_hdw_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_hdw_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;
 | |
| 	cptr->hdw->enc_unsafe_stale = 0;
 | |
| }
 | |
| 
 | |
| static int ctrl_cx2341x_get(struct pvr2_ctrl *cptr,int *vp)
 | |
| {
 | |
| 	int ret;
 | |
| 	struct v4l2_ext_controls cs;
 | |
| 	struct v4l2_ext_control c1;
 | |
| 	memset(&cs,0,sizeof(cs));
 | |
| 	memset(&c1,0,sizeof(c1));
 | |
| 	cs.controls = &c1;
 | |
| 	cs.count = 1;
 | |
| 	c1.id = cptr->info->v4l_id;
 | |
| 	ret = cx2341x_ext_ctrls(&cptr->hdw->enc_ctl_state, 0, &cs,
 | |
| 				VIDIOC_G_EXT_CTRLS);
 | |
| 	if (ret) return ret;
 | |
| 	*vp = c1.value;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ctrl_cx2341x_set(struct pvr2_ctrl *cptr,int m,int v)
 | |
| {
 | |
| 	int ret;
 | |
| 	struct pvr2_hdw *hdw = cptr->hdw;
 | |
| 	struct v4l2_ext_controls cs;
 | |
| 	struct v4l2_ext_control c1;
 | |
| 	memset(&cs,0,sizeof(cs));
 | |
| 	memset(&c1,0,sizeof(c1));
 | |
| 	cs.controls = &c1;
 | |
| 	cs.count = 1;
 | |
| 	c1.id = cptr->info->v4l_id;
 | |
| 	c1.value = v;
 | |
| 	ret = cx2341x_ext_ctrls(&hdw->enc_ctl_state,
 | |
| 				hdw->state_encoder_run, &cs,
 | |
| 				VIDIOC_S_EXT_CTRLS);
 | |
| 	if (ret == -EBUSY) {
 | |
| 		/* Oops.  cx2341x is telling us it's not safe to change
 | |
| 		   this control while we're capturing.  Make a note of this
 | |
| 		   fact so that the pipeline will be stopped the next time
 | |
| 		   controls are committed.  Then go on ahead and store this
 | |
| 		   change anyway. */
 | |
| 		ret = cx2341x_ext_ctrls(&hdw->enc_ctl_state,
 | |
| 					0, &cs,
 | |
| 					VIDIOC_S_EXT_CTRLS);
 | |
| 		if (!ret) hdw->enc_unsafe_stale = !0;
 | |
| 	}
 | |
| 	if (ret) return ret;
 | |
| 	hdw->enc_stale = !0;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static unsigned int ctrl_cx2341x_getv4lflags(struct pvr2_ctrl *cptr)
 | |
| {
 | |
| 	struct v4l2_queryctrl qctrl = {};
 | |
| 	struct pvr2_ctl_info *info;
 | |
| 	qctrl.id = cptr->info->v4l_id;
 | |
| 	cx2341x_ctrl_query(&cptr->hdw->enc_ctl_state,&qctrl);
 | |
| 	/* Strip out the const so we can adjust a function pointer.  It's
 | |
| 	   OK to do this here because we know this is a dynamically created
 | |
| 	   control, so the underlying storage for the info pointer is (a)
 | |
| 	   private to us, and (b) not in read-only storage.  Either we do
 | |
| 	   this or we significantly complicate the underlying control
 | |
| 	   implementation. */
 | |
| 	info = (struct pvr2_ctl_info *)(cptr->info);
 | |
| 	if (qctrl.flags & V4L2_CTRL_FLAG_READ_ONLY) {
 | |
| 		if (info->set_value) {
 | |
| 			info->set_value = NULL;
 | |
| 		}
 | |
| 	} else {
 | |
| 		if (!(info->set_value)) {
 | |
| 			info->set_value = ctrl_cx2341x_set;
 | |
| 		}
 | |
| 	}
 | |
| 	return qctrl.flags;
 | |
| }
 | |
| 
 | |
| static int ctrl_streamingenabled_get(struct pvr2_ctrl *cptr,int *vp)
 | |
| {
 | |
| 	*vp = cptr->hdw->state_pipeline_req;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ctrl_masterstate_get(struct pvr2_ctrl *cptr,int *vp)
 | |
| {
 | |
| 	*vp = cptr->hdw->master_state;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ctrl_hsm_get(struct pvr2_ctrl *cptr,int *vp)
 | |
| {
 | |
| 	int result = pvr2_hdw_is_hsm(cptr->hdw);
 | |
| 	*vp = PVR2_CVAL_HSM_FULL;
 | |
| 	if (result < 0) *vp = PVR2_CVAL_HSM_FAIL;
 | |
| 	if (result) *vp = PVR2_CVAL_HSM_HIGH;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ctrl_stddetect_get(struct pvr2_ctrl *cptr, int *vp)
 | |
| {
 | |
| 	*vp = pvr2_hdw_get_detected_std(cptr->hdw);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ctrl_stdavail_get(struct pvr2_ctrl *cptr,int *vp)
 | |
| {
 | |
| 	*vp = cptr->hdw->std_mask_avail;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ctrl_stdavail_set(struct pvr2_ctrl *cptr,int m,int v)
 | |
| {
 | |
| 	struct pvr2_hdw *hdw = cptr->hdw;
 | |
| 	v4l2_std_id ns;
 | |
| 	ns = hdw->std_mask_avail;
 | |
| 	ns = (ns & ~m) | (v & m);
 | |
| 	if (ns == hdw->std_mask_avail) return 0;
 | |
| 	hdw->std_mask_avail = ns;
 | |
| 	hdw->std_info_cur.def.type_bitmask.valid_bits = hdw->std_mask_avail;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ctrl_std_val_to_sym(struct pvr2_ctrl *cptr,int msk,int val,
 | |
| 			       char *bufPtr,unsigned int bufSize,
 | |
| 			       unsigned int *len)
 | |
| {
 | |
| 	*len = pvr2_std_id_to_str(bufPtr,bufSize,msk & val);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ctrl_std_sym_to_val(struct pvr2_ctrl *cptr,
 | |
| 			       const char *bufPtr,unsigned int bufSize,
 | |
| 			       int *mskp,int *valp)
 | |
| {
 | |
| 	v4l2_std_id id;
 | |
| 	if (!pvr2_std_str_to_id(&id, bufPtr, bufSize))
 | |
| 		return -EINVAL;
 | |
| 	if (mskp) *mskp = id;
 | |
| 	if (valp) *valp = id;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ctrl_stdcur_get(struct pvr2_ctrl *cptr,int *vp)
 | |
| {
 | |
| 	*vp = cptr->hdw->std_mask_cur;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ctrl_stdcur_set(struct pvr2_ctrl *cptr,int m,int v)
 | |
| {
 | |
| 	struct pvr2_hdw *hdw = cptr->hdw;
 | |
| 	v4l2_std_id ns;
 | |
| 	ns = hdw->std_mask_cur;
 | |
| 	ns = (ns & ~m) | (v & m);
 | |
| 	if (ns == hdw->std_mask_cur) return 0;
 | |
| 	hdw->std_mask_cur = ns;
 | |
| 	hdw->std_dirty = !0;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ctrl_stdcur_is_dirty(struct pvr2_ctrl *cptr)
 | |
| {
 | |
| 	return cptr->hdw->std_dirty != 0;
 | |
| }
 | |
| 
 | |
| static void ctrl_stdcur_clear_dirty(struct pvr2_ctrl *cptr)
 | |
| {
 | |
| 	cptr->hdw->std_dirty = 0;
 | |
| }
 | |
| 
 | |
| static int ctrl_signal_get(struct pvr2_ctrl *cptr,int *vp)
 | |
| {
 | |
| 	struct pvr2_hdw *hdw = cptr->hdw;
 | |
| 	pvr2_hdw_status_poll(hdw);
 | |
| 	*vp = hdw->tuner_signal_info.signal;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ctrl_audio_modes_present_get(struct pvr2_ctrl *cptr,int *vp)
 | |
| {
 | |
| 	int val = 0;
 | |
| 	unsigned int subchan;
 | |
| 	struct pvr2_hdw *hdw = cptr->hdw;
 | |
| 	pvr2_hdw_status_poll(hdw);
 | |
| 	subchan = hdw->tuner_signal_info.rxsubchans;
 | |
| 	if (subchan & V4L2_TUNER_SUB_MONO) {
 | |
| 		val |= (1 << V4L2_TUNER_MODE_MONO);
 | |
| 	}
 | |
| 	if (subchan & V4L2_TUNER_SUB_STEREO) {
 | |
| 		val |= (1 << V4L2_TUNER_MODE_STEREO);
 | |
| 	}
 | |
| 	if (subchan & V4L2_TUNER_SUB_LANG1) {
 | |
| 		val |= (1 << V4L2_TUNER_MODE_LANG1);
 | |
| 	}
 | |
| 	if (subchan & V4L2_TUNER_SUB_LANG2) {
 | |
| 		val |= (1 << V4L2_TUNER_MODE_LANG2);
 | |
| 	}
 | |
| 	*vp = val;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| #define DEFINT(vmin,vmax) \
 | |
| 	.type = pvr2_ctl_int, \
 | |
| 	.def.type_int.min_value = vmin, \
 | |
| 	.def.type_int.max_value = vmax
 | |
| 
 | |
| #define DEFENUM(tab) \
 | |
| 	.type = pvr2_ctl_enum, \
 | |
| 	.def.type_enum.count = ARRAY_SIZE(tab), \
 | |
| 	.def.type_enum.value_names = tab
 | |
| 
 | |
| #define DEFBOOL \
 | |
| 	.type = pvr2_ctl_bool
 | |
| 
 | |
| #define DEFMASK(msk,tab) \
 | |
| 	.type = pvr2_ctl_bitmask, \
 | |
| 	.def.type_bitmask.valid_bits = msk, \
 | |
| 	.def.type_bitmask.bit_names = tab
 | |
| 
 | |
| #define DEFREF(vname) \
 | |
| 	.set_value = ctrl_set_##vname, \
 | |
| 	.get_value = ctrl_get_##vname, \
 | |
| 	.is_dirty = ctrl_isdirty_##vname, \
 | |
| 	.clear_dirty = ctrl_cleardirty_##vname
 | |
| 
 | |
| 
 | |
| #define VCREATE_FUNCS(vname) \
 | |
| static int ctrl_get_##vname(struct pvr2_ctrl *cptr,int *vp) \
 | |
| {*vp = cptr->hdw->vname##_val; return 0;} \
 | |
| static int ctrl_set_##vname(struct pvr2_ctrl *cptr,int m,int v) \
 | |
| {cptr->hdw->vname##_val = v; cptr->hdw->vname##_dirty = !0; return 0;} \
 | |
| static int ctrl_isdirty_##vname(struct pvr2_ctrl *cptr) \
 | |
| {return cptr->hdw->vname##_dirty != 0;} \
 | |
| static void ctrl_cleardirty_##vname(struct pvr2_ctrl *cptr) \
 | |
| {cptr->hdw->vname##_dirty = 0;}
 | |
| 
 | |
| VCREATE_FUNCS(brightness)
 | |
| VCREATE_FUNCS(contrast)
 | |
| VCREATE_FUNCS(saturation)
 | |
| VCREATE_FUNCS(hue)
 | |
| VCREATE_FUNCS(volume)
 | |
| VCREATE_FUNCS(balance)
 | |
| VCREATE_FUNCS(bass)
 | |
| VCREATE_FUNCS(treble)
 | |
| VCREATE_FUNCS(mute)
 | |
| VCREATE_FUNCS(cropl)
 | |
| VCREATE_FUNCS(cropt)
 | |
| VCREATE_FUNCS(cropw)
 | |
| VCREATE_FUNCS(croph)
 | |
| VCREATE_FUNCS(audiomode)
 | |
| VCREATE_FUNCS(res_hor)
 | |
| VCREATE_FUNCS(res_ver)
 | |
| VCREATE_FUNCS(srate)
 | |
| 
 | |
| /* Table definition of all controls which can be manipulated */
 | |
| static const struct pvr2_ctl_info control_defs[] = {
 | |
| 	{
 | |
| 		.v4l_id = V4L2_CID_BRIGHTNESS,
 | |
| 		.desc = "Brightness",
 | |
| 		.name = "brightness",
 | |
| 		.default_value = 128,
 | |
| 		DEFREF(brightness),
 | |
| 		DEFINT(0,255),
 | |
| 	},{
 | |
| 		.v4l_id = V4L2_CID_CONTRAST,
 | |
| 		.desc = "Contrast",
 | |
| 		.name = "contrast",
 | |
| 		.default_value = 68,
 | |
| 		DEFREF(contrast),
 | |
| 		DEFINT(0,127),
 | |
| 	},{
 | |
| 		.v4l_id = V4L2_CID_SATURATION,
 | |
| 		.desc = "Saturation",
 | |
| 		.name = "saturation",
 | |
| 		.default_value = 64,
 | |
| 		DEFREF(saturation),
 | |
| 		DEFINT(0,127),
 | |
| 	},{
 | |
| 		.v4l_id = V4L2_CID_HUE,
 | |
| 		.desc = "Hue",
 | |
| 		.name = "hue",
 | |
| 		.default_value = 0,
 | |
| 		DEFREF(hue),
 | |
| 		DEFINT(-128,127),
 | |
| 	},{
 | |
| 		.v4l_id = V4L2_CID_AUDIO_VOLUME,
 | |
| 		.desc = "Volume",
 | |
| 		.name = "volume",
 | |
| 		.default_value = 62000,
 | |
| 		DEFREF(volume),
 | |
| 		DEFINT(0,65535),
 | |
| 	},{
 | |
| 		.v4l_id = V4L2_CID_AUDIO_BALANCE,
 | |
| 		.desc = "Balance",
 | |
| 		.name = "balance",
 | |
| 		.default_value = 0,
 | |
| 		DEFREF(balance),
 | |
| 		DEFINT(-32768,32767),
 | |
| 	},{
 | |
| 		.v4l_id = V4L2_CID_AUDIO_BASS,
 | |
| 		.desc = "Bass",
 | |
| 		.name = "bass",
 | |
| 		.default_value = 0,
 | |
| 		DEFREF(bass),
 | |
| 		DEFINT(-32768,32767),
 | |
| 	},{
 | |
| 		.v4l_id = V4L2_CID_AUDIO_TREBLE,
 | |
| 		.desc = "Treble",
 | |
| 		.name = "treble",
 | |
| 		.default_value = 0,
 | |
| 		DEFREF(treble),
 | |
| 		DEFINT(-32768,32767),
 | |
| 	},{
 | |
| 		.v4l_id = V4L2_CID_AUDIO_MUTE,
 | |
| 		.desc = "Mute",
 | |
| 		.name = "mute",
 | |
| 		.default_value = 0,
 | |
| 		DEFREF(mute),
 | |
| 		DEFBOOL,
 | |
| 	}, {
 | |
| 		.desc = "Capture crop left margin",
 | |
| 		.name = "crop_left",
 | |
| 		.internal_id = PVR2_CID_CROPL,
 | |
| 		.default_value = 0,
 | |
| 		DEFREF(cropl),
 | |
| 		DEFINT(-129, 340),
 | |
| 		.get_min_value = ctrl_cropl_min_get,
 | |
| 		.get_max_value = ctrl_cropl_max_get,
 | |
| 		.get_def_value = ctrl_get_cropcapdl,
 | |
| 	}, {
 | |
| 		.desc = "Capture crop top margin",
 | |
| 		.name = "crop_top",
 | |
| 		.internal_id = PVR2_CID_CROPT,
 | |
| 		.default_value = 0,
 | |
| 		DEFREF(cropt),
 | |
| 		DEFINT(-35, 544),
 | |
| 		.get_min_value = ctrl_cropt_min_get,
 | |
| 		.get_max_value = ctrl_cropt_max_get,
 | |
| 		.get_def_value = ctrl_get_cropcapdt,
 | |
| 	}, {
 | |
| 		.desc = "Capture crop width",
 | |
| 		.name = "crop_width",
 | |
| 		.internal_id = PVR2_CID_CROPW,
 | |
| 		.default_value = 720,
 | |
| 		DEFREF(cropw),
 | |
| 		DEFINT(0, 864),
 | |
| 		.get_max_value = ctrl_cropw_max_get,
 | |
| 		.get_def_value = ctrl_get_cropcapdw,
 | |
| 	}, {
 | |
| 		.desc = "Capture crop height",
 | |
| 		.name = "crop_height",
 | |
| 		.internal_id = PVR2_CID_CROPH,
 | |
| 		.default_value = 480,
 | |
| 		DEFREF(croph),
 | |
| 		DEFINT(0, 576),
 | |
| 		.get_max_value = ctrl_croph_max_get,
 | |
| 		.get_def_value = ctrl_get_cropcapdh,
 | |
| 	}, {
 | |
| 		.desc = "Capture capability pixel aspect numerator",
 | |
| 		.name = "cropcap_pixel_numerator",
 | |
| 		.internal_id = PVR2_CID_CROPCAPPAN,
 | |
| 		.get_value = ctrl_get_cropcappan,
 | |
| 	}, {
 | |
| 		.desc = "Capture capability pixel aspect denominator",
 | |
| 		.name = "cropcap_pixel_denominator",
 | |
| 		.internal_id = PVR2_CID_CROPCAPPAD,
 | |
| 		.get_value = ctrl_get_cropcappad,
 | |
| 	}, {
 | |
| 		.desc = "Capture capability bounds top",
 | |
| 		.name = "cropcap_bounds_top",
 | |
| 		.internal_id = PVR2_CID_CROPCAPBT,
 | |
| 		.get_value = ctrl_get_cropcapbt,
 | |
| 	}, {
 | |
| 		.desc = "Capture capability bounds left",
 | |
| 		.name = "cropcap_bounds_left",
 | |
| 		.internal_id = PVR2_CID_CROPCAPBL,
 | |
| 		.get_value = ctrl_get_cropcapbl,
 | |
| 	}, {
 | |
| 		.desc = "Capture capability bounds width",
 | |
| 		.name = "cropcap_bounds_width",
 | |
| 		.internal_id = PVR2_CID_CROPCAPBW,
 | |
| 		.get_value = ctrl_get_cropcapbw,
 | |
| 	}, {
 | |
| 		.desc = "Capture capability bounds height",
 | |
| 		.name = "cropcap_bounds_height",
 | |
| 		.internal_id = PVR2_CID_CROPCAPBH,
 | |
| 		.get_value = ctrl_get_cropcapbh,
 | |
| 	},{
 | |
| 		.desc = "Video Source",
 | |
| 		.name = "input",
 | |
| 		.internal_id = PVR2_CID_INPUT,
 | |
| 		.default_value = PVR2_CVAL_INPUT_TV,
 | |
| 		.check_value = ctrl_check_input,
 | |
| 		DEFREF(input),
 | |
| 		DEFENUM(control_values_input),
 | |
| 	},{
 | |
| 		.desc = "Audio Mode",
 | |
| 		.name = "audio_mode",
 | |
| 		.internal_id = PVR2_CID_AUDIOMODE,
 | |
| 		.default_value = V4L2_TUNER_MODE_STEREO,
 | |
| 		DEFREF(audiomode),
 | |
| 		DEFENUM(control_values_audiomode),
 | |
| 	},{
 | |
| 		.desc = "Horizontal capture resolution",
 | |
| 		.name = "resolution_hor",
 | |
| 		.internal_id = PVR2_CID_HRES,
 | |
| 		.default_value = 720,
 | |
| 		DEFREF(res_hor),
 | |
| 		DEFINT(19,720),
 | |
| 	},{
 | |
| 		.desc = "Vertical capture resolution",
 | |
| 		.name = "resolution_ver",
 | |
| 		.internal_id = PVR2_CID_VRES,
 | |
| 		.default_value = 480,
 | |
| 		DEFREF(res_ver),
 | |
| 		DEFINT(17,576),
 | |
| 		/* Hook in check for video standard and adjust maximum
 | |
| 		   depending on the standard. */
 | |
| 		.get_max_value = ctrl_vres_max_get,
 | |
| 		.get_min_value = ctrl_vres_min_get,
 | |
| 	},{
 | |
| 		.v4l_id = V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ,
 | |
| 		.default_value = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000,
 | |
| 		.desc = "Audio Sampling Frequency",
 | |
| 		.name = "srate",
 | |
| 		DEFREF(srate),
 | |
| 		DEFENUM(control_values_srate),
 | |
| 	},{
 | |
| 		.desc = "Tuner Frequency (Hz)",
 | |
| 		.name = "frequency",
 | |
| 		.internal_id = PVR2_CID_FREQUENCY,
 | |
| 		.default_value = 0,
 | |
| 		.set_value = ctrl_freq_set,
 | |
| 		.get_value = ctrl_freq_get,
 | |
| 		.is_dirty = ctrl_freq_is_dirty,
 | |
| 		.clear_dirty = ctrl_freq_clear_dirty,
 | |
| 		DEFINT(0,0),
 | |
| 		/* Hook in check for input value (tv/radio) and adjust
 | |
| 		   max/min values accordingly */
 | |
| 		.get_max_value = ctrl_freq_max_get,
 | |
| 		.get_min_value = ctrl_freq_min_get,
 | |
| 	},{
 | |
| 		.desc = "Channel",
 | |
| 		.name = "channel",
 | |
| 		.set_value = ctrl_channel_set,
 | |
| 		.get_value = ctrl_channel_get,
 | |
| 		DEFINT(0,FREQTABLE_SIZE),
 | |
| 	},{
 | |
| 		.desc = "Channel Program Frequency",
 | |
| 		.name = "freq_table_value",
 | |
| 		.set_value = ctrl_channelfreq_set,
 | |
| 		.get_value = ctrl_channelfreq_get,
 | |
| 		DEFINT(0,0),
 | |
| 		/* Hook in check for input value (tv/radio) and adjust
 | |
| 		   max/min values accordingly */
 | |
| 		.get_max_value = ctrl_freq_max_get,
 | |
| 		.get_min_value = ctrl_freq_min_get,
 | |
| 	},{
 | |
| 		.desc = "Channel Program ID",
 | |
| 		.name = "freq_table_channel",
 | |
| 		.set_value = ctrl_channelprog_set,
 | |
| 		.get_value = ctrl_channelprog_get,
 | |
| 		DEFINT(0,FREQTABLE_SIZE),
 | |
| 	},{
 | |
| 		.desc = "Streaming Enabled",
 | |
| 		.name = "streaming_enabled",
 | |
| 		.get_value = ctrl_streamingenabled_get,
 | |
| 		DEFBOOL,
 | |
| 	},{
 | |
| 		.desc = "USB Speed",
 | |
| 		.name = "usb_speed",
 | |
| 		.get_value = ctrl_hsm_get,
 | |
| 		DEFENUM(control_values_hsm),
 | |
| 	},{
 | |
| 		.desc = "Master State",
 | |
| 		.name = "master_state",
 | |
| 		.get_value = ctrl_masterstate_get,
 | |
| 		DEFENUM(pvr2_state_names),
 | |
| 	},{
 | |
| 		.desc = "Signal Present",
 | |
| 		.name = "signal_present",
 | |
| 		.get_value = ctrl_signal_get,
 | |
| 		DEFINT(0,65535),
 | |
| 	},{
 | |
| 		.desc = "Audio Modes Present",
 | |
| 		.name = "audio_modes_present",
 | |
| 		.get_value = ctrl_audio_modes_present_get,
 | |
| 		/* For this type we "borrow" the V4L2_TUNER_MODE enum from
 | |
| 		   v4l.  Nothing outside of this module cares about this,
 | |
| 		   but I reuse it in order to also reuse the
 | |
| 		   control_values_audiomode string table. */
 | |
| 		DEFMASK(((1 << V4L2_TUNER_MODE_MONO)|
 | |
| 			 (1 << V4L2_TUNER_MODE_STEREO)|
 | |
| 			 (1 << V4L2_TUNER_MODE_LANG1)|
 | |
| 			 (1 << V4L2_TUNER_MODE_LANG2)),
 | |
| 			control_values_audiomode),
 | |
| 	},{
 | |
| 		.desc = "Video Standards Available Mask",
 | |
| 		.name = "video_standard_mask_available",
 | |
| 		.internal_id = PVR2_CID_STDAVAIL,
 | |
| 		.skip_init = !0,
 | |
| 		.get_value = ctrl_stdavail_get,
 | |
| 		.set_value = ctrl_stdavail_set,
 | |
| 		.val_to_sym = ctrl_std_val_to_sym,
 | |
| 		.sym_to_val = ctrl_std_sym_to_val,
 | |
| 		.type = pvr2_ctl_bitmask,
 | |
| 	},{
 | |
| 		.desc = "Video Standards In Use Mask",
 | |
| 		.name = "video_standard_mask_active",
 | |
| 		.internal_id = PVR2_CID_STDCUR,
 | |
| 		.skip_init = !0,
 | |
| 		.get_value = ctrl_stdcur_get,
 | |
| 		.set_value = ctrl_stdcur_set,
 | |
| 		.is_dirty = ctrl_stdcur_is_dirty,
 | |
| 		.clear_dirty = ctrl_stdcur_clear_dirty,
 | |
| 		.val_to_sym = ctrl_std_val_to_sym,
 | |
| 		.sym_to_val = ctrl_std_sym_to_val,
 | |
| 		.type = pvr2_ctl_bitmask,
 | |
| 	},{
 | |
| 		.desc = "Video Standards Detected Mask",
 | |
| 		.name = "video_standard_mask_detected",
 | |
| 		.internal_id = PVR2_CID_STDDETECT,
 | |
| 		.skip_init = !0,
 | |
| 		.get_value = ctrl_stddetect_get,
 | |
| 		.val_to_sym = ctrl_std_val_to_sym,
 | |
| 		.sym_to_val = ctrl_std_sym_to_val,
 | |
| 		.type = pvr2_ctl_bitmask,
 | |
| 	}
 | |
| };
 | |
| 
 | |
| #define CTRLDEF_COUNT ARRAY_SIZE(control_defs)
 | |
| 
 | |
| 
 | |
| const char *pvr2_config_get_name(enum pvr2_config cfg)
 | |
| {
 | |
| 	switch (cfg) {
 | |
| 	case pvr2_config_empty: return "empty";
 | |
| 	case pvr2_config_mpeg: return "mpeg";
 | |
| 	case pvr2_config_vbi: return "vbi";
 | |
| 	case pvr2_config_pcm: return "pcm";
 | |
| 	case pvr2_config_rawvideo: return "raw video";
 | |
| 	}
 | |
| 	return "<unknown>";
 | |
| }
 | |
| 
 | |
| 
 | |
| struct usb_device *pvr2_hdw_get_dev(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	return hdw->usb_dev;
 | |
| }
 | |
| 
 | |
| 
 | |
| unsigned long pvr2_hdw_get_sn(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	return hdw->serial_number;
 | |
| }
 | |
| 
 | |
| 
 | |
| const char *pvr2_hdw_get_bus_info(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	return hdw->bus_info;
 | |
| }
 | |
| 
 | |
| 
 | |
| const char *pvr2_hdw_get_device_identifier(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	return hdw->identifier;
 | |
| }
 | |
| 
 | |
| 
 | |
| unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	return hdw->freqSelector ? hdw->freqValTelevision : hdw->freqValRadio;
 | |
| }
 | |
| 
 | |
| /* Set the currently tuned frequency and account for all possible
 | |
|    driver-core side effects of this action. */
 | |
| 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) {
 | |
| 			/* Swing over to radio frequency selection */
 | |
| 			hdw->freqSelector = 0;
 | |
| 			hdw->freqDirty = !0;
 | |
| 		}
 | |
| 		if (hdw->freqValRadio != val) {
 | |
| 			hdw->freqValRadio = val;
 | |
| 			hdw->freqSlotRadio = 0;
 | |
| 			hdw->freqDirty = !0;
 | |
| 		}
 | |
| 	} else {
 | |
| 		if (!(hdw->freqSelector)) {
 | |
| 			/* Swing over to television frequency selection */
 | |
| 			hdw->freqSelector = 1;
 | |
| 			hdw->freqDirty = !0;
 | |
| 		}
 | |
| 		if (hdw->freqValTelevision != val) {
 | |
| 			hdw->freqValTelevision = val;
 | |
| 			hdw->freqSlotTelevision = 0;
 | |
| 			hdw->freqDirty = !0;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| int pvr2_hdw_get_unit_number(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	return hdw->unit_number;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Attempt to locate one of the given set of files.  Messages are logged
 | |
|    appropriate to what has been found.  The return value will be 0 or
 | |
|    greater on success (it will be the index of the file name found) and
 | |
|    fw_entry will be filled in.  Otherwise a negative error is returned on
 | |
|    failure.  If the return value is -ENOENT then no viable firmware file
 | |
|    could be located. */
 | |
| static int pvr2_locate_firmware(struct pvr2_hdw *hdw,
 | |
| 				const struct firmware **fw_entry,
 | |
| 				const char *fwtypename,
 | |
| 				unsigned int fwcount,
 | |
| 				const char *fwnames[])
 | |
| {
 | |
| 	unsigned int idx;
 | |
| 	int ret = -EINVAL;
 | |
| 	for (idx = 0; idx < fwcount; idx++) {
 | |
| 		ret = request_firmware(fw_entry,
 | |
| 				       fwnames[idx],
 | |
| 				       &hdw->usb_dev->dev);
 | |
| 		if (!ret) {
 | |
| 			trace_firmware("Located %s firmware: %s; uploading...",
 | |
| 				       fwtypename,
 | |
| 				       fwnames[idx]);
 | |
| 			return idx;
 | |
| 		}
 | |
| 		if (ret == -ENOENT) continue;
 | |
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 			   "request_firmware fatal error with code=%d",ret);
 | |
| 		return ret;
 | |
| 	}
 | |
| 	pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 		   "***WARNING*** Device %s firmware seems to be missing.",
 | |
| 		   fwtypename);
 | |
| 	pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 		   "Did you install the pvrusb2 firmware files in their proper location?");
 | |
| 	if (fwcount == 1) {
 | |
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 			   "request_firmware unable to locate %s file %s",
 | |
| 			   fwtypename,fwnames[0]);
 | |
| 	} else {
 | |
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 			   "request_firmware unable to locate one of the following %s files:",
 | |
| 			   fwtypename);
 | |
| 		for (idx = 0; idx < fwcount; idx++) {
 | |
| 			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 				   "request_firmware: Failed to find %s",
 | |
| 				   fwnames[idx]);
 | |
| 		}
 | |
| 	}
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * pvr2_upload_firmware1().
 | |
|  *
 | |
|  * Send the 8051 firmware to the device.  After the upload, arrange for
 | |
|  * device to re-enumerate.
 | |
|  *
 | |
|  * NOTE : the pointer to the firmware data given by request_firmware()
 | |
|  * is not suitable for an usb transaction.
 | |
|  *
 | |
|  */
 | |
| static int pvr2_upload_firmware1(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	const struct firmware *fw_entry = NULL;
 | |
| 	void  *fw_ptr;
 | |
| 	unsigned int pipe;
 | |
| 	unsigned int fwsize;
 | |
| 	int ret;
 | |
| 	u16 address;
 | |
| 
 | |
| 	if (!hdw->hdw_desc->fx2_firmware.cnt) {
 | |
| 		hdw->fw1_state = FW1_STATE_OK;
 | |
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 			   "Connected device type defines no firmware to upload; ignoring firmware");
 | |
| 		return -ENOTTY;
 | |
| 	}
 | |
| 
 | |
| 	hdw->fw1_state = FW1_STATE_FAILED; // default result
 | |
| 
 | |
| 	trace_firmware("pvr2_upload_firmware1");
 | |
| 
 | |
| 	ret = pvr2_locate_firmware(hdw,&fw_entry,"fx2 controller",
 | |
| 				   hdw->hdw_desc->fx2_firmware.cnt,
 | |
| 				   hdw->hdw_desc->fx2_firmware.lst);
 | |
| 	if (ret < 0) {
 | |
| 		if (ret == -ENOENT) hdw->fw1_state = FW1_STATE_MISSING;
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	usb_clear_halt(hdw->usb_dev, usb_sndbulkpipe(hdw->usb_dev, 0 & 0x7f));
 | |
| 
 | |
| 	pipe = usb_sndctrlpipe(hdw->usb_dev, 0);
 | |
| 	fwsize = fw_entry->size;
 | |
| 
 | |
| 	if ((fwsize != 0x2000) &&
 | |
| 	    (!(hdw->hdw_desc->flag_fx2_16kb && (fwsize == 0x4000)))) {
 | |
| 		if (hdw->hdw_desc->flag_fx2_16kb) {
 | |
| 			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 				   "Wrong fx2 firmware size (expected 8192 or 16384, got %u)",
 | |
| 				   fwsize);
 | |
| 		} else {
 | |
| 			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 				   "Wrong fx2 firmware size (expected 8192, got %u)",
 | |
| 				   fwsize);
 | |
| 		}
 | |
| 		release_firmware(fw_entry);
 | |
| 		return -ENOMEM;
 | |
| 	}
 | |
| 
 | |
| 	fw_ptr = kmalloc(0x800, GFP_KERNEL);
 | |
| 	if (fw_ptr == NULL){
 | |
| 		release_firmware(fw_entry);
 | |
| 		return -ENOMEM;
 | |
| 	}
 | |
| 
 | |
| 	/* We have to hold the CPU during firmware upload. */
 | |
| 	pvr2_hdw_cpureset_assert(hdw,1);
 | |
| 
 | |
| 	/* upload the firmware to address 0000-1fff in 2048 (=0x800) bytes
 | |
| 	   chunk. */
 | |
| 
 | |
| 	ret = 0;
 | |
| 	for (address = 0; address < fwsize; address += 0x800) {
 | |
| 		memcpy(fw_ptr, fw_entry->data + address, 0x800);
 | |
| 		ret += usb_control_msg(hdw->usb_dev, pipe, 0xa0, 0x40, address,
 | |
| 				       0, fw_ptr, 0x800, 1000);
 | |
| 	}
 | |
| 
 | |
| 	trace_firmware("Upload done, releasing device's CPU");
 | |
| 
 | |
| 	/* Now release the CPU.  It will disconnect and reconnect later. */
 | |
| 	pvr2_hdw_cpureset_assert(hdw,0);
 | |
| 
 | |
| 	kfree(fw_ptr);
 | |
| 	release_firmware(fw_entry);
 | |
| 
 | |
| 	trace_firmware("Upload done (%d bytes sent)",ret);
 | |
| 
 | |
| 	/* We should have written fwsize bytes */
 | |
| 	if (ret == fwsize) {
 | |
| 		hdw->fw1_state = FW1_STATE_RELOAD;
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	return -EIO;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * pvr2_upload_firmware2()
 | |
|  *
 | |
|  * This uploads encoder firmware on endpoint 2.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| int pvr2_upload_firmware2(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	const struct firmware *fw_entry = NULL;
 | |
| 	void  *fw_ptr;
 | |
| 	unsigned int pipe, fw_len, fw_done, bcnt, icnt;
 | |
| 	int actual_length;
 | |
| 	int ret = 0;
 | |
| 	int fwidx;
 | |
| 	static const char *fw_files[] = {
 | |
| 		CX2341X_FIRM_ENC_FILENAME,
 | |
| 	};
 | |
| 
 | |
| 	if (hdw->hdw_desc->flag_skip_cx23416_firmware) {
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	trace_firmware("pvr2_upload_firmware2");
 | |
| 
 | |
| 	ret = pvr2_locate_firmware(hdw,&fw_entry,"encoder",
 | |
| 				   ARRAY_SIZE(fw_files), fw_files);
 | |
| 	if (ret < 0) return ret;
 | |
| 	fwidx = ret;
 | |
| 	ret = 0;
 | |
| 	/* Since we're about to completely reinitialize the encoder,
 | |
| 	   invalidate our cached copy of its configuration state.  Next
 | |
| 	   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*/
 | |
| 	ret |= pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000008); /*gpio output state*/
 | |
| 	ret |= pvr2_hdw_cmd_deep_reset(hdw);
 | |
| 	ret |= pvr2_write_register(hdw, 0xa064, 0x00000000); /*APU command*/
 | |
| 	ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000408); /*gpio dir*/
 | |
| 	ret |= pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000008); /*gpio output state*/
 | |
| 	ret |= pvr2_write_register(hdw, 0x9058, 0xffffffed); /*VPU ctrl*/
 | |
| 	ret |= pvr2_write_register(hdw, 0x9054, 0xfffffffd); /*reset hw blocks*/
 | |
| 	ret |= pvr2_write_register(hdw, 0x07f8, 0x80000800); /*encoder SDRAM refresh*/
 | |
| 	ret |= pvr2_write_register(hdw, 0x07fc, 0x0000001a); /*encoder SDRAM pre-charge*/
 | |
| 	ret |= pvr2_write_register(hdw, 0x0700, 0x00000000); /*I2C clock*/
 | |
| 	ret |= pvr2_write_register(hdw, 0xaa00, 0x00000000); /*unknown*/
 | |
| 	ret |= pvr2_write_register(hdw, 0xaa04, 0x00057810); /*unknown*/
 | |
| 	ret |= pvr2_write_register(hdw, 0xaa10, 0x00148500); /*unknown*/
 | |
| 	ret |= pvr2_write_register(hdw, 0xaa18, 0x00840000); /*unknown*/
 | |
| 	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,
 | |
| 			   "firmware2 upload prep failed, ret=%d",ret);
 | |
| 		release_firmware(fw_entry);
 | |
| 		goto done;
 | |
| 	}
 | |
| 
 | |
| 	/* Now send firmware */
 | |
| 
 | |
| 	fw_len = fw_entry->size;
 | |
| 
 | |
| 	if (fw_len % sizeof(u32)) {
 | |
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 			   "size of %s firmware must be a multiple of %zu bytes",
 | |
| 			   fw_files[fwidx],sizeof(u32));
 | |
| 		release_firmware(fw_entry);
 | |
| 		ret = -EINVAL;
 | |
| 		goto done;
 | |
| 	}
 | |
| 
 | |
| 	fw_ptr = kmalloc(FIRMWARE_CHUNK_SIZE, GFP_KERNEL);
 | |
| 	if (fw_ptr == NULL){
 | |
| 		release_firmware(fw_entry);
 | |
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 			   "failed to allocate memory for firmware2 upload");
 | |
| 		ret = -ENOMEM;
 | |
| 		goto done;
 | |
| 	}
 | |
| 
 | |
| 	pipe = usb_sndbulkpipe(hdw->usb_dev, PVR2_FIRMWARE_ENDPOINT);
 | |
| 
 | |
| 	fw_done = 0;
 | |
| 	for (fw_done = 0; fw_done < fw_len;) {
 | |
| 		bcnt = fw_len - fw_done;
 | |
| 		if (bcnt > FIRMWARE_CHUNK_SIZE) bcnt = FIRMWARE_CHUNK_SIZE;
 | |
| 		memcpy(fw_ptr, fw_entry->data + fw_done, bcnt);
 | |
| 		/* Usbsnoop log shows that we must swap bytes... */
 | |
| 		/* Some background info: The data being swapped here is a
 | |
| 		   firmware image destined for the mpeg encoder chip that
 | |
| 		   lives at the other end of a USB endpoint.  The encoder
 | |
| 		   chip always talks in 32 bit chunks and its storage is
 | |
| 		   organized into 32 bit words.  However from the file
 | |
| 		   system to the encoder chip everything is purely a byte
 | |
| 		   stream.  The firmware file's contents are always 32 bit
 | |
| 		   swapped from what the encoder expects.  Thus the need
 | |
| 		   always exists to swap the bytes regardless of the endian
 | |
| 		   type of the host processor and therefore swab32() makes
 | |
| 		   the most sense. */
 | |
| 		for (icnt = 0; icnt < bcnt/4 ; icnt++)
 | |
| 			((u32 *)fw_ptr)[icnt] = swab32(((u32 *)fw_ptr)[icnt]);
 | |
| 
 | |
| 		ret |= usb_bulk_msg(hdw->usb_dev, pipe, fw_ptr,bcnt,
 | |
| 				    &actual_length, 1000);
 | |
| 		ret |= (actual_length != bcnt);
 | |
| 		if (ret) break;
 | |
| 		fw_done += bcnt;
 | |
| 	}
 | |
| 
 | |
| 	trace_firmware("upload of %s : %i / %i ",
 | |
| 		       fw_files[fwidx],fw_done,fw_len);
 | |
| 
 | |
| 	kfree(fw_ptr);
 | |
| 	release_firmware(fw_entry);
 | |
| 
 | |
| 	if (ret) {
 | |
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 			   "firmware2 upload transfer failure");
 | |
| 		goto done;
 | |
| 	}
 | |
| 
 | |
| 	/* Finish upload */
 | |
| 
 | |
| 	ret |= pvr2_write_register(hdw, 0x9054, 0xffffffff); /*reset hw blocks*/
 | |
| 	ret |= pvr2_write_register(hdw, 0x9058, 0xffffffe8); /*VPU ctrl*/
 | |
| 	ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_MEMSEL | (1 << 8) | (0 << 16));
 | |
| 
 | |
| 	if (ret) {
 | |
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 			   "firmware2 upload post-proc failure");
 | |
| 	}
 | |
| 
 | |
|  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;
 | |
| }
 | |
| 
 | |
| 
 | |
| static const char *pvr2_get_state_name(unsigned int st)
 | |
| {
 | |
| 	if (st < ARRAY_SIZE(pvr2_state_names)) {
 | |
| 		return pvr2_state_names[st];
 | |
| 	}
 | |
| 	return "???";
 | |
| }
 | |
| 
 | |
| static int pvr2_decoder_enable(struct pvr2_hdw *hdw,int enablefl)
 | |
| {
 | |
| 	/* Even though we really only care about the video decoder chip at
 | |
| 	   this point, we'll broadcast stream on/off to all sub-devices
 | |
| 	   anyway, just in case somebody else wants to hear the
 | |
| 	   command... */
 | |
| 	pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 stream=%s",
 | |
| 		   (enablefl ? "on" : "off"));
 | |
| 	v4l2_device_call_all(&hdw->v4l2_dev, 0, video, s_stream, enablefl);
 | |
| 	v4l2_device_call_all(&hdw->v4l2_dev, 0, audio, s_stream, enablefl);
 | |
| 	if (hdw->decoder_client_id) {
 | |
| 		/* We get here if the encoder has been noticed.  Otherwise
 | |
| 		   we'll issue a warning to the user (which should
 | |
| 		   normally never happen). */
 | |
| 		return 0;
 | |
| 	}
 | |
| 	if (!hdw->flag_decoder_missed) {
 | |
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 			   "***WARNING*** No decoder present");
 | |
| 		hdw->flag_decoder_missed = !0;
 | |
| 		trace_stbit("flag_decoder_missed",
 | |
| 			    hdw->flag_decoder_missed);
 | |
| 	}
 | |
| 	return -EIO;
 | |
| }
 | |
| 
 | |
| 
 | |
| int pvr2_hdw_get_state(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	return hdw->master_state;
 | |
| }
 | |
| 
 | |
| 
 | |
| static int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	if (!hdw->flag_tripped) return 0;
 | |
| 	hdw->flag_tripped = 0;
 | |
| 	pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 		   "Clearing driver error status");
 | |
| 	return !0;
 | |
| }
 | |
| 
 | |
| 
 | |
| int pvr2_hdw_untrip(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	int fl;
 | |
| 	LOCK_TAKE(hdw->big_lock); do {
 | |
| 		fl = pvr2_hdw_untrip_unlocked(hdw);
 | |
| 	} while (0); LOCK_GIVE(hdw->big_lock);
 | |
| 	if (fl) pvr2_hdw_state_sched(hdw);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| int pvr2_hdw_get_streaming(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	return hdw->state_pipeline_req != 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| int pvr2_hdw_set_streaming(struct pvr2_hdw *hdw,int enable_flag)
 | |
| {
 | |
| 	int ret,st;
 | |
| 	LOCK_TAKE(hdw->big_lock);
 | |
| 	pvr2_hdw_untrip_unlocked(hdw);
 | |
| 	if (!enable_flag != !hdw->state_pipeline_req) {
 | |
| 		hdw->state_pipeline_req = enable_flag != 0;
 | |
| 		pvr2_trace(PVR2_TRACE_START_STOP,
 | |
| 			   "/*--TRACE_STREAM--*/ %s",
 | |
| 			   enable_flag ? "enable" : "disable");
 | |
| 	}
 | |
| 	pvr2_hdw_state_sched(hdw);
 | |
| 	LOCK_GIVE(hdw->big_lock);
 | |
| 	if ((ret = pvr2_hdw_wait(hdw,0)) < 0) return ret;
 | |
| 	if (enable_flag) {
 | |
| 		while ((st = hdw->master_state) != PVR2_STATE_RUN) {
 | |
| 			if (st != PVR2_STATE_READY) return -EIO;
 | |
| 			if ((ret = pvr2_hdw_wait(hdw,st)) < 0) return ret;
 | |
| 		}
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| int pvr2_hdw_set_stream_type(struct pvr2_hdw *hdw,enum pvr2_config config)
 | |
| {
 | |
| 	int fl;
 | |
| 	LOCK_TAKE(hdw->big_lock);
 | |
| 	if ((fl = (hdw->desired_stream_type != config)) != 0) {
 | |
| 		hdw->desired_stream_type = config;
 | |
| 		hdw->state_pipeline_config = 0;
 | |
| 		trace_stbit("state_pipeline_config",
 | |
| 			    hdw->state_pipeline_config);
 | |
| 		pvr2_hdw_state_sched(hdw);
 | |
| 	}
 | |
| 	LOCK_GIVE(hdw->big_lock);
 | |
| 	if (fl) return 0;
 | |
| 	return pvr2_hdw_wait(hdw,0);
 | |
| }
 | |
| 
 | |
| 
 | |
| static int get_default_tuner_type(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	int unit_number = hdw->unit_number;
 | |
| 	int tp = -1;
 | |
| 	if ((unit_number >= 0) && (unit_number < PVR_NUM)) {
 | |
| 		tp = tuner[unit_number];
 | |
| 	}
 | |
| 	if (tp < 0) return -EINVAL;
 | |
| 	hdw->tuner_type = tp;
 | |
| 	hdw->tuner_updated = !0;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| static v4l2_std_id get_default_standard(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	int unit_number = hdw->unit_number;
 | |
| 	int tp = 0;
 | |
| 	if ((unit_number >= 0) && (unit_number < PVR_NUM)) {
 | |
| 		tp = video_std[unit_number];
 | |
| 		if (tp) return tp;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| static unsigned int get_default_error_tolerance(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	int unit_number = hdw->unit_number;
 | |
| 	int tp = 0;
 | |
| 	if ((unit_number >= 0) && (unit_number < PVR_NUM)) {
 | |
| 		tp = tolerance[unit_number];
 | |
| 	}
 | |
| 	return tp;
 | |
| }
 | |
| 
 | |
| 
 | |
| static int pvr2_hdw_check_firmware(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	/* Try a harmless request to fetch the eeprom's address over
 | |
| 	   endpoint 1.  See what happens.  Only the full FX2 image can
 | |
| 	   respond to this.  If this probe fails then likely the FX2
 | |
| 	   firmware needs be loaded. */
 | |
| 	int result;
 | |
| 	LOCK_TAKE(hdw->ctl_lock); do {
 | |
| 		hdw->cmd_buffer[0] = FX2CMD_GET_EEPROM_ADDR;
 | |
| 		result = pvr2_send_request_ex(hdw,HZ*1,!0,
 | |
| 					   hdw->cmd_buffer,1,
 | |
| 					   hdw->cmd_buffer,1);
 | |
| 		if (result < 0) break;
 | |
| 	} while(0); LOCK_GIVE(hdw->ctl_lock);
 | |
| 	if (result) {
 | |
| 		pvr2_trace(PVR2_TRACE_INIT,
 | |
| 			   "Probe of device endpoint 1 result status %d",
 | |
| 			   result);
 | |
| 	} else {
 | |
| 		pvr2_trace(PVR2_TRACE_INIT,
 | |
| 			   "Probe of device endpoint 1 succeeded");
 | |
| 	}
 | |
| 	return result == 0;
 | |
| }
 | |
| 
 | |
| struct pvr2_std_hack {
 | |
| 	v4l2_std_id pat;  /* Pattern to match */
 | |
| 	v4l2_std_id msk;  /* Which bits we care about */
 | |
| 	v4l2_std_id std;  /* What additional standards or default to set */
 | |
| };
 | |
| 
 | |
| /* This data structure labels specific combinations of standards from
 | |
|    tveeprom that we'll try to recognize.  If we recognize one, then assume
 | |
|    a specified default standard to use.  This is here because tveeprom only
 | |
|    tells us about available standards not the intended default standard (if
 | |
|    any) for the device in question.  We guess the default based on what has
 | |
|    been reported as available.  Note that this is only for guessing a
 | |
|    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. */
 | |
| 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,
 | |
| 	},
 | |
| 	{	/* NTSC(M) */
 | |
| 		.pat = V4L2_STD_MN,
 | |
| 		.std = V4L2_STD_NTSC_M,
 | |
| 	},
 | |
| 	{	/* PAL(I) */
 | |
| 		.pat = V4L2_STD_PAL_I,
 | |
| 		.std = V4L2_STD_PAL_I,
 | |
| 	},
 | |
| 	{	/* SECAM(L/L') */
 | |
| 		.pat = V4L2_STD_SECAM_L|V4L2_STD_SECAM_LC,
 | |
| 		.std = V4L2_STD_SECAM_L|V4L2_STD_SECAM_LC,
 | |
| 	},
 | |
| 	{	/* PAL(D/D1/K) */
 | |
| 		.pat = V4L2_STD_DK,
 | |
| 		.std = V4L2_STD_PAL_D|V4L2_STD_PAL_D1|V4L2_STD_PAL_K,
 | |
| 	},
 | |
| };
 | |
| 
 | |
| static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	char buf[40];
 | |
| 	unsigned int bcnt;
 | |
| 	v4l2_std_id std1,std2,std3;
 | |
| 
 | |
| 	std1 = get_default_standard(hdw);
 | |
| 	std3 = std1 ? 0 : hdw->hdw_desc->default_std_mask;
 | |
| 
 | |
| 	bcnt = pvr2_std_id_to_str(buf,sizeof(buf),hdw->std_mask_eeprom);
 | |
| 	pvr2_trace(PVR2_TRACE_STD,
 | |
| 		   "Supported video standard(s) reported available in hardware: %.*s",
 | |
| 		   bcnt,buf);
 | |
| 
 | |
| 	hdw->std_mask_avail = hdw->std_mask_eeprom;
 | |
| 
 | |
| 	std2 = (std1|std3) & ~hdw->std_mask_avail;
 | |
| 	if (std2) {
 | |
| 		bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std2);
 | |
| 		pvr2_trace(PVR2_TRACE_STD,
 | |
| 			   "Expanding supported video standards to include: %.*s",
 | |
| 			   bcnt,buf);
 | |
| 		hdw->std_mask_avail |= std2;
 | |
| 	}
 | |
| 
 | |
| 	hdw->std_info_cur.def.type_bitmask.valid_bits = hdw->std_mask_avail;
 | |
| 
 | |
| 	if (std1) {
 | |
| 		bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std1);
 | |
| 		pvr2_trace(PVR2_TRACE_STD,
 | |
| 			   "Initial video standard forced to %.*s",
 | |
| 			   bcnt,buf);
 | |
| 		hdw->std_mask_cur = std1;
 | |
| 		hdw->std_dirty = !0;
 | |
| 		return;
 | |
| 	}
 | |
| 	if (std3) {
 | |
| 		bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std3);
 | |
| 		pvr2_trace(PVR2_TRACE_STD,
 | |
| 			   "Initial video standard (determined by device type): %.*s",
 | |
| 			   bcnt, buf);
 | |
| 		hdw->std_mask_cur = std3;
 | |
| 		hdw->std_dirty = !0;
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	{
 | |
| 		unsigned int idx;
 | |
| 		for (idx = 0; idx < ARRAY_SIZE(std_eeprom_maps); idx++) {
 | |
| 			if (std_eeprom_maps[idx].msk ?
 | |
| 			    ((std_eeprom_maps[idx].pat ^
 | |
| 			     hdw->std_mask_eeprom) &
 | |
| 			     std_eeprom_maps[idx].msk) :
 | |
| 			    (std_eeprom_maps[idx].pat !=
 | |
| 			     hdw->std_mask_eeprom)) continue;
 | |
| 			bcnt = pvr2_std_id_to_str(buf,sizeof(buf),
 | |
| 						  std_eeprom_maps[idx].std);
 | |
| 			pvr2_trace(PVR2_TRACE_STD,
 | |
| 				   "Initial video standard guessed as %.*s",
 | |
| 				   bcnt,buf);
 | |
| 			hdw->std_mask_cur = std_eeprom_maps[idx].std;
 | |
| 			hdw->std_dirty = !0;
 | |
| 			return;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| static unsigned int pvr2_copy_i2c_addr_list(
 | |
| 	unsigned short *dst, const unsigned char *src,
 | |
| 	unsigned int dst_max)
 | |
| {
 | |
| 	unsigned int cnt = 0;
 | |
| 	if (!src) return 0;
 | |
| 	while (src[cnt] && (cnt + 1) < dst_max) {
 | |
| 		dst[cnt] = src[cnt];
 | |
| 		cnt++;
 | |
| 	}
 | |
| 	dst[cnt] = I2C_CLIENT_END;
 | |
| 	return cnt;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void pvr2_hdw_cx25840_vbi_hack(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	/*
 | |
| 	  Mike Isely <isely@pobox.com> 19-Nov-2006 - This bit of nuttiness
 | |
| 	  for cx25840 causes that module to correctly set up its video
 | |
| 	  scaling.  This is really a problem in the cx25840 module itself,
 | |
| 	  but we work around it here.  The problem has not been seen in
 | |
| 	  ivtv because there VBI is supported and set up.  We don't do VBI
 | |
| 	  here (at least not yet) and thus we never attempted to even set
 | |
| 	  it up.
 | |
| 	*/
 | |
| 	struct v4l2_format fmt;
 | |
| 	if (hdw->decoder_client_id != PVR2_CLIENT_ID_CX25840) {
 | |
| 		/* We're not using a cx25840 so don't enable the hack */
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	pvr2_trace(PVR2_TRACE_INIT,
 | |
| 		   "Module ID %u: Executing cx25840 VBI hack",
 | |
| 		   hdw->decoder_client_id);
 | |
| 	memset(&fmt, 0, sizeof(fmt));
 | |
| 	fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
 | |
| 	fmt.fmt.sliced.service_lines[0][21] = V4L2_SLICED_CAPTION_525;
 | |
| 	fmt.fmt.sliced.service_lines[1][21] = V4L2_SLICED_CAPTION_525;
 | |
| 	v4l2_device_call_all(&hdw->v4l2_dev, hdw->decoder_client_id,
 | |
| 			     vbi, s_sliced_fmt, &fmt.fmt.sliced);
 | |
| }
 | |
| 
 | |
| 
 | |
| static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw,
 | |
| 				const struct pvr2_device_client_desc *cd)
 | |
| {
 | |
| 	const char *fname;
 | |
| 	unsigned char mid;
 | |
| 	struct v4l2_subdev *sd;
 | |
| 	unsigned int i2ccnt;
 | |
| 	const unsigned char *p;
 | |
| 	/* Arbitrary count - max # i2c addresses we will probe */
 | |
| 	unsigned short i2caddr[25];
 | |
| 
 | |
| 	mid = cd->module_id;
 | |
| 	fname = (mid < ARRAY_SIZE(module_names)) ? module_names[mid] : NULL;
 | |
| 	if (!fname) {
 | |
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 			   "Module ID %u for device %s has no name?  The driver might have a configuration problem.",
 | |
| 			   mid,
 | |
| 			   hdw->hdw_desc->description);
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 	pvr2_trace(PVR2_TRACE_INIT,
 | |
| 		   "Module ID %u (%s) for device %s being loaded...",
 | |
| 		   mid, fname,
 | |
| 		   hdw->hdw_desc->description);
 | |
| 
 | |
| 	i2ccnt = pvr2_copy_i2c_addr_list(i2caddr, cd->i2c_address_list,
 | |
| 					 ARRAY_SIZE(i2caddr));
 | |
| 	if (!i2ccnt && ((p = (mid < ARRAY_SIZE(module_i2c_addresses)) ?
 | |
| 			 module_i2c_addresses[mid] : NULL) != NULL)) {
 | |
| 		/* Second chance: Try default i2c address list */
 | |
| 		i2ccnt = pvr2_copy_i2c_addr_list(i2caddr, p,
 | |
| 						 ARRAY_SIZE(i2caddr));
 | |
| 		if (i2ccnt) {
 | |
| 			pvr2_trace(PVR2_TRACE_INIT,
 | |
| 				   "Module ID %u: Using default i2c address list",
 | |
| 				   mid);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (!i2ccnt) {
 | |
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 			   "Module ID %u (%s) for device %s: No i2c addresses.	The driver might have a configuration problem.",
 | |
| 			   mid, fname, hdw->hdw_desc->description);
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	if (i2ccnt == 1) {
 | |
| 		pvr2_trace(PVR2_TRACE_INIT,
 | |
| 			   "Module ID %u: Setting up with specified i2c address 0x%x",
 | |
| 			   mid, i2caddr[0]);
 | |
| 		sd = v4l2_i2c_new_subdev(&hdw->v4l2_dev, &hdw->i2c_adap,
 | |
| 					 fname, i2caddr[0], NULL);
 | |
| 	} else {
 | |
| 		pvr2_trace(PVR2_TRACE_INIT,
 | |
| 			   "Module ID %u: Setting up with address probe list",
 | |
| 			   mid);
 | |
| 		sd = v4l2_i2c_new_subdev(&hdw->v4l2_dev, &hdw->i2c_adap,
 | |
| 					 fname, 0, i2caddr);
 | |
| 	}
 | |
| 
 | |
| 	if (!sd) {
 | |
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 			   "Module ID %u (%s) for device %s failed to load.  Possible missing sub-device kernel module or initialization failure within module.",
 | |
| 			   mid, fname, hdw->hdw_desc->description);
 | |
| 		return -EIO;
 | |
| 	}
 | |
| 
 | |
| 	/* Tag this sub-device instance with the module ID we know about.
 | |
| 	   In other places we'll use that tag to determine if the instance
 | |
| 	   requires special handling. */
 | |
| 	sd->grp_id = mid;
 | |
| 
 | |
| 	pvr2_trace(PVR2_TRACE_INFO, "Attached sub-driver %s", fname);
 | |
| 
 | |
| 
 | |
| 	/* client-specific setup... */
 | |
| 	switch (mid) {
 | |
| 	case PVR2_CLIENT_ID_CX25840:
 | |
| 	case PVR2_CLIENT_ID_SAA7115:
 | |
| 		hdw->decoder_client_id = mid;
 | |
| 		break;
 | |
| 	default: break;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void pvr2_hdw_load_modules(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	unsigned int idx;
 | |
| 	const struct pvr2_string_table *cm;
 | |
| 	const struct pvr2_device_client_table *ct;
 | |
| 	int okFl = !0;
 | |
| 
 | |
| 	cm = &hdw->hdw_desc->client_modules;
 | |
| 	for (idx = 0; idx < cm->cnt; idx++) {
 | |
| 		request_module(cm->lst[idx]);
 | |
| 	}
 | |
| 
 | |
| 	ct = &hdw->hdw_desc->client_table;
 | |
| 	for (idx = 0; idx < ct->cnt; idx++) {
 | |
| 		if (pvr2_hdw_load_subdev(hdw, &ct->lst[idx]) < 0) okFl = 0;
 | |
| 	}
 | |
| 	if (!okFl) {
 | |
| 		hdw->flag_modulefail = !0;
 | |
| 		pvr2_hdw_render_useless(hdw);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	int ret;
 | |
| 	unsigned int idx;
 | |
| 	struct pvr2_ctrl *cptr;
 | |
| 	int reloadFl = 0;
 | |
| 	if (hdw->hdw_desc->fx2_firmware.cnt) {
 | |
| 		if (!reloadFl) {
 | |
| 			reloadFl =
 | |
| 				(hdw->usb_intf->cur_altsetting->desc.bNumEndpoints
 | |
| 				 == 0);
 | |
| 			if (reloadFl) {
 | |
| 				pvr2_trace(PVR2_TRACE_INIT,
 | |
| 					   "USB endpoint config looks strange; possibly firmware needs to be loaded");
 | |
| 			}
 | |
| 		}
 | |
| 		if (!reloadFl) {
 | |
| 			reloadFl = !pvr2_hdw_check_firmware(hdw);
 | |
| 			if (reloadFl) {
 | |
| 				pvr2_trace(PVR2_TRACE_INIT,
 | |
| 					   "Check for FX2 firmware failed; possibly firmware needs to be loaded");
 | |
| 			}
 | |
| 		}
 | |
| 		if (reloadFl) {
 | |
| 			if (pvr2_upload_firmware1(hdw) != 0) {
 | |
| 				pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 					   "Failure uploading firmware1");
 | |
| 			}
 | |
| 			return;
 | |
| 		}
 | |
| 	}
 | |
| 	hdw->fw1_state = FW1_STATE_OK;
 | |
| 
 | |
| 	if (!pvr2_hdw_dev_ok(hdw)) return;
 | |
| 
 | |
| 	hdw->force_dirty = !0;
 | |
| 
 | |
| 	if (!hdw->hdw_desc->flag_no_powerup) {
 | |
| 		pvr2_hdw_cmd_powerup(hdw);
 | |
| 		if (!pvr2_hdw_dev_ok(hdw)) return;
 | |
| 	}
 | |
| 
 | |
| 	/* Take the IR chip out of reset, if appropriate */
 | |
| 	if (hdw->ir_scheme_active == PVR2_IR_SCHEME_ZILOG) {
 | |
| 		pvr2_issue_simple_cmd(hdw,
 | |
| 				      FX2CMD_HCW_ZILOG_RESET |
 | |
| 				      (1 << 8) |
 | |
| 				      ((0) << 16));
 | |
| 	}
 | |
| 
 | |
| 	/* This step MUST happen after the earlier powerup step */
 | |
| 	pvr2_i2c_core_init(hdw);
 | |
| 	if (!pvr2_hdw_dev_ok(hdw)) return;
 | |
| 
 | |
| 	/* Reset demod only on Hauppauge 160xxx platform */
 | |
| 	if (le16_to_cpu(hdw->usb_dev->descriptor.idVendor) == 0x2040 &&
 | |
| 	    (le16_to_cpu(hdw->usb_dev->descriptor.idProduct) == 0x7502 ||
 | |
| 	     le16_to_cpu(hdw->usb_dev->descriptor.idProduct) == 0x7510)) {
 | |
| 		pr_info("%s(): resetting 160xxx demod\n", __func__);
 | |
| 		/* TODO: not sure this is proper place to reset once only */
 | |
| 		pvr2_issue_simple_cmd(hdw,
 | |
| 				      FX2CMD_HCW_DEMOD_RESET_PIN |
 | |
| 				      (1 << 8) |
 | |
| 				      ((0) << 16));
 | |
| 		usleep_range(10000, 10500);
 | |
| 		pvr2_issue_simple_cmd(hdw,
 | |
| 				      FX2CMD_HCW_DEMOD_RESET_PIN |
 | |
| 				      (1 << 8) |
 | |
| 				      ((1) << 16));
 | |
| 		usleep_range(10000, 10500);
 | |
| 	}
 | |
| 
 | |
| 	pvr2_hdw_load_modules(hdw);
 | |
| 	if (!pvr2_hdw_dev_ok(hdw)) return;
 | |
| 
 | |
| 	v4l2_device_call_all(&hdw->v4l2_dev, 0, core, load_fw);
 | |
| 
 | |
| 	for (idx = 0; idx < CTRLDEF_COUNT; idx++) {
 | |
| 		cptr = hdw->controls + idx;
 | |
| 		if (cptr->info->skip_init) continue;
 | |
| 		if (!cptr->info->set_value) continue;
 | |
| 		cptr->info->set_value(cptr,~0,cptr->info->default_value);
 | |
| 	}
 | |
| 
 | |
| 	pvr2_hdw_cx25840_vbi_hack(hdw);
 | |
| 
 | |
| 	/* Set up special default values for the television and radio
 | |
| 	   frequencies here.  It's not really important what these defaults
 | |
| 	   are, but I set them to something usable in the Chicago area just
 | |
| 	   to make driver testing a little easier. */
 | |
| 
 | |
| 	hdw->freqValTelevision = default_tv_freq;
 | |
| 	hdw->freqValRadio = default_radio_freq;
 | |
| 
 | |
| 	// Do not use pvr2_reset_ctl_endpoints() here.  It is not
 | |
| 	// thread-safe against the normal pvr2_send_request() mechanism.
 | |
| 	// (We should make it thread safe).
 | |
| 
 | |
| 	if (hdw->hdw_desc->flag_has_hauppauge_rom) {
 | |
| 		ret = pvr2_hdw_get_eeprom_addr(hdw);
 | |
| 		if (!pvr2_hdw_dev_ok(hdw)) return;
 | |
| 		if (ret < 0) {
 | |
| 			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 				   "Unable to determine location of eeprom, skipping");
 | |
| 		} else {
 | |
| 			hdw->eeprom_addr = ret;
 | |
| 			pvr2_eeprom_analyze(hdw);
 | |
| 			if (!pvr2_hdw_dev_ok(hdw)) return;
 | |
| 		}
 | |
| 	} else {
 | |
| 		hdw->tuner_type = hdw->hdw_desc->default_tuner_type;
 | |
| 		hdw->tuner_updated = !0;
 | |
| 		hdw->std_mask_eeprom = V4L2_STD_ALL;
 | |
| 	}
 | |
| 
 | |
| 	if (hdw->serial_number) {
 | |
| 		idx = scnprintf(hdw->identifier, sizeof(hdw->identifier) - 1,
 | |
| 				"sn-%lu", hdw->serial_number);
 | |
| 	} else if (hdw->unit_number >= 0) {
 | |
| 		idx = scnprintf(hdw->identifier, sizeof(hdw->identifier) - 1,
 | |
| 				"unit-%c",
 | |
| 				hdw->unit_number + 'a');
 | |
| 	} else {
 | |
| 		idx = scnprintf(hdw->identifier, sizeof(hdw->identifier) - 1,
 | |
| 				"unit-??");
 | |
| 	}
 | |
| 	hdw->identifier[idx] = 0;
 | |
| 
 | |
| 	pvr2_hdw_setup_std(hdw);
 | |
| 
 | |
| 	if (!get_default_tuner_type(hdw)) {
 | |
| 		pvr2_trace(PVR2_TRACE_INIT,
 | |
| 			   "pvr2_hdw_setup: Tuner type overridden to %d",
 | |
| 			   hdw->tuner_type);
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	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();
 | |
| 	if (!pvr2_hdw_dev_ok(hdw)) return;
 | |
| 	pvr2_trace(PVR2_TRACE_INIT,
 | |
| 		   "pvr2_hdw_setup: video stream is %p",hdw->vid_stream);
 | |
| 	if (hdw->vid_stream) {
 | |
| 		idx = get_default_error_tolerance(hdw);
 | |
| 		if (idx) {
 | |
| 			pvr2_trace(PVR2_TRACE_INIT,
 | |
| 				   "pvr2_hdw_setup: video stream %p setting tolerance %u",
 | |
| 				   hdw->vid_stream,idx);
 | |
| 		}
 | |
| 		pvr2_stream_setup(hdw->vid_stream,hdw->usb_dev,
 | |
| 				  PVR2_VID_ENDPOINT,idx);
 | |
| 	}
 | |
| 
 | |
| 	if (!pvr2_hdw_dev_ok(hdw)) return;
 | |
| 
 | |
| 	hdw->flag_init_ok = !0;
 | |
| 
 | |
| 	pvr2_hdw_state_sched(hdw);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Set up the structure and attempt to put the device into a usable state.
 | |
|    This can be a time-consuming operation, which is why it is not done
 | |
|    internally as part of the create() step. */
 | |
| static void pvr2_hdw_setup(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_setup(hdw=%p) begin",hdw);
 | |
| 	do {
 | |
| 		pvr2_hdw_setup_low(hdw);
 | |
| 		pvr2_trace(PVR2_TRACE_INIT,
 | |
| 			   "pvr2_hdw_setup(hdw=%p) done, ok=%d init_ok=%d",
 | |
| 			   hdw,pvr2_hdw_dev_ok(hdw),hdw->flag_init_ok);
 | |
| 		if (pvr2_hdw_dev_ok(hdw)) {
 | |
| 			if (hdw->flag_init_ok) {
 | |
| 				pvr2_trace(
 | |
| 					PVR2_TRACE_INFO,
 | |
| 					"Device initialization completed successfully.");
 | |
| 				break;
 | |
| 			}
 | |
| 			if (hdw->fw1_state == FW1_STATE_RELOAD) {
 | |
| 				pvr2_trace(
 | |
| 					PVR2_TRACE_INFO,
 | |
| 					"Device microcontroller firmware (re)loaded; it should now reset and reconnect.");
 | |
| 				break;
 | |
| 			}
 | |
| 			pvr2_trace(
 | |
| 				PVR2_TRACE_ERROR_LEGS,
 | |
| 				"Device initialization was not successful.");
 | |
| 			if (hdw->fw1_state == FW1_STATE_MISSING) {
 | |
| 				pvr2_trace(
 | |
| 					PVR2_TRACE_ERROR_LEGS,
 | |
| 					"Giving up since device microcontroller firmware appears to be missing.");
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 		if (hdw->flag_modulefail) {
 | |
| 			pvr2_trace(
 | |
| 				PVR2_TRACE_ERROR_LEGS,
 | |
| 				"***WARNING*** pvrusb2 driver initialization failed due to the failure of one or more sub-device kernel modules.");
 | |
| 			pvr2_trace(
 | |
| 				PVR2_TRACE_ERROR_LEGS,
 | |
| 				"You need to resolve the failing condition before this driver can function.  There should be some earlier messages giving more information about the problem.");
 | |
| 			break;
 | |
| 		}
 | |
| 		if (procreload) {
 | |
| 			pvr2_trace(
 | |
| 				PVR2_TRACE_ERROR_LEGS,
 | |
| 				"Attempting pvrusb2 recovery by reloading primary firmware.");
 | |
| 			pvr2_trace(
 | |
| 				PVR2_TRACE_ERROR_LEGS,
 | |
| 				"If this works, device should disconnect and reconnect in a sane state.");
 | |
| 			hdw->fw1_state = FW1_STATE_UNKNOWN;
 | |
| 			pvr2_upload_firmware1(hdw);
 | |
| 		} else {
 | |
| 			pvr2_trace(
 | |
| 				PVR2_TRACE_ERROR_LEGS,
 | |
| 				"***WARNING*** pvrusb2 device hardware appears to be jammed and I can't clear it.");
 | |
| 			pvr2_trace(
 | |
| 				PVR2_TRACE_ERROR_LEGS,
 | |
| 				"You might need to power cycle the pvrusb2 device in order to recover.");
 | |
| 		}
 | |
| 	} while (0);
 | |
| 	pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_setup(hdw=%p) end",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). */
 | |
| int pvr2_hdw_initialize(struct pvr2_hdw *hdw,
 | |
| 			void (*callback_func)(void *),
 | |
| 			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);
 | |
| 	return hdw->flag_init_ok;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* 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)
 | |
| {
 | |
| 	unsigned int idx,cnt1,cnt2,m;
 | |
| 	struct pvr2_hdw *hdw = NULL;
 | |
| 	int valid_std_mask;
 | |
| 	struct pvr2_ctrl *cptr;
 | |
| 	struct usb_device *usb_dev;
 | |
| 	const struct pvr2_device_desc *hdw_desc;
 | |
| 	__u8 ifnum;
 | |
| 	struct v4l2_queryctrl qctrl;
 | |
| 	struct pvr2_ctl_info *ciptr;
 | |
| 
 | |
| 	usb_dev = interface_to_usbdev(intf);
 | |
| 
 | |
| 	hdw_desc = (const struct pvr2_device_desc *)(devid->driver_info);
 | |
| 
 | |
| 	if (hdw_desc == NULL) {
 | |
| 		pvr2_trace(PVR2_TRACE_INIT, "pvr2_hdw_create: No device description pointer, unable to continue.");
 | |
| 		pvr2_trace(PVR2_TRACE_INIT,
 | |
| 			   "If you have a new device type, please contact Mike Isely <isely@pobox.com> to get it included in the driver");
 | |
| 		goto fail;
 | |
| 	}
 | |
| 
 | |
| 	hdw = kzalloc(sizeof(*hdw),GFP_KERNEL);
 | |
| 	pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_create: hdw=%p, type \"%s\"",
 | |
| 		   hdw,hdw_desc->description);
 | |
| 	pvr2_trace(PVR2_TRACE_INFO, "Hardware description: %s",
 | |
| 		hdw_desc->description);
 | |
| 	if (hdw_desc->flag_is_experimental) {
 | |
| 		pvr2_trace(PVR2_TRACE_INFO, "**********");
 | |
| 		pvr2_trace(PVR2_TRACE_INFO,
 | |
| 			   "***WARNING*** Support for this device (%s) is experimental.",
 | |
| 							      hdw_desc->description);
 | |
| 		pvr2_trace(PVR2_TRACE_INFO,
 | |
| 			   "Important functionality might not be entirely working.");
 | |
| 		pvr2_trace(PVR2_TRACE_INFO,
 | |
| 			   "Please consider contacting the driver author to help with further stabilization of the driver.");
 | |
| 		pvr2_trace(PVR2_TRACE_INFO, "**********");
 | |
| 	}
 | |
| 	if (!hdw) goto fail;
 | |
| 
 | |
| 	timer_setup(&hdw->quiescent_timer, pvr2_hdw_quiescent_timeout, 0);
 | |
| 
 | |
| 	timer_setup(&hdw->decoder_stabilization_timer,
 | |
| 		    pvr2_hdw_decoder_stabilization_timeout, 0);
 | |
| 
 | |
| 	timer_setup(&hdw->encoder_wait_timer, pvr2_hdw_encoder_wait_timeout,
 | |
| 		    0);
 | |
| 
 | |
| 	timer_setup(&hdw->encoder_run_timer, pvr2_hdw_encoder_run_timeout, 0);
 | |
| 
 | |
| 	hdw->master_state = PVR2_STATE_DEAD;
 | |
| 
 | |
| 	init_waitqueue_head(&hdw->state_wait_data);
 | |
| 
 | |
| 	hdw->tuner_signal_stale = !0;
 | |
| 	cx2341x_fill_defaults(&hdw->enc_ctl_state);
 | |
| 
 | |
| 	/* Calculate which inputs are OK */
 | |
| 	m = 0;
 | |
| 	if (hdw_desc->flag_has_analogtuner) m |= 1 << PVR2_CVAL_INPUT_TV;
 | |
| 	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;
 | |
| 	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. */
 | |
| 	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 = kcalloc(hdw->control_cnt, sizeof(struct pvr2_ctrl),
 | |
| 				GFP_KERNEL);
 | |
| 	if (!hdw->controls) goto fail;
 | |
| 	hdw->hdw_desc = hdw_desc;
 | |
| 	hdw->ir_scheme_active = hdw->hdw_desc->ir_scheme;
 | |
| 	for (idx = 0; idx < hdw->control_cnt; idx++) {
 | |
| 		cptr = hdw->controls + idx;
 | |
| 		cptr->hdw = hdw;
 | |
| 	}
 | |
| 	for (idx = 0; idx < 32; idx++) {
 | |
| 		hdw->std_mask_ptrs[idx] = hdw->std_mask_names[idx];
 | |
| 	}
 | |
| 	for (idx = 0; idx < CTRLDEF_COUNT; idx++) {
 | |
| 		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 (!((1UL << idx) & m)) continue;
 | |
| 		hdw->input_val = idx;
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	/* Define and configure additional controls from cx2341x module. */
 | |
| 	hdw->mpeg_ctrl_info = kcalloc(MPEGDEF_COUNT,
 | |
| 				      sizeof(*(hdw->mpeg_ctrl_info)),
 | |
| 				      GFP_KERNEL);
 | |
| 	if (!hdw->mpeg_ctrl_info) goto fail;
 | |
| 	for (idx = 0; idx < MPEGDEF_COUNT; idx++) {
 | |
| 		cptr = hdw->controls + idx + CTRLDEF_COUNT;
 | |
| 		ciptr = &(hdw->mpeg_ctrl_info[idx].info);
 | |
| 		ciptr->desc = hdw->mpeg_ctrl_info[idx].desc;
 | |
| 		ciptr->name = mpeg_ids[idx].strid;
 | |
| 		ciptr->v4l_id = mpeg_ids[idx].id;
 | |
| 		ciptr->skip_init = !0;
 | |
| 		ciptr->get_value = ctrl_cx2341x_get;
 | |
| 		ciptr->get_v4lflags = ctrl_cx2341x_getv4lflags;
 | |
| 		ciptr->is_dirty = ctrl_cx2341x_is_dirty;
 | |
| 		if (!idx) ciptr->clear_dirty = ctrl_cx2341x_clear_dirty;
 | |
| 		qctrl.id = ciptr->v4l_id;
 | |
| 		cx2341x_ctrl_query(&hdw->enc_ctl_state,&qctrl);
 | |
| 		if (!(qctrl.flags & V4L2_CTRL_FLAG_READ_ONLY)) {
 | |
| 			ciptr->set_value = ctrl_cx2341x_set;
 | |
| 		}
 | |
| 		strscpy(hdw->mpeg_ctrl_info[idx].desc, qctrl.name,
 | |
| 			sizeof(hdw->mpeg_ctrl_info[idx].desc));
 | |
| 		ciptr->default_value = qctrl.default_value;
 | |
| 		switch (qctrl.type) {
 | |
| 		default:
 | |
| 		case V4L2_CTRL_TYPE_INTEGER:
 | |
| 			ciptr->type = pvr2_ctl_int;
 | |
| 			ciptr->def.type_int.min_value = qctrl.minimum;
 | |
| 			ciptr->def.type_int.max_value = qctrl.maximum;
 | |
| 			break;
 | |
| 		case V4L2_CTRL_TYPE_BOOLEAN:
 | |
| 			ciptr->type = pvr2_ctl_bool;
 | |
| 			break;
 | |
| 		case V4L2_CTRL_TYPE_MENU:
 | |
| 			ciptr->type = pvr2_ctl_enum;
 | |
| 			ciptr->def.type_enum.value_names =
 | |
| 				cx2341x_ctrl_get_menu(&hdw->enc_ctl_state,
 | |
| 								ciptr->v4l_id);
 | |
| 			for (cnt1 = 0;
 | |
| 			     ciptr->def.type_enum.value_names[cnt1] != NULL;
 | |
| 			     cnt1++) { }
 | |
| 			ciptr->def.type_enum.count = cnt1;
 | |
| 			break;
 | |
| 		}
 | |
| 		cptr->info = ciptr;
 | |
| 	}
 | |
| 
 | |
| 	// Initialize control data regarding video standard masks
 | |
| 	valid_std_mask = pvr2_std_get_usable();
 | |
| 	for (idx = 0; idx < 32; idx++) {
 | |
| 		if (!(valid_std_mask & (1UL << idx))) continue;
 | |
| 		cnt1 = pvr2_std_id_to_str(
 | |
| 			hdw->std_mask_names[idx],
 | |
| 			sizeof(hdw->std_mask_names[idx])-1,
 | |
| 			1UL << idx);
 | |
| 		hdw->std_mask_names[idx][cnt1] = 0;
 | |
| 	}
 | |
| 	cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDAVAIL);
 | |
| 	if (cptr) {
 | |
| 		memcpy(&hdw->std_info_avail,cptr->info,
 | |
| 		       sizeof(hdw->std_info_avail));
 | |
| 		cptr->info = &hdw->std_info_avail;
 | |
| 		hdw->std_info_avail.def.type_bitmask.bit_names =
 | |
| 			hdw->std_mask_ptrs;
 | |
| 		hdw->std_info_avail.def.type_bitmask.valid_bits =
 | |
| 			valid_std_mask;
 | |
| 	}
 | |
| 	cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR);
 | |
| 	if (cptr) {
 | |
| 		memcpy(&hdw->std_info_cur,cptr->info,
 | |
| 		       sizeof(hdw->std_info_cur));
 | |
| 		cptr->info = &hdw->std_info_cur;
 | |
| 		hdw->std_info_cur.def.type_bitmask.bit_names =
 | |
| 			hdw->std_mask_ptrs;
 | |
| 		hdw->std_info_cur.def.type_bitmask.valid_bits =
 | |
| 			valid_std_mask;
 | |
| 	}
 | |
| 	cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDDETECT);
 | |
| 	if (cptr) {
 | |
| 		memcpy(&hdw->std_info_detect,cptr->info,
 | |
| 		       sizeof(hdw->std_info_detect));
 | |
| 		cptr->info = &hdw->std_info_detect;
 | |
| 		hdw->std_info_detect.def.type_bitmask.bit_names =
 | |
| 			hdw->std_mask_ptrs;
 | |
| 		hdw->std_info_detect.def.type_bitmask.valid_bits =
 | |
| 			valid_std_mask;
 | |
| 	}
 | |
| 
 | |
| 	hdw->cropcap_stale = !0;
 | |
| 	hdw->eeprom_addr = -1;
 | |
| 	hdw->unit_number = -1;
 | |
| 	hdw->v4l_minor_number_video = -1;
 | |
| 	hdw->v4l_minor_number_vbi = -1;
 | |
| 	hdw->v4l_minor_number_radio = -1;
 | |
| 	hdw->ctl_write_buffer = kmalloc(PVR2_CTL_BUFFSIZE,GFP_KERNEL);
 | |
| 	if (!hdw->ctl_write_buffer) goto fail;
 | |
| 	hdw->ctl_read_buffer = kmalloc(PVR2_CTL_BUFFSIZE,GFP_KERNEL);
 | |
| 	if (!hdw->ctl_read_buffer) goto fail;
 | |
| 	hdw->ctl_write_urb = usb_alloc_urb(0,GFP_KERNEL);
 | |
| 	if (!hdw->ctl_write_urb) goto fail;
 | |
| 	hdw->ctl_read_urb = usb_alloc_urb(0,GFP_KERNEL);
 | |
| 	if (!hdw->ctl_read_urb) goto fail;
 | |
| 
 | |
| 	if (v4l2_device_register(&intf->dev, &hdw->v4l2_dev) != 0) {
 | |
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 			   "Error registering with v4l core, giving up");
 | |
| 		goto fail;
 | |
| 	}
 | |
| 	mutex_lock(&pvr2_unit_mtx);
 | |
| 	do {
 | |
| 		for (idx = 0; idx < PVR_NUM; idx++) {
 | |
| 			if (unit_pointers[idx]) continue;
 | |
| 			hdw->unit_number = idx;
 | |
| 			unit_pointers[idx] = hdw;
 | |
| 			break;
 | |
| 		}
 | |
| 	} while (0);
 | |
| 	mutex_unlock(&pvr2_unit_mtx);
 | |
| 
 | |
| 	INIT_WORK(&hdw->workpoll, pvr2_hdw_worker_poll);
 | |
| 
 | |
| 	if (hdw->unit_number == -1)
 | |
| 		goto fail;
 | |
| 
 | |
| 	cnt1 = 0;
 | |
| 	cnt2 = scnprintf(hdw->name+cnt1,sizeof(hdw->name)-cnt1,"pvrusb2");
 | |
| 	cnt1 += cnt2;
 | |
| 	if (hdw->unit_number >= 0) {
 | |
| 		cnt2 = scnprintf(hdw->name+cnt1,sizeof(hdw->name)-cnt1,"_%c",
 | |
| 				 ('a' + hdw->unit_number));
 | |
| 		cnt1 += cnt2;
 | |
| 	}
 | |
| 	if (cnt1 >= sizeof(hdw->name)) cnt1 = sizeof(hdw->name)-1;
 | |
| 	hdw->name[cnt1] = 0;
 | |
| 
 | |
| 	pvr2_trace(PVR2_TRACE_INIT,"Driver unit number is %d, name is %s",
 | |
| 		   hdw->unit_number,hdw->name);
 | |
| 
 | |
| 	hdw->tuner_type = -1;
 | |
| 	hdw->flag_ok = !0;
 | |
| 
 | |
| 	hdw->usb_intf = intf;
 | |
| 	hdw->usb_dev = usb_dev;
 | |
| 
 | |
| 	usb_make_path(hdw->usb_dev, hdw->bus_info, sizeof(hdw->bus_info));
 | |
| 
 | |
| 	ifnum = hdw->usb_intf->cur_altsetting->desc.bInterfaceNumber;
 | |
| 	usb_set_interface(hdw->usb_dev,ifnum,0);
 | |
| 
 | |
| 	mutex_init(&hdw->ctl_lock_mutex);
 | |
| 	mutex_init(&hdw->big_lock_mutex);
 | |
| 
 | |
| 	return hdw;
 | |
|  fail:
 | |
| 	if (hdw) {
 | |
| 		del_timer_sync(&hdw->quiescent_timer);
 | |
| 		del_timer_sync(&hdw->decoder_stabilization_timer);
 | |
| 		del_timer_sync(&hdw->encoder_run_timer);
 | |
| 		del_timer_sync(&hdw->encoder_wait_timer);
 | |
| 		flush_work(&hdw->workpoll);
 | |
| 		v4l2_device_unregister(&hdw->v4l2_dev);
 | |
| 		usb_free_urb(hdw->ctl_read_urb);
 | |
| 		usb_free_urb(hdw->ctl_write_urb);
 | |
| 		kfree(hdw->ctl_read_buffer);
 | |
| 		kfree(hdw->ctl_write_buffer);
 | |
| 		kfree(hdw->controls);
 | |
| 		kfree(hdw->mpeg_ctrl_info);
 | |
| 		kfree(hdw);
 | |
| 	}
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Remove _all_ associations between this driver and the underlying USB
 | |
|    layer. */
 | |
| static void pvr2_hdw_remove_usb_stuff(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	if (hdw->flag_disconnected) return;
 | |
| 	pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_remove_usb_stuff: hdw=%p",hdw);
 | |
| 	if (hdw->ctl_read_urb) {
 | |
| 		usb_kill_urb(hdw->ctl_read_urb);
 | |
| 		usb_free_urb(hdw->ctl_read_urb);
 | |
| 		hdw->ctl_read_urb = NULL;
 | |
| 	}
 | |
| 	if (hdw->ctl_write_urb) {
 | |
| 		usb_kill_urb(hdw->ctl_write_urb);
 | |
| 		usb_free_urb(hdw->ctl_write_urb);
 | |
| 		hdw->ctl_write_urb = NULL;
 | |
| 	}
 | |
| 	if (hdw->ctl_read_buffer) {
 | |
| 		kfree(hdw->ctl_read_buffer);
 | |
| 		hdw->ctl_read_buffer = NULL;
 | |
| 	}
 | |
| 	if (hdw->ctl_write_buffer) {
 | |
| 		kfree(hdw->ctl_write_buffer);
 | |
| 		hdw->ctl_write_buffer = NULL;
 | |
| 	}
 | |
| 	hdw->flag_disconnected = !0;
 | |
| 	/* If we don't do this, then there will be a dangling struct device
 | |
| 	   reference to our disappearing device persisting inside the V4L
 | |
| 	   core... */
 | |
| 	v4l2_device_disconnect(&hdw->v4l2_dev);
 | |
| 	hdw->usb_dev = NULL;
 | |
| 	hdw->usb_intf = NULL;
 | |
| 	pvr2_hdw_render_useless(hdw);
 | |
| }
 | |
| 
 | |
| void pvr2_hdw_set_v4l2_dev(struct pvr2_hdw *hdw, struct video_device *vdev)
 | |
| {
 | |
| 	vdev->v4l2_dev = &hdw->v4l2_dev;
 | |
| }
 | |
| 
 | |
| /* Destroy hardware interaction structure */
 | |
| void pvr2_hdw_destroy(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	if (!hdw) return;
 | |
| 	pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_destroy: hdw=%p",hdw);
 | |
| 	flush_work(&hdw->workpoll);
 | |
| 	del_timer_sync(&hdw->quiescent_timer);
 | |
| 	del_timer_sync(&hdw->decoder_stabilization_timer);
 | |
| 	del_timer_sync(&hdw->encoder_run_timer);
 | |
| 	del_timer_sync(&hdw->encoder_wait_timer);
 | |
| 	if (hdw->fw_buffer) {
 | |
| 		kfree(hdw->fw_buffer);
 | |
| 		hdw->fw_buffer = NULL;
 | |
| 	}
 | |
| 	if (hdw->vid_stream) {
 | |
| 		pvr2_stream_destroy(hdw->vid_stream);
 | |
| 		hdw->vid_stream = NULL;
 | |
| 	}
 | |
| 	v4l2_device_unregister(&hdw->v4l2_dev);
 | |
| 	pvr2_hdw_disconnect(hdw);
 | |
| 	mutex_lock(&pvr2_unit_mtx);
 | |
| 	do {
 | |
| 		if ((hdw->unit_number >= 0) &&
 | |
| 		    (hdw->unit_number < PVR_NUM) &&
 | |
| 		    (unit_pointers[hdw->unit_number] == hdw)) {
 | |
| 			unit_pointers[hdw->unit_number] = NULL;
 | |
| 		}
 | |
| 	} while (0);
 | |
| 	mutex_unlock(&pvr2_unit_mtx);
 | |
| 	kfree(hdw->controls);
 | |
| 	kfree(hdw->mpeg_ctrl_info);
 | |
| 	kfree(hdw);
 | |
| }
 | |
| 
 | |
| 
 | |
| int pvr2_hdw_dev_ok(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	return (hdw && hdw->flag_ok);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Called when hardware has been unplugged */
 | |
| void pvr2_hdw_disconnect(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_disconnect(hdw=%p)",hdw);
 | |
| 	LOCK_TAKE(hdw->big_lock);
 | |
| 	pvr2_i2c_core_done(hdw);
 | |
| 	LOCK_TAKE(hdw->ctl_lock);
 | |
| 	pvr2_hdw_remove_usb_stuff(hdw);
 | |
| 	LOCK_GIVE(hdw->ctl_lock);
 | |
| 	LOCK_GIVE(hdw->big_lock);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Get the number of defined controls */
 | |
| unsigned int pvr2_hdw_get_ctrl_count(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	return hdw->control_cnt;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Retrieve a control handle given its index (0..count-1) */
 | |
| struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_index(struct pvr2_hdw *hdw,
 | |
| 					     unsigned int idx)
 | |
| {
 | |
| 	if (idx >= hdw->control_cnt) return NULL;
 | |
| 	return hdw->controls + idx;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Retrieve a control handle given its index (0..count-1) */
 | |
| struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_id(struct pvr2_hdw *hdw,
 | |
| 					  unsigned int ctl_id)
 | |
| {
 | |
| 	struct pvr2_ctrl *cptr;
 | |
| 	unsigned int idx;
 | |
| 	int i;
 | |
| 
 | |
| 	/* This could be made a lot more efficient, but for now... */
 | |
| 	for (idx = 0; idx < hdw->control_cnt; idx++) {
 | |
| 		cptr = hdw->controls + idx;
 | |
| 		i = cptr->info->internal_id;
 | |
| 		if (i && (i == ctl_id)) return cptr;
 | |
| 	}
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Given a V4L ID, retrieve the control structure associated with it. */
 | |
| struct pvr2_ctrl *pvr2_hdw_get_ctrl_v4l(struct pvr2_hdw *hdw,unsigned int ctl_id)
 | |
| {
 | |
| 	struct pvr2_ctrl *cptr;
 | |
| 	unsigned int idx;
 | |
| 	int i;
 | |
| 
 | |
| 	/* This could be made a lot more efficient, but for now... */
 | |
| 	for (idx = 0; idx < hdw->control_cnt; idx++) {
 | |
| 		cptr = hdw->controls + idx;
 | |
| 		i = cptr->info->v4l_id;
 | |
| 		if (i && (i == ctl_id)) return cptr;
 | |
| 	}
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Given a V4L ID for its immediate predecessor, retrieve the control
 | |
|    structure associated with it. */
 | |
| struct pvr2_ctrl *pvr2_hdw_get_ctrl_nextv4l(struct pvr2_hdw *hdw,
 | |
| 					    unsigned int ctl_id)
 | |
| {
 | |
| 	struct pvr2_ctrl *cptr,*cp2;
 | |
| 	unsigned int idx;
 | |
| 	int i;
 | |
| 
 | |
| 	/* This could be made a lot more efficient, but for now... */
 | |
| 	cp2 = NULL;
 | |
| 	for (idx = 0; idx < hdw->control_cnt; idx++) {
 | |
| 		cptr = hdw->controls + idx;
 | |
| 		i = cptr->info->v4l_id;
 | |
| 		if (!i) continue;
 | |
| 		if (i <= ctl_id) continue;
 | |
| 		if (cp2 && (cp2->info->v4l_id < i)) continue;
 | |
| 		cp2 = cptr;
 | |
| 	}
 | |
| 	return cp2;
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| static const char *get_ctrl_typename(enum pvr2_ctl_type tp)
 | |
| {
 | |
| 	switch (tp) {
 | |
| 	case pvr2_ctl_int: return "integer";
 | |
| 	case pvr2_ctl_enum: return "enum";
 | |
| 	case pvr2_ctl_bool: return "boolean";
 | |
| 	case pvr2_ctl_bitmask: return "bitmask";
 | |
| 	}
 | |
| 	return "";
 | |
| }
 | |
| 
 | |
| 
 | |
| static void pvr2_subdev_set_control(struct pvr2_hdw *hdw, int id,
 | |
| 				    const char *name, int val)
 | |
| {
 | |
| 	struct v4l2_control ctrl;
 | |
| 	struct v4l2_subdev *sd;
 | |
| 
 | |
| 	pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 %s=%d", name, val);
 | |
| 	memset(&ctrl, 0, sizeof(ctrl));
 | |
| 	ctrl.id = id;
 | |
| 	ctrl.value = val;
 | |
| 
 | |
| 	v4l2_device_for_each_subdev(sd, &hdw->v4l2_dev)
 | |
| 		v4l2_s_ctrl(NULL, sd->ctrl_handler, &ctrl);
 | |
| }
 | |
| 
 | |
| #define PVR2_SUBDEV_SET_CONTROL(hdw, id, lab) \
 | |
| 	if ((hdw)->lab##_dirty || (hdw)->force_dirty) {		\
 | |
| 		pvr2_subdev_set_control(hdw, id, #lab, (hdw)->lab##_val); \
 | |
| 	}
 | |
| 
 | |
| static v4l2_std_id pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	v4l2_std_id std;
 | |
| 	std = (v4l2_std_id)hdw->std_mask_avail;
 | |
| 	v4l2_device_call_all(&hdw->v4l2_dev, 0,
 | |
| 			     video, querystd, &std);
 | |
| 	return std;
 | |
| }
 | |
| 
 | |
| /* Execute whatever commands are required to update the state of all the
 | |
|    sub-devices so that they match our current control values. */
 | |
| static void pvr2_subdev_update(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	struct v4l2_subdev *sd;
 | |
| 	unsigned int id;
 | |
| 	pvr2_subdev_update_func fp;
 | |
| 
 | |
| 	pvr2_trace(PVR2_TRACE_CHIPS, "subdev update...");
 | |
| 
 | |
| 	if (hdw->tuner_updated || hdw->force_dirty) {
 | |
| 		struct tuner_setup setup;
 | |
| 		pvr2_trace(PVR2_TRACE_CHIPS, "subdev tuner set_type(%d)",
 | |
| 			   hdw->tuner_type);
 | |
| 		if (((int)(hdw->tuner_type)) >= 0) {
 | |
| 			memset(&setup, 0, sizeof(setup));
 | |
| 			setup.addr = ADDR_UNSET;
 | |
| 			setup.type = hdw->tuner_type;
 | |
| 			setup.mode_mask = T_RADIO | T_ANALOG_TV;
 | |
| 			v4l2_device_call_all(&hdw->v4l2_dev, 0,
 | |
| 					     tuner, s_type_addr, &setup);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (hdw->input_dirty || hdw->std_dirty || hdw->force_dirty) {
 | |
| 		pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_standard");
 | |
| 		if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
 | |
| 			v4l2_device_call_all(&hdw->v4l2_dev, 0,
 | |
| 					     tuner, s_radio);
 | |
| 		} else {
 | |
| 			v4l2_std_id vs;
 | |
| 			vs = hdw->std_mask_cur;
 | |
| 			v4l2_device_call_all(&hdw->v4l2_dev, 0,
 | |
| 					     video, s_std, vs);
 | |
| 			pvr2_hdw_cx25840_vbi_hack(hdw);
 | |
| 		}
 | |
| 		hdw->tuner_signal_stale = !0;
 | |
| 		hdw->cropcap_stale = !0;
 | |
| 	}
 | |
| 
 | |
| 	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_BRIGHTNESS, brightness);
 | |
| 	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_CONTRAST, contrast);
 | |
| 	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_SATURATION, saturation);
 | |
| 	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_HUE, hue);
 | |
| 	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_MUTE, mute);
 | |
| 	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_VOLUME, volume);
 | |
| 	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_BALANCE, balance);
 | |
| 	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_BASS, bass);
 | |
| 	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_TREBLE, treble);
 | |
| 
 | |
| 	if (hdw->input_dirty || hdw->audiomode_dirty || hdw->force_dirty) {
 | |
| 		struct v4l2_tuner vt;
 | |
| 		memset(&vt, 0, sizeof(vt));
 | |
| 		vt.type = (hdw->input_val == PVR2_CVAL_INPUT_RADIO) ?
 | |
| 			V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
 | |
| 		vt.audmode = hdw->audiomode_val;
 | |
| 		v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, s_tuner, &vt);
 | |
| 	}
 | |
| 
 | |
| 	if (hdw->freqDirty || hdw->force_dirty) {
 | |
| 		unsigned long fv;
 | |
| 		struct v4l2_frequency freq;
 | |
| 		fv = pvr2_hdw_get_cur_freq(hdw);
 | |
| 		pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_freq(%lu)", fv);
 | |
| 		if (hdw->tuner_signal_stale) pvr2_hdw_status_poll(hdw);
 | |
| 		memset(&freq, 0, sizeof(freq));
 | |
| 		if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) {
 | |
| 			/* ((fv * 1000) / 62500) */
 | |
| 			freq.frequency = (fv * 2) / 125;
 | |
| 		} else {
 | |
| 			freq.frequency = fv / 62500;
 | |
| 		}
 | |
| 		/* tuner-core currently doesn't seem to care about this, but
 | |
| 		   let's set it anyway for completeness. */
 | |
| 		if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
 | |
| 			freq.type = V4L2_TUNER_RADIO;
 | |
| 		} else {
 | |
| 			freq.type = V4L2_TUNER_ANALOG_TV;
 | |
| 		}
 | |
| 		freq.tuner = 0;
 | |
| 		v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner,
 | |
| 				     s_frequency, &freq);
 | |
| 	}
 | |
| 
 | |
| 	if (hdw->res_hor_dirty || hdw->res_ver_dirty || hdw->force_dirty) {
 | |
| 		struct v4l2_subdev_format format = {
 | |
| 			.which = V4L2_SUBDEV_FORMAT_ACTIVE,
 | |
| 		};
 | |
| 
 | |
| 		format.format.width = hdw->res_hor_val;
 | |
| 		format.format.height = hdw->res_ver_val;
 | |
| 		format.format.code = MEDIA_BUS_FMT_FIXED;
 | |
| 		pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_size(%dx%d)",
 | |
| 			   format.format.width, format.format.height);
 | |
| 		v4l2_device_call_all(&hdw->v4l2_dev, 0, pad, set_fmt,
 | |
| 				     NULL, &format);
 | |
| 	}
 | |
| 
 | |
| 	if (hdw->srate_dirty || hdw->force_dirty) {
 | |
| 		u32 val;
 | |
| 		pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_audio %d",
 | |
| 			   hdw->srate_val);
 | |
| 		switch (hdw->srate_val) {
 | |
| 		default:
 | |
| 		case V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000:
 | |
| 			val = 48000;
 | |
| 			break;
 | |
| 		case V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100:
 | |
| 			val = 44100;
 | |
| 			break;
 | |
| 		case V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000:
 | |
| 			val = 32000;
 | |
| 			break;
 | |
| 		}
 | |
| 		v4l2_device_call_all(&hdw->v4l2_dev, 0,
 | |
| 				     audio, s_clock_freq, val);
 | |
| 	}
 | |
| 
 | |
| 	/* Unable to set crop parameters; there is apparently no equivalent
 | |
| 	   for VIDIOC_S_CROP */
 | |
| 
 | |
| 	v4l2_device_for_each_subdev(sd, &hdw->v4l2_dev) {
 | |
| 		id = sd->grp_id;
 | |
| 		if (id >= ARRAY_SIZE(pvr2_module_update_functions)) continue;
 | |
| 		fp = pvr2_module_update_functions[id];
 | |
| 		if (!fp) continue;
 | |
| 		(*fp)(hdw, sd);
 | |
| 	}
 | |
| 
 | |
| 	if (hdw->tuner_signal_stale || hdw->cropcap_stale) {
 | |
| 		pvr2_hdw_status_poll(hdw);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Figure out if we need to commit control changes.  If so, mark internal
 | |
|    state flags to indicate this fact and return true.  Otherwise do nothing
 | |
|    else and return false. */
 | |
| static int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	unsigned int idx;
 | |
| 	struct pvr2_ctrl *cptr;
 | |
| 	int value;
 | |
| 	int commit_flag = hdw->force_dirty;
 | |
| 	char buf[100];
 | |
| 	unsigned int bcnt,ccnt;
 | |
| 
 | |
| 	for (idx = 0; idx < hdw->control_cnt; idx++) {
 | |
| 		cptr = hdw->controls + idx;
 | |
| 		if (!cptr->info->is_dirty) continue;
 | |
| 		if (!cptr->info->is_dirty(cptr)) continue;
 | |
| 		commit_flag = !0;
 | |
| 
 | |
| 		if (!(pvrusb2_debug & PVR2_TRACE_CTL)) continue;
 | |
| 		bcnt = scnprintf(buf,sizeof(buf),"\"%s\" <-- ",
 | |
| 				 cptr->info->name);
 | |
| 		value = 0;
 | |
| 		cptr->info->get_value(cptr,&value);
 | |
| 		pvr2_ctrl_value_to_sym_internal(cptr,~0,value,
 | |
| 						buf+bcnt,
 | |
| 						sizeof(buf)-bcnt,&ccnt);
 | |
| 		bcnt += ccnt;
 | |
| 		bcnt += scnprintf(buf+bcnt,sizeof(buf)-bcnt," <%s>",
 | |
| 				  get_ctrl_typename(cptr->info->type));
 | |
| 		pvr2_trace(PVR2_TRACE_CTL,
 | |
| 			   "/*--TRACE_COMMIT--*/ %.*s",
 | |
| 			   bcnt,buf);
 | |
| 	}
 | |
| 
 | |
| 	if (!commit_flag) {
 | |
| 		/* Nothing has changed */
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	hdw->state_pipeline_config = 0;
 | |
| 	trace_stbit("state_pipeline_config",hdw->state_pipeline_config);
 | |
| 	pvr2_hdw_state_sched(hdw);
 | |
| 
 | |
| 	return !0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Perform all operations needed to commit all control changes.  This must
 | |
|    be performed in synchronization with the pipeline state and is thus
 | |
|    expected to be called as part of the driver's worker thread.  Return
 | |
|    true if commit successful, otherwise return false to indicate that
 | |
|    commit isn't possible at this time. */
 | |
| static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	unsigned int idx;
 | |
| 	struct pvr2_ctrl *cptr;
 | |
| 	int disruptive_change;
 | |
| 
 | |
| 	if (hdw->input_dirty && hdw->state_pathway_ok &&
 | |
| 	    (((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;
 | |
| 		trace_stbit("state_pathway_ok", hdw->state_pathway_ok);
 | |
| 	}
 | |
| 	if (!hdw->state_pathway_ok) {
 | |
| 		/* Can't commit anything until pathway is ok. */
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	/* Handle some required side effects when the video standard is
 | |
| 	   changed.... */
 | |
| 	if (hdw->std_dirty) {
 | |
| 		int nvres;
 | |
| 		int gop_size;
 | |
| 		if (hdw->std_mask_cur & V4L2_STD_525_60) {
 | |
| 			nvres = 480;
 | |
| 			gop_size = 15;
 | |
| 		} else {
 | |
| 			nvres = 576;
 | |
| 			gop_size = 12;
 | |
| 		}
 | |
| 		/* Rewrite the vertical resolution to be appropriate to the
 | |
| 		   video standard that has been selected. */
 | |
| 		if (nvres != hdw->res_ver_val) {
 | |
| 			hdw->res_ver_val = nvres;
 | |
| 			hdw->res_ver_dirty = !0;
 | |
| 		}
 | |
| 		/* Rewrite the GOP size to be appropriate to the video
 | |
| 		   standard that has been selected. */
 | |
| 		if (gop_size != hdw->enc_ctl_state.video_gop_size) {
 | |
| 			struct v4l2_ext_controls cs;
 | |
| 			struct v4l2_ext_control c1;
 | |
| 			memset(&cs, 0, sizeof(cs));
 | |
| 			memset(&c1, 0, sizeof(c1));
 | |
| 			cs.controls = &c1;
 | |
| 			cs.count = 1;
 | |
| 			c1.id = V4L2_CID_MPEG_VIDEO_GOP_SIZE;
 | |
| 			c1.value = gop_size;
 | |
| 			cx2341x_ext_ctrls(&hdw->enc_ctl_state, 0, &cs,
 | |
| 					  VIDIOC_S_EXT_CTRLS);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* The broadcast decoder can only scale down, so if
 | |
| 	 * res_*_dirty && crop window < output format ==> enlarge crop.
 | |
| 	 *
 | |
| 	 * The mpeg encoder receives fields of res_hor_val dots and
 | |
| 	 * res_ver_val halflines.  Limits: hor<=720, ver<=576.
 | |
| 	 */
 | |
| 	if (hdw->res_hor_dirty && hdw->cropw_val < hdw->res_hor_val) {
 | |
| 		hdw->cropw_val = hdw->res_hor_val;
 | |
| 		hdw->cropw_dirty = !0;
 | |
| 	} else if (hdw->cropw_dirty) {
 | |
| 		hdw->res_hor_dirty = !0;           /* must rescale */
 | |
| 		hdw->res_hor_val = min(720, hdw->cropw_val);
 | |
| 	}
 | |
| 	if (hdw->res_ver_dirty && hdw->croph_val < hdw->res_ver_val) {
 | |
| 		hdw->croph_val = hdw->res_ver_val;
 | |
| 		hdw->croph_dirty = !0;
 | |
| 	} else if (hdw->croph_dirty) {
 | |
| 		int nvres = hdw->std_mask_cur & V4L2_STD_525_60 ? 480 : 576;
 | |
| 		hdw->res_ver_dirty = !0;
 | |
| 		hdw->res_ver_val = min(nvres, hdw->croph_val);
 | |
| 	}
 | |
| 
 | |
| 	/* If any of the below has changed, then we can't do the update
 | |
| 	   while the pipeline is running.  Pipeline must be paused first
 | |
| 	   and decoder -> encoder connection be made quiescent before we
 | |
| 	   can proceed. */
 | |
| 	disruptive_change =
 | |
| 		(hdw->std_dirty ||
 | |
| 		 hdw->enc_unsafe_stale ||
 | |
| 		 hdw->srate_dirty ||
 | |
| 		 hdw->res_ver_dirty ||
 | |
| 		 hdw->res_hor_dirty ||
 | |
| 		 hdw->cropw_dirty ||
 | |
| 		 hdw->croph_dirty ||
 | |
| 		 hdw->input_dirty ||
 | |
| 		 (hdw->active_stream_type != hdw->desired_stream_type));
 | |
| 	if (disruptive_change && !hdw->state_pipeline_idle) {
 | |
| 		/* Pipeline is not idle; we can't proceed.  Arrange to
 | |
| 		   cause pipeline to stop so that we can try this again
 | |
| 		   later.... */
 | |
| 		hdw->state_pipeline_pause = !0;
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	if (hdw->srate_dirty) {
 | |
| 		/* Write new sample rate into control structure since
 | |
| 		 * the master copy is stale.  We must track srate
 | |
| 		 * separate from the mpeg control structure because
 | |
| 		 * other logic also uses this value. */
 | |
| 		struct v4l2_ext_controls cs;
 | |
| 		struct v4l2_ext_control c1;
 | |
| 		memset(&cs,0,sizeof(cs));
 | |
| 		memset(&c1,0,sizeof(c1));
 | |
| 		cs.controls = &c1;
 | |
| 		cs.count = 1;
 | |
| 		c1.id = V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ;
 | |
| 		c1.value = hdw->srate_val;
 | |
| 		cx2341x_ext_ctrls(&hdw->enc_ctl_state, 0, &cs,VIDIOC_S_EXT_CTRLS);
 | |
| 	}
 | |
| 
 | |
| 	if (hdw->active_stream_type != hdw->desired_stream_type) {
 | |
| 		/* Handle any side effects of stream config here */
 | |
| 		hdw->active_stream_type = hdw->desired_stream_type;
 | |
| 	}
 | |
| 
 | |
| 	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);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* Check and update state for all sub-devices. */
 | |
| 	pvr2_subdev_update(hdw);
 | |
| 
 | |
| 	hdw->tuner_updated = 0;
 | |
| 	hdw->force_dirty = 0;
 | |
| 	for (idx = 0; idx < hdw->control_cnt; idx++) {
 | |
| 		cptr = hdw->controls + idx;
 | |
| 		if (!cptr->info->clear_dirty) continue;
 | |
| 		cptr->info->clear_dirty(cptr);
 | |
| 	}
 | |
| 
 | |
| 	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;
 | |
| 	}
 | |
| 
 | |
| 	hdw->state_pipeline_config = !0;
 | |
| 	/* Hardware state may have changed in a way to cause the cropping
 | |
| 	   capabilities to have changed.  So mark it stale, which will
 | |
| 	   cause a later re-fetch. */
 | |
| 	trace_stbit("state_pipeline_config",hdw->state_pipeline_config);
 | |
| 	return !0;
 | |
| }
 | |
| 
 | |
| 
 | |
| int pvr2_hdw_commit_ctl(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	int fl;
 | |
| 	LOCK_TAKE(hdw->big_lock);
 | |
| 	fl = pvr2_hdw_commit_setup(hdw);
 | |
| 	LOCK_GIVE(hdw->big_lock);
 | |
| 	if (!fl) return 0;
 | |
| 	return pvr2_hdw_wait(hdw,0);
 | |
| }
 | |
| 
 | |
| 
 | |
| static void pvr2_hdw_worker_poll(struct work_struct *work)
 | |
| {
 | |
| 	int fl = 0;
 | |
| 	struct pvr2_hdw *hdw = container_of(work,struct pvr2_hdw,workpoll);
 | |
| 	LOCK_TAKE(hdw->big_lock); do {
 | |
| 		fl = pvr2_hdw_state_eval(hdw);
 | |
| 	} while (0); LOCK_GIVE(hdw->big_lock);
 | |
| 	if (fl && hdw->state_func) {
 | |
| 		hdw->state_func(hdw->state_data);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| static int pvr2_hdw_wait(struct pvr2_hdw *hdw,int state)
 | |
| {
 | |
| 	return wait_event_interruptible(
 | |
| 		hdw->state_wait_data,
 | |
| 		(hdw->state_stale == 0) &&
 | |
| 		(!state || (hdw->master_state != state)));
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Return name for this driver instance */
 | |
| const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	return hdw->name;
 | |
| }
 | |
| 
 | |
| 
 | |
| const char *pvr2_hdw_get_desc(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	return hdw->hdw_desc->description;
 | |
| }
 | |
| 
 | |
| 
 | |
| const char *pvr2_hdw_get_type(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	return hdw->hdw_desc->shortname;
 | |
| }
 | |
| 
 | |
| 
 | |
| int pvr2_hdw_is_hsm(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	int result;
 | |
| 	LOCK_TAKE(hdw->ctl_lock); do {
 | |
| 		hdw->cmd_buffer[0] = FX2CMD_GET_USB_SPEED;
 | |
| 		result = pvr2_send_request(hdw,
 | |
| 					   hdw->cmd_buffer,1,
 | |
| 					   hdw->cmd_buffer,1);
 | |
| 		if (result < 0) break;
 | |
| 		result = (hdw->cmd_buffer[0] != 0);
 | |
| 	} while(0); LOCK_GIVE(hdw->ctl_lock);
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Execute poll of tuner status */
 | |
| void pvr2_hdw_execute_tuner_poll(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	LOCK_TAKE(hdw->big_lock); do {
 | |
| 		pvr2_hdw_status_poll(hdw);
 | |
| 	} while (0); LOCK_GIVE(hdw->big_lock);
 | |
| }
 | |
| 
 | |
| 
 | |
| static int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	if (!hdw->cropcap_stale) {
 | |
| 		return 0;
 | |
| 	}
 | |
| 	pvr2_hdw_status_poll(hdw);
 | |
| 	if (hdw->cropcap_stale) {
 | |
| 		return -EIO;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Return information about cropping capabilities */
 | |
| int pvr2_hdw_get_cropcap(struct pvr2_hdw *hdw, struct v4l2_cropcap *pp)
 | |
| {
 | |
| 	int stat = 0;
 | |
| 	LOCK_TAKE(hdw->big_lock);
 | |
| 	stat = pvr2_hdw_check_cropcap(hdw);
 | |
| 	if (!stat) {
 | |
| 		memcpy(pp, &hdw->cropcap_info, sizeof(hdw->cropcap_info));
 | |
| 	}
 | |
| 	LOCK_GIVE(hdw->big_lock);
 | |
| 	return stat;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Return information about the tuner */
 | |
| int pvr2_hdw_get_tuner_status(struct pvr2_hdw *hdw,struct v4l2_tuner *vtp)
 | |
| {
 | |
| 	LOCK_TAKE(hdw->big_lock); do {
 | |
| 		if (hdw->tuner_signal_stale) {
 | |
| 			pvr2_hdw_status_poll(hdw);
 | |
| 		}
 | |
| 		memcpy(vtp,&hdw->tuner_signal_info,sizeof(struct v4l2_tuner));
 | |
| 	} while (0); LOCK_GIVE(hdw->big_lock);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Get handle to video output stream */
 | |
| struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *hp)
 | |
| {
 | |
| 	return hp->vid_stream;
 | |
| }
 | |
| 
 | |
| 
 | |
| void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	int nr = pvr2_hdw_get_unit_number(hdw);
 | |
| 	LOCK_TAKE(hdw->big_lock);
 | |
| 	do {
 | |
| 		pr_info("pvrusb2: =================  START STATUS CARD #%d  =================\n", nr);
 | |
| 		v4l2_device_call_all(&hdw->v4l2_dev, 0, core, log_status);
 | |
| 		pvr2_trace(PVR2_TRACE_INFO,"cx2341x config:");
 | |
| 		cx2341x_log_status(&hdw->enc_ctl_state, "pvrusb2");
 | |
| 		pvr2_hdw_state_log_state(hdw);
 | |
| 		pr_info("pvrusb2: ==================  END STATUS CARD #%d  ==================\n", nr);
 | |
| 	} while (0);
 | |
| 	LOCK_GIVE(hdw->big_lock);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Grab EEPROM contents, needed for direct method. */
 | |
| #define EEPROM_SIZE 8192
 | |
| #define trace_eeprom(...) pvr2_trace(PVR2_TRACE_EEPROM,__VA_ARGS__)
 | |
| static u8 *pvr2_full_eeprom_fetch(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	struct i2c_msg msg[2];
 | |
| 	u8 *eeprom;
 | |
| 	u8 iadd[2];
 | |
| 	u8 addr;
 | |
| 	u16 eepromSize;
 | |
| 	unsigned int offs;
 | |
| 	int ret;
 | |
| 	int mode16 = 0;
 | |
| 	unsigned pcnt,tcnt;
 | |
| 	eeprom = kzalloc(EEPROM_SIZE, GFP_KERNEL);
 | |
| 	if (!eeprom) {
 | |
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 			   "Failed to allocate memory required to read eeprom");
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	trace_eeprom("Value for eeprom addr from controller was 0x%x",
 | |
| 		     hdw->eeprom_addr);
 | |
| 	addr = hdw->eeprom_addr;
 | |
| 	/* Seems that if the high bit is set, then the *real* eeprom
 | |
| 	   address is shifted right now bit position (noticed this in
 | |
| 	   newer PVR USB2 hardware) */
 | |
| 	if (addr & 0x80) addr >>= 1;
 | |
| 
 | |
| 	/* FX2 documentation states that a 16bit-addressed eeprom is
 | |
| 	   expected if the I2C address is an odd number (yeah, this is
 | |
| 	   strange but it's what they do) */
 | |
| 	mode16 = (addr & 1);
 | |
| 	eepromSize = (mode16 ? EEPROM_SIZE : 256);
 | |
| 	trace_eeprom("Examining %d byte eeprom at location 0x%x using %d bit addressing",
 | |
| 		     eepromSize, addr,
 | |
| 		     mode16 ? 16 : 8);
 | |
| 
 | |
| 	msg[0].addr = addr;
 | |
| 	msg[0].flags = 0;
 | |
| 	msg[0].len = mode16 ? 2 : 1;
 | |
| 	msg[0].buf = iadd;
 | |
| 	msg[1].addr = addr;
 | |
| 	msg[1].flags = I2C_M_RD;
 | |
| 
 | |
| 	/* We have to do the actual eeprom data fetch ourselves, because
 | |
| 	   (1) we're only fetching part of the eeprom, and (2) if we were
 | |
| 	   getting the whole thing our I2C driver can't grab it in one
 | |
| 	   pass - which is what tveeprom is otherwise going to attempt */
 | |
| 	for (tcnt = 0; tcnt < EEPROM_SIZE; tcnt += pcnt) {
 | |
| 		pcnt = 16;
 | |
| 		if (pcnt + tcnt > EEPROM_SIZE) pcnt = EEPROM_SIZE-tcnt;
 | |
| 		offs = tcnt + (eepromSize - EEPROM_SIZE);
 | |
| 		if (mode16) {
 | |
| 			iadd[0] = offs >> 8;
 | |
| 			iadd[1] = offs;
 | |
| 		} else {
 | |
| 			iadd[0] = offs;
 | |
| 		}
 | |
| 		msg[1].len = pcnt;
 | |
| 		msg[1].buf = eeprom+tcnt;
 | |
| 		if ((ret = i2c_transfer(&hdw->i2c_adap,
 | |
| 					msg,ARRAY_SIZE(msg))) != 2) {
 | |
| 			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 				   "eeprom fetch set offs err=%d",ret);
 | |
| 			kfree(eeprom);
 | |
| 			return NULL;
 | |
| 		}
 | |
| 	}
 | |
| 	return eeprom;
 | |
| }
 | |
| 
 | |
| 
 | |
| void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *hdw,
 | |
| 				int mode,
 | |
| 				int enable_flag)
 | |
| {
 | |
| 	int ret;
 | |
| 	u16 address;
 | |
| 	unsigned int pipe;
 | |
| 	LOCK_TAKE(hdw->big_lock);
 | |
| 	do {
 | |
| 		if ((hdw->fw_buffer == NULL) == !enable_flag) break;
 | |
| 
 | |
| 		if (!enable_flag) {
 | |
| 			pvr2_trace(PVR2_TRACE_FIRMWARE,
 | |
| 				   "Cleaning up after CPU firmware fetch");
 | |
| 			kfree(hdw->fw_buffer);
 | |
| 			hdw->fw_buffer = NULL;
 | |
| 			hdw->fw_size = 0;
 | |
| 			if (hdw->fw_cpu_flag) {
 | |
| 				/* Now release the CPU.  It will disconnect
 | |
| 				   and reconnect later. */
 | |
| 				pvr2_hdw_cpureset_assert(hdw,0);
 | |
| 			}
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		hdw->fw_cpu_flag = (mode != 2);
 | |
| 		if (hdw->fw_cpu_flag) {
 | |
| 			hdw->fw_size = (mode == 1) ? 0x4000 : 0x2000;
 | |
| 			pvr2_trace(PVR2_TRACE_FIRMWARE,
 | |
| 				   "Preparing to suck out CPU firmware (size=%u)",
 | |
| 				   hdw->fw_size);
 | |
| 			hdw->fw_buffer = kzalloc(hdw->fw_size,GFP_KERNEL);
 | |
| 			if (!hdw->fw_buffer) {
 | |
| 				hdw->fw_size = 0;
 | |
| 				break;
 | |
| 			}
 | |
| 
 | |
| 			/* We have to hold the CPU during firmware upload. */
 | |
| 			pvr2_hdw_cpureset_assert(hdw,1);
 | |
| 
 | |
| 			/* download the firmware from address 0000-1fff in 2048
 | |
| 			   (=0x800) bytes chunk. */
 | |
| 
 | |
| 			pvr2_trace(PVR2_TRACE_FIRMWARE,
 | |
| 				   "Grabbing CPU firmware");
 | |
| 			pipe = usb_rcvctrlpipe(hdw->usb_dev, 0);
 | |
| 			for(address = 0; address < hdw->fw_size;
 | |
| 			    address += 0x800) {
 | |
| 				ret = usb_control_msg(hdw->usb_dev,pipe,
 | |
| 						      0xa0,0xc0,
 | |
| 						      address,0,
 | |
| 						      hdw->fw_buffer+address,
 | |
| 						      0x800,1000);
 | |
| 				if (ret < 0) break;
 | |
| 			}
 | |
| 
 | |
| 			pvr2_trace(PVR2_TRACE_FIRMWARE,
 | |
| 				   "Done grabbing CPU firmware");
 | |
| 		} else {
 | |
| 			pvr2_trace(PVR2_TRACE_FIRMWARE,
 | |
| 				   "Sucking down EEPROM contents");
 | |
| 			hdw->fw_buffer = pvr2_full_eeprom_fetch(hdw);
 | |
| 			if (!hdw->fw_buffer) {
 | |
| 				pvr2_trace(PVR2_TRACE_FIRMWARE,
 | |
| 					   "EEPROM content suck failed.");
 | |
| 				break;
 | |
| 			}
 | |
| 			hdw->fw_size = EEPROM_SIZE;
 | |
| 			pvr2_trace(PVR2_TRACE_FIRMWARE,
 | |
| 				   "Done sucking down EEPROM contents");
 | |
| 		}
 | |
| 	} while (0);
 | |
| 	LOCK_GIVE(hdw->big_lock);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Return true if we're in a mode for retrieval CPU firmware */
 | |
| int pvr2_hdw_cpufw_get_enabled(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	return hdw->fw_buffer != NULL;
 | |
| }
 | |
| 
 | |
| 
 | |
| int pvr2_hdw_cpufw_get(struct pvr2_hdw *hdw,unsigned int offs,
 | |
| 		       char *buf,unsigned int cnt)
 | |
| {
 | |
| 	int ret = -EINVAL;
 | |
| 	LOCK_TAKE(hdw->big_lock);
 | |
| 	do {
 | |
| 		if (!buf) break;
 | |
| 		if (!cnt) break;
 | |
| 
 | |
| 		if (!hdw->fw_buffer) {
 | |
| 			ret = -EIO;
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		if (offs >= hdw->fw_size) {
 | |
| 			pvr2_trace(PVR2_TRACE_FIRMWARE,
 | |
| 				   "Read firmware data offs=%d EOF",
 | |
| 				   offs);
 | |
| 			ret = 0;
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		if (offs + cnt > hdw->fw_size) cnt = hdw->fw_size - offs;
 | |
| 
 | |
| 		memcpy(buf,hdw->fw_buffer+offs,cnt);
 | |
| 
 | |
| 		pvr2_trace(PVR2_TRACE_FIRMWARE,
 | |
| 			   "Read firmware data offs=%d cnt=%d",
 | |
| 			   offs,cnt);
 | |
| 		ret = cnt;
 | |
| 	} while (0);
 | |
| 	LOCK_GIVE(hdw->big_lock);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| 
 | |
| int pvr2_hdw_v4l_get_minor_number(struct pvr2_hdw *hdw,
 | |
| 				  enum pvr2_v4l_type index)
 | |
| {
 | |
| 	switch (index) {
 | |
| 	case pvr2_v4l_type_video: return hdw->v4l_minor_number_video;
 | |
| 	case pvr2_v4l_type_vbi: return hdw->v4l_minor_number_vbi;
 | |
| 	case pvr2_v4l_type_radio: return hdw->v4l_minor_number_radio;
 | |
| 	default: return -1;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Store a v4l minor device number */
 | |
| void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *hdw,
 | |
| 				     enum pvr2_v4l_type index,int v)
 | |
| {
 | |
| 	switch (index) {
 | |
| 	case pvr2_v4l_type_video: hdw->v4l_minor_number_video = v;break;
 | |
| 	case pvr2_v4l_type_vbi: hdw->v4l_minor_number_vbi = v;break;
 | |
| 	case pvr2_v4l_type_radio: hdw->v4l_minor_number_radio = v;break;
 | |
| 	default: break;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| static void pvr2_ctl_write_complete(struct urb *urb)
 | |
| {
 | |
| 	struct pvr2_hdw *hdw = urb->context;
 | |
| 	hdw->ctl_write_pend_flag = 0;
 | |
| 	if (hdw->ctl_read_pend_flag) return;
 | |
| 	complete(&hdw->ctl_done);
 | |
| }
 | |
| 
 | |
| 
 | |
| static void pvr2_ctl_read_complete(struct urb *urb)
 | |
| {
 | |
| 	struct pvr2_hdw *hdw = urb->context;
 | |
| 	hdw->ctl_read_pend_flag = 0;
 | |
| 	if (hdw->ctl_write_pend_flag) return;
 | |
| 	complete(&hdw->ctl_done);
 | |
| }
 | |
| 
 | |
| struct hdw_timer {
 | |
| 	struct timer_list timer;
 | |
| 	struct pvr2_hdw *hdw;
 | |
| };
 | |
| 
 | |
| static void pvr2_ctl_timeout(struct timer_list *t)
 | |
| {
 | |
| 	struct hdw_timer *timer = from_timer(timer, t, timer);
 | |
| 	struct pvr2_hdw *hdw = timer->hdw;
 | |
| 
 | |
| 	if (hdw->ctl_write_pend_flag || hdw->ctl_read_pend_flag) {
 | |
| 		hdw->ctl_timeout_flag = !0;
 | |
| 		if (hdw->ctl_write_pend_flag)
 | |
| 			usb_unlink_urb(hdw->ctl_write_urb);
 | |
| 		if (hdw->ctl_read_pend_flag)
 | |
| 			usb_unlink_urb(hdw->ctl_read_urb);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Issue a command and get a response from the device.  This extended
 | |
|    version includes a probe flag (which if set means that device errors
 | |
|    should not be logged or treated as fatal) and a timeout in jiffies.
 | |
|    This can be used to non-lethally probe the health of endpoint 1. */
 | |
| 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)
 | |
| {
 | |
| 	unsigned int idx;
 | |
| 	int status = 0;
 | |
| 	struct hdw_timer timer = {
 | |
| 		.hdw = hdw,
 | |
| 	};
 | |
| 
 | |
| 	if (!hdw->ctl_lock_held) {
 | |
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 			   "Attempted to execute control transfer without lock!!");
 | |
| 		return -EDEADLK;
 | |
| 	}
 | |
| 	if (!hdw->flag_ok && !probe_fl) {
 | |
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 			   "Attempted to execute control transfer when device not ok");
 | |
| 		return -EIO;
 | |
| 	}
 | |
| 	if (!(hdw->ctl_read_urb && hdw->ctl_write_urb)) {
 | |
| 		if (!probe_fl) {
 | |
| 			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 				   "Attempted to execute control transfer when USB is disconnected");
 | |
| 		}
 | |
| 		return -ENOTTY;
 | |
| 	}
 | |
| 
 | |
| 	/* Ensure that we have sane parameters */
 | |
| 	if (!write_data) write_len = 0;
 | |
| 	if (!read_data) read_len = 0;
 | |
| 	if (write_len > PVR2_CTL_BUFFSIZE) {
 | |
| 		pvr2_trace(
 | |
| 			PVR2_TRACE_ERROR_LEGS,
 | |
| 			"Attempted to execute %d byte control-write transfer (limit=%d)",
 | |
| 			write_len,PVR2_CTL_BUFFSIZE);
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 	if (read_len > PVR2_CTL_BUFFSIZE) {
 | |
| 		pvr2_trace(
 | |
| 			PVR2_TRACE_ERROR_LEGS,
 | |
| 			"Attempted to execute %d byte control-read transfer (limit=%d)",
 | |
| 			write_len,PVR2_CTL_BUFFSIZE);
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 	if ((!write_len) && (!read_len)) {
 | |
| 		pvr2_trace(
 | |
| 			PVR2_TRACE_ERROR_LEGS,
 | |
| 			"Attempted to execute null control transfer?");
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	hdw->cmd_debug_state = 1;
 | |
| 	if (write_len && write_data)
 | |
| 		hdw->cmd_debug_code = ((unsigned char *)write_data)[0];
 | |
| 	else
 | |
| 		hdw->cmd_debug_code = 0;
 | |
| 	hdw->cmd_debug_write_len = write_len;
 | |
| 	hdw->cmd_debug_read_len = read_len;
 | |
| 
 | |
| 	/* Initialize common stuff */
 | |
| 	init_completion(&hdw->ctl_done);
 | |
| 	hdw->ctl_timeout_flag = 0;
 | |
| 	hdw->ctl_write_pend_flag = 0;
 | |
| 	hdw->ctl_read_pend_flag = 0;
 | |
| 	timer_setup_on_stack(&timer.timer, pvr2_ctl_timeout, 0);
 | |
| 	timer.timer.expires = jiffies + timeout;
 | |
| 
 | |
| 	if (write_len && write_data) {
 | |
| 		hdw->cmd_debug_state = 2;
 | |
| 		/* Transfer write data to internal buffer */
 | |
| 		for (idx = 0; idx < write_len; idx++) {
 | |
| 			hdw->ctl_write_buffer[idx] =
 | |
| 				((unsigned char *)write_data)[idx];
 | |
| 		}
 | |
| 		/* Initiate a write request */
 | |
| 		usb_fill_bulk_urb(hdw->ctl_write_urb,
 | |
| 				  hdw->usb_dev,
 | |
| 				  usb_sndbulkpipe(hdw->usb_dev,
 | |
| 						  PVR2_CTL_WRITE_ENDPOINT),
 | |
| 				  hdw->ctl_write_buffer,
 | |
| 				  write_len,
 | |
| 				  pvr2_ctl_write_complete,
 | |
| 				  hdw);
 | |
| 		hdw->ctl_write_urb->actual_length = 0;
 | |
| 		hdw->ctl_write_pend_flag = !0;
 | |
| 		if (usb_urb_ep_type_check(hdw->ctl_write_urb)) {
 | |
| 			pvr2_trace(
 | |
| 				PVR2_TRACE_ERROR_LEGS,
 | |
| 				"Invalid write control endpoint");
 | |
| 			return -EINVAL;
 | |
| 		}
 | |
| 		status = usb_submit_urb(hdw->ctl_write_urb,GFP_KERNEL);
 | |
| 		if (status < 0) {
 | |
| 			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 				   "Failed to submit write-control URB status=%d",
 | |
| status);
 | |
| 			hdw->ctl_write_pend_flag = 0;
 | |
| 			goto done;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (read_len) {
 | |
| 		hdw->cmd_debug_state = 3;
 | |
| 		memset(hdw->ctl_read_buffer,0x43,read_len);
 | |
| 		/* Initiate a read request */
 | |
| 		usb_fill_bulk_urb(hdw->ctl_read_urb,
 | |
| 				  hdw->usb_dev,
 | |
| 				  usb_rcvbulkpipe(hdw->usb_dev,
 | |
| 						  PVR2_CTL_READ_ENDPOINT),
 | |
| 				  hdw->ctl_read_buffer,
 | |
| 				  read_len,
 | |
| 				  pvr2_ctl_read_complete,
 | |
| 				  hdw);
 | |
| 		hdw->ctl_read_urb->actual_length = 0;
 | |
| 		hdw->ctl_read_pend_flag = !0;
 | |
| 		if (usb_urb_ep_type_check(hdw->ctl_read_urb)) {
 | |
| 			pvr2_trace(
 | |
| 				PVR2_TRACE_ERROR_LEGS,
 | |
| 				"Invalid read control endpoint");
 | |
| 			return -EINVAL;
 | |
| 		}
 | |
| 		status = usb_submit_urb(hdw->ctl_read_urb,GFP_KERNEL);
 | |
| 		if (status < 0) {
 | |
| 			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 				   "Failed to submit read-control URB status=%d",
 | |
| status);
 | |
| 			hdw->ctl_read_pend_flag = 0;
 | |
| 			goto done;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* Start timer */
 | |
| 	add_timer(&timer.timer);
 | |
| 
 | |
| 	/* Now wait for all I/O to complete */
 | |
| 	hdw->cmd_debug_state = 4;
 | |
| 	while (hdw->ctl_write_pend_flag || hdw->ctl_read_pend_flag) {
 | |
| 		wait_for_completion(&hdw->ctl_done);
 | |
| 	}
 | |
| 	hdw->cmd_debug_state = 5;
 | |
| 
 | |
| 	/* Stop timer */
 | |
| 	del_timer_sync(&timer.timer);
 | |
| 
 | |
| 	hdw->cmd_debug_state = 6;
 | |
| 	status = 0;
 | |
| 
 | |
| 	if (hdw->ctl_timeout_flag) {
 | |
| 		status = -ETIMEDOUT;
 | |
| 		if (!probe_fl) {
 | |
| 			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 				   "Timed out control-write");
 | |
| 		}
 | |
| 		goto done;
 | |
| 	}
 | |
| 
 | |
| 	if (write_len) {
 | |
| 		/* Validate results of write request */
 | |
| 		if ((hdw->ctl_write_urb->status != 0) &&
 | |
| 		    (hdw->ctl_write_urb->status != -ENOENT) &&
 | |
| 		    (hdw->ctl_write_urb->status != -ESHUTDOWN) &&
 | |
| 		    (hdw->ctl_write_urb->status != -ECONNRESET)) {
 | |
| 			/* USB subsystem is reporting some kind of failure
 | |
| 			   on the write */
 | |
| 			status = hdw->ctl_write_urb->status;
 | |
| 			if (!probe_fl) {
 | |
| 				pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 					   "control-write URB failure, status=%d",
 | |
| 					   status);
 | |
| 			}
 | |
| 			goto done;
 | |
| 		}
 | |
| 		if (hdw->ctl_write_urb->actual_length < write_len) {
 | |
| 			/* Failed to write enough data */
 | |
| 			status = -EIO;
 | |
| 			if (!probe_fl) {
 | |
| 				pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 					   "control-write URB short, expected=%d got=%d",
 | |
| 					   write_len,
 | |
| 					   hdw->ctl_write_urb->actual_length);
 | |
| 			}
 | |
| 			goto done;
 | |
| 		}
 | |
| 	}
 | |
| 	if (read_len && read_data) {
 | |
| 		/* Validate results of read request */
 | |
| 		if ((hdw->ctl_read_urb->status != 0) &&
 | |
| 		    (hdw->ctl_read_urb->status != -ENOENT) &&
 | |
| 		    (hdw->ctl_read_urb->status != -ESHUTDOWN) &&
 | |
| 		    (hdw->ctl_read_urb->status != -ECONNRESET)) {
 | |
| 			/* USB subsystem is reporting some kind of failure
 | |
| 			   on the read */
 | |
| 			status = hdw->ctl_read_urb->status;
 | |
| 			if (!probe_fl) {
 | |
| 				pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 					   "control-read URB failure, status=%d",
 | |
| 					   status);
 | |
| 			}
 | |
| 			goto done;
 | |
| 		}
 | |
| 		if (hdw->ctl_read_urb->actual_length < read_len) {
 | |
| 			/* Failed to read enough data */
 | |
| 			status = -EIO;
 | |
| 			if (!probe_fl) {
 | |
| 				pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 					   "control-read URB short, expected=%d got=%d",
 | |
| 					   read_len,
 | |
| 					   hdw->ctl_read_urb->actual_length);
 | |
| 			}
 | |
| 			goto done;
 | |
| 		}
 | |
| 		/* Transfer retrieved data out from internal buffer */
 | |
| 		for (idx = 0; idx < read_len; idx++) {
 | |
| 			((unsigned char *)read_data)[idx] =
 | |
| 				hdw->ctl_read_buffer[idx];
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
|  done:
 | |
| 
 | |
| 	hdw->cmd_debug_state = 0;
 | |
| 	if ((status < 0) && (!probe_fl)) {
 | |
| 		pvr2_hdw_render_useless(hdw);
 | |
| 	}
 | |
| 	destroy_timer_on_stack(&timer.timer);
 | |
| 
 | |
| 	return status;
 | |
| }
 | |
| 
 | |
| 
 | |
| int pvr2_send_request(struct pvr2_hdw *hdw,
 | |
| 		      void *write_data,unsigned int write_len,
 | |
| 		      void *read_data,unsigned int read_len)
 | |
| {
 | |
| 	return pvr2_send_request_ex(hdw,HZ*4,0,
 | |
| 				    write_data,write_len,
 | |
| 				    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;
 | |
| 
 | |
| 	LOCK_TAKE(hdw->ctl_lock);
 | |
| 
 | |
| 	hdw->cmd_buffer[0] = FX2CMD_REG_WRITE;  /* write register prefix */
 | |
| 	PVR2_DECOMPOSE_LE(hdw->cmd_buffer,1,data);
 | |
| 	hdw->cmd_buffer[5] = 0;
 | |
| 	hdw->cmd_buffer[6] = (reg >> 8) & 0xff;
 | |
| 	hdw->cmd_buffer[7] = reg & 0xff;
 | |
| 
 | |
| 
 | |
| 	ret = pvr2_send_request(hdw, hdw->cmd_buffer, 8, hdw->cmd_buffer, 0);
 | |
| 
 | |
| 	LOCK_GIVE(hdw->ctl_lock);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| 
 | |
| static int pvr2_read_register(struct pvr2_hdw *hdw, u16 reg, u32 *data)
 | |
| {
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	LOCK_TAKE(hdw->ctl_lock);
 | |
| 
 | |
| 	hdw->cmd_buffer[0] = FX2CMD_REG_READ;  /* read register prefix */
 | |
| 	hdw->cmd_buffer[1] = 0;
 | |
| 	hdw->cmd_buffer[2] = 0;
 | |
| 	hdw->cmd_buffer[3] = 0;
 | |
| 	hdw->cmd_buffer[4] = 0;
 | |
| 	hdw->cmd_buffer[5] = 0;
 | |
| 	hdw->cmd_buffer[6] = (reg >> 8) & 0xff;
 | |
| 	hdw->cmd_buffer[7] = reg & 0xff;
 | |
| 
 | |
| 	ret |= pvr2_send_request(hdw, hdw->cmd_buffer, 8, hdw->cmd_buffer, 4);
 | |
| 	*data = PVR2_COMPOSE_LE(hdw->cmd_buffer,0);
 | |
| 
 | |
| 	LOCK_GIVE(hdw->ctl_lock);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| 
 | |
| void pvr2_hdw_render_useless(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	if (!hdw->flag_ok) return;
 | |
| 	pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 		   "Device being rendered inoperable");
 | |
| 	if (hdw->vid_stream) {
 | |
| 		pvr2_stream_setup(hdw->vid_stream,NULL,0,0);
 | |
| 	}
 | |
| 	hdw->flag_ok = 0;
 | |
| 	trace_stbit("flag_ok",hdw->flag_ok);
 | |
| 	pvr2_hdw_state_sched(hdw);
 | |
| }
 | |
| 
 | |
| 
 | |
| void pvr2_hdw_device_reset(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	int ret;
 | |
| 	pvr2_trace(PVR2_TRACE_INIT,"Performing a device reset...");
 | |
| 	ret = usb_lock_device_for_reset(hdw->usb_dev,NULL);
 | |
| 	if (ret == 0) {
 | |
| 		ret = usb_reset_device(hdw->usb_dev);
 | |
| 		usb_unlock_device(hdw->usb_dev);
 | |
| 	} else {
 | |
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 			   "Failed to lock USB device ret=%d",ret);
 | |
| 	}
 | |
| 	if (init_pause_msec) {
 | |
| 		pvr2_trace(PVR2_TRACE_INFO,
 | |
| 			   "Waiting %u msec for hardware to settle",
 | |
| 			   init_pause_msec);
 | |
| 		msleep(init_pause_msec);
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| void pvr2_hdw_cpureset_assert(struct pvr2_hdw *hdw,int val)
 | |
| {
 | |
| 	char *da;
 | |
| 	unsigned int pipe;
 | |
| 	int ret;
 | |
| 
 | |
| 	if (!hdw->usb_dev) return;
 | |
| 
 | |
| 	da = kmalloc(16, GFP_KERNEL);
 | |
| 
 | |
| 	if (da == NULL) {
 | |
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 			   "Unable to allocate memory to control CPU reset");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	pvr2_trace(PVR2_TRACE_INIT,"cpureset_assert(%d)",val);
 | |
| 
 | |
| 	da[0] = val ? 0x01 : 0x00;
 | |
| 
 | |
| 	/* Write the CPUCS register on the 8051.  The lsb of the register
 | |
| 	   is the reset bit; a 1 asserts reset while a 0 clears it. */
 | |
| 	pipe = usb_sndctrlpipe(hdw->usb_dev, 0);
 | |
| 	ret = usb_control_msg(hdw->usb_dev,pipe,0xa0,0x40,0xe600,0,da,1,1000);
 | |
| 	if (ret < 0) {
 | |
| 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
 | |
| 			   "cpureset_assert(%d) error=%d",val,ret);
 | |
| 		pvr2_hdw_render_useless(hdw);
 | |
| 	}
 | |
| 
 | |
| 	kfree(da);
 | |
| }
 | |
| 
 | |
| 
 | |
| int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	return pvr2_issue_simple_cmd(hdw,FX2CMD_DEEP_RESET);
 | |
| }
 | |
| 
 | |
| 
 | |
| int pvr2_hdw_cmd_powerup(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	return pvr2_issue_simple_cmd(hdw,FX2CMD_POWER_ON);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	pvr2_trace(PVR2_TRACE_INIT,
 | |
| 		   "Requesting decoder reset");
 | |
| 	if (hdw->decoder_client_id) {
 | |
| 		v4l2_device_call_all(&hdw->v4l2_dev, hdw->decoder_client_id,
 | |
| 				     core, reset, 0);
 | |
| 		pvr2_hdw_cx25840_vbi_hack(hdw);
 | |
| 		return 0;
 | |
| 	}
 | |
| 	pvr2_trace(PVR2_TRACE_INIT,
 | |
| 		   "Unable to reset decoder: nothing attached");
 | |
| 	return -ENOTTY;
 | |
| }
 | |
| 
 | |
| 
 | |
| static int pvr2_hdw_cmd_hcw_demod_reset(struct pvr2_hdw *hdw, int onoff)
 | |
| {
 | |
| 	hdw->flag_ok = !0;
 | |
| 
 | |
| 	/* Use this for Hauppauge 160xxx only */
 | |
| 	if (le16_to_cpu(hdw->usb_dev->descriptor.idVendor) == 0x2040 &&
 | |
| 	    (le16_to_cpu(hdw->usb_dev->descriptor.idProduct) == 0x7502 ||
 | |
| 	     le16_to_cpu(hdw->usb_dev->descriptor.idProduct) == 0x7510)) {
 | |
| 		pr_debug("%s(): resetting demod on Hauppauge 160xxx platform skipped\n",
 | |
| 			 __func__);
 | |
| 		/* Can't reset 160xxx or it will trash Demod tristate */
 | |
| 		return pvr2_issue_simple_cmd(hdw,
 | |
| 					     FX2CMD_HCW_MAKO_SLEEP_PIN |
 | |
| 					     (1 << 8) |
 | |
| 					     ((onoff ? 1 : 0) << 16));
 | |
| 	}
 | |
| 
 | |
| 	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)
 | |
| {
 | |
| 	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)
 | |
| {
 | |
| 	return pvr2_issue_simple_cmd(hdw,(onoff ?
 | |
| 					  FX2CMD_ONAIR_DTV_STREAMING_ON :
 | |
| 					  FX2CMD_ONAIR_DTV_STREAMING_OFF));
 | |
| }
 | |
| 
 | |
| 
 | |
| 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... */
 | |
| 		pvr2_hdw_cmd_onair_fe_power_ctrl(hdw,digitalFl);
 | |
| 		break;
 | |
| 	default: break;
 | |
| 	}
 | |
| 
 | |
| 	pvr2_hdw_untrip_unlocked(hdw);
 | |
| 	hdw->pathway_state = cmode;
 | |
| }
 | |
| 
 | |
| 
 | |
| static 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.
 | |
| 	 *
 | |
| 	 */
 | |
| 	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);
 | |
| }
 | |
| 
 | |
| 
 | |
| 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);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Stop / start video stream transport */
 | |
| static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl)
 | |
| {
 | |
| 	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;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| /* 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;
 | |
| 	trace_stbit("state_pathway_ok",hdw->state_pathway_ok);
 | |
| 	return !0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Evaluate whether or not state_encoder_ok can change */
 | |
| static int state_eval_encoder_ok(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	if (hdw->state_encoder_ok) return 0;
 | |
| 	if (hdw->flag_tripped) return 0;
 | |
| 	if (hdw->state_encoder_run) return 0;
 | |
| 	if (hdw->state_encoder_config) return 0;
 | |
| 	if (hdw->state_decoder_run) return 0;
 | |
| 	if (hdw->state_usbstream_run) return 0;
 | |
| 	if (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);
 | |
| 		return !0;
 | |
| 	}
 | |
| 	hdw->state_encoder_ok = !0;
 | |
| 	trace_stbit("state_encoder_ok",hdw->state_encoder_ok);
 | |
| 	return !0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Evaluate whether or not state_encoder_config can change */
 | |
| static int state_eval_encoder_config(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	if (hdw->state_encoder_config) {
 | |
| 		if (hdw->state_encoder_ok) {
 | |
| 			if (hdw->state_pipeline_req &&
 | |
| 			    !hdw->state_pipeline_pause) return 0;
 | |
| 		}
 | |
| 		hdw->state_encoder_config = 0;
 | |
| 		hdw->state_encoder_waitok = 0;
 | |
| 		trace_stbit("state_encoder_waitok",hdw->state_encoder_waitok);
 | |
| 		/* paranoia - solve race if timer just completed */
 | |
| 		del_timer_sync(&hdw->encoder_wait_timer);
 | |
| 	} else {
 | |
| 		if (!hdw->state_pathway_ok ||
 | |
| 		    (hdw->pathway_state != PVR2_PATHWAY_ANALOG) ||
 | |
| 		    !hdw->state_encoder_ok ||
 | |
| 		    !hdw->state_pipeline_idle ||
 | |
| 		    hdw->state_pipeline_pause ||
 | |
| 		    !hdw->state_pipeline_req ||
 | |
| 		    !hdw->state_pipeline_config) {
 | |
| 			/* We must reset the enforced wait interval if
 | |
| 			   anything has happened that might have disturbed
 | |
| 			   the encoder.  This should be a rare case. */
 | |
| 			if (timer_pending(&hdw->encoder_wait_timer)) {
 | |
| 				del_timer_sync(&hdw->encoder_wait_timer);
 | |
| 			}
 | |
| 			if (hdw->state_encoder_waitok) {
 | |
| 				/* Must clear the state - therefore we did
 | |
| 				   something to a state bit and must also
 | |
| 				   return true. */
 | |
| 				hdw->state_encoder_waitok = 0;
 | |
| 				trace_stbit("state_encoder_waitok",
 | |
| 					    hdw->state_encoder_waitok);
 | |
| 				return !0;
 | |
| 			}
 | |
| 			return 0;
 | |
| 		}
 | |
| 		if (!hdw->state_encoder_waitok) {
 | |
| 			if (!timer_pending(&hdw->encoder_wait_timer)) {
 | |
| 				/* waitok flag wasn't set and timer isn't
 | |
| 				   running.  Check flag once more to avoid
 | |
| 				   a race then start the timer.  This is
 | |
| 				   the point when we measure out a minimal
 | |
| 				   quiet interval before doing something to
 | |
| 				   the encoder. */
 | |
| 				if (!hdw->state_encoder_waitok) {
 | |
| 					hdw->encoder_wait_timer.expires =
 | |
| 						jiffies + msecs_to_jiffies(
 | |
| 						TIME_MSEC_ENCODER_WAIT);
 | |
| 					add_timer(&hdw->encoder_wait_timer);
 | |
| 				}
 | |
| 			}
 | |
| 			/* We can't continue until we know we have been
 | |
| 			   quiet for the interval measured by this
 | |
| 			   timer. */
 | |
| 			return 0;
 | |
| 		}
 | |
| 		pvr2_encoder_configure(hdw);
 | |
| 		if (hdw->state_encoder_ok) hdw->state_encoder_config = !0;
 | |
| 	}
 | |
| 	trace_stbit("state_encoder_config",hdw->state_encoder_config);
 | |
| 	return !0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* 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 && hdw->state_decoder_ready) {
 | |
| 			/* 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) {
 | |
| 			del_timer_sync(&hdw->encoder_run_timer);
 | |
| 			if (pvr2_encoder_stop(hdw) < 0) return !0;
 | |
| 		}
 | |
| 		hdw->state_encoder_run = 0;
 | |
| 	} else {
 | |
| 		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 +
 | |
| 				 msecs_to_jiffies(TIME_MSEC_ENCODER_OK);
 | |
| 			add_timer(&hdw->encoder_run_timer);
 | |
| 		}
 | |
| 	}
 | |
| 	trace_stbit("state_encoder_run",hdw->state_encoder_run);
 | |
| 	return !0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Timeout function for quiescent timer. */
 | |
| static void pvr2_hdw_quiescent_timeout(struct timer_list *t)
 | |
| {
 | |
| 	struct pvr2_hdw *hdw = from_timer(hdw, t, quiescent_timer);
 | |
| 	hdw->state_decoder_quiescent = !0;
 | |
| 	trace_stbit("state_decoder_quiescent",hdw->state_decoder_quiescent);
 | |
| 	hdw->state_stale = !0;
 | |
| 	schedule_work(&hdw->workpoll);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Timeout function for decoder stabilization timer. */
 | |
| static void pvr2_hdw_decoder_stabilization_timeout(struct timer_list *t)
 | |
| {
 | |
| 	struct pvr2_hdw *hdw = from_timer(hdw, t, decoder_stabilization_timer);
 | |
| 	hdw->state_decoder_ready = !0;
 | |
| 	trace_stbit("state_decoder_ready", hdw->state_decoder_ready);
 | |
| 	hdw->state_stale = !0;
 | |
| 	schedule_work(&hdw->workpoll);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Timeout function for encoder wait timer. */
 | |
| static void pvr2_hdw_encoder_wait_timeout(struct timer_list *t)
 | |
| {
 | |
| 	struct pvr2_hdw *hdw = from_timer(hdw, t, encoder_wait_timer);
 | |
| 	hdw->state_encoder_waitok = !0;
 | |
| 	trace_stbit("state_encoder_waitok",hdw->state_encoder_waitok);
 | |
| 	hdw->state_stale = !0;
 | |
| 	schedule_work(&hdw->workpoll);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Timeout function for encoder run timer. */
 | |
| static void pvr2_hdw_encoder_run_timeout(struct timer_list *t)
 | |
| {
 | |
| 	struct pvr2_hdw *hdw = from_timer(hdw, t, encoder_run_timer);
 | |
| 	if (!hdw->state_encoder_runok) {
 | |
| 		hdw->state_encoder_runok = !0;
 | |
| 		trace_stbit("state_encoder_runok",hdw->state_encoder_runok);
 | |
| 		hdw->state_stale = !0;
 | |
| 		schedule_work(&hdw->workpoll);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Evaluate whether or not state_decoder_run can change */
 | |
| static int state_eval_decoder_run(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	if (hdw->state_decoder_run) {
 | |
| 		if (hdw->state_encoder_ok) {
 | |
| 			if (hdw->state_pipeline_req &&
 | |
| 			    !hdw->state_pipeline_pause &&
 | |
| 			    hdw->state_pathway_ok) return 0;
 | |
| 		}
 | |
| 		if (!hdw->flag_decoder_missed) {
 | |
| 			pvr2_decoder_enable(hdw,0);
 | |
| 		}
 | |
| 		hdw->state_decoder_quiescent = 0;
 | |
| 		hdw->state_decoder_run = 0;
 | |
| 		/* paranoia - solve race if timer(s) just completed */
 | |
| 		del_timer_sync(&hdw->quiescent_timer);
 | |
| 		/* Kill the stabilization timer, in case we're killing the
 | |
| 		   encoder before the previous stabilization interval has
 | |
| 		   been properly timed. */
 | |
| 		del_timer_sync(&hdw->decoder_stabilization_timer);
 | |
| 		hdw->state_decoder_ready = 0;
 | |
| 	} else {
 | |
| 		if (!hdw->state_decoder_quiescent) {
 | |
| 			if (!timer_pending(&hdw->quiescent_timer)) {
 | |
| 				/* We don't do something about the
 | |
| 				   quiescent timer until right here because
 | |
| 				   we also want to catch cases where the
 | |
| 				   decoder was already not running (like
 | |
| 				   after initialization) as opposed to
 | |
| 				   knowing that we had just stopped it.
 | |
| 				   The second flag check is here to cover a
 | |
| 				   race - the timer could have run and set
 | |
| 				   this flag just after the previous check
 | |
| 				   but before we did the pending check. */
 | |
| 				if (!hdw->state_decoder_quiescent) {
 | |
| 					hdw->quiescent_timer.expires =
 | |
| 						jiffies + msecs_to_jiffies(
 | |
| 						TIME_MSEC_DECODER_WAIT);
 | |
| 					add_timer(&hdw->quiescent_timer);
 | |
| 				}
 | |
| 			}
 | |
| 			/* Don't allow decoder to start again until it has
 | |
| 			   been quiesced first.  This little detail should
 | |
| 			   hopefully further stabilize the encoder. */
 | |
| 			return 0;
 | |
| 		}
 | |
| 		if (!hdw->state_pathway_ok ||
 | |
| 		    (hdw->pathway_state != PVR2_PATHWAY_ANALOG) ||
 | |
| 		    !hdw->state_pipeline_req ||
 | |
| 		    hdw->state_pipeline_pause ||
 | |
| 		    !hdw->state_pipeline_config ||
 | |
| 		    !hdw->state_encoder_config ||
 | |
| 		    !hdw->state_encoder_ok) return 0;
 | |
| 		del_timer_sync(&hdw->quiescent_timer);
 | |
| 		if (hdw->flag_decoder_missed) return 0;
 | |
| 		if (pvr2_decoder_enable(hdw,!0) < 0) return 0;
 | |
| 		hdw->state_decoder_quiescent = 0;
 | |
| 		hdw->state_decoder_ready = 0;
 | |
| 		hdw->state_decoder_run = !0;
 | |
| 		if (hdw->decoder_client_id == PVR2_CLIENT_ID_SAA7115) {
 | |
| 			hdw->decoder_stabilization_timer.expires =
 | |
| 				jiffies + msecs_to_jiffies(
 | |
| 				TIME_MSEC_DECODER_STABILIZATION_WAIT);
 | |
| 			add_timer(&hdw->decoder_stabilization_timer);
 | |
| 		} else {
 | |
| 			hdw->state_decoder_ready = !0;
 | |
| 		}
 | |
| 	}
 | |
| 	trace_stbit("state_decoder_quiescent",hdw->state_decoder_quiescent);
 | |
| 	trace_stbit("state_decoder_run",hdw->state_decoder_run);
 | |
| 	trace_stbit("state_decoder_ready", hdw->state_decoder_ready);
 | |
| 	return !0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Evaluate whether or not state_usbstream_run can change */
 | |
| static int state_eval_usbstream_run(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	if (hdw->state_usbstream_run) {
 | |
| 		int fl = !0;
 | |
| 		if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) {
 | |
| 			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;
 | |
| 	} else {
 | |
| 		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;
 | |
| 		} 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;
 | |
| 	}
 | |
| 	trace_stbit("state_usbstream_run",hdw->state_usbstream_run);
 | |
| 	return !0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Attempt to configure pipeline, if needed */
 | |
| static int state_eval_pipeline_config(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	if (hdw->state_pipeline_config ||
 | |
| 	    hdw->state_pipeline_pause) return 0;
 | |
| 	pvr2_hdw_commit_execute(hdw);
 | |
| 	return !0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Update pipeline idle and pipeline pause tracking states based on other
 | |
|    inputs.  This must be called whenever the other relevant inputs have
 | |
|    changed. */
 | |
| static int state_update_pipeline_state(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	unsigned int st;
 | |
| 	int updatedFl = 0;
 | |
| 	/* Update pipeline state */
 | |
| 	st = !(hdw->state_encoder_run ||
 | |
| 	       hdw->state_decoder_run ||
 | |
| 	       hdw->state_usbstream_run ||
 | |
| 	       (!hdw->state_decoder_quiescent));
 | |
| 	if (!st != !hdw->state_pipeline_idle) {
 | |
| 		hdw->state_pipeline_idle = st;
 | |
| 		updatedFl = !0;
 | |
| 	}
 | |
| 	if (hdw->state_pipeline_idle && hdw->state_pipeline_pause) {
 | |
| 		hdw->state_pipeline_pause = 0;
 | |
| 		updatedFl = !0;
 | |
| 	}
 | |
| 	return updatedFl;
 | |
| }
 | |
| 
 | |
| 
 | |
| typedef int (*state_eval_func)(struct pvr2_hdw *);
 | |
| 
 | |
| /* Set of functions to be run to evaluate various states in the driver. */
 | |
| static const state_eval_func eval_funcs[] = {
 | |
| 	state_eval_pathway_ok,
 | |
| 	state_eval_pipeline_config,
 | |
| 	state_eval_encoder_ok,
 | |
| 	state_eval_encoder_config,
 | |
| 	state_eval_decoder_run,
 | |
| 	state_eval_encoder_run,
 | |
| 	state_eval_usbstream_run,
 | |
| };
 | |
| 
 | |
| 
 | |
| /* Process various states and return true if we did anything interesting. */
 | |
| static int pvr2_hdw_state_update(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	unsigned int i;
 | |
| 	int state_updated = 0;
 | |
| 	int check_flag;
 | |
| 
 | |
| 	if (!hdw->state_stale) return 0;
 | |
| 	if ((hdw->fw1_state != FW1_STATE_OK) ||
 | |
| 	    !hdw->flag_ok) {
 | |
| 		hdw->state_stale = 0;
 | |
| 		return !0;
 | |
| 	}
 | |
| 	/* This loop is the heart of the entire driver.  It keeps trying to
 | |
| 	   evaluate various bits of driver state until nothing changes for
 | |
| 	   one full iteration.  Each "bit of state" tracks some global
 | |
| 	   aspect of the driver, e.g. whether decoder should run, if
 | |
| 	   pipeline is configured, usb streaming is on, etc.  We separately
 | |
| 	   evaluate each of those questions based on other driver state to
 | |
| 	   arrive at the correct running configuration. */
 | |
| 	do {
 | |
| 		check_flag = 0;
 | |
| 		state_update_pipeline_state(hdw);
 | |
| 		/* Iterate over each bit of state */
 | |
| 		for (i = 0; (i<ARRAY_SIZE(eval_funcs)) && hdw->flag_ok; i++) {
 | |
| 			if ((*eval_funcs[i])(hdw)) {
 | |
| 				check_flag = !0;
 | |
| 				state_updated = !0;
 | |
| 				state_update_pipeline_state(hdw);
 | |
| 			}
 | |
| 		}
 | |
| 	} while (check_flag && hdw->flag_ok);
 | |
| 	hdw->state_stale = 0;
 | |
| 	trace_stbit("state_stale",hdw->state_stale);
 | |
| 	return state_updated;
 | |
| }
 | |
| 
 | |
| 
 | |
| static unsigned int 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 (!((1UL << 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) {
 | |
| 	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)
 | |
| {
 | |
| 	switch (which) {
 | |
| 	case 0:
 | |
| 		return scnprintf(
 | |
| 			buf,acnt,
 | |
| 			"driver:%s%s%s%s%s <mode=%s>",
 | |
| 			(hdw->flag_ok ? " <ok>" : " <fail>"),
 | |
| 			(hdw->flag_init_ok ? " <init>" : " <uninitialized>"),
 | |
| 			(hdw->flag_disconnected ? " <disconnected>" :
 | |
| 			 " <connected>"),
 | |
| 			(hdw->flag_tripped ? " <tripped>" : ""),
 | |
| 			(hdw->flag_decoder_missed ? " <no decoder>" : ""),
 | |
| 			pvr2_pathway_state_name(hdw->pathway_state));
 | |
| 
 | |
| 	case 1:
 | |
| 		return scnprintf(
 | |
| 			buf,acnt,
 | |
| 			"pipeline:%s%s%s%s",
 | |
| 			(hdw->state_pipeline_idle ? " <idle>" : ""),
 | |
| 			(hdw->state_pipeline_config ?
 | |
| 			 " <configok>" : " <stale>"),
 | |
| 			(hdw->state_pipeline_req ? " <req>" : ""),
 | |
| 			(hdw->state_pipeline_pause ? " <pause>" : ""));
 | |
| 	case 2:
 | |
| 		return scnprintf(
 | |
| 			buf,acnt,
 | |
| 			"worker:%s%s%s%s%s%s%s",
 | |
| 			(hdw->state_decoder_run ?
 | |
| 			 (hdw->state_decoder_ready ?
 | |
| 			  "<decode:run>" : " <decode:start>") :
 | |
| 			 (hdw->state_decoder_quiescent ?
 | |
| 			  "" : " <decode:stop>")),
 | |
| 			(hdw->state_decoder_quiescent ?
 | |
| 			 " <decode:quiescent>" : ""),
 | |
| 			(hdw->state_encoder_ok ?
 | |
| 			 "" : " <encode:init>"),
 | |
| 			(hdw->state_encoder_run ?
 | |
| 			 (hdw->state_encoder_runok ?
 | |
| 			  " <encode:run>" :
 | |
| 			  " <encode:firstrun>") :
 | |
| 			 (hdw->state_encoder_runok ?
 | |
| 			  " <encode:stop>" :
 | |
| 			  " <encode:virgin>")),
 | |
| 			(hdw->state_encoder_config ?
 | |
| 			 " <encode:configok>" :
 | |
| 			 (hdw->state_encoder_waitok ?
 | |
| 			  "" : " <encode:waitok>")),
 | |
| 			(hdw->state_usbstream_run ?
 | |
| 			 " <usb:run>" : " <usb:stop>"),
 | |
| 			(hdw->state_pathway_ok ?
 | |
| 			 " <pathway:ok>" : ""));
 | |
| 	case 3:
 | |
| 		return scnprintf(
 | |
| 			buf,acnt,
 | |
| 			"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,
 | |
| 				      &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);
 | |
| 	}
 | |
| 	case 6: {
 | |
| 		unsigned int id = hdw->ir_scheme_active;
 | |
| 		return scnprintf(buf, acnt, "ir scheme: id=%d %s", id,
 | |
| 				 (id >= ARRAY_SIZE(ir_scheme_names) ?
 | |
| 				  "?" : ir_scheme_names[id]));
 | |
| 	}
 | |
| 	default: break;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Generate report containing info about attached sub-devices and attached
 | |
|    i2c clients, including an indication of which attached i2c clients are
 | |
|    actually sub-devices. */
 | |
| static unsigned int pvr2_hdw_report_clients(struct pvr2_hdw *hdw,
 | |
| 					    char *buf, unsigned int acnt)
 | |
| {
 | |
| 	struct v4l2_subdev *sd;
 | |
| 	unsigned int tcnt = 0;
 | |
| 	unsigned int ccnt;
 | |
| 	struct i2c_client *client;
 | |
| 	const char *p;
 | |
| 	unsigned int id;
 | |
| 
 | |
| 	ccnt = scnprintf(buf, acnt, "Associated v4l2-subdev drivers and I2C clients:\n");
 | |
| 	tcnt += ccnt;
 | |
| 	v4l2_device_for_each_subdev(sd, &hdw->v4l2_dev) {
 | |
| 		id = sd->grp_id;
 | |
| 		p = NULL;
 | |
| 		if (id < ARRAY_SIZE(module_names)) p = module_names[id];
 | |
| 		if (p) {
 | |
| 			ccnt = scnprintf(buf + tcnt, acnt - tcnt, "  %s:", p);
 | |
| 			tcnt += ccnt;
 | |
| 		} else {
 | |
| 			ccnt = scnprintf(buf + tcnt, acnt - tcnt,
 | |
| 					 "  (unknown id=%u):", id);
 | |
| 			tcnt += ccnt;
 | |
| 		}
 | |
| 		client = v4l2_get_subdevdata(sd);
 | |
| 		if (client) {
 | |
| 			ccnt = scnprintf(buf + tcnt, acnt - tcnt,
 | |
| 					 " %s @ %02x\n", client->name,
 | |
| 					 client->addr);
 | |
| 			tcnt += ccnt;
 | |
| 		} else {
 | |
| 			ccnt = scnprintf(buf + tcnt, acnt - tcnt,
 | |
| 					 " no i2c client\n");
 | |
| 			tcnt += ccnt;
 | |
| 		}
 | |
| 	}
 | |
| 	return tcnt;
 | |
| }
 | |
| 
 | |
| 
 | |
| unsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw,
 | |
| 				   char *buf,unsigned int acnt)
 | |
| {
 | |
| 	unsigned int bcnt,ccnt,idx;
 | |
| 	bcnt = 0;
 | |
| 	LOCK_TAKE(hdw->big_lock);
 | |
| 	for (idx = 0; ; idx++) {
 | |
| 		ccnt = pvr2_hdw_report_unlocked(hdw,idx,buf,acnt);
 | |
| 		if (!ccnt) break;
 | |
| 		bcnt += ccnt; acnt -= ccnt; buf += ccnt;
 | |
| 		if (!acnt) break;
 | |
| 		buf[0] = '\n'; ccnt = 1;
 | |
| 		bcnt += ccnt; acnt -= ccnt; buf += ccnt;
 | |
| 	}
 | |
| 	ccnt = pvr2_hdw_report_clients(hdw, buf, acnt);
 | |
| 	bcnt += ccnt; acnt -= ccnt; buf += ccnt;
 | |
| 	LOCK_GIVE(hdw->big_lock);
 | |
| 	return bcnt;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void pvr2_hdw_state_log_state(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	char buf[256];
 | |
| 	unsigned int idx, ccnt;
 | |
| 	unsigned int lcnt, ucnt;
 | |
| 
 | |
| 	for (idx = 0; ; idx++) {
 | |
| 		ccnt = pvr2_hdw_report_unlocked(hdw,idx,buf,sizeof(buf));
 | |
| 		if (!ccnt) break;
 | |
| 		pr_info("%s %.*s\n", hdw->name, ccnt, buf);
 | |
| 	}
 | |
| 	ccnt = pvr2_hdw_report_clients(hdw, buf, sizeof(buf));
 | |
| 	if (ccnt >= sizeof(buf))
 | |
| 		ccnt = sizeof(buf);
 | |
| 
 | |
| 	ucnt = 0;
 | |
| 	while (ucnt < ccnt) {
 | |
| 		lcnt = 0;
 | |
| 		while ((lcnt + ucnt < ccnt) && (buf[lcnt + ucnt] != '\n')) {
 | |
| 			lcnt++;
 | |
| 		}
 | |
| 		pr_info("%s %.*s\n", hdw->name, lcnt, buf + ucnt);
 | |
| 		ucnt += lcnt + 1;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Evaluate and update the driver's current state, taking various actions
 | |
|    as appropriate for the update. */
 | |
| static int pvr2_hdw_state_eval(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	unsigned int st;
 | |
| 	int state_updated = 0;
 | |
| 	int callback_flag = 0;
 | |
| 	int analog_mode;
 | |
| 
 | |
| 	pvr2_trace(PVR2_TRACE_STBITS,
 | |
| 		   "Drive state check START");
 | |
| 	if (pvrusb2_debug & PVR2_TRACE_STBITS) {
 | |
| 		pvr2_hdw_state_log_state(hdw);
 | |
| 	}
 | |
| 
 | |
| 	/* Process all state and get back over disposition */
 | |
| 	state_updated = pvr2_hdw_state_update(hdw);
 | |
| 
 | |
| 	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 ((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)) {
 | |
| 		st = PVR2_STATE_ERROR;
 | |
| 	} else if (hdw->state_usbstream_run &&
 | |
| 		   (!analog_mode ||
 | |
| 		    (hdw->state_encoder_run && hdw->state_decoder_run))) {
 | |
| 		st = PVR2_STATE_RUN;
 | |
| 	} else {
 | |
| 		st = PVR2_STATE_READY;
 | |
| 	}
 | |
| 	if (hdw->master_state != st) {
 | |
| 		pvr2_trace(PVR2_TRACE_STATE,
 | |
| 			   "Device state change from %s to %s",
 | |
| 			   pvr2_get_state_name(hdw->master_state),
 | |
| 			   pvr2_get_state_name(st));
 | |
| 		pvr2_led_ctrl(hdw,st == PVR2_STATE_RUN);
 | |
| 		hdw->master_state = st;
 | |
| 		state_updated = !0;
 | |
| 		callback_flag = !0;
 | |
| 	}
 | |
| 	if (state_updated) {
 | |
| 		/* Trigger anyone waiting on any state changes here. */
 | |
| 		wake_up(&hdw->state_wait_data);
 | |
| 	}
 | |
| 
 | |
| 	if (pvrusb2_debug & PVR2_TRACE_STBITS) {
 | |
| 		pvr2_hdw_state_log_state(hdw);
 | |
| 	}
 | |
| 	pvr2_trace(PVR2_TRACE_STBITS,
 | |
| 		   "Drive state check DONE callback=%d",callback_flag);
 | |
| 
 | |
| 	return callback_flag;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Cause kernel thread to check / update driver state */
 | |
| static void pvr2_hdw_state_sched(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	if (hdw->state_stale) return;
 | |
| 	hdw->state_stale = !0;
 | |
| 	trace_stbit("state_stale",hdw->state_stale);
 | |
| 	schedule_work(&hdw->workpoll);
 | |
| }
 | |
| 
 | |
| 
 | |
| int pvr2_hdw_gpio_get_dir(struct pvr2_hdw *hdw,u32 *dp)
 | |
| {
 | |
| 	return pvr2_read_register(hdw,PVR2_GPIO_DIR,dp);
 | |
| }
 | |
| 
 | |
| 
 | |
| int pvr2_hdw_gpio_get_out(struct pvr2_hdw *hdw,u32 *dp)
 | |
| {
 | |
| 	return pvr2_read_register(hdw,PVR2_GPIO_OUT,dp);
 | |
| }
 | |
| 
 | |
| 
 | |
| int pvr2_hdw_gpio_get_in(struct pvr2_hdw *hdw,u32 *dp)
 | |
| {
 | |
| 	return pvr2_read_register(hdw,PVR2_GPIO_IN,dp);
 | |
| }
 | |
| 
 | |
| 
 | |
| int pvr2_hdw_gpio_chg_dir(struct pvr2_hdw *hdw,u32 msk,u32 val)
 | |
| {
 | |
| 	u32 cval,nval;
 | |
| 	int ret;
 | |
| 	if (~msk) {
 | |
| 		ret = pvr2_read_register(hdw,PVR2_GPIO_DIR,&cval);
 | |
| 		if (ret) return ret;
 | |
| 		nval = (cval & ~msk) | (val & msk);
 | |
| 		pvr2_trace(PVR2_TRACE_GPIO,
 | |
| 			   "GPIO direction changing 0x%x:0x%x from 0x%x to 0x%x",
 | |
| 			   msk,val,cval,nval);
 | |
| 	} else {
 | |
| 		nval = val;
 | |
| 		pvr2_trace(PVR2_TRACE_GPIO,
 | |
| 			   "GPIO direction changing to 0x%x",nval);
 | |
| 	}
 | |
| 	return pvr2_write_register(hdw,PVR2_GPIO_DIR,nval);
 | |
| }
 | |
| 
 | |
| 
 | |
| int pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val)
 | |
| {
 | |
| 	u32 cval,nval;
 | |
| 	int ret;
 | |
| 	if (~msk) {
 | |
| 		ret = pvr2_read_register(hdw,PVR2_GPIO_OUT,&cval);
 | |
| 		if (ret) return ret;
 | |
| 		nval = (cval & ~msk) | (val & msk);
 | |
| 		pvr2_trace(PVR2_TRACE_GPIO,
 | |
| 			   "GPIO output changing 0x%x:0x%x from 0x%x to 0x%x",
 | |
| 			   msk,val,cval,nval);
 | |
| 	} else {
 | |
| 		nval = val;
 | |
| 		pvr2_trace(PVR2_TRACE_GPIO,
 | |
| 			   "GPIO output changing to 0x%x",nval);
 | |
| 	}
 | |
| 	return pvr2_write_register(hdw,PVR2_GPIO_OUT,nval);
 | |
| }
 | |
| 
 | |
| 
 | |
| void pvr2_hdw_status_poll(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	struct v4l2_tuner *vtp = &hdw->tuner_signal_info;
 | |
| 	memset(vtp, 0, sizeof(*vtp));
 | |
| 	vtp->type = (hdw->input_val == PVR2_CVAL_INPUT_RADIO) ?
 | |
| 		V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
 | |
| 	hdw->tuner_signal_stale = 0;
 | |
| 	/* Note: There apparently is no replacement for VIDIOC_CROPCAP
 | |
| 	   using v4l2-subdev - therefore we can't support that AT ALL right
 | |
| 	   now.  (Of course, no sub-drivers seem to implement it either.
 | |
| 	   But now it's a chicken and egg problem...) */
 | |
| 	v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, g_tuner, vtp);
 | |
| 	pvr2_trace(PVR2_TRACE_CHIPS, "subdev status poll type=%u strength=%u audio=0x%x cap=0x%x low=%u hi=%u",
 | |
| 		   vtp->type,
 | |
| 		   vtp->signal, vtp->rxsubchans, vtp->capability,
 | |
| 		   vtp->rangelow, vtp->rangehigh);
 | |
| 
 | |
| 	/* We have to do this to avoid getting into constant polling if
 | |
| 	   there's nobody to answer a poll of cropcap info. */
 | |
| 	hdw->cropcap_stale = 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| unsigned int pvr2_hdw_get_input_available(struct pvr2_hdw *hdw)
 | |
| {
 | |
| 	return hdw->input_avail_mask;
 | |
| }
 | |
| 
 | |
| 
 | |
| 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 ((1UL << 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 (!((1UL << 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)
 | |
| {
 | |
| 	int result;
 | |
| 	LOCK_TAKE(hdw->ctl_lock); do {
 | |
| 		hdw->cmd_buffer[0] = FX2CMD_GET_EEPROM_ADDR;
 | |
| 		result = pvr2_send_request(hdw,
 | |
| 					   hdw->cmd_buffer,1,
 | |
| 					   hdw->cmd_buffer,1);
 | |
| 		if (result < 0) break;
 | |
| 		result = hdw->cmd_buffer[0];
 | |
| 	} while(0); LOCK_GIVE(hdw->ctl_lock);
 | |
| 	return result;
 | |
| }
 |