12504ba9fSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
20c0d06caSMauro Carvalho Chehab /*
30c0d06caSMauro Carvalho Chehab  *
40c0d06caSMauro Carvalho Chehab  *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
50c0d06caSMauro Carvalho Chehab  */
60c0d06caSMauro Carvalho Chehab 
70c0d06caSMauro Carvalho Chehab #include <linux/errno.h>
80c0d06caSMauro Carvalho Chehab #include <linux/string.h>
90c0d06caSMauro Carvalho Chehab #include <linux/slab.h>
100c0d06caSMauro Carvalho Chehab #include <linux/module.h>
110c0d06caSMauro Carvalho Chehab #include <linux/firmware.h>
120c0d06caSMauro Carvalho Chehab #include <linux/videodev2.h>
130c0d06caSMauro Carvalho Chehab #include <media/v4l2-common.h>
140c0d06caSMauro Carvalho Chehab #include <media/tuner.h>
150c0d06caSMauro Carvalho Chehab #include "pvrusb2.h"
160c0d06caSMauro Carvalho Chehab #include "pvrusb2-std.h"
170c0d06caSMauro Carvalho Chehab #include "pvrusb2-util.h"
180c0d06caSMauro Carvalho Chehab #include "pvrusb2-hdw.h"
190c0d06caSMauro Carvalho Chehab #include "pvrusb2-i2c-core.h"
200c0d06caSMauro Carvalho Chehab #include "pvrusb2-eeprom.h"
210c0d06caSMauro Carvalho Chehab #include "pvrusb2-hdw-internal.h"
220c0d06caSMauro Carvalho Chehab #include "pvrusb2-encoder.h"
230c0d06caSMauro Carvalho Chehab #include "pvrusb2-debug.h"
240c0d06caSMauro Carvalho Chehab #include "pvrusb2-fx2-cmd.h"
250c0d06caSMauro Carvalho Chehab #include "pvrusb2-wm8775.h"
260c0d06caSMauro Carvalho Chehab #include "pvrusb2-video-v4l.h"
270c0d06caSMauro Carvalho Chehab #include "pvrusb2-cx2584x-v4l.h"
280c0d06caSMauro Carvalho Chehab #include "pvrusb2-cs53l32a.h"
290c0d06caSMauro Carvalho Chehab #include "pvrusb2-audio.h"
300c0d06caSMauro Carvalho Chehab 
310c0d06caSMauro Carvalho Chehab #define TV_MIN_FREQ     55250000L
320c0d06caSMauro Carvalho Chehab #define TV_MAX_FREQ    850000000L
330c0d06caSMauro Carvalho Chehab 
340c0d06caSMauro Carvalho Chehab /* This defines a minimum interval that the decoder must remain quiet
350c0d06caSMauro Carvalho Chehab    before we are allowed to start it running. */
360c0d06caSMauro Carvalho Chehab #define TIME_MSEC_DECODER_WAIT 50
370c0d06caSMauro Carvalho Chehab 
380c0d06caSMauro Carvalho Chehab /* This defines a minimum interval that the decoder must be allowed to run
390c0d06caSMauro Carvalho Chehab    before we can safely begin using its streaming output. */
400c0d06caSMauro Carvalho Chehab #define TIME_MSEC_DECODER_STABILIZATION_WAIT 300
410c0d06caSMauro Carvalho Chehab 
420c0d06caSMauro Carvalho Chehab /* This defines a minimum interval that the encoder must remain quiet
430c0d06caSMauro Carvalho Chehab    before we are allowed to configure it. */
440c0d06caSMauro Carvalho Chehab #define TIME_MSEC_ENCODER_WAIT 50
450c0d06caSMauro Carvalho Chehab 
460c0d06caSMauro Carvalho Chehab /* This defines the minimum interval that the encoder must successfully run
470c0d06caSMauro Carvalho Chehab    before we consider that the encoder has run at least once since its
480c0d06caSMauro Carvalho Chehab    firmware has been loaded.  This measurement is in important for cases
490c0d06caSMauro Carvalho Chehab    where we can't do something until we know that the encoder has been run
500c0d06caSMauro Carvalho Chehab    at least once. */
510c0d06caSMauro Carvalho Chehab #define TIME_MSEC_ENCODER_OK 250
520c0d06caSMauro Carvalho Chehab 
530c0d06caSMauro Carvalho Chehab static struct pvr2_hdw *unit_pointers[PVR_NUM] = {[ 0 ... PVR_NUM-1 ] = NULL};
540c0d06caSMauro Carvalho Chehab static DEFINE_MUTEX(pvr2_unit_mtx);
550c0d06caSMauro Carvalho Chehab 
560c0d06caSMauro Carvalho Chehab static int ctlchg;
570c0d06caSMauro Carvalho Chehab static int procreload;
580c0d06caSMauro Carvalho Chehab static int tuner[PVR_NUM] = { [0 ... PVR_NUM-1] = -1 };
590c0d06caSMauro Carvalho Chehab static int tolerance[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 };
600c0d06caSMauro Carvalho Chehab static int video_std[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 };
610c0d06caSMauro Carvalho Chehab static int init_pause_msec;
620c0d06caSMauro Carvalho Chehab 
630c0d06caSMauro Carvalho Chehab module_param(ctlchg, int, S_IRUGO|S_IWUSR);
640c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(ctlchg, "0=optimize ctl change 1=always accept new ctl value");
650c0d06caSMauro Carvalho Chehab module_param(init_pause_msec, int, S_IRUGO|S_IWUSR);
660c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(init_pause_msec, "hardware initialization settling delay");
670c0d06caSMauro Carvalho Chehab module_param(procreload, int, S_IRUGO|S_IWUSR);
680c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(procreload,
690c0d06caSMauro Carvalho Chehab 		 "Attempt init failure recovery with firmware reload");
700c0d06caSMauro Carvalho Chehab module_param_array(tuner,    int, NULL, 0444);
710c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(tuner,"specify installed tuner type");
720c0d06caSMauro Carvalho Chehab module_param_array(video_std,    int, NULL, 0444);
730c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(video_std,"specify initial video standard");
740c0d06caSMauro Carvalho Chehab module_param_array(tolerance,    int, NULL, 0444);
750c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(tolerance,"specify stream error tolerance");
760c0d06caSMauro Carvalho Chehab 
770c0d06caSMauro Carvalho Chehab /* US Broadcast channel 3 (61.25 MHz), to help with testing */
780c0d06caSMauro Carvalho Chehab static int default_tv_freq    = 61250000L;
790c0d06caSMauro Carvalho Chehab /* 104.3 MHz, a usable FM station for my area */
800c0d06caSMauro Carvalho Chehab static int default_radio_freq = 104300000L;
810c0d06caSMauro Carvalho Chehab 
820c0d06caSMauro Carvalho Chehab module_param_named(tv_freq, default_tv_freq, int, 0444);
830c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(tv_freq, "specify initial television frequency");
840c0d06caSMauro Carvalho Chehab module_param_named(radio_freq, default_radio_freq, int, 0444);
850c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(radio_freq, "specify initial radio frequency");
860c0d06caSMauro Carvalho Chehab 
870c0d06caSMauro Carvalho Chehab #define PVR2_CTL_WRITE_ENDPOINT  0x01
880c0d06caSMauro Carvalho Chehab #define PVR2_CTL_READ_ENDPOINT   0x81
890c0d06caSMauro Carvalho Chehab 
900c0d06caSMauro Carvalho Chehab #define PVR2_GPIO_IN 0x9008
910c0d06caSMauro Carvalho Chehab #define PVR2_GPIO_OUT 0x900c
920c0d06caSMauro Carvalho Chehab #define PVR2_GPIO_DIR 0x9020
930c0d06caSMauro Carvalho Chehab 
940c0d06caSMauro Carvalho Chehab #define trace_firmware(...) pvr2_trace(PVR2_TRACE_FIRMWARE,__VA_ARGS__)
950c0d06caSMauro Carvalho Chehab 
960c0d06caSMauro Carvalho Chehab #define PVR2_FIRMWARE_ENDPOINT   0x02
970c0d06caSMauro Carvalho Chehab 
980c0d06caSMauro Carvalho Chehab /* size of a firmware chunk */
990c0d06caSMauro Carvalho Chehab #define FIRMWARE_CHUNK_SIZE 0x2000
1000c0d06caSMauro Carvalho Chehab 
1010c0d06caSMauro Carvalho Chehab typedef void (*pvr2_subdev_update_func)(struct pvr2_hdw *,
1020c0d06caSMauro Carvalho Chehab 					struct v4l2_subdev *);
1030c0d06caSMauro Carvalho Chehab 
1040c0d06caSMauro Carvalho Chehab static const pvr2_subdev_update_func pvr2_module_update_functions[] = {
1050c0d06caSMauro Carvalho Chehab 	[PVR2_CLIENT_ID_WM8775] = pvr2_wm8775_subdev_update,
1060c0d06caSMauro Carvalho Chehab 	[PVR2_CLIENT_ID_SAA7115] = pvr2_saa7115_subdev_update,
1070c0d06caSMauro Carvalho Chehab 	[PVR2_CLIENT_ID_MSP3400] = pvr2_msp3400_subdev_update,
1080c0d06caSMauro Carvalho Chehab 	[PVR2_CLIENT_ID_CX25840] = pvr2_cx25840_subdev_update,
1090c0d06caSMauro Carvalho Chehab 	[PVR2_CLIENT_ID_CS53L32A] = pvr2_cs53l32a_subdev_update,
1100c0d06caSMauro Carvalho Chehab };
1110c0d06caSMauro Carvalho Chehab 
1120c0d06caSMauro Carvalho Chehab static const char *module_names[] = {
1130c0d06caSMauro Carvalho Chehab 	[PVR2_CLIENT_ID_MSP3400] = "msp3400",
1140c0d06caSMauro Carvalho Chehab 	[PVR2_CLIENT_ID_CX25840] = "cx25840",
1150c0d06caSMauro Carvalho Chehab 	[PVR2_CLIENT_ID_SAA7115] = "saa7115",
1160c0d06caSMauro Carvalho Chehab 	[PVR2_CLIENT_ID_TUNER] = "tuner",
1170c0d06caSMauro Carvalho Chehab 	[PVR2_CLIENT_ID_DEMOD] = "tuner",
1180c0d06caSMauro Carvalho Chehab 	[PVR2_CLIENT_ID_CS53L32A] = "cs53l32a",
1190c0d06caSMauro Carvalho Chehab 	[PVR2_CLIENT_ID_WM8775] = "wm8775",
1200c0d06caSMauro Carvalho Chehab };
1210c0d06caSMauro Carvalho Chehab 
1220c0d06caSMauro Carvalho Chehab 
1230c0d06caSMauro Carvalho Chehab static const unsigned char *module_i2c_addresses[] = {
1240c0d06caSMauro Carvalho Chehab 	[PVR2_CLIENT_ID_TUNER] = "\x60\x61\x62\x63",
1250c0d06caSMauro Carvalho Chehab 	[PVR2_CLIENT_ID_DEMOD] = "\x43",
1260c0d06caSMauro Carvalho Chehab 	[PVR2_CLIENT_ID_MSP3400] = "\x40",
1270c0d06caSMauro Carvalho Chehab 	[PVR2_CLIENT_ID_SAA7115] = "\x21",
1280c0d06caSMauro Carvalho Chehab 	[PVR2_CLIENT_ID_WM8775] = "\x1b",
1290c0d06caSMauro Carvalho Chehab 	[PVR2_CLIENT_ID_CX25840] = "\x44",
1300c0d06caSMauro Carvalho Chehab 	[PVR2_CLIENT_ID_CS53L32A] = "\x11",
1310c0d06caSMauro Carvalho Chehab };
1320c0d06caSMauro Carvalho Chehab 
1330c0d06caSMauro Carvalho Chehab 
1340c0d06caSMauro Carvalho Chehab static const char *ir_scheme_names[] = {
1350c0d06caSMauro Carvalho Chehab 	[PVR2_IR_SCHEME_NONE] = "none",
1360c0d06caSMauro Carvalho Chehab 	[PVR2_IR_SCHEME_29XXX] = "29xxx",
1370c0d06caSMauro Carvalho Chehab 	[PVR2_IR_SCHEME_24XXX] = "24xxx (29xxx emulation)",
1380c0d06caSMauro Carvalho Chehab 	[PVR2_IR_SCHEME_24XXX_MCE] = "24xxx (MCE device)",
1390c0d06caSMauro Carvalho Chehab 	[PVR2_IR_SCHEME_ZILOG] = "Zilog",
1400c0d06caSMauro Carvalho Chehab };
1410c0d06caSMauro Carvalho Chehab 
1420c0d06caSMauro Carvalho Chehab 
1430c0d06caSMauro Carvalho Chehab /* Define the list of additional controls we'll dynamically construct based
1440c0d06caSMauro Carvalho Chehab    on query of the cx2341x module. */
1450c0d06caSMauro Carvalho Chehab struct pvr2_mpeg_ids {
1460c0d06caSMauro Carvalho Chehab 	const char *strid;
1470c0d06caSMauro Carvalho Chehab 	int id;
1480c0d06caSMauro Carvalho Chehab };
1490c0d06caSMauro Carvalho Chehab static const struct pvr2_mpeg_ids mpeg_ids[] = {
1500c0d06caSMauro Carvalho Chehab 	{
1510c0d06caSMauro Carvalho Chehab 		.strid = "audio_layer",
1520c0d06caSMauro Carvalho Chehab 		.id = V4L2_CID_MPEG_AUDIO_ENCODING,
1530c0d06caSMauro Carvalho Chehab 	},{
1540c0d06caSMauro Carvalho Chehab 		.strid = "audio_bitrate",
1550c0d06caSMauro Carvalho Chehab 		.id = V4L2_CID_MPEG_AUDIO_L2_BITRATE,
1560c0d06caSMauro Carvalho Chehab 	},{
1570c0d06caSMauro Carvalho Chehab 		/* Already using audio_mode elsewhere :-( */
1580c0d06caSMauro Carvalho Chehab 		.strid = "mpeg_audio_mode",
1590c0d06caSMauro Carvalho Chehab 		.id = V4L2_CID_MPEG_AUDIO_MODE,
1600c0d06caSMauro Carvalho Chehab 	},{
1610c0d06caSMauro Carvalho Chehab 		.strid = "mpeg_audio_mode_extension",
1620c0d06caSMauro Carvalho Chehab 		.id = V4L2_CID_MPEG_AUDIO_MODE_EXTENSION,
1630c0d06caSMauro Carvalho Chehab 	},{
1640c0d06caSMauro Carvalho Chehab 		.strid = "audio_emphasis",
1650c0d06caSMauro Carvalho Chehab 		.id = V4L2_CID_MPEG_AUDIO_EMPHASIS,
1660c0d06caSMauro Carvalho Chehab 	},{
1670c0d06caSMauro Carvalho Chehab 		.strid = "audio_crc",
1680c0d06caSMauro Carvalho Chehab 		.id = V4L2_CID_MPEG_AUDIO_CRC,
1690c0d06caSMauro Carvalho Chehab 	},{
1700c0d06caSMauro Carvalho Chehab 		.strid = "video_aspect",
1710c0d06caSMauro Carvalho Chehab 		.id = V4L2_CID_MPEG_VIDEO_ASPECT,
1720c0d06caSMauro Carvalho Chehab 	},{
1730c0d06caSMauro Carvalho Chehab 		.strid = "video_b_frames",
1740c0d06caSMauro Carvalho Chehab 		.id = V4L2_CID_MPEG_VIDEO_B_FRAMES,
1750c0d06caSMauro Carvalho Chehab 	},{
1760c0d06caSMauro Carvalho Chehab 		.strid = "video_gop_size",
1770c0d06caSMauro Carvalho Chehab 		.id = V4L2_CID_MPEG_VIDEO_GOP_SIZE,
1780c0d06caSMauro Carvalho Chehab 	},{
1790c0d06caSMauro Carvalho Chehab 		.strid = "video_gop_closure",
1800c0d06caSMauro Carvalho Chehab 		.id = V4L2_CID_MPEG_VIDEO_GOP_CLOSURE,
1810c0d06caSMauro Carvalho Chehab 	},{
1820c0d06caSMauro Carvalho Chehab 		.strid = "video_bitrate_mode",
1830c0d06caSMauro Carvalho Chehab 		.id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
1840c0d06caSMauro Carvalho Chehab 	},{
1850c0d06caSMauro Carvalho Chehab 		.strid = "video_bitrate",
1860c0d06caSMauro Carvalho Chehab 		.id = V4L2_CID_MPEG_VIDEO_BITRATE,
1870c0d06caSMauro Carvalho Chehab 	},{
1880c0d06caSMauro Carvalho Chehab 		.strid = "video_bitrate_peak",
1890c0d06caSMauro Carvalho Chehab 		.id = V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
1900c0d06caSMauro Carvalho Chehab 	},{
1910c0d06caSMauro Carvalho Chehab 		.strid = "video_temporal_decimation",
1920c0d06caSMauro Carvalho Chehab 		.id = V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION,
1930c0d06caSMauro Carvalho Chehab 	},{
1940c0d06caSMauro Carvalho Chehab 		.strid = "stream_type",
1950c0d06caSMauro Carvalho Chehab 		.id = V4L2_CID_MPEG_STREAM_TYPE,
1960c0d06caSMauro Carvalho Chehab 	},{
1970c0d06caSMauro Carvalho Chehab 		.strid = "video_spatial_filter_mode",
1980c0d06caSMauro Carvalho Chehab 		.id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE,
1990c0d06caSMauro Carvalho Chehab 	},{
2000c0d06caSMauro Carvalho Chehab 		.strid = "video_spatial_filter",
2010c0d06caSMauro Carvalho Chehab 		.id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER,
2020c0d06caSMauro Carvalho Chehab 	},{
2030c0d06caSMauro Carvalho Chehab 		.strid = "video_luma_spatial_filter_type",
2040c0d06caSMauro Carvalho Chehab 		.id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE,
2050c0d06caSMauro Carvalho Chehab 	},{
2060c0d06caSMauro Carvalho Chehab 		.strid = "video_chroma_spatial_filter_type",
2070c0d06caSMauro Carvalho Chehab 		.id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE,
2080c0d06caSMauro Carvalho Chehab 	},{
2090c0d06caSMauro Carvalho Chehab 		.strid = "video_temporal_filter_mode",
2100c0d06caSMauro Carvalho Chehab 		.id = V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE,
2110c0d06caSMauro Carvalho Chehab 	},{
2120c0d06caSMauro Carvalho Chehab 		.strid = "video_temporal_filter",
2130c0d06caSMauro Carvalho Chehab 		.id = V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER,
2140c0d06caSMauro Carvalho Chehab 	},{
2150c0d06caSMauro Carvalho Chehab 		.strid = "video_median_filter_type",
2160c0d06caSMauro Carvalho Chehab 		.id = V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE,
2170c0d06caSMauro Carvalho Chehab 	},{
2180c0d06caSMauro Carvalho Chehab 		.strid = "video_luma_median_filter_top",
2190c0d06caSMauro Carvalho Chehab 		.id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP,
2200c0d06caSMauro Carvalho Chehab 	},{
2210c0d06caSMauro Carvalho Chehab 		.strid = "video_luma_median_filter_bottom",
2220c0d06caSMauro Carvalho Chehab 		.id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM,
2230c0d06caSMauro Carvalho Chehab 	},{
2240c0d06caSMauro Carvalho Chehab 		.strid = "video_chroma_median_filter_top",
2250c0d06caSMauro Carvalho Chehab 		.id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP,
2260c0d06caSMauro Carvalho Chehab 	},{
2270c0d06caSMauro Carvalho Chehab 		.strid = "video_chroma_median_filter_bottom",
2280c0d06caSMauro Carvalho Chehab 		.id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM,
2290c0d06caSMauro Carvalho Chehab 	}
2300c0d06caSMauro Carvalho Chehab };
2310c0d06caSMauro Carvalho Chehab #define MPEGDEF_COUNT ARRAY_SIZE(mpeg_ids)
2320c0d06caSMauro Carvalho Chehab 
2330c0d06caSMauro Carvalho Chehab 
2340c0d06caSMauro Carvalho Chehab static const char *control_values_srate[] = {
2350c0d06caSMauro Carvalho Chehab 	[V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100]   = "44.1 kHz",
2360c0d06caSMauro Carvalho Chehab 	[V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000]   = "48 kHz",
2370c0d06caSMauro Carvalho Chehab 	[V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000]   = "32 kHz",
2380c0d06caSMauro Carvalho Chehab };
2390c0d06caSMauro Carvalho Chehab 
2400c0d06caSMauro Carvalho Chehab 
2410c0d06caSMauro Carvalho Chehab 
2420c0d06caSMauro Carvalho Chehab static const char *control_values_input[] = {
2430c0d06caSMauro Carvalho Chehab 	[PVR2_CVAL_INPUT_TV]        = "television",  /*xawtv needs this name*/
2440c0d06caSMauro Carvalho Chehab 	[PVR2_CVAL_INPUT_DTV]       = "dtv",
2450c0d06caSMauro Carvalho Chehab 	[PVR2_CVAL_INPUT_RADIO]     = "radio",
2460c0d06caSMauro Carvalho Chehab 	[PVR2_CVAL_INPUT_SVIDEO]    = "s-video",
2470c0d06caSMauro Carvalho Chehab 	[PVR2_CVAL_INPUT_COMPOSITE] = "composite",
2480c0d06caSMauro Carvalho Chehab };
2490c0d06caSMauro Carvalho Chehab 
2500c0d06caSMauro Carvalho Chehab 
2510c0d06caSMauro Carvalho Chehab static const char *control_values_audiomode[] = {
2520c0d06caSMauro Carvalho Chehab 	[V4L2_TUNER_MODE_MONO]   = "Mono",
2530c0d06caSMauro Carvalho Chehab 	[V4L2_TUNER_MODE_STEREO] = "Stereo",
2540c0d06caSMauro Carvalho Chehab 	[V4L2_TUNER_MODE_LANG1]  = "Lang1",
2550c0d06caSMauro Carvalho Chehab 	[V4L2_TUNER_MODE_LANG2]  = "Lang2",
2560c0d06caSMauro Carvalho Chehab 	[V4L2_TUNER_MODE_LANG1_LANG2] = "Lang1+Lang2",
2570c0d06caSMauro Carvalho Chehab };
2580c0d06caSMauro Carvalho Chehab 
2590c0d06caSMauro Carvalho Chehab 
2600c0d06caSMauro Carvalho Chehab static const char *control_values_hsm[] = {
2610c0d06caSMauro Carvalho Chehab 	[PVR2_CVAL_HSM_FAIL] = "Fail",
2620c0d06caSMauro Carvalho Chehab 	[PVR2_CVAL_HSM_HIGH] = "High",
2630c0d06caSMauro Carvalho Chehab 	[PVR2_CVAL_HSM_FULL] = "Full",
2640c0d06caSMauro Carvalho Chehab };
2650c0d06caSMauro Carvalho Chehab 
2660c0d06caSMauro Carvalho Chehab 
2670c0d06caSMauro Carvalho Chehab static const char *pvr2_state_names[] = {
2680c0d06caSMauro Carvalho Chehab 	[PVR2_STATE_NONE] =    "none",
2690c0d06caSMauro Carvalho Chehab 	[PVR2_STATE_DEAD] =    "dead",
2700c0d06caSMauro Carvalho Chehab 	[PVR2_STATE_COLD] =    "cold",
2710c0d06caSMauro Carvalho Chehab 	[PVR2_STATE_WARM] =    "warm",
2720c0d06caSMauro Carvalho Chehab 	[PVR2_STATE_ERROR] =   "error",
2730c0d06caSMauro Carvalho Chehab 	[PVR2_STATE_READY] =   "ready",
2740c0d06caSMauro Carvalho Chehab 	[PVR2_STATE_RUN] =     "run",
2750c0d06caSMauro Carvalho Chehab };
2760c0d06caSMauro Carvalho Chehab 
2770c0d06caSMauro Carvalho Chehab 
2780c0d06caSMauro Carvalho Chehab struct pvr2_fx2cmd_descdef {
2790c0d06caSMauro Carvalho Chehab 	unsigned char id;
2800c0d06caSMauro Carvalho Chehab 	unsigned char *desc;
2810c0d06caSMauro Carvalho Chehab };
2820c0d06caSMauro Carvalho Chehab 
2830c0d06caSMauro Carvalho Chehab static const struct pvr2_fx2cmd_descdef pvr2_fx2cmd_desc[] = {
2840c0d06caSMauro Carvalho Chehab 	{FX2CMD_MEM_WRITE_DWORD, "write encoder dword"},
2850c0d06caSMauro Carvalho Chehab 	{FX2CMD_MEM_READ_DWORD, "read encoder dword"},
2860c0d06caSMauro Carvalho Chehab 	{FX2CMD_HCW_ZILOG_RESET, "zilog IR reset control"},
2870c0d06caSMauro Carvalho Chehab 	{FX2CMD_MEM_READ_64BYTES, "read encoder 64bytes"},
2880c0d06caSMauro Carvalho Chehab 	{FX2CMD_REG_WRITE, "write encoder register"},
2890c0d06caSMauro Carvalho Chehab 	{FX2CMD_REG_READ, "read encoder register"},
2900c0d06caSMauro Carvalho Chehab 	{FX2CMD_MEMSEL, "encoder memsel"},
2910c0d06caSMauro Carvalho Chehab 	{FX2CMD_I2C_WRITE, "i2c write"},
2920c0d06caSMauro Carvalho Chehab 	{FX2CMD_I2C_READ, "i2c read"},
2930c0d06caSMauro Carvalho Chehab 	{FX2CMD_GET_USB_SPEED, "get USB speed"},
2940c0d06caSMauro Carvalho Chehab 	{FX2CMD_STREAMING_ON, "stream on"},
2950c0d06caSMauro Carvalho Chehab 	{FX2CMD_STREAMING_OFF, "stream off"},
2960c0d06caSMauro Carvalho Chehab 	{FX2CMD_FWPOST1, "fwpost1"},
2970c0d06caSMauro Carvalho Chehab 	{FX2CMD_POWER_OFF, "power off"},
2980c0d06caSMauro Carvalho Chehab 	{FX2CMD_POWER_ON, "power on"},
2990c0d06caSMauro Carvalho Chehab 	{FX2CMD_DEEP_RESET, "deep reset"},
3000c0d06caSMauro Carvalho Chehab 	{FX2CMD_GET_EEPROM_ADDR, "get rom addr"},
3010c0d06caSMauro Carvalho Chehab 	{FX2CMD_GET_IR_CODE, "get IR code"},
3020c0d06caSMauro Carvalho Chehab 	{FX2CMD_HCW_DEMOD_RESETIN, "hcw demod resetin"},
3030c0d06caSMauro Carvalho Chehab 	{FX2CMD_HCW_DTV_STREAMING_ON, "hcw dtv stream on"},
3040c0d06caSMauro Carvalho Chehab 	{FX2CMD_HCW_DTV_STREAMING_OFF, "hcw dtv stream off"},
3050c0d06caSMauro Carvalho Chehab 	{FX2CMD_ONAIR_DTV_STREAMING_ON, "onair dtv stream on"},
3060c0d06caSMauro Carvalho Chehab 	{FX2CMD_ONAIR_DTV_STREAMING_OFF, "onair dtv stream off"},
3070c0d06caSMauro Carvalho Chehab 	{FX2CMD_ONAIR_DTV_POWER_ON, "onair dtv power on"},
3080c0d06caSMauro Carvalho Chehab 	{FX2CMD_ONAIR_DTV_POWER_OFF, "onair dtv power off"},
309dd60bf43SBrad Love 	{FX2CMD_HCW_DEMOD_RESET_PIN, "hcw demod reset pin"},
310dd60bf43SBrad Love 	{FX2CMD_HCW_MAKO_SLEEP_PIN, "hcw mako sleep pin"},
3110c0d06caSMauro Carvalho Chehab };
3120c0d06caSMauro Carvalho Chehab 
3130c0d06caSMauro Carvalho Chehab 
3140c0d06caSMauro Carvalho Chehab static int pvr2_hdw_set_input(struct pvr2_hdw *hdw,int v);
3150c0d06caSMauro Carvalho Chehab static void pvr2_hdw_state_sched(struct pvr2_hdw *);
3160c0d06caSMauro Carvalho Chehab static int pvr2_hdw_state_eval(struct pvr2_hdw *);
3170c0d06caSMauro Carvalho Chehab static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *,unsigned long);
3180c0d06caSMauro Carvalho Chehab static void pvr2_hdw_worker_poll(struct work_struct *work);
3190c0d06caSMauro Carvalho Chehab static int pvr2_hdw_wait(struct pvr2_hdw *,int state);
3200c0d06caSMauro Carvalho Chehab static int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *);
3210c0d06caSMauro Carvalho Chehab static void pvr2_hdw_state_log_state(struct pvr2_hdw *);
3220c0d06caSMauro Carvalho Chehab static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl);
3230c0d06caSMauro Carvalho Chehab static int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw);
3240c0d06caSMauro Carvalho Chehab static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw);
3258da0edf2SKees Cook static void pvr2_hdw_quiescent_timeout(struct timer_list *);
3268da0edf2SKees Cook static void pvr2_hdw_decoder_stabilization_timeout(struct timer_list *);
3278da0edf2SKees Cook static void pvr2_hdw_encoder_wait_timeout(struct timer_list *);
3288da0edf2SKees Cook static void pvr2_hdw_encoder_run_timeout(struct timer_list *);
3290c0d06caSMauro Carvalho Chehab static int pvr2_issue_simple_cmd(struct pvr2_hdw *,u32);
3300c0d06caSMauro Carvalho Chehab static int pvr2_send_request_ex(struct pvr2_hdw *hdw,
3310c0d06caSMauro Carvalho Chehab 				unsigned int timeout,int probe_fl,
3320c0d06caSMauro Carvalho Chehab 				void *write_data,unsigned int write_len,
3330c0d06caSMauro Carvalho Chehab 				void *read_data,unsigned int read_len);
3340c0d06caSMauro Carvalho Chehab static int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw);
3350c0d06caSMauro Carvalho Chehab static v4l2_std_id pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw);
3360c0d06caSMauro Carvalho Chehab 
trace_stbit(const char * name,int val)3370c0d06caSMauro Carvalho Chehab static void trace_stbit(const char *name,int val)
3380c0d06caSMauro Carvalho Chehab {
3390c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_STBITS,
3400c0d06caSMauro Carvalho Chehab 		   "State bit %s <-- %s",
3410c0d06caSMauro Carvalho Chehab 		   name,(val ? "true" : "false"));
3420c0d06caSMauro Carvalho Chehab }
3430c0d06caSMauro Carvalho Chehab 
ctrl_channelfreq_get(struct pvr2_ctrl * cptr,int * vp)3440c0d06caSMauro Carvalho Chehab static int ctrl_channelfreq_get(struct pvr2_ctrl *cptr,int *vp)
3450c0d06caSMauro Carvalho Chehab {
3460c0d06caSMauro Carvalho Chehab 	struct pvr2_hdw *hdw = cptr->hdw;
3470c0d06caSMauro Carvalho Chehab 	if ((hdw->freqProgSlot > 0) && (hdw->freqProgSlot <= FREQTABLE_SIZE)) {
3480c0d06caSMauro Carvalho Chehab 		*vp = hdw->freqTable[hdw->freqProgSlot-1];
3490c0d06caSMauro Carvalho Chehab 	} else {
3500c0d06caSMauro Carvalho Chehab 		*vp = 0;
3510c0d06caSMauro Carvalho Chehab 	}
3520c0d06caSMauro Carvalho Chehab 	return 0;
3530c0d06caSMauro Carvalho Chehab }
3540c0d06caSMauro Carvalho Chehab 
ctrl_channelfreq_set(struct pvr2_ctrl * cptr,int m,int v)3550c0d06caSMauro Carvalho Chehab static int ctrl_channelfreq_set(struct pvr2_ctrl *cptr,int m,int v)
3560c0d06caSMauro Carvalho Chehab {
3570c0d06caSMauro Carvalho Chehab 	struct pvr2_hdw *hdw = cptr->hdw;
3580c0d06caSMauro Carvalho Chehab 	unsigned int slotId = hdw->freqProgSlot;
3590c0d06caSMauro Carvalho Chehab 	if ((slotId > 0) && (slotId <= FREQTABLE_SIZE)) {
3600c0d06caSMauro Carvalho Chehab 		hdw->freqTable[slotId-1] = v;
3610c0d06caSMauro Carvalho Chehab 		/* Handle side effects correctly - if we're tuned to this
3620c0d06caSMauro Carvalho Chehab 		   slot, then forgot the slot id relation since the stored
3630c0d06caSMauro Carvalho Chehab 		   frequency has been changed. */
3640c0d06caSMauro Carvalho Chehab 		if (hdw->freqSelector) {
3650c0d06caSMauro Carvalho Chehab 			if (hdw->freqSlotRadio == slotId) {
3660c0d06caSMauro Carvalho Chehab 				hdw->freqSlotRadio = 0;
3670c0d06caSMauro Carvalho Chehab 			}
3680c0d06caSMauro Carvalho Chehab 		} else {
3690c0d06caSMauro Carvalho Chehab 			if (hdw->freqSlotTelevision == slotId) {
3700c0d06caSMauro Carvalho Chehab 				hdw->freqSlotTelevision = 0;
3710c0d06caSMauro Carvalho Chehab 			}
3720c0d06caSMauro Carvalho Chehab 		}
3730c0d06caSMauro Carvalho Chehab 	}
3740c0d06caSMauro Carvalho Chehab 	return 0;
3750c0d06caSMauro Carvalho Chehab }
3760c0d06caSMauro Carvalho Chehab 
ctrl_channelprog_get(struct pvr2_ctrl * cptr,int * vp)3770c0d06caSMauro Carvalho Chehab static int ctrl_channelprog_get(struct pvr2_ctrl *cptr,int *vp)
3780c0d06caSMauro Carvalho Chehab {
3790c0d06caSMauro Carvalho Chehab 	*vp = cptr->hdw->freqProgSlot;
3800c0d06caSMauro Carvalho Chehab 	return 0;
3810c0d06caSMauro Carvalho Chehab }
3820c0d06caSMauro Carvalho Chehab 
ctrl_channelprog_set(struct pvr2_ctrl * cptr,int m,int v)3830c0d06caSMauro Carvalho Chehab static int ctrl_channelprog_set(struct pvr2_ctrl *cptr,int m,int v)
3840c0d06caSMauro Carvalho Chehab {
3850c0d06caSMauro Carvalho Chehab 	struct pvr2_hdw *hdw = cptr->hdw;
3860c0d06caSMauro Carvalho Chehab 	if ((v >= 0) && (v <= FREQTABLE_SIZE)) {
3870c0d06caSMauro Carvalho Chehab 		hdw->freqProgSlot = v;
3880c0d06caSMauro Carvalho Chehab 	}
3890c0d06caSMauro Carvalho Chehab 	return 0;
3900c0d06caSMauro Carvalho Chehab }
3910c0d06caSMauro Carvalho Chehab 
ctrl_channel_get(struct pvr2_ctrl * cptr,int * vp)3920c0d06caSMauro Carvalho Chehab static int ctrl_channel_get(struct pvr2_ctrl *cptr,int *vp)
3930c0d06caSMauro Carvalho Chehab {
3940c0d06caSMauro Carvalho Chehab 	struct pvr2_hdw *hdw = cptr->hdw;
3950c0d06caSMauro Carvalho Chehab 	*vp = hdw->freqSelector ? hdw->freqSlotRadio : hdw->freqSlotTelevision;
3960c0d06caSMauro Carvalho Chehab 	return 0;
3970c0d06caSMauro Carvalho Chehab }
3980c0d06caSMauro Carvalho Chehab 
ctrl_channel_set(struct pvr2_ctrl * cptr,int m,int slotId)3990c0d06caSMauro Carvalho Chehab static int ctrl_channel_set(struct pvr2_ctrl *cptr,int m,int slotId)
4000c0d06caSMauro Carvalho Chehab {
4010c0d06caSMauro Carvalho Chehab 	unsigned freq = 0;
4020c0d06caSMauro Carvalho Chehab 	struct pvr2_hdw *hdw = cptr->hdw;
4030c0d06caSMauro Carvalho Chehab 	if ((slotId < 0) || (slotId > FREQTABLE_SIZE)) return 0;
4040c0d06caSMauro Carvalho Chehab 	if (slotId > 0) {
4050c0d06caSMauro Carvalho Chehab 		freq = hdw->freqTable[slotId-1];
4060c0d06caSMauro Carvalho Chehab 		if (!freq) return 0;
4070c0d06caSMauro Carvalho Chehab 		pvr2_hdw_set_cur_freq(hdw,freq);
4080c0d06caSMauro Carvalho Chehab 	}
4090c0d06caSMauro Carvalho Chehab 	if (hdw->freqSelector) {
4100c0d06caSMauro Carvalho Chehab 		hdw->freqSlotRadio = slotId;
4110c0d06caSMauro Carvalho Chehab 	} else {
4120c0d06caSMauro Carvalho Chehab 		hdw->freqSlotTelevision = slotId;
4130c0d06caSMauro Carvalho Chehab 	}
4140c0d06caSMauro Carvalho Chehab 	return 0;
4150c0d06caSMauro Carvalho Chehab }
4160c0d06caSMauro Carvalho Chehab 
ctrl_freq_get(struct pvr2_ctrl * cptr,int * vp)4170c0d06caSMauro Carvalho Chehab static int ctrl_freq_get(struct pvr2_ctrl *cptr,int *vp)
4180c0d06caSMauro Carvalho Chehab {
4190c0d06caSMauro Carvalho Chehab 	*vp = pvr2_hdw_get_cur_freq(cptr->hdw);
4200c0d06caSMauro Carvalho Chehab 	return 0;
4210c0d06caSMauro Carvalho Chehab }
4220c0d06caSMauro Carvalho Chehab 
ctrl_freq_is_dirty(struct pvr2_ctrl * cptr)4230c0d06caSMauro Carvalho Chehab static int ctrl_freq_is_dirty(struct pvr2_ctrl *cptr)
4240c0d06caSMauro Carvalho Chehab {
4250c0d06caSMauro Carvalho Chehab 	return cptr->hdw->freqDirty != 0;
4260c0d06caSMauro Carvalho Chehab }
4270c0d06caSMauro Carvalho Chehab 
ctrl_freq_clear_dirty(struct pvr2_ctrl * cptr)4280c0d06caSMauro Carvalho Chehab static void ctrl_freq_clear_dirty(struct pvr2_ctrl *cptr)
4290c0d06caSMauro Carvalho Chehab {
4300c0d06caSMauro Carvalho Chehab 	cptr->hdw->freqDirty = 0;
4310c0d06caSMauro Carvalho Chehab }
4320c0d06caSMauro Carvalho Chehab 
ctrl_freq_set(struct pvr2_ctrl * cptr,int m,int v)4330c0d06caSMauro Carvalho Chehab static int ctrl_freq_set(struct pvr2_ctrl *cptr,int m,int v)
4340c0d06caSMauro Carvalho Chehab {
4350c0d06caSMauro Carvalho Chehab 	pvr2_hdw_set_cur_freq(cptr->hdw,v);
4360c0d06caSMauro Carvalho Chehab 	return 0;
4370c0d06caSMauro Carvalho Chehab }
4380c0d06caSMauro Carvalho Chehab 
ctrl_cropl_min_get(struct pvr2_ctrl * cptr,int * left)4390c0d06caSMauro Carvalho Chehab static int ctrl_cropl_min_get(struct pvr2_ctrl *cptr, int *left)
4400c0d06caSMauro Carvalho Chehab {
4410c0d06caSMauro Carvalho Chehab 	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
4420c0d06caSMauro Carvalho Chehab 	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
4430c0d06caSMauro Carvalho Chehab 	if (stat != 0) {
4440c0d06caSMauro Carvalho Chehab 		return stat;
4450c0d06caSMauro Carvalho Chehab 	}
4460c0d06caSMauro Carvalho Chehab 	*left = cap->bounds.left;
4470c0d06caSMauro Carvalho Chehab 	return 0;
4480c0d06caSMauro Carvalho Chehab }
4490c0d06caSMauro Carvalho Chehab 
ctrl_cropl_max_get(struct pvr2_ctrl * cptr,int * left)4500c0d06caSMauro Carvalho Chehab static int ctrl_cropl_max_get(struct pvr2_ctrl *cptr, int *left)
4510c0d06caSMauro Carvalho Chehab {
4520c0d06caSMauro Carvalho Chehab 	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
4530c0d06caSMauro Carvalho Chehab 	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
4540c0d06caSMauro Carvalho Chehab 	if (stat != 0) {
4550c0d06caSMauro Carvalho Chehab 		return stat;
4560c0d06caSMauro Carvalho Chehab 	}
4570c0d06caSMauro Carvalho Chehab 	*left = cap->bounds.left;
4580c0d06caSMauro Carvalho Chehab 	if (cap->bounds.width > cptr->hdw->cropw_val) {
4590c0d06caSMauro Carvalho Chehab 		*left += cap->bounds.width - cptr->hdw->cropw_val;
4600c0d06caSMauro Carvalho Chehab 	}
4610c0d06caSMauro Carvalho Chehab 	return 0;
4620c0d06caSMauro Carvalho Chehab }
4630c0d06caSMauro Carvalho Chehab 
ctrl_cropt_min_get(struct pvr2_ctrl * cptr,int * top)4640c0d06caSMauro Carvalho Chehab static int ctrl_cropt_min_get(struct pvr2_ctrl *cptr, int *top)
4650c0d06caSMauro Carvalho Chehab {
4660c0d06caSMauro Carvalho Chehab 	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
4670c0d06caSMauro Carvalho Chehab 	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
4680c0d06caSMauro Carvalho Chehab 	if (stat != 0) {
4690c0d06caSMauro Carvalho Chehab 		return stat;
4700c0d06caSMauro Carvalho Chehab 	}
4710c0d06caSMauro Carvalho Chehab 	*top = cap->bounds.top;
4720c0d06caSMauro Carvalho Chehab 	return 0;
4730c0d06caSMauro Carvalho Chehab }
4740c0d06caSMauro Carvalho Chehab 
ctrl_cropt_max_get(struct pvr2_ctrl * cptr,int * top)4750c0d06caSMauro Carvalho Chehab static int ctrl_cropt_max_get(struct pvr2_ctrl *cptr, int *top)
4760c0d06caSMauro Carvalho Chehab {
4770c0d06caSMauro Carvalho Chehab 	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
4780c0d06caSMauro Carvalho Chehab 	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
4790c0d06caSMauro Carvalho Chehab 	if (stat != 0) {
4800c0d06caSMauro Carvalho Chehab 		return stat;
4810c0d06caSMauro Carvalho Chehab 	}
4820c0d06caSMauro Carvalho Chehab 	*top = cap->bounds.top;
4830c0d06caSMauro Carvalho Chehab 	if (cap->bounds.height > cptr->hdw->croph_val) {
4840c0d06caSMauro Carvalho Chehab 		*top += cap->bounds.height - cptr->hdw->croph_val;
4850c0d06caSMauro Carvalho Chehab 	}
4860c0d06caSMauro Carvalho Chehab 	return 0;
4870c0d06caSMauro Carvalho Chehab }
4880c0d06caSMauro Carvalho Chehab 
ctrl_cropw_max_get(struct pvr2_ctrl * cptr,int * width)4890c0d06caSMauro Carvalho Chehab static int ctrl_cropw_max_get(struct pvr2_ctrl *cptr, int *width)
4900c0d06caSMauro Carvalho Chehab {
4910c0d06caSMauro Carvalho Chehab 	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
4920c0d06caSMauro Carvalho Chehab 	int stat, bleftend, cleft;
4930c0d06caSMauro Carvalho Chehab 
4940c0d06caSMauro Carvalho Chehab 	stat = pvr2_hdw_check_cropcap(cptr->hdw);
4950c0d06caSMauro Carvalho Chehab 	if (stat != 0) {
4960c0d06caSMauro Carvalho Chehab 		return stat;
4970c0d06caSMauro Carvalho Chehab 	}
4980c0d06caSMauro Carvalho Chehab 	bleftend = cap->bounds.left+cap->bounds.width;
4990c0d06caSMauro Carvalho Chehab 	cleft = cptr->hdw->cropl_val;
5000c0d06caSMauro Carvalho Chehab 
5010c0d06caSMauro Carvalho Chehab 	*width = cleft < bleftend ? bleftend-cleft : 0;
5020c0d06caSMauro Carvalho Chehab 	return 0;
5030c0d06caSMauro Carvalho Chehab }
5040c0d06caSMauro Carvalho Chehab 
ctrl_croph_max_get(struct pvr2_ctrl * cptr,int * height)5050c0d06caSMauro Carvalho Chehab static int ctrl_croph_max_get(struct pvr2_ctrl *cptr, int *height)
5060c0d06caSMauro Carvalho Chehab {
5070c0d06caSMauro Carvalho Chehab 	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
5080c0d06caSMauro Carvalho Chehab 	int stat, btopend, ctop;
5090c0d06caSMauro Carvalho Chehab 
5100c0d06caSMauro Carvalho Chehab 	stat = pvr2_hdw_check_cropcap(cptr->hdw);
5110c0d06caSMauro Carvalho Chehab 	if (stat != 0) {
5120c0d06caSMauro Carvalho Chehab 		return stat;
5130c0d06caSMauro Carvalho Chehab 	}
5140c0d06caSMauro Carvalho Chehab 	btopend = cap->bounds.top+cap->bounds.height;
5150c0d06caSMauro Carvalho Chehab 	ctop = cptr->hdw->cropt_val;
5160c0d06caSMauro Carvalho Chehab 
5170c0d06caSMauro Carvalho Chehab 	*height = ctop < btopend ? btopend-ctop : 0;
5180c0d06caSMauro Carvalho Chehab 	return 0;
5190c0d06caSMauro Carvalho Chehab }
5200c0d06caSMauro Carvalho Chehab 
ctrl_get_cropcapbl(struct pvr2_ctrl * cptr,int * val)5210c0d06caSMauro Carvalho Chehab static int ctrl_get_cropcapbl(struct pvr2_ctrl *cptr, int *val)
5220c0d06caSMauro Carvalho Chehab {
5230c0d06caSMauro Carvalho Chehab 	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
5240c0d06caSMauro Carvalho Chehab 	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
5250c0d06caSMauro Carvalho Chehab 	if (stat != 0) {
5260c0d06caSMauro Carvalho Chehab 		return stat;
5270c0d06caSMauro Carvalho Chehab 	}
5280c0d06caSMauro Carvalho Chehab 	*val = cap->bounds.left;
5290c0d06caSMauro Carvalho Chehab 	return 0;
5300c0d06caSMauro Carvalho Chehab }
5310c0d06caSMauro Carvalho Chehab 
ctrl_get_cropcapbt(struct pvr2_ctrl * cptr,int * val)5320c0d06caSMauro Carvalho Chehab static int ctrl_get_cropcapbt(struct pvr2_ctrl *cptr, int *val)
5330c0d06caSMauro Carvalho Chehab {
5340c0d06caSMauro Carvalho Chehab 	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
5350c0d06caSMauro Carvalho Chehab 	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
5360c0d06caSMauro Carvalho Chehab 	if (stat != 0) {
5370c0d06caSMauro Carvalho Chehab 		return stat;
5380c0d06caSMauro Carvalho Chehab 	}
5390c0d06caSMauro Carvalho Chehab 	*val = cap->bounds.top;
5400c0d06caSMauro Carvalho Chehab 	return 0;
5410c0d06caSMauro Carvalho Chehab }
5420c0d06caSMauro Carvalho Chehab 
ctrl_get_cropcapbw(struct pvr2_ctrl * cptr,int * val)5430c0d06caSMauro Carvalho Chehab static int ctrl_get_cropcapbw(struct pvr2_ctrl *cptr, int *val)
5440c0d06caSMauro Carvalho Chehab {
5450c0d06caSMauro Carvalho Chehab 	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
5460c0d06caSMauro Carvalho Chehab 	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
5470c0d06caSMauro Carvalho Chehab 	if (stat != 0) {
5480c0d06caSMauro Carvalho Chehab 		return stat;
5490c0d06caSMauro Carvalho Chehab 	}
5500c0d06caSMauro Carvalho Chehab 	*val = cap->bounds.width;
5510c0d06caSMauro Carvalho Chehab 	return 0;
5520c0d06caSMauro Carvalho Chehab }
5530c0d06caSMauro Carvalho Chehab 
ctrl_get_cropcapbh(struct pvr2_ctrl * cptr,int * val)5540c0d06caSMauro Carvalho Chehab static int ctrl_get_cropcapbh(struct pvr2_ctrl *cptr, int *val)
5550c0d06caSMauro Carvalho Chehab {
5560c0d06caSMauro Carvalho Chehab 	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
5570c0d06caSMauro Carvalho Chehab 	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
5580c0d06caSMauro Carvalho Chehab 	if (stat != 0) {
5590c0d06caSMauro Carvalho Chehab 		return stat;
5600c0d06caSMauro Carvalho Chehab 	}
5610c0d06caSMauro Carvalho Chehab 	*val = cap->bounds.height;
5620c0d06caSMauro Carvalho Chehab 	return 0;
5630c0d06caSMauro Carvalho Chehab }
5640c0d06caSMauro Carvalho Chehab 
ctrl_get_cropcapdl(struct pvr2_ctrl * cptr,int * val)5650c0d06caSMauro Carvalho Chehab static int ctrl_get_cropcapdl(struct pvr2_ctrl *cptr, int *val)
5660c0d06caSMauro Carvalho Chehab {
5670c0d06caSMauro Carvalho Chehab 	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
5680c0d06caSMauro Carvalho Chehab 	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
5690c0d06caSMauro Carvalho Chehab 	if (stat != 0) {
5700c0d06caSMauro Carvalho Chehab 		return stat;
5710c0d06caSMauro Carvalho Chehab 	}
5720c0d06caSMauro Carvalho Chehab 	*val = cap->defrect.left;
5730c0d06caSMauro Carvalho Chehab 	return 0;
5740c0d06caSMauro Carvalho Chehab }
5750c0d06caSMauro Carvalho Chehab 
ctrl_get_cropcapdt(struct pvr2_ctrl * cptr,int * val)5760c0d06caSMauro Carvalho Chehab static int ctrl_get_cropcapdt(struct pvr2_ctrl *cptr, int *val)
5770c0d06caSMauro Carvalho Chehab {
5780c0d06caSMauro Carvalho Chehab 	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
5790c0d06caSMauro Carvalho Chehab 	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
5800c0d06caSMauro Carvalho Chehab 	if (stat != 0) {
5810c0d06caSMauro Carvalho Chehab 		return stat;
5820c0d06caSMauro Carvalho Chehab 	}
5830c0d06caSMauro Carvalho Chehab 	*val = cap->defrect.top;
5840c0d06caSMauro Carvalho Chehab 	return 0;
5850c0d06caSMauro Carvalho Chehab }
5860c0d06caSMauro Carvalho Chehab 
ctrl_get_cropcapdw(struct pvr2_ctrl * cptr,int * val)5870c0d06caSMauro Carvalho Chehab static int ctrl_get_cropcapdw(struct pvr2_ctrl *cptr, int *val)
5880c0d06caSMauro Carvalho Chehab {
5890c0d06caSMauro Carvalho Chehab 	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
5900c0d06caSMauro Carvalho Chehab 	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
5910c0d06caSMauro Carvalho Chehab 	if (stat != 0) {
5920c0d06caSMauro Carvalho Chehab 		return stat;
5930c0d06caSMauro Carvalho Chehab 	}
5940c0d06caSMauro Carvalho Chehab 	*val = cap->defrect.width;
5950c0d06caSMauro Carvalho Chehab 	return 0;
5960c0d06caSMauro Carvalho Chehab }
5970c0d06caSMauro Carvalho Chehab 
ctrl_get_cropcapdh(struct pvr2_ctrl * cptr,int * val)5980c0d06caSMauro Carvalho Chehab static int ctrl_get_cropcapdh(struct pvr2_ctrl *cptr, int *val)
5990c0d06caSMauro Carvalho Chehab {
6000c0d06caSMauro Carvalho Chehab 	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
6010c0d06caSMauro Carvalho Chehab 	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
6020c0d06caSMauro Carvalho Chehab 	if (stat != 0) {
6030c0d06caSMauro Carvalho Chehab 		return stat;
6040c0d06caSMauro Carvalho Chehab 	}
6050c0d06caSMauro Carvalho Chehab 	*val = cap->defrect.height;
6060c0d06caSMauro Carvalho Chehab 	return 0;
6070c0d06caSMauro Carvalho Chehab }
6080c0d06caSMauro Carvalho Chehab 
ctrl_get_cropcappan(struct pvr2_ctrl * cptr,int * val)6090c0d06caSMauro Carvalho Chehab static int ctrl_get_cropcappan(struct pvr2_ctrl *cptr, int *val)
6100c0d06caSMauro Carvalho Chehab {
6110c0d06caSMauro Carvalho Chehab 	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
6120c0d06caSMauro Carvalho Chehab 	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
6130c0d06caSMauro Carvalho Chehab 	if (stat != 0) {
6140c0d06caSMauro Carvalho Chehab 		return stat;
6150c0d06caSMauro Carvalho Chehab 	}
6160c0d06caSMauro Carvalho Chehab 	*val = cap->pixelaspect.numerator;
6170c0d06caSMauro Carvalho Chehab 	return 0;
6180c0d06caSMauro Carvalho Chehab }
6190c0d06caSMauro Carvalho Chehab 
ctrl_get_cropcappad(struct pvr2_ctrl * cptr,int * val)6200c0d06caSMauro Carvalho Chehab static int ctrl_get_cropcappad(struct pvr2_ctrl *cptr, int *val)
6210c0d06caSMauro Carvalho Chehab {
6220c0d06caSMauro Carvalho Chehab 	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
6230c0d06caSMauro Carvalho Chehab 	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
6240c0d06caSMauro Carvalho Chehab 	if (stat != 0) {
6250c0d06caSMauro Carvalho Chehab 		return stat;
6260c0d06caSMauro Carvalho Chehab 	}
6270c0d06caSMauro Carvalho Chehab 	*val = cap->pixelaspect.denominator;
6280c0d06caSMauro Carvalho Chehab 	return 0;
6290c0d06caSMauro Carvalho Chehab }
6300c0d06caSMauro Carvalho Chehab 
ctrl_vres_max_get(struct pvr2_ctrl * cptr,int * vp)6310c0d06caSMauro Carvalho Chehab static int ctrl_vres_max_get(struct pvr2_ctrl *cptr,int *vp)
6320c0d06caSMauro Carvalho Chehab {
6330c0d06caSMauro Carvalho Chehab 	/* Actual maximum depends on the video standard in effect. */
6340c0d06caSMauro Carvalho Chehab 	if (cptr->hdw->std_mask_cur & V4L2_STD_525_60) {
6350c0d06caSMauro Carvalho Chehab 		*vp = 480;
6360c0d06caSMauro Carvalho Chehab 	} else {
6370c0d06caSMauro Carvalho Chehab 		*vp = 576;
6380c0d06caSMauro Carvalho Chehab 	}
6390c0d06caSMauro Carvalho Chehab 	return 0;
6400c0d06caSMauro Carvalho Chehab }
6410c0d06caSMauro Carvalho Chehab 
ctrl_vres_min_get(struct pvr2_ctrl * cptr,int * vp)6420c0d06caSMauro Carvalho Chehab static int ctrl_vres_min_get(struct pvr2_ctrl *cptr,int *vp)
6430c0d06caSMauro Carvalho Chehab {
6440c0d06caSMauro Carvalho Chehab 	/* Actual minimum depends on device digitizer type. */
6450c0d06caSMauro Carvalho Chehab 	if (cptr->hdw->hdw_desc->flag_has_cx25840) {
6460c0d06caSMauro Carvalho Chehab 		*vp = 75;
6470c0d06caSMauro Carvalho Chehab 	} else {
6480c0d06caSMauro Carvalho Chehab 		*vp = 17;
6490c0d06caSMauro Carvalho Chehab 	}
6500c0d06caSMauro Carvalho Chehab 	return 0;
6510c0d06caSMauro Carvalho Chehab }
6520c0d06caSMauro Carvalho Chehab 
ctrl_get_input(struct pvr2_ctrl * cptr,int * vp)6530c0d06caSMauro Carvalho Chehab static int ctrl_get_input(struct pvr2_ctrl *cptr,int *vp)
6540c0d06caSMauro Carvalho Chehab {
6550c0d06caSMauro Carvalho Chehab 	*vp = cptr->hdw->input_val;
6560c0d06caSMauro Carvalho Chehab 	return 0;
6570c0d06caSMauro Carvalho Chehab }
6580c0d06caSMauro Carvalho Chehab 
ctrl_check_input(struct pvr2_ctrl * cptr,int v)6590c0d06caSMauro Carvalho Chehab static int ctrl_check_input(struct pvr2_ctrl *cptr,int v)
6600c0d06caSMauro Carvalho Chehab {
661c1ced46cSDan Carpenter 	if (v < 0 || v > PVR2_CVAL_INPUT_MAX)
662c1ced46cSDan Carpenter 		return 0;
66395c52069SMauro Carvalho Chehab 	return ((1UL << v) & cptr->hdw->input_allowed_mask) != 0;
6640c0d06caSMauro Carvalho Chehab }
6650c0d06caSMauro Carvalho Chehab 
ctrl_set_input(struct pvr2_ctrl * cptr,int m,int v)6660c0d06caSMauro Carvalho Chehab static int ctrl_set_input(struct pvr2_ctrl *cptr,int m,int v)
6670c0d06caSMauro Carvalho Chehab {
6680c0d06caSMauro Carvalho Chehab 	return pvr2_hdw_set_input(cptr->hdw,v);
6690c0d06caSMauro Carvalho Chehab }
6700c0d06caSMauro Carvalho Chehab 
ctrl_isdirty_input(struct pvr2_ctrl * cptr)6710c0d06caSMauro Carvalho Chehab static int ctrl_isdirty_input(struct pvr2_ctrl *cptr)
6720c0d06caSMauro Carvalho Chehab {
6730c0d06caSMauro Carvalho Chehab 	return cptr->hdw->input_dirty != 0;
6740c0d06caSMauro Carvalho Chehab }
6750c0d06caSMauro Carvalho Chehab 
ctrl_cleardirty_input(struct pvr2_ctrl * cptr)6760c0d06caSMauro Carvalho Chehab static void ctrl_cleardirty_input(struct pvr2_ctrl *cptr)
6770c0d06caSMauro Carvalho Chehab {
6780c0d06caSMauro Carvalho Chehab 	cptr->hdw->input_dirty = 0;
6790c0d06caSMauro Carvalho Chehab }
6800c0d06caSMauro Carvalho Chehab 
6810c0d06caSMauro Carvalho Chehab 
ctrl_freq_max_get(struct pvr2_ctrl * cptr,int * vp)6820c0d06caSMauro Carvalho Chehab static int ctrl_freq_max_get(struct pvr2_ctrl *cptr, int *vp)
6830c0d06caSMauro Carvalho Chehab {
6840c0d06caSMauro Carvalho Chehab 	unsigned long fv;
6850c0d06caSMauro Carvalho Chehab 	struct pvr2_hdw *hdw = cptr->hdw;
6860c0d06caSMauro Carvalho Chehab 	if (hdw->tuner_signal_stale) {
6870c0d06caSMauro Carvalho Chehab 		pvr2_hdw_status_poll(hdw);
6880c0d06caSMauro Carvalho Chehab 	}
6890c0d06caSMauro Carvalho Chehab 	fv = hdw->tuner_signal_info.rangehigh;
6900c0d06caSMauro Carvalho Chehab 	if (!fv) {
6910c0d06caSMauro Carvalho Chehab 		/* Safety fallback */
6920c0d06caSMauro Carvalho Chehab 		*vp = TV_MAX_FREQ;
6930c0d06caSMauro Carvalho Chehab 		return 0;
6940c0d06caSMauro Carvalho Chehab 	}
6950c0d06caSMauro Carvalho Chehab 	if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) {
6960c0d06caSMauro Carvalho Chehab 		fv = (fv * 125) / 2;
6970c0d06caSMauro Carvalho Chehab 	} else {
6980c0d06caSMauro Carvalho Chehab 		fv = fv * 62500;
6990c0d06caSMauro Carvalho Chehab 	}
7000c0d06caSMauro Carvalho Chehab 	*vp = fv;
7010c0d06caSMauro Carvalho Chehab 	return 0;
7020c0d06caSMauro Carvalho Chehab }
7030c0d06caSMauro Carvalho Chehab 
ctrl_freq_min_get(struct pvr2_ctrl * cptr,int * vp)7040c0d06caSMauro Carvalho Chehab static int ctrl_freq_min_get(struct pvr2_ctrl *cptr, int *vp)
7050c0d06caSMauro Carvalho Chehab {
7060c0d06caSMauro Carvalho Chehab 	unsigned long fv;
7070c0d06caSMauro Carvalho Chehab 	struct pvr2_hdw *hdw = cptr->hdw;
7080c0d06caSMauro Carvalho Chehab 	if (hdw->tuner_signal_stale) {
7090c0d06caSMauro Carvalho Chehab 		pvr2_hdw_status_poll(hdw);
7100c0d06caSMauro Carvalho Chehab 	}
7110c0d06caSMauro Carvalho Chehab 	fv = hdw->tuner_signal_info.rangelow;
7120c0d06caSMauro Carvalho Chehab 	if (!fv) {
7130c0d06caSMauro Carvalho Chehab 		/* Safety fallback */
7140c0d06caSMauro Carvalho Chehab 		*vp = TV_MIN_FREQ;
7150c0d06caSMauro Carvalho Chehab 		return 0;
7160c0d06caSMauro Carvalho Chehab 	}
7170c0d06caSMauro Carvalho Chehab 	if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) {
7180c0d06caSMauro Carvalho Chehab 		fv = (fv * 125) / 2;
7190c0d06caSMauro Carvalho Chehab 	} else {
7200c0d06caSMauro Carvalho Chehab 		fv = fv * 62500;
7210c0d06caSMauro Carvalho Chehab 	}
7220c0d06caSMauro Carvalho Chehab 	*vp = fv;
7230c0d06caSMauro Carvalho Chehab 	return 0;
7240c0d06caSMauro Carvalho Chehab }
7250c0d06caSMauro Carvalho Chehab 
ctrl_cx2341x_is_dirty(struct pvr2_ctrl * cptr)7260c0d06caSMauro Carvalho Chehab static int ctrl_cx2341x_is_dirty(struct pvr2_ctrl *cptr)
7270c0d06caSMauro Carvalho Chehab {
7280c0d06caSMauro Carvalho Chehab 	return cptr->hdw->enc_stale != 0;
7290c0d06caSMauro Carvalho Chehab }
7300c0d06caSMauro Carvalho Chehab 
ctrl_cx2341x_clear_dirty(struct pvr2_ctrl * cptr)7310c0d06caSMauro Carvalho Chehab static void ctrl_cx2341x_clear_dirty(struct pvr2_ctrl *cptr)
7320c0d06caSMauro Carvalho Chehab {
7330c0d06caSMauro Carvalho Chehab 	cptr->hdw->enc_stale = 0;
7340c0d06caSMauro Carvalho Chehab 	cptr->hdw->enc_unsafe_stale = 0;
7350c0d06caSMauro Carvalho Chehab }
7360c0d06caSMauro Carvalho Chehab 
ctrl_cx2341x_get(struct pvr2_ctrl * cptr,int * vp)7370c0d06caSMauro Carvalho Chehab static int ctrl_cx2341x_get(struct pvr2_ctrl *cptr,int *vp)
7380c0d06caSMauro Carvalho Chehab {
7390c0d06caSMauro Carvalho Chehab 	int ret;
7400c0d06caSMauro Carvalho Chehab 	struct v4l2_ext_controls cs;
7410c0d06caSMauro Carvalho Chehab 	struct v4l2_ext_control c1;
7420c0d06caSMauro Carvalho Chehab 	memset(&cs,0,sizeof(cs));
7430c0d06caSMauro Carvalho Chehab 	memset(&c1,0,sizeof(c1));
7440c0d06caSMauro Carvalho Chehab 	cs.controls = &c1;
7450c0d06caSMauro Carvalho Chehab 	cs.count = 1;
7460c0d06caSMauro Carvalho Chehab 	c1.id = cptr->info->v4l_id;
7470c0d06caSMauro Carvalho Chehab 	ret = cx2341x_ext_ctrls(&cptr->hdw->enc_ctl_state, 0, &cs,
7480c0d06caSMauro Carvalho Chehab 				VIDIOC_G_EXT_CTRLS);
7490c0d06caSMauro Carvalho Chehab 	if (ret) return ret;
7500c0d06caSMauro Carvalho Chehab 	*vp = c1.value;
7510c0d06caSMauro Carvalho Chehab 	return 0;
7520c0d06caSMauro Carvalho Chehab }
7530c0d06caSMauro Carvalho Chehab 
ctrl_cx2341x_set(struct pvr2_ctrl * cptr,int m,int v)7540c0d06caSMauro Carvalho Chehab static int ctrl_cx2341x_set(struct pvr2_ctrl *cptr,int m,int v)
7550c0d06caSMauro Carvalho Chehab {
7560c0d06caSMauro Carvalho Chehab 	int ret;
7570c0d06caSMauro Carvalho Chehab 	struct pvr2_hdw *hdw = cptr->hdw;
7580c0d06caSMauro Carvalho Chehab 	struct v4l2_ext_controls cs;
7590c0d06caSMauro Carvalho Chehab 	struct v4l2_ext_control c1;
7600c0d06caSMauro Carvalho Chehab 	memset(&cs,0,sizeof(cs));
7610c0d06caSMauro Carvalho Chehab 	memset(&c1,0,sizeof(c1));
7620c0d06caSMauro Carvalho Chehab 	cs.controls = &c1;
7630c0d06caSMauro Carvalho Chehab 	cs.count = 1;
7640c0d06caSMauro Carvalho Chehab 	c1.id = cptr->info->v4l_id;
7650c0d06caSMauro Carvalho Chehab 	c1.value = v;
7660c0d06caSMauro Carvalho Chehab 	ret = cx2341x_ext_ctrls(&hdw->enc_ctl_state,
7670c0d06caSMauro Carvalho Chehab 				hdw->state_encoder_run, &cs,
7680c0d06caSMauro Carvalho Chehab 				VIDIOC_S_EXT_CTRLS);
7690c0d06caSMauro Carvalho Chehab 	if (ret == -EBUSY) {
7700c0d06caSMauro Carvalho Chehab 		/* Oops.  cx2341x is telling us it's not safe to change
7710c0d06caSMauro Carvalho Chehab 		   this control while we're capturing.  Make a note of this
7720c0d06caSMauro Carvalho Chehab 		   fact so that the pipeline will be stopped the next time
7730c0d06caSMauro Carvalho Chehab 		   controls are committed.  Then go on ahead and store this
7740c0d06caSMauro Carvalho Chehab 		   change anyway. */
7750c0d06caSMauro Carvalho Chehab 		ret = cx2341x_ext_ctrls(&hdw->enc_ctl_state,
7760c0d06caSMauro Carvalho Chehab 					0, &cs,
7770c0d06caSMauro Carvalho Chehab 					VIDIOC_S_EXT_CTRLS);
7780c0d06caSMauro Carvalho Chehab 		if (!ret) hdw->enc_unsafe_stale = !0;
7790c0d06caSMauro Carvalho Chehab 	}
7800c0d06caSMauro Carvalho Chehab 	if (ret) return ret;
7810c0d06caSMauro Carvalho Chehab 	hdw->enc_stale = !0;
7820c0d06caSMauro Carvalho Chehab 	return 0;
7830c0d06caSMauro Carvalho Chehab }
7840c0d06caSMauro Carvalho Chehab 
ctrl_cx2341x_getv4lflags(struct pvr2_ctrl * cptr)7850c0d06caSMauro Carvalho Chehab static unsigned int ctrl_cx2341x_getv4lflags(struct pvr2_ctrl *cptr)
7860c0d06caSMauro Carvalho Chehab {
7870fb68ce0SYizhuo 	struct v4l2_queryctrl qctrl = {};
7880c0d06caSMauro Carvalho Chehab 	struct pvr2_ctl_info *info;
7890c0d06caSMauro Carvalho Chehab 	qctrl.id = cptr->info->v4l_id;
7900c0d06caSMauro Carvalho Chehab 	cx2341x_ctrl_query(&cptr->hdw->enc_ctl_state,&qctrl);
7910c0d06caSMauro Carvalho Chehab 	/* Strip out the const so we can adjust a function pointer.  It's
7920c0d06caSMauro Carvalho Chehab 	   OK to do this here because we know this is a dynamically created
7930c0d06caSMauro Carvalho Chehab 	   control, so the underlying storage for the info pointer is (a)
7940c0d06caSMauro Carvalho Chehab 	   private to us, and (b) not in read-only storage.  Either we do
7950c0d06caSMauro Carvalho Chehab 	   this or we significantly complicate the underlying control
7960c0d06caSMauro Carvalho Chehab 	   implementation. */
7970c0d06caSMauro Carvalho Chehab 	info = (struct pvr2_ctl_info *)(cptr->info);
7980c0d06caSMauro Carvalho Chehab 	if (qctrl.flags & V4L2_CTRL_FLAG_READ_ONLY) {
7990c0d06caSMauro Carvalho Chehab 		if (info->set_value) {
8000c0d06caSMauro Carvalho Chehab 			info->set_value = NULL;
8010c0d06caSMauro Carvalho Chehab 		}
8020c0d06caSMauro Carvalho Chehab 	} else {
8030c0d06caSMauro Carvalho Chehab 		if (!(info->set_value)) {
8040c0d06caSMauro Carvalho Chehab 			info->set_value = ctrl_cx2341x_set;
8050c0d06caSMauro Carvalho Chehab 		}
8060c0d06caSMauro Carvalho Chehab 	}
8070c0d06caSMauro Carvalho Chehab 	return qctrl.flags;
8080c0d06caSMauro Carvalho Chehab }
8090c0d06caSMauro Carvalho Chehab 
ctrl_streamingenabled_get(struct pvr2_ctrl * cptr,int * vp)8100c0d06caSMauro Carvalho Chehab static int ctrl_streamingenabled_get(struct pvr2_ctrl *cptr,int *vp)
8110c0d06caSMauro Carvalho Chehab {
8120c0d06caSMauro Carvalho Chehab 	*vp = cptr->hdw->state_pipeline_req;
8130c0d06caSMauro Carvalho Chehab 	return 0;
8140c0d06caSMauro Carvalho Chehab }
8150c0d06caSMauro Carvalho Chehab 
ctrl_masterstate_get(struct pvr2_ctrl * cptr,int * vp)8160c0d06caSMauro Carvalho Chehab static int ctrl_masterstate_get(struct pvr2_ctrl *cptr,int *vp)
8170c0d06caSMauro Carvalho Chehab {
8180c0d06caSMauro Carvalho Chehab 	*vp = cptr->hdw->master_state;
8190c0d06caSMauro Carvalho Chehab 	return 0;
8200c0d06caSMauro Carvalho Chehab }
8210c0d06caSMauro Carvalho Chehab 
ctrl_hsm_get(struct pvr2_ctrl * cptr,int * vp)8220c0d06caSMauro Carvalho Chehab static int ctrl_hsm_get(struct pvr2_ctrl *cptr,int *vp)
8230c0d06caSMauro Carvalho Chehab {
8240c0d06caSMauro Carvalho Chehab 	int result = pvr2_hdw_is_hsm(cptr->hdw);
8250c0d06caSMauro Carvalho Chehab 	*vp = PVR2_CVAL_HSM_FULL;
8260c0d06caSMauro Carvalho Chehab 	if (result < 0) *vp = PVR2_CVAL_HSM_FAIL;
8270c0d06caSMauro Carvalho Chehab 	if (result) *vp = PVR2_CVAL_HSM_HIGH;
8280c0d06caSMauro Carvalho Chehab 	return 0;
8290c0d06caSMauro Carvalho Chehab }
8300c0d06caSMauro Carvalho Chehab 
ctrl_stddetect_get(struct pvr2_ctrl * cptr,int * vp)8310c0d06caSMauro Carvalho Chehab static int ctrl_stddetect_get(struct pvr2_ctrl *cptr, int *vp)
8320c0d06caSMauro Carvalho Chehab {
8330c0d06caSMauro Carvalho Chehab 	*vp = pvr2_hdw_get_detected_std(cptr->hdw);
8340c0d06caSMauro Carvalho Chehab 	return 0;
8350c0d06caSMauro Carvalho Chehab }
8360c0d06caSMauro Carvalho Chehab 
ctrl_stdavail_get(struct pvr2_ctrl * cptr,int * vp)8370c0d06caSMauro Carvalho Chehab static int ctrl_stdavail_get(struct pvr2_ctrl *cptr,int *vp)
8380c0d06caSMauro Carvalho Chehab {
8390c0d06caSMauro Carvalho Chehab 	*vp = cptr->hdw->std_mask_avail;
8400c0d06caSMauro Carvalho Chehab 	return 0;
8410c0d06caSMauro Carvalho Chehab }
8420c0d06caSMauro Carvalho Chehab 
ctrl_stdavail_set(struct pvr2_ctrl * cptr,int m,int v)8430c0d06caSMauro Carvalho Chehab static int ctrl_stdavail_set(struct pvr2_ctrl *cptr,int m,int v)
8440c0d06caSMauro Carvalho Chehab {
8450c0d06caSMauro Carvalho Chehab 	struct pvr2_hdw *hdw = cptr->hdw;
8460c0d06caSMauro Carvalho Chehab 	v4l2_std_id ns;
8470c0d06caSMauro Carvalho Chehab 	ns = hdw->std_mask_avail;
8480c0d06caSMauro Carvalho Chehab 	ns = (ns & ~m) | (v & m);
8490c0d06caSMauro Carvalho Chehab 	if (ns == hdw->std_mask_avail) return 0;
8500c0d06caSMauro Carvalho Chehab 	hdw->std_mask_avail = ns;
8510c0d06caSMauro Carvalho Chehab 	hdw->std_info_cur.def.type_bitmask.valid_bits = hdw->std_mask_avail;
8520c0d06caSMauro Carvalho Chehab 	return 0;
8530c0d06caSMauro Carvalho Chehab }
8540c0d06caSMauro Carvalho Chehab 
ctrl_std_val_to_sym(struct pvr2_ctrl * cptr,int msk,int val,char * bufPtr,unsigned int bufSize,unsigned int * len)8550c0d06caSMauro Carvalho Chehab static int ctrl_std_val_to_sym(struct pvr2_ctrl *cptr,int msk,int val,
8560c0d06caSMauro Carvalho Chehab 			       char *bufPtr,unsigned int bufSize,
8570c0d06caSMauro Carvalho Chehab 			       unsigned int *len)
8580c0d06caSMauro Carvalho Chehab {
8590c0d06caSMauro Carvalho Chehab 	*len = pvr2_std_id_to_str(bufPtr,bufSize,msk & val);
8600c0d06caSMauro Carvalho Chehab 	return 0;
8610c0d06caSMauro Carvalho Chehab }
8620c0d06caSMauro Carvalho Chehab 
ctrl_std_sym_to_val(struct pvr2_ctrl * cptr,const char * bufPtr,unsigned int bufSize,int * mskp,int * valp)8630c0d06caSMauro Carvalho Chehab static int ctrl_std_sym_to_val(struct pvr2_ctrl *cptr,
8640c0d06caSMauro Carvalho Chehab 			       const char *bufPtr,unsigned int bufSize,
8650c0d06caSMauro Carvalho Chehab 			       int *mskp,int *valp)
8660c0d06caSMauro Carvalho Chehab {
8670c0d06caSMauro Carvalho Chehab 	v4l2_std_id id;
868ab0e9fa1STong Zhang 	if (!pvr2_std_str_to_id(&id, bufPtr, bufSize))
869ab0e9fa1STong Zhang 		return -EINVAL;
8700c0d06caSMauro Carvalho Chehab 	if (mskp) *mskp = id;
8710c0d06caSMauro Carvalho Chehab 	if (valp) *valp = id;
8720c0d06caSMauro Carvalho Chehab 	return 0;
8730c0d06caSMauro Carvalho Chehab }
8740c0d06caSMauro Carvalho Chehab 
ctrl_stdcur_get(struct pvr2_ctrl * cptr,int * vp)8750c0d06caSMauro Carvalho Chehab static int ctrl_stdcur_get(struct pvr2_ctrl *cptr,int *vp)
8760c0d06caSMauro Carvalho Chehab {
8770c0d06caSMauro Carvalho Chehab 	*vp = cptr->hdw->std_mask_cur;
8780c0d06caSMauro Carvalho Chehab 	return 0;
8790c0d06caSMauro Carvalho Chehab }
8800c0d06caSMauro Carvalho Chehab 
ctrl_stdcur_set(struct pvr2_ctrl * cptr,int m,int v)8810c0d06caSMauro Carvalho Chehab static int ctrl_stdcur_set(struct pvr2_ctrl *cptr,int m,int v)
8820c0d06caSMauro Carvalho Chehab {
8830c0d06caSMauro Carvalho Chehab 	struct pvr2_hdw *hdw = cptr->hdw;
8840c0d06caSMauro Carvalho Chehab 	v4l2_std_id ns;
8850c0d06caSMauro Carvalho Chehab 	ns = hdw->std_mask_cur;
8860c0d06caSMauro Carvalho Chehab 	ns = (ns & ~m) | (v & m);
8870c0d06caSMauro Carvalho Chehab 	if (ns == hdw->std_mask_cur) return 0;
8880c0d06caSMauro Carvalho Chehab 	hdw->std_mask_cur = ns;
8890c0d06caSMauro Carvalho Chehab 	hdw->std_dirty = !0;
8900c0d06caSMauro Carvalho Chehab 	return 0;
8910c0d06caSMauro Carvalho Chehab }
8920c0d06caSMauro Carvalho Chehab 
ctrl_stdcur_is_dirty(struct pvr2_ctrl * cptr)8930c0d06caSMauro Carvalho Chehab static int ctrl_stdcur_is_dirty(struct pvr2_ctrl *cptr)
8940c0d06caSMauro Carvalho Chehab {
8950c0d06caSMauro Carvalho Chehab 	return cptr->hdw->std_dirty != 0;
8960c0d06caSMauro Carvalho Chehab }
8970c0d06caSMauro Carvalho Chehab 
ctrl_stdcur_clear_dirty(struct pvr2_ctrl * cptr)8980c0d06caSMauro Carvalho Chehab static void ctrl_stdcur_clear_dirty(struct pvr2_ctrl *cptr)
8990c0d06caSMauro Carvalho Chehab {
9000c0d06caSMauro Carvalho Chehab 	cptr->hdw->std_dirty = 0;
9010c0d06caSMauro Carvalho Chehab }
9020c0d06caSMauro Carvalho Chehab 
ctrl_signal_get(struct pvr2_ctrl * cptr,int * vp)9030c0d06caSMauro Carvalho Chehab static int ctrl_signal_get(struct pvr2_ctrl *cptr,int *vp)
9040c0d06caSMauro Carvalho Chehab {
9050c0d06caSMauro Carvalho Chehab 	struct pvr2_hdw *hdw = cptr->hdw;
9060c0d06caSMauro Carvalho Chehab 	pvr2_hdw_status_poll(hdw);
9070c0d06caSMauro Carvalho Chehab 	*vp = hdw->tuner_signal_info.signal;
9080c0d06caSMauro Carvalho Chehab 	return 0;
9090c0d06caSMauro Carvalho Chehab }
9100c0d06caSMauro Carvalho Chehab 
ctrl_audio_modes_present_get(struct pvr2_ctrl * cptr,int * vp)9110c0d06caSMauro Carvalho Chehab static int ctrl_audio_modes_present_get(struct pvr2_ctrl *cptr,int *vp)
9120c0d06caSMauro Carvalho Chehab {
9130c0d06caSMauro Carvalho Chehab 	int val = 0;
9140c0d06caSMauro Carvalho Chehab 	unsigned int subchan;
9150c0d06caSMauro Carvalho Chehab 	struct pvr2_hdw *hdw = cptr->hdw;
9160c0d06caSMauro Carvalho Chehab 	pvr2_hdw_status_poll(hdw);
9170c0d06caSMauro Carvalho Chehab 	subchan = hdw->tuner_signal_info.rxsubchans;
9180c0d06caSMauro Carvalho Chehab 	if (subchan & V4L2_TUNER_SUB_MONO) {
9190c0d06caSMauro Carvalho Chehab 		val |= (1 << V4L2_TUNER_MODE_MONO);
9200c0d06caSMauro Carvalho Chehab 	}
9210c0d06caSMauro Carvalho Chehab 	if (subchan & V4L2_TUNER_SUB_STEREO) {
9220c0d06caSMauro Carvalho Chehab 		val |= (1 << V4L2_TUNER_MODE_STEREO);
9230c0d06caSMauro Carvalho Chehab 	}
9240c0d06caSMauro Carvalho Chehab 	if (subchan & V4L2_TUNER_SUB_LANG1) {
9250c0d06caSMauro Carvalho Chehab 		val |= (1 << V4L2_TUNER_MODE_LANG1);
9260c0d06caSMauro Carvalho Chehab 	}
9270c0d06caSMauro Carvalho Chehab 	if (subchan & V4L2_TUNER_SUB_LANG2) {
9280c0d06caSMauro Carvalho Chehab 		val |= (1 << V4L2_TUNER_MODE_LANG2);
9290c0d06caSMauro Carvalho Chehab 	}
9300c0d06caSMauro Carvalho Chehab 	*vp = val;
9310c0d06caSMauro Carvalho Chehab 	return 0;
9320c0d06caSMauro Carvalho Chehab }
9330c0d06caSMauro Carvalho Chehab 
9340c0d06caSMauro Carvalho Chehab 
9350c0d06caSMauro Carvalho Chehab #define DEFINT(vmin,vmax) \
9360c0d06caSMauro Carvalho Chehab 	.type = pvr2_ctl_int, \
9370c0d06caSMauro Carvalho Chehab 	.def.type_int.min_value = vmin, \
9380c0d06caSMauro Carvalho Chehab 	.def.type_int.max_value = vmax
9390c0d06caSMauro Carvalho Chehab 
9400c0d06caSMauro Carvalho Chehab #define DEFENUM(tab) \
9410c0d06caSMauro Carvalho Chehab 	.type = pvr2_ctl_enum, \
9420c0d06caSMauro Carvalho Chehab 	.def.type_enum.count = ARRAY_SIZE(tab), \
9430c0d06caSMauro Carvalho Chehab 	.def.type_enum.value_names = tab
9440c0d06caSMauro Carvalho Chehab 
9450c0d06caSMauro Carvalho Chehab #define DEFBOOL \
9460c0d06caSMauro Carvalho Chehab 	.type = pvr2_ctl_bool
9470c0d06caSMauro Carvalho Chehab 
9480c0d06caSMauro Carvalho Chehab #define DEFMASK(msk,tab) \
9490c0d06caSMauro Carvalho Chehab 	.type = pvr2_ctl_bitmask, \
9500c0d06caSMauro Carvalho Chehab 	.def.type_bitmask.valid_bits = msk, \
9510c0d06caSMauro Carvalho Chehab 	.def.type_bitmask.bit_names = tab
9520c0d06caSMauro Carvalho Chehab 
9530c0d06caSMauro Carvalho Chehab #define DEFREF(vname) \
9540c0d06caSMauro Carvalho Chehab 	.set_value = ctrl_set_##vname, \
9550c0d06caSMauro Carvalho Chehab 	.get_value = ctrl_get_##vname, \
9560c0d06caSMauro Carvalho Chehab 	.is_dirty = ctrl_isdirty_##vname, \
9570c0d06caSMauro Carvalho Chehab 	.clear_dirty = ctrl_cleardirty_##vname
9580c0d06caSMauro Carvalho Chehab 
9590c0d06caSMauro Carvalho Chehab 
9600c0d06caSMauro Carvalho Chehab #define VCREATE_FUNCS(vname) \
9610c0d06caSMauro Carvalho Chehab static int ctrl_get_##vname(struct pvr2_ctrl *cptr,int *vp) \
9620c0d06caSMauro Carvalho Chehab {*vp = cptr->hdw->vname##_val; return 0;} \
9630c0d06caSMauro Carvalho Chehab static int ctrl_set_##vname(struct pvr2_ctrl *cptr,int m,int v) \
9640c0d06caSMauro Carvalho Chehab {cptr->hdw->vname##_val = v; cptr->hdw->vname##_dirty = !0; return 0;} \
9650c0d06caSMauro Carvalho Chehab static int ctrl_isdirty_##vname(struct pvr2_ctrl *cptr) \
9660c0d06caSMauro Carvalho Chehab {return cptr->hdw->vname##_dirty != 0;} \
9670c0d06caSMauro Carvalho Chehab static void ctrl_cleardirty_##vname(struct pvr2_ctrl *cptr) \
9680c0d06caSMauro Carvalho Chehab {cptr->hdw->vname##_dirty = 0;}
9690c0d06caSMauro Carvalho Chehab 
9700c0d06caSMauro Carvalho Chehab VCREATE_FUNCS(brightness)
9710c0d06caSMauro Carvalho Chehab VCREATE_FUNCS(contrast)
9720c0d06caSMauro Carvalho Chehab VCREATE_FUNCS(saturation)
9730c0d06caSMauro Carvalho Chehab VCREATE_FUNCS(hue)
9740c0d06caSMauro Carvalho Chehab VCREATE_FUNCS(volume)
9750c0d06caSMauro Carvalho Chehab VCREATE_FUNCS(balance)
9760c0d06caSMauro Carvalho Chehab VCREATE_FUNCS(bass)
9770c0d06caSMauro Carvalho Chehab VCREATE_FUNCS(treble)
9780c0d06caSMauro Carvalho Chehab VCREATE_FUNCS(mute)
9790c0d06caSMauro Carvalho Chehab VCREATE_FUNCS(cropl)
9800c0d06caSMauro Carvalho Chehab VCREATE_FUNCS(cropt)
9810c0d06caSMauro Carvalho Chehab VCREATE_FUNCS(cropw)
9820c0d06caSMauro Carvalho Chehab VCREATE_FUNCS(croph)
9830c0d06caSMauro Carvalho Chehab VCREATE_FUNCS(audiomode)
9840c0d06caSMauro Carvalho Chehab VCREATE_FUNCS(res_hor)
9850c0d06caSMauro Carvalho Chehab VCREATE_FUNCS(res_ver)
9860c0d06caSMauro Carvalho Chehab VCREATE_FUNCS(srate)
9870c0d06caSMauro Carvalho Chehab 
9880c0d06caSMauro Carvalho Chehab /* Table definition of all controls which can be manipulated */
9890c0d06caSMauro Carvalho Chehab static const struct pvr2_ctl_info control_defs[] = {
9900c0d06caSMauro Carvalho Chehab 	{
9910c0d06caSMauro Carvalho Chehab 		.v4l_id = V4L2_CID_BRIGHTNESS,
9920c0d06caSMauro Carvalho Chehab 		.desc = "Brightness",
9930c0d06caSMauro Carvalho Chehab 		.name = "brightness",
9940c0d06caSMauro Carvalho Chehab 		.default_value = 128,
9950c0d06caSMauro Carvalho Chehab 		DEFREF(brightness),
9960c0d06caSMauro Carvalho Chehab 		DEFINT(0,255),
9970c0d06caSMauro Carvalho Chehab 	},{
9980c0d06caSMauro Carvalho Chehab 		.v4l_id = V4L2_CID_CONTRAST,
9990c0d06caSMauro Carvalho Chehab 		.desc = "Contrast",
10000c0d06caSMauro Carvalho Chehab 		.name = "contrast",
10010c0d06caSMauro Carvalho Chehab 		.default_value = 68,
10020c0d06caSMauro Carvalho Chehab 		DEFREF(contrast),
10030c0d06caSMauro Carvalho Chehab 		DEFINT(0,127),
10040c0d06caSMauro Carvalho Chehab 	},{
10050c0d06caSMauro Carvalho Chehab 		.v4l_id = V4L2_CID_SATURATION,
10060c0d06caSMauro Carvalho Chehab 		.desc = "Saturation",
10070c0d06caSMauro Carvalho Chehab 		.name = "saturation",
10080c0d06caSMauro Carvalho Chehab 		.default_value = 64,
10090c0d06caSMauro Carvalho Chehab 		DEFREF(saturation),
10100c0d06caSMauro Carvalho Chehab 		DEFINT(0,127),
10110c0d06caSMauro Carvalho Chehab 	},{
10120c0d06caSMauro Carvalho Chehab 		.v4l_id = V4L2_CID_HUE,
10130c0d06caSMauro Carvalho Chehab 		.desc = "Hue",
10140c0d06caSMauro Carvalho Chehab 		.name = "hue",
10150c0d06caSMauro Carvalho Chehab 		.default_value = 0,
10160c0d06caSMauro Carvalho Chehab 		DEFREF(hue),
10170c0d06caSMauro Carvalho Chehab 		DEFINT(-128,127),
10180c0d06caSMauro Carvalho Chehab 	},{
10190c0d06caSMauro Carvalho Chehab 		.v4l_id = V4L2_CID_AUDIO_VOLUME,
10200c0d06caSMauro Carvalho Chehab 		.desc = "Volume",
10210c0d06caSMauro Carvalho Chehab 		.name = "volume",
10220c0d06caSMauro Carvalho Chehab 		.default_value = 62000,
10230c0d06caSMauro Carvalho Chehab 		DEFREF(volume),
10240c0d06caSMauro Carvalho Chehab 		DEFINT(0,65535),
10250c0d06caSMauro Carvalho Chehab 	},{
10260c0d06caSMauro Carvalho Chehab 		.v4l_id = V4L2_CID_AUDIO_BALANCE,
10270c0d06caSMauro Carvalho Chehab 		.desc = "Balance",
10280c0d06caSMauro Carvalho Chehab 		.name = "balance",
10290c0d06caSMauro Carvalho Chehab 		.default_value = 0,
10300c0d06caSMauro Carvalho Chehab 		DEFREF(balance),
10310c0d06caSMauro Carvalho Chehab 		DEFINT(-32768,32767),
10320c0d06caSMauro Carvalho Chehab 	},{
10330c0d06caSMauro Carvalho Chehab 		.v4l_id = V4L2_CID_AUDIO_BASS,
10340c0d06caSMauro Carvalho Chehab 		.desc = "Bass",
10350c0d06caSMauro Carvalho Chehab 		.name = "bass",
10360c0d06caSMauro Carvalho Chehab 		.default_value = 0,
10370c0d06caSMauro Carvalho Chehab 		DEFREF(bass),
10380c0d06caSMauro Carvalho Chehab 		DEFINT(-32768,32767),
10390c0d06caSMauro Carvalho Chehab 	},{
10400c0d06caSMauro Carvalho Chehab 		.v4l_id = V4L2_CID_AUDIO_TREBLE,
10410c0d06caSMauro Carvalho Chehab 		.desc = "Treble",
10420c0d06caSMauro Carvalho Chehab 		.name = "treble",
10430c0d06caSMauro Carvalho Chehab 		.default_value = 0,
10440c0d06caSMauro Carvalho Chehab 		DEFREF(treble),
10450c0d06caSMauro Carvalho Chehab 		DEFINT(-32768,32767),
10460c0d06caSMauro Carvalho Chehab 	},{
10470c0d06caSMauro Carvalho Chehab 		.v4l_id = V4L2_CID_AUDIO_MUTE,
10480c0d06caSMauro Carvalho Chehab 		.desc = "Mute",
10490c0d06caSMauro Carvalho Chehab 		.name = "mute",
10500c0d06caSMauro Carvalho Chehab 		.default_value = 0,
10510c0d06caSMauro Carvalho Chehab 		DEFREF(mute),
10520c0d06caSMauro Carvalho Chehab 		DEFBOOL,
10530c0d06caSMauro Carvalho Chehab 	}, {
10540c0d06caSMauro Carvalho Chehab 		.desc = "Capture crop left margin",
10550c0d06caSMauro Carvalho Chehab 		.name = "crop_left",
10560c0d06caSMauro Carvalho Chehab 		.internal_id = PVR2_CID_CROPL,
10570c0d06caSMauro Carvalho Chehab 		.default_value = 0,
10580c0d06caSMauro Carvalho Chehab 		DEFREF(cropl),
10590c0d06caSMauro Carvalho Chehab 		DEFINT(-129, 340),
10600c0d06caSMauro Carvalho Chehab 		.get_min_value = ctrl_cropl_min_get,
10610c0d06caSMauro Carvalho Chehab 		.get_max_value = ctrl_cropl_max_get,
10620c0d06caSMauro Carvalho Chehab 		.get_def_value = ctrl_get_cropcapdl,
10630c0d06caSMauro Carvalho Chehab 	}, {
10640c0d06caSMauro Carvalho Chehab 		.desc = "Capture crop top margin",
10650c0d06caSMauro Carvalho Chehab 		.name = "crop_top",
10660c0d06caSMauro Carvalho Chehab 		.internal_id = PVR2_CID_CROPT,
10670c0d06caSMauro Carvalho Chehab 		.default_value = 0,
10680c0d06caSMauro Carvalho Chehab 		DEFREF(cropt),
10690c0d06caSMauro Carvalho Chehab 		DEFINT(-35, 544),
10700c0d06caSMauro Carvalho Chehab 		.get_min_value = ctrl_cropt_min_get,
10710c0d06caSMauro Carvalho Chehab 		.get_max_value = ctrl_cropt_max_get,
10720c0d06caSMauro Carvalho Chehab 		.get_def_value = ctrl_get_cropcapdt,
10730c0d06caSMauro Carvalho Chehab 	}, {
10740c0d06caSMauro Carvalho Chehab 		.desc = "Capture crop width",
10750c0d06caSMauro Carvalho Chehab 		.name = "crop_width",
10760c0d06caSMauro Carvalho Chehab 		.internal_id = PVR2_CID_CROPW,
10770c0d06caSMauro Carvalho Chehab 		.default_value = 720,
10780c0d06caSMauro Carvalho Chehab 		DEFREF(cropw),
10790c0d06caSMauro Carvalho Chehab 		DEFINT(0, 864),
10800c0d06caSMauro Carvalho Chehab 		.get_max_value = ctrl_cropw_max_get,
10810c0d06caSMauro Carvalho Chehab 		.get_def_value = ctrl_get_cropcapdw,
10820c0d06caSMauro Carvalho Chehab 	}, {
10830c0d06caSMauro Carvalho Chehab 		.desc = "Capture crop height",
10840c0d06caSMauro Carvalho Chehab 		.name = "crop_height",
10850c0d06caSMauro Carvalho Chehab 		.internal_id = PVR2_CID_CROPH,
10860c0d06caSMauro Carvalho Chehab 		.default_value = 480,
10870c0d06caSMauro Carvalho Chehab 		DEFREF(croph),
10880c0d06caSMauro Carvalho Chehab 		DEFINT(0, 576),
10890c0d06caSMauro Carvalho Chehab 		.get_max_value = ctrl_croph_max_get,
10900c0d06caSMauro Carvalho Chehab 		.get_def_value = ctrl_get_cropcapdh,
10910c0d06caSMauro Carvalho Chehab 	}, {
10920c0d06caSMauro Carvalho Chehab 		.desc = "Capture capability pixel aspect numerator",
10930c0d06caSMauro Carvalho Chehab 		.name = "cropcap_pixel_numerator",
10940c0d06caSMauro Carvalho Chehab 		.internal_id = PVR2_CID_CROPCAPPAN,
10950c0d06caSMauro Carvalho Chehab 		.get_value = ctrl_get_cropcappan,
10960c0d06caSMauro Carvalho Chehab 	}, {
10970c0d06caSMauro Carvalho Chehab 		.desc = "Capture capability pixel aspect denominator",
10980c0d06caSMauro Carvalho Chehab 		.name = "cropcap_pixel_denominator",
10990c0d06caSMauro Carvalho Chehab 		.internal_id = PVR2_CID_CROPCAPPAD,
11000c0d06caSMauro Carvalho Chehab 		.get_value = ctrl_get_cropcappad,
11010c0d06caSMauro Carvalho Chehab 	}, {
11020c0d06caSMauro Carvalho Chehab 		.desc = "Capture capability bounds top",
11030c0d06caSMauro Carvalho Chehab 		.name = "cropcap_bounds_top",
11040c0d06caSMauro Carvalho Chehab 		.internal_id = PVR2_CID_CROPCAPBT,
11050c0d06caSMauro Carvalho Chehab 		.get_value = ctrl_get_cropcapbt,
11060c0d06caSMauro Carvalho Chehab 	}, {
11070c0d06caSMauro Carvalho Chehab 		.desc = "Capture capability bounds left",
11080c0d06caSMauro Carvalho Chehab 		.name = "cropcap_bounds_left",
11090c0d06caSMauro Carvalho Chehab 		.internal_id = PVR2_CID_CROPCAPBL,
11100c0d06caSMauro Carvalho Chehab 		.get_value = ctrl_get_cropcapbl,
11110c0d06caSMauro Carvalho Chehab 	}, {
11120c0d06caSMauro Carvalho Chehab 		.desc = "Capture capability bounds width",
11130c0d06caSMauro Carvalho Chehab 		.name = "cropcap_bounds_width",
11140c0d06caSMauro Carvalho Chehab 		.internal_id = PVR2_CID_CROPCAPBW,
11150c0d06caSMauro Carvalho Chehab 		.get_value = ctrl_get_cropcapbw,
11160c0d06caSMauro Carvalho Chehab 	}, {
11170c0d06caSMauro Carvalho Chehab 		.desc = "Capture capability bounds height",
11180c0d06caSMauro Carvalho Chehab 		.name = "cropcap_bounds_height",
11190c0d06caSMauro Carvalho Chehab 		.internal_id = PVR2_CID_CROPCAPBH,
11200c0d06caSMauro Carvalho Chehab 		.get_value = ctrl_get_cropcapbh,
11210c0d06caSMauro Carvalho Chehab 	},{
11220c0d06caSMauro Carvalho Chehab 		.desc = "Video Source",
11230c0d06caSMauro Carvalho Chehab 		.name = "input",
11240c0d06caSMauro Carvalho Chehab 		.internal_id = PVR2_CID_INPUT,
11250c0d06caSMauro Carvalho Chehab 		.default_value = PVR2_CVAL_INPUT_TV,
11260c0d06caSMauro Carvalho Chehab 		.check_value = ctrl_check_input,
11270c0d06caSMauro Carvalho Chehab 		DEFREF(input),
11280c0d06caSMauro Carvalho Chehab 		DEFENUM(control_values_input),
11290c0d06caSMauro Carvalho Chehab 	},{
11300c0d06caSMauro Carvalho Chehab 		.desc = "Audio Mode",
11310c0d06caSMauro Carvalho Chehab 		.name = "audio_mode",
11320c0d06caSMauro Carvalho Chehab 		.internal_id = PVR2_CID_AUDIOMODE,
11330c0d06caSMauro Carvalho Chehab 		.default_value = V4L2_TUNER_MODE_STEREO,
11340c0d06caSMauro Carvalho Chehab 		DEFREF(audiomode),
11350c0d06caSMauro Carvalho Chehab 		DEFENUM(control_values_audiomode),
11360c0d06caSMauro Carvalho Chehab 	},{
11370c0d06caSMauro Carvalho Chehab 		.desc = "Horizontal capture resolution",
11380c0d06caSMauro Carvalho Chehab 		.name = "resolution_hor",
11390c0d06caSMauro Carvalho Chehab 		.internal_id = PVR2_CID_HRES,
11400c0d06caSMauro Carvalho Chehab 		.default_value = 720,
11410c0d06caSMauro Carvalho Chehab 		DEFREF(res_hor),
11420c0d06caSMauro Carvalho Chehab 		DEFINT(19,720),
11430c0d06caSMauro Carvalho Chehab 	},{
11440c0d06caSMauro Carvalho Chehab 		.desc = "Vertical capture resolution",
11450c0d06caSMauro Carvalho Chehab 		.name = "resolution_ver",
11460c0d06caSMauro Carvalho Chehab 		.internal_id = PVR2_CID_VRES,
11470c0d06caSMauro Carvalho Chehab 		.default_value = 480,
11480c0d06caSMauro Carvalho Chehab 		DEFREF(res_ver),
11490c0d06caSMauro Carvalho Chehab 		DEFINT(17,576),
11500c0d06caSMauro Carvalho Chehab 		/* Hook in check for video standard and adjust maximum
11510c0d06caSMauro Carvalho Chehab 		   depending on the standard. */
11520c0d06caSMauro Carvalho Chehab 		.get_max_value = ctrl_vres_max_get,
11530c0d06caSMauro Carvalho Chehab 		.get_min_value = ctrl_vres_min_get,
11540c0d06caSMauro Carvalho Chehab 	},{
11550c0d06caSMauro Carvalho Chehab 		.v4l_id = V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ,
11560c0d06caSMauro Carvalho Chehab 		.default_value = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000,
11570c0d06caSMauro Carvalho Chehab 		.desc = "Audio Sampling Frequency",
11580c0d06caSMauro Carvalho Chehab 		.name = "srate",
11590c0d06caSMauro Carvalho Chehab 		DEFREF(srate),
11600c0d06caSMauro Carvalho Chehab 		DEFENUM(control_values_srate),
11610c0d06caSMauro Carvalho Chehab 	},{
11620c0d06caSMauro Carvalho Chehab 		.desc = "Tuner Frequency (Hz)",
11630c0d06caSMauro Carvalho Chehab 		.name = "frequency",
11640c0d06caSMauro Carvalho Chehab 		.internal_id = PVR2_CID_FREQUENCY,
11650c0d06caSMauro Carvalho Chehab 		.default_value = 0,
11660c0d06caSMauro Carvalho Chehab 		.set_value = ctrl_freq_set,
11670c0d06caSMauro Carvalho Chehab 		.get_value = ctrl_freq_get,
11680c0d06caSMauro Carvalho Chehab 		.is_dirty = ctrl_freq_is_dirty,
11690c0d06caSMauro Carvalho Chehab 		.clear_dirty = ctrl_freq_clear_dirty,
11700c0d06caSMauro Carvalho Chehab 		DEFINT(0,0),
11710c0d06caSMauro Carvalho Chehab 		/* Hook in check for input value (tv/radio) and adjust
11720c0d06caSMauro Carvalho Chehab 		   max/min values accordingly */
11730c0d06caSMauro Carvalho Chehab 		.get_max_value = ctrl_freq_max_get,
11740c0d06caSMauro Carvalho Chehab 		.get_min_value = ctrl_freq_min_get,
11750c0d06caSMauro Carvalho Chehab 	},{
11760c0d06caSMauro Carvalho Chehab 		.desc = "Channel",
11770c0d06caSMauro Carvalho Chehab 		.name = "channel",
11780c0d06caSMauro Carvalho Chehab 		.set_value = ctrl_channel_set,
11790c0d06caSMauro Carvalho Chehab 		.get_value = ctrl_channel_get,
11800c0d06caSMauro Carvalho Chehab 		DEFINT(0,FREQTABLE_SIZE),
11810c0d06caSMauro Carvalho Chehab 	},{
11820c0d06caSMauro Carvalho Chehab 		.desc = "Channel Program Frequency",
11830c0d06caSMauro Carvalho Chehab 		.name = "freq_table_value",
11840c0d06caSMauro Carvalho Chehab 		.set_value = ctrl_channelfreq_set,
11850c0d06caSMauro Carvalho Chehab 		.get_value = ctrl_channelfreq_get,
11860c0d06caSMauro Carvalho Chehab 		DEFINT(0,0),
11870c0d06caSMauro Carvalho Chehab 		/* Hook in check for input value (tv/radio) and adjust
11880c0d06caSMauro Carvalho Chehab 		   max/min values accordingly */
11890c0d06caSMauro Carvalho Chehab 		.get_max_value = ctrl_freq_max_get,
11900c0d06caSMauro Carvalho Chehab 		.get_min_value = ctrl_freq_min_get,
11910c0d06caSMauro Carvalho Chehab 	},{
11920c0d06caSMauro Carvalho Chehab 		.desc = "Channel Program ID",
11930c0d06caSMauro Carvalho Chehab 		.name = "freq_table_channel",
11940c0d06caSMauro Carvalho Chehab 		.set_value = ctrl_channelprog_set,
11950c0d06caSMauro Carvalho Chehab 		.get_value = ctrl_channelprog_get,
11960c0d06caSMauro Carvalho Chehab 		DEFINT(0,FREQTABLE_SIZE),
11970c0d06caSMauro Carvalho Chehab 	},{
11980c0d06caSMauro Carvalho Chehab 		.desc = "Streaming Enabled",
11990c0d06caSMauro Carvalho Chehab 		.name = "streaming_enabled",
12000c0d06caSMauro Carvalho Chehab 		.get_value = ctrl_streamingenabled_get,
12010c0d06caSMauro Carvalho Chehab 		DEFBOOL,
12020c0d06caSMauro Carvalho Chehab 	},{
12030c0d06caSMauro Carvalho Chehab 		.desc = "USB Speed",
12040c0d06caSMauro Carvalho Chehab 		.name = "usb_speed",
12050c0d06caSMauro Carvalho Chehab 		.get_value = ctrl_hsm_get,
12060c0d06caSMauro Carvalho Chehab 		DEFENUM(control_values_hsm),
12070c0d06caSMauro Carvalho Chehab 	},{
12080c0d06caSMauro Carvalho Chehab 		.desc = "Master State",
12090c0d06caSMauro Carvalho Chehab 		.name = "master_state",
12100c0d06caSMauro Carvalho Chehab 		.get_value = ctrl_masterstate_get,
12110c0d06caSMauro Carvalho Chehab 		DEFENUM(pvr2_state_names),
12120c0d06caSMauro Carvalho Chehab 	},{
12130c0d06caSMauro Carvalho Chehab 		.desc = "Signal Present",
12140c0d06caSMauro Carvalho Chehab 		.name = "signal_present",
12150c0d06caSMauro Carvalho Chehab 		.get_value = ctrl_signal_get,
12160c0d06caSMauro Carvalho Chehab 		DEFINT(0,65535),
12170c0d06caSMauro Carvalho Chehab 	},{
12180c0d06caSMauro Carvalho Chehab 		.desc = "Audio Modes Present",
12190c0d06caSMauro Carvalho Chehab 		.name = "audio_modes_present",
12200c0d06caSMauro Carvalho Chehab 		.get_value = ctrl_audio_modes_present_get,
12210c0d06caSMauro Carvalho Chehab 		/* For this type we "borrow" the V4L2_TUNER_MODE enum from
12220c0d06caSMauro Carvalho Chehab 		   v4l.  Nothing outside of this module cares about this,
12230c0d06caSMauro Carvalho Chehab 		   but I reuse it in order to also reuse the
12240c0d06caSMauro Carvalho Chehab 		   control_values_audiomode string table. */
12250c0d06caSMauro Carvalho Chehab 		DEFMASK(((1 << V4L2_TUNER_MODE_MONO)|
12260c0d06caSMauro Carvalho Chehab 			 (1 << V4L2_TUNER_MODE_STEREO)|
12270c0d06caSMauro Carvalho Chehab 			 (1 << V4L2_TUNER_MODE_LANG1)|
12280c0d06caSMauro Carvalho Chehab 			 (1 << V4L2_TUNER_MODE_LANG2)),
12290c0d06caSMauro Carvalho Chehab 			control_values_audiomode),
12300c0d06caSMauro Carvalho Chehab 	},{
12310c0d06caSMauro Carvalho Chehab 		.desc = "Video Standards Available Mask",
12320c0d06caSMauro Carvalho Chehab 		.name = "video_standard_mask_available",
12330c0d06caSMauro Carvalho Chehab 		.internal_id = PVR2_CID_STDAVAIL,
12340c0d06caSMauro Carvalho Chehab 		.skip_init = !0,
12350c0d06caSMauro Carvalho Chehab 		.get_value = ctrl_stdavail_get,
12360c0d06caSMauro Carvalho Chehab 		.set_value = ctrl_stdavail_set,
12370c0d06caSMauro Carvalho Chehab 		.val_to_sym = ctrl_std_val_to_sym,
12380c0d06caSMauro Carvalho Chehab 		.sym_to_val = ctrl_std_sym_to_val,
12390c0d06caSMauro Carvalho Chehab 		.type = pvr2_ctl_bitmask,
12400c0d06caSMauro Carvalho Chehab 	},{
12410c0d06caSMauro Carvalho Chehab 		.desc = "Video Standards In Use Mask",
12420c0d06caSMauro Carvalho Chehab 		.name = "video_standard_mask_active",
12430c0d06caSMauro Carvalho Chehab 		.internal_id = PVR2_CID_STDCUR,
12440c0d06caSMauro Carvalho Chehab 		.skip_init = !0,
12450c0d06caSMauro Carvalho Chehab 		.get_value = ctrl_stdcur_get,
12460c0d06caSMauro Carvalho Chehab 		.set_value = ctrl_stdcur_set,
12470c0d06caSMauro Carvalho Chehab 		.is_dirty = ctrl_stdcur_is_dirty,
12480c0d06caSMauro Carvalho Chehab 		.clear_dirty = ctrl_stdcur_clear_dirty,
12490c0d06caSMauro Carvalho Chehab 		.val_to_sym = ctrl_std_val_to_sym,
12500c0d06caSMauro Carvalho Chehab 		.sym_to_val = ctrl_std_sym_to_val,
12510c0d06caSMauro Carvalho Chehab 		.type = pvr2_ctl_bitmask,
12520c0d06caSMauro Carvalho Chehab 	},{
12530c0d06caSMauro Carvalho Chehab 		.desc = "Video Standards Detected Mask",
12540c0d06caSMauro Carvalho Chehab 		.name = "video_standard_mask_detected",
12550c0d06caSMauro Carvalho Chehab 		.internal_id = PVR2_CID_STDDETECT,
12560c0d06caSMauro Carvalho Chehab 		.skip_init = !0,
12570c0d06caSMauro Carvalho Chehab 		.get_value = ctrl_stddetect_get,
12580c0d06caSMauro Carvalho Chehab 		.val_to_sym = ctrl_std_val_to_sym,
12590c0d06caSMauro Carvalho Chehab 		.sym_to_val = ctrl_std_sym_to_val,
12600c0d06caSMauro Carvalho Chehab 		.type = pvr2_ctl_bitmask,
12610c0d06caSMauro Carvalho Chehab 	}
12620c0d06caSMauro Carvalho Chehab };
12630c0d06caSMauro Carvalho Chehab 
12640c0d06caSMauro Carvalho Chehab #define CTRLDEF_COUNT ARRAY_SIZE(control_defs)
12650c0d06caSMauro Carvalho Chehab 
12660c0d06caSMauro Carvalho Chehab 
pvr2_config_get_name(enum pvr2_config cfg)12670c0d06caSMauro Carvalho Chehab const char *pvr2_config_get_name(enum pvr2_config cfg)
12680c0d06caSMauro Carvalho Chehab {
12690c0d06caSMauro Carvalho Chehab 	switch (cfg) {
12700c0d06caSMauro Carvalho Chehab 	case pvr2_config_empty: return "empty";
12710c0d06caSMauro Carvalho Chehab 	case pvr2_config_mpeg: return "mpeg";
12720c0d06caSMauro Carvalho Chehab 	case pvr2_config_vbi: return "vbi";
12730c0d06caSMauro Carvalho Chehab 	case pvr2_config_pcm: return "pcm";
12740c0d06caSMauro Carvalho Chehab 	case pvr2_config_rawvideo: return "raw video";
12750c0d06caSMauro Carvalho Chehab 	}
12760c0d06caSMauro Carvalho Chehab 	return "<unknown>";
12770c0d06caSMauro Carvalho Chehab }
12780c0d06caSMauro Carvalho Chehab 
12790c0d06caSMauro Carvalho Chehab 
pvr2_hdw_get_dev(struct pvr2_hdw * hdw)12800c0d06caSMauro Carvalho Chehab struct usb_device *pvr2_hdw_get_dev(struct pvr2_hdw *hdw)
12810c0d06caSMauro Carvalho Chehab {
12820c0d06caSMauro Carvalho Chehab 	return hdw->usb_dev;
12830c0d06caSMauro Carvalho Chehab }
12840c0d06caSMauro Carvalho Chehab 
12850c0d06caSMauro Carvalho Chehab 
pvr2_hdw_get_sn(struct pvr2_hdw * hdw)12860c0d06caSMauro Carvalho Chehab unsigned long pvr2_hdw_get_sn(struct pvr2_hdw *hdw)
12870c0d06caSMauro Carvalho Chehab {
12880c0d06caSMauro Carvalho Chehab 	return hdw->serial_number;
12890c0d06caSMauro Carvalho Chehab }
12900c0d06caSMauro Carvalho Chehab 
12910c0d06caSMauro Carvalho Chehab 
pvr2_hdw_get_bus_info(struct pvr2_hdw * hdw)12920c0d06caSMauro Carvalho Chehab const char *pvr2_hdw_get_bus_info(struct pvr2_hdw *hdw)
12930c0d06caSMauro Carvalho Chehab {
12940c0d06caSMauro Carvalho Chehab 	return hdw->bus_info;
12950c0d06caSMauro Carvalho Chehab }
12960c0d06caSMauro Carvalho Chehab 
12970c0d06caSMauro Carvalho Chehab 
pvr2_hdw_get_device_identifier(struct pvr2_hdw * hdw)12980c0d06caSMauro Carvalho Chehab const char *pvr2_hdw_get_device_identifier(struct pvr2_hdw *hdw)
12990c0d06caSMauro Carvalho Chehab {
13000c0d06caSMauro Carvalho Chehab 	return hdw->identifier;
13010c0d06caSMauro Carvalho Chehab }
13020c0d06caSMauro Carvalho Chehab 
13030c0d06caSMauro Carvalho Chehab 
pvr2_hdw_get_cur_freq(struct pvr2_hdw * hdw)13040c0d06caSMauro Carvalho Chehab unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *hdw)
13050c0d06caSMauro Carvalho Chehab {
13060c0d06caSMauro Carvalho Chehab 	return hdw->freqSelector ? hdw->freqValTelevision : hdw->freqValRadio;
13070c0d06caSMauro Carvalho Chehab }
13080c0d06caSMauro Carvalho Chehab 
13090c0d06caSMauro Carvalho Chehab /* Set the currently tuned frequency and account for all possible
13100c0d06caSMauro Carvalho Chehab    driver-core side effects of this action. */
pvr2_hdw_set_cur_freq(struct pvr2_hdw * hdw,unsigned long val)13110c0d06caSMauro Carvalho Chehab static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *hdw,unsigned long val)
13120c0d06caSMauro Carvalho Chehab {
13130c0d06caSMauro Carvalho Chehab 	if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
13140c0d06caSMauro Carvalho Chehab 		if (hdw->freqSelector) {
13150c0d06caSMauro Carvalho Chehab 			/* Swing over to radio frequency selection */
13160c0d06caSMauro Carvalho Chehab 			hdw->freqSelector = 0;
13170c0d06caSMauro Carvalho Chehab 			hdw->freqDirty = !0;
13180c0d06caSMauro Carvalho Chehab 		}
13190c0d06caSMauro Carvalho Chehab 		if (hdw->freqValRadio != val) {
13200c0d06caSMauro Carvalho Chehab 			hdw->freqValRadio = val;
13210c0d06caSMauro Carvalho Chehab 			hdw->freqSlotRadio = 0;
13220c0d06caSMauro Carvalho Chehab 			hdw->freqDirty = !0;
13230c0d06caSMauro Carvalho Chehab 		}
13240c0d06caSMauro Carvalho Chehab 	} else {
13250c0d06caSMauro Carvalho Chehab 		if (!(hdw->freqSelector)) {
13260c0d06caSMauro Carvalho Chehab 			/* Swing over to television frequency selection */
13270c0d06caSMauro Carvalho Chehab 			hdw->freqSelector = 1;
13280c0d06caSMauro Carvalho Chehab 			hdw->freqDirty = !0;
13290c0d06caSMauro Carvalho Chehab 		}
13300c0d06caSMauro Carvalho Chehab 		if (hdw->freqValTelevision != val) {
13310c0d06caSMauro Carvalho Chehab 			hdw->freqValTelevision = val;
13320c0d06caSMauro Carvalho Chehab 			hdw->freqSlotTelevision = 0;
13330c0d06caSMauro Carvalho Chehab 			hdw->freqDirty = !0;
13340c0d06caSMauro Carvalho Chehab 		}
13350c0d06caSMauro Carvalho Chehab 	}
13360c0d06caSMauro Carvalho Chehab }
13370c0d06caSMauro Carvalho Chehab 
pvr2_hdw_get_unit_number(struct pvr2_hdw * hdw)13380c0d06caSMauro Carvalho Chehab int pvr2_hdw_get_unit_number(struct pvr2_hdw *hdw)
13390c0d06caSMauro Carvalho Chehab {
13400c0d06caSMauro Carvalho Chehab 	return hdw->unit_number;
13410c0d06caSMauro Carvalho Chehab }
13420c0d06caSMauro Carvalho Chehab 
13430c0d06caSMauro Carvalho Chehab 
13440c0d06caSMauro Carvalho Chehab /* Attempt to locate one of the given set of files.  Messages are logged
13450c0d06caSMauro Carvalho Chehab    appropriate to what has been found.  The return value will be 0 or
13460c0d06caSMauro Carvalho Chehab    greater on success (it will be the index of the file name found) and
13470c0d06caSMauro Carvalho Chehab    fw_entry will be filled in.  Otherwise a negative error is returned on
13480c0d06caSMauro Carvalho Chehab    failure.  If the return value is -ENOENT then no viable firmware file
13490c0d06caSMauro Carvalho Chehab    could be located. */
pvr2_locate_firmware(struct pvr2_hdw * hdw,const struct firmware ** fw_entry,const char * fwtypename,unsigned int fwcount,const char * fwnames[])13500c0d06caSMauro Carvalho Chehab static int pvr2_locate_firmware(struct pvr2_hdw *hdw,
13510c0d06caSMauro Carvalho Chehab 				const struct firmware **fw_entry,
13520c0d06caSMauro Carvalho Chehab 				const char *fwtypename,
13530c0d06caSMauro Carvalho Chehab 				unsigned int fwcount,
13540c0d06caSMauro Carvalho Chehab 				const char *fwnames[])
13550c0d06caSMauro Carvalho Chehab {
13560c0d06caSMauro Carvalho Chehab 	unsigned int idx;
13570c0d06caSMauro Carvalho Chehab 	int ret = -EINVAL;
13580c0d06caSMauro Carvalho Chehab 	for (idx = 0; idx < fwcount; idx++) {
13590c0d06caSMauro Carvalho Chehab 		ret = request_firmware(fw_entry,
13600c0d06caSMauro Carvalho Chehab 				       fwnames[idx],
13610c0d06caSMauro Carvalho Chehab 				       &hdw->usb_dev->dev);
13620c0d06caSMauro Carvalho Chehab 		if (!ret) {
136396292c89SMauro Carvalho Chehab 			trace_firmware("Located %s firmware: %s; uploading...",
13640c0d06caSMauro Carvalho Chehab 				       fwtypename,
13650c0d06caSMauro Carvalho Chehab 				       fwnames[idx]);
13660c0d06caSMauro Carvalho Chehab 			return idx;
13670c0d06caSMauro Carvalho Chehab 		}
13680c0d06caSMauro Carvalho Chehab 		if (ret == -ENOENT) continue;
13690c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
13700c0d06caSMauro Carvalho Chehab 			   "request_firmware fatal error with code=%d",ret);
13710c0d06caSMauro Carvalho Chehab 		return ret;
13720c0d06caSMauro Carvalho Chehab 	}
13730c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_ERROR_LEGS,
137496292c89SMauro Carvalho Chehab 		   "***WARNING*** Device %s firmware seems to be missing.",
13750c0d06caSMauro Carvalho Chehab 		   fwtypename);
13760c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_ERROR_LEGS,
137796292c89SMauro Carvalho Chehab 		   "Did you install the pvrusb2 firmware files in their proper location?");
13780c0d06caSMauro Carvalho Chehab 	if (fwcount == 1) {
13790c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
13800c0d06caSMauro Carvalho Chehab 			   "request_firmware unable to locate %s file %s",
13810c0d06caSMauro Carvalho Chehab 			   fwtypename,fwnames[0]);
13820c0d06caSMauro Carvalho Chehab 	} else {
13830c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
138496292c89SMauro Carvalho Chehab 			   "request_firmware unable to locate one of the following %s files:",
13850c0d06caSMauro Carvalho Chehab 			   fwtypename);
13860c0d06caSMauro Carvalho Chehab 		for (idx = 0; idx < fwcount; idx++) {
13870c0d06caSMauro Carvalho Chehab 			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
13880c0d06caSMauro Carvalho Chehab 				   "request_firmware: Failed to find %s",
13890c0d06caSMauro Carvalho Chehab 				   fwnames[idx]);
13900c0d06caSMauro Carvalho Chehab 		}
13910c0d06caSMauro Carvalho Chehab 	}
13920c0d06caSMauro Carvalho Chehab 	return ret;
13930c0d06caSMauro Carvalho Chehab }
13940c0d06caSMauro Carvalho Chehab 
13950c0d06caSMauro Carvalho Chehab 
13960c0d06caSMauro Carvalho Chehab /*
13970c0d06caSMauro Carvalho Chehab  * pvr2_upload_firmware1().
13980c0d06caSMauro Carvalho Chehab  *
13990c0d06caSMauro Carvalho Chehab  * Send the 8051 firmware to the device.  After the upload, arrange for
14000c0d06caSMauro Carvalho Chehab  * device to re-enumerate.
14010c0d06caSMauro Carvalho Chehab  *
14020c0d06caSMauro Carvalho Chehab  * NOTE : the pointer to the firmware data given by request_firmware()
14030c0d06caSMauro Carvalho Chehab  * is not suitable for an usb transaction.
14040c0d06caSMauro Carvalho Chehab  *
14050c0d06caSMauro Carvalho Chehab  */
pvr2_upload_firmware1(struct pvr2_hdw * hdw)14060c0d06caSMauro Carvalho Chehab static int pvr2_upload_firmware1(struct pvr2_hdw *hdw)
14070c0d06caSMauro Carvalho Chehab {
14080c0d06caSMauro Carvalho Chehab 	const struct firmware *fw_entry = NULL;
14090c0d06caSMauro Carvalho Chehab 	void  *fw_ptr;
14100c0d06caSMauro Carvalho Chehab 	unsigned int pipe;
14110c0d06caSMauro Carvalho Chehab 	unsigned int fwsize;
14120c0d06caSMauro Carvalho Chehab 	int ret;
14130c0d06caSMauro Carvalho Chehab 	u16 address;
14140c0d06caSMauro Carvalho Chehab 
14150c0d06caSMauro Carvalho Chehab 	if (!hdw->hdw_desc->fx2_firmware.cnt) {
14160c0d06caSMauro Carvalho Chehab 		hdw->fw1_state = FW1_STATE_OK;
14170c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
141896292c89SMauro Carvalho Chehab 			   "Connected device type defines no firmware to upload; ignoring firmware");
14190c0d06caSMauro Carvalho Chehab 		return -ENOTTY;
14200c0d06caSMauro Carvalho Chehab 	}
14210c0d06caSMauro Carvalho Chehab 
14220c0d06caSMauro Carvalho Chehab 	hdw->fw1_state = FW1_STATE_FAILED; // default result
14230c0d06caSMauro Carvalho Chehab 
14240c0d06caSMauro Carvalho Chehab 	trace_firmware("pvr2_upload_firmware1");
14250c0d06caSMauro Carvalho Chehab 
14260c0d06caSMauro Carvalho Chehab 	ret = pvr2_locate_firmware(hdw,&fw_entry,"fx2 controller",
14270c0d06caSMauro Carvalho Chehab 				   hdw->hdw_desc->fx2_firmware.cnt,
14280c0d06caSMauro Carvalho Chehab 				   hdw->hdw_desc->fx2_firmware.lst);
14290c0d06caSMauro Carvalho Chehab 	if (ret < 0) {
14300c0d06caSMauro Carvalho Chehab 		if (ret == -ENOENT) hdw->fw1_state = FW1_STATE_MISSING;
14310c0d06caSMauro Carvalho Chehab 		return ret;
14320c0d06caSMauro Carvalho Chehab 	}
14330c0d06caSMauro Carvalho Chehab 
14340c0d06caSMauro Carvalho Chehab 	usb_clear_halt(hdw->usb_dev, usb_sndbulkpipe(hdw->usb_dev, 0 & 0x7f));
14350c0d06caSMauro Carvalho Chehab 
14360c0d06caSMauro Carvalho Chehab 	pipe = usb_sndctrlpipe(hdw->usb_dev, 0);
14370c0d06caSMauro Carvalho Chehab 	fwsize = fw_entry->size;
14380c0d06caSMauro Carvalho Chehab 
14390c0d06caSMauro Carvalho Chehab 	if ((fwsize != 0x2000) &&
14400c0d06caSMauro Carvalho Chehab 	    (!(hdw->hdw_desc->flag_fx2_16kb && (fwsize == 0x4000)))) {
14410c0d06caSMauro Carvalho Chehab 		if (hdw->hdw_desc->flag_fx2_16kb) {
14420c0d06caSMauro Carvalho Chehab 			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
144396292c89SMauro Carvalho Chehab 				   "Wrong fx2 firmware size (expected 8192 or 16384, got %u)",
14440c0d06caSMauro Carvalho Chehab 				   fwsize);
14450c0d06caSMauro Carvalho Chehab 		} else {
14460c0d06caSMauro Carvalho Chehab 			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
144796292c89SMauro Carvalho Chehab 				   "Wrong fx2 firmware size (expected 8192, got %u)",
14480c0d06caSMauro Carvalho Chehab 				   fwsize);
14490c0d06caSMauro Carvalho Chehab 		}
14500c0d06caSMauro Carvalho Chehab 		release_firmware(fw_entry);
14510c0d06caSMauro Carvalho Chehab 		return -ENOMEM;
14520c0d06caSMauro Carvalho Chehab 	}
14530c0d06caSMauro Carvalho Chehab 
14540c0d06caSMauro Carvalho Chehab 	fw_ptr = kmalloc(0x800, GFP_KERNEL);
14550c0d06caSMauro Carvalho Chehab 	if (fw_ptr == NULL){
14560c0d06caSMauro Carvalho Chehab 		release_firmware(fw_entry);
14570c0d06caSMauro Carvalho Chehab 		return -ENOMEM;
14580c0d06caSMauro Carvalho Chehab 	}
14590c0d06caSMauro Carvalho Chehab 
14600c0d06caSMauro Carvalho Chehab 	/* We have to hold the CPU during firmware upload. */
14610c0d06caSMauro Carvalho Chehab 	pvr2_hdw_cpureset_assert(hdw,1);
14620c0d06caSMauro Carvalho Chehab 
14630c0d06caSMauro Carvalho Chehab 	/* upload the firmware to address 0000-1fff in 2048 (=0x800) bytes
14640c0d06caSMauro Carvalho Chehab 	   chunk. */
14650c0d06caSMauro Carvalho Chehab 
14660c0d06caSMauro Carvalho Chehab 	ret = 0;
14670c0d06caSMauro Carvalho Chehab 	for (address = 0; address < fwsize; address += 0x800) {
14680c0d06caSMauro Carvalho Chehab 		memcpy(fw_ptr, fw_entry->data + address, 0x800);
14690c0d06caSMauro Carvalho Chehab 		ret += usb_control_msg(hdw->usb_dev, pipe, 0xa0, 0x40, address,
1470b82bf9b9SJohan Hovold 				       0, fw_ptr, 0x800, 1000);
14710c0d06caSMauro Carvalho Chehab 	}
14720c0d06caSMauro Carvalho Chehab 
14730c0d06caSMauro Carvalho Chehab 	trace_firmware("Upload done, releasing device's CPU");
14740c0d06caSMauro Carvalho Chehab 
14750c0d06caSMauro Carvalho Chehab 	/* Now release the CPU.  It will disconnect and reconnect later. */
14760c0d06caSMauro Carvalho Chehab 	pvr2_hdw_cpureset_assert(hdw,0);
14770c0d06caSMauro Carvalho Chehab 
14780c0d06caSMauro Carvalho Chehab 	kfree(fw_ptr);
14790c0d06caSMauro Carvalho Chehab 	release_firmware(fw_entry);
14800c0d06caSMauro Carvalho Chehab 
14810c0d06caSMauro Carvalho Chehab 	trace_firmware("Upload done (%d bytes sent)",ret);
14820c0d06caSMauro Carvalho Chehab 
14830c0d06caSMauro Carvalho Chehab 	/* We should have written fwsize bytes */
14840c0d06caSMauro Carvalho Chehab 	if (ret == fwsize) {
14850c0d06caSMauro Carvalho Chehab 		hdw->fw1_state = FW1_STATE_RELOAD;
14860c0d06caSMauro Carvalho Chehab 		return 0;
14870c0d06caSMauro Carvalho Chehab 	}
14880c0d06caSMauro Carvalho Chehab 
14890c0d06caSMauro Carvalho Chehab 	return -EIO;
14900c0d06caSMauro Carvalho Chehab }
14910c0d06caSMauro Carvalho Chehab 
14920c0d06caSMauro Carvalho Chehab 
14930c0d06caSMauro Carvalho Chehab /*
14940c0d06caSMauro Carvalho Chehab  * pvr2_upload_firmware2()
14950c0d06caSMauro Carvalho Chehab  *
14960c0d06caSMauro Carvalho Chehab  * This uploads encoder firmware on endpoint 2.
14970c0d06caSMauro Carvalho Chehab  *
14980c0d06caSMauro Carvalho Chehab  */
14990c0d06caSMauro Carvalho Chehab 
pvr2_upload_firmware2(struct pvr2_hdw * hdw)15000c0d06caSMauro Carvalho Chehab int pvr2_upload_firmware2(struct pvr2_hdw *hdw)
15010c0d06caSMauro Carvalho Chehab {
15020c0d06caSMauro Carvalho Chehab 	const struct firmware *fw_entry = NULL;
15030c0d06caSMauro Carvalho Chehab 	void  *fw_ptr;
15040c0d06caSMauro Carvalho Chehab 	unsigned int pipe, fw_len, fw_done, bcnt, icnt;
15050c0d06caSMauro Carvalho Chehab 	int actual_length;
15060c0d06caSMauro Carvalho Chehab 	int ret = 0;
15070c0d06caSMauro Carvalho Chehab 	int fwidx;
15080c0d06caSMauro Carvalho Chehab 	static const char *fw_files[] = {
15090c0d06caSMauro Carvalho Chehab 		CX2341X_FIRM_ENC_FILENAME,
15100c0d06caSMauro Carvalho Chehab 	};
15110c0d06caSMauro Carvalho Chehab 
15120c0d06caSMauro Carvalho Chehab 	if (hdw->hdw_desc->flag_skip_cx23416_firmware) {
15130c0d06caSMauro Carvalho Chehab 		return 0;
15140c0d06caSMauro Carvalho Chehab 	}
15150c0d06caSMauro Carvalho Chehab 
15160c0d06caSMauro Carvalho Chehab 	trace_firmware("pvr2_upload_firmware2");
15170c0d06caSMauro Carvalho Chehab 
15180c0d06caSMauro Carvalho Chehab 	ret = pvr2_locate_firmware(hdw,&fw_entry,"encoder",
15190c0d06caSMauro Carvalho Chehab 				   ARRAY_SIZE(fw_files), fw_files);
15200c0d06caSMauro Carvalho Chehab 	if (ret < 0) return ret;
15210c0d06caSMauro Carvalho Chehab 	fwidx = ret;
15220c0d06caSMauro Carvalho Chehab 	ret = 0;
15230c0d06caSMauro Carvalho Chehab 	/* Since we're about to completely reinitialize the encoder,
15240c0d06caSMauro Carvalho Chehab 	   invalidate our cached copy of its configuration state.  Next
15250c0d06caSMauro Carvalho Chehab 	   time we configure the encoder, then we'll fully configure it. */
15260c0d06caSMauro Carvalho Chehab 	hdw->enc_cur_valid = 0;
15270c0d06caSMauro Carvalho Chehab 
15280c0d06caSMauro Carvalho Chehab 	/* Encoder is about to be reset so note that as far as we're
15290c0d06caSMauro Carvalho Chehab 	   concerned now, the encoder has never been run. */
15300c0d06caSMauro Carvalho Chehab 	del_timer_sync(&hdw->encoder_run_timer);
15310c0d06caSMauro Carvalho Chehab 	if (hdw->state_encoder_runok) {
15320c0d06caSMauro Carvalho Chehab 		hdw->state_encoder_runok = 0;
15330c0d06caSMauro Carvalho Chehab 		trace_stbit("state_encoder_runok",hdw->state_encoder_runok);
15340c0d06caSMauro Carvalho Chehab 	}
15350c0d06caSMauro Carvalho Chehab 
15360c0d06caSMauro Carvalho Chehab 	/* First prepare firmware loading */
15370c0d06caSMauro Carvalho Chehab 	ret |= pvr2_write_register(hdw, 0x0048, 0xffffffff); /*interrupt mask*/
15380c0d06caSMauro Carvalho Chehab 	ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000088); /*gpio dir*/
15390c0d06caSMauro Carvalho Chehab 	ret |= pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000008); /*gpio output state*/
15400c0d06caSMauro Carvalho Chehab 	ret |= pvr2_hdw_cmd_deep_reset(hdw);
15410c0d06caSMauro Carvalho Chehab 	ret |= pvr2_write_register(hdw, 0xa064, 0x00000000); /*APU command*/
15420c0d06caSMauro Carvalho Chehab 	ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000408); /*gpio dir*/
15430c0d06caSMauro Carvalho Chehab 	ret |= pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000008); /*gpio output state*/
15440c0d06caSMauro Carvalho Chehab 	ret |= pvr2_write_register(hdw, 0x9058, 0xffffffed); /*VPU ctrl*/
15450c0d06caSMauro Carvalho Chehab 	ret |= pvr2_write_register(hdw, 0x9054, 0xfffffffd); /*reset hw blocks*/
15460c0d06caSMauro Carvalho Chehab 	ret |= pvr2_write_register(hdw, 0x07f8, 0x80000800); /*encoder SDRAM refresh*/
15470c0d06caSMauro Carvalho Chehab 	ret |= pvr2_write_register(hdw, 0x07fc, 0x0000001a); /*encoder SDRAM pre-charge*/
15480c0d06caSMauro Carvalho Chehab 	ret |= pvr2_write_register(hdw, 0x0700, 0x00000000); /*I2C clock*/
15490c0d06caSMauro Carvalho Chehab 	ret |= pvr2_write_register(hdw, 0xaa00, 0x00000000); /*unknown*/
15500c0d06caSMauro Carvalho Chehab 	ret |= pvr2_write_register(hdw, 0xaa04, 0x00057810); /*unknown*/
15510c0d06caSMauro Carvalho Chehab 	ret |= pvr2_write_register(hdw, 0xaa10, 0x00148500); /*unknown*/
15520c0d06caSMauro Carvalho Chehab 	ret |= pvr2_write_register(hdw, 0xaa18, 0x00840000); /*unknown*/
15530c0d06caSMauro Carvalho Chehab 	ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_FWPOST1);
15540c0d06caSMauro Carvalho Chehab 	ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_MEMSEL | (1 << 8) | (0 << 16));
15550c0d06caSMauro Carvalho Chehab 
15560c0d06caSMauro Carvalho Chehab 	if (ret) {
15570c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
15580c0d06caSMauro Carvalho Chehab 			   "firmware2 upload prep failed, ret=%d",ret);
15590c0d06caSMauro Carvalho Chehab 		release_firmware(fw_entry);
15600c0d06caSMauro Carvalho Chehab 		goto done;
15610c0d06caSMauro Carvalho Chehab 	}
15620c0d06caSMauro Carvalho Chehab 
15630c0d06caSMauro Carvalho Chehab 	/* Now send firmware */
15640c0d06caSMauro Carvalho Chehab 
15650c0d06caSMauro Carvalho Chehab 	fw_len = fw_entry->size;
15660c0d06caSMauro Carvalho Chehab 
15670c0d06caSMauro Carvalho Chehab 	if (fw_len % sizeof(u32)) {
15680c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
156996292c89SMauro Carvalho Chehab 			   "size of %s firmware must be a multiple of %zu bytes",
15700c0d06caSMauro Carvalho Chehab 			   fw_files[fwidx],sizeof(u32));
15710c0d06caSMauro Carvalho Chehab 		release_firmware(fw_entry);
15720c0d06caSMauro Carvalho Chehab 		ret = -EINVAL;
15730c0d06caSMauro Carvalho Chehab 		goto done;
15740c0d06caSMauro Carvalho Chehab 	}
15750c0d06caSMauro Carvalho Chehab 
15760c0d06caSMauro Carvalho Chehab 	fw_ptr = kmalloc(FIRMWARE_CHUNK_SIZE, GFP_KERNEL);
15770c0d06caSMauro Carvalho Chehab 	if (fw_ptr == NULL){
15780c0d06caSMauro Carvalho Chehab 		release_firmware(fw_entry);
15790c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
15800c0d06caSMauro Carvalho Chehab 			   "failed to allocate memory for firmware2 upload");
15810c0d06caSMauro Carvalho Chehab 		ret = -ENOMEM;
15820c0d06caSMauro Carvalho Chehab 		goto done;
15830c0d06caSMauro Carvalho Chehab 	}
15840c0d06caSMauro Carvalho Chehab 
15850c0d06caSMauro Carvalho Chehab 	pipe = usb_sndbulkpipe(hdw->usb_dev, PVR2_FIRMWARE_ENDPOINT);
15860c0d06caSMauro Carvalho Chehab 
15870c0d06caSMauro Carvalho Chehab 	fw_done = 0;
15880c0d06caSMauro Carvalho Chehab 	for (fw_done = 0; fw_done < fw_len;) {
15890c0d06caSMauro Carvalho Chehab 		bcnt = fw_len - fw_done;
15900c0d06caSMauro Carvalho Chehab 		if (bcnt > FIRMWARE_CHUNK_SIZE) bcnt = FIRMWARE_CHUNK_SIZE;
15910c0d06caSMauro Carvalho Chehab 		memcpy(fw_ptr, fw_entry->data + fw_done, bcnt);
15920c0d06caSMauro Carvalho Chehab 		/* Usbsnoop log shows that we must swap bytes... */
15930c0d06caSMauro Carvalho Chehab 		/* Some background info: The data being swapped here is a
15940c0d06caSMauro Carvalho Chehab 		   firmware image destined for the mpeg encoder chip that
15950c0d06caSMauro Carvalho Chehab 		   lives at the other end of a USB endpoint.  The encoder
15960c0d06caSMauro Carvalho Chehab 		   chip always talks in 32 bit chunks and its storage is
15970c0d06caSMauro Carvalho Chehab 		   organized into 32 bit words.  However from the file
15980c0d06caSMauro Carvalho Chehab 		   system to the encoder chip everything is purely a byte
15990c0d06caSMauro Carvalho Chehab 		   stream.  The firmware file's contents are always 32 bit
16000c0d06caSMauro Carvalho Chehab 		   swapped from what the encoder expects.  Thus the need
16010c0d06caSMauro Carvalho Chehab 		   always exists to swap the bytes regardless of the endian
16020c0d06caSMauro Carvalho Chehab 		   type of the host processor and therefore swab32() makes
16030c0d06caSMauro Carvalho Chehab 		   the most sense. */
16040c0d06caSMauro Carvalho Chehab 		for (icnt = 0; icnt < bcnt/4 ; icnt++)
16050c0d06caSMauro Carvalho Chehab 			((u32 *)fw_ptr)[icnt] = swab32(((u32 *)fw_ptr)[icnt]);
16060c0d06caSMauro Carvalho Chehab 
16070c0d06caSMauro Carvalho Chehab 		ret |= usb_bulk_msg(hdw->usb_dev, pipe, fw_ptr,bcnt,
1608b82bf9b9SJohan Hovold 				    &actual_length, 1000);
16090c0d06caSMauro Carvalho Chehab 		ret |= (actual_length != bcnt);
16100c0d06caSMauro Carvalho Chehab 		if (ret) break;
16110c0d06caSMauro Carvalho Chehab 		fw_done += bcnt;
16120c0d06caSMauro Carvalho Chehab 	}
16130c0d06caSMauro Carvalho Chehab 
16140c0d06caSMauro Carvalho Chehab 	trace_firmware("upload of %s : %i / %i ",
16150c0d06caSMauro Carvalho Chehab 		       fw_files[fwidx],fw_done,fw_len);
16160c0d06caSMauro Carvalho Chehab 
16170c0d06caSMauro Carvalho Chehab 	kfree(fw_ptr);
16180c0d06caSMauro Carvalho Chehab 	release_firmware(fw_entry);
16190c0d06caSMauro Carvalho Chehab 
16200c0d06caSMauro Carvalho Chehab 	if (ret) {
16210c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
16220c0d06caSMauro Carvalho Chehab 			   "firmware2 upload transfer failure");
16230c0d06caSMauro Carvalho Chehab 		goto done;
16240c0d06caSMauro Carvalho Chehab 	}
16250c0d06caSMauro Carvalho Chehab 
16260c0d06caSMauro Carvalho Chehab 	/* Finish upload */
16270c0d06caSMauro Carvalho Chehab 
16280c0d06caSMauro Carvalho Chehab 	ret |= pvr2_write_register(hdw, 0x9054, 0xffffffff); /*reset hw blocks*/
16290c0d06caSMauro Carvalho Chehab 	ret |= pvr2_write_register(hdw, 0x9058, 0xffffffe8); /*VPU ctrl*/
16300c0d06caSMauro Carvalho Chehab 	ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_MEMSEL | (1 << 8) | (0 << 16));
16310c0d06caSMauro Carvalho Chehab 
16320c0d06caSMauro Carvalho Chehab 	if (ret) {
16330c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
16340c0d06caSMauro Carvalho Chehab 			   "firmware2 upload post-proc failure");
16350c0d06caSMauro Carvalho Chehab 	}
16360c0d06caSMauro Carvalho Chehab 
16370c0d06caSMauro Carvalho Chehab  done:
16380c0d06caSMauro Carvalho Chehab 	if (hdw->hdw_desc->signal_routing_scheme ==
16390c0d06caSMauro Carvalho Chehab 	    PVR2_ROUTING_SCHEME_GOTVIEW) {
16400c0d06caSMauro Carvalho Chehab 		/* Ensure that GPIO 11 is set to output for GOTVIEW
16410c0d06caSMauro Carvalho Chehab 		   hardware. */
16420c0d06caSMauro Carvalho Chehab 		pvr2_hdw_gpio_chg_dir(hdw,(1 << 11),~0);
16430c0d06caSMauro Carvalho Chehab 	}
16440c0d06caSMauro Carvalho Chehab 	return ret;
16450c0d06caSMauro Carvalho Chehab }
16460c0d06caSMauro Carvalho Chehab 
16470c0d06caSMauro Carvalho Chehab 
pvr2_get_state_name(unsigned int st)16480c0d06caSMauro Carvalho Chehab static const char *pvr2_get_state_name(unsigned int st)
16490c0d06caSMauro Carvalho Chehab {
16500c0d06caSMauro Carvalho Chehab 	if (st < ARRAY_SIZE(pvr2_state_names)) {
16510c0d06caSMauro Carvalho Chehab 		return pvr2_state_names[st];
16520c0d06caSMauro Carvalho Chehab 	}
16530c0d06caSMauro Carvalho Chehab 	return "???";
16540c0d06caSMauro Carvalho Chehab }
16550c0d06caSMauro Carvalho Chehab 
pvr2_decoder_enable(struct pvr2_hdw * hdw,int enablefl)16560c0d06caSMauro Carvalho Chehab static int pvr2_decoder_enable(struct pvr2_hdw *hdw,int enablefl)
16570c0d06caSMauro Carvalho Chehab {
16580c0d06caSMauro Carvalho Chehab 	/* Even though we really only care about the video decoder chip at
16590c0d06caSMauro Carvalho Chehab 	   this point, we'll broadcast stream on/off to all sub-devices
16600c0d06caSMauro Carvalho Chehab 	   anyway, just in case somebody else wants to hear the
16610c0d06caSMauro Carvalho Chehab 	   command... */
16620c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 stream=%s",
16630c0d06caSMauro Carvalho Chehab 		   (enablefl ? "on" : "off"));
16640c0d06caSMauro Carvalho Chehab 	v4l2_device_call_all(&hdw->v4l2_dev, 0, video, s_stream, enablefl);
16650c0d06caSMauro Carvalho Chehab 	v4l2_device_call_all(&hdw->v4l2_dev, 0, audio, s_stream, enablefl);
16660c0d06caSMauro Carvalho Chehab 	if (hdw->decoder_client_id) {
16670c0d06caSMauro Carvalho Chehab 		/* We get here if the encoder has been noticed.  Otherwise
16680c0d06caSMauro Carvalho Chehab 		   we'll issue a warning to the user (which should
16690c0d06caSMauro Carvalho Chehab 		   normally never happen). */
16700c0d06caSMauro Carvalho Chehab 		return 0;
16710c0d06caSMauro Carvalho Chehab 	}
16720c0d06caSMauro Carvalho Chehab 	if (!hdw->flag_decoder_missed) {
16730c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
16741753c7c4SAndrey Konovalov 			   "***WARNING*** No decoder present");
16750c0d06caSMauro Carvalho Chehab 		hdw->flag_decoder_missed = !0;
16760c0d06caSMauro Carvalho Chehab 		trace_stbit("flag_decoder_missed",
16770c0d06caSMauro Carvalho Chehab 			    hdw->flag_decoder_missed);
16780c0d06caSMauro Carvalho Chehab 	}
16790c0d06caSMauro Carvalho Chehab 	return -EIO;
16800c0d06caSMauro Carvalho Chehab }
16810c0d06caSMauro Carvalho Chehab 
16820c0d06caSMauro Carvalho Chehab 
pvr2_hdw_get_state(struct pvr2_hdw * hdw)16830c0d06caSMauro Carvalho Chehab int pvr2_hdw_get_state(struct pvr2_hdw *hdw)
16840c0d06caSMauro Carvalho Chehab {
16850c0d06caSMauro Carvalho Chehab 	return hdw->master_state;
16860c0d06caSMauro Carvalho Chehab }
16870c0d06caSMauro Carvalho Chehab 
16880c0d06caSMauro Carvalho Chehab 
pvr2_hdw_untrip_unlocked(struct pvr2_hdw * hdw)16890c0d06caSMauro Carvalho Chehab static int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *hdw)
16900c0d06caSMauro Carvalho Chehab {
16910c0d06caSMauro Carvalho Chehab 	if (!hdw->flag_tripped) return 0;
16920c0d06caSMauro Carvalho Chehab 	hdw->flag_tripped = 0;
16930c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_ERROR_LEGS,
169487c9f197SColin Ian King 		   "Clearing driver error status");
16950c0d06caSMauro Carvalho Chehab 	return !0;
16960c0d06caSMauro Carvalho Chehab }
16970c0d06caSMauro Carvalho Chehab 
16980c0d06caSMauro Carvalho Chehab 
pvr2_hdw_untrip(struct pvr2_hdw * hdw)16990c0d06caSMauro Carvalho Chehab int pvr2_hdw_untrip(struct pvr2_hdw *hdw)
17000c0d06caSMauro Carvalho Chehab {
17010c0d06caSMauro Carvalho Chehab 	int fl;
17020c0d06caSMauro Carvalho Chehab 	LOCK_TAKE(hdw->big_lock); do {
17030c0d06caSMauro Carvalho Chehab 		fl = pvr2_hdw_untrip_unlocked(hdw);
17040c0d06caSMauro Carvalho Chehab 	} while (0); LOCK_GIVE(hdw->big_lock);
17050c0d06caSMauro Carvalho Chehab 	if (fl) pvr2_hdw_state_sched(hdw);
17060c0d06caSMauro Carvalho Chehab 	return 0;
17070c0d06caSMauro Carvalho Chehab }
17080c0d06caSMauro Carvalho Chehab 
17090c0d06caSMauro Carvalho Chehab 
17100c0d06caSMauro Carvalho Chehab 
17110c0d06caSMauro Carvalho Chehab 
pvr2_hdw_get_streaming(struct pvr2_hdw * hdw)17120c0d06caSMauro Carvalho Chehab int pvr2_hdw_get_streaming(struct pvr2_hdw *hdw)
17130c0d06caSMauro Carvalho Chehab {
17140c0d06caSMauro Carvalho Chehab 	return hdw->state_pipeline_req != 0;
17150c0d06caSMauro Carvalho Chehab }
17160c0d06caSMauro Carvalho Chehab 
17170c0d06caSMauro Carvalho Chehab 
pvr2_hdw_set_streaming(struct pvr2_hdw * hdw,int enable_flag)17180c0d06caSMauro Carvalho Chehab int pvr2_hdw_set_streaming(struct pvr2_hdw *hdw,int enable_flag)
17190c0d06caSMauro Carvalho Chehab {
17200c0d06caSMauro Carvalho Chehab 	int ret,st;
1721339df438SHans Verkuil 	LOCK_TAKE(hdw->big_lock);
17220c0d06caSMauro Carvalho Chehab 	pvr2_hdw_untrip_unlocked(hdw);
1723339df438SHans Verkuil 	if (!enable_flag != !hdw->state_pipeline_req) {
17240c0d06caSMauro Carvalho Chehab 		hdw->state_pipeline_req = enable_flag != 0;
17250c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_START_STOP,
17260c0d06caSMauro Carvalho Chehab 			   "/*--TRACE_STREAM--*/ %s",
17270c0d06caSMauro Carvalho Chehab 			   enable_flag ? "enable" : "disable");
17280c0d06caSMauro Carvalho Chehab 	}
17290c0d06caSMauro Carvalho Chehab 	pvr2_hdw_state_sched(hdw);
1730339df438SHans Verkuil 	LOCK_GIVE(hdw->big_lock);
17310c0d06caSMauro Carvalho Chehab 	if ((ret = pvr2_hdw_wait(hdw,0)) < 0) return ret;
17320c0d06caSMauro Carvalho Chehab 	if (enable_flag) {
17330c0d06caSMauro Carvalho Chehab 		while ((st = hdw->master_state) != PVR2_STATE_RUN) {
17340c0d06caSMauro Carvalho Chehab 			if (st != PVR2_STATE_READY) return -EIO;
17350c0d06caSMauro Carvalho Chehab 			if ((ret = pvr2_hdw_wait(hdw,st)) < 0) return ret;
17360c0d06caSMauro Carvalho Chehab 		}
17370c0d06caSMauro Carvalho Chehab 	}
17380c0d06caSMauro Carvalho Chehab 	return 0;
17390c0d06caSMauro Carvalho Chehab }
17400c0d06caSMauro Carvalho Chehab 
17410c0d06caSMauro Carvalho Chehab 
pvr2_hdw_set_stream_type(struct pvr2_hdw * hdw,enum pvr2_config config)17420c0d06caSMauro Carvalho Chehab int pvr2_hdw_set_stream_type(struct pvr2_hdw *hdw,enum pvr2_config config)
17430c0d06caSMauro Carvalho Chehab {
17440c0d06caSMauro Carvalho Chehab 	int fl;
17450c0d06caSMauro Carvalho Chehab 	LOCK_TAKE(hdw->big_lock);
17460c0d06caSMauro Carvalho Chehab 	if ((fl = (hdw->desired_stream_type != config)) != 0) {
17470c0d06caSMauro Carvalho Chehab 		hdw->desired_stream_type = config;
17480c0d06caSMauro Carvalho Chehab 		hdw->state_pipeline_config = 0;
17490c0d06caSMauro Carvalho Chehab 		trace_stbit("state_pipeline_config",
17500c0d06caSMauro Carvalho Chehab 			    hdw->state_pipeline_config);
17510c0d06caSMauro Carvalho Chehab 		pvr2_hdw_state_sched(hdw);
17520c0d06caSMauro Carvalho Chehab 	}
17530c0d06caSMauro Carvalho Chehab 	LOCK_GIVE(hdw->big_lock);
17540c0d06caSMauro Carvalho Chehab 	if (fl) return 0;
17550c0d06caSMauro Carvalho Chehab 	return pvr2_hdw_wait(hdw,0);
17560c0d06caSMauro Carvalho Chehab }
17570c0d06caSMauro Carvalho Chehab 
17580c0d06caSMauro Carvalho Chehab 
get_default_tuner_type(struct pvr2_hdw * hdw)17590c0d06caSMauro Carvalho Chehab static int get_default_tuner_type(struct pvr2_hdw *hdw)
17600c0d06caSMauro Carvalho Chehab {
17610c0d06caSMauro Carvalho Chehab 	int unit_number = hdw->unit_number;
17620c0d06caSMauro Carvalho Chehab 	int tp = -1;
17630c0d06caSMauro Carvalho Chehab 	if ((unit_number >= 0) && (unit_number < PVR_NUM)) {
17640c0d06caSMauro Carvalho Chehab 		tp = tuner[unit_number];
17650c0d06caSMauro Carvalho Chehab 	}
17660c0d06caSMauro Carvalho Chehab 	if (tp < 0) return -EINVAL;
17670c0d06caSMauro Carvalho Chehab 	hdw->tuner_type = tp;
17680c0d06caSMauro Carvalho Chehab 	hdw->tuner_updated = !0;
17690c0d06caSMauro Carvalho Chehab 	return 0;
17700c0d06caSMauro Carvalho Chehab }
17710c0d06caSMauro Carvalho Chehab 
17720c0d06caSMauro Carvalho Chehab 
get_default_standard(struct pvr2_hdw * hdw)17730c0d06caSMauro Carvalho Chehab static v4l2_std_id get_default_standard(struct pvr2_hdw *hdw)
17740c0d06caSMauro Carvalho Chehab {
17750c0d06caSMauro Carvalho Chehab 	int unit_number = hdw->unit_number;
17760c0d06caSMauro Carvalho Chehab 	int tp = 0;
17770c0d06caSMauro Carvalho Chehab 	if ((unit_number >= 0) && (unit_number < PVR_NUM)) {
17780c0d06caSMauro Carvalho Chehab 		tp = video_std[unit_number];
17790c0d06caSMauro Carvalho Chehab 		if (tp) return tp;
17800c0d06caSMauro Carvalho Chehab 	}
17810c0d06caSMauro Carvalho Chehab 	return 0;
17820c0d06caSMauro Carvalho Chehab }
17830c0d06caSMauro Carvalho Chehab 
17840c0d06caSMauro Carvalho Chehab 
get_default_error_tolerance(struct pvr2_hdw * hdw)17850c0d06caSMauro Carvalho Chehab static unsigned int get_default_error_tolerance(struct pvr2_hdw *hdw)
17860c0d06caSMauro Carvalho Chehab {
17870c0d06caSMauro Carvalho Chehab 	int unit_number = hdw->unit_number;
17880c0d06caSMauro Carvalho Chehab 	int tp = 0;
17890c0d06caSMauro Carvalho Chehab 	if ((unit_number >= 0) && (unit_number < PVR_NUM)) {
17900c0d06caSMauro Carvalho Chehab 		tp = tolerance[unit_number];
17910c0d06caSMauro Carvalho Chehab 	}
17920c0d06caSMauro Carvalho Chehab 	return tp;
17930c0d06caSMauro Carvalho Chehab }
17940c0d06caSMauro Carvalho Chehab 
17950c0d06caSMauro Carvalho Chehab 
pvr2_hdw_check_firmware(struct pvr2_hdw * hdw)17960c0d06caSMauro Carvalho Chehab static int pvr2_hdw_check_firmware(struct pvr2_hdw *hdw)
17970c0d06caSMauro Carvalho Chehab {
17980c0d06caSMauro Carvalho Chehab 	/* Try a harmless request to fetch the eeprom's address over
17990c0d06caSMauro Carvalho Chehab 	   endpoint 1.  See what happens.  Only the full FX2 image can
18000c0d06caSMauro Carvalho Chehab 	   respond to this.  If this probe fails then likely the FX2
18010c0d06caSMauro Carvalho Chehab 	   firmware needs be loaded. */
18020c0d06caSMauro Carvalho Chehab 	int result;
18030c0d06caSMauro Carvalho Chehab 	LOCK_TAKE(hdw->ctl_lock); do {
18040c0d06caSMauro Carvalho Chehab 		hdw->cmd_buffer[0] = FX2CMD_GET_EEPROM_ADDR;
18050c0d06caSMauro Carvalho Chehab 		result = pvr2_send_request_ex(hdw,HZ*1,!0,
18060c0d06caSMauro Carvalho Chehab 					   hdw->cmd_buffer,1,
18070c0d06caSMauro Carvalho Chehab 					   hdw->cmd_buffer,1);
18080c0d06caSMauro Carvalho Chehab 		if (result < 0) break;
18090c0d06caSMauro Carvalho Chehab 	} while(0); LOCK_GIVE(hdw->ctl_lock);
18100c0d06caSMauro Carvalho Chehab 	if (result) {
18110c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_INIT,
18120c0d06caSMauro Carvalho Chehab 			   "Probe of device endpoint 1 result status %d",
18130c0d06caSMauro Carvalho Chehab 			   result);
18140c0d06caSMauro Carvalho Chehab 	} else {
18150c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_INIT,
18160c0d06caSMauro Carvalho Chehab 			   "Probe of device endpoint 1 succeeded");
18170c0d06caSMauro Carvalho Chehab 	}
18180c0d06caSMauro Carvalho Chehab 	return result == 0;
18190c0d06caSMauro Carvalho Chehab }
18200c0d06caSMauro Carvalho Chehab 
18210c0d06caSMauro Carvalho Chehab struct pvr2_std_hack {
18220c0d06caSMauro Carvalho Chehab 	v4l2_std_id pat;  /* Pattern to match */
18230c0d06caSMauro Carvalho Chehab 	v4l2_std_id msk;  /* Which bits we care about */
18240c0d06caSMauro Carvalho Chehab 	v4l2_std_id std;  /* What additional standards or default to set */
18250c0d06caSMauro Carvalho Chehab };
18260c0d06caSMauro Carvalho Chehab 
18270c0d06caSMauro Carvalho Chehab /* This data structure labels specific combinations of standards from
18280c0d06caSMauro Carvalho Chehab    tveeprom that we'll try to recognize.  If we recognize one, then assume
18290c0d06caSMauro Carvalho Chehab    a specified default standard to use.  This is here because tveeprom only
18300c0d06caSMauro Carvalho Chehab    tells us about available standards not the intended default standard (if
18310c0d06caSMauro Carvalho Chehab    any) for the device in question.  We guess the default based on what has
18320c0d06caSMauro Carvalho Chehab    been reported as available.  Note that this is only for guessing a
18330c0d06caSMauro Carvalho Chehab    default - which can always be overridden explicitly - and if the user
18340c0d06caSMauro Carvalho Chehab    has otherwise named a default then that default will always be used in
18350c0d06caSMauro Carvalho Chehab    place of this table. */
18360c0d06caSMauro Carvalho Chehab static const struct pvr2_std_hack std_eeprom_maps[] = {
18370c0d06caSMauro Carvalho Chehab 	{	/* PAL(B/G) */
18380c0d06caSMauro Carvalho Chehab 		.pat = V4L2_STD_B|V4L2_STD_GH,
18390c0d06caSMauro Carvalho Chehab 		.std = V4L2_STD_PAL_B|V4L2_STD_PAL_B1|V4L2_STD_PAL_G,
18400c0d06caSMauro Carvalho Chehab 	},
18410c0d06caSMauro Carvalho Chehab 	{	/* NTSC(M) */
18420c0d06caSMauro Carvalho Chehab 		.pat = V4L2_STD_MN,
18430c0d06caSMauro Carvalho Chehab 		.std = V4L2_STD_NTSC_M,
18440c0d06caSMauro Carvalho Chehab 	},
18450c0d06caSMauro Carvalho Chehab 	{	/* PAL(I) */
18460c0d06caSMauro Carvalho Chehab 		.pat = V4L2_STD_PAL_I,
18470c0d06caSMauro Carvalho Chehab 		.std = V4L2_STD_PAL_I,
18480c0d06caSMauro Carvalho Chehab 	},
18490c0d06caSMauro Carvalho Chehab 	{	/* SECAM(L/L') */
18500c0d06caSMauro Carvalho Chehab 		.pat = V4L2_STD_SECAM_L|V4L2_STD_SECAM_LC,
18510c0d06caSMauro Carvalho Chehab 		.std = V4L2_STD_SECAM_L|V4L2_STD_SECAM_LC,
18520c0d06caSMauro Carvalho Chehab 	},
18530c0d06caSMauro Carvalho Chehab 	{	/* PAL(D/D1/K) */
18540c0d06caSMauro Carvalho Chehab 		.pat = V4L2_STD_DK,
18550c0d06caSMauro Carvalho Chehab 		.std = V4L2_STD_PAL_D|V4L2_STD_PAL_D1|V4L2_STD_PAL_K,
18560c0d06caSMauro Carvalho Chehab 	},
18570c0d06caSMauro Carvalho Chehab };
18580c0d06caSMauro Carvalho Chehab 
pvr2_hdw_setup_std(struct pvr2_hdw * hdw)18590c0d06caSMauro Carvalho Chehab static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw)
18600c0d06caSMauro Carvalho Chehab {
18610c0d06caSMauro Carvalho Chehab 	char buf[40];
18620c0d06caSMauro Carvalho Chehab 	unsigned int bcnt;
18630c0d06caSMauro Carvalho Chehab 	v4l2_std_id std1,std2,std3;
18640c0d06caSMauro Carvalho Chehab 
18650c0d06caSMauro Carvalho Chehab 	std1 = get_default_standard(hdw);
18660c0d06caSMauro Carvalho Chehab 	std3 = std1 ? 0 : hdw->hdw_desc->default_std_mask;
18670c0d06caSMauro Carvalho Chehab 
18680c0d06caSMauro Carvalho Chehab 	bcnt = pvr2_std_id_to_str(buf,sizeof(buf),hdw->std_mask_eeprom);
18690c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_STD,
187096292c89SMauro Carvalho Chehab 		   "Supported video standard(s) reported available in hardware: %.*s",
18710c0d06caSMauro Carvalho Chehab 		   bcnt,buf);
18720c0d06caSMauro Carvalho Chehab 
18730c0d06caSMauro Carvalho Chehab 	hdw->std_mask_avail = hdw->std_mask_eeprom;
18740c0d06caSMauro Carvalho Chehab 
18750c0d06caSMauro Carvalho Chehab 	std2 = (std1|std3) & ~hdw->std_mask_avail;
18760c0d06caSMauro Carvalho Chehab 	if (std2) {
18770c0d06caSMauro Carvalho Chehab 		bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std2);
18780c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_STD,
187996292c89SMauro Carvalho Chehab 			   "Expanding supported video standards to include: %.*s",
18800c0d06caSMauro Carvalho Chehab 			   bcnt,buf);
18810c0d06caSMauro Carvalho Chehab 		hdw->std_mask_avail |= std2;
18820c0d06caSMauro Carvalho Chehab 	}
18830c0d06caSMauro Carvalho Chehab 
18840c0d06caSMauro Carvalho Chehab 	hdw->std_info_cur.def.type_bitmask.valid_bits = hdw->std_mask_avail;
18850c0d06caSMauro Carvalho Chehab 
18860c0d06caSMauro Carvalho Chehab 	if (std1) {
18870c0d06caSMauro Carvalho Chehab 		bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std1);
18880c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_STD,
18890c0d06caSMauro Carvalho Chehab 			   "Initial video standard forced to %.*s",
18900c0d06caSMauro Carvalho Chehab 			   bcnt,buf);
18910c0d06caSMauro Carvalho Chehab 		hdw->std_mask_cur = std1;
18920c0d06caSMauro Carvalho Chehab 		hdw->std_dirty = !0;
18930c0d06caSMauro Carvalho Chehab 		return;
18940c0d06caSMauro Carvalho Chehab 	}
18950c0d06caSMauro Carvalho Chehab 	if (std3) {
18960c0d06caSMauro Carvalho Chehab 		bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std3);
18970c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_STD,
189896292c89SMauro Carvalho Chehab 			   "Initial video standard (determined by device type): %.*s",
189996292c89SMauro Carvalho Chehab 			   bcnt, buf);
19000c0d06caSMauro Carvalho Chehab 		hdw->std_mask_cur = std3;
19010c0d06caSMauro Carvalho Chehab 		hdw->std_dirty = !0;
19020c0d06caSMauro Carvalho Chehab 		return;
19030c0d06caSMauro Carvalho Chehab 	}
19040c0d06caSMauro Carvalho Chehab 
19050c0d06caSMauro Carvalho Chehab 	{
19060c0d06caSMauro Carvalho Chehab 		unsigned int idx;
19070c0d06caSMauro Carvalho Chehab 		for (idx = 0; idx < ARRAY_SIZE(std_eeprom_maps); idx++) {
19080c0d06caSMauro Carvalho Chehab 			if (std_eeprom_maps[idx].msk ?
19090c0d06caSMauro Carvalho Chehab 			    ((std_eeprom_maps[idx].pat ^
19100c0d06caSMauro Carvalho Chehab 			     hdw->std_mask_eeprom) &
19110c0d06caSMauro Carvalho Chehab 			     std_eeprom_maps[idx].msk) :
19120c0d06caSMauro Carvalho Chehab 			    (std_eeprom_maps[idx].pat !=
19130c0d06caSMauro Carvalho Chehab 			     hdw->std_mask_eeprom)) continue;
19140c0d06caSMauro Carvalho Chehab 			bcnt = pvr2_std_id_to_str(buf,sizeof(buf),
19150c0d06caSMauro Carvalho Chehab 						  std_eeprom_maps[idx].std);
19160c0d06caSMauro Carvalho Chehab 			pvr2_trace(PVR2_TRACE_STD,
19170c0d06caSMauro Carvalho Chehab 				   "Initial video standard guessed as %.*s",
19180c0d06caSMauro Carvalho Chehab 				   bcnt,buf);
19190c0d06caSMauro Carvalho Chehab 			hdw->std_mask_cur = std_eeprom_maps[idx].std;
19200c0d06caSMauro Carvalho Chehab 			hdw->std_dirty = !0;
19210c0d06caSMauro Carvalho Chehab 			return;
19220c0d06caSMauro Carvalho Chehab 		}
19230c0d06caSMauro Carvalho Chehab 	}
19240c0d06caSMauro Carvalho Chehab 
19250c0d06caSMauro Carvalho Chehab }
19260c0d06caSMauro Carvalho Chehab 
19270c0d06caSMauro Carvalho Chehab 
pvr2_copy_i2c_addr_list(unsigned short * dst,const unsigned char * src,unsigned int dst_max)19280c0d06caSMauro Carvalho Chehab static unsigned int pvr2_copy_i2c_addr_list(
19290c0d06caSMauro Carvalho Chehab 	unsigned short *dst, const unsigned char *src,
19300c0d06caSMauro Carvalho Chehab 	unsigned int dst_max)
19310c0d06caSMauro Carvalho Chehab {
19320c0d06caSMauro Carvalho Chehab 	unsigned int cnt = 0;
19330c0d06caSMauro Carvalho Chehab 	if (!src) return 0;
19340c0d06caSMauro Carvalho Chehab 	while (src[cnt] && (cnt + 1) < dst_max) {
19350c0d06caSMauro Carvalho Chehab 		dst[cnt] = src[cnt];
19360c0d06caSMauro Carvalho Chehab 		cnt++;
19370c0d06caSMauro Carvalho Chehab 	}
19380c0d06caSMauro Carvalho Chehab 	dst[cnt] = I2C_CLIENT_END;
19390c0d06caSMauro Carvalho Chehab 	return cnt;
19400c0d06caSMauro Carvalho Chehab }
19410c0d06caSMauro Carvalho Chehab 
19420c0d06caSMauro Carvalho Chehab 
pvr2_hdw_cx25840_vbi_hack(struct pvr2_hdw * hdw)19430c0d06caSMauro Carvalho Chehab static void pvr2_hdw_cx25840_vbi_hack(struct pvr2_hdw *hdw)
19440c0d06caSMauro Carvalho Chehab {
19450c0d06caSMauro Carvalho Chehab 	/*
19460c0d06caSMauro Carvalho Chehab 	  Mike Isely <isely@pobox.com> 19-Nov-2006 - This bit of nuttiness
19470c0d06caSMauro Carvalho Chehab 	  for cx25840 causes that module to correctly set up its video
19480c0d06caSMauro Carvalho Chehab 	  scaling.  This is really a problem in the cx25840 module itself,
19490c0d06caSMauro Carvalho Chehab 	  but we work around it here.  The problem has not been seen in
19500c0d06caSMauro Carvalho Chehab 	  ivtv because there VBI is supported and set up.  We don't do VBI
19510c0d06caSMauro Carvalho Chehab 	  here (at least not yet) and thus we never attempted to even set
19520c0d06caSMauro Carvalho Chehab 	  it up.
19530c0d06caSMauro Carvalho Chehab 	*/
19540c0d06caSMauro Carvalho Chehab 	struct v4l2_format fmt;
19550c0d06caSMauro Carvalho Chehab 	if (hdw->decoder_client_id != PVR2_CLIENT_ID_CX25840) {
19560c0d06caSMauro Carvalho Chehab 		/* We're not using a cx25840 so don't enable the hack */
19570c0d06caSMauro Carvalho Chehab 		return;
19580c0d06caSMauro Carvalho Chehab 	}
19590c0d06caSMauro Carvalho Chehab 
19600c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_INIT,
196196292c89SMauro Carvalho Chehab 		   "Module ID %u: Executing cx25840 VBI hack",
19620c0d06caSMauro Carvalho Chehab 		   hdw->decoder_client_id);
19630c0d06caSMauro Carvalho Chehab 	memset(&fmt, 0, sizeof(fmt));
19640c0d06caSMauro Carvalho Chehab 	fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
19650c0d06caSMauro Carvalho Chehab 	fmt.fmt.sliced.service_lines[0][21] = V4L2_SLICED_CAPTION_525;
19660c0d06caSMauro Carvalho Chehab 	fmt.fmt.sliced.service_lines[1][21] = V4L2_SLICED_CAPTION_525;
19670c0d06caSMauro Carvalho Chehab 	v4l2_device_call_all(&hdw->v4l2_dev, hdw->decoder_client_id,
19680c0d06caSMauro Carvalho Chehab 			     vbi, s_sliced_fmt, &fmt.fmt.sliced);
19690c0d06caSMauro Carvalho Chehab }
19700c0d06caSMauro Carvalho Chehab 
19710c0d06caSMauro Carvalho Chehab 
pvr2_hdw_load_subdev(struct pvr2_hdw * hdw,const struct pvr2_device_client_desc * cd)19720c0d06caSMauro Carvalho Chehab static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw,
19730c0d06caSMauro Carvalho Chehab 				const struct pvr2_device_client_desc *cd)
19740c0d06caSMauro Carvalho Chehab {
19750c0d06caSMauro Carvalho Chehab 	const char *fname;
19760c0d06caSMauro Carvalho Chehab 	unsigned char mid;
19770c0d06caSMauro Carvalho Chehab 	struct v4l2_subdev *sd;
19780c0d06caSMauro Carvalho Chehab 	unsigned int i2ccnt;
19790c0d06caSMauro Carvalho Chehab 	const unsigned char *p;
19800c0d06caSMauro Carvalho Chehab 	/* Arbitrary count - max # i2c addresses we will probe */
19810c0d06caSMauro Carvalho Chehab 	unsigned short i2caddr[25];
19820c0d06caSMauro Carvalho Chehab 
19830c0d06caSMauro Carvalho Chehab 	mid = cd->module_id;
19840c0d06caSMauro Carvalho Chehab 	fname = (mid < ARRAY_SIZE(module_names)) ? module_names[mid] : NULL;
19850c0d06caSMauro Carvalho Chehab 	if (!fname) {
19860c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
198796292c89SMauro Carvalho Chehab 			   "Module ID %u for device %s has no name?  The driver might have a configuration problem.",
19880c0d06caSMauro Carvalho Chehab 			   mid,
19890c0d06caSMauro Carvalho Chehab 			   hdw->hdw_desc->description);
19900c0d06caSMauro Carvalho Chehab 		return -EINVAL;
19910c0d06caSMauro Carvalho Chehab 	}
19920c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_INIT,
19930c0d06caSMauro Carvalho Chehab 		   "Module ID %u (%s) for device %s being loaded...",
19940c0d06caSMauro Carvalho Chehab 		   mid, fname,
19950c0d06caSMauro Carvalho Chehab 		   hdw->hdw_desc->description);
19960c0d06caSMauro Carvalho Chehab 
19970c0d06caSMauro Carvalho Chehab 	i2ccnt = pvr2_copy_i2c_addr_list(i2caddr, cd->i2c_address_list,
19980c0d06caSMauro Carvalho Chehab 					 ARRAY_SIZE(i2caddr));
19990c0d06caSMauro Carvalho Chehab 	if (!i2ccnt && ((p = (mid < ARRAY_SIZE(module_i2c_addresses)) ?
20000c0d06caSMauro Carvalho Chehab 			 module_i2c_addresses[mid] : NULL) != NULL)) {
20010c0d06caSMauro Carvalho Chehab 		/* Second chance: Try default i2c address list */
20020c0d06caSMauro Carvalho Chehab 		i2ccnt = pvr2_copy_i2c_addr_list(i2caddr, p,
20030c0d06caSMauro Carvalho Chehab 						 ARRAY_SIZE(i2caddr));
20040c0d06caSMauro Carvalho Chehab 		if (i2ccnt) {
20050c0d06caSMauro Carvalho Chehab 			pvr2_trace(PVR2_TRACE_INIT,
200696292c89SMauro Carvalho Chehab 				   "Module ID %u: Using default i2c address list",
20070c0d06caSMauro Carvalho Chehab 				   mid);
20080c0d06caSMauro Carvalho Chehab 		}
20090c0d06caSMauro Carvalho Chehab 	}
20100c0d06caSMauro Carvalho Chehab 
20110c0d06caSMauro Carvalho Chehab 	if (!i2ccnt) {
20120c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
201396292c89SMauro Carvalho Chehab 			   "Module ID %u (%s) for device %s: No i2c addresses.	The driver might have a configuration problem.",
20140c0d06caSMauro Carvalho Chehab 			   mid, fname, hdw->hdw_desc->description);
20150c0d06caSMauro Carvalho Chehab 		return -EINVAL;
20160c0d06caSMauro Carvalho Chehab 	}
20170c0d06caSMauro Carvalho Chehab 
20180c0d06caSMauro Carvalho Chehab 	if (i2ccnt == 1) {
20190c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_INIT,
202096292c89SMauro Carvalho Chehab 			   "Module ID %u: Setting up with specified i2c address 0x%x",
20210c0d06caSMauro Carvalho Chehab 			   mid, i2caddr[0]);
20220c0d06caSMauro Carvalho Chehab 		sd = v4l2_i2c_new_subdev(&hdw->v4l2_dev, &hdw->i2c_adap,
20230c0d06caSMauro Carvalho Chehab 					 fname, i2caddr[0], NULL);
20240c0d06caSMauro Carvalho Chehab 	} else {
20250c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_INIT,
202696292c89SMauro Carvalho Chehab 			   "Module ID %u: Setting up with address probe list",
20270c0d06caSMauro Carvalho Chehab 			   mid);
20280c0d06caSMauro Carvalho Chehab 		sd = v4l2_i2c_new_subdev(&hdw->v4l2_dev, &hdw->i2c_adap,
20290c0d06caSMauro Carvalho Chehab 					 fname, 0, i2caddr);
20300c0d06caSMauro Carvalho Chehab 	}
20310c0d06caSMauro Carvalho Chehab 
20320c0d06caSMauro Carvalho Chehab 	if (!sd) {
20330c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
203496292c89SMauro Carvalho Chehab 			   "Module ID %u (%s) for device %s failed to load.  Possible missing sub-device kernel module or initialization failure within module.",
20350c0d06caSMauro Carvalho Chehab 			   mid, fname, hdw->hdw_desc->description);
20360c0d06caSMauro Carvalho Chehab 		return -EIO;
20370c0d06caSMauro Carvalho Chehab 	}
20380c0d06caSMauro Carvalho Chehab 
20390c0d06caSMauro Carvalho Chehab 	/* Tag this sub-device instance with the module ID we know about.
20400c0d06caSMauro Carvalho Chehab 	   In other places we'll use that tag to determine if the instance
20410c0d06caSMauro Carvalho Chehab 	   requires special handling. */
20420c0d06caSMauro Carvalho Chehab 	sd->grp_id = mid;
20430c0d06caSMauro Carvalho Chehab 
20440c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_INFO, "Attached sub-driver %s", fname);
20450c0d06caSMauro Carvalho Chehab 
20460c0d06caSMauro Carvalho Chehab 
20470c0d06caSMauro Carvalho Chehab 	/* client-specific setup... */
20480c0d06caSMauro Carvalho Chehab 	switch (mid) {
20490c0d06caSMauro Carvalho Chehab 	case PVR2_CLIENT_ID_CX25840:
20500c0d06caSMauro Carvalho Chehab 	case PVR2_CLIENT_ID_SAA7115:
20510c0d06caSMauro Carvalho Chehab 		hdw->decoder_client_id = mid;
20520c0d06caSMauro Carvalho Chehab 		break;
20530c0d06caSMauro Carvalho Chehab 	default: break;
20540c0d06caSMauro Carvalho Chehab 	}
20550c0d06caSMauro Carvalho Chehab 
20560c0d06caSMauro Carvalho Chehab 	return 0;
20570c0d06caSMauro Carvalho Chehab }
20580c0d06caSMauro Carvalho Chehab 
20590c0d06caSMauro Carvalho Chehab 
pvr2_hdw_load_modules(struct pvr2_hdw * hdw)20600c0d06caSMauro Carvalho Chehab static void pvr2_hdw_load_modules(struct pvr2_hdw *hdw)
20610c0d06caSMauro Carvalho Chehab {
20620c0d06caSMauro Carvalho Chehab 	unsigned int idx;
20630c0d06caSMauro Carvalho Chehab 	const struct pvr2_string_table *cm;
20640c0d06caSMauro Carvalho Chehab 	const struct pvr2_device_client_table *ct;
20650c0d06caSMauro Carvalho Chehab 	int okFl = !0;
20660c0d06caSMauro Carvalho Chehab 
20670c0d06caSMauro Carvalho Chehab 	cm = &hdw->hdw_desc->client_modules;
20680c0d06caSMauro Carvalho Chehab 	for (idx = 0; idx < cm->cnt; idx++) {
20690c0d06caSMauro Carvalho Chehab 		request_module(cm->lst[idx]);
20700c0d06caSMauro Carvalho Chehab 	}
20710c0d06caSMauro Carvalho Chehab 
20720c0d06caSMauro Carvalho Chehab 	ct = &hdw->hdw_desc->client_table;
20730c0d06caSMauro Carvalho Chehab 	for (idx = 0; idx < ct->cnt; idx++) {
20740c0d06caSMauro Carvalho Chehab 		if (pvr2_hdw_load_subdev(hdw, &ct->lst[idx]) < 0) okFl = 0;
20750c0d06caSMauro Carvalho Chehab 	}
20760c0d06caSMauro Carvalho Chehab 	if (!okFl) {
20770c0d06caSMauro Carvalho Chehab 		hdw->flag_modulefail = !0;
20780c0d06caSMauro Carvalho Chehab 		pvr2_hdw_render_useless(hdw);
20790c0d06caSMauro Carvalho Chehab 	}
20800c0d06caSMauro Carvalho Chehab }
20810c0d06caSMauro Carvalho Chehab 
20820c0d06caSMauro Carvalho Chehab 
pvr2_hdw_setup_low(struct pvr2_hdw * hdw)20830c0d06caSMauro Carvalho Chehab static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
20840c0d06caSMauro Carvalho Chehab {
20850c0d06caSMauro Carvalho Chehab 	int ret;
20860c0d06caSMauro Carvalho Chehab 	unsigned int idx;
20870c0d06caSMauro Carvalho Chehab 	struct pvr2_ctrl *cptr;
20880c0d06caSMauro Carvalho Chehab 	int reloadFl = 0;
20890c0d06caSMauro Carvalho Chehab 	if (hdw->hdw_desc->fx2_firmware.cnt) {
20900c0d06caSMauro Carvalho Chehab 		if (!reloadFl) {
20910c0d06caSMauro Carvalho Chehab 			reloadFl =
20920c0d06caSMauro Carvalho Chehab 				(hdw->usb_intf->cur_altsetting->desc.bNumEndpoints
20930c0d06caSMauro Carvalho Chehab 				 == 0);
20940c0d06caSMauro Carvalho Chehab 			if (reloadFl) {
20950c0d06caSMauro Carvalho Chehab 				pvr2_trace(PVR2_TRACE_INIT,
209696292c89SMauro Carvalho Chehab 					   "USB endpoint config looks strange; possibly firmware needs to be loaded");
20970c0d06caSMauro Carvalho Chehab 			}
20980c0d06caSMauro Carvalho Chehab 		}
20990c0d06caSMauro Carvalho Chehab 		if (!reloadFl) {
21000c0d06caSMauro Carvalho Chehab 			reloadFl = !pvr2_hdw_check_firmware(hdw);
21010c0d06caSMauro Carvalho Chehab 			if (reloadFl) {
21020c0d06caSMauro Carvalho Chehab 				pvr2_trace(PVR2_TRACE_INIT,
210396292c89SMauro Carvalho Chehab 					   "Check for FX2 firmware failed; possibly firmware needs to be loaded");
21040c0d06caSMauro Carvalho Chehab 			}
21050c0d06caSMauro Carvalho Chehab 		}
21060c0d06caSMauro Carvalho Chehab 		if (reloadFl) {
21070c0d06caSMauro Carvalho Chehab 			if (pvr2_upload_firmware1(hdw) != 0) {
21080c0d06caSMauro Carvalho Chehab 				pvr2_trace(PVR2_TRACE_ERROR_LEGS,
21090c0d06caSMauro Carvalho Chehab 					   "Failure uploading firmware1");
21100c0d06caSMauro Carvalho Chehab 			}
21110c0d06caSMauro Carvalho Chehab 			return;
21120c0d06caSMauro Carvalho Chehab 		}
21130c0d06caSMauro Carvalho Chehab 	}
21140c0d06caSMauro Carvalho Chehab 	hdw->fw1_state = FW1_STATE_OK;
21150c0d06caSMauro Carvalho Chehab 
21160c0d06caSMauro Carvalho Chehab 	if (!pvr2_hdw_dev_ok(hdw)) return;
21170c0d06caSMauro Carvalho Chehab 
21180c0d06caSMauro Carvalho Chehab 	hdw->force_dirty = !0;
21190c0d06caSMauro Carvalho Chehab 
21200c0d06caSMauro Carvalho Chehab 	if (!hdw->hdw_desc->flag_no_powerup) {
21210c0d06caSMauro Carvalho Chehab 		pvr2_hdw_cmd_powerup(hdw);
21220c0d06caSMauro Carvalho Chehab 		if (!pvr2_hdw_dev_ok(hdw)) return;
21230c0d06caSMauro Carvalho Chehab 	}
21240c0d06caSMauro Carvalho Chehab 
21250c0d06caSMauro Carvalho Chehab 	/* Take the IR chip out of reset, if appropriate */
21260c0d06caSMauro Carvalho Chehab 	if (hdw->ir_scheme_active == PVR2_IR_SCHEME_ZILOG) {
21270c0d06caSMauro Carvalho Chehab 		pvr2_issue_simple_cmd(hdw,
21280c0d06caSMauro Carvalho Chehab 				      FX2CMD_HCW_ZILOG_RESET |
21290c0d06caSMauro Carvalho Chehab 				      (1 << 8) |
21300c0d06caSMauro Carvalho Chehab 				      ((0) << 16));
21310c0d06caSMauro Carvalho Chehab 	}
21320c0d06caSMauro Carvalho Chehab 
2133dd60bf43SBrad Love 	/* This step MUST happen after the earlier powerup step */
21340c0d06caSMauro Carvalho Chehab 	pvr2_i2c_core_init(hdw);
21350c0d06caSMauro Carvalho Chehab 	if (!pvr2_hdw_dev_ok(hdw)) return;
21360c0d06caSMauro Carvalho Chehab 
2137dd60bf43SBrad Love 	/* Reset demod only on Hauppauge 160xxx platform */
2138dd60bf43SBrad Love 	if (le16_to_cpu(hdw->usb_dev->descriptor.idVendor) == 0x2040 &&
2139dd60bf43SBrad Love 	    (le16_to_cpu(hdw->usb_dev->descriptor.idProduct) == 0x7502 ||
2140dd60bf43SBrad Love 	     le16_to_cpu(hdw->usb_dev->descriptor.idProduct) == 0x7510)) {
2141dd60bf43SBrad Love 		pr_info("%s(): resetting 160xxx demod\n", __func__);
2142dd60bf43SBrad Love 		/* TODO: not sure this is proper place to reset once only */
2143dd60bf43SBrad Love 		pvr2_issue_simple_cmd(hdw,
2144dd60bf43SBrad Love 				      FX2CMD_HCW_DEMOD_RESET_PIN |
2145dd60bf43SBrad Love 				      (1 << 8) |
2146dd60bf43SBrad Love 				      ((0) << 16));
2147dd60bf43SBrad Love 		usleep_range(10000, 10500);
2148dd60bf43SBrad Love 		pvr2_issue_simple_cmd(hdw,
2149dd60bf43SBrad Love 				      FX2CMD_HCW_DEMOD_RESET_PIN |
2150dd60bf43SBrad Love 				      (1 << 8) |
2151dd60bf43SBrad Love 				      ((1) << 16));
2152dd60bf43SBrad Love 		usleep_range(10000, 10500);
2153dd60bf43SBrad Love 	}
2154dd60bf43SBrad Love 
21550c0d06caSMauro Carvalho Chehab 	pvr2_hdw_load_modules(hdw);
21560c0d06caSMauro Carvalho Chehab 	if (!pvr2_hdw_dev_ok(hdw)) return;
21570c0d06caSMauro Carvalho Chehab 
21580c0d06caSMauro Carvalho Chehab 	v4l2_device_call_all(&hdw->v4l2_dev, 0, core, load_fw);
21590c0d06caSMauro Carvalho Chehab 
21600c0d06caSMauro Carvalho Chehab 	for (idx = 0; idx < CTRLDEF_COUNT; idx++) {
21610c0d06caSMauro Carvalho Chehab 		cptr = hdw->controls + idx;
21620c0d06caSMauro Carvalho Chehab 		if (cptr->info->skip_init) continue;
21630c0d06caSMauro Carvalho Chehab 		if (!cptr->info->set_value) continue;
21640c0d06caSMauro Carvalho Chehab 		cptr->info->set_value(cptr,~0,cptr->info->default_value);
21650c0d06caSMauro Carvalho Chehab 	}
21660c0d06caSMauro Carvalho Chehab 
21670c0d06caSMauro Carvalho Chehab 	pvr2_hdw_cx25840_vbi_hack(hdw);
21680c0d06caSMauro Carvalho Chehab 
21690c0d06caSMauro Carvalho Chehab 	/* Set up special default values for the television and radio
21700c0d06caSMauro Carvalho Chehab 	   frequencies here.  It's not really important what these defaults
21710c0d06caSMauro Carvalho Chehab 	   are, but I set them to something usable in the Chicago area just
21720c0d06caSMauro Carvalho Chehab 	   to make driver testing a little easier. */
21730c0d06caSMauro Carvalho Chehab 
21740c0d06caSMauro Carvalho Chehab 	hdw->freqValTelevision = default_tv_freq;
21750c0d06caSMauro Carvalho Chehab 	hdw->freqValRadio = default_radio_freq;
21760c0d06caSMauro Carvalho Chehab 
21770c0d06caSMauro Carvalho Chehab 	// Do not use pvr2_reset_ctl_endpoints() here.  It is not
21780c0d06caSMauro Carvalho Chehab 	// thread-safe against the normal pvr2_send_request() mechanism.
21790c0d06caSMauro Carvalho Chehab 	// (We should make it thread safe).
21800c0d06caSMauro Carvalho Chehab 
21810c0d06caSMauro Carvalho Chehab 	if (hdw->hdw_desc->flag_has_hauppauge_rom) {
21820c0d06caSMauro Carvalho Chehab 		ret = pvr2_hdw_get_eeprom_addr(hdw);
21830c0d06caSMauro Carvalho Chehab 		if (!pvr2_hdw_dev_ok(hdw)) return;
21840c0d06caSMauro Carvalho Chehab 		if (ret < 0) {
21850c0d06caSMauro Carvalho Chehab 			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
218696292c89SMauro Carvalho Chehab 				   "Unable to determine location of eeprom, skipping");
21870c0d06caSMauro Carvalho Chehab 		} else {
21880c0d06caSMauro Carvalho Chehab 			hdw->eeprom_addr = ret;
21890c0d06caSMauro Carvalho Chehab 			pvr2_eeprom_analyze(hdw);
21900c0d06caSMauro Carvalho Chehab 			if (!pvr2_hdw_dev_ok(hdw)) return;
21910c0d06caSMauro Carvalho Chehab 		}
21920c0d06caSMauro Carvalho Chehab 	} else {
21930c0d06caSMauro Carvalho Chehab 		hdw->tuner_type = hdw->hdw_desc->default_tuner_type;
21940c0d06caSMauro Carvalho Chehab 		hdw->tuner_updated = !0;
21950c0d06caSMauro Carvalho Chehab 		hdw->std_mask_eeprom = V4L2_STD_ALL;
21960c0d06caSMauro Carvalho Chehab 	}
21970c0d06caSMauro Carvalho Chehab 
21980c0d06caSMauro Carvalho Chehab 	if (hdw->serial_number) {
21990c0d06caSMauro Carvalho Chehab 		idx = scnprintf(hdw->identifier, sizeof(hdw->identifier) - 1,
22000c0d06caSMauro Carvalho Chehab 				"sn-%lu", hdw->serial_number);
22010c0d06caSMauro Carvalho Chehab 	} else if (hdw->unit_number >= 0) {
22020c0d06caSMauro Carvalho Chehab 		idx = scnprintf(hdw->identifier, sizeof(hdw->identifier) - 1,
22030c0d06caSMauro Carvalho Chehab 				"unit-%c",
22040c0d06caSMauro Carvalho Chehab 				hdw->unit_number + 'a');
22050c0d06caSMauro Carvalho Chehab 	} else {
22060c0d06caSMauro Carvalho Chehab 		idx = scnprintf(hdw->identifier, sizeof(hdw->identifier) - 1,
22070c0d06caSMauro Carvalho Chehab 				"unit-??");
22080c0d06caSMauro Carvalho Chehab 	}
22090c0d06caSMauro Carvalho Chehab 	hdw->identifier[idx] = 0;
22100c0d06caSMauro Carvalho Chehab 
22110c0d06caSMauro Carvalho Chehab 	pvr2_hdw_setup_std(hdw);
22120c0d06caSMauro Carvalho Chehab 
22130c0d06caSMauro Carvalho Chehab 	if (!get_default_tuner_type(hdw)) {
22140c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_INIT,
22150c0d06caSMauro Carvalho Chehab 			   "pvr2_hdw_setup: Tuner type overridden to %d",
22160c0d06caSMauro Carvalho Chehab 			   hdw->tuner_type);
22170c0d06caSMauro Carvalho Chehab 	}
22180c0d06caSMauro Carvalho Chehab 
22190c0d06caSMauro Carvalho Chehab 
22200c0d06caSMauro Carvalho Chehab 	if (!pvr2_hdw_dev_ok(hdw)) return;
22210c0d06caSMauro Carvalho Chehab 
22220c0d06caSMauro Carvalho Chehab 	if (hdw->hdw_desc->signal_routing_scheme ==
22230c0d06caSMauro Carvalho Chehab 	    PVR2_ROUTING_SCHEME_GOTVIEW) {
22240c0d06caSMauro Carvalho Chehab 		/* Ensure that GPIO 11 is set to output for GOTVIEW
22250c0d06caSMauro Carvalho Chehab 		   hardware. */
22260c0d06caSMauro Carvalho Chehab 		pvr2_hdw_gpio_chg_dir(hdw,(1 << 11),~0);
22270c0d06caSMauro Carvalho Chehab 	}
22280c0d06caSMauro Carvalho Chehab 
22290c0d06caSMauro Carvalho Chehab 	pvr2_hdw_commit_setup(hdw);
22300c0d06caSMauro Carvalho Chehab 
22310c0d06caSMauro Carvalho Chehab 	hdw->vid_stream = pvr2_stream_create();
22320c0d06caSMauro Carvalho Chehab 	if (!pvr2_hdw_dev_ok(hdw)) return;
22330c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_INIT,
22340c0d06caSMauro Carvalho Chehab 		   "pvr2_hdw_setup: video stream is %p",hdw->vid_stream);
22350c0d06caSMauro Carvalho Chehab 	if (hdw->vid_stream) {
22360c0d06caSMauro Carvalho Chehab 		idx = get_default_error_tolerance(hdw);
22370c0d06caSMauro Carvalho Chehab 		if (idx) {
22380c0d06caSMauro Carvalho Chehab 			pvr2_trace(PVR2_TRACE_INIT,
223996292c89SMauro Carvalho Chehab 				   "pvr2_hdw_setup: video stream %p setting tolerance %u",
22400c0d06caSMauro Carvalho Chehab 				   hdw->vid_stream,idx);
22410c0d06caSMauro Carvalho Chehab 		}
22420c0d06caSMauro Carvalho Chehab 		pvr2_stream_setup(hdw->vid_stream,hdw->usb_dev,
22430c0d06caSMauro Carvalho Chehab 				  PVR2_VID_ENDPOINT,idx);
22440c0d06caSMauro Carvalho Chehab 	}
22450c0d06caSMauro Carvalho Chehab 
22460c0d06caSMauro Carvalho Chehab 	if (!pvr2_hdw_dev_ok(hdw)) return;
22470c0d06caSMauro Carvalho Chehab 
22480c0d06caSMauro Carvalho Chehab 	hdw->flag_init_ok = !0;
22490c0d06caSMauro Carvalho Chehab 
22500c0d06caSMauro Carvalho Chehab 	pvr2_hdw_state_sched(hdw);
22510c0d06caSMauro Carvalho Chehab }
22520c0d06caSMauro Carvalho Chehab 
22530c0d06caSMauro Carvalho Chehab 
22540c0d06caSMauro Carvalho Chehab /* Set up the structure and attempt to put the device into a usable state.
22550c0d06caSMauro Carvalho Chehab    This can be a time-consuming operation, which is why it is not done
22560c0d06caSMauro Carvalho Chehab    internally as part of the create() step. */
pvr2_hdw_setup(struct pvr2_hdw * hdw)22570c0d06caSMauro Carvalho Chehab static void pvr2_hdw_setup(struct pvr2_hdw *hdw)
22580c0d06caSMauro Carvalho Chehab {
22590c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_setup(hdw=%p) begin",hdw);
22600c0d06caSMauro Carvalho Chehab 	do {
22610c0d06caSMauro Carvalho Chehab 		pvr2_hdw_setup_low(hdw);
22620c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_INIT,
22630c0d06caSMauro Carvalho Chehab 			   "pvr2_hdw_setup(hdw=%p) done, ok=%d init_ok=%d",
22640c0d06caSMauro Carvalho Chehab 			   hdw,pvr2_hdw_dev_ok(hdw),hdw->flag_init_ok);
22650c0d06caSMauro Carvalho Chehab 		if (pvr2_hdw_dev_ok(hdw)) {
22660c0d06caSMauro Carvalho Chehab 			if (hdw->flag_init_ok) {
22670c0d06caSMauro Carvalho Chehab 				pvr2_trace(
22680c0d06caSMauro Carvalho Chehab 					PVR2_TRACE_INFO,
226996292c89SMauro Carvalho Chehab 					"Device initialization completed successfully.");
22700c0d06caSMauro Carvalho Chehab 				break;
22710c0d06caSMauro Carvalho Chehab 			}
22720c0d06caSMauro Carvalho Chehab 			if (hdw->fw1_state == FW1_STATE_RELOAD) {
22730c0d06caSMauro Carvalho Chehab 				pvr2_trace(
22740c0d06caSMauro Carvalho Chehab 					PVR2_TRACE_INFO,
227596292c89SMauro Carvalho Chehab 					"Device microcontroller firmware (re)loaded; it should now reset and reconnect.");
22760c0d06caSMauro Carvalho Chehab 				break;
22770c0d06caSMauro Carvalho Chehab 			}
22780c0d06caSMauro Carvalho Chehab 			pvr2_trace(
22790c0d06caSMauro Carvalho Chehab 				PVR2_TRACE_ERROR_LEGS,
22800c0d06caSMauro Carvalho Chehab 				"Device initialization was not successful.");
22810c0d06caSMauro Carvalho Chehab 			if (hdw->fw1_state == FW1_STATE_MISSING) {
22820c0d06caSMauro Carvalho Chehab 				pvr2_trace(
22830c0d06caSMauro Carvalho Chehab 					PVR2_TRACE_ERROR_LEGS,
228496292c89SMauro Carvalho Chehab 					"Giving up since device microcontroller firmware appears to be missing.");
22850c0d06caSMauro Carvalho Chehab 				break;
22860c0d06caSMauro Carvalho Chehab 			}
22870c0d06caSMauro Carvalho Chehab 		}
22880c0d06caSMauro Carvalho Chehab 		if (hdw->flag_modulefail) {
22890c0d06caSMauro Carvalho Chehab 			pvr2_trace(
22900c0d06caSMauro Carvalho Chehab 				PVR2_TRACE_ERROR_LEGS,
229196292c89SMauro Carvalho Chehab 				"***WARNING*** pvrusb2 driver initialization failed due to the failure of one or more sub-device kernel modules.");
22920c0d06caSMauro Carvalho Chehab 			pvr2_trace(
22930c0d06caSMauro Carvalho Chehab 				PVR2_TRACE_ERROR_LEGS,
229496292c89SMauro Carvalho Chehab 				"You need to resolve the failing condition before this driver can function.  There should be some earlier messages giving more information about the problem.");
22950c0d06caSMauro Carvalho Chehab 			break;
22960c0d06caSMauro Carvalho Chehab 		}
22970c0d06caSMauro Carvalho Chehab 		if (procreload) {
22980c0d06caSMauro Carvalho Chehab 			pvr2_trace(
22990c0d06caSMauro Carvalho Chehab 				PVR2_TRACE_ERROR_LEGS,
230096292c89SMauro Carvalho Chehab 				"Attempting pvrusb2 recovery by reloading primary firmware.");
23010c0d06caSMauro Carvalho Chehab 			pvr2_trace(
23020c0d06caSMauro Carvalho Chehab 				PVR2_TRACE_ERROR_LEGS,
230396292c89SMauro Carvalho Chehab 				"If this works, device should disconnect and reconnect in a sane state.");
23040c0d06caSMauro Carvalho Chehab 			hdw->fw1_state = FW1_STATE_UNKNOWN;
23050c0d06caSMauro Carvalho Chehab 			pvr2_upload_firmware1(hdw);
23060c0d06caSMauro Carvalho Chehab 		} else {
23070c0d06caSMauro Carvalho Chehab 			pvr2_trace(
23080c0d06caSMauro Carvalho Chehab 				PVR2_TRACE_ERROR_LEGS,
230996292c89SMauro Carvalho Chehab 				"***WARNING*** pvrusb2 device hardware appears to be jammed and I can't clear it.");
23100c0d06caSMauro Carvalho Chehab 			pvr2_trace(
23110c0d06caSMauro Carvalho Chehab 				PVR2_TRACE_ERROR_LEGS,
231296292c89SMauro Carvalho Chehab 				"You might need to power cycle the pvrusb2 device in order to recover.");
23130c0d06caSMauro Carvalho Chehab 		}
23140c0d06caSMauro Carvalho Chehab 	} while (0);
23150c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_setup(hdw=%p) end",hdw);
23160c0d06caSMauro Carvalho Chehab }
23170c0d06caSMauro Carvalho Chehab 
23180c0d06caSMauro Carvalho Chehab 
23190c0d06caSMauro Carvalho Chehab /* Perform second stage initialization.  Set callback pointer first so that
23200c0d06caSMauro Carvalho Chehab    we can avoid a possible initialization race (if the kernel thread runs
23210c0d06caSMauro Carvalho Chehab    before the callback has been set). */
pvr2_hdw_initialize(struct pvr2_hdw * hdw,void (* callback_func)(void *),void * callback_data)23220c0d06caSMauro Carvalho Chehab int pvr2_hdw_initialize(struct pvr2_hdw *hdw,
23230c0d06caSMauro Carvalho Chehab 			void (*callback_func)(void *),
23240c0d06caSMauro Carvalho Chehab 			void *callback_data)
23250c0d06caSMauro Carvalho Chehab {
23260c0d06caSMauro Carvalho Chehab 	LOCK_TAKE(hdw->big_lock); do {
23270c0d06caSMauro Carvalho Chehab 		if (hdw->flag_disconnected) {
23280c0d06caSMauro Carvalho Chehab 			/* Handle a race here: If we're already
23290c0d06caSMauro Carvalho Chehab 			   disconnected by this point, then give up.  If we
23300c0d06caSMauro Carvalho Chehab 			   get past this then we'll remain connected for
23310c0d06caSMauro Carvalho Chehab 			   the duration of initialization since the entire
23320c0d06caSMauro Carvalho Chehab 			   initialization sequence is now protected by the
23330c0d06caSMauro Carvalho Chehab 			   big_lock. */
23340c0d06caSMauro Carvalho Chehab 			break;
23350c0d06caSMauro Carvalho Chehab 		}
23360c0d06caSMauro Carvalho Chehab 		hdw->state_data = callback_data;
23370c0d06caSMauro Carvalho Chehab 		hdw->state_func = callback_func;
23380c0d06caSMauro Carvalho Chehab 		pvr2_hdw_setup(hdw);
23390c0d06caSMauro Carvalho Chehab 	} while (0); LOCK_GIVE(hdw->big_lock);
23400c0d06caSMauro Carvalho Chehab 	return hdw->flag_init_ok;
23410c0d06caSMauro Carvalho Chehab }
23420c0d06caSMauro Carvalho Chehab 
23430c0d06caSMauro Carvalho Chehab 
23440c0d06caSMauro Carvalho Chehab /* Create, set up, and return a structure for interacting with the
23450c0d06caSMauro Carvalho Chehab    underlying hardware.  */
pvr2_hdw_create(struct usb_interface * intf,const struct usb_device_id * devid)23460c0d06caSMauro Carvalho Chehab struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
23470c0d06caSMauro Carvalho Chehab 				 const struct usb_device_id *devid)
23480c0d06caSMauro Carvalho Chehab {
23490c0d06caSMauro Carvalho Chehab 	unsigned int idx,cnt1,cnt2,m;
23500c0d06caSMauro Carvalho Chehab 	struct pvr2_hdw *hdw = NULL;
23510c0d06caSMauro Carvalho Chehab 	int valid_std_mask;
23520c0d06caSMauro Carvalho Chehab 	struct pvr2_ctrl *cptr;
23530c0d06caSMauro Carvalho Chehab 	struct usb_device *usb_dev;
23540c0d06caSMauro Carvalho Chehab 	const struct pvr2_device_desc *hdw_desc;
23550c0d06caSMauro Carvalho Chehab 	__u8 ifnum;
23560c0d06caSMauro Carvalho Chehab 	struct v4l2_queryctrl qctrl;
23570c0d06caSMauro Carvalho Chehab 	struct pvr2_ctl_info *ciptr;
23580c0d06caSMauro Carvalho Chehab 
23590c0d06caSMauro Carvalho Chehab 	usb_dev = interface_to_usbdev(intf);
23600c0d06caSMauro Carvalho Chehab 
23610c0d06caSMauro Carvalho Chehab 	hdw_desc = (const struct pvr2_device_desc *)(devid->driver_info);
23620c0d06caSMauro Carvalho Chehab 
23630c0d06caSMauro Carvalho Chehab 	if (hdw_desc == NULL) {
236496292c89SMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_INIT, "pvr2_hdw_create: No device description pointer, unable to continue.");
2365510bb915SJulia Lawall 		pvr2_trace(PVR2_TRACE_INIT,
2366510bb915SJulia Lawall 			   "If you have a new device type, please contact Mike Isely <isely@pobox.com> to get it included in the driver");
23670c0d06caSMauro Carvalho Chehab 		goto fail;
23680c0d06caSMauro Carvalho Chehab 	}
23690c0d06caSMauro Carvalho Chehab 
23700c0d06caSMauro Carvalho Chehab 	hdw = kzalloc(sizeof(*hdw),GFP_KERNEL);
23710c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_create: hdw=%p, type \"%s\"",
23720c0d06caSMauro Carvalho Chehab 		   hdw,hdw_desc->description);
23730c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_INFO, "Hardware description: %s",
23740c0d06caSMauro Carvalho Chehab 		hdw_desc->description);
23750c0d06caSMauro Carvalho Chehab 	if (hdw_desc->flag_is_experimental) {
23760c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_INFO, "**********");
23770c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_INFO,
23781753c7c4SAndrey Konovalov 			   "***WARNING*** Support for this device (%s) is experimental.",
237996292c89SMauro Carvalho Chehab 							      hdw_desc->description);
23800c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_INFO,
238196292c89SMauro Carvalho Chehab 			   "Important functionality might not be entirely working.");
23820c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_INFO,
238396292c89SMauro Carvalho Chehab 			   "Please consider contacting the driver author to help with further stabilization of the driver.");
23840c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_INFO, "**********");
23850c0d06caSMauro Carvalho Chehab 	}
23860c0d06caSMauro Carvalho Chehab 	if (!hdw) goto fail;
23870c0d06caSMauro Carvalho Chehab 
23888da0edf2SKees Cook 	timer_setup(&hdw->quiescent_timer, pvr2_hdw_quiescent_timeout, 0);
23890c0d06caSMauro Carvalho Chehab 
23908da0edf2SKees Cook 	timer_setup(&hdw->decoder_stabilization_timer,
23918da0edf2SKees Cook 		    pvr2_hdw_decoder_stabilization_timeout, 0);
23920c0d06caSMauro Carvalho Chehab 
23938da0edf2SKees Cook 	timer_setup(&hdw->encoder_wait_timer, pvr2_hdw_encoder_wait_timeout,
23948da0edf2SKees Cook 		    0);
23950c0d06caSMauro Carvalho Chehab 
23968da0edf2SKees Cook 	timer_setup(&hdw->encoder_run_timer, pvr2_hdw_encoder_run_timeout, 0);
23970c0d06caSMauro Carvalho Chehab 
23980c0d06caSMauro Carvalho Chehab 	hdw->master_state = PVR2_STATE_DEAD;
23990c0d06caSMauro Carvalho Chehab 
24000c0d06caSMauro Carvalho Chehab 	init_waitqueue_head(&hdw->state_wait_data);
24010c0d06caSMauro Carvalho Chehab 
24020c0d06caSMauro Carvalho Chehab 	hdw->tuner_signal_stale = !0;
24030c0d06caSMauro Carvalho Chehab 	cx2341x_fill_defaults(&hdw->enc_ctl_state);
24040c0d06caSMauro Carvalho Chehab 
24050c0d06caSMauro Carvalho Chehab 	/* Calculate which inputs are OK */
24060c0d06caSMauro Carvalho Chehab 	m = 0;
24070c0d06caSMauro Carvalho Chehab 	if (hdw_desc->flag_has_analogtuner) m |= 1 << PVR2_CVAL_INPUT_TV;
24080c0d06caSMauro Carvalho Chehab 	if (hdw_desc->digital_control_scheme != PVR2_DIGITAL_SCHEME_NONE) {
24090c0d06caSMauro Carvalho Chehab 		m |= 1 << PVR2_CVAL_INPUT_DTV;
24100c0d06caSMauro Carvalho Chehab 	}
24110c0d06caSMauro Carvalho Chehab 	if (hdw_desc->flag_has_svideo) m |= 1 << PVR2_CVAL_INPUT_SVIDEO;
24120c0d06caSMauro Carvalho Chehab 	if (hdw_desc->flag_has_composite) m |= 1 << PVR2_CVAL_INPUT_COMPOSITE;
24130c0d06caSMauro Carvalho Chehab 	if (hdw_desc->flag_has_fmradio) m |= 1 << PVR2_CVAL_INPUT_RADIO;
24140c0d06caSMauro Carvalho Chehab 	hdw->input_avail_mask = m;
24150c0d06caSMauro Carvalho Chehab 	hdw->input_allowed_mask = hdw->input_avail_mask;
24160c0d06caSMauro Carvalho Chehab 
24170c0d06caSMauro Carvalho Chehab 	/* If not a hybrid device, pathway_state never changes.  So
24180c0d06caSMauro Carvalho Chehab 	   initialize it here to what it should forever be. */
24190c0d06caSMauro Carvalho Chehab 	if (!(hdw->input_avail_mask & (1 << PVR2_CVAL_INPUT_DTV))) {
24200c0d06caSMauro Carvalho Chehab 		hdw->pathway_state = PVR2_PATHWAY_ANALOG;
24210c0d06caSMauro Carvalho Chehab 	} else if (!(hdw->input_avail_mask & (1 << PVR2_CVAL_INPUT_TV))) {
24220c0d06caSMauro Carvalho Chehab 		hdw->pathway_state = PVR2_PATHWAY_DIGITAL;
24230c0d06caSMauro Carvalho Chehab 	}
24240c0d06caSMauro Carvalho Chehab 
24250c0d06caSMauro Carvalho Chehab 	hdw->control_cnt = CTRLDEF_COUNT;
24260c0d06caSMauro Carvalho Chehab 	hdw->control_cnt += MPEGDEF_COUNT;
24276396bb22SKees Cook 	hdw->controls = kcalloc(hdw->control_cnt, sizeof(struct pvr2_ctrl),
24280c0d06caSMauro Carvalho Chehab 				GFP_KERNEL);
24290c0d06caSMauro Carvalho Chehab 	if (!hdw->controls) goto fail;
24300c0d06caSMauro Carvalho Chehab 	hdw->hdw_desc = hdw_desc;
24310c0d06caSMauro Carvalho Chehab 	hdw->ir_scheme_active = hdw->hdw_desc->ir_scheme;
24320c0d06caSMauro Carvalho Chehab 	for (idx = 0; idx < hdw->control_cnt; idx++) {
24330c0d06caSMauro Carvalho Chehab 		cptr = hdw->controls + idx;
24340c0d06caSMauro Carvalho Chehab 		cptr->hdw = hdw;
24350c0d06caSMauro Carvalho Chehab 	}
24360c0d06caSMauro Carvalho Chehab 	for (idx = 0; idx < 32; idx++) {
24370c0d06caSMauro Carvalho Chehab 		hdw->std_mask_ptrs[idx] = hdw->std_mask_names[idx];
24380c0d06caSMauro Carvalho Chehab 	}
24390c0d06caSMauro Carvalho Chehab 	for (idx = 0; idx < CTRLDEF_COUNT; idx++) {
24400c0d06caSMauro Carvalho Chehab 		cptr = hdw->controls + idx;
24410c0d06caSMauro Carvalho Chehab 		cptr->info = control_defs+idx;
24420c0d06caSMauro Carvalho Chehab 	}
24430c0d06caSMauro Carvalho Chehab 
24440c0d06caSMauro Carvalho Chehab 	/* Ensure that default input choice is a valid one. */
24450c0d06caSMauro Carvalho Chehab 	m = hdw->input_avail_mask;
24460c0d06caSMauro Carvalho Chehab 	if (m) for (idx = 0; idx < (sizeof(m) << 3); idx++) {
244795c52069SMauro Carvalho Chehab 		if (!((1UL << idx) & m)) continue;
24480c0d06caSMauro Carvalho Chehab 		hdw->input_val = idx;
24490c0d06caSMauro Carvalho Chehab 		break;
24500c0d06caSMauro Carvalho Chehab 	}
24510c0d06caSMauro Carvalho Chehab 
24520c0d06caSMauro Carvalho Chehab 	/* Define and configure additional controls from cx2341x module. */
24530c0d06caSMauro Carvalho Chehab 	hdw->mpeg_ctrl_info = kcalloc(MPEGDEF_COUNT,
24540c0d06caSMauro Carvalho Chehab 				      sizeof(*(hdw->mpeg_ctrl_info)),
24550c0d06caSMauro Carvalho Chehab 				      GFP_KERNEL);
24560c0d06caSMauro Carvalho Chehab 	if (!hdw->mpeg_ctrl_info) goto fail;
24570c0d06caSMauro Carvalho Chehab 	for (idx = 0; idx < MPEGDEF_COUNT; idx++) {
24580c0d06caSMauro Carvalho Chehab 		cptr = hdw->controls + idx + CTRLDEF_COUNT;
24590c0d06caSMauro Carvalho Chehab 		ciptr = &(hdw->mpeg_ctrl_info[idx].info);
24600c0d06caSMauro Carvalho Chehab 		ciptr->desc = hdw->mpeg_ctrl_info[idx].desc;
24610c0d06caSMauro Carvalho Chehab 		ciptr->name = mpeg_ids[idx].strid;
24620c0d06caSMauro Carvalho Chehab 		ciptr->v4l_id = mpeg_ids[idx].id;
24630c0d06caSMauro Carvalho Chehab 		ciptr->skip_init = !0;
24640c0d06caSMauro Carvalho Chehab 		ciptr->get_value = ctrl_cx2341x_get;
24650c0d06caSMauro Carvalho Chehab 		ciptr->get_v4lflags = ctrl_cx2341x_getv4lflags;
24660c0d06caSMauro Carvalho Chehab 		ciptr->is_dirty = ctrl_cx2341x_is_dirty;
24670c0d06caSMauro Carvalho Chehab 		if (!idx) ciptr->clear_dirty = ctrl_cx2341x_clear_dirty;
24680c0d06caSMauro Carvalho Chehab 		qctrl.id = ciptr->v4l_id;
24690c0d06caSMauro Carvalho Chehab 		cx2341x_ctrl_query(&hdw->enc_ctl_state,&qctrl);
24700c0d06caSMauro Carvalho Chehab 		if (!(qctrl.flags & V4L2_CTRL_FLAG_READ_ONLY)) {
24710c0d06caSMauro Carvalho Chehab 			ciptr->set_value = ctrl_cx2341x_set;
24720c0d06caSMauro Carvalho Chehab 		}
247385709cbfSMauro Carvalho Chehab 		strscpy(hdw->mpeg_ctrl_info[idx].desc, qctrl.name,
247485709cbfSMauro Carvalho Chehab 			sizeof(hdw->mpeg_ctrl_info[idx].desc));
24750c0d06caSMauro Carvalho Chehab 		ciptr->default_value = qctrl.default_value;
24760c0d06caSMauro Carvalho Chehab 		switch (qctrl.type) {
24770c0d06caSMauro Carvalho Chehab 		default:
24780c0d06caSMauro Carvalho Chehab 		case V4L2_CTRL_TYPE_INTEGER:
24790c0d06caSMauro Carvalho Chehab 			ciptr->type = pvr2_ctl_int;
24800c0d06caSMauro Carvalho Chehab 			ciptr->def.type_int.min_value = qctrl.minimum;
24810c0d06caSMauro Carvalho Chehab 			ciptr->def.type_int.max_value = qctrl.maximum;
24820c0d06caSMauro Carvalho Chehab 			break;
24830c0d06caSMauro Carvalho Chehab 		case V4L2_CTRL_TYPE_BOOLEAN:
24840c0d06caSMauro Carvalho Chehab 			ciptr->type = pvr2_ctl_bool;
24850c0d06caSMauro Carvalho Chehab 			break;
24860c0d06caSMauro Carvalho Chehab 		case V4L2_CTRL_TYPE_MENU:
24870c0d06caSMauro Carvalho Chehab 			ciptr->type = pvr2_ctl_enum;
24880c0d06caSMauro Carvalho Chehab 			ciptr->def.type_enum.value_names =
24890c0d06caSMauro Carvalho Chehab 				cx2341x_ctrl_get_menu(&hdw->enc_ctl_state,
24900c0d06caSMauro Carvalho Chehab 								ciptr->v4l_id);
24910c0d06caSMauro Carvalho Chehab 			for (cnt1 = 0;
24920c0d06caSMauro Carvalho Chehab 			     ciptr->def.type_enum.value_names[cnt1] != NULL;
24930c0d06caSMauro Carvalho Chehab 			     cnt1++) { }
24940c0d06caSMauro Carvalho Chehab 			ciptr->def.type_enum.count = cnt1;
24950c0d06caSMauro Carvalho Chehab 			break;
24960c0d06caSMauro Carvalho Chehab 		}
24970c0d06caSMauro Carvalho Chehab 		cptr->info = ciptr;
24980c0d06caSMauro Carvalho Chehab 	}
24990c0d06caSMauro Carvalho Chehab 
25000c0d06caSMauro Carvalho Chehab 	// Initialize control data regarding video standard masks
25010c0d06caSMauro Carvalho Chehab 	valid_std_mask = pvr2_std_get_usable();
25020c0d06caSMauro Carvalho Chehab 	for (idx = 0; idx < 32; idx++) {
250395c52069SMauro Carvalho Chehab 		if (!(valid_std_mask & (1UL << idx))) continue;
25040c0d06caSMauro Carvalho Chehab 		cnt1 = pvr2_std_id_to_str(
25050c0d06caSMauro Carvalho Chehab 			hdw->std_mask_names[idx],
25060c0d06caSMauro Carvalho Chehab 			sizeof(hdw->std_mask_names[idx])-1,
250795c52069SMauro Carvalho Chehab 			1UL << idx);
25080c0d06caSMauro Carvalho Chehab 		hdw->std_mask_names[idx][cnt1] = 0;
25090c0d06caSMauro Carvalho Chehab 	}
25100c0d06caSMauro Carvalho Chehab 	cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDAVAIL);
25110c0d06caSMauro Carvalho Chehab 	if (cptr) {
25120c0d06caSMauro Carvalho Chehab 		memcpy(&hdw->std_info_avail,cptr->info,
25130c0d06caSMauro Carvalho Chehab 		       sizeof(hdw->std_info_avail));
25140c0d06caSMauro Carvalho Chehab 		cptr->info = &hdw->std_info_avail;
25150c0d06caSMauro Carvalho Chehab 		hdw->std_info_avail.def.type_bitmask.bit_names =
25160c0d06caSMauro Carvalho Chehab 			hdw->std_mask_ptrs;
25170c0d06caSMauro Carvalho Chehab 		hdw->std_info_avail.def.type_bitmask.valid_bits =
25180c0d06caSMauro Carvalho Chehab 			valid_std_mask;
25190c0d06caSMauro Carvalho Chehab 	}
25200c0d06caSMauro Carvalho Chehab 	cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR);
25210c0d06caSMauro Carvalho Chehab 	if (cptr) {
25220c0d06caSMauro Carvalho Chehab 		memcpy(&hdw->std_info_cur,cptr->info,
25230c0d06caSMauro Carvalho Chehab 		       sizeof(hdw->std_info_cur));
25240c0d06caSMauro Carvalho Chehab 		cptr->info = &hdw->std_info_cur;
25250c0d06caSMauro Carvalho Chehab 		hdw->std_info_cur.def.type_bitmask.bit_names =
25260c0d06caSMauro Carvalho Chehab 			hdw->std_mask_ptrs;
25270c0d06caSMauro Carvalho Chehab 		hdw->std_info_cur.def.type_bitmask.valid_bits =
25280c0d06caSMauro Carvalho Chehab 			valid_std_mask;
25290c0d06caSMauro Carvalho Chehab 	}
25300c0d06caSMauro Carvalho Chehab 	cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDDETECT);
25310c0d06caSMauro Carvalho Chehab 	if (cptr) {
25320c0d06caSMauro Carvalho Chehab 		memcpy(&hdw->std_info_detect,cptr->info,
25330c0d06caSMauro Carvalho Chehab 		       sizeof(hdw->std_info_detect));
25340c0d06caSMauro Carvalho Chehab 		cptr->info = &hdw->std_info_detect;
25350c0d06caSMauro Carvalho Chehab 		hdw->std_info_detect.def.type_bitmask.bit_names =
25360c0d06caSMauro Carvalho Chehab 			hdw->std_mask_ptrs;
25370c0d06caSMauro Carvalho Chehab 		hdw->std_info_detect.def.type_bitmask.valid_bits =
25380c0d06caSMauro Carvalho Chehab 			valid_std_mask;
25390c0d06caSMauro Carvalho Chehab 	}
25400c0d06caSMauro Carvalho Chehab 
25410c0d06caSMauro Carvalho Chehab 	hdw->cropcap_stale = !0;
25420c0d06caSMauro Carvalho Chehab 	hdw->eeprom_addr = -1;
25430c0d06caSMauro Carvalho Chehab 	hdw->unit_number = -1;
25440c0d06caSMauro Carvalho Chehab 	hdw->v4l_minor_number_video = -1;
25450c0d06caSMauro Carvalho Chehab 	hdw->v4l_minor_number_vbi = -1;
25460c0d06caSMauro Carvalho Chehab 	hdw->v4l_minor_number_radio = -1;
25470c0d06caSMauro Carvalho Chehab 	hdw->ctl_write_buffer = kmalloc(PVR2_CTL_BUFFSIZE,GFP_KERNEL);
25480c0d06caSMauro Carvalho Chehab 	if (!hdw->ctl_write_buffer) goto fail;
25490c0d06caSMauro Carvalho Chehab 	hdw->ctl_read_buffer = kmalloc(PVR2_CTL_BUFFSIZE,GFP_KERNEL);
25500c0d06caSMauro Carvalho Chehab 	if (!hdw->ctl_read_buffer) goto fail;
25510c0d06caSMauro Carvalho Chehab 	hdw->ctl_write_urb = usb_alloc_urb(0,GFP_KERNEL);
25520c0d06caSMauro Carvalho Chehab 	if (!hdw->ctl_write_urb) goto fail;
25530c0d06caSMauro Carvalho Chehab 	hdw->ctl_read_urb = usb_alloc_urb(0,GFP_KERNEL);
25540c0d06caSMauro Carvalho Chehab 	if (!hdw->ctl_read_urb) goto fail;
25550c0d06caSMauro Carvalho Chehab 
25560c0d06caSMauro Carvalho Chehab 	if (v4l2_device_register(&intf->dev, &hdw->v4l2_dev) != 0) {
25570c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
25580c0d06caSMauro Carvalho Chehab 			   "Error registering with v4l core, giving up");
25590c0d06caSMauro Carvalho Chehab 		goto fail;
25600c0d06caSMauro Carvalho Chehab 	}
2561f419edd4SMauro Carvalho Chehab 	mutex_lock(&pvr2_unit_mtx);
2562f419edd4SMauro Carvalho Chehab 	do {
25630c0d06caSMauro Carvalho Chehab 		for (idx = 0; idx < PVR_NUM; idx++) {
25640c0d06caSMauro Carvalho Chehab 			if (unit_pointers[idx]) continue;
25650c0d06caSMauro Carvalho Chehab 			hdw->unit_number = idx;
25660c0d06caSMauro Carvalho Chehab 			unit_pointers[idx] = hdw;
25670c0d06caSMauro Carvalho Chehab 			break;
25680c0d06caSMauro Carvalho Chehab 		}
2569f419edd4SMauro Carvalho Chehab 	} while (0);
2570f419edd4SMauro Carvalho Chehab 	mutex_unlock(&pvr2_unit_mtx);
25710c0d06caSMauro Carvalho Chehab 
2572471bec68SPavel Skripkin 	INIT_WORK(&hdw->workpoll, pvr2_hdw_worker_poll);
2573471bec68SPavel Skripkin 
2574471bec68SPavel Skripkin 	if (hdw->unit_number == -1)
2575471bec68SPavel Skripkin 		goto fail;
2576471bec68SPavel Skripkin 
25770c0d06caSMauro Carvalho Chehab 	cnt1 = 0;
25780c0d06caSMauro Carvalho Chehab 	cnt2 = scnprintf(hdw->name+cnt1,sizeof(hdw->name)-cnt1,"pvrusb2");
25790c0d06caSMauro Carvalho Chehab 	cnt1 += cnt2;
25800c0d06caSMauro Carvalho Chehab 	if (hdw->unit_number >= 0) {
25810c0d06caSMauro Carvalho Chehab 		cnt2 = scnprintf(hdw->name+cnt1,sizeof(hdw->name)-cnt1,"_%c",
25820c0d06caSMauro Carvalho Chehab 				 ('a' + hdw->unit_number));
25830c0d06caSMauro Carvalho Chehab 		cnt1 += cnt2;
25840c0d06caSMauro Carvalho Chehab 	}
25850c0d06caSMauro Carvalho Chehab 	if (cnt1 >= sizeof(hdw->name)) cnt1 = sizeof(hdw->name)-1;
25860c0d06caSMauro Carvalho Chehab 	hdw->name[cnt1] = 0;
25870c0d06caSMauro Carvalho Chehab 
25880c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_INIT,"Driver unit number is %d, name is %s",
25890c0d06caSMauro Carvalho Chehab 		   hdw->unit_number,hdw->name);
25900c0d06caSMauro Carvalho Chehab 
25910c0d06caSMauro Carvalho Chehab 	hdw->tuner_type = -1;
25920c0d06caSMauro Carvalho Chehab 	hdw->flag_ok = !0;
25930c0d06caSMauro Carvalho Chehab 
25940c0d06caSMauro Carvalho Chehab 	hdw->usb_intf = intf;
25950c0d06caSMauro Carvalho Chehab 	hdw->usb_dev = usb_dev;
25960c0d06caSMauro Carvalho Chehab 
25970c0d06caSMauro Carvalho Chehab 	usb_make_path(hdw->usb_dev, hdw->bus_info, sizeof(hdw->bus_info));
25980c0d06caSMauro Carvalho Chehab 
25990c0d06caSMauro Carvalho Chehab 	ifnum = hdw->usb_intf->cur_altsetting->desc.bInterfaceNumber;
26000c0d06caSMauro Carvalho Chehab 	usb_set_interface(hdw->usb_dev,ifnum,0);
26010c0d06caSMauro Carvalho Chehab 
26020c0d06caSMauro Carvalho Chehab 	mutex_init(&hdw->ctl_lock_mutex);
26030c0d06caSMauro Carvalho Chehab 	mutex_init(&hdw->big_lock_mutex);
26040c0d06caSMauro Carvalho Chehab 
26050c0d06caSMauro Carvalho Chehab 	return hdw;
26060c0d06caSMauro Carvalho Chehab  fail:
26070c0d06caSMauro Carvalho Chehab 	if (hdw) {
2608292a089dSSteven Rostedt (Google) 		timer_shutdown_sync(&hdw->quiescent_timer);
2609292a089dSSteven Rostedt (Google) 		timer_shutdown_sync(&hdw->decoder_stabilization_timer);
2610292a089dSSteven Rostedt (Google) 		timer_shutdown_sync(&hdw->encoder_run_timer);
2611292a089dSSteven Rostedt (Google) 		timer_shutdown_sync(&hdw->encoder_wait_timer);
261229a3006eSBhaktipriya Shridhar 		flush_work(&hdw->workpoll);
2613945a9a8eSDongliang Mu 		v4l2_device_unregister(&hdw->v4l2_dev);
26140c0d06caSMauro Carvalho Chehab 		usb_free_urb(hdw->ctl_read_urb);
26150c0d06caSMauro Carvalho Chehab 		usb_free_urb(hdw->ctl_write_urb);
26160c0d06caSMauro Carvalho Chehab 		kfree(hdw->ctl_read_buffer);
26170c0d06caSMauro Carvalho Chehab 		kfree(hdw->ctl_write_buffer);
26180c0d06caSMauro Carvalho Chehab 		kfree(hdw->controls);
26190c0d06caSMauro Carvalho Chehab 		kfree(hdw->mpeg_ctrl_info);
26200c0d06caSMauro Carvalho Chehab 		kfree(hdw);
26210c0d06caSMauro Carvalho Chehab 	}
26220c0d06caSMauro Carvalho Chehab 	return NULL;
26230c0d06caSMauro Carvalho Chehab }
26240c0d06caSMauro Carvalho Chehab 
26250c0d06caSMauro Carvalho Chehab 
26260c0d06caSMauro Carvalho Chehab /* Remove _all_ associations between this driver and the underlying USB
26270c0d06caSMauro Carvalho Chehab    layer. */
pvr2_hdw_remove_usb_stuff(struct pvr2_hdw * hdw)26280c0d06caSMauro Carvalho Chehab static void pvr2_hdw_remove_usb_stuff(struct pvr2_hdw *hdw)
26290c0d06caSMauro Carvalho Chehab {
26300c0d06caSMauro Carvalho Chehab 	if (hdw->flag_disconnected) return;
26310c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_remove_usb_stuff: hdw=%p",hdw);
26320c0d06caSMauro Carvalho Chehab 	if (hdw->ctl_read_urb) {
26330c0d06caSMauro Carvalho Chehab 		usb_kill_urb(hdw->ctl_read_urb);
26340c0d06caSMauro Carvalho Chehab 		usb_free_urb(hdw->ctl_read_urb);
26350c0d06caSMauro Carvalho Chehab 		hdw->ctl_read_urb = NULL;
26360c0d06caSMauro Carvalho Chehab 	}
26370c0d06caSMauro Carvalho Chehab 	if (hdw->ctl_write_urb) {
26380c0d06caSMauro Carvalho Chehab 		usb_kill_urb(hdw->ctl_write_urb);
26390c0d06caSMauro Carvalho Chehab 		usb_free_urb(hdw->ctl_write_urb);
26400c0d06caSMauro Carvalho Chehab 		hdw->ctl_write_urb = NULL;
26410c0d06caSMauro Carvalho Chehab 	}
26420c0d06caSMauro Carvalho Chehab 	if (hdw->ctl_read_buffer) {
26430c0d06caSMauro Carvalho Chehab 		kfree(hdw->ctl_read_buffer);
26440c0d06caSMauro Carvalho Chehab 		hdw->ctl_read_buffer = NULL;
26450c0d06caSMauro Carvalho Chehab 	}
26460c0d06caSMauro Carvalho Chehab 	if (hdw->ctl_write_buffer) {
26470c0d06caSMauro Carvalho Chehab 		kfree(hdw->ctl_write_buffer);
26480c0d06caSMauro Carvalho Chehab 		hdw->ctl_write_buffer = NULL;
26490c0d06caSMauro Carvalho Chehab 	}
26500c0d06caSMauro Carvalho Chehab 	hdw->flag_disconnected = !0;
26510c0d06caSMauro Carvalho Chehab 	/* If we don't do this, then there will be a dangling struct device
26520c0d06caSMauro Carvalho Chehab 	   reference to our disappearing device persisting inside the V4L
26530c0d06caSMauro Carvalho Chehab 	   core... */
26540c0d06caSMauro Carvalho Chehab 	v4l2_device_disconnect(&hdw->v4l2_dev);
26550c0d06caSMauro Carvalho Chehab 	hdw->usb_dev = NULL;
26560c0d06caSMauro Carvalho Chehab 	hdw->usb_intf = NULL;
26570c0d06caSMauro Carvalho Chehab 	pvr2_hdw_render_useless(hdw);
26580c0d06caSMauro Carvalho Chehab }
26590c0d06caSMauro Carvalho Chehab 
pvr2_hdw_set_v4l2_dev(struct pvr2_hdw * hdw,struct video_device * vdev)2660a28fbd04SHans Verkuil void pvr2_hdw_set_v4l2_dev(struct pvr2_hdw *hdw, struct video_device *vdev)
2661a28fbd04SHans Verkuil {
2662a28fbd04SHans Verkuil 	vdev->v4l2_dev = &hdw->v4l2_dev;
2663a28fbd04SHans Verkuil }
26640c0d06caSMauro Carvalho Chehab 
26650c0d06caSMauro Carvalho Chehab /* Destroy hardware interaction structure */
pvr2_hdw_destroy(struct pvr2_hdw * hdw)26660c0d06caSMauro Carvalho Chehab void pvr2_hdw_destroy(struct pvr2_hdw *hdw)
26670c0d06caSMauro Carvalho Chehab {
26680c0d06caSMauro Carvalho Chehab 	if (!hdw) return;
26690c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_destroy: hdw=%p",hdw);
267029a3006eSBhaktipriya Shridhar 	flush_work(&hdw->workpoll);
2671292a089dSSteven Rostedt (Google) 	timer_shutdown_sync(&hdw->quiescent_timer);
2672292a089dSSteven Rostedt (Google) 	timer_shutdown_sync(&hdw->decoder_stabilization_timer);
2673292a089dSSteven Rostedt (Google) 	timer_shutdown_sync(&hdw->encoder_run_timer);
2674292a089dSSteven Rostedt (Google) 	timer_shutdown_sync(&hdw->encoder_wait_timer);
26750c0d06caSMauro Carvalho Chehab 	if (hdw->fw_buffer) {
26760c0d06caSMauro Carvalho Chehab 		kfree(hdw->fw_buffer);
26770c0d06caSMauro Carvalho Chehab 		hdw->fw_buffer = NULL;
26780c0d06caSMauro Carvalho Chehab 	}
26790c0d06caSMauro Carvalho Chehab 	if (hdw->vid_stream) {
26800c0d06caSMauro Carvalho Chehab 		pvr2_stream_destroy(hdw->vid_stream);
26810c0d06caSMauro Carvalho Chehab 		hdw->vid_stream = NULL;
26820c0d06caSMauro Carvalho Chehab 	}
26830c0d06caSMauro Carvalho Chehab 	v4l2_device_unregister(&hdw->v4l2_dev);
2684f8194e5eSAnirudh Rayabharam 	pvr2_hdw_disconnect(hdw);
2685f419edd4SMauro Carvalho Chehab 	mutex_lock(&pvr2_unit_mtx);
2686f419edd4SMauro Carvalho Chehab 	do {
26870c0d06caSMauro Carvalho Chehab 		if ((hdw->unit_number >= 0) &&
26880c0d06caSMauro Carvalho Chehab 		    (hdw->unit_number < PVR_NUM) &&
26890c0d06caSMauro Carvalho Chehab 		    (unit_pointers[hdw->unit_number] == hdw)) {
26900c0d06caSMauro Carvalho Chehab 			unit_pointers[hdw->unit_number] = NULL;
26910c0d06caSMauro Carvalho Chehab 		}
2692f419edd4SMauro Carvalho Chehab 	} while (0);
2693f419edd4SMauro Carvalho Chehab 	mutex_unlock(&pvr2_unit_mtx);
26940c0d06caSMauro Carvalho Chehab 	kfree(hdw->controls);
26950c0d06caSMauro Carvalho Chehab 	kfree(hdw->mpeg_ctrl_info);
26960c0d06caSMauro Carvalho Chehab 	kfree(hdw);
26970c0d06caSMauro Carvalho Chehab }
26980c0d06caSMauro Carvalho Chehab 
26990c0d06caSMauro Carvalho Chehab 
pvr2_hdw_dev_ok(struct pvr2_hdw * hdw)27000c0d06caSMauro Carvalho Chehab int pvr2_hdw_dev_ok(struct pvr2_hdw *hdw)
27010c0d06caSMauro Carvalho Chehab {
27020c0d06caSMauro Carvalho Chehab 	return (hdw && hdw->flag_ok);
27030c0d06caSMauro Carvalho Chehab }
27040c0d06caSMauro Carvalho Chehab 
27050c0d06caSMauro Carvalho Chehab 
27060c0d06caSMauro Carvalho Chehab /* Called when hardware has been unplugged */
pvr2_hdw_disconnect(struct pvr2_hdw * hdw)27070c0d06caSMauro Carvalho Chehab void pvr2_hdw_disconnect(struct pvr2_hdw *hdw)
27080c0d06caSMauro Carvalho Chehab {
27090c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_disconnect(hdw=%p)",hdw);
27100c0d06caSMauro Carvalho Chehab 	LOCK_TAKE(hdw->big_lock);
2711f8194e5eSAnirudh Rayabharam 	pvr2_i2c_core_done(hdw);
27120c0d06caSMauro Carvalho Chehab 	LOCK_TAKE(hdw->ctl_lock);
27130c0d06caSMauro Carvalho Chehab 	pvr2_hdw_remove_usb_stuff(hdw);
27140c0d06caSMauro Carvalho Chehab 	LOCK_GIVE(hdw->ctl_lock);
27150c0d06caSMauro Carvalho Chehab 	LOCK_GIVE(hdw->big_lock);
27160c0d06caSMauro Carvalho Chehab }
27170c0d06caSMauro Carvalho Chehab 
27180c0d06caSMauro Carvalho Chehab 
27190c0d06caSMauro Carvalho Chehab /* Get the number of defined controls */
pvr2_hdw_get_ctrl_count(struct pvr2_hdw * hdw)27200c0d06caSMauro Carvalho Chehab unsigned int pvr2_hdw_get_ctrl_count(struct pvr2_hdw *hdw)
27210c0d06caSMauro Carvalho Chehab {
27220c0d06caSMauro Carvalho Chehab 	return hdw->control_cnt;
27230c0d06caSMauro Carvalho Chehab }
27240c0d06caSMauro Carvalho Chehab 
27250c0d06caSMauro Carvalho Chehab 
27260c0d06caSMauro Carvalho Chehab /* Retrieve a control handle given its index (0..count-1) */
pvr2_hdw_get_ctrl_by_index(struct pvr2_hdw * hdw,unsigned int idx)27270c0d06caSMauro Carvalho Chehab struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_index(struct pvr2_hdw *hdw,
27280c0d06caSMauro Carvalho Chehab 					     unsigned int idx)
27290c0d06caSMauro Carvalho Chehab {
27300c0d06caSMauro Carvalho Chehab 	if (idx >= hdw->control_cnt) return NULL;
27310c0d06caSMauro Carvalho Chehab 	return hdw->controls + idx;
27320c0d06caSMauro Carvalho Chehab }
27330c0d06caSMauro Carvalho Chehab 
27340c0d06caSMauro Carvalho Chehab 
27350c0d06caSMauro Carvalho Chehab /* Retrieve a control handle given its index (0..count-1) */
pvr2_hdw_get_ctrl_by_id(struct pvr2_hdw * hdw,unsigned int ctl_id)27360c0d06caSMauro Carvalho Chehab struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_id(struct pvr2_hdw *hdw,
27370c0d06caSMauro Carvalho Chehab 					  unsigned int ctl_id)
27380c0d06caSMauro Carvalho Chehab {
27390c0d06caSMauro Carvalho Chehab 	struct pvr2_ctrl *cptr;
27400c0d06caSMauro Carvalho Chehab 	unsigned int idx;
27410c0d06caSMauro Carvalho Chehab 	int i;
27420c0d06caSMauro Carvalho Chehab 
27430c0d06caSMauro Carvalho Chehab 	/* This could be made a lot more efficient, but for now... */
27440c0d06caSMauro Carvalho Chehab 	for (idx = 0; idx < hdw->control_cnt; idx++) {
27450c0d06caSMauro Carvalho Chehab 		cptr = hdw->controls + idx;
27460c0d06caSMauro Carvalho Chehab 		i = cptr->info->internal_id;
27470c0d06caSMauro Carvalho Chehab 		if (i && (i == ctl_id)) return cptr;
27480c0d06caSMauro Carvalho Chehab 	}
27490c0d06caSMauro Carvalho Chehab 	return NULL;
27500c0d06caSMauro Carvalho Chehab }
27510c0d06caSMauro Carvalho Chehab 
27520c0d06caSMauro Carvalho Chehab 
27530c0d06caSMauro Carvalho Chehab /* Given a V4L ID, retrieve the control structure associated with it. */
pvr2_hdw_get_ctrl_v4l(struct pvr2_hdw * hdw,unsigned int ctl_id)27540c0d06caSMauro Carvalho Chehab struct pvr2_ctrl *pvr2_hdw_get_ctrl_v4l(struct pvr2_hdw *hdw,unsigned int ctl_id)
27550c0d06caSMauro Carvalho Chehab {
27560c0d06caSMauro Carvalho Chehab 	struct pvr2_ctrl *cptr;
27570c0d06caSMauro Carvalho Chehab 	unsigned int idx;
27580c0d06caSMauro Carvalho Chehab 	int i;
27590c0d06caSMauro Carvalho Chehab 
27600c0d06caSMauro Carvalho Chehab 	/* This could be made a lot more efficient, but for now... */
27610c0d06caSMauro Carvalho Chehab 	for (idx = 0; idx < hdw->control_cnt; idx++) {
27620c0d06caSMauro Carvalho Chehab 		cptr = hdw->controls + idx;
27630c0d06caSMauro Carvalho Chehab 		i = cptr->info->v4l_id;
27640c0d06caSMauro Carvalho Chehab 		if (i && (i == ctl_id)) return cptr;
27650c0d06caSMauro Carvalho Chehab 	}
27660c0d06caSMauro Carvalho Chehab 	return NULL;
27670c0d06caSMauro Carvalho Chehab }
27680c0d06caSMauro Carvalho Chehab 
27690c0d06caSMauro Carvalho Chehab 
27700c0d06caSMauro Carvalho Chehab /* Given a V4L ID for its immediate predecessor, retrieve the control
27710c0d06caSMauro Carvalho Chehab    structure associated with it. */
pvr2_hdw_get_ctrl_nextv4l(struct pvr2_hdw * hdw,unsigned int ctl_id)27720c0d06caSMauro Carvalho Chehab struct pvr2_ctrl *pvr2_hdw_get_ctrl_nextv4l(struct pvr2_hdw *hdw,
27730c0d06caSMauro Carvalho Chehab 					    unsigned int ctl_id)
27740c0d06caSMauro Carvalho Chehab {
27750c0d06caSMauro Carvalho Chehab 	struct pvr2_ctrl *cptr,*cp2;
27760c0d06caSMauro Carvalho Chehab 	unsigned int idx;
27770c0d06caSMauro Carvalho Chehab 	int i;
27780c0d06caSMauro Carvalho Chehab 
27790c0d06caSMauro Carvalho Chehab 	/* This could be made a lot more efficient, but for now... */
27800c0d06caSMauro Carvalho Chehab 	cp2 = NULL;
27810c0d06caSMauro Carvalho Chehab 	for (idx = 0; idx < hdw->control_cnt; idx++) {
27820c0d06caSMauro Carvalho Chehab 		cptr = hdw->controls + idx;
27830c0d06caSMauro Carvalho Chehab 		i = cptr->info->v4l_id;
27840c0d06caSMauro Carvalho Chehab 		if (!i) continue;
27850c0d06caSMauro Carvalho Chehab 		if (i <= ctl_id) continue;
27860c0d06caSMauro Carvalho Chehab 		if (cp2 && (cp2->info->v4l_id < i)) continue;
27870c0d06caSMauro Carvalho Chehab 		cp2 = cptr;
27880c0d06caSMauro Carvalho Chehab 	}
27890c0d06caSMauro Carvalho Chehab 	return cp2;
27900c0d06caSMauro Carvalho Chehab 	return NULL;
27910c0d06caSMauro Carvalho Chehab }
27920c0d06caSMauro Carvalho Chehab 
27930c0d06caSMauro Carvalho Chehab 
get_ctrl_typename(enum pvr2_ctl_type tp)27940c0d06caSMauro Carvalho Chehab static const char *get_ctrl_typename(enum pvr2_ctl_type tp)
27950c0d06caSMauro Carvalho Chehab {
27960c0d06caSMauro Carvalho Chehab 	switch (tp) {
27970c0d06caSMauro Carvalho Chehab 	case pvr2_ctl_int: return "integer";
27980c0d06caSMauro Carvalho Chehab 	case pvr2_ctl_enum: return "enum";
27990c0d06caSMauro Carvalho Chehab 	case pvr2_ctl_bool: return "boolean";
28000c0d06caSMauro Carvalho Chehab 	case pvr2_ctl_bitmask: return "bitmask";
28010c0d06caSMauro Carvalho Chehab 	}
28020c0d06caSMauro Carvalho Chehab 	return "";
28030c0d06caSMauro Carvalho Chehab }
28040c0d06caSMauro Carvalho Chehab 
28050c0d06caSMauro Carvalho Chehab 
pvr2_subdev_set_control(struct pvr2_hdw * hdw,int id,const char * name,int val)28060c0d06caSMauro Carvalho Chehab static void pvr2_subdev_set_control(struct pvr2_hdw *hdw, int id,
28070c0d06caSMauro Carvalho Chehab 				    const char *name, int val)
28080c0d06caSMauro Carvalho Chehab {
28090c0d06caSMauro Carvalho Chehab 	struct v4l2_control ctrl;
2810b5b97f34SHans Verkuil 	struct v4l2_subdev *sd;
2811b5b97f34SHans Verkuil 
28120c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 %s=%d", name, val);
28130c0d06caSMauro Carvalho Chehab 	memset(&ctrl, 0, sizeof(ctrl));
28140c0d06caSMauro Carvalho Chehab 	ctrl.id = id;
28150c0d06caSMauro Carvalho Chehab 	ctrl.value = val;
2816b5b97f34SHans Verkuil 
2817b5b97f34SHans Verkuil 	v4l2_device_for_each_subdev(sd, &hdw->v4l2_dev)
2818b5b97f34SHans Verkuil 		v4l2_s_ctrl(NULL, sd->ctrl_handler, &ctrl);
28190c0d06caSMauro Carvalho Chehab }
28200c0d06caSMauro Carvalho Chehab 
28210c0d06caSMauro Carvalho Chehab #define PVR2_SUBDEV_SET_CONTROL(hdw, id, lab) \
28220c0d06caSMauro Carvalho Chehab 	if ((hdw)->lab##_dirty || (hdw)->force_dirty) {		\
28230c0d06caSMauro Carvalho Chehab 		pvr2_subdev_set_control(hdw, id, #lab, (hdw)->lab##_val); \
28240c0d06caSMauro Carvalho Chehab 	}
28250c0d06caSMauro Carvalho Chehab 
pvr2_hdw_get_detected_std(struct pvr2_hdw * hdw)282645bc3fceSHans Verkuil static v4l2_std_id pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw)
28270c0d06caSMauro Carvalho Chehab {
28280c0d06caSMauro Carvalho Chehab 	v4l2_std_id std;
28290c0d06caSMauro Carvalho Chehab 	std = (v4l2_std_id)hdw->std_mask_avail;
28300c0d06caSMauro Carvalho Chehab 	v4l2_device_call_all(&hdw->v4l2_dev, 0,
28310c0d06caSMauro Carvalho Chehab 			     video, querystd, &std);
28320c0d06caSMauro Carvalho Chehab 	return std;
28330c0d06caSMauro Carvalho Chehab }
28340c0d06caSMauro Carvalho Chehab 
28350c0d06caSMauro Carvalho Chehab /* Execute whatever commands are required to update the state of all the
28360c0d06caSMauro Carvalho Chehab    sub-devices so that they match our current control values. */
pvr2_subdev_update(struct pvr2_hdw * hdw)28370c0d06caSMauro Carvalho Chehab static void pvr2_subdev_update(struct pvr2_hdw *hdw)
28380c0d06caSMauro Carvalho Chehab {
28390c0d06caSMauro Carvalho Chehab 	struct v4l2_subdev *sd;
28400c0d06caSMauro Carvalho Chehab 	unsigned int id;
28410c0d06caSMauro Carvalho Chehab 	pvr2_subdev_update_func fp;
28420c0d06caSMauro Carvalho Chehab 
28430c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_CHIPS, "subdev update...");
28440c0d06caSMauro Carvalho Chehab 
28450c0d06caSMauro Carvalho Chehab 	if (hdw->tuner_updated || hdw->force_dirty) {
28460c0d06caSMauro Carvalho Chehab 		struct tuner_setup setup;
28470c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_CHIPS, "subdev tuner set_type(%d)",
28480c0d06caSMauro Carvalho Chehab 			   hdw->tuner_type);
28490c0d06caSMauro Carvalho Chehab 		if (((int)(hdw->tuner_type)) >= 0) {
28500c0d06caSMauro Carvalho Chehab 			memset(&setup, 0, sizeof(setup));
28510c0d06caSMauro Carvalho Chehab 			setup.addr = ADDR_UNSET;
28520c0d06caSMauro Carvalho Chehab 			setup.type = hdw->tuner_type;
28530c0d06caSMauro Carvalho Chehab 			setup.mode_mask = T_RADIO | T_ANALOG_TV;
28540c0d06caSMauro Carvalho Chehab 			v4l2_device_call_all(&hdw->v4l2_dev, 0,
28550c0d06caSMauro Carvalho Chehab 					     tuner, s_type_addr, &setup);
28560c0d06caSMauro Carvalho Chehab 		}
28570c0d06caSMauro Carvalho Chehab 	}
28580c0d06caSMauro Carvalho Chehab 
28590c0d06caSMauro Carvalho Chehab 	if (hdw->input_dirty || hdw->std_dirty || hdw->force_dirty) {
28600c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_standard");
28610c0d06caSMauro Carvalho Chehab 		if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
28620c0d06caSMauro Carvalho Chehab 			v4l2_device_call_all(&hdw->v4l2_dev, 0,
28630c0d06caSMauro Carvalho Chehab 					     tuner, s_radio);
28640c0d06caSMauro Carvalho Chehab 		} else {
28650c0d06caSMauro Carvalho Chehab 			v4l2_std_id vs;
28660c0d06caSMauro Carvalho Chehab 			vs = hdw->std_mask_cur;
28670c0d06caSMauro Carvalho Chehab 			v4l2_device_call_all(&hdw->v4l2_dev, 0,
28688774bed9SLaurent Pinchart 					     video, s_std, vs);
28690c0d06caSMauro Carvalho Chehab 			pvr2_hdw_cx25840_vbi_hack(hdw);
28700c0d06caSMauro Carvalho Chehab 		}
28710c0d06caSMauro Carvalho Chehab 		hdw->tuner_signal_stale = !0;
28720c0d06caSMauro Carvalho Chehab 		hdw->cropcap_stale = !0;
28730c0d06caSMauro Carvalho Chehab 	}
28740c0d06caSMauro Carvalho Chehab 
28750c0d06caSMauro Carvalho Chehab 	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_BRIGHTNESS, brightness);
28760c0d06caSMauro Carvalho Chehab 	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_CONTRAST, contrast);
28770c0d06caSMauro Carvalho Chehab 	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_SATURATION, saturation);
28780c0d06caSMauro Carvalho Chehab 	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_HUE, hue);
28790c0d06caSMauro Carvalho Chehab 	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_MUTE, mute);
28800c0d06caSMauro Carvalho Chehab 	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_VOLUME, volume);
28810c0d06caSMauro Carvalho Chehab 	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_BALANCE, balance);
28820c0d06caSMauro Carvalho Chehab 	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_BASS, bass);
28830c0d06caSMauro Carvalho Chehab 	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_TREBLE, treble);
28840c0d06caSMauro Carvalho Chehab 
28850c0d06caSMauro Carvalho Chehab 	if (hdw->input_dirty || hdw->audiomode_dirty || hdw->force_dirty) {
28860c0d06caSMauro Carvalho Chehab 		struct v4l2_tuner vt;
28870c0d06caSMauro Carvalho Chehab 		memset(&vt, 0, sizeof(vt));
28880c0d06caSMauro Carvalho Chehab 		vt.type = (hdw->input_val == PVR2_CVAL_INPUT_RADIO) ?
28890c0d06caSMauro Carvalho Chehab 			V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
28900c0d06caSMauro Carvalho Chehab 		vt.audmode = hdw->audiomode_val;
28910c0d06caSMauro Carvalho Chehab 		v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, s_tuner, &vt);
28920c0d06caSMauro Carvalho Chehab 	}
28930c0d06caSMauro Carvalho Chehab 
28940c0d06caSMauro Carvalho Chehab 	if (hdw->freqDirty || hdw->force_dirty) {
28950c0d06caSMauro Carvalho Chehab 		unsigned long fv;
28960c0d06caSMauro Carvalho Chehab 		struct v4l2_frequency freq;
28970c0d06caSMauro Carvalho Chehab 		fv = pvr2_hdw_get_cur_freq(hdw);
28980c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_freq(%lu)", fv);
28990c0d06caSMauro Carvalho Chehab 		if (hdw->tuner_signal_stale) pvr2_hdw_status_poll(hdw);
29000c0d06caSMauro Carvalho Chehab 		memset(&freq, 0, sizeof(freq));
29010c0d06caSMauro Carvalho Chehab 		if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) {
29020c0d06caSMauro Carvalho Chehab 			/* ((fv * 1000) / 62500) */
29030c0d06caSMauro Carvalho Chehab 			freq.frequency = (fv * 2) / 125;
29040c0d06caSMauro Carvalho Chehab 		} else {
29050c0d06caSMauro Carvalho Chehab 			freq.frequency = fv / 62500;
29060c0d06caSMauro Carvalho Chehab 		}
29070c0d06caSMauro Carvalho Chehab 		/* tuner-core currently doesn't seem to care about this, but
29080c0d06caSMauro Carvalho Chehab 		   let's set it anyway for completeness. */
29090c0d06caSMauro Carvalho Chehab 		if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
29100c0d06caSMauro Carvalho Chehab 			freq.type = V4L2_TUNER_RADIO;
29110c0d06caSMauro Carvalho Chehab 		} else {
29120c0d06caSMauro Carvalho Chehab 			freq.type = V4L2_TUNER_ANALOG_TV;
29130c0d06caSMauro Carvalho Chehab 		}
29140c0d06caSMauro Carvalho Chehab 		freq.tuner = 0;
29150c0d06caSMauro Carvalho Chehab 		v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner,
29160c0d06caSMauro Carvalho Chehab 				     s_frequency, &freq);
29170c0d06caSMauro Carvalho Chehab 	}
29180c0d06caSMauro Carvalho Chehab 
29190c0d06caSMauro Carvalho Chehab 	if (hdw->res_hor_dirty || hdw->res_ver_dirty || hdw->force_dirty) {
2920ebf984bbSHans Verkuil 		struct v4l2_subdev_format format = {
2921ebf984bbSHans Verkuil 			.which = V4L2_SUBDEV_FORMAT_ACTIVE,
2922ebf984bbSHans Verkuil 		};
2923ebf984bbSHans Verkuil 
2924ebf984bbSHans Verkuil 		format.format.width = hdw->res_hor_val;
2925ebf984bbSHans Verkuil 		format.format.height = hdw->res_ver_val;
2926ebf984bbSHans Verkuil 		format.format.code = MEDIA_BUS_FMT_FIXED;
29270c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_size(%dx%d)",
2928ebf984bbSHans Verkuil 			   format.format.width, format.format.height);
2929ebf984bbSHans Verkuil 		v4l2_device_call_all(&hdw->v4l2_dev, 0, pad, set_fmt,
2930ebf984bbSHans Verkuil 				     NULL, &format);
29310c0d06caSMauro Carvalho Chehab 	}
29320c0d06caSMauro Carvalho Chehab 
29330c0d06caSMauro Carvalho Chehab 	if (hdw->srate_dirty || hdw->force_dirty) {
29340c0d06caSMauro Carvalho Chehab 		u32 val;
29350c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_audio %d",
29360c0d06caSMauro Carvalho Chehab 			   hdw->srate_val);
29370c0d06caSMauro Carvalho Chehab 		switch (hdw->srate_val) {
29380c0d06caSMauro Carvalho Chehab 		default:
29390c0d06caSMauro Carvalho Chehab 		case V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000:
29400c0d06caSMauro Carvalho Chehab 			val = 48000;
29410c0d06caSMauro Carvalho Chehab 			break;
29420c0d06caSMauro Carvalho Chehab 		case V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100:
29430c0d06caSMauro Carvalho Chehab 			val = 44100;
29440c0d06caSMauro Carvalho Chehab 			break;
29450c0d06caSMauro Carvalho Chehab 		case V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000:
29460c0d06caSMauro Carvalho Chehab 			val = 32000;
29470c0d06caSMauro Carvalho Chehab 			break;
29480c0d06caSMauro Carvalho Chehab 		}
29490c0d06caSMauro Carvalho Chehab 		v4l2_device_call_all(&hdw->v4l2_dev, 0,
29500c0d06caSMauro Carvalho Chehab 				     audio, s_clock_freq, val);
29510c0d06caSMauro Carvalho Chehab 	}
29520c0d06caSMauro Carvalho Chehab 
29530c0d06caSMauro Carvalho Chehab 	/* Unable to set crop parameters; there is apparently no equivalent
29540c0d06caSMauro Carvalho Chehab 	   for VIDIOC_S_CROP */
29550c0d06caSMauro Carvalho Chehab 
29560c0d06caSMauro Carvalho Chehab 	v4l2_device_for_each_subdev(sd, &hdw->v4l2_dev) {
29570c0d06caSMauro Carvalho Chehab 		id = sd->grp_id;
29580c0d06caSMauro Carvalho Chehab 		if (id >= ARRAY_SIZE(pvr2_module_update_functions)) continue;
29590c0d06caSMauro Carvalho Chehab 		fp = pvr2_module_update_functions[id];
29600c0d06caSMauro Carvalho Chehab 		if (!fp) continue;
29610c0d06caSMauro Carvalho Chehab 		(*fp)(hdw, sd);
29620c0d06caSMauro Carvalho Chehab 	}
29630c0d06caSMauro Carvalho Chehab 
29640c0d06caSMauro Carvalho Chehab 	if (hdw->tuner_signal_stale || hdw->cropcap_stale) {
29650c0d06caSMauro Carvalho Chehab 		pvr2_hdw_status_poll(hdw);
29660c0d06caSMauro Carvalho Chehab 	}
29670c0d06caSMauro Carvalho Chehab }
29680c0d06caSMauro Carvalho Chehab 
29690c0d06caSMauro Carvalho Chehab 
29700c0d06caSMauro Carvalho Chehab /* Figure out if we need to commit control changes.  If so, mark internal
29710c0d06caSMauro Carvalho Chehab    state flags to indicate this fact and return true.  Otherwise do nothing
29720c0d06caSMauro Carvalho Chehab    else and return false. */
pvr2_hdw_commit_setup(struct pvr2_hdw * hdw)29730c0d06caSMauro Carvalho Chehab static int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw)
29740c0d06caSMauro Carvalho Chehab {
29750c0d06caSMauro Carvalho Chehab 	unsigned int idx;
29760c0d06caSMauro Carvalho Chehab 	struct pvr2_ctrl *cptr;
29770c0d06caSMauro Carvalho Chehab 	int value;
29780c0d06caSMauro Carvalho Chehab 	int commit_flag = hdw->force_dirty;
29790c0d06caSMauro Carvalho Chehab 	char buf[100];
29800c0d06caSMauro Carvalho Chehab 	unsigned int bcnt,ccnt;
29810c0d06caSMauro Carvalho Chehab 
29820c0d06caSMauro Carvalho Chehab 	for (idx = 0; idx < hdw->control_cnt; idx++) {
29830c0d06caSMauro Carvalho Chehab 		cptr = hdw->controls + idx;
29840c0d06caSMauro Carvalho Chehab 		if (!cptr->info->is_dirty) continue;
29850c0d06caSMauro Carvalho Chehab 		if (!cptr->info->is_dirty(cptr)) continue;
29860c0d06caSMauro Carvalho Chehab 		commit_flag = !0;
29870c0d06caSMauro Carvalho Chehab 
29880c0d06caSMauro Carvalho Chehab 		if (!(pvrusb2_debug & PVR2_TRACE_CTL)) continue;
29890c0d06caSMauro Carvalho Chehab 		bcnt = scnprintf(buf,sizeof(buf),"\"%s\" <-- ",
29900c0d06caSMauro Carvalho Chehab 				 cptr->info->name);
29910c0d06caSMauro Carvalho Chehab 		value = 0;
29920c0d06caSMauro Carvalho Chehab 		cptr->info->get_value(cptr,&value);
29930c0d06caSMauro Carvalho Chehab 		pvr2_ctrl_value_to_sym_internal(cptr,~0,value,
29940c0d06caSMauro Carvalho Chehab 						buf+bcnt,
29950c0d06caSMauro Carvalho Chehab 						sizeof(buf)-bcnt,&ccnt);
29960c0d06caSMauro Carvalho Chehab 		bcnt += ccnt;
29970c0d06caSMauro Carvalho Chehab 		bcnt += scnprintf(buf+bcnt,sizeof(buf)-bcnt," <%s>",
29980c0d06caSMauro Carvalho Chehab 				  get_ctrl_typename(cptr->info->type));
29990c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_CTL,
30000c0d06caSMauro Carvalho Chehab 			   "/*--TRACE_COMMIT--*/ %.*s",
30010c0d06caSMauro Carvalho Chehab 			   bcnt,buf);
30020c0d06caSMauro Carvalho Chehab 	}
30030c0d06caSMauro Carvalho Chehab 
30040c0d06caSMauro Carvalho Chehab 	if (!commit_flag) {
30050c0d06caSMauro Carvalho Chehab 		/* Nothing has changed */
30060c0d06caSMauro Carvalho Chehab 		return 0;
30070c0d06caSMauro Carvalho Chehab 	}
30080c0d06caSMauro Carvalho Chehab 
30090c0d06caSMauro Carvalho Chehab 	hdw->state_pipeline_config = 0;
30100c0d06caSMauro Carvalho Chehab 	trace_stbit("state_pipeline_config",hdw->state_pipeline_config);
30110c0d06caSMauro Carvalho Chehab 	pvr2_hdw_state_sched(hdw);
30120c0d06caSMauro Carvalho Chehab 
30130c0d06caSMauro Carvalho Chehab 	return !0;
30140c0d06caSMauro Carvalho Chehab }
30150c0d06caSMauro Carvalho Chehab 
30160c0d06caSMauro Carvalho Chehab 
30170c0d06caSMauro Carvalho Chehab /* Perform all operations needed to commit all control changes.  This must
30180c0d06caSMauro Carvalho Chehab    be performed in synchronization with the pipeline state and is thus
30190c0d06caSMauro Carvalho Chehab    expected to be called as part of the driver's worker thread.  Return
30200c0d06caSMauro Carvalho Chehab    true if commit successful, otherwise return false to indicate that
30210c0d06caSMauro Carvalho Chehab    commit isn't possible at this time. */
pvr2_hdw_commit_execute(struct pvr2_hdw * hdw)30220c0d06caSMauro Carvalho Chehab static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw)
30230c0d06caSMauro Carvalho Chehab {
30240c0d06caSMauro Carvalho Chehab 	unsigned int idx;
30250c0d06caSMauro Carvalho Chehab 	struct pvr2_ctrl *cptr;
30260c0d06caSMauro Carvalho Chehab 	int disruptive_change;
30270c0d06caSMauro Carvalho Chehab 
30280c0d06caSMauro Carvalho Chehab 	if (hdw->input_dirty && hdw->state_pathway_ok &&
30290c0d06caSMauro Carvalho Chehab 	    (((hdw->input_val == PVR2_CVAL_INPUT_DTV) ?
30300c0d06caSMauro Carvalho Chehab 	      PVR2_PATHWAY_DIGITAL : PVR2_PATHWAY_ANALOG) !=
30310c0d06caSMauro Carvalho Chehab 	     hdw->pathway_state)) {
30320c0d06caSMauro Carvalho Chehab 		/* Change of mode being asked for... */
30330c0d06caSMauro Carvalho Chehab 		hdw->state_pathway_ok = 0;
30340c0d06caSMauro Carvalho Chehab 		trace_stbit("state_pathway_ok", hdw->state_pathway_ok);
30350c0d06caSMauro Carvalho Chehab 	}
30360c0d06caSMauro Carvalho Chehab 	if (!hdw->state_pathway_ok) {
30370c0d06caSMauro Carvalho Chehab 		/* Can't commit anything until pathway is ok. */
30380c0d06caSMauro Carvalho Chehab 		return 0;
30390c0d06caSMauro Carvalho Chehab 	}
30400c0d06caSMauro Carvalho Chehab 
30410c0d06caSMauro Carvalho Chehab 	/* Handle some required side effects when the video standard is
30420c0d06caSMauro Carvalho Chehab 	   changed.... */
30430c0d06caSMauro Carvalho Chehab 	if (hdw->std_dirty) {
30440c0d06caSMauro Carvalho Chehab 		int nvres;
30450c0d06caSMauro Carvalho Chehab 		int gop_size;
30460c0d06caSMauro Carvalho Chehab 		if (hdw->std_mask_cur & V4L2_STD_525_60) {
30470c0d06caSMauro Carvalho Chehab 			nvres = 480;
30480c0d06caSMauro Carvalho Chehab 			gop_size = 15;
30490c0d06caSMauro Carvalho Chehab 		} else {
30500c0d06caSMauro Carvalho Chehab 			nvres = 576;
30510c0d06caSMauro Carvalho Chehab 			gop_size = 12;
30520c0d06caSMauro Carvalho Chehab 		}
30530c0d06caSMauro Carvalho Chehab 		/* Rewrite the vertical resolution to be appropriate to the
30540c0d06caSMauro Carvalho Chehab 		   video standard that has been selected. */
30550c0d06caSMauro Carvalho Chehab 		if (nvres != hdw->res_ver_val) {
30560c0d06caSMauro Carvalho Chehab 			hdw->res_ver_val = nvres;
30570c0d06caSMauro Carvalho Chehab 			hdw->res_ver_dirty = !0;
30580c0d06caSMauro Carvalho Chehab 		}
30590c0d06caSMauro Carvalho Chehab 		/* Rewrite the GOP size to be appropriate to the video
30600c0d06caSMauro Carvalho Chehab 		   standard that has been selected. */
30610c0d06caSMauro Carvalho Chehab 		if (gop_size != hdw->enc_ctl_state.video_gop_size) {
30620c0d06caSMauro Carvalho Chehab 			struct v4l2_ext_controls cs;
30630c0d06caSMauro Carvalho Chehab 			struct v4l2_ext_control c1;
30640c0d06caSMauro Carvalho Chehab 			memset(&cs, 0, sizeof(cs));
30650c0d06caSMauro Carvalho Chehab 			memset(&c1, 0, sizeof(c1));
30660c0d06caSMauro Carvalho Chehab 			cs.controls = &c1;
30670c0d06caSMauro Carvalho Chehab 			cs.count = 1;
30680c0d06caSMauro Carvalho Chehab 			c1.id = V4L2_CID_MPEG_VIDEO_GOP_SIZE;
30690c0d06caSMauro Carvalho Chehab 			c1.value = gop_size;
30700c0d06caSMauro Carvalho Chehab 			cx2341x_ext_ctrls(&hdw->enc_ctl_state, 0, &cs,
30710c0d06caSMauro Carvalho Chehab 					  VIDIOC_S_EXT_CTRLS);
30720c0d06caSMauro Carvalho Chehab 		}
30730c0d06caSMauro Carvalho Chehab 	}
30740c0d06caSMauro Carvalho Chehab 
30750c0d06caSMauro Carvalho Chehab 	/* The broadcast decoder can only scale down, so if
30760c0d06caSMauro Carvalho Chehab 	 * res_*_dirty && crop window < output format ==> enlarge crop.
30770c0d06caSMauro Carvalho Chehab 	 *
30780c0d06caSMauro Carvalho Chehab 	 * The mpeg encoder receives fields of res_hor_val dots and
30790c0d06caSMauro Carvalho Chehab 	 * res_ver_val halflines.  Limits: hor<=720, ver<=576.
30800c0d06caSMauro Carvalho Chehab 	 */
30810c0d06caSMauro Carvalho Chehab 	if (hdw->res_hor_dirty && hdw->cropw_val < hdw->res_hor_val) {
30820c0d06caSMauro Carvalho Chehab 		hdw->cropw_val = hdw->res_hor_val;
30830c0d06caSMauro Carvalho Chehab 		hdw->cropw_dirty = !0;
30840c0d06caSMauro Carvalho Chehab 	} else if (hdw->cropw_dirty) {
30850c0d06caSMauro Carvalho Chehab 		hdw->res_hor_dirty = !0;           /* must rescale */
30860c0d06caSMauro Carvalho Chehab 		hdw->res_hor_val = min(720, hdw->cropw_val);
30870c0d06caSMauro Carvalho Chehab 	}
30880c0d06caSMauro Carvalho Chehab 	if (hdw->res_ver_dirty && hdw->croph_val < hdw->res_ver_val) {
30890c0d06caSMauro Carvalho Chehab 		hdw->croph_val = hdw->res_ver_val;
30900c0d06caSMauro Carvalho Chehab 		hdw->croph_dirty = !0;
30910c0d06caSMauro Carvalho Chehab 	} else if (hdw->croph_dirty) {
30920c0d06caSMauro Carvalho Chehab 		int nvres = hdw->std_mask_cur & V4L2_STD_525_60 ? 480 : 576;
30930c0d06caSMauro Carvalho Chehab 		hdw->res_ver_dirty = !0;
30940c0d06caSMauro Carvalho Chehab 		hdw->res_ver_val = min(nvres, hdw->croph_val);
30950c0d06caSMauro Carvalho Chehab 	}
30960c0d06caSMauro Carvalho Chehab 
30970c0d06caSMauro Carvalho Chehab 	/* If any of the below has changed, then we can't do the update
30980c0d06caSMauro Carvalho Chehab 	   while the pipeline is running.  Pipeline must be paused first
30990c0d06caSMauro Carvalho Chehab 	   and decoder -> encoder connection be made quiescent before we
31000c0d06caSMauro Carvalho Chehab 	   can proceed. */
31010c0d06caSMauro Carvalho Chehab 	disruptive_change =
31020c0d06caSMauro Carvalho Chehab 		(hdw->std_dirty ||
31030c0d06caSMauro Carvalho Chehab 		 hdw->enc_unsafe_stale ||
31040c0d06caSMauro Carvalho Chehab 		 hdw->srate_dirty ||
31050c0d06caSMauro Carvalho Chehab 		 hdw->res_ver_dirty ||
31060c0d06caSMauro Carvalho Chehab 		 hdw->res_hor_dirty ||
31070c0d06caSMauro Carvalho Chehab 		 hdw->cropw_dirty ||
31080c0d06caSMauro Carvalho Chehab 		 hdw->croph_dirty ||
31090c0d06caSMauro Carvalho Chehab 		 hdw->input_dirty ||
31100c0d06caSMauro Carvalho Chehab 		 (hdw->active_stream_type != hdw->desired_stream_type));
31110c0d06caSMauro Carvalho Chehab 	if (disruptive_change && !hdw->state_pipeline_idle) {
31120c0d06caSMauro Carvalho Chehab 		/* Pipeline is not idle; we can't proceed.  Arrange to
31130c0d06caSMauro Carvalho Chehab 		   cause pipeline to stop so that we can try this again
31140c0d06caSMauro Carvalho Chehab 		   later.... */
31150c0d06caSMauro Carvalho Chehab 		hdw->state_pipeline_pause = !0;
31160c0d06caSMauro Carvalho Chehab 		return 0;
31170c0d06caSMauro Carvalho Chehab 	}
31180c0d06caSMauro Carvalho Chehab 
31190c0d06caSMauro Carvalho Chehab 	if (hdw->srate_dirty) {
31200c0d06caSMauro Carvalho Chehab 		/* Write new sample rate into control structure since
31210c0d06caSMauro Carvalho Chehab 		 * the master copy is stale.  We must track srate
31220c0d06caSMauro Carvalho Chehab 		 * separate from the mpeg control structure because
31230c0d06caSMauro Carvalho Chehab 		 * other logic also uses this value. */
31240c0d06caSMauro Carvalho Chehab 		struct v4l2_ext_controls cs;
31250c0d06caSMauro Carvalho Chehab 		struct v4l2_ext_control c1;
31260c0d06caSMauro Carvalho Chehab 		memset(&cs,0,sizeof(cs));
31270c0d06caSMauro Carvalho Chehab 		memset(&c1,0,sizeof(c1));
31280c0d06caSMauro Carvalho Chehab 		cs.controls = &c1;
31290c0d06caSMauro Carvalho Chehab 		cs.count = 1;
31300c0d06caSMauro Carvalho Chehab 		c1.id = V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ;
31310c0d06caSMauro Carvalho Chehab 		c1.value = hdw->srate_val;
31320c0d06caSMauro Carvalho Chehab 		cx2341x_ext_ctrls(&hdw->enc_ctl_state, 0, &cs,VIDIOC_S_EXT_CTRLS);
31330c0d06caSMauro Carvalho Chehab 	}
31340c0d06caSMauro Carvalho Chehab 
31350c0d06caSMauro Carvalho Chehab 	if (hdw->active_stream_type != hdw->desired_stream_type) {
31360c0d06caSMauro Carvalho Chehab 		/* Handle any side effects of stream config here */
31370c0d06caSMauro Carvalho Chehab 		hdw->active_stream_type = hdw->desired_stream_type;
31380c0d06caSMauro Carvalho Chehab 	}
31390c0d06caSMauro Carvalho Chehab 
31400c0d06caSMauro Carvalho Chehab 	if (hdw->hdw_desc->signal_routing_scheme ==
31410c0d06caSMauro Carvalho Chehab 	    PVR2_ROUTING_SCHEME_GOTVIEW) {
31420c0d06caSMauro Carvalho Chehab 		u32 b;
31430c0d06caSMauro Carvalho Chehab 		/* Handle GOTVIEW audio switching */
31440c0d06caSMauro Carvalho Chehab 		pvr2_hdw_gpio_get_out(hdw,&b);
31450c0d06caSMauro Carvalho Chehab 		if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
31460c0d06caSMauro Carvalho Chehab 			/* Set GPIO 11 */
31470c0d06caSMauro Carvalho Chehab 			pvr2_hdw_gpio_chg_out(hdw,(1 << 11),~0);
31480c0d06caSMauro Carvalho Chehab 		} else {
31490c0d06caSMauro Carvalho Chehab 			/* Clear GPIO 11 */
31500c0d06caSMauro Carvalho Chehab 			pvr2_hdw_gpio_chg_out(hdw,(1 << 11),0);
31510c0d06caSMauro Carvalho Chehab 		}
31520c0d06caSMauro Carvalho Chehab 	}
31530c0d06caSMauro Carvalho Chehab 
31540c0d06caSMauro Carvalho Chehab 	/* Check and update state for all sub-devices. */
31550c0d06caSMauro Carvalho Chehab 	pvr2_subdev_update(hdw);
31560c0d06caSMauro Carvalho Chehab 
31570c0d06caSMauro Carvalho Chehab 	hdw->tuner_updated = 0;
31580c0d06caSMauro Carvalho Chehab 	hdw->force_dirty = 0;
31590c0d06caSMauro Carvalho Chehab 	for (idx = 0; idx < hdw->control_cnt; idx++) {
31600c0d06caSMauro Carvalho Chehab 		cptr = hdw->controls + idx;
31610c0d06caSMauro Carvalho Chehab 		if (!cptr->info->clear_dirty) continue;
31620c0d06caSMauro Carvalho Chehab 		cptr->info->clear_dirty(cptr);
31630c0d06caSMauro Carvalho Chehab 	}
31640c0d06caSMauro Carvalho Chehab 
31650c0d06caSMauro Carvalho Chehab 	if ((hdw->pathway_state == PVR2_PATHWAY_ANALOG) &&
31660c0d06caSMauro Carvalho Chehab 	    hdw->state_encoder_run) {
31670c0d06caSMauro Carvalho Chehab 		/* If encoder isn't running or it can't be touched, then
31680c0d06caSMauro Carvalho Chehab 		   this will get worked out later when we start the
31690c0d06caSMauro Carvalho Chehab 		   encoder. */
31700c0d06caSMauro Carvalho Chehab 		if (pvr2_encoder_adjust(hdw) < 0) return !0;
31710c0d06caSMauro Carvalho Chehab 	}
31720c0d06caSMauro Carvalho Chehab 
31730c0d06caSMauro Carvalho Chehab 	hdw->state_pipeline_config = !0;
31740c0d06caSMauro Carvalho Chehab 	/* Hardware state may have changed in a way to cause the cropping
31750c0d06caSMauro Carvalho Chehab 	   capabilities to have changed.  So mark it stale, which will
31760c0d06caSMauro Carvalho Chehab 	   cause a later re-fetch. */
31770c0d06caSMauro Carvalho Chehab 	trace_stbit("state_pipeline_config",hdw->state_pipeline_config);
31780c0d06caSMauro Carvalho Chehab 	return !0;
31790c0d06caSMauro Carvalho Chehab }
31800c0d06caSMauro Carvalho Chehab 
31810c0d06caSMauro Carvalho Chehab 
pvr2_hdw_commit_ctl(struct pvr2_hdw * hdw)31820c0d06caSMauro Carvalho Chehab int pvr2_hdw_commit_ctl(struct pvr2_hdw *hdw)
31830c0d06caSMauro Carvalho Chehab {
31840c0d06caSMauro Carvalho Chehab 	int fl;
31850c0d06caSMauro Carvalho Chehab 	LOCK_TAKE(hdw->big_lock);
31860c0d06caSMauro Carvalho Chehab 	fl = pvr2_hdw_commit_setup(hdw);
31870c0d06caSMauro Carvalho Chehab 	LOCK_GIVE(hdw->big_lock);
31880c0d06caSMauro Carvalho Chehab 	if (!fl) return 0;
31890c0d06caSMauro Carvalho Chehab 	return pvr2_hdw_wait(hdw,0);
31900c0d06caSMauro Carvalho Chehab }
31910c0d06caSMauro Carvalho Chehab 
31920c0d06caSMauro Carvalho Chehab 
pvr2_hdw_worker_poll(struct work_struct * work)31930c0d06caSMauro Carvalho Chehab static void pvr2_hdw_worker_poll(struct work_struct *work)
31940c0d06caSMauro Carvalho Chehab {
31950c0d06caSMauro Carvalho Chehab 	int fl = 0;
31960c0d06caSMauro Carvalho Chehab 	struct pvr2_hdw *hdw = container_of(work,struct pvr2_hdw,workpoll);
31970c0d06caSMauro Carvalho Chehab 	LOCK_TAKE(hdw->big_lock); do {
31980c0d06caSMauro Carvalho Chehab 		fl = pvr2_hdw_state_eval(hdw);
31990c0d06caSMauro Carvalho Chehab 	} while (0); LOCK_GIVE(hdw->big_lock);
32000c0d06caSMauro Carvalho Chehab 	if (fl && hdw->state_func) {
32010c0d06caSMauro Carvalho Chehab 		hdw->state_func(hdw->state_data);
32020c0d06caSMauro Carvalho Chehab 	}
32030c0d06caSMauro Carvalho Chehab }
32040c0d06caSMauro Carvalho Chehab 
32050c0d06caSMauro Carvalho Chehab 
pvr2_hdw_wait(struct pvr2_hdw * hdw,int state)32060c0d06caSMauro Carvalho Chehab static int pvr2_hdw_wait(struct pvr2_hdw *hdw,int state)
32070c0d06caSMauro Carvalho Chehab {
32080c0d06caSMauro Carvalho Chehab 	return wait_event_interruptible(
32090c0d06caSMauro Carvalho Chehab 		hdw->state_wait_data,
32100c0d06caSMauro Carvalho Chehab 		(hdw->state_stale == 0) &&
32110c0d06caSMauro Carvalho Chehab 		(!state || (hdw->master_state != state)));
32120c0d06caSMauro Carvalho Chehab }
32130c0d06caSMauro Carvalho Chehab 
32140c0d06caSMauro Carvalho Chehab 
32150c0d06caSMauro Carvalho Chehab /* Return name for this driver instance */
pvr2_hdw_get_driver_name(struct pvr2_hdw * hdw)32160c0d06caSMauro Carvalho Chehab const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *hdw)
32170c0d06caSMauro Carvalho Chehab {
32180c0d06caSMauro Carvalho Chehab 	return hdw->name;
32190c0d06caSMauro Carvalho Chehab }
32200c0d06caSMauro Carvalho Chehab 
32210c0d06caSMauro Carvalho Chehab 
pvr2_hdw_get_desc(struct pvr2_hdw * hdw)32220c0d06caSMauro Carvalho Chehab const char *pvr2_hdw_get_desc(struct pvr2_hdw *hdw)
32230c0d06caSMauro Carvalho Chehab {
32240c0d06caSMauro Carvalho Chehab 	return hdw->hdw_desc->description;
32250c0d06caSMauro Carvalho Chehab }
32260c0d06caSMauro Carvalho Chehab 
32270c0d06caSMauro Carvalho Chehab 
pvr2_hdw_get_type(struct pvr2_hdw * hdw)32280c0d06caSMauro Carvalho Chehab const char *pvr2_hdw_get_type(struct pvr2_hdw *hdw)
32290c0d06caSMauro Carvalho Chehab {
32300c0d06caSMauro Carvalho Chehab 	return hdw->hdw_desc->shortname;
32310c0d06caSMauro Carvalho Chehab }
32320c0d06caSMauro Carvalho Chehab 
32330c0d06caSMauro Carvalho Chehab 
pvr2_hdw_is_hsm(struct pvr2_hdw * hdw)32340c0d06caSMauro Carvalho Chehab int pvr2_hdw_is_hsm(struct pvr2_hdw *hdw)
32350c0d06caSMauro Carvalho Chehab {
32360c0d06caSMauro Carvalho Chehab 	int result;
32370c0d06caSMauro Carvalho Chehab 	LOCK_TAKE(hdw->ctl_lock); do {
32380c0d06caSMauro Carvalho Chehab 		hdw->cmd_buffer[0] = FX2CMD_GET_USB_SPEED;
32390c0d06caSMauro Carvalho Chehab 		result = pvr2_send_request(hdw,
32400c0d06caSMauro Carvalho Chehab 					   hdw->cmd_buffer,1,
32410c0d06caSMauro Carvalho Chehab 					   hdw->cmd_buffer,1);
32420c0d06caSMauro Carvalho Chehab 		if (result < 0) break;
32430c0d06caSMauro Carvalho Chehab 		result = (hdw->cmd_buffer[0] != 0);
32440c0d06caSMauro Carvalho Chehab 	} while(0); LOCK_GIVE(hdw->ctl_lock);
32450c0d06caSMauro Carvalho Chehab 	return result;
32460c0d06caSMauro Carvalho Chehab }
32470c0d06caSMauro Carvalho Chehab 
32480c0d06caSMauro Carvalho Chehab 
32490c0d06caSMauro Carvalho Chehab /* Execute poll of tuner status */
pvr2_hdw_execute_tuner_poll(struct pvr2_hdw * hdw)32500c0d06caSMauro Carvalho Chehab void pvr2_hdw_execute_tuner_poll(struct pvr2_hdw *hdw)
32510c0d06caSMauro Carvalho Chehab {
32520c0d06caSMauro Carvalho Chehab 	LOCK_TAKE(hdw->big_lock); do {
32530c0d06caSMauro Carvalho Chehab 		pvr2_hdw_status_poll(hdw);
32540c0d06caSMauro Carvalho Chehab 	} while (0); LOCK_GIVE(hdw->big_lock);
32550c0d06caSMauro Carvalho Chehab }
32560c0d06caSMauro Carvalho Chehab 
32570c0d06caSMauro Carvalho Chehab 
pvr2_hdw_check_cropcap(struct pvr2_hdw * hdw)32580c0d06caSMauro Carvalho Chehab static int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw)
32590c0d06caSMauro Carvalho Chehab {
32600c0d06caSMauro Carvalho Chehab 	if (!hdw->cropcap_stale) {
32610c0d06caSMauro Carvalho Chehab 		return 0;
32620c0d06caSMauro Carvalho Chehab 	}
32630c0d06caSMauro Carvalho Chehab 	pvr2_hdw_status_poll(hdw);
32640c0d06caSMauro Carvalho Chehab 	if (hdw->cropcap_stale) {
32650c0d06caSMauro Carvalho Chehab 		return -EIO;
32660c0d06caSMauro Carvalho Chehab 	}
32670c0d06caSMauro Carvalho Chehab 	return 0;
32680c0d06caSMauro Carvalho Chehab }
32690c0d06caSMauro Carvalho Chehab 
32700c0d06caSMauro Carvalho Chehab 
32710c0d06caSMauro Carvalho Chehab /* Return information about cropping capabilities */
pvr2_hdw_get_cropcap(struct pvr2_hdw * hdw,struct v4l2_cropcap * pp)32720c0d06caSMauro Carvalho Chehab int pvr2_hdw_get_cropcap(struct pvr2_hdw *hdw, struct v4l2_cropcap *pp)
32730c0d06caSMauro Carvalho Chehab {
32740c0d06caSMauro Carvalho Chehab 	int stat = 0;
32750c0d06caSMauro Carvalho Chehab 	LOCK_TAKE(hdw->big_lock);
32760c0d06caSMauro Carvalho Chehab 	stat = pvr2_hdw_check_cropcap(hdw);
32770c0d06caSMauro Carvalho Chehab 	if (!stat) {
32780c0d06caSMauro Carvalho Chehab 		memcpy(pp, &hdw->cropcap_info, sizeof(hdw->cropcap_info));
32790c0d06caSMauro Carvalho Chehab 	}
32800c0d06caSMauro Carvalho Chehab 	LOCK_GIVE(hdw->big_lock);
32810c0d06caSMauro Carvalho Chehab 	return stat;
32820c0d06caSMauro Carvalho Chehab }
32830c0d06caSMauro Carvalho Chehab 
32840c0d06caSMauro Carvalho Chehab 
32850c0d06caSMauro Carvalho Chehab /* Return information about the tuner */
pvr2_hdw_get_tuner_status(struct pvr2_hdw * hdw,struct v4l2_tuner * vtp)32860c0d06caSMauro Carvalho Chehab int pvr2_hdw_get_tuner_status(struct pvr2_hdw *hdw,struct v4l2_tuner *vtp)
32870c0d06caSMauro Carvalho Chehab {
3288*935ae6f8SHans Verkuil 	LOCK_TAKE(hdw->big_lock);
3289*935ae6f8SHans Verkuil 	do {
32900c0d06caSMauro Carvalho Chehab 		if (hdw->tuner_signal_stale) {
32910c0d06caSMauro Carvalho Chehab 			pvr2_hdw_status_poll(hdw);
32920c0d06caSMauro Carvalho Chehab 		}
32930c0d06caSMauro Carvalho Chehab 		memcpy(vtp,&hdw->tuner_signal_info,sizeof(struct v4l2_tuner));
3294*935ae6f8SHans Verkuil 	} while (0);
3295*935ae6f8SHans Verkuil 	LOCK_GIVE(hdw->big_lock);
32960c0d06caSMauro Carvalho Chehab 	return 0;
32970c0d06caSMauro Carvalho Chehab }
32980c0d06caSMauro Carvalho Chehab 
32990c0d06caSMauro Carvalho Chehab 
33000c0d06caSMauro Carvalho Chehab /* Get handle to video output stream */
pvr2_hdw_get_video_stream(struct pvr2_hdw * hp)33010c0d06caSMauro Carvalho Chehab struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *hp)
33020c0d06caSMauro Carvalho Chehab {
33030c0d06caSMauro Carvalho Chehab 	return hp->vid_stream;
33040c0d06caSMauro Carvalho Chehab }
33050c0d06caSMauro Carvalho Chehab 
33060c0d06caSMauro Carvalho Chehab 
pvr2_hdw_trigger_module_log(struct pvr2_hdw * hdw)33070c0d06caSMauro Carvalho Chehab void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw)
33080c0d06caSMauro Carvalho Chehab {
33090c0d06caSMauro Carvalho Chehab 	int nr = pvr2_hdw_get_unit_number(hdw);
3310f419edd4SMauro Carvalho Chehab 	LOCK_TAKE(hdw->big_lock);
3311f419edd4SMauro Carvalho Chehab 	do {
33128f845c63SDafna Hirschfeld 		pr_info("pvrusb2: =================  START STATUS CARD #%d  =================\n", nr);
33130c0d06caSMauro Carvalho Chehab 		v4l2_device_call_all(&hdw->v4l2_dev, 0, core, log_status);
33140c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_INFO,"cx2341x config:");
33150c0d06caSMauro Carvalho Chehab 		cx2341x_log_status(&hdw->enc_ctl_state, "pvrusb2");
33160c0d06caSMauro Carvalho Chehab 		pvr2_hdw_state_log_state(hdw);
33178f845c63SDafna Hirschfeld 		pr_info("pvrusb2: ==================  END STATUS CARD #%d  ==================\n", nr);
3318f419edd4SMauro Carvalho Chehab 	} while (0);
3319f419edd4SMauro Carvalho Chehab 	LOCK_GIVE(hdw->big_lock);
33200c0d06caSMauro Carvalho Chehab }
33210c0d06caSMauro Carvalho Chehab 
33220c0d06caSMauro Carvalho Chehab 
33230c0d06caSMauro Carvalho Chehab /* Grab EEPROM contents, needed for direct method. */
33240c0d06caSMauro Carvalho Chehab #define EEPROM_SIZE 8192
33250c0d06caSMauro Carvalho Chehab #define trace_eeprom(...) pvr2_trace(PVR2_TRACE_EEPROM,__VA_ARGS__)
pvr2_full_eeprom_fetch(struct pvr2_hdw * hdw)33260c0d06caSMauro Carvalho Chehab static u8 *pvr2_full_eeprom_fetch(struct pvr2_hdw *hdw)
33270c0d06caSMauro Carvalho Chehab {
33280c0d06caSMauro Carvalho Chehab 	struct i2c_msg msg[2];
33290c0d06caSMauro Carvalho Chehab 	u8 *eeprom;
33300c0d06caSMauro Carvalho Chehab 	u8 iadd[2];
33310c0d06caSMauro Carvalho Chehab 	u8 addr;
33320c0d06caSMauro Carvalho Chehab 	u16 eepromSize;
33330c0d06caSMauro Carvalho Chehab 	unsigned int offs;
33340c0d06caSMauro Carvalho Chehab 	int ret;
33350c0d06caSMauro Carvalho Chehab 	int mode16 = 0;
33360c0d06caSMauro Carvalho Chehab 	unsigned pcnt,tcnt;
33375db8a692SFuqian Huang 	eeprom = kzalloc(EEPROM_SIZE, GFP_KERNEL);
33380c0d06caSMauro Carvalho Chehab 	if (!eeprom) {
33390c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
334096292c89SMauro Carvalho Chehab 			   "Failed to allocate memory required to read eeprom");
33410c0d06caSMauro Carvalho Chehab 		return NULL;
33420c0d06caSMauro Carvalho Chehab 	}
33430c0d06caSMauro Carvalho Chehab 
33440c0d06caSMauro Carvalho Chehab 	trace_eeprom("Value for eeprom addr from controller was 0x%x",
33450c0d06caSMauro Carvalho Chehab 		     hdw->eeprom_addr);
33460c0d06caSMauro Carvalho Chehab 	addr = hdw->eeprom_addr;
33470c0d06caSMauro Carvalho Chehab 	/* Seems that if the high bit is set, then the *real* eeprom
33480c0d06caSMauro Carvalho Chehab 	   address is shifted right now bit position (noticed this in
33490c0d06caSMauro Carvalho Chehab 	   newer PVR USB2 hardware) */
33500c0d06caSMauro Carvalho Chehab 	if (addr & 0x80) addr >>= 1;
33510c0d06caSMauro Carvalho Chehab 
33520c0d06caSMauro Carvalho Chehab 	/* FX2 documentation states that a 16bit-addressed eeprom is
33530c0d06caSMauro Carvalho Chehab 	   expected if the I2C address is an odd number (yeah, this is
33540c0d06caSMauro Carvalho Chehab 	   strange but it's what they do) */
33550c0d06caSMauro Carvalho Chehab 	mode16 = (addr & 1);
33560c0d06caSMauro Carvalho Chehab 	eepromSize = (mode16 ? EEPROM_SIZE : 256);
335796292c89SMauro Carvalho Chehab 	trace_eeprom("Examining %d byte eeprom at location 0x%x using %d bit addressing",
335896292c89SMauro Carvalho Chehab 		     eepromSize, addr,
33590c0d06caSMauro Carvalho Chehab 		     mode16 ? 16 : 8);
33600c0d06caSMauro Carvalho Chehab 
33610c0d06caSMauro Carvalho Chehab 	msg[0].addr = addr;
33620c0d06caSMauro Carvalho Chehab 	msg[0].flags = 0;
33630c0d06caSMauro Carvalho Chehab 	msg[0].len = mode16 ? 2 : 1;
33640c0d06caSMauro Carvalho Chehab 	msg[0].buf = iadd;
33650c0d06caSMauro Carvalho Chehab 	msg[1].addr = addr;
33660c0d06caSMauro Carvalho Chehab 	msg[1].flags = I2C_M_RD;
33670c0d06caSMauro Carvalho Chehab 
33680c0d06caSMauro Carvalho Chehab 	/* We have to do the actual eeprom data fetch ourselves, because
33690c0d06caSMauro Carvalho Chehab 	   (1) we're only fetching part of the eeprom, and (2) if we were
33700c0d06caSMauro Carvalho Chehab 	   getting the whole thing our I2C driver can't grab it in one
33710c0d06caSMauro Carvalho Chehab 	   pass - which is what tveeprom is otherwise going to attempt */
33720c0d06caSMauro Carvalho Chehab 	for (tcnt = 0; tcnt < EEPROM_SIZE; tcnt += pcnt) {
33730c0d06caSMauro Carvalho Chehab 		pcnt = 16;
33740c0d06caSMauro Carvalho Chehab 		if (pcnt + tcnt > EEPROM_SIZE) pcnt = EEPROM_SIZE-tcnt;
33750c0d06caSMauro Carvalho Chehab 		offs = tcnt + (eepromSize - EEPROM_SIZE);
33760c0d06caSMauro Carvalho Chehab 		if (mode16) {
33770c0d06caSMauro Carvalho Chehab 			iadd[0] = offs >> 8;
33780c0d06caSMauro Carvalho Chehab 			iadd[1] = offs;
33790c0d06caSMauro Carvalho Chehab 		} else {
33800c0d06caSMauro Carvalho Chehab 			iadd[0] = offs;
33810c0d06caSMauro Carvalho Chehab 		}
33820c0d06caSMauro Carvalho Chehab 		msg[1].len = pcnt;
33830c0d06caSMauro Carvalho Chehab 		msg[1].buf = eeprom+tcnt;
33840c0d06caSMauro Carvalho Chehab 		if ((ret = i2c_transfer(&hdw->i2c_adap,
33850c0d06caSMauro Carvalho Chehab 					msg,ARRAY_SIZE(msg))) != 2) {
33860c0d06caSMauro Carvalho Chehab 			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
33870c0d06caSMauro Carvalho Chehab 				   "eeprom fetch set offs err=%d",ret);
33880c0d06caSMauro Carvalho Chehab 			kfree(eeprom);
33890c0d06caSMauro Carvalho Chehab 			return NULL;
33900c0d06caSMauro Carvalho Chehab 		}
33910c0d06caSMauro Carvalho Chehab 	}
33920c0d06caSMauro Carvalho Chehab 	return eeprom;
33930c0d06caSMauro Carvalho Chehab }
33940c0d06caSMauro Carvalho Chehab 
33950c0d06caSMauro Carvalho Chehab 
pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw * hdw,int mode,int enable_flag)33960c0d06caSMauro Carvalho Chehab void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *hdw,
33970c0d06caSMauro Carvalho Chehab 				int mode,
33980c0d06caSMauro Carvalho Chehab 				int enable_flag)
33990c0d06caSMauro Carvalho Chehab {
34000c0d06caSMauro Carvalho Chehab 	int ret;
34010c0d06caSMauro Carvalho Chehab 	u16 address;
34020c0d06caSMauro Carvalho Chehab 	unsigned int pipe;
3403339df438SHans Verkuil 	LOCK_TAKE(hdw->big_lock);
3404339df438SHans Verkuil 	do {
34050c0d06caSMauro Carvalho Chehab 		if ((hdw->fw_buffer == NULL) == !enable_flag) break;
34060c0d06caSMauro Carvalho Chehab 
34070c0d06caSMauro Carvalho Chehab 		if (!enable_flag) {
34080c0d06caSMauro Carvalho Chehab 			pvr2_trace(PVR2_TRACE_FIRMWARE,
34090c0d06caSMauro Carvalho Chehab 				   "Cleaning up after CPU firmware fetch");
34100c0d06caSMauro Carvalho Chehab 			kfree(hdw->fw_buffer);
34110c0d06caSMauro Carvalho Chehab 			hdw->fw_buffer = NULL;
34120c0d06caSMauro Carvalho Chehab 			hdw->fw_size = 0;
34130c0d06caSMauro Carvalho Chehab 			if (hdw->fw_cpu_flag) {
34140c0d06caSMauro Carvalho Chehab 				/* Now release the CPU.  It will disconnect
34150c0d06caSMauro Carvalho Chehab 				   and reconnect later. */
34160c0d06caSMauro Carvalho Chehab 				pvr2_hdw_cpureset_assert(hdw,0);
34170c0d06caSMauro Carvalho Chehab 			}
34180c0d06caSMauro Carvalho Chehab 			break;
34190c0d06caSMauro Carvalho Chehab 		}
34200c0d06caSMauro Carvalho Chehab 
34210c0d06caSMauro Carvalho Chehab 		hdw->fw_cpu_flag = (mode != 2);
34220c0d06caSMauro Carvalho Chehab 		if (hdw->fw_cpu_flag) {
34230c0d06caSMauro Carvalho Chehab 			hdw->fw_size = (mode == 1) ? 0x4000 : 0x2000;
34240c0d06caSMauro Carvalho Chehab 			pvr2_trace(PVR2_TRACE_FIRMWARE,
342596292c89SMauro Carvalho Chehab 				   "Preparing to suck out CPU firmware (size=%u)",
342696292c89SMauro Carvalho Chehab 				   hdw->fw_size);
34270c0d06caSMauro Carvalho Chehab 			hdw->fw_buffer = kzalloc(hdw->fw_size,GFP_KERNEL);
34280c0d06caSMauro Carvalho Chehab 			if (!hdw->fw_buffer) {
34290c0d06caSMauro Carvalho Chehab 				hdw->fw_size = 0;
34300c0d06caSMauro Carvalho Chehab 				break;
34310c0d06caSMauro Carvalho Chehab 			}
34320c0d06caSMauro Carvalho Chehab 
34330c0d06caSMauro Carvalho Chehab 			/* We have to hold the CPU during firmware upload. */
34340c0d06caSMauro Carvalho Chehab 			pvr2_hdw_cpureset_assert(hdw,1);
34350c0d06caSMauro Carvalho Chehab 
34360c0d06caSMauro Carvalho Chehab 			/* download the firmware from address 0000-1fff in 2048
34370c0d06caSMauro Carvalho Chehab 			   (=0x800) bytes chunk. */
34380c0d06caSMauro Carvalho Chehab 
34390c0d06caSMauro Carvalho Chehab 			pvr2_trace(PVR2_TRACE_FIRMWARE,
34400c0d06caSMauro Carvalho Chehab 				   "Grabbing CPU firmware");
34410c0d06caSMauro Carvalho Chehab 			pipe = usb_rcvctrlpipe(hdw->usb_dev, 0);
34420c0d06caSMauro Carvalho Chehab 			for(address = 0; address < hdw->fw_size;
34430c0d06caSMauro Carvalho Chehab 			    address += 0x800) {
34440c0d06caSMauro Carvalho Chehab 				ret = usb_control_msg(hdw->usb_dev,pipe,
34450c0d06caSMauro Carvalho Chehab 						      0xa0,0xc0,
34460c0d06caSMauro Carvalho Chehab 						      address,0,
34470c0d06caSMauro Carvalho Chehab 						      hdw->fw_buffer+address,
3448b82bf9b9SJohan Hovold 						      0x800,1000);
34490c0d06caSMauro Carvalho Chehab 				if (ret < 0) break;
34500c0d06caSMauro Carvalho Chehab 			}
34510c0d06caSMauro Carvalho Chehab 
34520c0d06caSMauro Carvalho Chehab 			pvr2_trace(PVR2_TRACE_FIRMWARE,
34530c0d06caSMauro Carvalho Chehab 				   "Done grabbing CPU firmware");
34540c0d06caSMauro Carvalho Chehab 		} else {
34550c0d06caSMauro Carvalho Chehab 			pvr2_trace(PVR2_TRACE_FIRMWARE,
34560c0d06caSMauro Carvalho Chehab 				   "Sucking down EEPROM contents");
34570c0d06caSMauro Carvalho Chehab 			hdw->fw_buffer = pvr2_full_eeprom_fetch(hdw);
34580c0d06caSMauro Carvalho Chehab 			if (!hdw->fw_buffer) {
34590c0d06caSMauro Carvalho Chehab 				pvr2_trace(PVR2_TRACE_FIRMWARE,
34600c0d06caSMauro Carvalho Chehab 					   "EEPROM content suck failed.");
34610c0d06caSMauro Carvalho Chehab 				break;
34620c0d06caSMauro Carvalho Chehab 			}
34630c0d06caSMauro Carvalho Chehab 			hdw->fw_size = EEPROM_SIZE;
34640c0d06caSMauro Carvalho Chehab 			pvr2_trace(PVR2_TRACE_FIRMWARE,
34650c0d06caSMauro Carvalho Chehab 				   "Done sucking down EEPROM contents");
34660c0d06caSMauro Carvalho Chehab 		}
3467339df438SHans Verkuil 	} while (0);
3468339df438SHans Verkuil 	LOCK_GIVE(hdw->big_lock);
34690c0d06caSMauro Carvalho Chehab }
34700c0d06caSMauro Carvalho Chehab 
34710c0d06caSMauro Carvalho Chehab 
34720c0d06caSMauro Carvalho Chehab /* Return true if we're in a mode for retrieval CPU firmware */
pvr2_hdw_cpufw_get_enabled(struct pvr2_hdw * hdw)34730c0d06caSMauro Carvalho Chehab int pvr2_hdw_cpufw_get_enabled(struct pvr2_hdw *hdw)
34740c0d06caSMauro Carvalho Chehab {
34750c0d06caSMauro Carvalho Chehab 	return hdw->fw_buffer != NULL;
34760c0d06caSMauro Carvalho Chehab }
34770c0d06caSMauro Carvalho Chehab 
34780c0d06caSMauro Carvalho Chehab 
pvr2_hdw_cpufw_get(struct pvr2_hdw * hdw,unsigned int offs,char * buf,unsigned int cnt)34790c0d06caSMauro Carvalho Chehab int pvr2_hdw_cpufw_get(struct pvr2_hdw *hdw,unsigned int offs,
34800c0d06caSMauro Carvalho Chehab 		       char *buf,unsigned int cnt)
34810c0d06caSMauro Carvalho Chehab {
34820c0d06caSMauro Carvalho Chehab 	int ret = -EINVAL;
3483339df438SHans Verkuil 	LOCK_TAKE(hdw->big_lock);
3484339df438SHans Verkuil 	do {
34850c0d06caSMauro Carvalho Chehab 		if (!buf) break;
34860c0d06caSMauro Carvalho Chehab 		if (!cnt) break;
34870c0d06caSMauro Carvalho Chehab 
34880c0d06caSMauro Carvalho Chehab 		if (!hdw->fw_buffer) {
34890c0d06caSMauro Carvalho Chehab 			ret = -EIO;
34900c0d06caSMauro Carvalho Chehab 			break;
34910c0d06caSMauro Carvalho Chehab 		}
34920c0d06caSMauro Carvalho Chehab 
34930c0d06caSMauro Carvalho Chehab 		if (offs >= hdw->fw_size) {
34940c0d06caSMauro Carvalho Chehab 			pvr2_trace(PVR2_TRACE_FIRMWARE,
34950c0d06caSMauro Carvalho Chehab 				   "Read firmware data offs=%d EOF",
34960c0d06caSMauro Carvalho Chehab 				   offs);
34970c0d06caSMauro Carvalho Chehab 			ret = 0;
34980c0d06caSMauro Carvalho Chehab 			break;
34990c0d06caSMauro Carvalho Chehab 		}
35000c0d06caSMauro Carvalho Chehab 
35010c0d06caSMauro Carvalho Chehab 		if (offs + cnt > hdw->fw_size) cnt = hdw->fw_size - offs;
35020c0d06caSMauro Carvalho Chehab 
35030c0d06caSMauro Carvalho Chehab 		memcpy(buf,hdw->fw_buffer+offs,cnt);
35040c0d06caSMauro Carvalho Chehab 
35050c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_FIRMWARE,
35060c0d06caSMauro Carvalho Chehab 			   "Read firmware data offs=%d cnt=%d",
35070c0d06caSMauro Carvalho Chehab 			   offs,cnt);
35080c0d06caSMauro Carvalho Chehab 		ret = cnt;
3509339df438SHans Verkuil 	} while (0);
3510339df438SHans Verkuil 	LOCK_GIVE(hdw->big_lock);
35110c0d06caSMauro Carvalho Chehab 
35120c0d06caSMauro Carvalho Chehab 	return ret;
35130c0d06caSMauro Carvalho Chehab }
35140c0d06caSMauro Carvalho Chehab 
35150c0d06caSMauro Carvalho Chehab 
pvr2_hdw_v4l_get_minor_number(struct pvr2_hdw * hdw,enum pvr2_v4l_type index)35160c0d06caSMauro Carvalho Chehab int pvr2_hdw_v4l_get_minor_number(struct pvr2_hdw *hdw,
35170c0d06caSMauro Carvalho Chehab 				  enum pvr2_v4l_type index)
35180c0d06caSMauro Carvalho Chehab {
35190c0d06caSMauro Carvalho Chehab 	switch (index) {
35200c0d06caSMauro Carvalho Chehab 	case pvr2_v4l_type_video: return hdw->v4l_minor_number_video;
35210c0d06caSMauro Carvalho Chehab 	case pvr2_v4l_type_vbi: return hdw->v4l_minor_number_vbi;
35220c0d06caSMauro Carvalho Chehab 	case pvr2_v4l_type_radio: return hdw->v4l_minor_number_radio;
35230c0d06caSMauro Carvalho Chehab 	default: return -1;
35240c0d06caSMauro Carvalho Chehab 	}
35250c0d06caSMauro Carvalho Chehab }
35260c0d06caSMauro Carvalho Chehab 
35270c0d06caSMauro Carvalho Chehab 
35280c0d06caSMauro Carvalho Chehab /* Store a v4l minor device number */
pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw * hdw,enum pvr2_v4l_type index,int v)35290c0d06caSMauro Carvalho Chehab void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *hdw,
35300c0d06caSMauro Carvalho Chehab 				     enum pvr2_v4l_type index,int v)
35310c0d06caSMauro Carvalho Chehab {
35320c0d06caSMauro Carvalho Chehab 	switch (index) {
35336c058fb6SAlan Cox 	case pvr2_v4l_type_video: hdw->v4l_minor_number_video = v;break;
35346c058fb6SAlan Cox 	case pvr2_v4l_type_vbi: hdw->v4l_minor_number_vbi = v;break;
35356c058fb6SAlan Cox 	case pvr2_v4l_type_radio: hdw->v4l_minor_number_radio = v;break;
35360c0d06caSMauro Carvalho Chehab 	default: break;
35370c0d06caSMauro Carvalho Chehab 	}
35380c0d06caSMauro Carvalho Chehab }
35390c0d06caSMauro Carvalho Chehab 
35400c0d06caSMauro Carvalho Chehab 
pvr2_ctl_write_complete(struct urb * urb)35410c0d06caSMauro Carvalho Chehab static void pvr2_ctl_write_complete(struct urb *urb)
35420c0d06caSMauro Carvalho Chehab {
35430c0d06caSMauro Carvalho Chehab 	struct pvr2_hdw *hdw = urb->context;
35440c0d06caSMauro Carvalho Chehab 	hdw->ctl_write_pend_flag = 0;
35450c0d06caSMauro Carvalho Chehab 	if (hdw->ctl_read_pend_flag) return;
35460c0d06caSMauro Carvalho Chehab 	complete(&hdw->ctl_done);
35470c0d06caSMauro Carvalho Chehab }
35480c0d06caSMauro Carvalho Chehab 
35490c0d06caSMauro Carvalho Chehab 
pvr2_ctl_read_complete(struct urb * urb)35500c0d06caSMauro Carvalho Chehab static void pvr2_ctl_read_complete(struct urb *urb)
35510c0d06caSMauro Carvalho Chehab {
35520c0d06caSMauro Carvalho Chehab 	struct pvr2_hdw *hdw = urb->context;
35530c0d06caSMauro Carvalho Chehab 	hdw->ctl_read_pend_flag = 0;
35540c0d06caSMauro Carvalho Chehab 	if (hdw->ctl_write_pend_flag) return;
35550c0d06caSMauro Carvalho Chehab 	complete(&hdw->ctl_done);
35560c0d06caSMauro Carvalho Chehab }
35570c0d06caSMauro Carvalho Chehab 
35588da0edf2SKees Cook struct hdw_timer {
35598da0edf2SKees Cook 	struct timer_list timer;
35608da0edf2SKees Cook 	struct pvr2_hdw *hdw;
35618da0edf2SKees Cook };
35620c0d06caSMauro Carvalho Chehab 
pvr2_ctl_timeout(struct timer_list * t)35638da0edf2SKees Cook static void pvr2_ctl_timeout(struct timer_list *t)
35640c0d06caSMauro Carvalho Chehab {
35658da0edf2SKees Cook 	struct hdw_timer *timer = from_timer(timer, t, timer);
35668da0edf2SKees Cook 	struct pvr2_hdw *hdw = timer->hdw;
35678da0edf2SKees Cook 
35680c0d06caSMauro Carvalho Chehab 	if (hdw->ctl_write_pend_flag || hdw->ctl_read_pend_flag) {
35690c0d06caSMauro Carvalho Chehab 		hdw->ctl_timeout_flag = !0;
35700c0d06caSMauro Carvalho Chehab 		if (hdw->ctl_write_pend_flag)
35710c0d06caSMauro Carvalho Chehab 			usb_unlink_urb(hdw->ctl_write_urb);
35720c0d06caSMauro Carvalho Chehab 		if (hdw->ctl_read_pend_flag)
35730c0d06caSMauro Carvalho Chehab 			usb_unlink_urb(hdw->ctl_read_urb);
35740c0d06caSMauro Carvalho Chehab 	}
35750c0d06caSMauro Carvalho Chehab }
35760c0d06caSMauro Carvalho Chehab 
35770c0d06caSMauro Carvalho Chehab 
35780c0d06caSMauro Carvalho Chehab /* Issue a command and get a response from the device.  This extended
35790c0d06caSMauro Carvalho Chehab    version includes a probe flag (which if set means that device errors
35800c0d06caSMauro Carvalho Chehab    should not be logged or treated as fatal) and a timeout in jiffies.
35810c0d06caSMauro Carvalho Chehab    This can be used to non-lethally probe the health of endpoint 1. */
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)35820c0d06caSMauro Carvalho Chehab static int pvr2_send_request_ex(struct pvr2_hdw *hdw,
35830c0d06caSMauro Carvalho Chehab 				unsigned int timeout,int probe_fl,
35840c0d06caSMauro Carvalho Chehab 				void *write_data,unsigned int write_len,
35850c0d06caSMauro Carvalho Chehab 				void *read_data,unsigned int read_len)
35860c0d06caSMauro Carvalho Chehab {
35870c0d06caSMauro Carvalho Chehab 	unsigned int idx;
35880c0d06caSMauro Carvalho Chehab 	int status = 0;
35898da0edf2SKees Cook 	struct hdw_timer timer = {
35908da0edf2SKees Cook 		.hdw = hdw,
35918da0edf2SKees Cook 	};
35928da0edf2SKees Cook 
35930c0d06caSMauro Carvalho Chehab 	if (!hdw->ctl_lock_held) {
35940c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
359596292c89SMauro Carvalho Chehab 			   "Attempted to execute control transfer without lock!!");
35960c0d06caSMauro Carvalho Chehab 		return -EDEADLK;
35970c0d06caSMauro Carvalho Chehab 	}
35980c0d06caSMauro Carvalho Chehab 	if (!hdw->flag_ok && !probe_fl) {
35990c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
360096292c89SMauro Carvalho Chehab 			   "Attempted to execute control transfer when device not ok");
36010c0d06caSMauro Carvalho Chehab 		return -EIO;
36020c0d06caSMauro Carvalho Chehab 	}
36030c0d06caSMauro Carvalho Chehab 	if (!(hdw->ctl_read_urb && hdw->ctl_write_urb)) {
36040c0d06caSMauro Carvalho Chehab 		if (!probe_fl) {
36050c0d06caSMauro Carvalho Chehab 			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
360696292c89SMauro Carvalho Chehab 				   "Attempted to execute control transfer when USB is disconnected");
36070c0d06caSMauro Carvalho Chehab 		}
36080c0d06caSMauro Carvalho Chehab 		return -ENOTTY;
36090c0d06caSMauro Carvalho Chehab 	}
36100c0d06caSMauro Carvalho Chehab 
36110c0d06caSMauro Carvalho Chehab 	/* Ensure that we have sane parameters */
36120c0d06caSMauro Carvalho Chehab 	if (!write_data) write_len = 0;
36130c0d06caSMauro Carvalho Chehab 	if (!read_data) read_len = 0;
36140c0d06caSMauro Carvalho Chehab 	if (write_len > PVR2_CTL_BUFFSIZE) {
36150c0d06caSMauro Carvalho Chehab 		pvr2_trace(
36160c0d06caSMauro Carvalho Chehab 			PVR2_TRACE_ERROR_LEGS,
361796292c89SMauro Carvalho Chehab 			"Attempted to execute %d byte control-write transfer (limit=%d)",
36180c0d06caSMauro Carvalho Chehab 			write_len,PVR2_CTL_BUFFSIZE);
36190c0d06caSMauro Carvalho Chehab 		return -EINVAL;
36200c0d06caSMauro Carvalho Chehab 	}
36210c0d06caSMauro Carvalho Chehab 	if (read_len > PVR2_CTL_BUFFSIZE) {
36220c0d06caSMauro Carvalho Chehab 		pvr2_trace(
36230c0d06caSMauro Carvalho Chehab 			PVR2_TRACE_ERROR_LEGS,
362496292c89SMauro Carvalho Chehab 			"Attempted to execute %d byte control-read transfer (limit=%d)",
36250c0d06caSMauro Carvalho Chehab 			write_len,PVR2_CTL_BUFFSIZE);
36260c0d06caSMauro Carvalho Chehab 		return -EINVAL;
36270c0d06caSMauro Carvalho Chehab 	}
36280c0d06caSMauro Carvalho Chehab 	if ((!write_len) && (!read_len)) {
36290c0d06caSMauro Carvalho Chehab 		pvr2_trace(
36300c0d06caSMauro Carvalho Chehab 			PVR2_TRACE_ERROR_LEGS,
36310c0d06caSMauro Carvalho Chehab 			"Attempted to execute null control transfer?");
36320c0d06caSMauro Carvalho Chehab 		return -EINVAL;
36330c0d06caSMauro Carvalho Chehab 	}
36340c0d06caSMauro Carvalho Chehab 
36350c0d06caSMauro Carvalho Chehab 
36360c0d06caSMauro Carvalho Chehab 	hdw->cmd_debug_state = 1;
363762220293SHans Verkuil 	if (write_len && write_data)
36380c0d06caSMauro Carvalho Chehab 		hdw->cmd_debug_code = ((unsigned char *)write_data)[0];
363962220293SHans Verkuil 	else
36400c0d06caSMauro Carvalho Chehab 		hdw->cmd_debug_code = 0;
36410c0d06caSMauro Carvalho Chehab 	hdw->cmd_debug_write_len = write_len;
36420c0d06caSMauro Carvalho Chehab 	hdw->cmd_debug_read_len = read_len;
36430c0d06caSMauro Carvalho Chehab 
36440c0d06caSMauro Carvalho Chehab 	/* Initialize common stuff */
36450c0d06caSMauro Carvalho Chehab 	init_completion(&hdw->ctl_done);
36460c0d06caSMauro Carvalho Chehab 	hdw->ctl_timeout_flag = 0;
36470c0d06caSMauro Carvalho Chehab 	hdw->ctl_write_pend_flag = 0;
36480c0d06caSMauro Carvalho Chehab 	hdw->ctl_read_pend_flag = 0;
36498da0edf2SKees Cook 	timer_setup_on_stack(&timer.timer, pvr2_ctl_timeout, 0);
36508da0edf2SKees Cook 	timer.timer.expires = jiffies + timeout;
36510c0d06caSMauro Carvalho Chehab 
365262220293SHans Verkuil 	if (write_len && write_data) {
36530c0d06caSMauro Carvalho Chehab 		hdw->cmd_debug_state = 2;
36540c0d06caSMauro Carvalho Chehab 		/* Transfer write data to internal buffer */
36550c0d06caSMauro Carvalho Chehab 		for (idx = 0; idx < write_len; idx++) {
36560c0d06caSMauro Carvalho Chehab 			hdw->ctl_write_buffer[idx] =
36570c0d06caSMauro Carvalho Chehab 				((unsigned char *)write_data)[idx];
36580c0d06caSMauro Carvalho Chehab 		}
36590c0d06caSMauro Carvalho Chehab 		/* Initiate a write request */
36600c0d06caSMauro Carvalho Chehab 		usb_fill_bulk_urb(hdw->ctl_write_urb,
36610c0d06caSMauro Carvalho Chehab 				  hdw->usb_dev,
36620c0d06caSMauro Carvalho Chehab 				  usb_sndbulkpipe(hdw->usb_dev,
36630c0d06caSMauro Carvalho Chehab 						  PVR2_CTL_WRITE_ENDPOINT),
36640c0d06caSMauro Carvalho Chehab 				  hdw->ctl_write_buffer,
36650c0d06caSMauro Carvalho Chehab 				  write_len,
36660c0d06caSMauro Carvalho Chehab 				  pvr2_ctl_write_complete,
36670c0d06caSMauro Carvalho Chehab 				  hdw);
36680c0d06caSMauro Carvalho Chehab 		hdw->ctl_write_urb->actual_length = 0;
36690c0d06caSMauro Carvalho Chehab 		hdw->ctl_write_pend_flag = !0;
367072c27a68SAndrey Konovalov 		if (usb_urb_ep_type_check(hdw->ctl_write_urb)) {
367172c27a68SAndrey Konovalov 			pvr2_trace(
367272c27a68SAndrey Konovalov 				PVR2_TRACE_ERROR_LEGS,
367372c27a68SAndrey Konovalov 				"Invalid write control endpoint");
367472c27a68SAndrey Konovalov 			return -EINVAL;
367572c27a68SAndrey Konovalov 		}
36760c0d06caSMauro Carvalho Chehab 		status = usb_submit_urb(hdw->ctl_write_urb,GFP_KERNEL);
36770c0d06caSMauro Carvalho Chehab 		if (status < 0) {
36780c0d06caSMauro Carvalho Chehab 			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
367996292c89SMauro Carvalho Chehab 				   "Failed to submit write-control URB status=%d",
368096292c89SMauro Carvalho Chehab status);
36810c0d06caSMauro Carvalho Chehab 			hdw->ctl_write_pend_flag = 0;
36820c0d06caSMauro Carvalho Chehab 			goto done;
36830c0d06caSMauro Carvalho Chehab 		}
36840c0d06caSMauro Carvalho Chehab 	}
36850c0d06caSMauro Carvalho Chehab 
36860c0d06caSMauro Carvalho Chehab 	if (read_len) {
36870c0d06caSMauro Carvalho Chehab 		hdw->cmd_debug_state = 3;
36880c0d06caSMauro Carvalho Chehab 		memset(hdw->ctl_read_buffer,0x43,read_len);
36890c0d06caSMauro Carvalho Chehab 		/* Initiate a read request */
36900c0d06caSMauro Carvalho Chehab 		usb_fill_bulk_urb(hdw->ctl_read_urb,
36910c0d06caSMauro Carvalho Chehab 				  hdw->usb_dev,
36920c0d06caSMauro Carvalho Chehab 				  usb_rcvbulkpipe(hdw->usb_dev,
36930c0d06caSMauro Carvalho Chehab 						  PVR2_CTL_READ_ENDPOINT),
36940c0d06caSMauro Carvalho Chehab 				  hdw->ctl_read_buffer,
36950c0d06caSMauro Carvalho Chehab 				  read_len,
36960c0d06caSMauro Carvalho Chehab 				  pvr2_ctl_read_complete,
36970c0d06caSMauro Carvalho Chehab 				  hdw);
36980c0d06caSMauro Carvalho Chehab 		hdw->ctl_read_urb->actual_length = 0;
36990c0d06caSMauro Carvalho Chehab 		hdw->ctl_read_pend_flag = !0;
370072c27a68SAndrey Konovalov 		if (usb_urb_ep_type_check(hdw->ctl_read_urb)) {
370172c27a68SAndrey Konovalov 			pvr2_trace(
370272c27a68SAndrey Konovalov 				PVR2_TRACE_ERROR_LEGS,
370372c27a68SAndrey Konovalov 				"Invalid read control endpoint");
370472c27a68SAndrey Konovalov 			return -EINVAL;
370572c27a68SAndrey Konovalov 		}
37060c0d06caSMauro Carvalho Chehab 		status = usb_submit_urb(hdw->ctl_read_urb,GFP_KERNEL);
37070c0d06caSMauro Carvalho Chehab 		if (status < 0) {
37080c0d06caSMauro Carvalho Chehab 			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
370996292c89SMauro Carvalho Chehab 				   "Failed to submit read-control URB status=%d",
371096292c89SMauro Carvalho Chehab status);
37110c0d06caSMauro Carvalho Chehab 			hdw->ctl_read_pend_flag = 0;
37120c0d06caSMauro Carvalho Chehab 			goto done;
37130c0d06caSMauro Carvalho Chehab 		}
37140c0d06caSMauro Carvalho Chehab 	}
37150c0d06caSMauro Carvalho Chehab 
37160c0d06caSMauro Carvalho Chehab 	/* Start timer */
37178da0edf2SKees Cook 	add_timer(&timer.timer);
37180c0d06caSMauro Carvalho Chehab 
37190c0d06caSMauro Carvalho Chehab 	/* Now wait for all I/O to complete */
37200c0d06caSMauro Carvalho Chehab 	hdw->cmd_debug_state = 4;
37210c0d06caSMauro Carvalho Chehab 	while (hdw->ctl_write_pend_flag || hdw->ctl_read_pend_flag) {
37220c0d06caSMauro Carvalho Chehab 		wait_for_completion(&hdw->ctl_done);
37230c0d06caSMauro Carvalho Chehab 	}
37240c0d06caSMauro Carvalho Chehab 	hdw->cmd_debug_state = 5;
37250c0d06caSMauro Carvalho Chehab 
37260c0d06caSMauro Carvalho Chehab 	/* Stop timer */
37278da0edf2SKees Cook 	del_timer_sync(&timer.timer);
37280c0d06caSMauro Carvalho Chehab 
37290c0d06caSMauro Carvalho Chehab 	hdw->cmd_debug_state = 6;
37300c0d06caSMauro Carvalho Chehab 	status = 0;
37310c0d06caSMauro Carvalho Chehab 
37320c0d06caSMauro Carvalho Chehab 	if (hdw->ctl_timeout_flag) {
37330c0d06caSMauro Carvalho Chehab 		status = -ETIMEDOUT;
37340c0d06caSMauro Carvalho Chehab 		if (!probe_fl) {
37350c0d06caSMauro Carvalho Chehab 			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
37360c0d06caSMauro Carvalho Chehab 				   "Timed out control-write");
37370c0d06caSMauro Carvalho Chehab 		}
37380c0d06caSMauro Carvalho Chehab 		goto done;
37390c0d06caSMauro Carvalho Chehab 	}
37400c0d06caSMauro Carvalho Chehab 
37410c0d06caSMauro Carvalho Chehab 	if (write_len) {
37420c0d06caSMauro Carvalho Chehab 		/* Validate results of write request */
37430c0d06caSMauro Carvalho Chehab 		if ((hdw->ctl_write_urb->status != 0) &&
37440c0d06caSMauro Carvalho Chehab 		    (hdw->ctl_write_urb->status != -ENOENT) &&
37450c0d06caSMauro Carvalho Chehab 		    (hdw->ctl_write_urb->status != -ESHUTDOWN) &&
37460c0d06caSMauro Carvalho Chehab 		    (hdw->ctl_write_urb->status != -ECONNRESET)) {
37470c0d06caSMauro Carvalho Chehab 			/* USB subsystem is reporting some kind of failure
37480c0d06caSMauro Carvalho Chehab 			   on the write */
37490c0d06caSMauro Carvalho Chehab 			status = hdw->ctl_write_urb->status;
37500c0d06caSMauro Carvalho Chehab 			if (!probe_fl) {
37510c0d06caSMauro Carvalho Chehab 				pvr2_trace(PVR2_TRACE_ERROR_LEGS,
375296292c89SMauro Carvalho Chehab 					   "control-write URB failure, status=%d",
37530c0d06caSMauro Carvalho Chehab 					   status);
37540c0d06caSMauro Carvalho Chehab 			}
37550c0d06caSMauro Carvalho Chehab 			goto done;
37560c0d06caSMauro Carvalho Chehab 		}
37570c0d06caSMauro Carvalho Chehab 		if (hdw->ctl_write_urb->actual_length < write_len) {
37580c0d06caSMauro Carvalho Chehab 			/* Failed to write enough data */
37590c0d06caSMauro Carvalho Chehab 			status = -EIO;
37600c0d06caSMauro Carvalho Chehab 			if (!probe_fl) {
37610c0d06caSMauro Carvalho Chehab 				pvr2_trace(PVR2_TRACE_ERROR_LEGS,
376296292c89SMauro Carvalho Chehab 					   "control-write URB short, expected=%d got=%d",
37630c0d06caSMauro Carvalho Chehab 					   write_len,
37640c0d06caSMauro Carvalho Chehab 					   hdw->ctl_write_urb->actual_length);
37650c0d06caSMauro Carvalho Chehab 			}
37660c0d06caSMauro Carvalho Chehab 			goto done;
37670c0d06caSMauro Carvalho Chehab 		}
37680c0d06caSMauro Carvalho Chehab 	}
376962220293SHans Verkuil 	if (read_len && read_data) {
37700c0d06caSMauro Carvalho Chehab 		/* Validate results of read request */
37710c0d06caSMauro Carvalho Chehab 		if ((hdw->ctl_read_urb->status != 0) &&
37720c0d06caSMauro Carvalho Chehab 		    (hdw->ctl_read_urb->status != -ENOENT) &&
37730c0d06caSMauro Carvalho Chehab 		    (hdw->ctl_read_urb->status != -ESHUTDOWN) &&
37740c0d06caSMauro Carvalho Chehab 		    (hdw->ctl_read_urb->status != -ECONNRESET)) {
37750c0d06caSMauro Carvalho Chehab 			/* USB subsystem is reporting some kind of failure
37760c0d06caSMauro Carvalho Chehab 			   on the read */
37770c0d06caSMauro Carvalho Chehab 			status = hdw->ctl_read_urb->status;
37780c0d06caSMauro Carvalho Chehab 			if (!probe_fl) {
37790c0d06caSMauro Carvalho Chehab 				pvr2_trace(PVR2_TRACE_ERROR_LEGS,
378096292c89SMauro Carvalho Chehab 					   "control-read URB failure, status=%d",
37810c0d06caSMauro Carvalho Chehab 					   status);
37820c0d06caSMauro Carvalho Chehab 			}
37830c0d06caSMauro Carvalho Chehab 			goto done;
37840c0d06caSMauro Carvalho Chehab 		}
37850c0d06caSMauro Carvalho Chehab 		if (hdw->ctl_read_urb->actual_length < read_len) {
37860c0d06caSMauro Carvalho Chehab 			/* Failed to read enough data */
37870c0d06caSMauro Carvalho Chehab 			status = -EIO;
37880c0d06caSMauro Carvalho Chehab 			if (!probe_fl) {
37890c0d06caSMauro Carvalho Chehab 				pvr2_trace(PVR2_TRACE_ERROR_LEGS,
379096292c89SMauro Carvalho Chehab 					   "control-read URB short, expected=%d got=%d",
37910c0d06caSMauro Carvalho Chehab 					   read_len,
37920c0d06caSMauro Carvalho Chehab 					   hdw->ctl_read_urb->actual_length);
37930c0d06caSMauro Carvalho Chehab 			}
37940c0d06caSMauro Carvalho Chehab 			goto done;
37950c0d06caSMauro Carvalho Chehab 		}
37960c0d06caSMauro Carvalho Chehab 		/* Transfer retrieved data out from internal buffer */
37970c0d06caSMauro Carvalho Chehab 		for (idx = 0; idx < read_len; idx++) {
37980c0d06caSMauro Carvalho Chehab 			((unsigned char *)read_data)[idx] =
37990c0d06caSMauro Carvalho Chehab 				hdw->ctl_read_buffer[idx];
38000c0d06caSMauro Carvalho Chehab 		}
38010c0d06caSMauro Carvalho Chehab 	}
38020c0d06caSMauro Carvalho Chehab 
38030c0d06caSMauro Carvalho Chehab  done:
38040c0d06caSMauro Carvalho Chehab 
38050c0d06caSMauro Carvalho Chehab 	hdw->cmd_debug_state = 0;
38060c0d06caSMauro Carvalho Chehab 	if ((status < 0) && (!probe_fl)) {
38070c0d06caSMauro Carvalho Chehab 		pvr2_hdw_render_useless(hdw);
38080c0d06caSMauro Carvalho Chehab 	}
38098da0edf2SKees Cook 	destroy_timer_on_stack(&timer.timer);
38108da0edf2SKees Cook 
38110c0d06caSMauro Carvalho Chehab 	return status;
38120c0d06caSMauro Carvalho Chehab }
38130c0d06caSMauro Carvalho Chehab 
38140c0d06caSMauro Carvalho Chehab 
pvr2_send_request(struct pvr2_hdw * hdw,void * write_data,unsigned int write_len,void * read_data,unsigned int read_len)38150c0d06caSMauro Carvalho Chehab int pvr2_send_request(struct pvr2_hdw *hdw,
38160c0d06caSMauro Carvalho Chehab 		      void *write_data,unsigned int write_len,
38170c0d06caSMauro Carvalho Chehab 		      void *read_data,unsigned int read_len)
38180c0d06caSMauro Carvalho Chehab {
38190c0d06caSMauro Carvalho Chehab 	return pvr2_send_request_ex(hdw,HZ*4,0,
38200c0d06caSMauro Carvalho Chehab 				    write_data,write_len,
38210c0d06caSMauro Carvalho Chehab 				    read_data,read_len);
38220c0d06caSMauro Carvalho Chehab }
38230c0d06caSMauro Carvalho Chehab 
38240c0d06caSMauro Carvalho Chehab 
pvr2_issue_simple_cmd(struct pvr2_hdw * hdw,u32 cmdcode)38250c0d06caSMauro Carvalho Chehab static int pvr2_issue_simple_cmd(struct pvr2_hdw *hdw,u32 cmdcode)
38260c0d06caSMauro Carvalho Chehab {
38270c0d06caSMauro Carvalho Chehab 	int ret;
38280c0d06caSMauro Carvalho Chehab 	unsigned int cnt = 1;
38290c0d06caSMauro Carvalho Chehab 	unsigned int args = 0;
38300c0d06caSMauro Carvalho Chehab 	LOCK_TAKE(hdw->ctl_lock);
38310c0d06caSMauro Carvalho Chehab 	hdw->cmd_buffer[0] = cmdcode & 0xffu;
38320c0d06caSMauro Carvalho Chehab 	args = (cmdcode >> 8) & 0xffu;
38330c0d06caSMauro Carvalho Chehab 	args = (args > 2) ? 2 : args;
38340c0d06caSMauro Carvalho Chehab 	if (args) {
38350c0d06caSMauro Carvalho Chehab 		cnt += args;
38360c0d06caSMauro Carvalho Chehab 		hdw->cmd_buffer[1] = (cmdcode >> 16) & 0xffu;
38370c0d06caSMauro Carvalho Chehab 		if (args > 1) {
38380c0d06caSMauro Carvalho Chehab 			hdw->cmd_buffer[2] = (cmdcode >> 24) & 0xffu;
38390c0d06caSMauro Carvalho Chehab 		}
38400c0d06caSMauro Carvalho Chehab 	}
38410c0d06caSMauro Carvalho Chehab 	if (pvrusb2_debug & PVR2_TRACE_INIT) {
38420c0d06caSMauro Carvalho Chehab 		unsigned int idx;
38430c0d06caSMauro Carvalho Chehab 		unsigned int ccnt,bcnt;
38440c0d06caSMauro Carvalho Chehab 		char tbuf[50];
38450c0d06caSMauro Carvalho Chehab 		cmdcode &= 0xffu;
38460c0d06caSMauro Carvalho Chehab 		bcnt = 0;
38470c0d06caSMauro Carvalho Chehab 		ccnt = scnprintf(tbuf+bcnt,
38480c0d06caSMauro Carvalho Chehab 				 sizeof(tbuf)-bcnt,
38490c0d06caSMauro Carvalho Chehab 				 "Sending FX2 command 0x%x",cmdcode);
38500c0d06caSMauro Carvalho Chehab 		bcnt += ccnt;
38510c0d06caSMauro Carvalho Chehab 		for (idx = 0; idx < ARRAY_SIZE(pvr2_fx2cmd_desc); idx++) {
38520c0d06caSMauro Carvalho Chehab 			if (pvr2_fx2cmd_desc[idx].id == cmdcode) {
38530c0d06caSMauro Carvalho Chehab 				ccnt = scnprintf(tbuf+bcnt,
38540c0d06caSMauro Carvalho Chehab 						 sizeof(tbuf)-bcnt,
38550c0d06caSMauro Carvalho Chehab 						 " \"%s\"",
38560c0d06caSMauro Carvalho Chehab 						 pvr2_fx2cmd_desc[idx].desc);
38570c0d06caSMauro Carvalho Chehab 				bcnt += ccnt;
38580c0d06caSMauro Carvalho Chehab 				break;
38590c0d06caSMauro Carvalho Chehab 			}
38600c0d06caSMauro Carvalho Chehab 		}
38610c0d06caSMauro Carvalho Chehab 		if (args) {
38620c0d06caSMauro Carvalho Chehab 			ccnt = scnprintf(tbuf+bcnt,
38630c0d06caSMauro Carvalho Chehab 					 sizeof(tbuf)-bcnt,
38640c0d06caSMauro Carvalho Chehab 					 " (%u",hdw->cmd_buffer[1]);
38650c0d06caSMauro Carvalho Chehab 			bcnt += ccnt;
38660c0d06caSMauro Carvalho Chehab 			if (args > 1) {
38670c0d06caSMauro Carvalho Chehab 				ccnt = scnprintf(tbuf+bcnt,
38680c0d06caSMauro Carvalho Chehab 						 sizeof(tbuf)-bcnt,
38690c0d06caSMauro Carvalho Chehab 						 ",%u",hdw->cmd_buffer[2]);
38700c0d06caSMauro Carvalho Chehab 				bcnt += ccnt;
38710c0d06caSMauro Carvalho Chehab 			}
38720c0d06caSMauro Carvalho Chehab 			ccnt = scnprintf(tbuf+bcnt,
38730c0d06caSMauro Carvalho Chehab 					 sizeof(tbuf)-bcnt,
38740c0d06caSMauro Carvalho Chehab 					 ")");
38750c0d06caSMauro Carvalho Chehab 			bcnt += ccnt;
38760c0d06caSMauro Carvalho Chehab 		}
38770c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_INIT,"%.*s",bcnt,tbuf);
38780c0d06caSMauro Carvalho Chehab 	}
38790c0d06caSMauro Carvalho Chehab 	ret = pvr2_send_request(hdw,hdw->cmd_buffer,cnt,NULL,0);
38800c0d06caSMauro Carvalho Chehab 	LOCK_GIVE(hdw->ctl_lock);
38810c0d06caSMauro Carvalho Chehab 	return ret;
38820c0d06caSMauro Carvalho Chehab }
38830c0d06caSMauro Carvalho Chehab 
38840c0d06caSMauro Carvalho Chehab 
pvr2_write_register(struct pvr2_hdw * hdw,u16 reg,u32 data)38850c0d06caSMauro Carvalho Chehab int pvr2_write_register(struct pvr2_hdw *hdw, u16 reg, u32 data)
38860c0d06caSMauro Carvalho Chehab {
38870c0d06caSMauro Carvalho Chehab 	int ret;
38880c0d06caSMauro Carvalho Chehab 
38890c0d06caSMauro Carvalho Chehab 	LOCK_TAKE(hdw->ctl_lock);
38900c0d06caSMauro Carvalho Chehab 
38910c0d06caSMauro Carvalho Chehab 	hdw->cmd_buffer[0] = FX2CMD_REG_WRITE;  /* write register prefix */
38920c0d06caSMauro Carvalho Chehab 	PVR2_DECOMPOSE_LE(hdw->cmd_buffer,1,data);
38930c0d06caSMauro Carvalho Chehab 	hdw->cmd_buffer[5] = 0;
38940c0d06caSMauro Carvalho Chehab 	hdw->cmd_buffer[6] = (reg >> 8) & 0xff;
38950c0d06caSMauro Carvalho Chehab 	hdw->cmd_buffer[7] = reg & 0xff;
38960c0d06caSMauro Carvalho Chehab 
38970c0d06caSMauro Carvalho Chehab 
38980c0d06caSMauro Carvalho Chehab 	ret = pvr2_send_request(hdw, hdw->cmd_buffer, 8, hdw->cmd_buffer, 0);
38990c0d06caSMauro Carvalho Chehab 
39000c0d06caSMauro Carvalho Chehab 	LOCK_GIVE(hdw->ctl_lock);
39010c0d06caSMauro Carvalho Chehab 
39020c0d06caSMauro Carvalho Chehab 	return ret;
39030c0d06caSMauro Carvalho Chehab }
39040c0d06caSMauro Carvalho Chehab 
39050c0d06caSMauro Carvalho Chehab 
pvr2_read_register(struct pvr2_hdw * hdw,u16 reg,u32 * data)39060c0d06caSMauro Carvalho Chehab static int pvr2_read_register(struct pvr2_hdw *hdw, u16 reg, u32 *data)
39070c0d06caSMauro Carvalho Chehab {
39080c0d06caSMauro Carvalho Chehab 	int ret = 0;
39090c0d06caSMauro Carvalho Chehab 
39100c0d06caSMauro Carvalho Chehab 	LOCK_TAKE(hdw->ctl_lock);
39110c0d06caSMauro Carvalho Chehab 
39120c0d06caSMauro Carvalho Chehab 	hdw->cmd_buffer[0] = FX2CMD_REG_READ;  /* read register prefix */
39130c0d06caSMauro Carvalho Chehab 	hdw->cmd_buffer[1] = 0;
39140c0d06caSMauro Carvalho Chehab 	hdw->cmd_buffer[2] = 0;
39150c0d06caSMauro Carvalho Chehab 	hdw->cmd_buffer[3] = 0;
39160c0d06caSMauro Carvalho Chehab 	hdw->cmd_buffer[4] = 0;
39170c0d06caSMauro Carvalho Chehab 	hdw->cmd_buffer[5] = 0;
39180c0d06caSMauro Carvalho Chehab 	hdw->cmd_buffer[6] = (reg >> 8) & 0xff;
39190c0d06caSMauro Carvalho Chehab 	hdw->cmd_buffer[7] = reg & 0xff;
39200c0d06caSMauro Carvalho Chehab 
39210c0d06caSMauro Carvalho Chehab 	ret |= pvr2_send_request(hdw, hdw->cmd_buffer, 8, hdw->cmd_buffer, 4);
39220c0d06caSMauro Carvalho Chehab 	*data = PVR2_COMPOSE_LE(hdw->cmd_buffer,0);
39230c0d06caSMauro Carvalho Chehab 
39240c0d06caSMauro Carvalho Chehab 	LOCK_GIVE(hdw->ctl_lock);
39250c0d06caSMauro Carvalho Chehab 
39260c0d06caSMauro Carvalho Chehab 	return ret;
39270c0d06caSMauro Carvalho Chehab }
39280c0d06caSMauro Carvalho Chehab 
39290c0d06caSMauro Carvalho Chehab 
pvr2_hdw_render_useless(struct pvr2_hdw * hdw)39300c0d06caSMauro Carvalho Chehab void pvr2_hdw_render_useless(struct pvr2_hdw *hdw)
39310c0d06caSMauro Carvalho Chehab {
39320c0d06caSMauro Carvalho Chehab 	if (!hdw->flag_ok) return;
39330c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_ERROR_LEGS,
39340c0d06caSMauro Carvalho Chehab 		   "Device being rendered inoperable");
39350c0d06caSMauro Carvalho Chehab 	if (hdw->vid_stream) {
39360c0d06caSMauro Carvalho Chehab 		pvr2_stream_setup(hdw->vid_stream,NULL,0,0);
39370c0d06caSMauro Carvalho Chehab 	}
39380c0d06caSMauro Carvalho Chehab 	hdw->flag_ok = 0;
39390c0d06caSMauro Carvalho Chehab 	trace_stbit("flag_ok",hdw->flag_ok);
39400c0d06caSMauro Carvalho Chehab 	pvr2_hdw_state_sched(hdw);
39410c0d06caSMauro Carvalho Chehab }
39420c0d06caSMauro Carvalho Chehab 
39430c0d06caSMauro Carvalho Chehab 
pvr2_hdw_device_reset(struct pvr2_hdw * hdw)39440c0d06caSMauro Carvalho Chehab void pvr2_hdw_device_reset(struct pvr2_hdw *hdw)
39450c0d06caSMauro Carvalho Chehab {
39460c0d06caSMauro Carvalho Chehab 	int ret;
39470c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_INIT,"Performing a device reset...");
39480c0d06caSMauro Carvalho Chehab 	ret = usb_lock_device_for_reset(hdw->usb_dev,NULL);
39490c0d06caSMauro Carvalho Chehab 	if (ret == 0) {
39500c0d06caSMauro Carvalho Chehab 		ret = usb_reset_device(hdw->usb_dev);
39510c0d06caSMauro Carvalho Chehab 		usb_unlock_device(hdw->usb_dev);
39520c0d06caSMauro Carvalho Chehab 	} else {
39530c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
39540c0d06caSMauro Carvalho Chehab 			   "Failed to lock USB device ret=%d",ret);
39550c0d06caSMauro Carvalho Chehab 	}
39560c0d06caSMauro Carvalho Chehab 	if (init_pause_msec) {
39570c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_INFO,
39580c0d06caSMauro Carvalho Chehab 			   "Waiting %u msec for hardware to settle",
39590c0d06caSMauro Carvalho Chehab 			   init_pause_msec);
39600c0d06caSMauro Carvalho Chehab 		msleep(init_pause_msec);
39610c0d06caSMauro Carvalho Chehab 	}
39620c0d06caSMauro Carvalho Chehab 
39630c0d06caSMauro Carvalho Chehab }
39640c0d06caSMauro Carvalho Chehab 
39650c0d06caSMauro Carvalho Chehab 
pvr2_hdw_cpureset_assert(struct pvr2_hdw * hdw,int val)39660c0d06caSMauro Carvalho Chehab void pvr2_hdw_cpureset_assert(struct pvr2_hdw *hdw,int val)
39670c0d06caSMauro Carvalho Chehab {
39680c0d06caSMauro Carvalho Chehab 	char *da;
39690c0d06caSMauro Carvalho Chehab 	unsigned int pipe;
39700c0d06caSMauro Carvalho Chehab 	int ret;
39710c0d06caSMauro Carvalho Chehab 
39720c0d06caSMauro Carvalho Chehab 	if (!hdw->usb_dev) return;
39730c0d06caSMauro Carvalho Chehab 
39740c0d06caSMauro Carvalho Chehab 	da = kmalloc(16, GFP_KERNEL);
39750c0d06caSMauro Carvalho Chehab 
39760c0d06caSMauro Carvalho Chehab 	if (da == NULL) {
39770c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
39780c0d06caSMauro Carvalho Chehab 			   "Unable to allocate memory to control CPU reset");
39790c0d06caSMauro Carvalho Chehab 		return;
39800c0d06caSMauro Carvalho Chehab 	}
39810c0d06caSMauro Carvalho Chehab 
39820c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_INIT,"cpureset_assert(%d)",val);
39830c0d06caSMauro Carvalho Chehab 
39840c0d06caSMauro Carvalho Chehab 	da[0] = val ? 0x01 : 0x00;
39850c0d06caSMauro Carvalho Chehab 
39860c0d06caSMauro Carvalho Chehab 	/* Write the CPUCS register on the 8051.  The lsb of the register
39870c0d06caSMauro Carvalho Chehab 	   is the reset bit; a 1 asserts reset while a 0 clears it. */
39880c0d06caSMauro Carvalho Chehab 	pipe = usb_sndctrlpipe(hdw->usb_dev, 0);
3989b82bf9b9SJohan Hovold 	ret = usb_control_msg(hdw->usb_dev,pipe,0xa0,0x40,0xe600,0,da,1,1000);
39900c0d06caSMauro Carvalho Chehab 	if (ret < 0) {
39910c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
39920c0d06caSMauro Carvalho Chehab 			   "cpureset_assert(%d) error=%d",val,ret);
39930c0d06caSMauro Carvalho Chehab 		pvr2_hdw_render_useless(hdw);
39940c0d06caSMauro Carvalho Chehab 	}
39950c0d06caSMauro Carvalho Chehab 
39960c0d06caSMauro Carvalho Chehab 	kfree(da);
39970c0d06caSMauro Carvalho Chehab }
39980c0d06caSMauro Carvalho Chehab 
39990c0d06caSMauro Carvalho Chehab 
pvr2_hdw_cmd_deep_reset(struct pvr2_hdw * hdw)40000c0d06caSMauro Carvalho Chehab int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *hdw)
40010c0d06caSMauro Carvalho Chehab {
40020c0d06caSMauro Carvalho Chehab 	return pvr2_issue_simple_cmd(hdw,FX2CMD_DEEP_RESET);
40030c0d06caSMauro Carvalho Chehab }
40040c0d06caSMauro Carvalho Chehab 
40050c0d06caSMauro Carvalho Chehab 
pvr2_hdw_cmd_powerup(struct pvr2_hdw * hdw)40060c0d06caSMauro Carvalho Chehab int pvr2_hdw_cmd_powerup(struct pvr2_hdw *hdw)
40070c0d06caSMauro Carvalho Chehab {
40080c0d06caSMauro Carvalho Chehab 	return pvr2_issue_simple_cmd(hdw,FX2CMD_POWER_ON);
40090c0d06caSMauro Carvalho Chehab }
40100c0d06caSMauro Carvalho Chehab 
40110c0d06caSMauro Carvalho Chehab 
40120c0d06caSMauro Carvalho Chehab 
pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw * hdw)40130c0d06caSMauro Carvalho Chehab int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw)
40140c0d06caSMauro Carvalho Chehab {
40150c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_INIT,
40160c0d06caSMauro Carvalho Chehab 		   "Requesting decoder reset");
40170c0d06caSMauro Carvalho Chehab 	if (hdw->decoder_client_id) {
40180c0d06caSMauro Carvalho Chehab 		v4l2_device_call_all(&hdw->v4l2_dev, hdw->decoder_client_id,
40190c0d06caSMauro Carvalho Chehab 				     core, reset, 0);
40200c0d06caSMauro Carvalho Chehab 		pvr2_hdw_cx25840_vbi_hack(hdw);
40210c0d06caSMauro Carvalho Chehab 		return 0;
40220c0d06caSMauro Carvalho Chehab 	}
40230c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_INIT,
40240c0d06caSMauro Carvalho Chehab 		   "Unable to reset decoder: nothing attached");
40250c0d06caSMauro Carvalho Chehab 	return -ENOTTY;
40260c0d06caSMauro Carvalho Chehab }
40270c0d06caSMauro Carvalho Chehab 
40280c0d06caSMauro Carvalho Chehab 
pvr2_hdw_cmd_hcw_demod_reset(struct pvr2_hdw * hdw,int onoff)40290c0d06caSMauro Carvalho Chehab static int pvr2_hdw_cmd_hcw_demod_reset(struct pvr2_hdw *hdw, int onoff)
40300c0d06caSMauro Carvalho Chehab {
40310c0d06caSMauro Carvalho Chehab 	hdw->flag_ok = !0;
4032dd60bf43SBrad Love 
4033dd60bf43SBrad Love 	/* Use this for Hauppauge 160xxx only */
4034dd60bf43SBrad Love 	if (le16_to_cpu(hdw->usb_dev->descriptor.idVendor) == 0x2040 &&
4035dd60bf43SBrad Love 	    (le16_to_cpu(hdw->usb_dev->descriptor.idProduct) == 0x7502 ||
4036dd60bf43SBrad Love 	     le16_to_cpu(hdw->usb_dev->descriptor.idProduct) == 0x7510)) {
4037dd60bf43SBrad Love 		pr_debug("%s(): resetting demod on Hauppauge 160xxx platform skipped\n",
4038dd60bf43SBrad Love 			 __func__);
4039dd60bf43SBrad Love 		/* Can't reset 160xxx or it will trash Demod tristate */
4040dd60bf43SBrad Love 		return pvr2_issue_simple_cmd(hdw,
4041dd60bf43SBrad Love 					     FX2CMD_HCW_MAKO_SLEEP_PIN |
4042dd60bf43SBrad Love 					     (1 << 8) |
4043dd60bf43SBrad Love 					     ((onoff ? 1 : 0) << 16));
4044dd60bf43SBrad Love 	}
4045dd60bf43SBrad Love 
40460c0d06caSMauro Carvalho Chehab 	return pvr2_issue_simple_cmd(hdw,
40470c0d06caSMauro Carvalho Chehab 				     FX2CMD_HCW_DEMOD_RESETIN |
40480c0d06caSMauro Carvalho Chehab 				     (1 << 8) |
40490c0d06caSMauro Carvalho Chehab 				     ((onoff ? 1 : 0) << 16));
40500c0d06caSMauro Carvalho Chehab }
40510c0d06caSMauro Carvalho Chehab 
40520c0d06caSMauro Carvalho Chehab 
pvr2_hdw_cmd_onair_fe_power_ctrl(struct pvr2_hdw * hdw,int onoff)40530c0d06caSMauro Carvalho Chehab static int pvr2_hdw_cmd_onair_fe_power_ctrl(struct pvr2_hdw *hdw, int onoff)
40540c0d06caSMauro Carvalho Chehab {
40550c0d06caSMauro Carvalho Chehab 	hdw->flag_ok = !0;
40560c0d06caSMauro Carvalho Chehab 	return pvr2_issue_simple_cmd(hdw,(onoff ?
40570c0d06caSMauro Carvalho Chehab 					  FX2CMD_ONAIR_DTV_POWER_ON :
40580c0d06caSMauro Carvalho Chehab 					  FX2CMD_ONAIR_DTV_POWER_OFF));
40590c0d06caSMauro Carvalho Chehab }
40600c0d06caSMauro Carvalho Chehab 
40610c0d06caSMauro Carvalho Chehab 
pvr2_hdw_cmd_onair_digital_path_ctrl(struct pvr2_hdw * hdw,int onoff)40620c0d06caSMauro Carvalho Chehab static int pvr2_hdw_cmd_onair_digital_path_ctrl(struct pvr2_hdw *hdw,
40630c0d06caSMauro Carvalho Chehab 						int onoff)
40640c0d06caSMauro Carvalho Chehab {
40650c0d06caSMauro Carvalho Chehab 	return pvr2_issue_simple_cmd(hdw,(onoff ?
40660c0d06caSMauro Carvalho Chehab 					  FX2CMD_ONAIR_DTV_STREAMING_ON :
40670c0d06caSMauro Carvalho Chehab 					  FX2CMD_ONAIR_DTV_STREAMING_OFF));
40680c0d06caSMauro Carvalho Chehab }
40690c0d06caSMauro Carvalho Chehab 
40700c0d06caSMauro Carvalho Chehab 
pvr2_hdw_cmd_modeswitch(struct pvr2_hdw * hdw,int digitalFl)40710c0d06caSMauro Carvalho Chehab static void pvr2_hdw_cmd_modeswitch(struct pvr2_hdw *hdw,int digitalFl)
40720c0d06caSMauro Carvalho Chehab {
40730c0d06caSMauro Carvalho Chehab 	int cmode;
40740c0d06caSMauro Carvalho Chehab 	/* Compare digital/analog desired setting with current setting.  If
40750c0d06caSMauro Carvalho Chehab 	   they don't match, fix it... */
40760c0d06caSMauro Carvalho Chehab 	cmode = (digitalFl ? PVR2_PATHWAY_DIGITAL : PVR2_PATHWAY_ANALOG);
40770c0d06caSMauro Carvalho Chehab 	if (cmode == hdw->pathway_state) {
40780c0d06caSMauro Carvalho Chehab 		/* They match; nothing to do */
40790c0d06caSMauro Carvalho Chehab 		return;
40800c0d06caSMauro Carvalho Chehab 	}
40810c0d06caSMauro Carvalho Chehab 
40820c0d06caSMauro Carvalho Chehab 	switch (hdw->hdw_desc->digital_control_scheme) {
40830c0d06caSMauro Carvalho Chehab 	case PVR2_DIGITAL_SCHEME_HAUPPAUGE:
40840c0d06caSMauro Carvalho Chehab 		pvr2_hdw_cmd_hcw_demod_reset(hdw,digitalFl);
40850c0d06caSMauro Carvalho Chehab 		if (cmode == PVR2_PATHWAY_ANALOG) {
40860c0d06caSMauro Carvalho Chehab 			/* If moving to analog mode, also force the decoder
40870c0d06caSMauro Carvalho Chehab 			   to reset.  If no decoder is attached, then it's
40880c0d06caSMauro Carvalho Chehab 			   ok to ignore this because if/when the decoder
40890c0d06caSMauro Carvalho Chehab 			   attaches, it will reset itself at that time. */
40900c0d06caSMauro Carvalho Chehab 			pvr2_hdw_cmd_decoder_reset(hdw);
40910c0d06caSMauro Carvalho Chehab 		}
40920c0d06caSMauro Carvalho Chehab 		break;
40930c0d06caSMauro Carvalho Chehab 	case PVR2_DIGITAL_SCHEME_ONAIR:
40940c0d06caSMauro Carvalho Chehab 		/* Supposedly we should always have the power on whether in
40950c0d06caSMauro Carvalho Chehab 		   digital or analog mode.  But for now do what appears to
40960c0d06caSMauro Carvalho Chehab 		   work... */
40970c0d06caSMauro Carvalho Chehab 		pvr2_hdw_cmd_onair_fe_power_ctrl(hdw,digitalFl);
40980c0d06caSMauro Carvalho Chehab 		break;
40990c0d06caSMauro Carvalho Chehab 	default: break;
41000c0d06caSMauro Carvalho Chehab 	}
41010c0d06caSMauro Carvalho Chehab 
41020c0d06caSMauro Carvalho Chehab 	pvr2_hdw_untrip_unlocked(hdw);
41030c0d06caSMauro Carvalho Chehab 	hdw->pathway_state = cmode;
41040c0d06caSMauro Carvalho Chehab }
41050c0d06caSMauro Carvalho Chehab 
41060c0d06caSMauro Carvalho Chehab 
pvr2_led_ctrl_hauppauge(struct pvr2_hdw * hdw,int onoff)41070c0d06caSMauro Carvalho Chehab static void pvr2_led_ctrl_hauppauge(struct pvr2_hdw *hdw, int onoff)
41080c0d06caSMauro Carvalho Chehab {
41090c0d06caSMauro Carvalho Chehab 	/* change some GPIO data
41100c0d06caSMauro Carvalho Chehab 	 *
41110c0d06caSMauro Carvalho Chehab 	 * note: bit d7 of dir appears to control the LED,
41120c0d06caSMauro Carvalho Chehab 	 * so we shut it off here.
41130c0d06caSMauro Carvalho Chehab 	 *
41140c0d06caSMauro Carvalho Chehab 	 */
41150c0d06caSMauro Carvalho Chehab 	if (onoff) {
41160c0d06caSMauro Carvalho Chehab 		pvr2_hdw_gpio_chg_dir(hdw, 0xffffffff, 0x00000481);
41170c0d06caSMauro Carvalho Chehab 	} else {
41180c0d06caSMauro Carvalho Chehab 		pvr2_hdw_gpio_chg_dir(hdw, 0xffffffff, 0x00000401);
41190c0d06caSMauro Carvalho Chehab 	}
41200c0d06caSMauro Carvalho Chehab 	pvr2_hdw_gpio_chg_out(hdw, 0xffffffff, 0x00000000);
41210c0d06caSMauro Carvalho Chehab }
41220c0d06caSMauro Carvalho Chehab 
41230c0d06caSMauro Carvalho Chehab 
41240c0d06caSMauro Carvalho Chehab typedef void (*led_method_func)(struct pvr2_hdw *,int);
41250c0d06caSMauro Carvalho Chehab 
41260c0d06caSMauro Carvalho Chehab static led_method_func led_methods[] = {
41270c0d06caSMauro Carvalho Chehab 	[PVR2_LED_SCHEME_HAUPPAUGE] = pvr2_led_ctrl_hauppauge,
41280c0d06caSMauro Carvalho Chehab };
41290c0d06caSMauro Carvalho Chehab 
41300c0d06caSMauro Carvalho Chehab 
41310c0d06caSMauro Carvalho Chehab /* Toggle LED */
pvr2_led_ctrl(struct pvr2_hdw * hdw,int onoff)41320c0d06caSMauro Carvalho Chehab static void pvr2_led_ctrl(struct pvr2_hdw *hdw,int onoff)
41330c0d06caSMauro Carvalho Chehab {
41340c0d06caSMauro Carvalho Chehab 	unsigned int scheme_id;
41350c0d06caSMauro Carvalho Chehab 	led_method_func fp;
41360c0d06caSMauro Carvalho Chehab 
41370c0d06caSMauro Carvalho Chehab 	if ((!onoff) == (!hdw->led_on)) return;
41380c0d06caSMauro Carvalho Chehab 
41390c0d06caSMauro Carvalho Chehab 	hdw->led_on = onoff != 0;
41400c0d06caSMauro Carvalho Chehab 
41410c0d06caSMauro Carvalho Chehab 	scheme_id = hdw->hdw_desc->led_scheme;
41420c0d06caSMauro Carvalho Chehab 	if (scheme_id < ARRAY_SIZE(led_methods)) {
41430c0d06caSMauro Carvalho Chehab 		fp = led_methods[scheme_id];
41440c0d06caSMauro Carvalho Chehab 	} else {
41450c0d06caSMauro Carvalho Chehab 		fp = NULL;
41460c0d06caSMauro Carvalho Chehab 	}
41470c0d06caSMauro Carvalho Chehab 
41480c0d06caSMauro Carvalho Chehab 	if (fp) (*fp)(hdw,onoff);
41490c0d06caSMauro Carvalho Chehab }
41500c0d06caSMauro Carvalho Chehab 
41510c0d06caSMauro Carvalho Chehab 
41520c0d06caSMauro Carvalho Chehab /* Stop / start video stream transport */
pvr2_hdw_cmd_usbstream(struct pvr2_hdw * hdw,int runFl)41530c0d06caSMauro Carvalho Chehab static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl)
41540c0d06caSMauro Carvalho Chehab {
41550c0d06caSMauro Carvalho Chehab 	int ret;
41560c0d06caSMauro Carvalho Chehab 
41570c0d06caSMauro Carvalho Chehab 	/* If we're in analog mode, then just issue the usual analog
41580c0d06caSMauro Carvalho Chehab 	   command. */
41590c0d06caSMauro Carvalho Chehab 	if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) {
41600c0d06caSMauro Carvalho Chehab 		return pvr2_issue_simple_cmd(hdw,
41610c0d06caSMauro Carvalho Chehab 					     (runFl ?
41620c0d06caSMauro Carvalho Chehab 					      FX2CMD_STREAMING_ON :
41630c0d06caSMauro Carvalho Chehab 					      FX2CMD_STREAMING_OFF));
41640c0d06caSMauro Carvalho Chehab 		/*Note: Not reached */
41650c0d06caSMauro Carvalho Chehab 	}
41660c0d06caSMauro Carvalho Chehab 
41670c0d06caSMauro Carvalho Chehab 	if (hdw->pathway_state != PVR2_PATHWAY_DIGITAL) {
41680c0d06caSMauro Carvalho Chehab 		/* Whoops, we don't know what mode we're in... */
41690c0d06caSMauro Carvalho Chehab 		return -EINVAL;
41700c0d06caSMauro Carvalho Chehab 	}
41710c0d06caSMauro Carvalho Chehab 
41720c0d06caSMauro Carvalho Chehab 	/* To get here we have to be in digital mode.  The mechanism here
41730c0d06caSMauro Carvalho Chehab 	   is unfortunately different for different vendors.  So we switch
41740c0d06caSMauro Carvalho Chehab 	   on the device's digital scheme attribute in order to figure out
41750c0d06caSMauro Carvalho Chehab 	   what to do. */
41760c0d06caSMauro Carvalho Chehab 	switch (hdw->hdw_desc->digital_control_scheme) {
41770c0d06caSMauro Carvalho Chehab 	case PVR2_DIGITAL_SCHEME_HAUPPAUGE:
41780c0d06caSMauro Carvalho Chehab 		return pvr2_issue_simple_cmd(hdw,
41790c0d06caSMauro Carvalho Chehab 					     (runFl ?
41800c0d06caSMauro Carvalho Chehab 					      FX2CMD_HCW_DTV_STREAMING_ON :
41810c0d06caSMauro Carvalho Chehab 					      FX2CMD_HCW_DTV_STREAMING_OFF));
41820c0d06caSMauro Carvalho Chehab 	case PVR2_DIGITAL_SCHEME_ONAIR:
41830c0d06caSMauro Carvalho Chehab 		ret = pvr2_issue_simple_cmd(hdw,
41840c0d06caSMauro Carvalho Chehab 					    (runFl ?
41850c0d06caSMauro Carvalho Chehab 					     FX2CMD_STREAMING_ON :
41860c0d06caSMauro Carvalho Chehab 					     FX2CMD_STREAMING_OFF));
41870c0d06caSMauro Carvalho Chehab 		if (ret) return ret;
41880c0d06caSMauro Carvalho Chehab 		return pvr2_hdw_cmd_onair_digital_path_ctrl(hdw,runFl);
41890c0d06caSMauro Carvalho Chehab 	default:
41900c0d06caSMauro Carvalho Chehab 		return -EINVAL;
41910c0d06caSMauro Carvalho Chehab 	}
41920c0d06caSMauro Carvalho Chehab }
41930c0d06caSMauro Carvalho Chehab 
41940c0d06caSMauro Carvalho Chehab 
41950c0d06caSMauro Carvalho Chehab /* Evaluate whether or not state_pathway_ok can change */
state_eval_pathway_ok(struct pvr2_hdw * hdw)41960c0d06caSMauro Carvalho Chehab static int state_eval_pathway_ok(struct pvr2_hdw *hdw)
41970c0d06caSMauro Carvalho Chehab {
41980c0d06caSMauro Carvalho Chehab 	if (hdw->state_pathway_ok) {
41990c0d06caSMauro Carvalho Chehab 		/* Nothing to do if pathway is already ok */
42000c0d06caSMauro Carvalho Chehab 		return 0;
42010c0d06caSMauro Carvalho Chehab 	}
42020c0d06caSMauro Carvalho Chehab 	if (!hdw->state_pipeline_idle) {
42030c0d06caSMauro Carvalho Chehab 		/* Not allowed to change anything if pipeline is not idle */
42040c0d06caSMauro Carvalho Chehab 		return 0;
42050c0d06caSMauro Carvalho Chehab 	}
42060c0d06caSMauro Carvalho Chehab 	pvr2_hdw_cmd_modeswitch(hdw,hdw->input_val == PVR2_CVAL_INPUT_DTV);
42070c0d06caSMauro Carvalho Chehab 	hdw->state_pathway_ok = !0;
42080c0d06caSMauro Carvalho Chehab 	trace_stbit("state_pathway_ok",hdw->state_pathway_ok);
42090c0d06caSMauro Carvalho Chehab 	return !0;
42100c0d06caSMauro Carvalho Chehab }
42110c0d06caSMauro Carvalho Chehab 
42120c0d06caSMauro Carvalho Chehab 
42130c0d06caSMauro Carvalho Chehab /* Evaluate whether or not state_encoder_ok can change */
state_eval_encoder_ok(struct pvr2_hdw * hdw)42140c0d06caSMauro Carvalho Chehab static int state_eval_encoder_ok(struct pvr2_hdw *hdw)
42150c0d06caSMauro Carvalho Chehab {
42160c0d06caSMauro Carvalho Chehab 	if (hdw->state_encoder_ok) return 0;
42170c0d06caSMauro Carvalho Chehab 	if (hdw->flag_tripped) return 0;
42180c0d06caSMauro Carvalho Chehab 	if (hdw->state_encoder_run) return 0;
42190c0d06caSMauro Carvalho Chehab 	if (hdw->state_encoder_config) return 0;
42200c0d06caSMauro Carvalho Chehab 	if (hdw->state_decoder_run) return 0;
42210c0d06caSMauro Carvalho Chehab 	if (hdw->state_usbstream_run) return 0;
42220c0d06caSMauro Carvalho Chehab 	if (hdw->pathway_state == PVR2_PATHWAY_DIGITAL) {
42230c0d06caSMauro Carvalho Chehab 		if (!hdw->hdw_desc->flag_digital_requires_cx23416) return 0;
42240c0d06caSMauro Carvalho Chehab 	} else if (hdw->pathway_state != PVR2_PATHWAY_ANALOG) {
42250c0d06caSMauro Carvalho Chehab 		return 0;
42260c0d06caSMauro Carvalho Chehab 	}
42270c0d06caSMauro Carvalho Chehab 
42280c0d06caSMauro Carvalho Chehab 	if (pvr2_upload_firmware2(hdw) < 0) {
42290c0d06caSMauro Carvalho Chehab 		hdw->flag_tripped = !0;
42300c0d06caSMauro Carvalho Chehab 		trace_stbit("flag_tripped",hdw->flag_tripped);
42310c0d06caSMauro Carvalho Chehab 		return !0;
42320c0d06caSMauro Carvalho Chehab 	}
42330c0d06caSMauro Carvalho Chehab 	hdw->state_encoder_ok = !0;
42340c0d06caSMauro Carvalho Chehab 	trace_stbit("state_encoder_ok",hdw->state_encoder_ok);
42350c0d06caSMauro Carvalho Chehab 	return !0;
42360c0d06caSMauro Carvalho Chehab }
42370c0d06caSMauro Carvalho Chehab 
42380c0d06caSMauro Carvalho Chehab 
42390c0d06caSMauro Carvalho Chehab /* Evaluate whether or not state_encoder_config can change */
state_eval_encoder_config(struct pvr2_hdw * hdw)42400c0d06caSMauro Carvalho Chehab static int state_eval_encoder_config(struct pvr2_hdw *hdw)
42410c0d06caSMauro Carvalho Chehab {
42420c0d06caSMauro Carvalho Chehab 	if (hdw->state_encoder_config) {
42430c0d06caSMauro Carvalho Chehab 		if (hdw->state_encoder_ok) {
42440c0d06caSMauro Carvalho Chehab 			if (hdw->state_pipeline_req &&
42450c0d06caSMauro Carvalho Chehab 			    !hdw->state_pipeline_pause) return 0;
42460c0d06caSMauro Carvalho Chehab 		}
42470c0d06caSMauro Carvalho Chehab 		hdw->state_encoder_config = 0;
42480c0d06caSMauro Carvalho Chehab 		hdw->state_encoder_waitok = 0;
42490c0d06caSMauro Carvalho Chehab 		trace_stbit("state_encoder_waitok",hdw->state_encoder_waitok);
42500c0d06caSMauro Carvalho Chehab 		/* paranoia - solve race if timer just completed */
42510c0d06caSMauro Carvalho Chehab 		del_timer_sync(&hdw->encoder_wait_timer);
42520c0d06caSMauro Carvalho Chehab 	} else {
42530c0d06caSMauro Carvalho Chehab 		if (!hdw->state_pathway_ok ||
42540c0d06caSMauro Carvalho Chehab 		    (hdw->pathway_state != PVR2_PATHWAY_ANALOG) ||
42550c0d06caSMauro Carvalho Chehab 		    !hdw->state_encoder_ok ||
42560c0d06caSMauro Carvalho Chehab 		    !hdw->state_pipeline_idle ||
42570c0d06caSMauro Carvalho Chehab 		    hdw->state_pipeline_pause ||
42580c0d06caSMauro Carvalho Chehab 		    !hdw->state_pipeline_req ||
42590c0d06caSMauro Carvalho Chehab 		    !hdw->state_pipeline_config) {
42600c0d06caSMauro Carvalho Chehab 			/* We must reset the enforced wait interval if
42610c0d06caSMauro Carvalho Chehab 			   anything has happened that might have disturbed
42620c0d06caSMauro Carvalho Chehab 			   the encoder.  This should be a rare case. */
42630c0d06caSMauro Carvalho Chehab 			if (timer_pending(&hdw->encoder_wait_timer)) {
42640c0d06caSMauro Carvalho Chehab 				del_timer_sync(&hdw->encoder_wait_timer);
42650c0d06caSMauro Carvalho Chehab 			}
42660c0d06caSMauro Carvalho Chehab 			if (hdw->state_encoder_waitok) {
42670c0d06caSMauro Carvalho Chehab 				/* Must clear the state - therefore we did
42680c0d06caSMauro Carvalho Chehab 				   something to a state bit and must also
42690c0d06caSMauro Carvalho Chehab 				   return true. */
42700c0d06caSMauro Carvalho Chehab 				hdw->state_encoder_waitok = 0;
42710c0d06caSMauro Carvalho Chehab 				trace_stbit("state_encoder_waitok",
42720c0d06caSMauro Carvalho Chehab 					    hdw->state_encoder_waitok);
42730c0d06caSMauro Carvalho Chehab 				return !0;
42740c0d06caSMauro Carvalho Chehab 			}
42750c0d06caSMauro Carvalho Chehab 			return 0;
42760c0d06caSMauro Carvalho Chehab 		}
42770c0d06caSMauro Carvalho Chehab 		if (!hdw->state_encoder_waitok) {
42780c0d06caSMauro Carvalho Chehab 			if (!timer_pending(&hdw->encoder_wait_timer)) {
42790c0d06caSMauro Carvalho Chehab 				/* waitok flag wasn't set and timer isn't
42800c0d06caSMauro Carvalho Chehab 				   running.  Check flag once more to avoid
42810c0d06caSMauro Carvalho Chehab 				   a race then start the timer.  This is
42820c0d06caSMauro Carvalho Chehab 				   the point when we measure out a minimal
42830c0d06caSMauro Carvalho Chehab 				   quiet interval before doing something to
42840c0d06caSMauro Carvalho Chehab 				   the encoder. */
42850c0d06caSMauro Carvalho Chehab 				if (!hdw->state_encoder_waitok) {
42860c0d06caSMauro Carvalho Chehab 					hdw->encoder_wait_timer.expires =
42873edf7eb8SNicholas Mc Guire 						jiffies + msecs_to_jiffies(
42883edf7eb8SNicholas Mc Guire 						TIME_MSEC_ENCODER_WAIT);
42890c0d06caSMauro Carvalho Chehab 					add_timer(&hdw->encoder_wait_timer);
42900c0d06caSMauro Carvalho Chehab 				}
42910c0d06caSMauro Carvalho Chehab 			}
42920c0d06caSMauro Carvalho Chehab 			/* We can't continue until we know we have been
42930c0d06caSMauro Carvalho Chehab 			   quiet for the interval measured by this
42940c0d06caSMauro Carvalho Chehab 			   timer. */
42950c0d06caSMauro Carvalho Chehab 			return 0;
42960c0d06caSMauro Carvalho Chehab 		}
42970c0d06caSMauro Carvalho Chehab 		pvr2_encoder_configure(hdw);
42980c0d06caSMauro Carvalho Chehab 		if (hdw->state_encoder_ok) hdw->state_encoder_config = !0;
42990c0d06caSMauro Carvalho Chehab 	}
43000c0d06caSMauro Carvalho Chehab 	trace_stbit("state_encoder_config",hdw->state_encoder_config);
43010c0d06caSMauro Carvalho Chehab 	return !0;
43020c0d06caSMauro Carvalho Chehab }
43030c0d06caSMauro Carvalho Chehab 
43040c0d06caSMauro Carvalho Chehab 
43050c0d06caSMauro Carvalho Chehab /* Return true if the encoder should not be running. */
state_check_disable_encoder_run(struct pvr2_hdw * hdw)43060c0d06caSMauro Carvalho Chehab static int state_check_disable_encoder_run(struct pvr2_hdw *hdw)
43070c0d06caSMauro Carvalho Chehab {
43080c0d06caSMauro Carvalho Chehab 	if (!hdw->state_encoder_ok) {
43090c0d06caSMauro Carvalho Chehab 		/* Encoder isn't healthy at the moment, so stop it. */
43100c0d06caSMauro Carvalho Chehab 		return !0;
43110c0d06caSMauro Carvalho Chehab 	}
43120c0d06caSMauro Carvalho Chehab 	if (!hdw->state_pathway_ok) {
43130c0d06caSMauro Carvalho Chehab 		/* Mode is not understood at the moment (i.e. it wants to
43140c0d06caSMauro Carvalho Chehab 		   change), so encoder must be stopped. */
43150c0d06caSMauro Carvalho Chehab 		return !0;
43160c0d06caSMauro Carvalho Chehab 	}
43170c0d06caSMauro Carvalho Chehab 
43180c0d06caSMauro Carvalho Chehab 	switch (hdw->pathway_state) {
43190c0d06caSMauro Carvalho Chehab 	case PVR2_PATHWAY_ANALOG:
43200c0d06caSMauro Carvalho Chehab 		if (!hdw->state_decoder_run) {
43210c0d06caSMauro Carvalho Chehab 			/* We're in analog mode and the decoder is not
43220c0d06caSMauro Carvalho Chehab 			   running; thus the encoder should be stopped as
43230c0d06caSMauro Carvalho Chehab 			   well. */
43240c0d06caSMauro Carvalho Chehab 			return !0;
43250c0d06caSMauro Carvalho Chehab 		}
43260c0d06caSMauro Carvalho Chehab 		break;
43270c0d06caSMauro Carvalho Chehab 	case PVR2_PATHWAY_DIGITAL:
43280c0d06caSMauro Carvalho Chehab 		if (hdw->state_encoder_runok) {
43290c0d06caSMauro Carvalho Chehab 			/* This is a funny case.  We're in digital mode so
43300c0d06caSMauro Carvalho Chehab 			   really the encoder should be stopped.  However
43310c0d06caSMauro Carvalho Chehab 			   if it really is running, only kill it after
43320c0d06caSMauro Carvalho Chehab 			   runok has been set.  This gives a chance for the
43330c0d06caSMauro Carvalho Chehab 			   onair quirk to function (encoder must run
43340c0d06caSMauro Carvalho Chehab 			   briefly first, at least once, before onair
43350c0d06caSMauro Carvalho Chehab 			   digital streaming can work). */
43360c0d06caSMauro Carvalho Chehab 			return !0;
43370c0d06caSMauro Carvalho Chehab 		}
43380c0d06caSMauro Carvalho Chehab 		break;
43390c0d06caSMauro Carvalho Chehab 	default:
43400c0d06caSMauro Carvalho Chehab 		/* Unknown mode; so encoder should be stopped. */
43410c0d06caSMauro Carvalho Chehab 		return !0;
43420c0d06caSMauro Carvalho Chehab 	}
43430c0d06caSMauro Carvalho Chehab 
43440c0d06caSMauro Carvalho Chehab 	/* If we get here, we haven't found a reason to stop the
43450c0d06caSMauro Carvalho Chehab 	   encoder. */
43460c0d06caSMauro Carvalho Chehab 	return 0;
43470c0d06caSMauro Carvalho Chehab }
43480c0d06caSMauro Carvalho Chehab 
43490c0d06caSMauro Carvalho Chehab 
43500c0d06caSMauro Carvalho Chehab /* Return true if the encoder should be running. */
state_check_enable_encoder_run(struct pvr2_hdw * hdw)43510c0d06caSMauro Carvalho Chehab static int state_check_enable_encoder_run(struct pvr2_hdw *hdw)
43520c0d06caSMauro Carvalho Chehab {
43530c0d06caSMauro Carvalho Chehab 	if (!hdw->state_encoder_ok) {
43540c0d06caSMauro Carvalho Chehab 		/* Don't run the encoder if it isn't healthy... */
43550c0d06caSMauro Carvalho Chehab 		return 0;
43560c0d06caSMauro Carvalho Chehab 	}
43570c0d06caSMauro Carvalho Chehab 	if (!hdw->state_pathway_ok) {
43580c0d06caSMauro Carvalho Chehab 		/* Don't run the encoder if we don't (yet) know what mode
43590c0d06caSMauro Carvalho Chehab 		   we need to be in... */
43600c0d06caSMauro Carvalho Chehab 		return 0;
43610c0d06caSMauro Carvalho Chehab 	}
43620c0d06caSMauro Carvalho Chehab 
43630c0d06caSMauro Carvalho Chehab 	switch (hdw->pathway_state) {
43640c0d06caSMauro Carvalho Chehab 	case PVR2_PATHWAY_ANALOG:
43650c0d06caSMauro Carvalho Chehab 		if (hdw->state_decoder_run && hdw->state_decoder_ready) {
43660c0d06caSMauro Carvalho Chehab 			/* In analog mode, if the decoder is running, then
43670c0d06caSMauro Carvalho Chehab 			   run the encoder. */
43680c0d06caSMauro Carvalho Chehab 			return !0;
43690c0d06caSMauro Carvalho Chehab 		}
43700c0d06caSMauro Carvalho Chehab 		break;
43710c0d06caSMauro Carvalho Chehab 	case PVR2_PATHWAY_DIGITAL:
43720c0d06caSMauro Carvalho Chehab 		if ((hdw->hdw_desc->digital_control_scheme ==
43730c0d06caSMauro Carvalho Chehab 		     PVR2_DIGITAL_SCHEME_ONAIR) &&
43740c0d06caSMauro Carvalho Chehab 		    !hdw->state_encoder_runok) {
43750c0d06caSMauro Carvalho Chehab 			/* This is a quirk.  OnAir hardware won't stream
43760c0d06caSMauro Carvalho Chehab 			   digital until the encoder has been run at least
43770c0d06caSMauro Carvalho Chehab 			   once, for a minimal period of time (empiricially
43780c0d06caSMauro Carvalho Chehab 			   measured to be 1/4 second).  So if we're on
43790c0d06caSMauro Carvalho Chehab 			   OnAir hardware and the encoder has never been
43800c0d06caSMauro Carvalho Chehab 			   run at all, then start the encoder.  Normal
43810c0d06caSMauro Carvalho Chehab 			   state machine logic in the driver will
43820c0d06caSMauro Carvalho Chehab 			   automatically handle the remaining bits. */
43830c0d06caSMauro Carvalho Chehab 			return !0;
43840c0d06caSMauro Carvalho Chehab 		}
43850c0d06caSMauro Carvalho Chehab 		break;
43860c0d06caSMauro Carvalho Chehab 	default:
43870c0d06caSMauro Carvalho Chehab 		/* For completeness (unknown mode; encoder won't run ever) */
43880c0d06caSMauro Carvalho Chehab 		break;
43890c0d06caSMauro Carvalho Chehab 	}
43900c0d06caSMauro Carvalho Chehab 	/* If we get here, then we haven't found any reason to run the
43910c0d06caSMauro Carvalho Chehab 	   encoder, so don't run it. */
43920c0d06caSMauro Carvalho Chehab 	return 0;
43930c0d06caSMauro Carvalho Chehab }
43940c0d06caSMauro Carvalho Chehab 
43950c0d06caSMauro Carvalho Chehab 
43960c0d06caSMauro Carvalho Chehab /* Evaluate whether or not state_encoder_run can change */
state_eval_encoder_run(struct pvr2_hdw * hdw)43970c0d06caSMauro Carvalho Chehab static int state_eval_encoder_run(struct pvr2_hdw *hdw)
43980c0d06caSMauro Carvalho Chehab {
43990c0d06caSMauro Carvalho Chehab 	if (hdw->state_encoder_run) {
44000c0d06caSMauro Carvalho Chehab 		if (!state_check_disable_encoder_run(hdw)) return 0;
44010c0d06caSMauro Carvalho Chehab 		if (hdw->state_encoder_ok) {
44020c0d06caSMauro Carvalho Chehab 			del_timer_sync(&hdw->encoder_run_timer);
44030c0d06caSMauro Carvalho Chehab 			if (pvr2_encoder_stop(hdw) < 0) return !0;
44040c0d06caSMauro Carvalho Chehab 		}
44050c0d06caSMauro Carvalho Chehab 		hdw->state_encoder_run = 0;
44060c0d06caSMauro Carvalho Chehab 	} else {
44070c0d06caSMauro Carvalho Chehab 		if (!state_check_enable_encoder_run(hdw)) return 0;
44080c0d06caSMauro Carvalho Chehab 		if (pvr2_encoder_start(hdw) < 0) return !0;
44090c0d06caSMauro Carvalho Chehab 		hdw->state_encoder_run = !0;
44100c0d06caSMauro Carvalho Chehab 		if (!hdw->state_encoder_runok) {
44113edf7eb8SNicholas Mc Guire 			hdw->encoder_run_timer.expires = jiffies +
44123edf7eb8SNicholas Mc Guire 				 msecs_to_jiffies(TIME_MSEC_ENCODER_OK);
44130c0d06caSMauro Carvalho Chehab 			add_timer(&hdw->encoder_run_timer);
44140c0d06caSMauro Carvalho Chehab 		}
44150c0d06caSMauro Carvalho Chehab 	}
44160c0d06caSMauro Carvalho Chehab 	trace_stbit("state_encoder_run",hdw->state_encoder_run);
44170c0d06caSMauro Carvalho Chehab 	return !0;
44180c0d06caSMauro Carvalho Chehab }
44190c0d06caSMauro Carvalho Chehab 
44200c0d06caSMauro Carvalho Chehab 
44210c0d06caSMauro Carvalho Chehab /* Timeout function for quiescent timer. */
pvr2_hdw_quiescent_timeout(struct timer_list * t)44228da0edf2SKees Cook static void pvr2_hdw_quiescent_timeout(struct timer_list *t)
44230c0d06caSMauro Carvalho Chehab {
44248da0edf2SKees Cook 	struct pvr2_hdw *hdw = from_timer(hdw, t, quiescent_timer);
44250c0d06caSMauro Carvalho Chehab 	hdw->state_decoder_quiescent = !0;
44260c0d06caSMauro Carvalho Chehab 	trace_stbit("state_decoder_quiescent",hdw->state_decoder_quiescent);
44270c0d06caSMauro Carvalho Chehab 	hdw->state_stale = !0;
442829a3006eSBhaktipriya Shridhar 	schedule_work(&hdw->workpoll);
44290c0d06caSMauro Carvalho Chehab }
44300c0d06caSMauro Carvalho Chehab 
44310c0d06caSMauro Carvalho Chehab 
44320c0d06caSMauro Carvalho Chehab /* Timeout function for decoder stabilization timer. */
pvr2_hdw_decoder_stabilization_timeout(struct timer_list * t)44338da0edf2SKees Cook static void pvr2_hdw_decoder_stabilization_timeout(struct timer_list *t)
44340c0d06caSMauro Carvalho Chehab {
44358da0edf2SKees Cook 	struct pvr2_hdw *hdw = from_timer(hdw, t, decoder_stabilization_timer);
44360c0d06caSMauro Carvalho Chehab 	hdw->state_decoder_ready = !0;
44370c0d06caSMauro Carvalho Chehab 	trace_stbit("state_decoder_ready", hdw->state_decoder_ready);
44380c0d06caSMauro Carvalho Chehab 	hdw->state_stale = !0;
443929a3006eSBhaktipriya Shridhar 	schedule_work(&hdw->workpoll);
44400c0d06caSMauro Carvalho Chehab }
44410c0d06caSMauro Carvalho Chehab 
44420c0d06caSMauro Carvalho Chehab 
44430c0d06caSMauro Carvalho Chehab /* Timeout function for encoder wait timer. */
pvr2_hdw_encoder_wait_timeout(struct timer_list * t)44448da0edf2SKees Cook static void pvr2_hdw_encoder_wait_timeout(struct timer_list *t)
44450c0d06caSMauro Carvalho Chehab {
44468da0edf2SKees Cook 	struct pvr2_hdw *hdw = from_timer(hdw, t, encoder_wait_timer);
44470c0d06caSMauro Carvalho Chehab 	hdw->state_encoder_waitok = !0;
44480c0d06caSMauro Carvalho Chehab 	trace_stbit("state_encoder_waitok",hdw->state_encoder_waitok);
44490c0d06caSMauro Carvalho Chehab 	hdw->state_stale = !0;
445029a3006eSBhaktipriya Shridhar 	schedule_work(&hdw->workpoll);
44510c0d06caSMauro Carvalho Chehab }
44520c0d06caSMauro Carvalho Chehab 
44530c0d06caSMauro Carvalho Chehab 
44540c0d06caSMauro Carvalho Chehab /* Timeout function for encoder run timer. */
pvr2_hdw_encoder_run_timeout(struct timer_list * t)44558da0edf2SKees Cook static void pvr2_hdw_encoder_run_timeout(struct timer_list *t)
44560c0d06caSMauro Carvalho Chehab {
44578da0edf2SKees Cook 	struct pvr2_hdw *hdw = from_timer(hdw, t, encoder_run_timer);
44580c0d06caSMauro Carvalho Chehab 	if (!hdw->state_encoder_runok) {
44590c0d06caSMauro Carvalho Chehab 		hdw->state_encoder_runok = !0;
44600c0d06caSMauro Carvalho Chehab 		trace_stbit("state_encoder_runok",hdw->state_encoder_runok);
44610c0d06caSMauro Carvalho Chehab 		hdw->state_stale = !0;
446229a3006eSBhaktipriya Shridhar 		schedule_work(&hdw->workpoll);
44630c0d06caSMauro Carvalho Chehab 	}
44640c0d06caSMauro Carvalho Chehab }
44650c0d06caSMauro Carvalho Chehab 
44660c0d06caSMauro Carvalho Chehab 
44670c0d06caSMauro Carvalho Chehab /* Evaluate whether or not state_decoder_run can change */
state_eval_decoder_run(struct pvr2_hdw * hdw)44680c0d06caSMauro Carvalho Chehab static int state_eval_decoder_run(struct pvr2_hdw *hdw)
44690c0d06caSMauro Carvalho Chehab {
44700c0d06caSMauro Carvalho Chehab 	if (hdw->state_decoder_run) {
44710c0d06caSMauro Carvalho Chehab 		if (hdw->state_encoder_ok) {
44720c0d06caSMauro Carvalho Chehab 			if (hdw->state_pipeline_req &&
44730c0d06caSMauro Carvalho Chehab 			    !hdw->state_pipeline_pause &&
44740c0d06caSMauro Carvalho Chehab 			    hdw->state_pathway_ok) return 0;
44750c0d06caSMauro Carvalho Chehab 		}
44760c0d06caSMauro Carvalho Chehab 		if (!hdw->flag_decoder_missed) {
44770c0d06caSMauro Carvalho Chehab 			pvr2_decoder_enable(hdw,0);
44780c0d06caSMauro Carvalho Chehab 		}
44790c0d06caSMauro Carvalho Chehab 		hdw->state_decoder_quiescent = 0;
44800c0d06caSMauro Carvalho Chehab 		hdw->state_decoder_run = 0;
44810c0d06caSMauro Carvalho Chehab 		/* paranoia - solve race if timer(s) just completed */
44820c0d06caSMauro Carvalho Chehab 		del_timer_sync(&hdw->quiescent_timer);
44830c0d06caSMauro Carvalho Chehab 		/* Kill the stabilization timer, in case we're killing the
44840c0d06caSMauro Carvalho Chehab 		   encoder before the previous stabilization interval has
44850c0d06caSMauro Carvalho Chehab 		   been properly timed. */
44860c0d06caSMauro Carvalho Chehab 		del_timer_sync(&hdw->decoder_stabilization_timer);
44870c0d06caSMauro Carvalho Chehab 		hdw->state_decoder_ready = 0;
44880c0d06caSMauro Carvalho Chehab 	} else {
44890c0d06caSMauro Carvalho Chehab 		if (!hdw->state_decoder_quiescent) {
44900c0d06caSMauro Carvalho Chehab 			if (!timer_pending(&hdw->quiescent_timer)) {
44910c0d06caSMauro Carvalho Chehab 				/* We don't do something about the
44920c0d06caSMauro Carvalho Chehab 				   quiescent timer until right here because
44930c0d06caSMauro Carvalho Chehab 				   we also want to catch cases where the
44940c0d06caSMauro Carvalho Chehab 				   decoder was already not running (like
44950c0d06caSMauro Carvalho Chehab 				   after initialization) as opposed to
44960c0d06caSMauro Carvalho Chehab 				   knowing that we had just stopped it.
44970c0d06caSMauro Carvalho Chehab 				   The second flag check is here to cover a
44980c0d06caSMauro Carvalho Chehab 				   race - the timer could have run and set
44990c0d06caSMauro Carvalho Chehab 				   this flag just after the previous check
45000c0d06caSMauro Carvalho Chehab 				   but before we did the pending check. */
45010c0d06caSMauro Carvalho Chehab 				if (!hdw->state_decoder_quiescent) {
45020c0d06caSMauro Carvalho Chehab 					hdw->quiescent_timer.expires =
45033edf7eb8SNicholas Mc Guire 						jiffies + msecs_to_jiffies(
45043edf7eb8SNicholas Mc Guire 						TIME_MSEC_DECODER_WAIT);
45050c0d06caSMauro Carvalho Chehab 					add_timer(&hdw->quiescent_timer);
45060c0d06caSMauro Carvalho Chehab 				}
45070c0d06caSMauro Carvalho Chehab 			}
45080c0d06caSMauro Carvalho Chehab 			/* Don't allow decoder to start again until it has
45090c0d06caSMauro Carvalho Chehab 			   been quiesced first.  This little detail should
45100c0d06caSMauro Carvalho Chehab 			   hopefully further stabilize the encoder. */
45110c0d06caSMauro Carvalho Chehab 			return 0;
45120c0d06caSMauro Carvalho Chehab 		}
45130c0d06caSMauro Carvalho Chehab 		if (!hdw->state_pathway_ok ||
45140c0d06caSMauro Carvalho Chehab 		    (hdw->pathway_state != PVR2_PATHWAY_ANALOG) ||
45150c0d06caSMauro Carvalho Chehab 		    !hdw->state_pipeline_req ||
45160c0d06caSMauro Carvalho Chehab 		    hdw->state_pipeline_pause ||
45170c0d06caSMauro Carvalho Chehab 		    !hdw->state_pipeline_config ||
45180c0d06caSMauro Carvalho Chehab 		    !hdw->state_encoder_config ||
45190c0d06caSMauro Carvalho Chehab 		    !hdw->state_encoder_ok) return 0;
45200c0d06caSMauro Carvalho Chehab 		del_timer_sync(&hdw->quiescent_timer);
45210c0d06caSMauro Carvalho Chehab 		if (hdw->flag_decoder_missed) return 0;
45220c0d06caSMauro Carvalho Chehab 		if (pvr2_decoder_enable(hdw,!0) < 0) return 0;
45230c0d06caSMauro Carvalho Chehab 		hdw->state_decoder_quiescent = 0;
45240c0d06caSMauro Carvalho Chehab 		hdw->state_decoder_ready = 0;
45250c0d06caSMauro Carvalho Chehab 		hdw->state_decoder_run = !0;
45260c0d06caSMauro Carvalho Chehab 		if (hdw->decoder_client_id == PVR2_CLIENT_ID_SAA7115) {
45270c0d06caSMauro Carvalho Chehab 			hdw->decoder_stabilization_timer.expires =
45283edf7eb8SNicholas Mc Guire 				jiffies + msecs_to_jiffies(
45293edf7eb8SNicholas Mc Guire 				TIME_MSEC_DECODER_STABILIZATION_WAIT);
45300c0d06caSMauro Carvalho Chehab 			add_timer(&hdw->decoder_stabilization_timer);
45310c0d06caSMauro Carvalho Chehab 		} else {
45320c0d06caSMauro Carvalho Chehab 			hdw->state_decoder_ready = !0;
45330c0d06caSMauro Carvalho Chehab 		}
45340c0d06caSMauro Carvalho Chehab 	}
45350c0d06caSMauro Carvalho Chehab 	trace_stbit("state_decoder_quiescent",hdw->state_decoder_quiescent);
45360c0d06caSMauro Carvalho Chehab 	trace_stbit("state_decoder_run",hdw->state_decoder_run);
45370c0d06caSMauro Carvalho Chehab 	trace_stbit("state_decoder_ready", hdw->state_decoder_ready);
45380c0d06caSMauro Carvalho Chehab 	return !0;
45390c0d06caSMauro Carvalho Chehab }
45400c0d06caSMauro Carvalho Chehab 
45410c0d06caSMauro Carvalho Chehab 
45420c0d06caSMauro Carvalho Chehab /* Evaluate whether or not state_usbstream_run can change */
state_eval_usbstream_run(struct pvr2_hdw * hdw)45430c0d06caSMauro Carvalho Chehab static int state_eval_usbstream_run(struct pvr2_hdw *hdw)
45440c0d06caSMauro Carvalho Chehab {
45450c0d06caSMauro Carvalho Chehab 	if (hdw->state_usbstream_run) {
45460c0d06caSMauro Carvalho Chehab 		int fl = !0;
45470c0d06caSMauro Carvalho Chehab 		if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) {
45480c0d06caSMauro Carvalho Chehab 			fl = (hdw->state_encoder_ok &&
45490c0d06caSMauro Carvalho Chehab 			      hdw->state_encoder_run);
45500c0d06caSMauro Carvalho Chehab 		} else if ((hdw->pathway_state == PVR2_PATHWAY_DIGITAL) &&
45510c0d06caSMauro Carvalho Chehab 			   (hdw->hdw_desc->flag_digital_requires_cx23416)) {
45520c0d06caSMauro Carvalho Chehab 			fl = hdw->state_encoder_ok;
45530c0d06caSMauro Carvalho Chehab 		}
45540c0d06caSMauro Carvalho Chehab 		if (fl &&
45550c0d06caSMauro Carvalho Chehab 		    hdw->state_pipeline_req &&
45560c0d06caSMauro Carvalho Chehab 		    !hdw->state_pipeline_pause &&
45570c0d06caSMauro Carvalho Chehab 		    hdw->state_pathway_ok) {
45580c0d06caSMauro Carvalho Chehab 			return 0;
45590c0d06caSMauro Carvalho Chehab 		}
45600c0d06caSMauro Carvalho Chehab 		pvr2_hdw_cmd_usbstream(hdw,0);
45610c0d06caSMauro Carvalho Chehab 		hdw->state_usbstream_run = 0;
45620c0d06caSMauro Carvalho Chehab 	} else {
45630c0d06caSMauro Carvalho Chehab 		if (!hdw->state_pipeline_req ||
45640c0d06caSMauro Carvalho Chehab 		    hdw->state_pipeline_pause ||
45650c0d06caSMauro Carvalho Chehab 		    !hdw->state_pathway_ok) return 0;
45660c0d06caSMauro Carvalho Chehab 		if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) {
45670c0d06caSMauro Carvalho Chehab 			if (!hdw->state_encoder_ok ||
45680c0d06caSMauro Carvalho Chehab 			    !hdw->state_encoder_run) return 0;
45690c0d06caSMauro Carvalho Chehab 		} else if ((hdw->pathway_state == PVR2_PATHWAY_DIGITAL) &&
45700c0d06caSMauro Carvalho Chehab 			   (hdw->hdw_desc->flag_digital_requires_cx23416)) {
45710c0d06caSMauro Carvalho Chehab 			if (!hdw->state_encoder_ok) return 0;
45720c0d06caSMauro Carvalho Chehab 			if (hdw->state_encoder_run) return 0;
45730c0d06caSMauro Carvalho Chehab 			if (hdw->hdw_desc->digital_control_scheme ==
45740c0d06caSMauro Carvalho Chehab 			    PVR2_DIGITAL_SCHEME_ONAIR) {
45750c0d06caSMauro Carvalho Chehab 				/* OnAir digital receivers won't stream
45760c0d06caSMauro Carvalho Chehab 				   unless the analog encoder has run first.
45770c0d06caSMauro Carvalho Chehab 				   Why?  I have no idea.  But don't even
45780c0d06caSMauro Carvalho Chehab 				   try until we know the analog side is
45790c0d06caSMauro Carvalho Chehab 				   known to have run. */
45800c0d06caSMauro Carvalho Chehab 				if (!hdw->state_encoder_runok) return 0;
45810c0d06caSMauro Carvalho Chehab 			}
45820c0d06caSMauro Carvalho Chehab 		}
45830c0d06caSMauro Carvalho Chehab 		if (pvr2_hdw_cmd_usbstream(hdw,!0) < 0) return 0;
45840c0d06caSMauro Carvalho Chehab 		hdw->state_usbstream_run = !0;
45850c0d06caSMauro Carvalho Chehab 	}
45860c0d06caSMauro Carvalho Chehab 	trace_stbit("state_usbstream_run",hdw->state_usbstream_run);
45870c0d06caSMauro Carvalho Chehab 	return !0;
45880c0d06caSMauro Carvalho Chehab }
45890c0d06caSMauro Carvalho Chehab 
45900c0d06caSMauro Carvalho Chehab 
45910c0d06caSMauro Carvalho Chehab /* Attempt to configure pipeline, if needed */
state_eval_pipeline_config(struct pvr2_hdw * hdw)45920c0d06caSMauro Carvalho Chehab static int state_eval_pipeline_config(struct pvr2_hdw *hdw)
45930c0d06caSMauro Carvalho Chehab {
45940c0d06caSMauro Carvalho Chehab 	if (hdw->state_pipeline_config ||
45950c0d06caSMauro Carvalho Chehab 	    hdw->state_pipeline_pause) return 0;
45960c0d06caSMauro Carvalho Chehab 	pvr2_hdw_commit_execute(hdw);
45970c0d06caSMauro Carvalho Chehab 	return !0;
45980c0d06caSMauro Carvalho Chehab }
45990c0d06caSMauro Carvalho Chehab 
46000c0d06caSMauro Carvalho Chehab 
46010c0d06caSMauro Carvalho Chehab /* Update pipeline idle and pipeline pause tracking states based on other
46020c0d06caSMauro Carvalho Chehab    inputs.  This must be called whenever the other relevant inputs have
46030c0d06caSMauro Carvalho Chehab    changed. */
state_update_pipeline_state(struct pvr2_hdw * hdw)46040c0d06caSMauro Carvalho Chehab static int state_update_pipeline_state(struct pvr2_hdw *hdw)
46050c0d06caSMauro Carvalho Chehab {
46060c0d06caSMauro Carvalho Chehab 	unsigned int st;
46070c0d06caSMauro Carvalho Chehab 	int updatedFl = 0;
46080c0d06caSMauro Carvalho Chehab 	/* Update pipeline state */
46090c0d06caSMauro Carvalho Chehab 	st = !(hdw->state_encoder_run ||
46100c0d06caSMauro Carvalho Chehab 	       hdw->state_decoder_run ||
46110c0d06caSMauro Carvalho Chehab 	       hdw->state_usbstream_run ||
46120c0d06caSMauro Carvalho Chehab 	       (!hdw->state_decoder_quiescent));
46130c0d06caSMauro Carvalho Chehab 	if (!st != !hdw->state_pipeline_idle) {
46140c0d06caSMauro Carvalho Chehab 		hdw->state_pipeline_idle = st;
46150c0d06caSMauro Carvalho Chehab 		updatedFl = !0;
46160c0d06caSMauro Carvalho Chehab 	}
46170c0d06caSMauro Carvalho Chehab 	if (hdw->state_pipeline_idle && hdw->state_pipeline_pause) {
46180c0d06caSMauro Carvalho Chehab 		hdw->state_pipeline_pause = 0;
46190c0d06caSMauro Carvalho Chehab 		updatedFl = !0;
46200c0d06caSMauro Carvalho Chehab 	}
46210c0d06caSMauro Carvalho Chehab 	return updatedFl;
46220c0d06caSMauro Carvalho Chehab }
46230c0d06caSMauro Carvalho Chehab 
46240c0d06caSMauro Carvalho Chehab 
46250c0d06caSMauro Carvalho Chehab typedef int (*state_eval_func)(struct pvr2_hdw *);
46260c0d06caSMauro Carvalho Chehab 
46270c0d06caSMauro Carvalho Chehab /* Set of functions to be run to evaluate various states in the driver. */
46280c0d06caSMauro Carvalho Chehab static const state_eval_func eval_funcs[] = {
46290c0d06caSMauro Carvalho Chehab 	state_eval_pathway_ok,
46300c0d06caSMauro Carvalho Chehab 	state_eval_pipeline_config,
46310c0d06caSMauro Carvalho Chehab 	state_eval_encoder_ok,
46320c0d06caSMauro Carvalho Chehab 	state_eval_encoder_config,
46330c0d06caSMauro Carvalho Chehab 	state_eval_decoder_run,
46340c0d06caSMauro Carvalho Chehab 	state_eval_encoder_run,
46350c0d06caSMauro Carvalho Chehab 	state_eval_usbstream_run,
46360c0d06caSMauro Carvalho Chehab };
46370c0d06caSMauro Carvalho Chehab 
46380c0d06caSMauro Carvalho Chehab 
46390c0d06caSMauro Carvalho Chehab /* Process various states and return true if we did anything interesting. */
pvr2_hdw_state_update(struct pvr2_hdw * hdw)46400c0d06caSMauro Carvalho Chehab static int pvr2_hdw_state_update(struct pvr2_hdw *hdw)
46410c0d06caSMauro Carvalho Chehab {
46420c0d06caSMauro Carvalho Chehab 	unsigned int i;
46430c0d06caSMauro Carvalho Chehab 	int state_updated = 0;
46440c0d06caSMauro Carvalho Chehab 	int check_flag;
46450c0d06caSMauro Carvalho Chehab 
46460c0d06caSMauro Carvalho Chehab 	if (!hdw->state_stale) return 0;
46470c0d06caSMauro Carvalho Chehab 	if ((hdw->fw1_state != FW1_STATE_OK) ||
46480c0d06caSMauro Carvalho Chehab 	    !hdw->flag_ok) {
46490c0d06caSMauro Carvalho Chehab 		hdw->state_stale = 0;
46500c0d06caSMauro Carvalho Chehab 		return !0;
46510c0d06caSMauro Carvalho Chehab 	}
46520c0d06caSMauro Carvalho Chehab 	/* This loop is the heart of the entire driver.  It keeps trying to
46530c0d06caSMauro Carvalho Chehab 	   evaluate various bits of driver state until nothing changes for
46540c0d06caSMauro Carvalho Chehab 	   one full iteration.  Each "bit of state" tracks some global
46550c0d06caSMauro Carvalho Chehab 	   aspect of the driver, e.g. whether decoder should run, if
46560c0d06caSMauro Carvalho Chehab 	   pipeline is configured, usb streaming is on, etc.  We separately
46570c0d06caSMauro Carvalho Chehab 	   evaluate each of those questions based on other driver state to
46580c0d06caSMauro Carvalho Chehab 	   arrive at the correct running configuration. */
46590c0d06caSMauro Carvalho Chehab 	do {
46600c0d06caSMauro Carvalho Chehab 		check_flag = 0;
46610c0d06caSMauro Carvalho Chehab 		state_update_pipeline_state(hdw);
46620c0d06caSMauro Carvalho Chehab 		/* Iterate over each bit of state */
46630c0d06caSMauro Carvalho Chehab 		for (i = 0; (i<ARRAY_SIZE(eval_funcs)) && hdw->flag_ok; i++) {
46640c0d06caSMauro Carvalho Chehab 			if ((*eval_funcs[i])(hdw)) {
46650c0d06caSMauro Carvalho Chehab 				check_flag = !0;
46660c0d06caSMauro Carvalho Chehab 				state_updated = !0;
46670c0d06caSMauro Carvalho Chehab 				state_update_pipeline_state(hdw);
46680c0d06caSMauro Carvalho Chehab 			}
46690c0d06caSMauro Carvalho Chehab 		}
46700c0d06caSMauro Carvalho Chehab 	} while (check_flag && hdw->flag_ok);
46710c0d06caSMauro Carvalho Chehab 	hdw->state_stale = 0;
46720c0d06caSMauro Carvalho Chehab 	trace_stbit("state_stale",hdw->state_stale);
46730c0d06caSMauro Carvalho Chehab 	return state_updated;
46740c0d06caSMauro Carvalho Chehab }
46750c0d06caSMauro Carvalho Chehab 
46760c0d06caSMauro Carvalho Chehab 
print_input_mask(unsigned int msk,char * buf,unsigned int acnt)46770c0d06caSMauro Carvalho Chehab static unsigned int print_input_mask(unsigned int msk,
46780c0d06caSMauro Carvalho Chehab 				     char *buf,unsigned int acnt)
46790c0d06caSMauro Carvalho Chehab {
46800c0d06caSMauro Carvalho Chehab 	unsigned int idx,ccnt;
46810c0d06caSMauro Carvalho Chehab 	unsigned int tcnt = 0;
46820c0d06caSMauro Carvalho Chehab 	for (idx = 0; idx < ARRAY_SIZE(control_values_input); idx++) {
468395c52069SMauro Carvalho Chehab 		if (!((1UL << idx) & msk)) continue;
46840c0d06caSMauro Carvalho Chehab 		ccnt = scnprintf(buf+tcnt,
46850c0d06caSMauro Carvalho Chehab 				 acnt-tcnt,
46860c0d06caSMauro Carvalho Chehab 				 "%s%s",
46870c0d06caSMauro Carvalho Chehab 				 (tcnt ? ", " : ""),
46880c0d06caSMauro Carvalho Chehab 				 control_values_input[idx]);
46890c0d06caSMauro Carvalho Chehab 		tcnt += ccnt;
46900c0d06caSMauro Carvalho Chehab 	}
46910c0d06caSMauro Carvalho Chehab 	return tcnt;
46920c0d06caSMauro Carvalho Chehab }
46930c0d06caSMauro Carvalho Chehab 
46940c0d06caSMauro Carvalho Chehab 
pvr2_pathway_state_name(int id)46950c0d06caSMauro Carvalho Chehab static const char *pvr2_pathway_state_name(int id)
46960c0d06caSMauro Carvalho Chehab {
46970c0d06caSMauro Carvalho Chehab 	switch (id) {
46980c0d06caSMauro Carvalho Chehab 	case PVR2_PATHWAY_ANALOG: return "analog";
46990c0d06caSMauro Carvalho Chehab 	case PVR2_PATHWAY_DIGITAL: return "digital";
47000c0d06caSMauro Carvalho Chehab 	default: return "unknown";
47010c0d06caSMauro Carvalho Chehab 	}
47020c0d06caSMauro Carvalho Chehab }
47030c0d06caSMauro Carvalho Chehab 
47040c0d06caSMauro Carvalho Chehab 
pvr2_hdw_report_unlocked(struct pvr2_hdw * hdw,int which,char * buf,unsigned int acnt)47050c0d06caSMauro Carvalho Chehab static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which,
47060c0d06caSMauro Carvalho Chehab 					     char *buf,unsigned int acnt)
47070c0d06caSMauro Carvalho Chehab {
47080c0d06caSMauro Carvalho Chehab 	switch (which) {
47090c0d06caSMauro Carvalho Chehab 	case 0:
47100c0d06caSMauro Carvalho Chehab 		return scnprintf(
47110c0d06caSMauro Carvalho Chehab 			buf,acnt,
47120c0d06caSMauro Carvalho Chehab 			"driver:%s%s%s%s%s <mode=%s>",
47130c0d06caSMauro Carvalho Chehab 			(hdw->flag_ok ? " <ok>" : " <fail>"),
47140c0d06caSMauro Carvalho Chehab 			(hdw->flag_init_ok ? " <init>" : " <uninitialized>"),
47150c0d06caSMauro Carvalho Chehab 			(hdw->flag_disconnected ? " <disconnected>" :
47160c0d06caSMauro Carvalho Chehab 			 " <connected>"),
47170c0d06caSMauro Carvalho Chehab 			(hdw->flag_tripped ? " <tripped>" : ""),
47180c0d06caSMauro Carvalho Chehab 			(hdw->flag_decoder_missed ? " <no decoder>" : ""),
47190c0d06caSMauro Carvalho Chehab 			pvr2_pathway_state_name(hdw->pathway_state));
47200c0d06caSMauro Carvalho Chehab 
47210c0d06caSMauro Carvalho Chehab 	case 1:
47220c0d06caSMauro Carvalho Chehab 		return scnprintf(
47230c0d06caSMauro Carvalho Chehab 			buf,acnt,
47240c0d06caSMauro Carvalho Chehab 			"pipeline:%s%s%s%s",
47250c0d06caSMauro Carvalho Chehab 			(hdw->state_pipeline_idle ? " <idle>" : ""),
47260c0d06caSMauro Carvalho Chehab 			(hdw->state_pipeline_config ?
47270c0d06caSMauro Carvalho Chehab 			 " <configok>" : " <stale>"),
47280c0d06caSMauro Carvalho Chehab 			(hdw->state_pipeline_req ? " <req>" : ""),
47290c0d06caSMauro Carvalho Chehab 			(hdw->state_pipeline_pause ? " <pause>" : ""));
47300c0d06caSMauro Carvalho Chehab 	case 2:
47310c0d06caSMauro Carvalho Chehab 		return scnprintf(
47320c0d06caSMauro Carvalho Chehab 			buf,acnt,
47330c0d06caSMauro Carvalho Chehab 			"worker:%s%s%s%s%s%s%s",
47340c0d06caSMauro Carvalho Chehab 			(hdw->state_decoder_run ?
47350c0d06caSMauro Carvalho Chehab 			 (hdw->state_decoder_ready ?
47360c0d06caSMauro Carvalho Chehab 			  "<decode:run>" : " <decode:start>") :
47370c0d06caSMauro Carvalho Chehab 			 (hdw->state_decoder_quiescent ?
47380c0d06caSMauro Carvalho Chehab 			  "" : " <decode:stop>")),
47390c0d06caSMauro Carvalho Chehab 			(hdw->state_decoder_quiescent ?
47400c0d06caSMauro Carvalho Chehab 			 " <decode:quiescent>" : ""),
47410c0d06caSMauro Carvalho Chehab 			(hdw->state_encoder_ok ?
47420c0d06caSMauro Carvalho Chehab 			 "" : " <encode:init>"),
47430c0d06caSMauro Carvalho Chehab 			(hdw->state_encoder_run ?
47440c0d06caSMauro Carvalho Chehab 			 (hdw->state_encoder_runok ?
47450c0d06caSMauro Carvalho Chehab 			  " <encode:run>" :
47460c0d06caSMauro Carvalho Chehab 			  " <encode:firstrun>") :
47470c0d06caSMauro Carvalho Chehab 			 (hdw->state_encoder_runok ?
47480c0d06caSMauro Carvalho Chehab 			  " <encode:stop>" :
47490c0d06caSMauro Carvalho Chehab 			  " <encode:virgin>")),
47500c0d06caSMauro Carvalho Chehab 			(hdw->state_encoder_config ?
47510c0d06caSMauro Carvalho Chehab 			 " <encode:configok>" :
47520c0d06caSMauro Carvalho Chehab 			 (hdw->state_encoder_waitok ?
47530c0d06caSMauro Carvalho Chehab 			  "" : " <encode:waitok>")),
47540c0d06caSMauro Carvalho Chehab 			(hdw->state_usbstream_run ?
47550c0d06caSMauro Carvalho Chehab 			 " <usb:run>" : " <usb:stop>"),
47560c0d06caSMauro Carvalho Chehab 			(hdw->state_pathway_ok ?
47570c0d06caSMauro Carvalho Chehab 			 " <pathway:ok>" : ""));
47580c0d06caSMauro Carvalho Chehab 	case 3:
47590c0d06caSMauro Carvalho Chehab 		return scnprintf(
47600c0d06caSMauro Carvalho Chehab 			buf,acnt,
47610c0d06caSMauro Carvalho Chehab 			"state: %s",
47620c0d06caSMauro Carvalho Chehab 			pvr2_get_state_name(hdw->master_state));
47630c0d06caSMauro Carvalho Chehab 	case 4: {
47640c0d06caSMauro Carvalho Chehab 		unsigned int tcnt = 0;
47650c0d06caSMauro Carvalho Chehab 		unsigned int ccnt;
47660c0d06caSMauro Carvalho Chehab 
47670c0d06caSMauro Carvalho Chehab 		ccnt = scnprintf(buf,
47680c0d06caSMauro Carvalho Chehab 				 acnt,
47690c0d06caSMauro Carvalho Chehab 				 "Hardware supported inputs: ");
47700c0d06caSMauro Carvalho Chehab 		tcnt += ccnt;
47710c0d06caSMauro Carvalho Chehab 		tcnt += print_input_mask(hdw->input_avail_mask,
47720c0d06caSMauro Carvalho Chehab 					 buf+tcnt,
47730c0d06caSMauro Carvalho Chehab 					 acnt-tcnt);
47740c0d06caSMauro Carvalho Chehab 		if (hdw->input_avail_mask != hdw->input_allowed_mask) {
47750c0d06caSMauro Carvalho Chehab 			ccnt = scnprintf(buf+tcnt,
47760c0d06caSMauro Carvalho Chehab 					 acnt-tcnt,
47770c0d06caSMauro Carvalho Chehab 					 "; allowed inputs: ");
47780c0d06caSMauro Carvalho Chehab 			tcnt += ccnt;
47790c0d06caSMauro Carvalho Chehab 			tcnt += print_input_mask(hdw->input_allowed_mask,
47800c0d06caSMauro Carvalho Chehab 						 buf+tcnt,
47810c0d06caSMauro Carvalho Chehab 						 acnt-tcnt);
47820c0d06caSMauro Carvalho Chehab 		}
47830c0d06caSMauro Carvalho Chehab 		return tcnt;
47840c0d06caSMauro Carvalho Chehab 	}
47850c0d06caSMauro Carvalho Chehab 	case 5: {
47860c0d06caSMauro Carvalho Chehab 		struct pvr2_stream_stats stats;
47870c0d06caSMauro Carvalho Chehab 		if (!hdw->vid_stream) break;
47880c0d06caSMauro Carvalho Chehab 		pvr2_stream_get_stats(hdw->vid_stream,
47890c0d06caSMauro Carvalho Chehab 				      &stats,
47900c0d06caSMauro Carvalho Chehab 				      0);
47910c0d06caSMauro Carvalho Chehab 		return scnprintf(
47920c0d06caSMauro Carvalho Chehab 			buf,acnt,
479396292c89SMauro Carvalho Chehab 			"Bytes streamed=%u URBs: queued=%u idle=%u ready=%u processed=%u failed=%u",
47940c0d06caSMauro Carvalho Chehab 			stats.bytes_processed,
47950c0d06caSMauro Carvalho Chehab 			stats.buffers_in_queue,
47960c0d06caSMauro Carvalho Chehab 			stats.buffers_in_idle,
47970c0d06caSMauro Carvalho Chehab 			stats.buffers_in_ready,
47980c0d06caSMauro Carvalho Chehab 			stats.buffers_processed,
47990c0d06caSMauro Carvalho Chehab 			stats.buffers_failed);
48000c0d06caSMauro Carvalho Chehab 	}
48010c0d06caSMauro Carvalho Chehab 	case 6: {
48020c0d06caSMauro Carvalho Chehab 		unsigned int id = hdw->ir_scheme_active;
48030c0d06caSMauro Carvalho Chehab 		return scnprintf(buf, acnt, "ir scheme: id=%d %s", id,
48040c0d06caSMauro Carvalho Chehab 				 (id >= ARRAY_SIZE(ir_scheme_names) ?
48050c0d06caSMauro Carvalho Chehab 				  "?" : ir_scheme_names[id]));
48060c0d06caSMauro Carvalho Chehab 	}
48070c0d06caSMauro Carvalho Chehab 	default: break;
48080c0d06caSMauro Carvalho Chehab 	}
48090c0d06caSMauro Carvalho Chehab 	return 0;
48100c0d06caSMauro Carvalho Chehab }
48110c0d06caSMauro Carvalho Chehab 
48120c0d06caSMauro Carvalho Chehab 
48130c0d06caSMauro Carvalho Chehab /* Generate report containing info about attached sub-devices and attached
48140c0d06caSMauro Carvalho Chehab    i2c clients, including an indication of which attached i2c clients are
48150c0d06caSMauro Carvalho Chehab    actually sub-devices. */
pvr2_hdw_report_clients(struct pvr2_hdw * hdw,char * buf,unsigned int acnt)48160c0d06caSMauro Carvalho Chehab static unsigned int pvr2_hdw_report_clients(struct pvr2_hdw *hdw,
48170c0d06caSMauro Carvalho Chehab 					    char *buf, unsigned int acnt)
48180c0d06caSMauro Carvalho Chehab {
48190c0d06caSMauro Carvalho Chehab 	struct v4l2_subdev *sd;
48200c0d06caSMauro Carvalho Chehab 	unsigned int tcnt = 0;
48210c0d06caSMauro Carvalho Chehab 	unsigned int ccnt;
48220c0d06caSMauro Carvalho Chehab 	struct i2c_client *client;
48230c0d06caSMauro Carvalho Chehab 	const char *p;
48240c0d06caSMauro Carvalho Chehab 	unsigned int id;
48250c0d06caSMauro Carvalho Chehab 
48260c0d06caSMauro Carvalho Chehab 	ccnt = scnprintf(buf, acnt, "Associated v4l2-subdev drivers and I2C clients:\n");
48270c0d06caSMauro Carvalho Chehab 	tcnt += ccnt;
48280c0d06caSMauro Carvalho Chehab 	v4l2_device_for_each_subdev(sd, &hdw->v4l2_dev) {
48290c0d06caSMauro Carvalho Chehab 		id = sd->grp_id;
48300c0d06caSMauro Carvalho Chehab 		p = NULL;
48310c0d06caSMauro Carvalho Chehab 		if (id < ARRAY_SIZE(module_names)) p = module_names[id];
48320c0d06caSMauro Carvalho Chehab 		if (p) {
48330c0d06caSMauro Carvalho Chehab 			ccnt = scnprintf(buf + tcnt, acnt - tcnt, "  %s:", p);
48340c0d06caSMauro Carvalho Chehab 			tcnt += ccnt;
48350c0d06caSMauro Carvalho Chehab 		} else {
48360c0d06caSMauro Carvalho Chehab 			ccnt = scnprintf(buf + tcnt, acnt - tcnt,
48370c0d06caSMauro Carvalho Chehab 					 "  (unknown id=%u):", id);
48380c0d06caSMauro Carvalho Chehab 			tcnt += ccnt;
48390c0d06caSMauro Carvalho Chehab 		}
48400c0d06caSMauro Carvalho Chehab 		client = v4l2_get_subdevdata(sd);
48410c0d06caSMauro Carvalho Chehab 		if (client) {
48420c0d06caSMauro Carvalho Chehab 			ccnt = scnprintf(buf + tcnt, acnt - tcnt,
48430c0d06caSMauro Carvalho Chehab 					 " %s @ %02x\n", client->name,
48440c0d06caSMauro Carvalho Chehab 					 client->addr);
48450c0d06caSMauro Carvalho Chehab 			tcnt += ccnt;
48460c0d06caSMauro Carvalho Chehab 		} else {
48470c0d06caSMauro Carvalho Chehab 			ccnt = scnprintf(buf + tcnt, acnt - tcnt,
48480c0d06caSMauro Carvalho Chehab 					 " no i2c client\n");
48490c0d06caSMauro Carvalho Chehab 			tcnt += ccnt;
48500c0d06caSMauro Carvalho Chehab 		}
48510c0d06caSMauro Carvalho Chehab 	}
48520c0d06caSMauro Carvalho Chehab 	return tcnt;
48530c0d06caSMauro Carvalho Chehab }
48540c0d06caSMauro Carvalho Chehab 
48550c0d06caSMauro Carvalho Chehab 
pvr2_hdw_state_report(struct pvr2_hdw * hdw,char * buf,unsigned int acnt)48560c0d06caSMauro Carvalho Chehab unsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw,
48570c0d06caSMauro Carvalho Chehab 				   char *buf,unsigned int acnt)
48580c0d06caSMauro Carvalho Chehab {
48590c0d06caSMauro Carvalho Chehab 	unsigned int bcnt,ccnt,idx;
48600c0d06caSMauro Carvalho Chehab 	bcnt = 0;
48610c0d06caSMauro Carvalho Chehab 	LOCK_TAKE(hdw->big_lock);
48620c0d06caSMauro Carvalho Chehab 	for (idx = 0; ; idx++) {
48630c0d06caSMauro Carvalho Chehab 		ccnt = pvr2_hdw_report_unlocked(hdw,idx,buf,acnt);
48640c0d06caSMauro Carvalho Chehab 		if (!ccnt) break;
48650c0d06caSMauro Carvalho Chehab 		bcnt += ccnt; acnt -= ccnt; buf += ccnt;
48660c0d06caSMauro Carvalho Chehab 		if (!acnt) break;
48670c0d06caSMauro Carvalho Chehab 		buf[0] = '\n'; ccnt = 1;
48680c0d06caSMauro Carvalho Chehab 		bcnt += ccnt; acnt -= ccnt; buf += ccnt;
48690c0d06caSMauro Carvalho Chehab 	}
48700c0d06caSMauro Carvalho Chehab 	ccnt = pvr2_hdw_report_clients(hdw, buf, acnt);
48710c0d06caSMauro Carvalho Chehab 	bcnt += ccnt; acnt -= ccnt; buf += ccnt;
48720c0d06caSMauro Carvalho Chehab 	LOCK_GIVE(hdw->big_lock);
48730c0d06caSMauro Carvalho Chehab 	return bcnt;
48740c0d06caSMauro Carvalho Chehab }
48750c0d06caSMauro Carvalho Chehab 
48760c0d06caSMauro Carvalho Chehab 
pvr2_hdw_state_log_state(struct pvr2_hdw * hdw)48770c0d06caSMauro Carvalho Chehab static void pvr2_hdw_state_log_state(struct pvr2_hdw *hdw)
48780c0d06caSMauro Carvalho Chehab {
48790c0d06caSMauro Carvalho Chehab 	char buf[256];
48800c0d06caSMauro Carvalho Chehab 	unsigned int idx, ccnt;
48810c0d06caSMauro Carvalho Chehab 	unsigned int lcnt, ucnt;
48820c0d06caSMauro Carvalho Chehab 
48830c0d06caSMauro Carvalho Chehab 	for (idx = 0; ; idx++) {
48840c0d06caSMauro Carvalho Chehab 		ccnt = pvr2_hdw_report_unlocked(hdw,idx,buf,sizeof(buf));
48850c0d06caSMauro Carvalho Chehab 		if (!ccnt) break;
48868f845c63SDafna Hirschfeld 		pr_info("%s %.*s\n", hdw->name, ccnt, buf);
48870c0d06caSMauro Carvalho Chehab 	}
48880c0d06caSMauro Carvalho Chehab 	ccnt = pvr2_hdw_report_clients(hdw, buf, sizeof(buf));
48892e158d45SMauro Carvalho Chehab 	if (ccnt >= sizeof(buf))
48902e158d45SMauro Carvalho Chehab 		ccnt = sizeof(buf);
48912e158d45SMauro Carvalho Chehab 
48920c0d06caSMauro Carvalho Chehab 	ucnt = 0;
48930c0d06caSMauro Carvalho Chehab 	while (ucnt < ccnt) {
48940c0d06caSMauro Carvalho Chehab 		lcnt = 0;
48950c0d06caSMauro Carvalho Chehab 		while ((lcnt + ucnt < ccnt) && (buf[lcnt + ucnt] != '\n')) {
48960c0d06caSMauro Carvalho Chehab 			lcnt++;
48970c0d06caSMauro Carvalho Chehab 		}
48988f845c63SDafna Hirschfeld 		pr_info("%s %.*s\n", hdw->name, lcnt, buf + ucnt);
48990c0d06caSMauro Carvalho Chehab 		ucnt += lcnt + 1;
49000c0d06caSMauro Carvalho Chehab 	}
49010c0d06caSMauro Carvalho Chehab }
49020c0d06caSMauro Carvalho Chehab 
49030c0d06caSMauro Carvalho Chehab 
49040c0d06caSMauro Carvalho Chehab /* Evaluate and update the driver's current state, taking various actions
49050c0d06caSMauro Carvalho Chehab    as appropriate for the update. */
pvr2_hdw_state_eval(struct pvr2_hdw * hdw)49060c0d06caSMauro Carvalho Chehab static int pvr2_hdw_state_eval(struct pvr2_hdw *hdw)
49070c0d06caSMauro Carvalho Chehab {
49080c0d06caSMauro Carvalho Chehab 	unsigned int st;
49090c0d06caSMauro Carvalho Chehab 	int state_updated = 0;
49100c0d06caSMauro Carvalho Chehab 	int callback_flag = 0;
49110c0d06caSMauro Carvalho Chehab 	int analog_mode;
49120c0d06caSMauro Carvalho Chehab 
49130c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_STBITS,
49140c0d06caSMauro Carvalho Chehab 		   "Drive state check START");
49150c0d06caSMauro Carvalho Chehab 	if (pvrusb2_debug & PVR2_TRACE_STBITS) {
49160c0d06caSMauro Carvalho Chehab 		pvr2_hdw_state_log_state(hdw);
49170c0d06caSMauro Carvalho Chehab 	}
49180c0d06caSMauro Carvalho Chehab 
49190c0d06caSMauro Carvalho Chehab 	/* Process all state and get back over disposition */
49200c0d06caSMauro Carvalho Chehab 	state_updated = pvr2_hdw_state_update(hdw);
49210c0d06caSMauro Carvalho Chehab 
49220c0d06caSMauro Carvalho Chehab 	analog_mode = (hdw->pathway_state != PVR2_PATHWAY_DIGITAL);
49230c0d06caSMauro Carvalho Chehab 
49240c0d06caSMauro Carvalho Chehab 	/* Update master state based upon all other states. */
49250c0d06caSMauro Carvalho Chehab 	if (!hdw->flag_ok) {
49260c0d06caSMauro Carvalho Chehab 		st = PVR2_STATE_DEAD;
49270c0d06caSMauro Carvalho Chehab 	} else if (hdw->fw1_state != FW1_STATE_OK) {
49280c0d06caSMauro Carvalho Chehab 		st = PVR2_STATE_COLD;
49290c0d06caSMauro Carvalho Chehab 	} else if ((analog_mode ||
49300c0d06caSMauro Carvalho Chehab 		    hdw->hdw_desc->flag_digital_requires_cx23416) &&
49310c0d06caSMauro Carvalho Chehab 		   !hdw->state_encoder_ok) {
49320c0d06caSMauro Carvalho Chehab 		st = PVR2_STATE_WARM;
49330c0d06caSMauro Carvalho Chehab 	} else if (hdw->flag_tripped ||
49340c0d06caSMauro Carvalho Chehab 		   (analog_mode && hdw->flag_decoder_missed)) {
49350c0d06caSMauro Carvalho Chehab 		st = PVR2_STATE_ERROR;
49360c0d06caSMauro Carvalho Chehab 	} else if (hdw->state_usbstream_run &&
49370c0d06caSMauro Carvalho Chehab 		   (!analog_mode ||
49380c0d06caSMauro Carvalho Chehab 		    (hdw->state_encoder_run && hdw->state_decoder_run))) {
49390c0d06caSMauro Carvalho Chehab 		st = PVR2_STATE_RUN;
49400c0d06caSMauro Carvalho Chehab 	} else {
49410c0d06caSMauro Carvalho Chehab 		st = PVR2_STATE_READY;
49420c0d06caSMauro Carvalho Chehab 	}
49430c0d06caSMauro Carvalho Chehab 	if (hdw->master_state != st) {
49440c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_STATE,
49450c0d06caSMauro Carvalho Chehab 			   "Device state change from %s to %s",
49460c0d06caSMauro Carvalho Chehab 			   pvr2_get_state_name(hdw->master_state),
49470c0d06caSMauro Carvalho Chehab 			   pvr2_get_state_name(st));
49480c0d06caSMauro Carvalho Chehab 		pvr2_led_ctrl(hdw,st == PVR2_STATE_RUN);
49490c0d06caSMauro Carvalho Chehab 		hdw->master_state = st;
49500c0d06caSMauro Carvalho Chehab 		state_updated = !0;
49510c0d06caSMauro Carvalho Chehab 		callback_flag = !0;
49520c0d06caSMauro Carvalho Chehab 	}
49530c0d06caSMauro Carvalho Chehab 	if (state_updated) {
49540c0d06caSMauro Carvalho Chehab 		/* Trigger anyone waiting on any state changes here. */
49550c0d06caSMauro Carvalho Chehab 		wake_up(&hdw->state_wait_data);
49560c0d06caSMauro Carvalho Chehab 	}
49570c0d06caSMauro Carvalho Chehab 
49580c0d06caSMauro Carvalho Chehab 	if (pvrusb2_debug & PVR2_TRACE_STBITS) {
49590c0d06caSMauro Carvalho Chehab 		pvr2_hdw_state_log_state(hdw);
49600c0d06caSMauro Carvalho Chehab 	}
49610c0d06caSMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_STBITS,
49620c0d06caSMauro Carvalho Chehab 		   "Drive state check DONE callback=%d",callback_flag);
49630c0d06caSMauro Carvalho Chehab 
49640c0d06caSMauro Carvalho Chehab 	return callback_flag;
49650c0d06caSMauro Carvalho Chehab }
49660c0d06caSMauro Carvalho Chehab 
49670c0d06caSMauro Carvalho Chehab 
49680c0d06caSMauro Carvalho Chehab /* Cause kernel thread to check / update driver state */
pvr2_hdw_state_sched(struct pvr2_hdw * hdw)49690c0d06caSMauro Carvalho Chehab static void pvr2_hdw_state_sched(struct pvr2_hdw *hdw)
49700c0d06caSMauro Carvalho Chehab {
49710c0d06caSMauro Carvalho Chehab 	if (hdw->state_stale) return;
49720c0d06caSMauro Carvalho Chehab 	hdw->state_stale = !0;
49730c0d06caSMauro Carvalho Chehab 	trace_stbit("state_stale",hdw->state_stale);
497429a3006eSBhaktipriya Shridhar 	schedule_work(&hdw->workpoll);
49750c0d06caSMauro Carvalho Chehab }
49760c0d06caSMauro Carvalho Chehab 
49770c0d06caSMauro Carvalho Chehab 
pvr2_hdw_gpio_get_dir(struct pvr2_hdw * hdw,u32 * dp)49780c0d06caSMauro Carvalho Chehab int pvr2_hdw_gpio_get_dir(struct pvr2_hdw *hdw,u32 *dp)
49790c0d06caSMauro Carvalho Chehab {
49800c0d06caSMauro Carvalho Chehab 	return pvr2_read_register(hdw,PVR2_GPIO_DIR,dp);
49810c0d06caSMauro Carvalho Chehab }
49820c0d06caSMauro Carvalho Chehab 
49830c0d06caSMauro Carvalho Chehab 
pvr2_hdw_gpio_get_out(struct pvr2_hdw * hdw,u32 * dp)49840c0d06caSMauro Carvalho Chehab int pvr2_hdw_gpio_get_out(struct pvr2_hdw *hdw,u32 *dp)
49850c0d06caSMauro Carvalho Chehab {
49860c0d06caSMauro Carvalho Chehab 	return pvr2_read_register(hdw,PVR2_GPIO_OUT,dp);
49870c0d06caSMauro Carvalho Chehab }
49880c0d06caSMauro Carvalho Chehab 
49890c0d06caSMauro Carvalho Chehab 
pvr2_hdw_gpio_get_in(struct pvr2_hdw * hdw,u32 * dp)49900c0d06caSMauro Carvalho Chehab int pvr2_hdw_gpio_get_in(struct pvr2_hdw *hdw,u32 *dp)
49910c0d06caSMauro Carvalho Chehab {
49920c0d06caSMauro Carvalho Chehab 	return pvr2_read_register(hdw,PVR2_GPIO_IN,dp);
49930c0d06caSMauro Carvalho Chehab }
49940c0d06caSMauro Carvalho Chehab 
49950c0d06caSMauro Carvalho Chehab 
pvr2_hdw_gpio_chg_dir(struct pvr2_hdw * hdw,u32 msk,u32 val)49960c0d06caSMauro Carvalho Chehab int pvr2_hdw_gpio_chg_dir(struct pvr2_hdw *hdw,u32 msk,u32 val)
49970c0d06caSMauro Carvalho Chehab {
49980c0d06caSMauro Carvalho Chehab 	u32 cval,nval;
49990c0d06caSMauro Carvalho Chehab 	int ret;
50000c0d06caSMauro Carvalho Chehab 	if (~msk) {
50010c0d06caSMauro Carvalho Chehab 		ret = pvr2_read_register(hdw,PVR2_GPIO_DIR,&cval);
50020c0d06caSMauro Carvalho Chehab 		if (ret) return ret;
50030c0d06caSMauro Carvalho Chehab 		nval = (cval & ~msk) | (val & msk);
50040c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_GPIO,
500596292c89SMauro Carvalho Chehab 			   "GPIO direction changing 0x%x:0x%x from 0x%x to 0x%x",
50060c0d06caSMauro Carvalho Chehab 			   msk,val,cval,nval);
50070c0d06caSMauro Carvalho Chehab 	} else {
50080c0d06caSMauro Carvalho Chehab 		nval = val;
50090c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_GPIO,
50100c0d06caSMauro Carvalho Chehab 			   "GPIO direction changing to 0x%x",nval);
50110c0d06caSMauro Carvalho Chehab 	}
50120c0d06caSMauro Carvalho Chehab 	return pvr2_write_register(hdw,PVR2_GPIO_DIR,nval);
50130c0d06caSMauro Carvalho Chehab }
50140c0d06caSMauro Carvalho Chehab 
50150c0d06caSMauro Carvalho Chehab 
pvr2_hdw_gpio_chg_out(struct pvr2_hdw * hdw,u32 msk,u32 val)50160c0d06caSMauro Carvalho Chehab int pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val)
50170c0d06caSMauro Carvalho Chehab {
50180c0d06caSMauro Carvalho Chehab 	u32 cval,nval;
50190c0d06caSMauro Carvalho Chehab 	int ret;
50200c0d06caSMauro Carvalho Chehab 	if (~msk) {
50210c0d06caSMauro Carvalho Chehab 		ret = pvr2_read_register(hdw,PVR2_GPIO_OUT,&cval);
50220c0d06caSMauro Carvalho Chehab 		if (ret) return ret;
50230c0d06caSMauro Carvalho Chehab 		nval = (cval & ~msk) | (val & msk);
50240c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_GPIO,
50250c0d06caSMauro Carvalho Chehab 			   "GPIO output changing 0x%x:0x%x from 0x%x to 0x%x",
50260c0d06caSMauro Carvalho Chehab 			   msk,val,cval,nval);
50270c0d06caSMauro Carvalho Chehab 	} else {
50280c0d06caSMauro Carvalho Chehab 		nval = val;
50290c0d06caSMauro Carvalho Chehab 		pvr2_trace(PVR2_TRACE_GPIO,
50300c0d06caSMauro Carvalho Chehab 			   "GPIO output changing to 0x%x",nval);
50310c0d06caSMauro Carvalho Chehab 	}
50320c0d06caSMauro Carvalho Chehab 	return pvr2_write_register(hdw,PVR2_GPIO_OUT,nval);
50330c0d06caSMauro Carvalho Chehab }
50340c0d06caSMauro Carvalho Chehab 
50350c0d06caSMauro Carvalho Chehab 
pvr2_hdw_status_poll(struct pvr2_hdw * hdw)50360c0d06caSMauro Carvalho Chehab void pvr2_hdw_status_poll(struct pvr2_hdw *hdw)
50370c0d06caSMauro Carvalho Chehab {
50380c0d06caSMauro Carvalho Chehab 	struct v4l2_tuner *vtp = &hdw->tuner_signal_info;
50390c0d06caSMauro Carvalho Chehab 	memset(vtp, 0, sizeof(*vtp));
50400c0d06caSMauro Carvalho Chehab 	vtp->type = (hdw->input_val == PVR2_CVAL_INPUT_RADIO) ?
50410c0d06caSMauro Carvalho Chehab 		V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
50420c0d06caSMauro Carvalho Chehab 	hdw->tuner_signal_stale = 0;
50430c0d06caSMauro Carvalho Chehab 	/* Note: There apparently is no replacement for VIDIOC_CROPCAP
50440c0d06caSMauro Carvalho Chehab 	   using v4l2-subdev - therefore we can't support that AT ALL right
50450c0d06caSMauro Carvalho Chehab 	   now.  (Of course, no sub-drivers seem to implement it either.
5046b813aa3dSJiang Jian 	   But now it's a chicken and egg problem...) */
50470c0d06caSMauro Carvalho Chehab 	v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, g_tuner, vtp);
504896292c89SMauro Carvalho Chehab 	pvr2_trace(PVR2_TRACE_CHIPS, "subdev status poll type=%u strength=%u audio=0x%x cap=0x%x low=%u hi=%u",
50490c0d06caSMauro Carvalho Chehab 		   vtp->type,
50500c0d06caSMauro Carvalho Chehab 		   vtp->signal, vtp->rxsubchans, vtp->capability,
50510c0d06caSMauro Carvalho Chehab 		   vtp->rangelow, vtp->rangehigh);
50520c0d06caSMauro Carvalho Chehab 
50530c0d06caSMauro Carvalho Chehab 	/* We have to do this to avoid getting into constant polling if
50540c0d06caSMauro Carvalho Chehab 	   there's nobody to answer a poll of cropcap info. */
50550c0d06caSMauro Carvalho Chehab 	hdw->cropcap_stale = 0;
50560c0d06caSMauro Carvalho Chehab }
50570c0d06caSMauro Carvalho Chehab 
50580c0d06caSMauro Carvalho Chehab 
pvr2_hdw_get_input_available(struct pvr2_hdw * hdw)50590c0d06caSMauro Carvalho Chehab unsigned int pvr2_hdw_get_input_available(struct pvr2_hdw *hdw)
50600c0d06caSMauro Carvalho Chehab {
50610c0d06caSMauro Carvalho Chehab 	return hdw->input_avail_mask;
50620c0d06caSMauro Carvalho Chehab }
50630c0d06caSMauro Carvalho Chehab 
50640c0d06caSMauro Carvalho Chehab 
pvr2_hdw_get_input_allowed(struct pvr2_hdw * hdw)50650c0d06caSMauro Carvalho Chehab unsigned int pvr2_hdw_get_input_allowed(struct pvr2_hdw *hdw)
50660c0d06caSMauro Carvalho Chehab {
50670c0d06caSMauro Carvalho Chehab 	return hdw->input_allowed_mask;
50680c0d06caSMauro Carvalho Chehab }
50690c0d06caSMauro Carvalho Chehab 
50700c0d06caSMauro Carvalho Chehab 
pvr2_hdw_set_input(struct pvr2_hdw * hdw,int v)50710c0d06caSMauro Carvalho Chehab static int pvr2_hdw_set_input(struct pvr2_hdw *hdw,int v)
50720c0d06caSMauro Carvalho Chehab {
50730c0d06caSMauro Carvalho Chehab 	if (hdw->input_val != v) {
50740c0d06caSMauro Carvalho Chehab 		hdw->input_val = v;
50750c0d06caSMauro Carvalho Chehab 		hdw->input_dirty = !0;
50760c0d06caSMauro Carvalho Chehab 	}
50770c0d06caSMauro Carvalho Chehab 
50780c0d06caSMauro Carvalho Chehab 	/* Handle side effects - if we switch to a mode that needs the RF
50790c0d06caSMauro Carvalho Chehab 	   tuner, then select the right frequency choice as well and mark
50800c0d06caSMauro Carvalho Chehab 	   it dirty. */
50810c0d06caSMauro Carvalho Chehab 	if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
50820c0d06caSMauro Carvalho Chehab 		hdw->freqSelector = 0;
50830c0d06caSMauro Carvalho Chehab 		hdw->freqDirty = !0;
50840c0d06caSMauro Carvalho Chehab 	} else if ((hdw->input_val == PVR2_CVAL_INPUT_TV) ||
50850c0d06caSMauro Carvalho Chehab 		   (hdw->input_val == PVR2_CVAL_INPUT_DTV)) {
50860c0d06caSMauro Carvalho Chehab 		hdw->freqSelector = 1;
50870c0d06caSMauro Carvalho Chehab 		hdw->freqDirty = !0;
50880c0d06caSMauro Carvalho Chehab 	}
50890c0d06caSMauro Carvalho Chehab 	return 0;
50900c0d06caSMauro Carvalho Chehab }
50910c0d06caSMauro Carvalho Chehab 
50920c0d06caSMauro Carvalho Chehab 
pvr2_hdw_set_input_allowed(struct pvr2_hdw * hdw,unsigned int change_mask,unsigned int change_val)50930c0d06caSMauro Carvalho Chehab int pvr2_hdw_set_input_allowed(struct pvr2_hdw *hdw,
50940c0d06caSMauro Carvalho Chehab 			       unsigned int change_mask,
50950c0d06caSMauro Carvalho Chehab 			       unsigned int change_val)
50960c0d06caSMauro Carvalho Chehab {
50970c0d06caSMauro Carvalho Chehab 	int ret = 0;
50980c0d06caSMauro Carvalho Chehab 	unsigned int nv,m,idx;
50990c0d06caSMauro Carvalho Chehab 	LOCK_TAKE(hdw->big_lock);
51000c0d06caSMauro Carvalho Chehab 	do {
51010c0d06caSMauro Carvalho Chehab 		nv = hdw->input_allowed_mask & ~change_mask;
51020c0d06caSMauro Carvalho Chehab 		nv |= (change_val & change_mask);
51030c0d06caSMauro Carvalho Chehab 		nv &= hdw->input_avail_mask;
51040c0d06caSMauro Carvalho Chehab 		if (!nv) {
51050c0d06caSMauro Carvalho Chehab 			/* No legal modes left; return error instead. */
51060c0d06caSMauro Carvalho Chehab 			ret = -EPERM;
51070c0d06caSMauro Carvalho Chehab 			break;
51080c0d06caSMauro Carvalho Chehab 		}
51090c0d06caSMauro Carvalho Chehab 		hdw->input_allowed_mask = nv;
511095c52069SMauro Carvalho Chehab 		if ((1UL << hdw->input_val) & hdw->input_allowed_mask) {
51110c0d06caSMauro Carvalho Chehab 			/* Current mode is still in the allowed mask, so
51120c0d06caSMauro Carvalho Chehab 			   we're done. */
51130c0d06caSMauro Carvalho Chehab 			break;
51140c0d06caSMauro Carvalho Chehab 		}
51150c0d06caSMauro Carvalho Chehab 		/* Select and switch to a mode that is still in the allowed
51160c0d06caSMauro Carvalho Chehab 		   mask */
51170c0d06caSMauro Carvalho Chehab 		if (!hdw->input_allowed_mask) {
51180c0d06caSMauro Carvalho Chehab 			/* Nothing legal; give up */
51190c0d06caSMauro Carvalho Chehab 			break;
51200c0d06caSMauro Carvalho Chehab 		}
51210c0d06caSMauro Carvalho Chehab 		m = hdw->input_allowed_mask;
51220c0d06caSMauro Carvalho Chehab 		for (idx = 0; idx < (sizeof(m) << 3); idx++) {
512395c52069SMauro Carvalho Chehab 			if (!((1UL << idx) & m)) continue;
51240c0d06caSMauro Carvalho Chehab 			pvr2_hdw_set_input(hdw,idx);
51250c0d06caSMauro Carvalho Chehab 			break;
51260c0d06caSMauro Carvalho Chehab 		}
51270c0d06caSMauro Carvalho Chehab 	} while (0);
51280c0d06caSMauro Carvalho Chehab 	LOCK_GIVE(hdw->big_lock);
51290c0d06caSMauro Carvalho Chehab 	return ret;
51300c0d06caSMauro Carvalho Chehab }
51310c0d06caSMauro Carvalho Chehab 
51320c0d06caSMauro Carvalho Chehab 
51330c0d06caSMauro Carvalho Chehab /* Find I2C address of eeprom */
pvr2_hdw_get_eeprom_addr(struct pvr2_hdw * hdw)51340c0d06caSMauro Carvalho Chehab static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw)
51350c0d06caSMauro Carvalho Chehab {
51360c0d06caSMauro Carvalho Chehab 	int result;
51370c0d06caSMauro Carvalho Chehab 	LOCK_TAKE(hdw->ctl_lock); do {
51380c0d06caSMauro Carvalho Chehab 		hdw->cmd_buffer[0] = FX2CMD_GET_EEPROM_ADDR;
51390c0d06caSMauro Carvalho Chehab 		result = pvr2_send_request(hdw,
51400c0d06caSMauro Carvalho Chehab 					   hdw->cmd_buffer,1,
51410c0d06caSMauro Carvalho Chehab 					   hdw->cmd_buffer,1);
51420c0d06caSMauro Carvalho Chehab 		if (result < 0) break;
51430c0d06caSMauro Carvalho Chehab 		result = hdw->cmd_buffer[0];
51440c0d06caSMauro Carvalho Chehab 	} while(0); LOCK_GIVE(hdw->ctl_lock);
51450c0d06caSMauro Carvalho Chehab 	return result;
51460c0d06caSMauro Carvalho Chehab }
5147