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