162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/errno.h>
862306a36Sopenharmony_ci#include <linux/string.h>
962306a36Sopenharmony_ci#include <linux/slab.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/firmware.h>
1262306a36Sopenharmony_ci#include <linux/videodev2.h>
1362306a36Sopenharmony_ci#include <media/v4l2-common.h>
1462306a36Sopenharmony_ci#include <media/tuner.h>
1562306a36Sopenharmony_ci#include "pvrusb2.h"
1662306a36Sopenharmony_ci#include "pvrusb2-std.h"
1762306a36Sopenharmony_ci#include "pvrusb2-util.h"
1862306a36Sopenharmony_ci#include "pvrusb2-hdw.h"
1962306a36Sopenharmony_ci#include "pvrusb2-i2c-core.h"
2062306a36Sopenharmony_ci#include "pvrusb2-eeprom.h"
2162306a36Sopenharmony_ci#include "pvrusb2-hdw-internal.h"
2262306a36Sopenharmony_ci#include "pvrusb2-encoder.h"
2362306a36Sopenharmony_ci#include "pvrusb2-debug.h"
2462306a36Sopenharmony_ci#include "pvrusb2-fx2-cmd.h"
2562306a36Sopenharmony_ci#include "pvrusb2-wm8775.h"
2662306a36Sopenharmony_ci#include "pvrusb2-video-v4l.h"
2762306a36Sopenharmony_ci#include "pvrusb2-cx2584x-v4l.h"
2862306a36Sopenharmony_ci#include "pvrusb2-cs53l32a.h"
2962306a36Sopenharmony_ci#include "pvrusb2-audio.h"
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define TV_MIN_FREQ     55250000L
3262306a36Sopenharmony_ci#define TV_MAX_FREQ    850000000L
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/* This defines a minimum interval that the decoder must remain quiet
3562306a36Sopenharmony_ci   before we are allowed to start it running. */
3662306a36Sopenharmony_ci#define TIME_MSEC_DECODER_WAIT 50
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/* This defines a minimum interval that the decoder must be allowed to run
3962306a36Sopenharmony_ci   before we can safely begin using its streaming output. */
4062306a36Sopenharmony_ci#define TIME_MSEC_DECODER_STABILIZATION_WAIT 300
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/* This defines a minimum interval that the encoder must remain quiet
4362306a36Sopenharmony_ci   before we are allowed to configure it. */
4462306a36Sopenharmony_ci#define TIME_MSEC_ENCODER_WAIT 50
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/* This defines the minimum interval that the encoder must successfully run
4762306a36Sopenharmony_ci   before we consider that the encoder has run at least once since its
4862306a36Sopenharmony_ci   firmware has been loaded.  This measurement is in important for cases
4962306a36Sopenharmony_ci   where we can't do something until we know that the encoder has been run
5062306a36Sopenharmony_ci   at least once. */
5162306a36Sopenharmony_ci#define TIME_MSEC_ENCODER_OK 250
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic struct pvr2_hdw *unit_pointers[PVR_NUM] = {[ 0 ... PVR_NUM-1 ] = NULL};
5462306a36Sopenharmony_cistatic DEFINE_MUTEX(pvr2_unit_mtx);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic int ctlchg;
5762306a36Sopenharmony_cistatic int procreload;
5862306a36Sopenharmony_cistatic int tuner[PVR_NUM] = { [0 ... PVR_NUM-1] = -1 };
5962306a36Sopenharmony_cistatic int tolerance[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 };
6062306a36Sopenharmony_cistatic int video_std[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 };
6162306a36Sopenharmony_cistatic int init_pause_msec;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cimodule_param(ctlchg, int, S_IRUGO|S_IWUSR);
6462306a36Sopenharmony_ciMODULE_PARM_DESC(ctlchg, "0=optimize ctl change 1=always accept new ctl value");
6562306a36Sopenharmony_cimodule_param(init_pause_msec, int, S_IRUGO|S_IWUSR);
6662306a36Sopenharmony_ciMODULE_PARM_DESC(init_pause_msec, "hardware initialization settling delay");
6762306a36Sopenharmony_cimodule_param(procreload, int, S_IRUGO|S_IWUSR);
6862306a36Sopenharmony_ciMODULE_PARM_DESC(procreload,
6962306a36Sopenharmony_ci		 "Attempt init failure recovery with firmware reload");
7062306a36Sopenharmony_cimodule_param_array(tuner,    int, NULL, 0444);
7162306a36Sopenharmony_ciMODULE_PARM_DESC(tuner,"specify installed tuner type");
7262306a36Sopenharmony_cimodule_param_array(video_std,    int, NULL, 0444);
7362306a36Sopenharmony_ciMODULE_PARM_DESC(video_std,"specify initial video standard");
7462306a36Sopenharmony_cimodule_param_array(tolerance,    int, NULL, 0444);
7562306a36Sopenharmony_ciMODULE_PARM_DESC(tolerance,"specify stream error tolerance");
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci/* US Broadcast channel 3 (61.25 MHz), to help with testing */
7862306a36Sopenharmony_cistatic int default_tv_freq    = 61250000L;
7962306a36Sopenharmony_ci/* 104.3 MHz, a usable FM station for my area */
8062306a36Sopenharmony_cistatic int default_radio_freq = 104300000L;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cimodule_param_named(tv_freq, default_tv_freq, int, 0444);
8362306a36Sopenharmony_ciMODULE_PARM_DESC(tv_freq, "specify initial television frequency");
8462306a36Sopenharmony_cimodule_param_named(radio_freq, default_radio_freq, int, 0444);
8562306a36Sopenharmony_ciMODULE_PARM_DESC(radio_freq, "specify initial radio frequency");
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci#define PVR2_CTL_WRITE_ENDPOINT  0x01
8862306a36Sopenharmony_ci#define PVR2_CTL_READ_ENDPOINT   0x81
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci#define PVR2_GPIO_IN 0x9008
9162306a36Sopenharmony_ci#define PVR2_GPIO_OUT 0x900c
9262306a36Sopenharmony_ci#define PVR2_GPIO_DIR 0x9020
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci#define trace_firmware(...) pvr2_trace(PVR2_TRACE_FIRMWARE,__VA_ARGS__)
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci#define PVR2_FIRMWARE_ENDPOINT   0x02
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci/* size of a firmware chunk */
9962306a36Sopenharmony_ci#define FIRMWARE_CHUNK_SIZE 0x2000
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_citypedef void (*pvr2_subdev_update_func)(struct pvr2_hdw *,
10262306a36Sopenharmony_ci					struct v4l2_subdev *);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic const pvr2_subdev_update_func pvr2_module_update_functions[] = {
10562306a36Sopenharmony_ci	[PVR2_CLIENT_ID_WM8775] = pvr2_wm8775_subdev_update,
10662306a36Sopenharmony_ci	[PVR2_CLIENT_ID_SAA7115] = pvr2_saa7115_subdev_update,
10762306a36Sopenharmony_ci	[PVR2_CLIENT_ID_MSP3400] = pvr2_msp3400_subdev_update,
10862306a36Sopenharmony_ci	[PVR2_CLIENT_ID_CX25840] = pvr2_cx25840_subdev_update,
10962306a36Sopenharmony_ci	[PVR2_CLIENT_ID_CS53L32A] = pvr2_cs53l32a_subdev_update,
11062306a36Sopenharmony_ci};
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic const char *module_names[] = {
11362306a36Sopenharmony_ci	[PVR2_CLIENT_ID_MSP3400] = "msp3400",
11462306a36Sopenharmony_ci	[PVR2_CLIENT_ID_CX25840] = "cx25840",
11562306a36Sopenharmony_ci	[PVR2_CLIENT_ID_SAA7115] = "saa7115",
11662306a36Sopenharmony_ci	[PVR2_CLIENT_ID_TUNER] = "tuner",
11762306a36Sopenharmony_ci	[PVR2_CLIENT_ID_DEMOD] = "tuner",
11862306a36Sopenharmony_ci	[PVR2_CLIENT_ID_CS53L32A] = "cs53l32a",
11962306a36Sopenharmony_ci	[PVR2_CLIENT_ID_WM8775] = "wm8775",
12062306a36Sopenharmony_ci};
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic const unsigned char *module_i2c_addresses[] = {
12462306a36Sopenharmony_ci	[PVR2_CLIENT_ID_TUNER] = "\x60\x61\x62\x63",
12562306a36Sopenharmony_ci	[PVR2_CLIENT_ID_DEMOD] = "\x43",
12662306a36Sopenharmony_ci	[PVR2_CLIENT_ID_MSP3400] = "\x40",
12762306a36Sopenharmony_ci	[PVR2_CLIENT_ID_SAA7115] = "\x21",
12862306a36Sopenharmony_ci	[PVR2_CLIENT_ID_WM8775] = "\x1b",
12962306a36Sopenharmony_ci	[PVR2_CLIENT_ID_CX25840] = "\x44",
13062306a36Sopenharmony_ci	[PVR2_CLIENT_ID_CS53L32A] = "\x11",
13162306a36Sopenharmony_ci};
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic const char *ir_scheme_names[] = {
13562306a36Sopenharmony_ci	[PVR2_IR_SCHEME_NONE] = "none",
13662306a36Sopenharmony_ci	[PVR2_IR_SCHEME_29XXX] = "29xxx",
13762306a36Sopenharmony_ci	[PVR2_IR_SCHEME_24XXX] = "24xxx (29xxx emulation)",
13862306a36Sopenharmony_ci	[PVR2_IR_SCHEME_24XXX_MCE] = "24xxx (MCE device)",
13962306a36Sopenharmony_ci	[PVR2_IR_SCHEME_ZILOG] = "Zilog",
14062306a36Sopenharmony_ci};
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/* Define the list of additional controls we'll dynamically construct based
14462306a36Sopenharmony_ci   on query of the cx2341x module. */
14562306a36Sopenharmony_cistruct pvr2_mpeg_ids {
14662306a36Sopenharmony_ci	const char *strid;
14762306a36Sopenharmony_ci	int id;
14862306a36Sopenharmony_ci};
14962306a36Sopenharmony_cistatic const struct pvr2_mpeg_ids mpeg_ids[] = {
15062306a36Sopenharmony_ci	{
15162306a36Sopenharmony_ci		.strid = "audio_layer",
15262306a36Sopenharmony_ci		.id = V4L2_CID_MPEG_AUDIO_ENCODING,
15362306a36Sopenharmony_ci	},{
15462306a36Sopenharmony_ci		.strid = "audio_bitrate",
15562306a36Sopenharmony_ci		.id = V4L2_CID_MPEG_AUDIO_L2_BITRATE,
15662306a36Sopenharmony_ci	},{
15762306a36Sopenharmony_ci		/* Already using audio_mode elsewhere :-( */
15862306a36Sopenharmony_ci		.strid = "mpeg_audio_mode",
15962306a36Sopenharmony_ci		.id = V4L2_CID_MPEG_AUDIO_MODE,
16062306a36Sopenharmony_ci	},{
16162306a36Sopenharmony_ci		.strid = "mpeg_audio_mode_extension",
16262306a36Sopenharmony_ci		.id = V4L2_CID_MPEG_AUDIO_MODE_EXTENSION,
16362306a36Sopenharmony_ci	},{
16462306a36Sopenharmony_ci		.strid = "audio_emphasis",
16562306a36Sopenharmony_ci		.id = V4L2_CID_MPEG_AUDIO_EMPHASIS,
16662306a36Sopenharmony_ci	},{
16762306a36Sopenharmony_ci		.strid = "audio_crc",
16862306a36Sopenharmony_ci		.id = V4L2_CID_MPEG_AUDIO_CRC,
16962306a36Sopenharmony_ci	},{
17062306a36Sopenharmony_ci		.strid = "video_aspect",
17162306a36Sopenharmony_ci		.id = V4L2_CID_MPEG_VIDEO_ASPECT,
17262306a36Sopenharmony_ci	},{
17362306a36Sopenharmony_ci		.strid = "video_b_frames",
17462306a36Sopenharmony_ci		.id = V4L2_CID_MPEG_VIDEO_B_FRAMES,
17562306a36Sopenharmony_ci	},{
17662306a36Sopenharmony_ci		.strid = "video_gop_size",
17762306a36Sopenharmony_ci		.id = V4L2_CID_MPEG_VIDEO_GOP_SIZE,
17862306a36Sopenharmony_ci	},{
17962306a36Sopenharmony_ci		.strid = "video_gop_closure",
18062306a36Sopenharmony_ci		.id = V4L2_CID_MPEG_VIDEO_GOP_CLOSURE,
18162306a36Sopenharmony_ci	},{
18262306a36Sopenharmony_ci		.strid = "video_bitrate_mode",
18362306a36Sopenharmony_ci		.id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
18462306a36Sopenharmony_ci	},{
18562306a36Sopenharmony_ci		.strid = "video_bitrate",
18662306a36Sopenharmony_ci		.id = V4L2_CID_MPEG_VIDEO_BITRATE,
18762306a36Sopenharmony_ci	},{
18862306a36Sopenharmony_ci		.strid = "video_bitrate_peak",
18962306a36Sopenharmony_ci		.id = V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
19062306a36Sopenharmony_ci	},{
19162306a36Sopenharmony_ci		.strid = "video_temporal_decimation",
19262306a36Sopenharmony_ci		.id = V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION,
19362306a36Sopenharmony_ci	},{
19462306a36Sopenharmony_ci		.strid = "stream_type",
19562306a36Sopenharmony_ci		.id = V4L2_CID_MPEG_STREAM_TYPE,
19662306a36Sopenharmony_ci	},{
19762306a36Sopenharmony_ci		.strid = "video_spatial_filter_mode",
19862306a36Sopenharmony_ci		.id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE,
19962306a36Sopenharmony_ci	},{
20062306a36Sopenharmony_ci		.strid = "video_spatial_filter",
20162306a36Sopenharmony_ci		.id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER,
20262306a36Sopenharmony_ci	},{
20362306a36Sopenharmony_ci		.strid = "video_luma_spatial_filter_type",
20462306a36Sopenharmony_ci		.id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE,
20562306a36Sopenharmony_ci	},{
20662306a36Sopenharmony_ci		.strid = "video_chroma_spatial_filter_type",
20762306a36Sopenharmony_ci		.id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE,
20862306a36Sopenharmony_ci	},{
20962306a36Sopenharmony_ci		.strid = "video_temporal_filter_mode",
21062306a36Sopenharmony_ci		.id = V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE,
21162306a36Sopenharmony_ci	},{
21262306a36Sopenharmony_ci		.strid = "video_temporal_filter",
21362306a36Sopenharmony_ci		.id = V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER,
21462306a36Sopenharmony_ci	},{
21562306a36Sopenharmony_ci		.strid = "video_median_filter_type",
21662306a36Sopenharmony_ci		.id = V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE,
21762306a36Sopenharmony_ci	},{
21862306a36Sopenharmony_ci		.strid = "video_luma_median_filter_top",
21962306a36Sopenharmony_ci		.id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP,
22062306a36Sopenharmony_ci	},{
22162306a36Sopenharmony_ci		.strid = "video_luma_median_filter_bottom",
22262306a36Sopenharmony_ci		.id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM,
22362306a36Sopenharmony_ci	},{
22462306a36Sopenharmony_ci		.strid = "video_chroma_median_filter_top",
22562306a36Sopenharmony_ci		.id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP,
22662306a36Sopenharmony_ci	},{
22762306a36Sopenharmony_ci		.strid = "video_chroma_median_filter_bottom",
22862306a36Sopenharmony_ci		.id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM,
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci};
23162306a36Sopenharmony_ci#define MPEGDEF_COUNT ARRAY_SIZE(mpeg_ids)
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_cistatic const char *control_values_srate[] = {
23562306a36Sopenharmony_ci	[V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100]   = "44.1 kHz",
23662306a36Sopenharmony_ci	[V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000]   = "48 kHz",
23762306a36Sopenharmony_ci	[V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000]   = "32 kHz",
23862306a36Sopenharmony_ci};
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic const char *control_values_input[] = {
24362306a36Sopenharmony_ci	[PVR2_CVAL_INPUT_TV]        = "television",  /*xawtv needs this name*/
24462306a36Sopenharmony_ci	[PVR2_CVAL_INPUT_DTV]       = "dtv",
24562306a36Sopenharmony_ci	[PVR2_CVAL_INPUT_RADIO]     = "radio",
24662306a36Sopenharmony_ci	[PVR2_CVAL_INPUT_SVIDEO]    = "s-video",
24762306a36Sopenharmony_ci	[PVR2_CVAL_INPUT_COMPOSITE] = "composite",
24862306a36Sopenharmony_ci};
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic const char *control_values_audiomode[] = {
25262306a36Sopenharmony_ci	[V4L2_TUNER_MODE_MONO]   = "Mono",
25362306a36Sopenharmony_ci	[V4L2_TUNER_MODE_STEREO] = "Stereo",
25462306a36Sopenharmony_ci	[V4L2_TUNER_MODE_LANG1]  = "Lang1",
25562306a36Sopenharmony_ci	[V4L2_TUNER_MODE_LANG2]  = "Lang2",
25662306a36Sopenharmony_ci	[V4L2_TUNER_MODE_LANG1_LANG2] = "Lang1+Lang2",
25762306a36Sopenharmony_ci};
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic const char *control_values_hsm[] = {
26162306a36Sopenharmony_ci	[PVR2_CVAL_HSM_FAIL] = "Fail",
26262306a36Sopenharmony_ci	[PVR2_CVAL_HSM_HIGH] = "High",
26362306a36Sopenharmony_ci	[PVR2_CVAL_HSM_FULL] = "Full",
26462306a36Sopenharmony_ci};
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic const char *pvr2_state_names[] = {
26862306a36Sopenharmony_ci	[PVR2_STATE_NONE] =    "none",
26962306a36Sopenharmony_ci	[PVR2_STATE_DEAD] =    "dead",
27062306a36Sopenharmony_ci	[PVR2_STATE_COLD] =    "cold",
27162306a36Sopenharmony_ci	[PVR2_STATE_WARM] =    "warm",
27262306a36Sopenharmony_ci	[PVR2_STATE_ERROR] =   "error",
27362306a36Sopenharmony_ci	[PVR2_STATE_READY] =   "ready",
27462306a36Sopenharmony_ci	[PVR2_STATE_RUN] =     "run",
27562306a36Sopenharmony_ci};
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_cistruct pvr2_fx2cmd_descdef {
27962306a36Sopenharmony_ci	unsigned char id;
28062306a36Sopenharmony_ci	unsigned char *desc;
28162306a36Sopenharmony_ci};
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic const struct pvr2_fx2cmd_descdef pvr2_fx2cmd_desc[] = {
28462306a36Sopenharmony_ci	{FX2CMD_MEM_WRITE_DWORD, "write encoder dword"},
28562306a36Sopenharmony_ci	{FX2CMD_MEM_READ_DWORD, "read encoder dword"},
28662306a36Sopenharmony_ci	{FX2CMD_HCW_ZILOG_RESET, "zilog IR reset control"},
28762306a36Sopenharmony_ci	{FX2CMD_MEM_READ_64BYTES, "read encoder 64bytes"},
28862306a36Sopenharmony_ci	{FX2CMD_REG_WRITE, "write encoder register"},
28962306a36Sopenharmony_ci	{FX2CMD_REG_READ, "read encoder register"},
29062306a36Sopenharmony_ci	{FX2CMD_MEMSEL, "encoder memsel"},
29162306a36Sopenharmony_ci	{FX2CMD_I2C_WRITE, "i2c write"},
29262306a36Sopenharmony_ci	{FX2CMD_I2C_READ, "i2c read"},
29362306a36Sopenharmony_ci	{FX2CMD_GET_USB_SPEED, "get USB speed"},
29462306a36Sopenharmony_ci	{FX2CMD_STREAMING_ON, "stream on"},
29562306a36Sopenharmony_ci	{FX2CMD_STREAMING_OFF, "stream off"},
29662306a36Sopenharmony_ci	{FX2CMD_FWPOST1, "fwpost1"},
29762306a36Sopenharmony_ci	{FX2CMD_POWER_OFF, "power off"},
29862306a36Sopenharmony_ci	{FX2CMD_POWER_ON, "power on"},
29962306a36Sopenharmony_ci	{FX2CMD_DEEP_RESET, "deep reset"},
30062306a36Sopenharmony_ci	{FX2CMD_GET_EEPROM_ADDR, "get rom addr"},
30162306a36Sopenharmony_ci	{FX2CMD_GET_IR_CODE, "get IR code"},
30262306a36Sopenharmony_ci	{FX2CMD_HCW_DEMOD_RESETIN, "hcw demod resetin"},
30362306a36Sopenharmony_ci	{FX2CMD_HCW_DTV_STREAMING_ON, "hcw dtv stream on"},
30462306a36Sopenharmony_ci	{FX2CMD_HCW_DTV_STREAMING_OFF, "hcw dtv stream off"},
30562306a36Sopenharmony_ci	{FX2CMD_ONAIR_DTV_STREAMING_ON, "onair dtv stream on"},
30662306a36Sopenharmony_ci	{FX2CMD_ONAIR_DTV_STREAMING_OFF, "onair dtv stream off"},
30762306a36Sopenharmony_ci	{FX2CMD_ONAIR_DTV_POWER_ON, "onair dtv power on"},
30862306a36Sopenharmony_ci	{FX2CMD_ONAIR_DTV_POWER_OFF, "onair dtv power off"},
30962306a36Sopenharmony_ci	{FX2CMD_HCW_DEMOD_RESET_PIN, "hcw demod reset pin"},
31062306a36Sopenharmony_ci	{FX2CMD_HCW_MAKO_SLEEP_PIN, "hcw mako sleep pin"},
31162306a36Sopenharmony_ci};
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic int pvr2_hdw_set_input(struct pvr2_hdw *hdw,int v);
31562306a36Sopenharmony_cistatic void pvr2_hdw_state_sched(struct pvr2_hdw *);
31662306a36Sopenharmony_cistatic int pvr2_hdw_state_eval(struct pvr2_hdw *);
31762306a36Sopenharmony_cistatic void pvr2_hdw_set_cur_freq(struct pvr2_hdw *,unsigned long);
31862306a36Sopenharmony_cistatic void pvr2_hdw_worker_poll(struct work_struct *work);
31962306a36Sopenharmony_cistatic int pvr2_hdw_wait(struct pvr2_hdw *,int state);
32062306a36Sopenharmony_cistatic int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *);
32162306a36Sopenharmony_cistatic void pvr2_hdw_state_log_state(struct pvr2_hdw *);
32262306a36Sopenharmony_cistatic int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl);
32362306a36Sopenharmony_cistatic int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw);
32462306a36Sopenharmony_cistatic int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw);
32562306a36Sopenharmony_cistatic void pvr2_hdw_quiescent_timeout(struct timer_list *);
32662306a36Sopenharmony_cistatic void pvr2_hdw_decoder_stabilization_timeout(struct timer_list *);
32762306a36Sopenharmony_cistatic void pvr2_hdw_encoder_wait_timeout(struct timer_list *);
32862306a36Sopenharmony_cistatic void pvr2_hdw_encoder_run_timeout(struct timer_list *);
32962306a36Sopenharmony_cistatic int pvr2_issue_simple_cmd(struct pvr2_hdw *,u32);
33062306a36Sopenharmony_cistatic int pvr2_send_request_ex(struct pvr2_hdw *hdw,
33162306a36Sopenharmony_ci				unsigned int timeout,int probe_fl,
33262306a36Sopenharmony_ci				void *write_data,unsigned int write_len,
33362306a36Sopenharmony_ci				void *read_data,unsigned int read_len);
33462306a36Sopenharmony_cistatic int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw);
33562306a36Sopenharmony_cistatic v4l2_std_id pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic void trace_stbit(const char *name,int val)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	pvr2_trace(PVR2_TRACE_STBITS,
34062306a36Sopenharmony_ci		   "State bit %s <-- %s",
34162306a36Sopenharmony_ci		   name,(val ? "true" : "false"));
34262306a36Sopenharmony_ci}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_cistatic int ctrl_channelfreq_get(struct pvr2_ctrl *cptr,int *vp)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	struct pvr2_hdw *hdw = cptr->hdw;
34762306a36Sopenharmony_ci	if ((hdw->freqProgSlot > 0) && (hdw->freqProgSlot <= FREQTABLE_SIZE)) {
34862306a36Sopenharmony_ci		*vp = hdw->freqTable[hdw->freqProgSlot-1];
34962306a36Sopenharmony_ci	} else {
35062306a36Sopenharmony_ci		*vp = 0;
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci	return 0;
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic int ctrl_channelfreq_set(struct pvr2_ctrl *cptr,int m,int v)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	struct pvr2_hdw *hdw = cptr->hdw;
35862306a36Sopenharmony_ci	unsigned int slotId = hdw->freqProgSlot;
35962306a36Sopenharmony_ci	if ((slotId > 0) && (slotId <= FREQTABLE_SIZE)) {
36062306a36Sopenharmony_ci		hdw->freqTable[slotId-1] = v;
36162306a36Sopenharmony_ci		/* Handle side effects correctly - if we're tuned to this
36262306a36Sopenharmony_ci		   slot, then forgot the slot id relation since the stored
36362306a36Sopenharmony_ci		   frequency has been changed. */
36462306a36Sopenharmony_ci		if (hdw->freqSelector) {
36562306a36Sopenharmony_ci			if (hdw->freqSlotRadio == slotId) {
36662306a36Sopenharmony_ci				hdw->freqSlotRadio = 0;
36762306a36Sopenharmony_ci			}
36862306a36Sopenharmony_ci		} else {
36962306a36Sopenharmony_ci			if (hdw->freqSlotTelevision == slotId) {
37062306a36Sopenharmony_ci				hdw->freqSlotTelevision = 0;
37162306a36Sopenharmony_ci			}
37262306a36Sopenharmony_ci		}
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci	return 0;
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_cistatic int ctrl_channelprog_get(struct pvr2_ctrl *cptr,int *vp)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	*vp = cptr->hdw->freqProgSlot;
38062306a36Sopenharmony_ci	return 0;
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cistatic int ctrl_channelprog_set(struct pvr2_ctrl *cptr,int m,int v)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	struct pvr2_hdw *hdw = cptr->hdw;
38662306a36Sopenharmony_ci	if ((v >= 0) && (v <= FREQTABLE_SIZE)) {
38762306a36Sopenharmony_ci		hdw->freqProgSlot = v;
38862306a36Sopenharmony_ci	}
38962306a36Sopenharmony_ci	return 0;
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_cistatic int ctrl_channel_get(struct pvr2_ctrl *cptr,int *vp)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	struct pvr2_hdw *hdw = cptr->hdw;
39562306a36Sopenharmony_ci	*vp = hdw->freqSelector ? hdw->freqSlotRadio : hdw->freqSlotTelevision;
39662306a36Sopenharmony_ci	return 0;
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cistatic int ctrl_channel_set(struct pvr2_ctrl *cptr,int m,int slotId)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	unsigned freq = 0;
40262306a36Sopenharmony_ci	struct pvr2_hdw *hdw = cptr->hdw;
40362306a36Sopenharmony_ci	if ((slotId < 0) || (slotId > FREQTABLE_SIZE)) return 0;
40462306a36Sopenharmony_ci	if (slotId > 0) {
40562306a36Sopenharmony_ci		freq = hdw->freqTable[slotId-1];
40662306a36Sopenharmony_ci		if (!freq) return 0;
40762306a36Sopenharmony_ci		pvr2_hdw_set_cur_freq(hdw,freq);
40862306a36Sopenharmony_ci	}
40962306a36Sopenharmony_ci	if (hdw->freqSelector) {
41062306a36Sopenharmony_ci		hdw->freqSlotRadio = slotId;
41162306a36Sopenharmony_ci	} else {
41262306a36Sopenharmony_ci		hdw->freqSlotTelevision = slotId;
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci	return 0;
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cistatic int ctrl_freq_get(struct pvr2_ctrl *cptr,int *vp)
41862306a36Sopenharmony_ci{
41962306a36Sopenharmony_ci	*vp = pvr2_hdw_get_cur_freq(cptr->hdw);
42062306a36Sopenharmony_ci	return 0;
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cistatic int ctrl_freq_is_dirty(struct pvr2_ctrl *cptr)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	return cptr->hdw->freqDirty != 0;
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_cistatic void ctrl_freq_clear_dirty(struct pvr2_ctrl *cptr)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	cptr->hdw->freqDirty = 0;
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_cistatic int ctrl_freq_set(struct pvr2_ctrl *cptr,int m,int v)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	pvr2_hdw_set_cur_freq(cptr->hdw,v);
43662306a36Sopenharmony_ci	return 0;
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic int ctrl_cropl_min_get(struct pvr2_ctrl *cptr, int *left)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
44262306a36Sopenharmony_ci	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
44362306a36Sopenharmony_ci	if (stat != 0) {
44462306a36Sopenharmony_ci		return stat;
44562306a36Sopenharmony_ci	}
44662306a36Sopenharmony_ci	*left = cap->bounds.left;
44762306a36Sopenharmony_ci	return 0;
44862306a36Sopenharmony_ci}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_cistatic int ctrl_cropl_max_get(struct pvr2_ctrl *cptr, int *left)
45162306a36Sopenharmony_ci{
45262306a36Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
45362306a36Sopenharmony_ci	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
45462306a36Sopenharmony_ci	if (stat != 0) {
45562306a36Sopenharmony_ci		return stat;
45662306a36Sopenharmony_ci	}
45762306a36Sopenharmony_ci	*left = cap->bounds.left;
45862306a36Sopenharmony_ci	if (cap->bounds.width > cptr->hdw->cropw_val) {
45962306a36Sopenharmony_ci		*left += cap->bounds.width - cptr->hdw->cropw_val;
46062306a36Sopenharmony_ci	}
46162306a36Sopenharmony_ci	return 0;
46262306a36Sopenharmony_ci}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_cistatic int ctrl_cropt_min_get(struct pvr2_ctrl *cptr, int *top)
46562306a36Sopenharmony_ci{
46662306a36Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
46762306a36Sopenharmony_ci	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
46862306a36Sopenharmony_ci	if (stat != 0) {
46962306a36Sopenharmony_ci		return stat;
47062306a36Sopenharmony_ci	}
47162306a36Sopenharmony_ci	*top = cap->bounds.top;
47262306a36Sopenharmony_ci	return 0;
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_cistatic int ctrl_cropt_max_get(struct pvr2_ctrl *cptr, int *top)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
47862306a36Sopenharmony_ci	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
47962306a36Sopenharmony_ci	if (stat != 0) {
48062306a36Sopenharmony_ci		return stat;
48162306a36Sopenharmony_ci	}
48262306a36Sopenharmony_ci	*top = cap->bounds.top;
48362306a36Sopenharmony_ci	if (cap->bounds.height > cptr->hdw->croph_val) {
48462306a36Sopenharmony_ci		*top += cap->bounds.height - cptr->hdw->croph_val;
48562306a36Sopenharmony_ci	}
48662306a36Sopenharmony_ci	return 0;
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic int ctrl_cropw_max_get(struct pvr2_ctrl *cptr, int *width)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
49262306a36Sopenharmony_ci	int stat, bleftend, cleft;
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	stat = pvr2_hdw_check_cropcap(cptr->hdw);
49562306a36Sopenharmony_ci	if (stat != 0) {
49662306a36Sopenharmony_ci		return stat;
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci	bleftend = cap->bounds.left+cap->bounds.width;
49962306a36Sopenharmony_ci	cleft = cptr->hdw->cropl_val;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	*width = cleft < bleftend ? bleftend-cleft : 0;
50262306a36Sopenharmony_ci	return 0;
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_cistatic int ctrl_croph_max_get(struct pvr2_ctrl *cptr, int *height)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
50862306a36Sopenharmony_ci	int stat, btopend, ctop;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	stat = pvr2_hdw_check_cropcap(cptr->hdw);
51162306a36Sopenharmony_ci	if (stat != 0) {
51262306a36Sopenharmony_ci		return stat;
51362306a36Sopenharmony_ci	}
51462306a36Sopenharmony_ci	btopend = cap->bounds.top+cap->bounds.height;
51562306a36Sopenharmony_ci	ctop = cptr->hdw->cropt_val;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	*height = ctop < btopend ? btopend-ctop : 0;
51862306a36Sopenharmony_ci	return 0;
51962306a36Sopenharmony_ci}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_cistatic int ctrl_get_cropcapbl(struct pvr2_ctrl *cptr, int *val)
52262306a36Sopenharmony_ci{
52362306a36Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
52462306a36Sopenharmony_ci	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
52562306a36Sopenharmony_ci	if (stat != 0) {
52662306a36Sopenharmony_ci		return stat;
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci	*val = cap->bounds.left;
52962306a36Sopenharmony_ci	return 0;
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cistatic int ctrl_get_cropcapbt(struct pvr2_ctrl *cptr, int *val)
53362306a36Sopenharmony_ci{
53462306a36Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
53562306a36Sopenharmony_ci	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
53662306a36Sopenharmony_ci	if (stat != 0) {
53762306a36Sopenharmony_ci		return stat;
53862306a36Sopenharmony_ci	}
53962306a36Sopenharmony_ci	*val = cap->bounds.top;
54062306a36Sopenharmony_ci	return 0;
54162306a36Sopenharmony_ci}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_cistatic int ctrl_get_cropcapbw(struct pvr2_ctrl *cptr, int *val)
54462306a36Sopenharmony_ci{
54562306a36Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
54662306a36Sopenharmony_ci	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
54762306a36Sopenharmony_ci	if (stat != 0) {
54862306a36Sopenharmony_ci		return stat;
54962306a36Sopenharmony_ci	}
55062306a36Sopenharmony_ci	*val = cap->bounds.width;
55162306a36Sopenharmony_ci	return 0;
55262306a36Sopenharmony_ci}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_cistatic int ctrl_get_cropcapbh(struct pvr2_ctrl *cptr, int *val)
55562306a36Sopenharmony_ci{
55662306a36Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
55762306a36Sopenharmony_ci	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
55862306a36Sopenharmony_ci	if (stat != 0) {
55962306a36Sopenharmony_ci		return stat;
56062306a36Sopenharmony_ci	}
56162306a36Sopenharmony_ci	*val = cap->bounds.height;
56262306a36Sopenharmony_ci	return 0;
56362306a36Sopenharmony_ci}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_cistatic int ctrl_get_cropcapdl(struct pvr2_ctrl *cptr, int *val)
56662306a36Sopenharmony_ci{
56762306a36Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
56862306a36Sopenharmony_ci	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
56962306a36Sopenharmony_ci	if (stat != 0) {
57062306a36Sopenharmony_ci		return stat;
57162306a36Sopenharmony_ci	}
57262306a36Sopenharmony_ci	*val = cap->defrect.left;
57362306a36Sopenharmony_ci	return 0;
57462306a36Sopenharmony_ci}
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_cistatic int ctrl_get_cropcapdt(struct pvr2_ctrl *cptr, int *val)
57762306a36Sopenharmony_ci{
57862306a36Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
57962306a36Sopenharmony_ci	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
58062306a36Sopenharmony_ci	if (stat != 0) {
58162306a36Sopenharmony_ci		return stat;
58262306a36Sopenharmony_ci	}
58362306a36Sopenharmony_ci	*val = cap->defrect.top;
58462306a36Sopenharmony_ci	return 0;
58562306a36Sopenharmony_ci}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_cistatic int ctrl_get_cropcapdw(struct pvr2_ctrl *cptr, int *val)
58862306a36Sopenharmony_ci{
58962306a36Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
59062306a36Sopenharmony_ci	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
59162306a36Sopenharmony_ci	if (stat != 0) {
59262306a36Sopenharmony_ci		return stat;
59362306a36Sopenharmony_ci	}
59462306a36Sopenharmony_ci	*val = cap->defrect.width;
59562306a36Sopenharmony_ci	return 0;
59662306a36Sopenharmony_ci}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_cistatic int ctrl_get_cropcapdh(struct pvr2_ctrl *cptr, int *val)
59962306a36Sopenharmony_ci{
60062306a36Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
60162306a36Sopenharmony_ci	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
60262306a36Sopenharmony_ci	if (stat != 0) {
60362306a36Sopenharmony_ci		return stat;
60462306a36Sopenharmony_ci	}
60562306a36Sopenharmony_ci	*val = cap->defrect.height;
60662306a36Sopenharmony_ci	return 0;
60762306a36Sopenharmony_ci}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_cistatic int ctrl_get_cropcappan(struct pvr2_ctrl *cptr, int *val)
61062306a36Sopenharmony_ci{
61162306a36Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
61262306a36Sopenharmony_ci	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
61362306a36Sopenharmony_ci	if (stat != 0) {
61462306a36Sopenharmony_ci		return stat;
61562306a36Sopenharmony_ci	}
61662306a36Sopenharmony_ci	*val = cap->pixelaspect.numerator;
61762306a36Sopenharmony_ci	return 0;
61862306a36Sopenharmony_ci}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_cistatic int ctrl_get_cropcappad(struct pvr2_ctrl *cptr, int *val)
62162306a36Sopenharmony_ci{
62262306a36Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
62362306a36Sopenharmony_ci	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
62462306a36Sopenharmony_ci	if (stat != 0) {
62562306a36Sopenharmony_ci		return stat;
62662306a36Sopenharmony_ci	}
62762306a36Sopenharmony_ci	*val = cap->pixelaspect.denominator;
62862306a36Sopenharmony_ci	return 0;
62962306a36Sopenharmony_ci}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_cistatic int ctrl_vres_max_get(struct pvr2_ctrl *cptr,int *vp)
63262306a36Sopenharmony_ci{
63362306a36Sopenharmony_ci	/* Actual maximum depends on the video standard in effect. */
63462306a36Sopenharmony_ci	if (cptr->hdw->std_mask_cur & V4L2_STD_525_60) {
63562306a36Sopenharmony_ci		*vp = 480;
63662306a36Sopenharmony_ci	} else {
63762306a36Sopenharmony_ci		*vp = 576;
63862306a36Sopenharmony_ci	}
63962306a36Sopenharmony_ci	return 0;
64062306a36Sopenharmony_ci}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_cistatic int ctrl_vres_min_get(struct pvr2_ctrl *cptr,int *vp)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	/* Actual minimum depends on device digitizer type. */
64562306a36Sopenharmony_ci	if (cptr->hdw->hdw_desc->flag_has_cx25840) {
64662306a36Sopenharmony_ci		*vp = 75;
64762306a36Sopenharmony_ci	} else {
64862306a36Sopenharmony_ci		*vp = 17;
64962306a36Sopenharmony_ci	}
65062306a36Sopenharmony_ci	return 0;
65162306a36Sopenharmony_ci}
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_cistatic int ctrl_get_input(struct pvr2_ctrl *cptr,int *vp)
65462306a36Sopenharmony_ci{
65562306a36Sopenharmony_ci	*vp = cptr->hdw->input_val;
65662306a36Sopenharmony_ci	return 0;
65762306a36Sopenharmony_ci}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_cistatic int ctrl_check_input(struct pvr2_ctrl *cptr,int v)
66062306a36Sopenharmony_ci{
66162306a36Sopenharmony_ci	if (v < 0 || v > PVR2_CVAL_INPUT_MAX)
66262306a36Sopenharmony_ci		return 0;
66362306a36Sopenharmony_ci	return ((1UL << v) & cptr->hdw->input_allowed_mask) != 0;
66462306a36Sopenharmony_ci}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_cistatic int ctrl_set_input(struct pvr2_ctrl *cptr,int m,int v)
66762306a36Sopenharmony_ci{
66862306a36Sopenharmony_ci	return pvr2_hdw_set_input(cptr->hdw,v);
66962306a36Sopenharmony_ci}
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_cistatic int ctrl_isdirty_input(struct pvr2_ctrl *cptr)
67262306a36Sopenharmony_ci{
67362306a36Sopenharmony_ci	return cptr->hdw->input_dirty != 0;
67462306a36Sopenharmony_ci}
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_cistatic void ctrl_cleardirty_input(struct pvr2_ctrl *cptr)
67762306a36Sopenharmony_ci{
67862306a36Sopenharmony_ci	cptr->hdw->input_dirty = 0;
67962306a36Sopenharmony_ci}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_cistatic int ctrl_freq_max_get(struct pvr2_ctrl *cptr, int *vp)
68362306a36Sopenharmony_ci{
68462306a36Sopenharmony_ci	unsigned long fv;
68562306a36Sopenharmony_ci	struct pvr2_hdw *hdw = cptr->hdw;
68662306a36Sopenharmony_ci	if (hdw->tuner_signal_stale) {
68762306a36Sopenharmony_ci		pvr2_hdw_status_poll(hdw);
68862306a36Sopenharmony_ci	}
68962306a36Sopenharmony_ci	fv = hdw->tuner_signal_info.rangehigh;
69062306a36Sopenharmony_ci	if (!fv) {
69162306a36Sopenharmony_ci		/* Safety fallback */
69262306a36Sopenharmony_ci		*vp = TV_MAX_FREQ;
69362306a36Sopenharmony_ci		return 0;
69462306a36Sopenharmony_ci	}
69562306a36Sopenharmony_ci	if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) {
69662306a36Sopenharmony_ci		fv = (fv * 125) / 2;
69762306a36Sopenharmony_ci	} else {
69862306a36Sopenharmony_ci		fv = fv * 62500;
69962306a36Sopenharmony_ci	}
70062306a36Sopenharmony_ci	*vp = fv;
70162306a36Sopenharmony_ci	return 0;
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_cistatic int ctrl_freq_min_get(struct pvr2_ctrl *cptr, int *vp)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci	unsigned long fv;
70762306a36Sopenharmony_ci	struct pvr2_hdw *hdw = cptr->hdw;
70862306a36Sopenharmony_ci	if (hdw->tuner_signal_stale) {
70962306a36Sopenharmony_ci		pvr2_hdw_status_poll(hdw);
71062306a36Sopenharmony_ci	}
71162306a36Sopenharmony_ci	fv = hdw->tuner_signal_info.rangelow;
71262306a36Sopenharmony_ci	if (!fv) {
71362306a36Sopenharmony_ci		/* Safety fallback */
71462306a36Sopenharmony_ci		*vp = TV_MIN_FREQ;
71562306a36Sopenharmony_ci		return 0;
71662306a36Sopenharmony_ci	}
71762306a36Sopenharmony_ci	if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) {
71862306a36Sopenharmony_ci		fv = (fv * 125) / 2;
71962306a36Sopenharmony_ci	} else {
72062306a36Sopenharmony_ci		fv = fv * 62500;
72162306a36Sopenharmony_ci	}
72262306a36Sopenharmony_ci	*vp = fv;
72362306a36Sopenharmony_ci	return 0;
72462306a36Sopenharmony_ci}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_cistatic int ctrl_cx2341x_is_dirty(struct pvr2_ctrl *cptr)
72762306a36Sopenharmony_ci{
72862306a36Sopenharmony_ci	return cptr->hdw->enc_stale != 0;
72962306a36Sopenharmony_ci}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_cistatic void ctrl_cx2341x_clear_dirty(struct pvr2_ctrl *cptr)
73262306a36Sopenharmony_ci{
73362306a36Sopenharmony_ci	cptr->hdw->enc_stale = 0;
73462306a36Sopenharmony_ci	cptr->hdw->enc_unsafe_stale = 0;
73562306a36Sopenharmony_ci}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_cistatic int ctrl_cx2341x_get(struct pvr2_ctrl *cptr,int *vp)
73862306a36Sopenharmony_ci{
73962306a36Sopenharmony_ci	int ret;
74062306a36Sopenharmony_ci	struct v4l2_ext_controls cs;
74162306a36Sopenharmony_ci	struct v4l2_ext_control c1;
74262306a36Sopenharmony_ci	memset(&cs,0,sizeof(cs));
74362306a36Sopenharmony_ci	memset(&c1,0,sizeof(c1));
74462306a36Sopenharmony_ci	cs.controls = &c1;
74562306a36Sopenharmony_ci	cs.count = 1;
74662306a36Sopenharmony_ci	c1.id = cptr->info->v4l_id;
74762306a36Sopenharmony_ci	ret = cx2341x_ext_ctrls(&cptr->hdw->enc_ctl_state, 0, &cs,
74862306a36Sopenharmony_ci				VIDIOC_G_EXT_CTRLS);
74962306a36Sopenharmony_ci	if (ret) return ret;
75062306a36Sopenharmony_ci	*vp = c1.value;
75162306a36Sopenharmony_ci	return 0;
75262306a36Sopenharmony_ci}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_cistatic int ctrl_cx2341x_set(struct pvr2_ctrl *cptr,int m,int v)
75562306a36Sopenharmony_ci{
75662306a36Sopenharmony_ci	int ret;
75762306a36Sopenharmony_ci	struct pvr2_hdw *hdw = cptr->hdw;
75862306a36Sopenharmony_ci	struct v4l2_ext_controls cs;
75962306a36Sopenharmony_ci	struct v4l2_ext_control c1;
76062306a36Sopenharmony_ci	memset(&cs,0,sizeof(cs));
76162306a36Sopenharmony_ci	memset(&c1,0,sizeof(c1));
76262306a36Sopenharmony_ci	cs.controls = &c1;
76362306a36Sopenharmony_ci	cs.count = 1;
76462306a36Sopenharmony_ci	c1.id = cptr->info->v4l_id;
76562306a36Sopenharmony_ci	c1.value = v;
76662306a36Sopenharmony_ci	ret = cx2341x_ext_ctrls(&hdw->enc_ctl_state,
76762306a36Sopenharmony_ci				hdw->state_encoder_run, &cs,
76862306a36Sopenharmony_ci				VIDIOC_S_EXT_CTRLS);
76962306a36Sopenharmony_ci	if (ret == -EBUSY) {
77062306a36Sopenharmony_ci		/* Oops.  cx2341x is telling us it's not safe to change
77162306a36Sopenharmony_ci		   this control while we're capturing.  Make a note of this
77262306a36Sopenharmony_ci		   fact so that the pipeline will be stopped the next time
77362306a36Sopenharmony_ci		   controls are committed.  Then go on ahead and store this
77462306a36Sopenharmony_ci		   change anyway. */
77562306a36Sopenharmony_ci		ret = cx2341x_ext_ctrls(&hdw->enc_ctl_state,
77662306a36Sopenharmony_ci					0, &cs,
77762306a36Sopenharmony_ci					VIDIOC_S_EXT_CTRLS);
77862306a36Sopenharmony_ci		if (!ret) hdw->enc_unsafe_stale = !0;
77962306a36Sopenharmony_ci	}
78062306a36Sopenharmony_ci	if (ret) return ret;
78162306a36Sopenharmony_ci	hdw->enc_stale = !0;
78262306a36Sopenharmony_ci	return 0;
78362306a36Sopenharmony_ci}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_cistatic unsigned int ctrl_cx2341x_getv4lflags(struct pvr2_ctrl *cptr)
78662306a36Sopenharmony_ci{
78762306a36Sopenharmony_ci	struct v4l2_queryctrl qctrl = {};
78862306a36Sopenharmony_ci	struct pvr2_ctl_info *info;
78962306a36Sopenharmony_ci	qctrl.id = cptr->info->v4l_id;
79062306a36Sopenharmony_ci	cx2341x_ctrl_query(&cptr->hdw->enc_ctl_state,&qctrl);
79162306a36Sopenharmony_ci	/* Strip out the const so we can adjust a function pointer.  It's
79262306a36Sopenharmony_ci	   OK to do this here because we know this is a dynamically created
79362306a36Sopenharmony_ci	   control, so the underlying storage for the info pointer is (a)
79462306a36Sopenharmony_ci	   private to us, and (b) not in read-only storage.  Either we do
79562306a36Sopenharmony_ci	   this or we significantly complicate the underlying control
79662306a36Sopenharmony_ci	   implementation. */
79762306a36Sopenharmony_ci	info = (struct pvr2_ctl_info *)(cptr->info);
79862306a36Sopenharmony_ci	if (qctrl.flags & V4L2_CTRL_FLAG_READ_ONLY) {
79962306a36Sopenharmony_ci		if (info->set_value) {
80062306a36Sopenharmony_ci			info->set_value = NULL;
80162306a36Sopenharmony_ci		}
80262306a36Sopenharmony_ci	} else {
80362306a36Sopenharmony_ci		if (!(info->set_value)) {
80462306a36Sopenharmony_ci			info->set_value = ctrl_cx2341x_set;
80562306a36Sopenharmony_ci		}
80662306a36Sopenharmony_ci	}
80762306a36Sopenharmony_ci	return qctrl.flags;
80862306a36Sopenharmony_ci}
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_cistatic int ctrl_streamingenabled_get(struct pvr2_ctrl *cptr,int *vp)
81162306a36Sopenharmony_ci{
81262306a36Sopenharmony_ci	*vp = cptr->hdw->state_pipeline_req;
81362306a36Sopenharmony_ci	return 0;
81462306a36Sopenharmony_ci}
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_cistatic int ctrl_masterstate_get(struct pvr2_ctrl *cptr,int *vp)
81762306a36Sopenharmony_ci{
81862306a36Sopenharmony_ci	*vp = cptr->hdw->master_state;
81962306a36Sopenharmony_ci	return 0;
82062306a36Sopenharmony_ci}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_cistatic int ctrl_hsm_get(struct pvr2_ctrl *cptr,int *vp)
82362306a36Sopenharmony_ci{
82462306a36Sopenharmony_ci	int result = pvr2_hdw_is_hsm(cptr->hdw);
82562306a36Sopenharmony_ci	*vp = PVR2_CVAL_HSM_FULL;
82662306a36Sopenharmony_ci	if (result < 0) *vp = PVR2_CVAL_HSM_FAIL;
82762306a36Sopenharmony_ci	if (result) *vp = PVR2_CVAL_HSM_HIGH;
82862306a36Sopenharmony_ci	return 0;
82962306a36Sopenharmony_ci}
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_cistatic int ctrl_stddetect_get(struct pvr2_ctrl *cptr, int *vp)
83262306a36Sopenharmony_ci{
83362306a36Sopenharmony_ci	*vp = pvr2_hdw_get_detected_std(cptr->hdw);
83462306a36Sopenharmony_ci	return 0;
83562306a36Sopenharmony_ci}
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_cistatic int ctrl_stdavail_get(struct pvr2_ctrl *cptr,int *vp)
83862306a36Sopenharmony_ci{
83962306a36Sopenharmony_ci	*vp = cptr->hdw->std_mask_avail;
84062306a36Sopenharmony_ci	return 0;
84162306a36Sopenharmony_ci}
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_cistatic int ctrl_stdavail_set(struct pvr2_ctrl *cptr,int m,int v)
84462306a36Sopenharmony_ci{
84562306a36Sopenharmony_ci	struct pvr2_hdw *hdw = cptr->hdw;
84662306a36Sopenharmony_ci	v4l2_std_id ns;
84762306a36Sopenharmony_ci	ns = hdw->std_mask_avail;
84862306a36Sopenharmony_ci	ns = (ns & ~m) | (v & m);
84962306a36Sopenharmony_ci	if (ns == hdw->std_mask_avail) return 0;
85062306a36Sopenharmony_ci	hdw->std_mask_avail = ns;
85162306a36Sopenharmony_ci	hdw->std_info_cur.def.type_bitmask.valid_bits = hdw->std_mask_avail;
85262306a36Sopenharmony_ci	return 0;
85362306a36Sopenharmony_ci}
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_cistatic int ctrl_std_val_to_sym(struct pvr2_ctrl *cptr,int msk,int val,
85662306a36Sopenharmony_ci			       char *bufPtr,unsigned int bufSize,
85762306a36Sopenharmony_ci			       unsigned int *len)
85862306a36Sopenharmony_ci{
85962306a36Sopenharmony_ci	*len = pvr2_std_id_to_str(bufPtr,bufSize,msk & val);
86062306a36Sopenharmony_ci	return 0;
86162306a36Sopenharmony_ci}
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_cistatic int ctrl_std_sym_to_val(struct pvr2_ctrl *cptr,
86462306a36Sopenharmony_ci			       const char *bufPtr,unsigned int bufSize,
86562306a36Sopenharmony_ci			       int *mskp,int *valp)
86662306a36Sopenharmony_ci{
86762306a36Sopenharmony_ci	v4l2_std_id id;
86862306a36Sopenharmony_ci	if (!pvr2_std_str_to_id(&id, bufPtr, bufSize))
86962306a36Sopenharmony_ci		return -EINVAL;
87062306a36Sopenharmony_ci	if (mskp) *mskp = id;
87162306a36Sopenharmony_ci	if (valp) *valp = id;
87262306a36Sopenharmony_ci	return 0;
87362306a36Sopenharmony_ci}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_cistatic int ctrl_stdcur_get(struct pvr2_ctrl *cptr,int *vp)
87662306a36Sopenharmony_ci{
87762306a36Sopenharmony_ci	*vp = cptr->hdw->std_mask_cur;
87862306a36Sopenharmony_ci	return 0;
87962306a36Sopenharmony_ci}
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_cistatic int ctrl_stdcur_set(struct pvr2_ctrl *cptr,int m,int v)
88262306a36Sopenharmony_ci{
88362306a36Sopenharmony_ci	struct pvr2_hdw *hdw = cptr->hdw;
88462306a36Sopenharmony_ci	v4l2_std_id ns;
88562306a36Sopenharmony_ci	ns = hdw->std_mask_cur;
88662306a36Sopenharmony_ci	ns = (ns & ~m) | (v & m);
88762306a36Sopenharmony_ci	if (ns == hdw->std_mask_cur) return 0;
88862306a36Sopenharmony_ci	hdw->std_mask_cur = ns;
88962306a36Sopenharmony_ci	hdw->std_dirty = !0;
89062306a36Sopenharmony_ci	return 0;
89162306a36Sopenharmony_ci}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_cistatic int ctrl_stdcur_is_dirty(struct pvr2_ctrl *cptr)
89462306a36Sopenharmony_ci{
89562306a36Sopenharmony_ci	return cptr->hdw->std_dirty != 0;
89662306a36Sopenharmony_ci}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_cistatic void ctrl_stdcur_clear_dirty(struct pvr2_ctrl *cptr)
89962306a36Sopenharmony_ci{
90062306a36Sopenharmony_ci	cptr->hdw->std_dirty = 0;
90162306a36Sopenharmony_ci}
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_cistatic int ctrl_signal_get(struct pvr2_ctrl *cptr,int *vp)
90462306a36Sopenharmony_ci{
90562306a36Sopenharmony_ci	struct pvr2_hdw *hdw = cptr->hdw;
90662306a36Sopenharmony_ci	pvr2_hdw_status_poll(hdw);
90762306a36Sopenharmony_ci	*vp = hdw->tuner_signal_info.signal;
90862306a36Sopenharmony_ci	return 0;
90962306a36Sopenharmony_ci}
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_cistatic int ctrl_audio_modes_present_get(struct pvr2_ctrl *cptr,int *vp)
91262306a36Sopenharmony_ci{
91362306a36Sopenharmony_ci	int val = 0;
91462306a36Sopenharmony_ci	unsigned int subchan;
91562306a36Sopenharmony_ci	struct pvr2_hdw *hdw = cptr->hdw;
91662306a36Sopenharmony_ci	pvr2_hdw_status_poll(hdw);
91762306a36Sopenharmony_ci	subchan = hdw->tuner_signal_info.rxsubchans;
91862306a36Sopenharmony_ci	if (subchan & V4L2_TUNER_SUB_MONO) {
91962306a36Sopenharmony_ci		val |= (1 << V4L2_TUNER_MODE_MONO);
92062306a36Sopenharmony_ci	}
92162306a36Sopenharmony_ci	if (subchan & V4L2_TUNER_SUB_STEREO) {
92262306a36Sopenharmony_ci		val |= (1 << V4L2_TUNER_MODE_STEREO);
92362306a36Sopenharmony_ci	}
92462306a36Sopenharmony_ci	if (subchan & V4L2_TUNER_SUB_LANG1) {
92562306a36Sopenharmony_ci		val |= (1 << V4L2_TUNER_MODE_LANG1);
92662306a36Sopenharmony_ci	}
92762306a36Sopenharmony_ci	if (subchan & V4L2_TUNER_SUB_LANG2) {
92862306a36Sopenharmony_ci		val |= (1 << V4L2_TUNER_MODE_LANG2);
92962306a36Sopenharmony_ci	}
93062306a36Sopenharmony_ci	*vp = val;
93162306a36Sopenharmony_ci	return 0;
93262306a36Sopenharmony_ci}
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci#define DEFINT(vmin,vmax) \
93662306a36Sopenharmony_ci	.type = pvr2_ctl_int, \
93762306a36Sopenharmony_ci	.def.type_int.min_value = vmin, \
93862306a36Sopenharmony_ci	.def.type_int.max_value = vmax
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci#define DEFENUM(tab) \
94162306a36Sopenharmony_ci	.type = pvr2_ctl_enum, \
94262306a36Sopenharmony_ci	.def.type_enum.count = ARRAY_SIZE(tab), \
94362306a36Sopenharmony_ci	.def.type_enum.value_names = tab
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci#define DEFBOOL \
94662306a36Sopenharmony_ci	.type = pvr2_ctl_bool
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci#define DEFMASK(msk,tab) \
94962306a36Sopenharmony_ci	.type = pvr2_ctl_bitmask, \
95062306a36Sopenharmony_ci	.def.type_bitmask.valid_bits = msk, \
95162306a36Sopenharmony_ci	.def.type_bitmask.bit_names = tab
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci#define DEFREF(vname) \
95462306a36Sopenharmony_ci	.set_value = ctrl_set_##vname, \
95562306a36Sopenharmony_ci	.get_value = ctrl_get_##vname, \
95662306a36Sopenharmony_ci	.is_dirty = ctrl_isdirty_##vname, \
95762306a36Sopenharmony_ci	.clear_dirty = ctrl_cleardirty_##vname
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci#define VCREATE_FUNCS(vname) \
96162306a36Sopenharmony_cistatic int ctrl_get_##vname(struct pvr2_ctrl *cptr,int *vp) \
96262306a36Sopenharmony_ci{*vp = cptr->hdw->vname##_val; return 0;} \
96362306a36Sopenharmony_cistatic int ctrl_set_##vname(struct pvr2_ctrl *cptr,int m,int v) \
96462306a36Sopenharmony_ci{cptr->hdw->vname##_val = v; cptr->hdw->vname##_dirty = !0; return 0;} \
96562306a36Sopenharmony_cistatic int ctrl_isdirty_##vname(struct pvr2_ctrl *cptr) \
96662306a36Sopenharmony_ci{return cptr->hdw->vname##_dirty != 0;} \
96762306a36Sopenharmony_cistatic void ctrl_cleardirty_##vname(struct pvr2_ctrl *cptr) \
96862306a36Sopenharmony_ci{cptr->hdw->vname##_dirty = 0;}
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ciVCREATE_FUNCS(brightness)
97162306a36Sopenharmony_ciVCREATE_FUNCS(contrast)
97262306a36Sopenharmony_ciVCREATE_FUNCS(saturation)
97362306a36Sopenharmony_ciVCREATE_FUNCS(hue)
97462306a36Sopenharmony_ciVCREATE_FUNCS(volume)
97562306a36Sopenharmony_ciVCREATE_FUNCS(balance)
97662306a36Sopenharmony_ciVCREATE_FUNCS(bass)
97762306a36Sopenharmony_ciVCREATE_FUNCS(treble)
97862306a36Sopenharmony_ciVCREATE_FUNCS(mute)
97962306a36Sopenharmony_ciVCREATE_FUNCS(cropl)
98062306a36Sopenharmony_ciVCREATE_FUNCS(cropt)
98162306a36Sopenharmony_ciVCREATE_FUNCS(cropw)
98262306a36Sopenharmony_ciVCREATE_FUNCS(croph)
98362306a36Sopenharmony_ciVCREATE_FUNCS(audiomode)
98462306a36Sopenharmony_ciVCREATE_FUNCS(res_hor)
98562306a36Sopenharmony_ciVCREATE_FUNCS(res_ver)
98662306a36Sopenharmony_ciVCREATE_FUNCS(srate)
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci/* Table definition of all controls which can be manipulated */
98962306a36Sopenharmony_cistatic const struct pvr2_ctl_info control_defs[] = {
99062306a36Sopenharmony_ci	{
99162306a36Sopenharmony_ci		.v4l_id = V4L2_CID_BRIGHTNESS,
99262306a36Sopenharmony_ci		.desc = "Brightness",
99362306a36Sopenharmony_ci		.name = "brightness",
99462306a36Sopenharmony_ci		.default_value = 128,
99562306a36Sopenharmony_ci		DEFREF(brightness),
99662306a36Sopenharmony_ci		DEFINT(0,255),
99762306a36Sopenharmony_ci	},{
99862306a36Sopenharmony_ci		.v4l_id = V4L2_CID_CONTRAST,
99962306a36Sopenharmony_ci		.desc = "Contrast",
100062306a36Sopenharmony_ci		.name = "contrast",
100162306a36Sopenharmony_ci		.default_value = 68,
100262306a36Sopenharmony_ci		DEFREF(contrast),
100362306a36Sopenharmony_ci		DEFINT(0,127),
100462306a36Sopenharmony_ci	},{
100562306a36Sopenharmony_ci		.v4l_id = V4L2_CID_SATURATION,
100662306a36Sopenharmony_ci		.desc = "Saturation",
100762306a36Sopenharmony_ci		.name = "saturation",
100862306a36Sopenharmony_ci		.default_value = 64,
100962306a36Sopenharmony_ci		DEFREF(saturation),
101062306a36Sopenharmony_ci		DEFINT(0,127),
101162306a36Sopenharmony_ci	},{
101262306a36Sopenharmony_ci		.v4l_id = V4L2_CID_HUE,
101362306a36Sopenharmony_ci		.desc = "Hue",
101462306a36Sopenharmony_ci		.name = "hue",
101562306a36Sopenharmony_ci		.default_value = 0,
101662306a36Sopenharmony_ci		DEFREF(hue),
101762306a36Sopenharmony_ci		DEFINT(-128,127),
101862306a36Sopenharmony_ci	},{
101962306a36Sopenharmony_ci		.v4l_id = V4L2_CID_AUDIO_VOLUME,
102062306a36Sopenharmony_ci		.desc = "Volume",
102162306a36Sopenharmony_ci		.name = "volume",
102262306a36Sopenharmony_ci		.default_value = 62000,
102362306a36Sopenharmony_ci		DEFREF(volume),
102462306a36Sopenharmony_ci		DEFINT(0,65535),
102562306a36Sopenharmony_ci	},{
102662306a36Sopenharmony_ci		.v4l_id = V4L2_CID_AUDIO_BALANCE,
102762306a36Sopenharmony_ci		.desc = "Balance",
102862306a36Sopenharmony_ci		.name = "balance",
102962306a36Sopenharmony_ci		.default_value = 0,
103062306a36Sopenharmony_ci		DEFREF(balance),
103162306a36Sopenharmony_ci		DEFINT(-32768,32767),
103262306a36Sopenharmony_ci	},{
103362306a36Sopenharmony_ci		.v4l_id = V4L2_CID_AUDIO_BASS,
103462306a36Sopenharmony_ci		.desc = "Bass",
103562306a36Sopenharmony_ci		.name = "bass",
103662306a36Sopenharmony_ci		.default_value = 0,
103762306a36Sopenharmony_ci		DEFREF(bass),
103862306a36Sopenharmony_ci		DEFINT(-32768,32767),
103962306a36Sopenharmony_ci	},{
104062306a36Sopenharmony_ci		.v4l_id = V4L2_CID_AUDIO_TREBLE,
104162306a36Sopenharmony_ci		.desc = "Treble",
104262306a36Sopenharmony_ci		.name = "treble",
104362306a36Sopenharmony_ci		.default_value = 0,
104462306a36Sopenharmony_ci		DEFREF(treble),
104562306a36Sopenharmony_ci		DEFINT(-32768,32767),
104662306a36Sopenharmony_ci	},{
104762306a36Sopenharmony_ci		.v4l_id = V4L2_CID_AUDIO_MUTE,
104862306a36Sopenharmony_ci		.desc = "Mute",
104962306a36Sopenharmony_ci		.name = "mute",
105062306a36Sopenharmony_ci		.default_value = 0,
105162306a36Sopenharmony_ci		DEFREF(mute),
105262306a36Sopenharmony_ci		DEFBOOL,
105362306a36Sopenharmony_ci	}, {
105462306a36Sopenharmony_ci		.desc = "Capture crop left margin",
105562306a36Sopenharmony_ci		.name = "crop_left",
105662306a36Sopenharmony_ci		.internal_id = PVR2_CID_CROPL,
105762306a36Sopenharmony_ci		.default_value = 0,
105862306a36Sopenharmony_ci		DEFREF(cropl),
105962306a36Sopenharmony_ci		DEFINT(-129, 340),
106062306a36Sopenharmony_ci		.get_min_value = ctrl_cropl_min_get,
106162306a36Sopenharmony_ci		.get_max_value = ctrl_cropl_max_get,
106262306a36Sopenharmony_ci		.get_def_value = ctrl_get_cropcapdl,
106362306a36Sopenharmony_ci	}, {
106462306a36Sopenharmony_ci		.desc = "Capture crop top margin",
106562306a36Sopenharmony_ci		.name = "crop_top",
106662306a36Sopenharmony_ci		.internal_id = PVR2_CID_CROPT,
106762306a36Sopenharmony_ci		.default_value = 0,
106862306a36Sopenharmony_ci		DEFREF(cropt),
106962306a36Sopenharmony_ci		DEFINT(-35, 544),
107062306a36Sopenharmony_ci		.get_min_value = ctrl_cropt_min_get,
107162306a36Sopenharmony_ci		.get_max_value = ctrl_cropt_max_get,
107262306a36Sopenharmony_ci		.get_def_value = ctrl_get_cropcapdt,
107362306a36Sopenharmony_ci	}, {
107462306a36Sopenharmony_ci		.desc = "Capture crop width",
107562306a36Sopenharmony_ci		.name = "crop_width",
107662306a36Sopenharmony_ci		.internal_id = PVR2_CID_CROPW,
107762306a36Sopenharmony_ci		.default_value = 720,
107862306a36Sopenharmony_ci		DEFREF(cropw),
107962306a36Sopenharmony_ci		DEFINT(0, 864),
108062306a36Sopenharmony_ci		.get_max_value = ctrl_cropw_max_get,
108162306a36Sopenharmony_ci		.get_def_value = ctrl_get_cropcapdw,
108262306a36Sopenharmony_ci	}, {
108362306a36Sopenharmony_ci		.desc = "Capture crop height",
108462306a36Sopenharmony_ci		.name = "crop_height",
108562306a36Sopenharmony_ci		.internal_id = PVR2_CID_CROPH,
108662306a36Sopenharmony_ci		.default_value = 480,
108762306a36Sopenharmony_ci		DEFREF(croph),
108862306a36Sopenharmony_ci		DEFINT(0, 576),
108962306a36Sopenharmony_ci		.get_max_value = ctrl_croph_max_get,
109062306a36Sopenharmony_ci		.get_def_value = ctrl_get_cropcapdh,
109162306a36Sopenharmony_ci	}, {
109262306a36Sopenharmony_ci		.desc = "Capture capability pixel aspect numerator",
109362306a36Sopenharmony_ci		.name = "cropcap_pixel_numerator",
109462306a36Sopenharmony_ci		.internal_id = PVR2_CID_CROPCAPPAN,
109562306a36Sopenharmony_ci		.get_value = ctrl_get_cropcappan,
109662306a36Sopenharmony_ci	}, {
109762306a36Sopenharmony_ci		.desc = "Capture capability pixel aspect denominator",
109862306a36Sopenharmony_ci		.name = "cropcap_pixel_denominator",
109962306a36Sopenharmony_ci		.internal_id = PVR2_CID_CROPCAPPAD,
110062306a36Sopenharmony_ci		.get_value = ctrl_get_cropcappad,
110162306a36Sopenharmony_ci	}, {
110262306a36Sopenharmony_ci		.desc = "Capture capability bounds top",
110362306a36Sopenharmony_ci		.name = "cropcap_bounds_top",
110462306a36Sopenharmony_ci		.internal_id = PVR2_CID_CROPCAPBT,
110562306a36Sopenharmony_ci		.get_value = ctrl_get_cropcapbt,
110662306a36Sopenharmony_ci	}, {
110762306a36Sopenharmony_ci		.desc = "Capture capability bounds left",
110862306a36Sopenharmony_ci		.name = "cropcap_bounds_left",
110962306a36Sopenharmony_ci		.internal_id = PVR2_CID_CROPCAPBL,
111062306a36Sopenharmony_ci		.get_value = ctrl_get_cropcapbl,
111162306a36Sopenharmony_ci	}, {
111262306a36Sopenharmony_ci		.desc = "Capture capability bounds width",
111362306a36Sopenharmony_ci		.name = "cropcap_bounds_width",
111462306a36Sopenharmony_ci		.internal_id = PVR2_CID_CROPCAPBW,
111562306a36Sopenharmony_ci		.get_value = ctrl_get_cropcapbw,
111662306a36Sopenharmony_ci	}, {
111762306a36Sopenharmony_ci		.desc = "Capture capability bounds height",
111862306a36Sopenharmony_ci		.name = "cropcap_bounds_height",
111962306a36Sopenharmony_ci		.internal_id = PVR2_CID_CROPCAPBH,
112062306a36Sopenharmony_ci		.get_value = ctrl_get_cropcapbh,
112162306a36Sopenharmony_ci	},{
112262306a36Sopenharmony_ci		.desc = "Video Source",
112362306a36Sopenharmony_ci		.name = "input",
112462306a36Sopenharmony_ci		.internal_id = PVR2_CID_INPUT,
112562306a36Sopenharmony_ci		.default_value = PVR2_CVAL_INPUT_TV,
112662306a36Sopenharmony_ci		.check_value = ctrl_check_input,
112762306a36Sopenharmony_ci		DEFREF(input),
112862306a36Sopenharmony_ci		DEFENUM(control_values_input),
112962306a36Sopenharmony_ci	},{
113062306a36Sopenharmony_ci		.desc = "Audio Mode",
113162306a36Sopenharmony_ci		.name = "audio_mode",
113262306a36Sopenharmony_ci		.internal_id = PVR2_CID_AUDIOMODE,
113362306a36Sopenharmony_ci		.default_value = V4L2_TUNER_MODE_STEREO,
113462306a36Sopenharmony_ci		DEFREF(audiomode),
113562306a36Sopenharmony_ci		DEFENUM(control_values_audiomode),
113662306a36Sopenharmony_ci	},{
113762306a36Sopenharmony_ci		.desc = "Horizontal capture resolution",
113862306a36Sopenharmony_ci		.name = "resolution_hor",
113962306a36Sopenharmony_ci		.internal_id = PVR2_CID_HRES,
114062306a36Sopenharmony_ci		.default_value = 720,
114162306a36Sopenharmony_ci		DEFREF(res_hor),
114262306a36Sopenharmony_ci		DEFINT(19,720),
114362306a36Sopenharmony_ci	},{
114462306a36Sopenharmony_ci		.desc = "Vertical capture resolution",
114562306a36Sopenharmony_ci		.name = "resolution_ver",
114662306a36Sopenharmony_ci		.internal_id = PVR2_CID_VRES,
114762306a36Sopenharmony_ci		.default_value = 480,
114862306a36Sopenharmony_ci		DEFREF(res_ver),
114962306a36Sopenharmony_ci		DEFINT(17,576),
115062306a36Sopenharmony_ci		/* Hook in check for video standard and adjust maximum
115162306a36Sopenharmony_ci		   depending on the standard. */
115262306a36Sopenharmony_ci		.get_max_value = ctrl_vres_max_get,
115362306a36Sopenharmony_ci		.get_min_value = ctrl_vres_min_get,
115462306a36Sopenharmony_ci	},{
115562306a36Sopenharmony_ci		.v4l_id = V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ,
115662306a36Sopenharmony_ci		.default_value = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000,
115762306a36Sopenharmony_ci		.desc = "Audio Sampling Frequency",
115862306a36Sopenharmony_ci		.name = "srate",
115962306a36Sopenharmony_ci		DEFREF(srate),
116062306a36Sopenharmony_ci		DEFENUM(control_values_srate),
116162306a36Sopenharmony_ci	},{
116262306a36Sopenharmony_ci		.desc = "Tuner Frequency (Hz)",
116362306a36Sopenharmony_ci		.name = "frequency",
116462306a36Sopenharmony_ci		.internal_id = PVR2_CID_FREQUENCY,
116562306a36Sopenharmony_ci		.default_value = 0,
116662306a36Sopenharmony_ci		.set_value = ctrl_freq_set,
116762306a36Sopenharmony_ci		.get_value = ctrl_freq_get,
116862306a36Sopenharmony_ci		.is_dirty = ctrl_freq_is_dirty,
116962306a36Sopenharmony_ci		.clear_dirty = ctrl_freq_clear_dirty,
117062306a36Sopenharmony_ci		DEFINT(0,0),
117162306a36Sopenharmony_ci		/* Hook in check for input value (tv/radio) and adjust
117262306a36Sopenharmony_ci		   max/min values accordingly */
117362306a36Sopenharmony_ci		.get_max_value = ctrl_freq_max_get,
117462306a36Sopenharmony_ci		.get_min_value = ctrl_freq_min_get,
117562306a36Sopenharmony_ci	},{
117662306a36Sopenharmony_ci		.desc = "Channel",
117762306a36Sopenharmony_ci		.name = "channel",
117862306a36Sopenharmony_ci		.set_value = ctrl_channel_set,
117962306a36Sopenharmony_ci		.get_value = ctrl_channel_get,
118062306a36Sopenharmony_ci		DEFINT(0,FREQTABLE_SIZE),
118162306a36Sopenharmony_ci	},{
118262306a36Sopenharmony_ci		.desc = "Channel Program Frequency",
118362306a36Sopenharmony_ci		.name = "freq_table_value",
118462306a36Sopenharmony_ci		.set_value = ctrl_channelfreq_set,
118562306a36Sopenharmony_ci		.get_value = ctrl_channelfreq_get,
118662306a36Sopenharmony_ci		DEFINT(0,0),
118762306a36Sopenharmony_ci		/* Hook in check for input value (tv/radio) and adjust
118862306a36Sopenharmony_ci		   max/min values accordingly */
118962306a36Sopenharmony_ci		.get_max_value = ctrl_freq_max_get,
119062306a36Sopenharmony_ci		.get_min_value = ctrl_freq_min_get,
119162306a36Sopenharmony_ci	},{
119262306a36Sopenharmony_ci		.desc = "Channel Program ID",
119362306a36Sopenharmony_ci		.name = "freq_table_channel",
119462306a36Sopenharmony_ci		.set_value = ctrl_channelprog_set,
119562306a36Sopenharmony_ci		.get_value = ctrl_channelprog_get,
119662306a36Sopenharmony_ci		DEFINT(0,FREQTABLE_SIZE),
119762306a36Sopenharmony_ci	},{
119862306a36Sopenharmony_ci		.desc = "Streaming Enabled",
119962306a36Sopenharmony_ci		.name = "streaming_enabled",
120062306a36Sopenharmony_ci		.get_value = ctrl_streamingenabled_get,
120162306a36Sopenharmony_ci		DEFBOOL,
120262306a36Sopenharmony_ci	},{
120362306a36Sopenharmony_ci		.desc = "USB Speed",
120462306a36Sopenharmony_ci		.name = "usb_speed",
120562306a36Sopenharmony_ci		.get_value = ctrl_hsm_get,
120662306a36Sopenharmony_ci		DEFENUM(control_values_hsm),
120762306a36Sopenharmony_ci	},{
120862306a36Sopenharmony_ci		.desc = "Master State",
120962306a36Sopenharmony_ci		.name = "master_state",
121062306a36Sopenharmony_ci		.get_value = ctrl_masterstate_get,
121162306a36Sopenharmony_ci		DEFENUM(pvr2_state_names),
121262306a36Sopenharmony_ci	},{
121362306a36Sopenharmony_ci		.desc = "Signal Present",
121462306a36Sopenharmony_ci		.name = "signal_present",
121562306a36Sopenharmony_ci		.get_value = ctrl_signal_get,
121662306a36Sopenharmony_ci		DEFINT(0,65535),
121762306a36Sopenharmony_ci	},{
121862306a36Sopenharmony_ci		.desc = "Audio Modes Present",
121962306a36Sopenharmony_ci		.name = "audio_modes_present",
122062306a36Sopenharmony_ci		.get_value = ctrl_audio_modes_present_get,
122162306a36Sopenharmony_ci		/* For this type we "borrow" the V4L2_TUNER_MODE enum from
122262306a36Sopenharmony_ci		   v4l.  Nothing outside of this module cares about this,
122362306a36Sopenharmony_ci		   but I reuse it in order to also reuse the
122462306a36Sopenharmony_ci		   control_values_audiomode string table. */
122562306a36Sopenharmony_ci		DEFMASK(((1 << V4L2_TUNER_MODE_MONO)|
122662306a36Sopenharmony_ci			 (1 << V4L2_TUNER_MODE_STEREO)|
122762306a36Sopenharmony_ci			 (1 << V4L2_TUNER_MODE_LANG1)|
122862306a36Sopenharmony_ci			 (1 << V4L2_TUNER_MODE_LANG2)),
122962306a36Sopenharmony_ci			control_values_audiomode),
123062306a36Sopenharmony_ci	},{
123162306a36Sopenharmony_ci		.desc = "Video Standards Available Mask",
123262306a36Sopenharmony_ci		.name = "video_standard_mask_available",
123362306a36Sopenharmony_ci		.internal_id = PVR2_CID_STDAVAIL,
123462306a36Sopenharmony_ci		.skip_init = !0,
123562306a36Sopenharmony_ci		.get_value = ctrl_stdavail_get,
123662306a36Sopenharmony_ci		.set_value = ctrl_stdavail_set,
123762306a36Sopenharmony_ci		.val_to_sym = ctrl_std_val_to_sym,
123862306a36Sopenharmony_ci		.sym_to_val = ctrl_std_sym_to_val,
123962306a36Sopenharmony_ci		.type = pvr2_ctl_bitmask,
124062306a36Sopenharmony_ci	},{
124162306a36Sopenharmony_ci		.desc = "Video Standards In Use Mask",
124262306a36Sopenharmony_ci		.name = "video_standard_mask_active",
124362306a36Sopenharmony_ci		.internal_id = PVR2_CID_STDCUR,
124462306a36Sopenharmony_ci		.skip_init = !0,
124562306a36Sopenharmony_ci		.get_value = ctrl_stdcur_get,
124662306a36Sopenharmony_ci		.set_value = ctrl_stdcur_set,
124762306a36Sopenharmony_ci		.is_dirty = ctrl_stdcur_is_dirty,
124862306a36Sopenharmony_ci		.clear_dirty = ctrl_stdcur_clear_dirty,
124962306a36Sopenharmony_ci		.val_to_sym = ctrl_std_val_to_sym,
125062306a36Sopenharmony_ci		.sym_to_val = ctrl_std_sym_to_val,
125162306a36Sopenharmony_ci		.type = pvr2_ctl_bitmask,
125262306a36Sopenharmony_ci	},{
125362306a36Sopenharmony_ci		.desc = "Video Standards Detected Mask",
125462306a36Sopenharmony_ci		.name = "video_standard_mask_detected",
125562306a36Sopenharmony_ci		.internal_id = PVR2_CID_STDDETECT,
125662306a36Sopenharmony_ci		.skip_init = !0,
125762306a36Sopenharmony_ci		.get_value = ctrl_stddetect_get,
125862306a36Sopenharmony_ci		.val_to_sym = ctrl_std_val_to_sym,
125962306a36Sopenharmony_ci		.sym_to_val = ctrl_std_sym_to_val,
126062306a36Sopenharmony_ci		.type = pvr2_ctl_bitmask,
126162306a36Sopenharmony_ci	}
126262306a36Sopenharmony_ci};
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci#define CTRLDEF_COUNT ARRAY_SIZE(control_defs)
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ciconst char *pvr2_config_get_name(enum pvr2_config cfg)
126862306a36Sopenharmony_ci{
126962306a36Sopenharmony_ci	switch (cfg) {
127062306a36Sopenharmony_ci	case pvr2_config_empty: return "empty";
127162306a36Sopenharmony_ci	case pvr2_config_mpeg: return "mpeg";
127262306a36Sopenharmony_ci	case pvr2_config_vbi: return "vbi";
127362306a36Sopenharmony_ci	case pvr2_config_pcm: return "pcm";
127462306a36Sopenharmony_ci	case pvr2_config_rawvideo: return "raw video";
127562306a36Sopenharmony_ci	}
127662306a36Sopenharmony_ci	return "<unknown>";
127762306a36Sopenharmony_ci}
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_cistruct usb_device *pvr2_hdw_get_dev(struct pvr2_hdw *hdw)
128162306a36Sopenharmony_ci{
128262306a36Sopenharmony_ci	return hdw->usb_dev;
128362306a36Sopenharmony_ci}
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ciunsigned long pvr2_hdw_get_sn(struct pvr2_hdw *hdw)
128762306a36Sopenharmony_ci{
128862306a36Sopenharmony_ci	return hdw->serial_number;
128962306a36Sopenharmony_ci}
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ciconst char *pvr2_hdw_get_bus_info(struct pvr2_hdw *hdw)
129362306a36Sopenharmony_ci{
129462306a36Sopenharmony_ci	return hdw->bus_info;
129562306a36Sopenharmony_ci}
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ciconst char *pvr2_hdw_get_device_identifier(struct pvr2_hdw *hdw)
129962306a36Sopenharmony_ci{
130062306a36Sopenharmony_ci	return hdw->identifier;
130162306a36Sopenharmony_ci}
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ciunsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *hdw)
130562306a36Sopenharmony_ci{
130662306a36Sopenharmony_ci	return hdw->freqSelector ? hdw->freqValTelevision : hdw->freqValRadio;
130762306a36Sopenharmony_ci}
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci/* Set the currently tuned frequency and account for all possible
131062306a36Sopenharmony_ci   driver-core side effects of this action. */
131162306a36Sopenharmony_cistatic void pvr2_hdw_set_cur_freq(struct pvr2_hdw *hdw,unsigned long val)
131262306a36Sopenharmony_ci{
131362306a36Sopenharmony_ci	if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
131462306a36Sopenharmony_ci		if (hdw->freqSelector) {
131562306a36Sopenharmony_ci			/* Swing over to radio frequency selection */
131662306a36Sopenharmony_ci			hdw->freqSelector = 0;
131762306a36Sopenharmony_ci			hdw->freqDirty = !0;
131862306a36Sopenharmony_ci		}
131962306a36Sopenharmony_ci		if (hdw->freqValRadio != val) {
132062306a36Sopenharmony_ci			hdw->freqValRadio = val;
132162306a36Sopenharmony_ci			hdw->freqSlotRadio = 0;
132262306a36Sopenharmony_ci			hdw->freqDirty = !0;
132362306a36Sopenharmony_ci		}
132462306a36Sopenharmony_ci	} else {
132562306a36Sopenharmony_ci		if (!(hdw->freqSelector)) {
132662306a36Sopenharmony_ci			/* Swing over to television frequency selection */
132762306a36Sopenharmony_ci			hdw->freqSelector = 1;
132862306a36Sopenharmony_ci			hdw->freqDirty = !0;
132962306a36Sopenharmony_ci		}
133062306a36Sopenharmony_ci		if (hdw->freqValTelevision != val) {
133162306a36Sopenharmony_ci			hdw->freqValTelevision = val;
133262306a36Sopenharmony_ci			hdw->freqSlotTelevision = 0;
133362306a36Sopenharmony_ci			hdw->freqDirty = !0;
133462306a36Sopenharmony_ci		}
133562306a36Sopenharmony_ci	}
133662306a36Sopenharmony_ci}
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ciint pvr2_hdw_get_unit_number(struct pvr2_hdw *hdw)
133962306a36Sopenharmony_ci{
134062306a36Sopenharmony_ci	return hdw->unit_number;
134162306a36Sopenharmony_ci}
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci/* Attempt to locate one of the given set of files.  Messages are logged
134562306a36Sopenharmony_ci   appropriate to what has been found.  The return value will be 0 or
134662306a36Sopenharmony_ci   greater on success (it will be the index of the file name found) and
134762306a36Sopenharmony_ci   fw_entry will be filled in.  Otherwise a negative error is returned on
134862306a36Sopenharmony_ci   failure.  If the return value is -ENOENT then no viable firmware file
134962306a36Sopenharmony_ci   could be located. */
135062306a36Sopenharmony_cistatic int pvr2_locate_firmware(struct pvr2_hdw *hdw,
135162306a36Sopenharmony_ci				const struct firmware **fw_entry,
135262306a36Sopenharmony_ci				const char *fwtypename,
135362306a36Sopenharmony_ci				unsigned int fwcount,
135462306a36Sopenharmony_ci				const char *fwnames[])
135562306a36Sopenharmony_ci{
135662306a36Sopenharmony_ci	unsigned int idx;
135762306a36Sopenharmony_ci	int ret = -EINVAL;
135862306a36Sopenharmony_ci	for (idx = 0; idx < fwcount; idx++) {
135962306a36Sopenharmony_ci		ret = request_firmware(fw_entry,
136062306a36Sopenharmony_ci				       fwnames[idx],
136162306a36Sopenharmony_ci				       &hdw->usb_dev->dev);
136262306a36Sopenharmony_ci		if (!ret) {
136362306a36Sopenharmony_ci			trace_firmware("Located %s firmware: %s; uploading...",
136462306a36Sopenharmony_ci				       fwtypename,
136562306a36Sopenharmony_ci				       fwnames[idx]);
136662306a36Sopenharmony_ci			return idx;
136762306a36Sopenharmony_ci		}
136862306a36Sopenharmony_ci		if (ret == -ENOENT) continue;
136962306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
137062306a36Sopenharmony_ci			   "request_firmware fatal error with code=%d",ret);
137162306a36Sopenharmony_ci		return ret;
137262306a36Sopenharmony_ci	}
137362306a36Sopenharmony_ci	pvr2_trace(PVR2_TRACE_ERROR_LEGS,
137462306a36Sopenharmony_ci		   "***WARNING*** Device %s firmware seems to be missing.",
137562306a36Sopenharmony_ci		   fwtypename);
137662306a36Sopenharmony_ci	pvr2_trace(PVR2_TRACE_ERROR_LEGS,
137762306a36Sopenharmony_ci		   "Did you install the pvrusb2 firmware files in their proper location?");
137862306a36Sopenharmony_ci	if (fwcount == 1) {
137962306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
138062306a36Sopenharmony_ci			   "request_firmware unable to locate %s file %s",
138162306a36Sopenharmony_ci			   fwtypename,fwnames[0]);
138262306a36Sopenharmony_ci	} else {
138362306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
138462306a36Sopenharmony_ci			   "request_firmware unable to locate one of the following %s files:",
138562306a36Sopenharmony_ci			   fwtypename);
138662306a36Sopenharmony_ci		for (idx = 0; idx < fwcount; idx++) {
138762306a36Sopenharmony_ci			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
138862306a36Sopenharmony_ci				   "request_firmware: Failed to find %s",
138962306a36Sopenharmony_ci				   fwnames[idx]);
139062306a36Sopenharmony_ci		}
139162306a36Sopenharmony_ci	}
139262306a36Sopenharmony_ci	return ret;
139362306a36Sopenharmony_ci}
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci/*
139762306a36Sopenharmony_ci * pvr2_upload_firmware1().
139862306a36Sopenharmony_ci *
139962306a36Sopenharmony_ci * Send the 8051 firmware to the device.  After the upload, arrange for
140062306a36Sopenharmony_ci * device to re-enumerate.
140162306a36Sopenharmony_ci *
140262306a36Sopenharmony_ci * NOTE : the pointer to the firmware data given by request_firmware()
140362306a36Sopenharmony_ci * is not suitable for an usb transaction.
140462306a36Sopenharmony_ci *
140562306a36Sopenharmony_ci */
140662306a36Sopenharmony_cistatic int pvr2_upload_firmware1(struct pvr2_hdw *hdw)
140762306a36Sopenharmony_ci{
140862306a36Sopenharmony_ci	const struct firmware *fw_entry = NULL;
140962306a36Sopenharmony_ci	void  *fw_ptr;
141062306a36Sopenharmony_ci	unsigned int pipe;
141162306a36Sopenharmony_ci	unsigned int fwsize;
141262306a36Sopenharmony_ci	int ret;
141362306a36Sopenharmony_ci	u16 address;
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci	if (!hdw->hdw_desc->fx2_firmware.cnt) {
141662306a36Sopenharmony_ci		hdw->fw1_state = FW1_STATE_OK;
141762306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
141862306a36Sopenharmony_ci			   "Connected device type defines no firmware to upload; ignoring firmware");
141962306a36Sopenharmony_ci		return -ENOTTY;
142062306a36Sopenharmony_ci	}
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci	hdw->fw1_state = FW1_STATE_FAILED; // default result
142362306a36Sopenharmony_ci
142462306a36Sopenharmony_ci	trace_firmware("pvr2_upload_firmware1");
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci	ret = pvr2_locate_firmware(hdw,&fw_entry,"fx2 controller",
142762306a36Sopenharmony_ci				   hdw->hdw_desc->fx2_firmware.cnt,
142862306a36Sopenharmony_ci				   hdw->hdw_desc->fx2_firmware.lst);
142962306a36Sopenharmony_ci	if (ret < 0) {
143062306a36Sopenharmony_ci		if (ret == -ENOENT) hdw->fw1_state = FW1_STATE_MISSING;
143162306a36Sopenharmony_ci		return ret;
143262306a36Sopenharmony_ci	}
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	usb_clear_halt(hdw->usb_dev, usb_sndbulkpipe(hdw->usb_dev, 0 & 0x7f));
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci	pipe = usb_sndctrlpipe(hdw->usb_dev, 0);
143762306a36Sopenharmony_ci	fwsize = fw_entry->size;
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci	if ((fwsize != 0x2000) &&
144062306a36Sopenharmony_ci	    (!(hdw->hdw_desc->flag_fx2_16kb && (fwsize == 0x4000)))) {
144162306a36Sopenharmony_ci		if (hdw->hdw_desc->flag_fx2_16kb) {
144262306a36Sopenharmony_ci			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
144362306a36Sopenharmony_ci				   "Wrong fx2 firmware size (expected 8192 or 16384, got %u)",
144462306a36Sopenharmony_ci				   fwsize);
144562306a36Sopenharmony_ci		} else {
144662306a36Sopenharmony_ci			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
144762306a36Sopenharmony_ci				   "Wrong fx2 firmware size (expected 8192, got %u)",
144862306a36Sopenharmony_ci				   fwsize);
144962306a36Sopenharmony_ci		}
145062306a36Sopenharmony_ci		release_firmware(fw_entry);
145162306a36Sopenharmony_ci		return -ENOMEM;
145262306a36Sopenharmony_ci	}
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci	fw_ptr = kmalloc(0x800, GFP_KERNEL);
145562306a36Sopenharmony_ci	if (fw_ptr == NULL){
145662306a36Sopenharmony_ci		release_firmware(fw_entry);
145762306a36Sopenharmony_ci		return -ENOMEM;
145862306a36Sopenharmony_ci	}
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	/* We have to hold the CPU during firmware upload. */
146162306a36Sopenharmony_ci	pvr2_hdw_cpureset_assert(hdw,1);
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	/* upload the firmware to address 0000-1fff in 2048 (=0x800) bytes
146462306a36Sopenharmony_ci	   chunk. */
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	ret = 0;
146762306a36Sopenharmony_ci	for (address = 0; address < fwsize; address += 0x800) {
146862306a36Sopenharmony_ci		memcpy(fw_ptr, fw_entry->data + address, 0x800);
146962306a36Sopenharmony_ci		ret += usb_control_msg(hdw->usb_dev, pipe, 0xa0, 0x40, address,
147062306a36Sopenharmony_ci				       0, fw_ptr, 0x800, 1000);
147162306a36Sopenharmony_ci	}
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci	trace_firmware("Upload done, releasing device's CPU");
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci	/* Now release the CPU.  It will disconnect and reconnect later. */
147662306a36Sopenharmony_ci	pvr2_hdw_cpureset_assert(hdw,0);
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci	kfree(fw_ptr);
147962306a36Sopenharmony_ci	release_firmware(fw_entry);
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	trace_firmware("Upload done (%d bytes sent)",ret);
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci	/* We should have written fwsize bytes */
148462306a36Sopenharmony_ci	if (ret == fwsize) {
148562306a36Sopenharmony_ci		hdw->fw1_state = FW1_STATE_RELOAD;
148662306a36Sopenharmony_ci		return 0;
148762306a36Sopenharmony_ci	}
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	return -EIO;
149062306a36Sopenharmony_ci}
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci/*
149462306a36Sopenharmony_ci * pvr2_upload_firmware2()
149562306a36Sopenharmony_ci *
149662306a36Sopenharmony_ci * This uploads encoder firmware on endpoint 2.
149762306a36Sopenharmony_ci *
149862306a36Sopenharmony_ci */
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ciint pvr2_upload_firmware2(struct pvr2_hdw *hdw)
150162306a36Sopenharmony_ci{
150262306a36Sopenharmony_ci	const struct firmware *fw_entry = NULL;
150362306a36Sopenharmony_ci	void  *fw_ptr;
150462306a36Sopenharmony_ci	unsigned int pipe, fw_len, fw_done, bcnt, icnt;
150562306a36Sopenharmony_ci	int actual_length;
150662306a36Sopenharmony_ci	int ret = 0;
150762306a36Sopenharmony_ci	int fwidx;
150862306a36Sopenharmony_ci	static const char *fw_files[] = {
150962306a36Sopenharmony_ci		CX2341X_FIRM_ENC_FILENAME,
151062306a36Sopenharmony_ci	};
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci	if (hdw->hdw_desc->flag_skip_cx23416_firmware) {
151362306a36Sopenharmony_ci		return 0;
151462306a36Sopenharmony_ci	}
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci	trace_firmware("pvr2_upload_firmware2");
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci	ret = pvr2_locate_firmware(hdw,&fw_entry,"encoder",
151962306a36Sopenharmony_ci				   ARRAY_SIZE(fw_files), fw_files);
152062306a36Sopenharmony_ci	if (ret < 0) return ret;
152162306a36Sopenharmony_ci	fwidx = ret;
152262306a36Sopenharmony_ci	ret = 0;
152362306a36Sopenharmony_ci	/* Since we're about to completely reinitialize the encoder,
152462306a36Sopenharmony_ci	   invalidate our cached copy of its configuration state.  Next
152562306a36Sopenharmony_ci	   time we configure the encoder, then we'll fully configure it. */
152662306a36Sopenharmony_ci	hdw->enc_cur_valid = 0;
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci	/* Encoder is about to be reset so note that as far as we're
152962306a36Sopenharmony_ci	   concerned now, the encoder has never been run. */
153062306a36Sopenharmony_ci	del_timer_sync(&hdw->encoder_run_timer);
153162306a36Sopenharmony_ci	if (hdw->state_encoder_runok) {
153262306a36Sopenharmony_ci		hdw->state_encoder_runok = 0;
153362306a36Sopenharmony_ci		trace_stbit("state_encoder_runok",hdw->state_encoder_runok);
153462306a36Sopenharmony_ci	}
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci	/* First prepare firmware loading */
153762306a36Sopenharmony_ci	ret |= pvr2_write_register(hdw, 0x0048, 0xffffffff); /*interrupt mask*/
153862306a36Sopenharmony_ci	ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000088); /*gpio dir*/
153962306a36Sopenharmony_ci	ret |= pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000008); /*gpio output state*/
154062306a36Sopenharmony_ci	ret |= pvr2_hdw_cmd_deep_reset(hdw);
154162306a36Sopenharmony_ci	ret |= pvr2_write_register(hdw, 0xa064, 0x00000000); /*APU command*/
154262306a36Sopenharmony_ci	ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000408); /*gpio dir*/
154362306a36Sopenharmony_ci	ret |= pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000008); /*gpio output state*/
154462306a36Sopenharmony_ci	ret |= pvr2_write_register(hdw, 0x9058, 0xffffffed); /*VPU ctrl*/
154562306a36Sopenharmony_ci	ret |= pvr2_write_register(hdw, 0x9054, 0xfffffffd); /*reset hw blocks*/
154662306a36Sopenharmony_ci	ret |= pvr2_write_register(hdw, 0x07f8, 0x80000800); /*encoder SDRAM refresh*/
154762306a36Sopenharmony_ci	ret |= pvr2_write_register(hdw, 0x07fc, 0x0000001a); /*encoder SDRAM pre-charge*/
154862306a36Sopenharmony_ci	ret |= pvr2_write_register(hdw, 0x0700, 0x00000000); /*I2C clock*/
154962306a36Sopenharmony_ci	ret |= pvr2_write_register(hdw, 0xaa00, 0x00000000); /*unknown*/
155062306a36Sopenharmony_ci	ret |= pvr2_write_register(hdw, 0xaa04, 0x00057810); /*unknown*/
155162306a36Sopenharmony_ci	ret |= pvr2_write_register(hdw, 0xaa10, 0x00148500); /*unknown*/
155262306a36Sopenharmony_ci	ret |= pvr2_write_register(hdw, 0xaa18, 0x00840000); /*unknown*/
155362306a36Sopenharmony_ci	ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_FWPOST1);
155462306a36Sopenharmony_ci	ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_MEMSEL | (1 << 8) | (0 << 16));
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci	if (ret) {
155762306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
155862306a36Sopenharmony_ci			   "firmware2 upload prep failed, ret=%d",ret);
155962306a36Sopenharmony_ci		release_firmware(fw_entry);
156062306a36Sopenharmony_ci		goto done;
156162306a36Sopenharmony_ci	}
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	/* Now send firmware */
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci	fw_len = fw_entry->size;
156662306a36Sopenharmony_ci
156762306a36Sopenharmony_ci	if (fw_len % sizeof(u32)) {
156862306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
156962306a36Sopenharmony_ci			   "size of %s firmware must be a multiple of %zu bytes",
157062306a36Sopenharmony_ci			   fw_files[fwidx],sizeof(u32));
157162306a36Sopenharmony_ci		release_firmware(fw_entry);
157262306a36Sopenharmony_ci		ret = -EINVAL;
157362306a36Sopenharmony_ci		goto done;
157462306a36Sopenharmony_ci	}
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci	fw_ptr = kmalloc(FIRMWARE_CHUNK_SIZE, GFP_KERNEL);
157762306a36Sopenharmony_ci	if (fw_ptr == NULL){
157862306a36Sopenharmony_ci		release_firmware(fw_entry);
157962306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
158062306a36Sopenharmony_ci			   "failed to allocate memory for firmware2 upload");
158162306a36Sopenharmony_ci		ret = -ENOMEM;
158262306a36Sopenharmony_ci		goto done;
158362306a36Sopenharmony_ci	}
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci	pipe = usb_sndbulkpipe(hdw->usb_dev, PVR2_FIRMWARE_ENDPOINT);
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	fw_done = 0;
158862306a36Sopenharmony_ci	for (fw_done = 0; fw_done < fw_len;) {
158962306a36Sopenharmony_ci		bcnt = fw_len - fw_done;
159062306a36Sopenharmony_ci		if (bcnt > FIRMWARE_CHUNK_SIZE) bcnt = FIRMWARE_CHUNK_SIZE;
159162306a36Sopenharmony_ci		memcpy(fw_ptr, fw_entry->data + fw_done, bcnt);
159262306a36Sopenharmony_ci		/* Usbsnoop log shows that we must swap bytes... */
159362306a36Sopenharmony_ci		/* Some background info: The data being swapped here is a
159462306a36Sopenharmony_ci		   firmware image destined for the mpeg encoder chip that
159562306a36Sopenharmony_ci		   lives at the other end of a USB endpoint.  The encoder
159662306a36Sopenharmony_ci		   chip always talks in 32 bit chunks and its storage is
159762306a36Sopenharmony_ci		   organized into 32 bit words.  However from the file
159862306a36Sopenharmony_ci		   system to the encoder chip everything is purely a byte
159962306a36Sopenharmony_ci		   stream.  The firmware file's contents are always 32 bit
160062306a36Sopenharmony_ci		   swapped from what the encoder expects.  Thus the need
160162306a36Sopenharmony_ci		   always exists to swap the bytes regardless of the endian
160262306a36Sopenharmony_ci		   type of the host processor and therefore swab32() makes
160362306a36Sopenharmony_ci		   the most sense. */
160462306a36Sopenharmony_ci		for (icnt = 0; icnt < bcnt/4 ; icnt++)
160562306a36Sopenharmony_ci			((u32 *)fw_ptr)[icnt] = swab32(((u32 *)fw_ptr)[icnt]);
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci		ret |= usb_bulk_msg(hdw->usb_dev, pipe, fw_ptr,bcnt,
160862306a36Sopenharmony_ci				    &actual_length, 1000);
160962306a36Sopenharmony_ci		ret |= (actual_length != bcnt);
161062306a36Sopenharmony_ci		if (ret) break;
161162306a36Sopenharmony_ci		fw_done += bcnt;
161262306a36Sopenharmony_ci	}
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_ci	trace_firmware("upload of %s : %i / %i ",
161562306a36Sopenharmony_ci		       fw_files[fwidx],fw_done,fw_len);
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci	kfree(fw_ptr);
161862306a36Sopenharmony_ci	release_firmware(fw_entry);
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	if (ret) {
162162306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
162262306a36Sopenharmony_ci			   "firmware2 upload transfer failure");
162362306a36Sopenharmony_ci		goto done;
162462306a36Sopenharmony_ci	}
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ci	/* Finish upload */
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci	ret |= pvr2_write_register(hdw, 0x9054, 0xffffffff); /*reset hw blocks*/
162962306a36Sopenharmony_ci	ret |= pvr2_write_register(hdw, 0x9058, 0xffffffe8); /*VPU ctrl*/
163062306a36Sopenharmony_ci	ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_MEMSEL | (1 << 8) | (0 << 16));
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci	if (ret) {
163362306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
163462306a36Sopenharmony_ci			   "firmware2 upload post-proc failure");
163562306a36Sopenharmony_ci	}
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci done:
163862306a36Sopenharmony_ci	if (hdw->hdw_desc->signal_routing_scheme ==
163962306a36Sopenharmony_ci	    PVR2_ROUTING_SCHEME_GOTVIEW) {
164062306a36Sopenharmony_ci		/* Ensure that GPIO 11 is set to output for GOTVIEW
164162306a36Sopenharmony_ci		   hardware. */
164262306a36Sopenharmony_ci		pvr2_hdw_gpio_chg_dir(hdw,(1 << 11),~0);
164362306a36Sopenharmony_ci	}
164462306a36Sopenharmony_ci	return ret;
164562306a36Sopenharmony_ci}
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_cistatic const char *pvr2_get_state_name(unsigned int st)
164962306a36Sopenharmony_ci{
165062306a36Sopenharmony_ci	if (st < ARRAY_SIZE(pvr2_state_names)) {
165162306a36Sopenharmony_ci		return pvr2_state_names[st];
165262306a36Sopenharmony_ci	}
165362306a36Sopenharmony_ci	return "???";
165462306a36Sopenharmony_ci}
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_cistatic int pvr2_decoder_enable(struct pvr2_hdw *hdw,int enablefl)
165762306a36Sopenharmony_ci{
165862306a36Sopenharmony_ci	/* Even though we really only care about the video decoder chip at
165962306a36Sopenharmony_ci	   this point, we'll broadcast stream on/off to all sub-devices
166062306a36Sopenharmony_ci	   anyway, just in case somebody else wants to hear the
166162306a36Sopenharmony_ci	   command... */
166262306a36Sopenharmony_ci	pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 stream=%s",
166362306a36Sopenharmony_ci		   (enablefl ? "on" : "off"));
166462306a36Sopenharmony_ci	v4l2_device_call_all(&hdw->v4l2_dev, 0, video, s_stream, enablefl);
166562306a36Sopenharmony_ci	v4l2_device_call_all(&hdw->v4l2_dev, 0, audio, s_stream, enablefl);
166662306a36Sopenharmony_ci	if (hdw->decoder_client_id) {
166762306a36Sopenharmony_ci		/* We get here if the encoder has been noticed.  Otherwise
166862306a36Sopenharmony_ci		   we'll issue a warning to the user (which should
166962306a36Sopenharmony_ci		   normally never happen). */
167062306a36Sopenharmony_ci		return 0;
167162306a36Sopenharmony_ci	}
167262306a36Sopenharmony_ci	if (!hdw->flag_decoder_missed) {
167362306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
167462306a36Sopenharmony_ci			   "***WARNING*** No decoder present");
167562306a36Sopenharmony_ci		hdw->flag_decoder_missed = !0;
167662306a36Sopenharmony_ci		trace_stbit("flag_decoder_missed",
167762306a36Sopenharmony_ci			    hdw->flag_decoder_missed);
167862306a36Sopenharmony_ci	}
167962306a36Sopenharmony_ci	return -EIO;
168062306a36Sopenharmony_ci}
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ciint pvr2_hdw_get_state(struct pvr2_hdw *hdw)
168462306a36Sopenharmony_ci{
168562306a36Sopenharmony_ci	return hdw->master_state;
168662306a36Sopenharmony_ci}
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_cistatic int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *hdw)
169062306a36Sopenharmony_ci{
169162306a36Sopenharmony_ci	if (!hdw->flag_tripped) return 0;
169262306a36Sopenharmony_ci	hdw->flag_tripped = 0;
169362306a36Sopenharmony_ci	pvr2_trace(PVR2_TRACE_ERROR_LEGS,
169462306a36Sopenharmony_ci		   "Clearing driver error status");
169562306a36Sopenharmony_ci	return !0;
169662306a36Sopenharmony_ci}
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ciint pvr2_hdw_untrip(struct pvr2_hdw *hdw)
170062306a36Sopenharmony_ci{
170162306a36Sopenharmony_ci	int fl;
170262306a36Sopenharmony_ci	LOCK_TAKE(hdw->big_lock); do {
170362306a36Sopenharmony_ci		fl = pvr2_hdw_untrip_unlocked(hdw);
170462306a36Sopenharmony_ci	} while (0); LOCK_GIVE(hdw->big_lock);
170562306a36Sopenharmony_ci	if (fl) pvr2_hdw_state_sched(hdw);
170662306a36Sopenharmony_ci	return 0;
170762306a36Sopenharmony_ci}
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ciint pvr2_hdw_get_streaming(struct pvr2_hdw *hdw)
171362306a36Sopenharmony_ci{
171462306a36Sopenharmony_ci	return hdw->state_pipeline_req != 0;
171562306a36Sopenharmony_ci}
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ciint pvr2_hdw_set_streaming(struct pvr2_hdw *hdw,int enable_flag)
171962306a36Sopenharmony_ci{
172062306a36Sopenharmony_ci	int ret,st;
172162306a36Sopenharmony_ci	LOCK_TAKE(hdw->big_lock);
172262306a36Sopenharmony_ci	pvr2_hdw_untrip_unlocked(hdw);
172362306a36Sopenharmony_ci	if (!enable_flag != !hdw->state_pipeline_req) {
172462306a36Sopenharmony_ci		hdw->state_pipeline_req = enable_flag != 0;
172562306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_START_STOP,
172662306a36Sopenharmony_ci			   "/*--TRACE_STREAM--*/ %s",
172762306a36Sopenharmony_ci			   enable_flag ? "enable" : "disable");
172862306a36Sopenharmony_ci	}
172962306a36Sopenharmony_ci	pvr2_hdw_state_sched(hdw);
173062306a36Sopenharmony_ci	LOCK_GIVE(hdw->big_lock);
173162306a36Sopenharmony_ci	if ((ret = pvr2_hdw_wait(hdw,0)) < 0) return ret;
173262306a36Sopenharmony_ci	if (enable_flag) {
173362306a36Sopenharmony_ci		while ((st = hdw->master_state) != PVR2_STATE_RUN) {
173462306a36Sopenharmony_ci			if (st != PVR2_STATE_READY) return -EIO;
173562306a36Sopenharmony_ci			if ((ret = pvr2_hdw_wait(hdw,st)) < 0) return ret;
173662306a36Sopenharmony_ci		}
173762306a36Sopenharmony_ci	}
173862306a36Sopenharmony_ci	return 0;
173962306a36Sopenharmony_ci}
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci
174262306a36Sopenharmony_ciint pvr2_hdw_set_stream_type(struct pvr2_hdw *hdw,enum pvr2_config config)
174362306a36Sopenharmony_ci{
174462306a36Sopenharmony_ci	int fl;
174562306a36Sopenharmony_ci	LOCK_TAKE(hdw->big_lock);
174662306a36Sopenharmony_ci	if ((fl = (hdw->desired_stream_type != config)) != 0) {
174762306a36Sopenharmony_ci		hdw->desired_stream_type = config;
174862306a36Sopenharmony_ci		hdw->state_pipeline_config = 0;
174962306a36Sopenharmony_ci		trace_stbit("state_pipeline_config",
175062306a36Sopenharmony_ci			    hdw->state_pipeline_config);
175162306a36Sopenharmony_ci		pvr2_hdw_state_sched(hdw);
175262306a36Sopenharmony_ci	}
175362306a36Sopenharmony_ci	LOCK_GIVE(hdw->big_lock);
175462306a36Sopenharmony_ci	if (fl) return 0;
175562306a36Sopenharmony_ci	return pvr2_hdw_wait(hdw,0);
175662306a36Sopenharmony_ci}
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_cistatic int get_default_tuner_type(struct pvr2_hdw *hdw)
176062306a36Sopenharmony_ci{
176162306a36Sopenharmony_ci	int unit_number = hdw->unit_number;
176262306a36Sopenharmony_ci	int tp = -1;
176362306a36Sopenharmony_ci	if ((unit_number >= 0) && (unit_number < PVR_NUM)) {
176462306a36Sopenharmony_ci		tp = tuner[unit_number];
176562306a36Sopenharmony_ci	}
176662306a36Sopenharmony_ci	if (tp < 0) return -EINVAL;
176762306a36Sopenharmony_ci	hdw->tuner_type = tp;
176862306a36Sopenharmony_ci	hdw->tuner_updated = !0;
176962306a36Sopenharmony_ci	return 0;
177062306a36Sopenharmony_ci}
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_cistatic v4l2_std_id get_default_standard(struct pvr2_hdw *hdw)
177462306a36Sopenharmony_ci{
177562306a36Sopenharmony_ci	int unit_number = hdw->unit_number;
177662306a36Sopenharmony_ci	int tp = 0;
177762306a36Sopenharmony_ci	if ((unit_number >= 0) && (unit_number < PVR_NUM)) {
177862306a36Sopenharmony_ci		tp = video_std[unit_number];
177962306a36Sopenharmony_ci		if (tp) return tp;
178062306a36Sopenharmony_ci	}
178162306a36Sopenharmony_ci	return 0;
178262306a36Sopenharmony_ci}
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_cistatic unsigned int get_default_error_tolerance(struct pvr2_hdw *hdw)
178662306a36Sopenharmony_ci{
178762306a36Sopenharmony_ci	int unit_number = hdw->unit_number;
178862306a36Sopenharmony_ci	int tp = 0;
178962306a36Sopenharmony_ci	if ((unit_number >= 0) && (unit_number < PVR_NUM)) {
179062306a36Sopenharmony_ci		tp = tolerance[unit_number];
179162306a36Sopenharmony_ci	}
179262306a36Sopenharmony_ci	return tp;
179362306a36Sopenharmony_ci}
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci
179662306a36Sopenharmony_cistatic int pvr2_hdw_check_firmware(struct pvr2_hdw *hdw)
179762306a36Sopenharmony_ci{
179862306a36Sopenharmony_ci	/* Try a harmless request to fetch the eeprom's address over
179962306a36Sopenharmony_ci	   endpoint 1.  See what happens.  Only the full FX2 image can
180062306a36Sopenharmony_ci	   respond to this.  If this probe fails then likely the FX2
180162306a36Sopenharmony_ci	   firmware needs be loaded. */
180262306a36Sopenharmony_ci	int result;
180362306a36Sopenharmony_ci	LOCK_TAKE(hdw->ctl_lock); do {
180462306a36Sopenharmony_ci		hdw->cmd_buffer[0] = FX2CMD_GET_EEPROM_ADDR;
180562306a36Sopenharmony_ci		result = pvr2_send_request_ex(hdw,HZ*1,!0,
180662306a36Sopenharmony_ci					   hdw->cmd_buffer,1,
180762306a36Sopenharmony_ci					   hdw->cmd_buffer,1);
180862306a36Sopenharmony_ci		if (result < 0) break;
180962306a36Sopenharmony_ci	} while(0); LOCK_GIVE(hdw->ctl_lock);
181062306a36Sopenharmony_ci	if (result) {
181162306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INIT,
181262306a36Sopenharmony_ci			   "Probe of device endpoint 1 result status %d",
181362306a36Sopenharmony_ci			   result);
181462306a36Sopenharmony_ci	} else {
181562306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INIT,
181662306a36Sopenharmony_ci			   "Probe of device endpoint 1 succeeded");
181762306a36Sopenharmony_ci	}
181862306a36Sopenharmony_ci	return result == 0;
181962306a36Sopenharmony_ci}
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_cistruct pvr2_std_hack {
182262306a36Sopenharmony_ci	v4l2_std_id pat;  /* Pattern to match */
182362306a36Sopenharmony_ci	v4l2_std_id msk;  /* Which bits we care about */
182462306a36Sopenharmony_ci	v4l2_std_id std;  /* What additional standards or default to set */
182562306a36Sopenharmony_ci};
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci/* This data structure labels specific combinations of standards from
182862306a36Sopenharmony_ci   tveeprom that we'll try to recognize.  If we recognize one, then assume
182962306a36Sopenharmony_ci   a specified default standard to use.  This is here because tveeprom only
183062306a36Sopenharmony_ci   tells us about available standards not the intended default standard (if
183162306a36Sopenharmony_ci   any) for the device in question.  We guess the default based on what has
183262306a36Sopenharmony_ci   been reported as available.  Note that this is only for guessing a
183362306a36Sopenharmony_ci   default - which can always be overridden explicitly - and if the user
183462306a36Sopenharmony_ci   has otherwise named a default then that default will always be used in
183562306a36Sopenharmony_ci   place of this table. */
183662306a36Sopenharmony_cistatic const struct pvr2_std_hack std_eeprom_maps[] = {
183762306a36Sopenharmony_ci	{	/* PAL(B/G) */
183862306a36Sopenharmony_ci		.pat = V4L2_STD_B|V4L2_STD_GH,
183962306a36Sopenharmony_ci		.std = V4L2_STD_PAL_B|V4L2_STD_PAL_B1|V4L2_STD_PAL_G,
184062306a36Sopenharmony_ci	},
184162306a36Sopenharmony_ci	{	/* NTSC(M) */
184262306a36Sopenharmony_ci		.pat = V4L2_STD_MN,
184362306a36Sopenharmony_ci		.std = V4L2_STD_NTSC_M,
184462306a36Sopenharmony_ci	},
184562306a36Sopenharmony_ci	{	/* PAL(I) */
184662306a36Sopenharmony_ci		.pat = V4L2_STD_PAL_I,
184762306a36Sopenharmony_ci		.std = V4L2_STD_PAL_I,
184862306a36Sopenharmony_ci	},
184962306a36Sopenharmony_ci	{	/* SECAM(L/L') */
185062306a36Sopenharmony_ci		.pat = V4L2_STD_SECAM_L|V4L2_STD_SECAM_LC,
185162306a36Sopenharmony_ci		.std = V4L2_STD_SECAM_L|V4L2_STD_SECAM_LC,
185262306a36Sopenharmony_ci	},
185362306a36Sopenharmony_ci	{	/* PAL(D/D1/K) */
185462306a36Sopenharmony_ci		.pat = V4L2_STD_DK,
185562306a36Sopenharmony_ci		.std = V4L2_STD_PAL_D|V4L2_STD_PAL_D1|V4L2_STD_PAL_K,
185662306a36Sopenharmony_ci	},
185762306a36Sopenharmony_ci};
185862306a36Sopenharmony_ci
185962306a36Sopenharmony_cistatic void pvr2_hdw_setup_std(struct pvr2_hdw *hdw)
186062306a36Sopenharmony_ci{
186162306a36Sopenharmony_ci	char buf[40];
186262306a36Sopenharmony_ci	unsigned int bcnt;
186362306a36Sopenharmony_ci	v4l2_std_id std1,std2,std3;
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_ci	std1 = get_default_standard(hdw);
186662306a36Sopenharmony_ci	std3 = std1 ? 0 : hdw->hdw_desc->default_std_mask;
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_ci	bcnt = pvr2_std_id_to_str(buf,sizeof(buf),hdw->std_mask_eeprom);
186962306a36Sopenharmony_ci	pvr2_trace(PVR2_TRACE_STD,
187062306a36Sopenharmony_ci		   "Supported video standard(s) reported available in hardware: %.*s",
187162306a36Sopenharmony_ci		   bcnt,buf);
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_ci	hdw->std_mask_avail = hdw->std_mask_eeprom;
187462306a36Sopenharmony_ci
187562306a36Sopenharmony_ci	std2 = (std1|std3) & ~hdw->std_mask_avail;
187662306a36Sopenharmony_ci	if (std2) {
187762306a36Sopenharmony_ci		bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std2);
187862306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_STD,
187962306a36Sopenharmony_ci			   "Expanding supported video standards to include: %.*s",
188062306a36Sopenharmony_ci			   bcnt,buf);
188162306a36Sopenharmony_ci		hdw->std_mask_avail |= std2;
188262306a36Sopenharmony_ci	}
188362306a36Sopenharmony_ci
188462306a36Sopenharmony_ci	hdw->std_info_cur.def.type_bitmask.valid_bits = hdw->std_mask_avail;
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_ci	if (std1) {
188762306a36Sopenharmony_ci		bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std1);
188862306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_STD,
188962306a36Sopenharmony_ci			   "Initial video standard forced to %.*s",
189062306a36Sopenharmony_ci			   bcnt,buf);
189162306a36Sopenharmony_ci		hdw->std_mask_cur = std1;
189262306a36Sopenharmony_ci		hdw->std_dirty = !0;
189362306a36Sopenharmony_ci		return;
189462306a36Sopenharmony_ci	}
189562306a36Sopenharmony_ci	if (std3) {
189662306a36Sopenharmony_ci		bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std3);
189762306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_STD,
189862306a36Sopenharmony_ci			   "Initial video standard (determined by device type): %.*s",
189962306a36Sopenharmony_ci			   bcnt, buf);
190062306a36Sopenharmony_ci		hdw->std_mask_cur = std3;
190162306a36Sopenharmony_ci		hdw->std_dirty = !0;
190262306a36Sopenharmony_ci		return;
190362306a36Sopenharmony_ci	}
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ci	{
190662306a36Sopenharmony_ci		unsigned int idx;
190762306a36Sopenharmony_ci		for (idx = 0; idx < ARRAY_SIZE(std_eeprom_maps); idx++) {
190862306a36Sopenharmony_ci			if (std_eeprom_maps[idx].msk ?
190962306a36Sopenharmony_ci			    ((std_eeprom_maps[idx].pat ^
191062306a36Sopenharmony_ci			     hdw->std_mask_eeprom) &
191162306a36Sopenharmony_ci			     std_eeprom_maps[idx].msk) :
191262306a36Sopenharmony_ci			    (std_eeprom_maps[idx].pat !=
191362306a36Sopenharmony_ci			     hdw->std_mask_eeprom)) continue;
191462306a36Sopenharmony_ci			bcnt = pvr2_std_id_to_str(buf,sizeof(buf),
191562306a36Sopenharmony_ci						  std_eeprom_maps[idx].std);
191662306a36Sopenharmony_ci			pvr2_trace(PVR2_TRACE_STD,
191762306a36Sopenharmony_ci				   "Initial video standard guessed as %.*s",
191862306a36Sopenharmony_ci				   bcnt,buf);
191962306a36Sopenharmony_ci			hdw->std_mask_cur = std_eeprom_maps[idx].std;
192062306a36Sopenharmony_ci			hdw->std_dirty = !0;
192162306a36Sopenharmony_ci			return;
192262306a36Sopenharmony_ci		}
192362306a36Sopenharmony_ci	}
192462306a36Sopenharmony_ci
192562306a36Sopenharmony_ci}
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_ci
192862306a36Sopenharmony_cistatic unsigned int pvr2_copy_i2c_addr_list(
192962306a36Sopenharmony_ci	unsigned short *dst, const unsigned char *src,
193062306a36Sopenharmony_ci	unsigned int dst_max)
193162306a36Sopenharmony_ci{
193262306a36Sopenharmony_ci	unsigned int cnt = 0;
193362306a36Sopenharmony_ci	if (!src) return 0;
193462306a36Sopenharmony_ci	while (src[cnt] && (cnt + 1) < dst_max) {
193562306a36Sopenharmony_ci		dst[cnt] = src[cnt];
193662306a36Sopenharmony_ci		cnt++;
193762306a36Sopenharmony_ci	}
193862306a36Sopenharmony_ci	dst[cnt] = I2C_CLIENT_END;
193962306a36Sopenharmony_ci	return cnt;
194062306a36Sopenharmony_ci}
194162306a36Sopenharmony_ci
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_cistatic void pvr2_hdw_cx25840_vbi_hack(struct pvr2_hdw *hdw)
194462306a36Sopenharmony_ci{
194562306a36Sopenharmony_ci	/*
194662306a36Sopenharmony_ci	  Mike Isely <isely@pobox.com> 19-Nov-2006 - This bit of nuttiness
194762306a36Sopenharmony_ci	  for cx25840 causes that module to correctly set up its video
194862306a36Sopenharmony_ci	  scaling.  This is really a problem in the cx25840 module itself,
194962306a36Sopenharmony_ci	  but we work around it here.  The problem has not been seen in
195062306a36Sopenharmony_ci	  ivtv because there VBI is supported and set up.  We don't do VBI
195162306a36Sopenharmony_ci	  here (at least not yet) and thus we never attempted to even set
195262306a36Sopenharmony_ci	  it up.
195362306a36Sopenharmony_ci	*/
195462306a36Sopenharmony_ci	struct v4l2_format fmt;
195562306a36Sopenharmony_ci	if (hdw->decoder_client_id != PVR2_CLIENT_ID_CX25840) {
195662306a36Sopenharmony_ci		/* We're not using a cx25840 so don't enable the hack */
195762306a36Sopenharmony_ci		return;
195862306a36Sopenharmony_ci	}
195962306a36Sopenharmony_ci
196062306a36Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INIT,
196162306a36Sopenharmony_ci		   "Module ID %u: Executing cx25840 VBI hack",
196262306a36Sopenharmony_ci		   hdw->decoder_client_id);
196362306a36Sopenharmony_ci	memset(&fmt, 0, sizeof(fmt));
196462306a36Sopenharmony_ci	fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
196562306a36Sopenharmony_ci	fmt.fmt.sliced.service_lines[0][21] = V4L2_SLICED_CAPTION_525;
196662306a36Sopenharmony_ci	fmt.fmt.sliced.service_lines[1][21] = V4L2_SLICED_CAPTION_525;
196762306a36Sopenharmony_ci	v4l2_device_call_all(&hdw->v4l2_dev, hdw->decoder_client_id,
196862306a36Sopenharmony_ci			     vbi, s_sliced_fmt, &fmt.fmt.sliced);
196962306a36Sopenharmony_ci}
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_ci
197262306a36Sopenharmony_cistatic int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw,
197362306a36Sopenharmony_ci				const struct pvr2_device_client_desc *cd)
197462306a36Sopenharmony_ci{
197562306a36Sopenharmony_ci	const char *fname;
197662306a36Sopenharmony_ci	unsigned char mid;
197762306a36Sopenharmony_ci	struct v4l2_subdev *sd;
197862306a36Sopenharmony_ci	unsigned int i2ccnt;
197962306a36Sopenharmony_ci	const unsigned char *p;
198062306a36Sopenharmony_ci	/* Arbitrary count - max # i2c addresses we will probe */
198162306a36Sopenharmony_ci	unsigned short i2caddr[25];
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_ci	mid = cd->module_id;
198462306a36Sopenharmony_ci	fname = (mid < ARRAY_SIZE(module_names)) ? module_names[mid] : NULL;
198562306a36Sopenharmony_ci	if (!fname) {
198662306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
198762306a36Sopenharmony_ci			   "Module ID %u for device %s has no name?  The driver might have a configuration problem.",
198862306a36Sopenharmony_ci			   mid,
198962306a36Sopenharmony_ci			   hdw->hdw_desc->description);
199062306a36Sopenharmony_ci		return -EINVAL;
199162306a36Sopenharmony_ci	}
199262306a36Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INIT,
199362306a36Sopenharmony_ci		   "Module ID %u (%s) for device %s being loaded...",
199462306a36Sopenharmony_ci		   mid, fname,
199562306a36Sopenharmony_ci		   hdw->hdw_desc->description);
199662306a36Sopenharmony_ci
199762306a36Sopenharmony_ci	i2ccnt = pvr2_copy_i2c_addr_list(i2caddr, cd->i2c_address_list,
199862306a36Sopenharmony_ci					 ARRAY_SIZE(i2caddr));
199962306a36Sopenharmony_ci	if (!i2ccnt && ((p = (mid < ARRAY_SIZE(module_i2c_addresses)) ?
200062306a36Sopenharmony_ci			 module_i2c_addresses[mid] : NULL) != NULL)) {
200162306a36Sopenharmony_ci		/* Second chance: Try default i2c address list */
200262306a36Sopenharmony_ci		i2ccnt = pvr2_copy_i2c_addr_list(i2caddr, p,
200362306a36Sopenharmony_ci						 ARRAY_SIZE(i2caddr));
200462306a36Sopenharmony_ci		if (i2ccnt) {
200562306a36Sopenharmony_ci			pvr2_trace(PVR2_TRACE_INIT,
200662306a36Sopenharmony_ci				   "Module ID %u: Using default i2c address list",
200762306a36Sopenharmony_ci				   mid);
200862306a36Sopenharmony_ci		}
200962306a36Sopenharmony_ci	}
201062306a36Sopenharmony_ci
201162306a36Sopenharmony_ci	if (!i2ccnt) {
201262306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
201362306a36Sopenharmony_ci			   "Module ID %u (%s) for device %s: No i2c addresses.	The driver might have a configuration problem.",
201462306a36Sopenharmony_ci			   mid, fname, hdw->hdw_desc->description);
201562306a36Sopenharmony_ci		return -EINVAL;
201662306a36Sopenharmony_ci	}
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_ci	if (i2ccnt == 1) {
201962306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INIT,
202062306a36Sopenharmony_ci			   "Module ID %u: Setting up with specified i2c address 0x%x",
202162306a36Sopenharmony_ci			   mid, i2caddr[0]);
202262306a36Sopenharmony_ci		sd = v4l2_i2c_new_subdev(&hdw->v4l2_dev, &hdw->i2c_adap,
202362306a36Sopenharmony_ci					 fname, i2caddr[0], NULL);
202462306a36Sopenharmony_ci	} else {
202562306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INIT,
202662306a36Sopenharmony_ci			   "Module ID %u: Setting up with address probe list",
202762306a36Sopenharmony_ci			   mid);
202862306a36Sopenharmony_ci		sd = v4l2_i2c_new_subdev(&hdw->v4l2_dev, &hdw->i2c_adap,
202962306a36Sopenharmony_ci					 fname, 0, i2caddr);
203062306a36Sopenharmony_ci	}
203162306a36Sopenharmony_ci
203262306a36Sopenharmony_ci	if (!sd) {
203362306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
203462306a36Sopenharmony_ci			   "Module ID %u (%s) for device %s failed to load.  Possible missing sub-device kernel module or initialization failure within module.",
203562306a36Sopenharmony_ci			   mid, fname, hdw->hdw_desc->description);
203662306a36Sopenharmony_ci		return -EIO;
203762306a36Sopenharmony_ci	}
203862306a36Sopenharmony_ci
203962306a36Sopenharmony_ci	/* Tag this sub-device instance with the module ID we know about.
204062306a36Sopenharmony_ci	   In other places we'll use that tag to determine if the instance
204162306a36Sopenharmony_ci	   requires special handling. */
204262306a36Sopenharmony_ci	sd->grp_id = mid;
204362306a36Sopenharmony_ci
204462306a36Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INFO, "Attached sub-driver %s", fname);
204562306a36Sopenharmony_ci
204662306a36Sopenharmony_ci
204762306a36Sopenharmony_ci	/* client-specific setup... */
204862306a36Sopenharmony_ci	switch (mid) {
204962306a36Sopenharmony_ci	case PVR2_CLIENT_ID_CX25840:
205062306a36Sopenharmony_ci	case PVR2_CLIENT_ID_SAA7115:
205162306a36Sopenharmony_ci		hdw->decoder_client_id = mid;
205262306a36Sopenharmony_ci		break;
205362306a36Sopenharmony_ci	default: break;
205462306a36Sopenharmony_ci	}
205562306a36Sopenharmony_ci
205662306a36Sopenharmony_ci	return 0;
205762306a36Sopenharmony_ci}
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci
206062306a36Sopenharmony_cistatic void pvr2_hdw_load_modules(struct pvr2_hdw *hdw)
206162306a36Sopenharmony_ci{
206262306a36Sopenharmony_ci	unsigned int idx;
206362306a36Sopenharmony_ci	const struct pvr2_string_table *cm;
206462306a36Sopenharmony_ci	const struct pvr2_device_client_table *ct;
206562306a36Sopenharmony_ci	int okFl = !0;
206662306a36Sopenharmony_ci
206762306a36Sopenharmony_ci	cm = &hdw->hdw_desc->client_modules;
206862306a36Sopenharmony_ci	for (idx = 0; idx < cm->cnt; idx++) {
206962306a36Sopenharmony_ci		request_module(cm->lst[idx]);
207062306a36Sopenharmony_ci	}
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_ci	ct = &hdw->hdw_desc->client_table;
207362306a36Sopenharmony_ci	for (idx = 0; idx < ct->cnt; idx++) {
207462306a36Sopenharmony_ci		if (pvr2_hdw_load_subdev(hdw, &ct->lst[idx]) < 0) okFl = 0;
207562306a36Sopenharmony_ci	}
207662306a36Sopenharmony_ci	if (!okFl) {
207762306a36Sopenharmony_ci		hdw->flag_modulefail = !0;
207862306a36Sopenharmony_ci		pvr2_hdw_render_useless(hdw);
207962306a36Sopenharmony_ci	}
208062306a36Sopenharmony_ci}
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_ci
208362306a36Sopenharmony_cistatic void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
208462306a36Sopenharmony_ci{
208562306a36Sopenharmony_ci	int ret;
208662306a36Sopenharmony_ci	unsigned int idx;
208762306a36Sopenharmony_ci	struct pvr2_ctrl *cptr;
208862306a36Sopenharmony_ci	int reloadFl = 0;
208962306a36Sopenharmony_ci	if (hdw->hdw_desc->fx2_firmware.cnt) {
209062306a36Sopenharmony_ci		if (!reloadFl) {
209162306a36Sopenharmony_ci			reloadFl =
209262306a36Sopenharmony_ci				(hdw->usb_intf->cur_altsetting->desc.bNumEndpoints
209362306a36Sopenharmony_ci				 == 0);
209462306a36Sopenharmony_ci			if (reloadFl) {
209562306a36Sopenharmony_ci				pvr2_trace(PVR2_TRACE_INIT,
209662306a36Sopenharmony_ci					   "USB endpoint config looks strange; possibly firmware needs to be loaded");
209762306a36Sopenharmony_ci			}
209862306a36Sopenharmony_ci		}
209962306a36Sopenharmony_ci		if (!reloadFl) {
210062306a36Sopenharmony_ci			reloadFl = !pvr2_hdw_check_firmware(hdw);
210162306a36Sopenharmony_ci			if (reloadFl) {
210262306a36Sopenharmony_ci				pvr2_trace(PVR2_TRACE_INIT,
210362306a36Sopenharmony_ci					   "Check for FX2 firmware failed; possibly firmware needs to be loaded");
210462306a36Sopenharmony_ci			}
210562306a36Sopenharmony_ci		}
210662306a36Sopenharmony_ci		if (reloadFl) {
210762306a36Sopenharmony_ci			if (pvr2_upload_firmware1(hdw) != 0) {
210862306a36Sopenharmony_ci				pvr2_trace(PVR2_TRACE_ERROR_LEGS,
210962306a36Sopenharmony_ci					   "Failure uploading firmware1");
211062306a36Sopenharmony_ci			}
211162306a36Sopenharmony_ci			return;
211262306a36Sopenharmony_ci		}
211362306a36Sopenharmony_ci	}
211462306a36Sopenharmony_ci	hdw->fw1_state = FW1_STATE_OK;
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_ci	if (!pvr2_hdw_dev_ok(hdw)) return;
211762306a36Sopenharmony_ci
211862306a36Sopenharmony_ci	hdw->force_dirty = !0;
211962306a36Sopenharmony_ci
212062306a36Sopenharmony_ci	if (!hdw->hdw_desc->flag_no_powerup) {
212162306a36Sopenharmony_ci		pvr2_hdw_cmd_powerup(hdw);
212262306a36Sopenharmony_ci		if (!pvr2_hdw_dev_ok(hdw)) return;
212362306a36Sopenharmony_ci	}
212462306a36Sopenharmony_ci
212562306a36Sopenharmony_ci	/* Take the IR chip out of reset, if appropriate */
212662306a36Sopenharmony_ci	if (hdw->ir_scheme_active == PVR2_IR_SCHEME_ZILOG) {
212762306a36Sopenharmony_ci		pvr2_issue_simple_cmd(hdw,
212862306a36Sopenharmony_ci				      FX2CMD_HCW_ZILOG_RESET |
212962306a36Sopenharmony_ci				      (1 << 8) |
213062306a36Sopenharmony_ci				      ((0) << 16));
213162306a36Sopenharmony_ci	}
213262306a36Sopenharmony_ci
213362306a36Sopenharmony_ci	/* This step MUST happen after the earlier powerup step */
213462306a36Sopenharmony_ci	pvr2_i2c_core_init(hdw);
213562306a36Sopenharmony_ci	if (!pvr2_hdw_dev_ok(hdw)) return;
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_ci	/* Reset demod only on Hauppauge 160xxx platform */
213862306a36Sopenharmony_ci	if (le16_to_cpu(hdw->usb_dev->descriptor.idVendor) == 0x2040 &&
213962306a36Sopenharmony_ci	    (le16_to_cpu(hdw->usb_dev->descriptor.idProduct) == 0x7502 ||
214062306a36Sopenharmony_ci	     le16_to_cpu(hdw->usb_dev->descriptor.idProduct) == 0x7510)) {
214162306a36Sopenharmony_ci		pr_info("%s(): resetting 160xxx demod\n", __func__);
214262306a36Sopenharmony_ci		/* TODO: not sure this is proper place to reset once only */
214362306a36Sopenharmony_ci		pvr2_issue_simple_cmd(hdw,
214462306a36Sopenharmony_ci				      FX2CMD_HCW_DEMOD_RESET_PIN |
214562306a36Sopenharmony_ci				      (1 << 8) |
214662306a36Sopenharmony_ci				      ((0) << 16));
214762306a36Sopenharmony_ci		usleep_range(10000, 10500);
214862306a36Sopenharmony_ci		pvr2_issue_simple_cmd(hdw,
214962306a36Sopenharmony_ci				      FX2CMD_HCW_DEMOD_RESET_PIN |
215062306a36Sopenharmony_ci				      (1 << 8) |
215162306a36Sopenharmony_ci				      ((1) << 16));
215262306a36Sopenharmony_ci		usleep_range(10000, 10500);
215362306a36Sopenharmony_ci	}
215462306a36Sopenharmony_ci
215562306a36Sopenharmony_ci	pvr2_hdw_load_modules(hdw);
215662306a36Sopenharmony_ci	if (!pvr2_hdw_dev_ok(hdw)) return;
215762306a36Sopenharmony_ci
215862306a36Sopenharmony_ci	v4l2_device_call_all(&hdw->v4l2_dev, 0, core, load_fw);
215962306a36Sopenharmony_ci
216062306a36Sopenharmony_ci	for (idx = 0; idx < CTRLDEF_COUNT; idx++) {
216162306a36Sopenharmony_ci		cptr = hdw->controls + idx;
216262306a36Sopenharmony_ci		if (cptr->info->skip_init) continue;
216362306a36Sopenharmony_ci		if (!cptr->info->set_value) continue;
216462306a36Sopenharmony_ci		cptr->info->set_value(cptr,~0,cptr->info->default_value);
216562306a36Sopenharmony_ci	}
216662306a36Sopenharmony_ci
216762306a36Sopenharmony_ci	pvr2_hdw_cx25840_vbi_hack(hdw);
216862306a36Sopenharmony_ci
216962306a36Sopenharmony_ci	/* Set up special default values for the television and radio
217062306a36Sopenharmony_ci	   frequencies here.  It's not really important what these defaults
217162306a36Sopenharmony_ci	   are, but I set them to something usable in the Chicago area just
217262306a36Sopenharmony_ci	   to make driver testing a little easier. */
217362306a36Sopenharmony_ci
217462306a36Sopenharmony_ci	hdw->freqValTelevision = default_tv_freq;
217562306a36Sopenharmony_ci	hdw->freqValRadio = default_radio_freq;
217662306a36Sopenharmony_ci
217762306a36Sopenharmony_ci	// Do not use pvr2_reset_ctl_endpoints() here.  It is not
217862306a36Sopenharmony_ci	// thread-safe against the normal pvr2_send_request() mechanism.
217962306a36Sopenharmony_ci	// (We should make it thread safe).
218062306a36Sopenharmony_ci
218162306a36Sopenharmony_ci	if (hdw->hdw_desc->flag_has_hauppauge_rom) {
218262306a36Sopenharmony_ci		ret = pvr2_hdw_get_eeprom_addr(hdw);
218362306a36Sopenharmony_ci		if (!pvr2_hdw_dev_ok(hdw)) return;
218462306a36Sopenharmony_ci		if (ret < 0) {
218562306a36Sopenharmony_ci			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
218662306a36Sopenharmony_ci				   "Unable to determine location of eeprom, skipping");
218762306a36Sopenharmony_ci		} else {
218862306a36Sopenharmony_ci			hdw->eeprom_addr = ret;
218962306a36Sopenharmony_ci			pvr2_eeprom_analyze(hdw);
219062306a36Sopenharmony_ci			if (!pvr2_hdw_dev_ok(hdw)) return;
219162306a36Sopenharmony_ci		}
219262306a36Sopenharmony_ci	} else {
219362306a36Sopenharmony_ci		hdw->tuner_type = hdw->hdw_desc->default_tuner_type;
219462306a36Sopenharmony_ci		hdw->tuner_updated = !0;
219562306a36Sopenharmony_ci		hdw->std_mask_eeprom = V4L2_STD_ALL;
219662306a36Sopenharmony_ci	}
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_ci	if (hdw->serial_number) {
219962306a36Sopenharmony_ci		idx = scnprintf(hdw->identifier, sizeof(hdw->identifier) - 1,
220062306a36Sopenharmony_ci				"sn-%lu", hdw->serial_number);
220162306a36Sopenharmony_ci	} else if (hdw->unit_number >= 0) {
220262306a36Sopenharmony_ci		idx = scnprintf(hdw->identifier, sizeof(hdw->identifier) - 1,
220362306a36Sopenharmony_ci				"unit-%c",
220462306a36Sopenharmony_ci				hdw->unit_number + 'a');
220562306a36Sopenharmony_ci	} else {
220662306a36Sopenharmony_ci		idx = scnprintf(hdw->identifier, sizeof(hdw->identifier) - 1,
220762306a36Sopenharmony_ci				"unit-??");
220862306a36Sopenharmony_ci	}
220962306a36Sopenharmony_ci	hdw->identifier[idx] = 0;
221062306a36Sopenharmony_ci
221162306a36Sopenharmony_ci	pvr2_hdw_setup_std(hdw);
221262306a36Sopenharmony_ci
221362306a36Sopenharmony_ci	if (!get_default_tuner_type(hdw)) {
221462306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INIT,
221562306a36Sopenharmony_ci			   "pvr2_hdw_setup: Tuner type overridden to %d",
221662306a36Sopenharmony_ci			   hdw->tuner_type);
221762306a36Sopenharmony_ci	}
221862306a36Sopenharmony_ci
221962306a36Sopenharmony_ci
222062306a36Sopenharmony_ci	if (!pvr2_hdw_dev_ok(hdw)) return;
222162306a36Sopenharmony_ci
222262306a36Sopenharmony_ci	if (hdw->hdw_desc->signal_routing_scheme ==
222362306a36Sopenharmony_ci	    PVR2_ROUTING_SCHEME_GOTVIEW) {
222462306a36Sopenharmony_ci		/* Ensure that GPIO 11 is set to output for GOTVIEW
222562306a36Sopenharmony_ci		   hardware. */
222662306a36Sopenharmony_ci		pvr2_hdw_gpio_chg_dir(hdw,(1 << 11),~0);
222762306a36Sopenharmony_ci	}
222862306a36Sopenharmony_ci
222962306a36Sopenharmony_ci	pvr2_hdw_commit_setup(hdw);
223062306a36Sopenharmony_ci
223162306a36Sopenharmony_ci	hdw->vid_stream = pvr2_stream_create();
223262306a36Sopenharmony_ci	if (!pvr2_hdw_dev_ok(hdw)) return;
223362306a36Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INIT,
223462306a36Sopenharmony_ci		   "pvr2_hdw_setup: video stream is %p",hdw->vid_stream);
223562306a36Sopenharmony_ci	if (hdw->vid_stream) {
223662306a36Sopenharmony_ci		idx = get_default_error_tolerance(hdw);
223762306a36Sopenharmony_ci		if (idx) {
223862306a36Sopenharmony_ci			pvr2_trace(PVR2_TRACE_INIT,
223962306a36Sopenharmony_ci				   "pvr2_hdw_setup: video stream %p setting tolerance %u",
224062306a36Sopenharmony_ci				   hdw->vid_stream,idx);
224162306a36Sopenharmony_ci		}
224262306a36Sopenharmony_ci		pvr2_stream_setup(hdw->vid_stream,hdw->usb_dev,
224362306a36Sopenharmony_ci				  PVR2_VID_ENDPOINT,idx);
224462306a36Sopenharmony_ci	}
224562306a36Sopenharmony_ci
224662306a36Sopenharmony_ci	if (!pvr2_hdw_dev_ok(hdw)) return;
224762306a36Sopenharmony_ci
224862306a36Sopenharmony_ci	hdw->flag_init_ok = !0;
224962306a36Sopenharmony_ci
225062306a36Sopenharmony_ci	pvr2_hdw_state_sched(hdw);
225162306a36Sopenharmony_ci}
225262306a36Sopenharmony_ci
225362306a36Sopenharmony_ci
225462306a36Sopenharmony_ci/* Set up the structure and attempt to put the device into a usable state.
225562306a36Sopenharmony_ci   This can be a time-consuming operation, which is why it is not done
225662306a36Sopenharmony_ci   internally as part of the create() step. */
225762306a36Sopenharmony_cistatic void pvr2_hdw_setup(struct pvr2_hdw *hdw)
225862306a36Sopenharmony_ci{
225962306a36Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_setup(hdw=%p) begin",hdw);
226062306a36Sopenharmony_ci	do {
226162306a36Sopenharmony_ci		pvr2_hdw_setup_low(hdw);
226262306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INIT,
226362306a36Sopenharmony_ci			   "pvr2_hdw_setup(hdw=%p) done, ok=%d init_ok=%d",
226462306a36Sopenharmony_ci			   hdw,pvr2_hdw_dev_ok(hdw),hdw->flag_init_ok);
226562306a36Sopenharmony_ci		if (pvr2_hdw_dev_ok(hdw)) {
226662306a36Sopenharmony_ci			if (hdw->flag_init_ok) {
226762306a36Sopenharmony_ci				pvr2_trace(
226862306a36Sopenharmony_ci					PVR2_TRACE_INFO,
226962306a36Sopenharmony_ci					"Device initialization completed successfully.");
227062306a36Sopenharmony_ci				break;
227162306a36Sopenharmony_ci			}
227262306a36Sopenharmony_ci			if (hdw->fw1_state == FW1_STATE_RELOAD) {
227362306a36Sopenharmony_ci				pvr2_trace(
227462306a36Sopenharmony_ci					PVR2_TRACE_INFO,
227562306a36Sopenharmony_ci					"Device microcontroller firmware (re)loaded; it should now reset and reconnect.");
227662306a36Sopenharmony_ci				break;
227762306a36Sopenharmony_ci			}
227862306a36Sopenharmony_ci			pvr2_trace(
227962306a36Sopenharmony_ci				PVR2_TRACE_ERROR_LEGS,
228062306a36Sopenharmony_ci				"Device initialization was not successful.");
228162306a36Sopenharmony_ci			if (hdw->fw1_state == FW1_STATE_MISSING) {
228262306a36Sopenharmony_ci				pvr2_trace(
228362306a36Sopenharmony_ci					PVR2_TRACE_ERROR_LEGS,
228462306a36Sopenharmony_ci					"Giving up since device microcontroller firmware appears to be missing.");
228562306a36Sopenharmony_ci				break;
228662306a36Sopenharmony_ci			}
228762306a36Sopenharmony_ci		}
228862306a36Sopenharmony_ci		if (hdw->flag_modulefail) {
228962306a36Sopenharmony_ci			pvr2_trace(
229062306a36Sopenharmony_ci				PVR2_TRACE_ERROR_LEGS,
229162306a36Sopenharmony_ci				"***WARNING*** pvrusb2 driver initialization failed due to the failure of one or more sub-device kernel modules.");
229262306a36Sopenharmony_ci			pvr2_trace(
229362306a36Sopenharmony_ci				PVR2_TRACE_ERROR_LEGS,
229462306a36Sopenharmony_ci				"You need to resolve the failing condition before this driver can function.  There should be some earlier messages giving more information about the problem.");
229562306a36Sopenharmony_ci			break;
229662306a36Sopenharmony_ci		}
229762306a36Sopenharmony_ci		if (procreload) {
229862306a36Sopenharmony_ci			pvr2_trace(
229962306a36Sopenharmony_ci				PVR2_TRACE_ERROR_LEGS,
230062306a36Sopenharmony_ci				"Attempting pvrusb2 recovery by reloading primary firmware.");
230162306a36Sopenharmony_ci			pvr2_trace(
230262306a36Sopenharmony_ci				PVR2_TRACE_ERROR_LEGS,
230362306a36Sopenharmony_ci				"If this works, device should disconnect and reconnect in a sane state.");
230462306a36Sopenharmony_ci			hdw->fw1_state = FW1_STATE_UNKNOWN;
230562306a36Sopenharmony_ci			pvr2_upload_firmware1(hdw);
230662306a36Sopenharmony_ci		} else {
230762306a36Sopenharmony_ci			pvr2_trace(
230862306a36Sopenharmony_ci				PVR2_TRACE_ERROR_LEGS,
230962306a36Sopenharmony_ci				"***WARNING*** pvrusb2 device hardware appears to be jammed and I can't clear it.");
231062306a36Sopenharmony_ci			pvr2_trace(
231162306a36Sopenharmony_ci				PVR2_TRACE_ERROR_LEGS,
231262306a36Sopenharmony_ci				"You might need to power cycle the pvrusb2 device in order to recover.");
231362306a36Sopenharmony_ci		}
231462306a36Sopenharmony_ci	} while (0);
231562306a36Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_setup(hdw=%p) end",hdw);
231662306a36Sopenharmony_ci}
231762306a36Sopenharmony_ci
231862306a36Sopenharmony_ci
231962306a36Sopenharmony_ci/* Perform second stage initialization.  Set callback pointer first so that
232062306a36Sopenharmony_ci   we can avoid a possible initialization race (if the kernel thread runs
232162306a36Sopenharmony_ci   before the callback has been set). */
232262306a36Sopenharmony_ciint pvr2_hdw_initialize(struct pvr2_hdw *hdw,
232362306a36Sopenharmony_ci			void (*callback_func)(void *),
232462306a36Sopenharmony_ci			void *callback_data)
232562306a36Sopenharmony_ci{
232662306a36Sopenharmony_ci	LOCK_TAKE(hdw->big_lock); do {
232762306a36Sopenharmony_ci		if (hdw->flag_disconnected) {
232862306a36Sopenharmony_ci			/* Handle a race here: If we're already
232962306a36Sopenharmony_ci			   disconnected by this point, then give up.  If we
233062306a36Sopenharmony_ci			   get past this then we'll remain connected for
233162306a36Sopenharmony_ci			   the duration of initialization since the entire
233262306a36Sopenharmony_ci			   initialization sequence is now protected by the
233362306a36Sopenharmony_ci			   big_lock. */
233462306a36Sopenharmony_ci			break;
233562306a36Sopenharmony_ci		}
233662306a36Sopenharmony_ci		hdw->state_data = callback_data;
233762306a36Sopenharmony_ci		hdw->state_func = callback_func;
233862306a36Sopenharmony_ci		pvr2_hdw_setup(hdw);
233962306a36Sopenharmony_ci	} while (0); LOCK_GIVE(hdw->big_lock);
234062306a36Sopenharmony_ci	return hdw->flag_init_ok;
234162306a36Sopenharmony_ci}
234262306a36Sopenharmony_ci
234362306a36Sopenharmony_ci
234462306a36Sopenharmony_ci/* Create, set up, and return a structure for interacting with the
234562306a36Sopenharmony_ci   underlying hardware.  */
234662306a36Sopenharmony_cistruct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
234762306a36Sopenharmony_ci				 const struct usb_device_id *devid)
234862306a36Sopenharmony_ci{
234962306a36Sopenharmony_ci	unsigned int idx,cnt1,cnt2,m;
235062306a36Sopenharmony_ci	struct pvr2_hdw *hdw = NULL;
235162306a36Sopenharmony_ci	int valid_std_mask;
235262306a36Sopenharmony_ci	struct pvr2_ctrl *cptr;
235362306a36Sopenharmony_ci	struct usb_device *usb_dev;
235462306a36Sopenharmony_ci	const struct pvr2_device_desc *hdw_desc;
235562306a36Sopenharmony_ci	__u8 ifnum;
235662306a36Sopenharmony_ci	struct v4l2_queryctrl qctrl;
235762306a36Sopenharmony_ci	struct pvr2_ctl_info *ciptr;
235862306a36Sopenharmony_ci
235962306a36Sopenharmony_ci	usb_dev = interface_to_usbdev(intf);
236062306a36Sopenharmony_ci
236162306a36Sopenharmony_ci	hdw_desc = (const struct pvr2_device_desc *)(devid->driver_info);
236262306a36Sopenharmony_ci
236362306a36Sopenharmony_ci	if (hdw_desc == NULL) {
236462306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INIT, "pvr2_hdw_create: No device description pointer, unable to continue.");
236562306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INIT,
236662306a36Sopenharmony_ci			   "If you have a new device type, please contact Mike Isely <isely@pobox.com> to get it included in the driver");
236762306a36Sopenharmony_ci		goto fail;
236862306a36Sopenharmony_ci	}
236962306a36Sopenharmony_ci
237062306a36Sopenharmony_ci	hdw = kzalloc(sizeof(*hdw),GFP_KERNEL);
237162306a36Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_create: hdw=%p, type \"%s\"",
237262306a36Sopenharmony_ci		   hdw,hdw_desc->description);
237362306a36Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INFO, "Hardware description: %s",
237462306a36Sopenharmony_ci		hdw_desc->description);
237562306a36Sopenharmony_ci	if (hdw_desc->flag_is_experimental) {
237662306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INFO, "**********");
237762306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INFO,
237862306a36Sopenharmony_ci			   "***WARNING*** Support for this device (%s) is experimental.",
237962306a36Sopenharmony_ci							      hdw_desc->description);
238062306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INFO,
238162306a36Sopenharmony_ci			   "Important functionality might not be entirely working.");
238262306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INFO,
238362306a36Sopenharmony_ci			   "Please consider contacting the driver author to help with further stabilization of the driver.");
238462306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INFO, "**********");
238562306a36Sopenharmony_ci	}
238662306a36Sopenharmony_ci	if (!hdw) goto fail;
238762306a36Sopenharmony_ci
238862306a36Sopenharmony_ci	timer_setup(&hdw->quiescent_timer, pvr2_hdw_quiescent_timeout, 0);
238962306a36Sopenharmony_ci
239062306a36Sopenharmony_ci	timer_setup(&hdw->decoder_stabilization_timer,
239162306a36Sopenharmony_ci		    pvr2_hdw_decoder_stabilization_timeout, 0);
239262306a36Sopenharmony_ci
239362306a36Sopenharmony_ci	timer_setup(&hdw->encoder_wait_timer, pvr2_hdw_encoder_wait_timeout,
239462306a36Sopenharmony_ci		    0);
239562306a36Sopenharmony_ci
239662306a36Sopenharmony_ci	timer_setup(&hdw->encoder_run_timer, pvr2_hdw_encoder_run_timeout, 0);
239762306a36Sopenharmony_ci
239862306a36Sopenharmony_ci	hdw->master_state = PVR2_STATE_DEAD;
239962306a36Sopenharmony_ci
240062306a36Sopenharmony_ci	init_waitqueue_head(&hdw->state_wait_data);
240162306a36Sopenharmony_ci
240262306a36Sopenharmony_ci	hdw->tuner_signal_stale = !0;
240362306a36Sopenharmony_ci	cx2341x_fill_defaults(&hdw->enc_ctl_state);
240462306a36Sopenharmony_ci
240562306a36Sopenharmony_ci	/* Calculate which inputs are OK */
240662306a36Sopenharmony_ci	m = 0;
240762306a36Sopenharmony_ci	if (hdw_desc->flag_has_analogtuner) m |= 1 << PVR2_CVAL_INPUT_TV;
240862306a36Sopenharmony_ci	if (hdw_desc->digital_control_scheme != PVR2_DIGITAL_SCHEME_NONE) {
240962306a36Sopenharmony_ci		m |= 1 << PVR2_CVAL_INPUT_DTV;
241062306a36Sopenharmony_ci	}
241162306a36Sopenharmony_ci	if (hdw_desc->flag_has_svideo) m |= 1 << PVR2_CVAL_INPUT_SVIDEO;
241262306a36Sopenharmony_ci	if (hdw_desc->flag_has_composite) m |= 1 << PVR2_CVAL_INPUT_COMPOSITE;
241362306a36Sopenharmony_ci	if (hdw_desc->flag_has_fmradio) m |= 1 << PVR2_CVAL_INPUT_RADIO;
241462306a36Sopenharmony_ci	hdw->input_avail_mask = m;
241562306a36Sopenharmony_ci	hdw->input_allowed_mask = hdw->input_avail_mask;
241662306a36Sopenharmony_ci
241762306a36Sopenharmony_ci	/* If not a hybrid device, pathway_state never changes.  So
241862306a36Sopenharmony_ci	   initialize it here to what it should forever be. */
241962306a36Sopenharmony_ci	if (!(hdw->input_avail_mask & (1 << PVR2_CVAL_INPUT_DTV))) {
242062306a36Sopenharmony_ci		hdw->pathway_state = PVR2_PATHWAY_ANALOG;
242162306a36Sopenharmony_ci	} else if (!(hdw->input_avail_mask & (1 << PVR2_CVAL_INPUT_TV))) {
242262306a36Sopenharmony_ci		hdw->pathway_state = PVR2_PATHWAY_DIGITAL;
242362306a36Sopenharmony_ci	}
242462306a36Sopenharmony_ci
242562306a36Sopenharmony_ci	hdw->control_cnt = CTRLDEF_COUNT;
242662306a36Sopenharmony_ci	hdw->control_cnt += MPEGDEF_COUNT;
242762306a36Sopenharmony_ci	hdw->controls = kcalloc(hdw->control_cnt, sizeof(struct pvr2_ctrl),
242862306a36Sopenharmony_ci				GFP_KERNEL);
242962306a36Sopenharmony_ci	if (!hdw->controls) goto fail;
243062306a36Sopenharmony_ci	hdw->hdw_desc = hdw_desc;
243162306a36Sopenharmony_ci	hdw->ir_scheme_active = hdw->hdw_desc->ir_scheme;
243262306a36Sopenharmony_ci	for (idx = 0; idx < hdw->control_cnt; idx++) {
243362306a36Sopenharmony_ci		cptr = hdw->controls + idx;
243462306a36Sopenharmony_ci		cptr->hdw = hdw;
243562306a36Sopenharmony_ci	}
243662306a36Sopenharmony_ci	for (idx = 0; idx < 32; idx++) {
243762306a36Sopenharmony_ci		hdw->std_mask_ptrs[idx] = hdw->std_mask_names[idx];
243862306a36Sopenharmony_ci	}
243962306a36Sopenharmony_ci	for (idx = 0; idx < CTRLDEF_COUNT; idx++) {
244062306a36Sopenharmony_ci		cptr = hdw->controls + idx;
244162306a36Sopenharmony_ci		cptr->info = control_defs+idx;
244262306a36Sopenharmony_ci	}
244362306a36Sopenharmony_ci
244462306a36Sopenharmony_ci	/* Ensure that default input choice is a valid one. */
244562306a36Sopenharmony_ci	m = hdw->input_avail_mask;
244662306a36Sopenharmony_ci	if (m) for (idx = 0; idx < (sizeof(m) << 3); idx++) {
244762306a36Sopenharmony_ci		if (!((1UL << idx) & m)) continue;
244862306a36Sopenharmony_ci		hdw->input_val = idx;
244962306a36Sopenharmony_ci		break;
245062306a36Sopenharmony_ci	}
245162306a36Sopenharmony_ci
245262306a36Sopenharmony_ci	/* Define and configure additional controls from cx2341x module. */
245362306a36Sopenharmony_ci	hdw->mpeg_ctrl_info = kcalloc(MPEGDEF_COUNT,
245462306a36Sopenharmony_ci				      sizeof(*(hdw->mpeg_ctrl_info)),
245562306a36Sopenharmony_ci				      GFP_KERNEL);
245662306a36Sopenharmony_ci	if (!hdw->mpeg_ctrl_info) goto fail;
245762306a36Sopenharmony_ci	for (idx = 0; idx < MPEGDEF_COUNT; idx++) {
245862306a36Sopenharmony_ci		cptr = hdw->controls + idx + CTRLDEF_COUNT;
245962306a36Sopenharmony_ci		ciptr = &(hdw->mpeg_ctrl_info[idx].info);
246062306a36Sopenharmony_ci		ciptr->desc = hdw->mpeg_ctrl_info[idx].desc;
246162306a36Sopenharmony_ci		ciptr->name = mpeg_ids[idx].strid;
246262306a36Sopenharmony_ci		ciptr->v4l_id = mpeg_ids[idx].id;
246362306a36Sopenharmony_ci		ciptr->skip_init = !0;
246462306a36Sopenharmony_ci		ciptr->get_value = ctrl_cx2341x_get;
246562306a36Sopenharmony_ci		ciptr->get_v4lflags = ctrl_cx2341x_getv4lflags;
246662306a36Sopenharmony_ci		ciptr->is_dirty = ctrl_cx2341x_is_dirty;
246762306a36Sopenharmony_ci		if (!idx) ciptr->clear_dirty = ctrl_cx2341x_clear_dirty;
246862306a36Sopenharmony_ci		qctrl.id = ciptr->v4l_id;
246962306a36Sopenharmony_ci		cx2341x_ctrl_query(&hdw->enc_ctl_state,&qctrl);
247062306a36Sopenharmony_ci		if (!(qctrl.flags & V4L2_CTRL_FLAG_READ_ONLY)) {
247162306a36Sopenharmony_ci			ciptr->set_value = ctrl_cx2341x_set;
247262306a36Sopenharmony_ci		}
247362306a36Sopenharmony_ci		strscpy(hdw->mpeg_ctrl_info[idx].desc, qctrl.name,
247462306a36Sopenharmony_ci			sizeof(hdw->mpeg_ctrl_info[idx].desc));
247562306a36Sopenharmony_ci		ciptr->default_value = qctrl.default_value;
247662306a36Sopenharmony_ci		switch (qctrl.type) {
247762306a36Sopenharmony_ci		default:
247862306a36Sopenharmony_ci		case V4L2_CTRL_TYPE_INTEGER:
247962306a36Sopenharmony_ci			ciptr->type = pvr2_ctl_int;
248062306a36Sopenharmony_ci			ciptr->def.type_int.min_value = qctrl.minimum;
248162306a36Sopenharmony_ci			ciptr->def.type_int.max_value = qctrl.maximum;
248262306a36Sopenharmony_ci			break;
248362306a36Sopenharmony_ci		case V4L2_CTRL_TYPE_BOOLEAN:
248462306a36Sopenharmony_ci			ciptr->type = pvr2_ctl_bool;
248562306a36Sopenharmony_ci			break;
248662306a36Sopenharmony_ci		case V4L2_CTRL_TYPE_MENU:
248762306a36Sopenharmony_ci			ciptr->type = pvr2_ctl_enum;
248862306a36Sopenharmony_ci			ciptr->def.type_enum.value_names =
248962306a36Sopenharmony_ci				cx2341x_ctrl_get_menu(&hdw->enc_ctl_state,
249062306a36Sopenharmony_ci								ciptr->v4l_id);
249162306a36Sopenharmony_ci			for (cnt1 = 0;
249262306a36Sopenharmony_ci			     ciptr->def.type_enum.value_names[cnt1] != NULL;
249362306a36Sopenharmony_ci			     cnt1++) { }
249462306a36Sopenharmony_ci			ciptr->def.type_enum.count = cnt1;
249562306a36Sopenharmony_ci			break;
249662306a36Sopenharmony_ci		}
249762306a36Sopenharmony_ci		cptr->info = ciptr;
249862306a36Sopenharmony_ci	}
249962306a36Sopenharmony_ci
250062306a36Sopenharmony_ci	// Initialize control data regarding video standard masks
250162306a36Sopenharmony_ci	valid_std_mask = pvr2_std_get_usable();
250262306a36Sopenharmony_ci	for (idx = 0; idx < 32; idx++) {
250362306a36Sopenharmony_ci		if (!(valid_std_mask & (1UL << idx))) continue;
250462306a36Sopenharmony_ci		cnt1 = pvr2_std_id_to_str(
250562306a36Sopenharmony_ci			hdw->std_mask_names[idx],
250662306a36Sopenharmony_ci			sizeof(hdw->std_mask_names[idx])-1,
250762306a36Sopenharmony_ci			1UL << idx);
250862306a36Sopenharmony_ci		hdw->std_mask_names[idx][cnt1] = 0;
250962306a36Sopenharmony_ci	}
251062306a36Sopenharmony_ci	cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDAVAIL);
251162306a36Sopenharmony_ci	if (cptr) {
251262306a36Sopenharmony_ci		memcpy(&hdw->std_info_avail,cptr->info,
251362306a36Sopenharmony_ci		       sizeof(hdw->std_info_avail));
251462306a36Sopenharmony_ci		cptr->info = &hdw->std_info_avail;
251562306a36Sopenharmony_ci		hdw->std_info_avail.def.type_bitmask.bit_names =
251662306a36Sopenharmony_ci			hdw->std_mask_ptrs;
251762306a36Sopenharmony_ci		hdw->std_info_avail.def.type_bitmask.valid_bits =
251862306a36Sopenharmony_ci			valid_std_mask;
251962306a36Sopenharmony_ci	}
252062306a36Sopenharmony_ci	cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR);
252162306a36Sopenharmony_ci	if (cptr) {
252262306a36Sopenharmony_ci		memcpy(&hdw->std_info_cur,cptr->info,
252362306a36Sopenharmony_ci		       sizeof(hdw->std_info_cur));
252462306a36Sopenharmony_ci		cptr->info = &hdw->std_info_cur;
252562306a36Sopenharmony_ci		hdw->std_info_cur.def.type_bitmask.bit_names =
252662306a36Sopenharmony_ci			hdw->std_mask_ptrs;
252762306a36Sopenharmony_ci		hdw->std_info_cur.def.type_bitmask.valid_bits =
252862306a36Sopenharmony_ci			valid_std_mask;
252962306a36Sopenharmony_ci	}
253062306a36Sopenharmony_ci	cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDDETECT);
253162306a36Sopenharmony_ci	if (cptr) {
253262306a36Sopenharmony_ci		memcpy(&hdw->std_info_detect,cptr->info,
253362306a36Sopenharmony_ci		       sizeof(hdw->std_info_detect));
253462306a36Sopenharmony_ci		cptr->info = &hdw->std_info_detect;
253562306a36Sopenharmony_ci		hdw->std_info_detect.def.type_bitmask.bit_names =
253662306a36Sopenharmony_ci			hdw->std_mask_ptrs;
253762306a36Sopenharmony_ci		hdw->std_info_detect.def.type_bitmask.valid_bits =
253862306a36Sopenharmony_ci			valid_std_mask;
253962306a36Sopenharmony_ci	}
254062306a36Sopenharmony_ci
254162306a36Sopenharmony_ci	hdw->cropcap_stale = !0;
254262306a36Sopenharmony_ci	hdw->eeprom_addr = -1;
254362306a36Sopenharmony_ci	hdw->unit_number = -1;
254462306a36Sopenharmony_ci	hdw->v4l_minor_number_video = -1;
254562306a36Sopenharmony_ci	hdw->v4l_minor_number_vbi = -1;
254662306a36Sopenharmony_ci	hdw->v4l_minor_number_radio = -1;
254762306a36Sopenharmony_ci	hdw->ctl_write_buffer = kmalloc(PVR2_CTL_BUFFSIZE,GFP_KERNEL);
254862306a36Sopenharmony_ci	if (!hdw->ctl_write_buffer) goto fail;
254962306a36Sopenharmony_ci	hdw->ctl_read_buffer = kmalloc(PVR2_CTL_BUFFSIZE,GFP_KERNEL);
255062306a36Sopenharmony_ci	if (!hdw->ctl_read_buffer) goto fail;
255162306a36Sopenharmony_ci	hdw->ctl_write_urb = usb_alloc_urb(0,GFP_KERNEL);
255262306a36Sopenharmony_ci	if (!hdw->ctl_write_urb) goto fail;
255362306a36Sopenharmony_ci	hdw->ctl_read_urb = usb_alloc_urb(0,GFP_KERNEL);
255462306a36Sopenharmony_ci	if (!hdw->ctl_read_urb) goto fail;
255562306a36Sopenharmony_ci
255662306a36Sopenharmony_ci	if (v4l2_device_register(&intf->dev, &hdw->v4l2_dev) != 0) {
255762306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
255862306a36Sopenharmony_ci			   "Error registering with v4l core, giving up");
255962306a36Sopenharmony_ci		goto fail;
256062306a36Sopenharmony_ci	}
256162306a36Sopenharmony_ci	mutex_lock(&pvr2_unit_mtx);
256262306a36Sopenharmony_ci	do {
256362306a36Sopenharmony_ci		for (idx = 0; idx < PVR_NUM; idx++) {
256462306a36Sopenharmony_ci			if (unit_pointers[idx]) continue;
256562306a36Sopenharmony_ci			hdw->unit_number = idx;
256662306a36Sopenharmony_ci			unit_pointers[idx] = hdw;
256762306a36Sopenharmony_ci			break;
256862306a36Sopenharmony_ci		}
256962306a36Sopenharmony_ci	} while (0);
257062306a36Sopenharmony_ci	mutex_unlock(&pvr2_unit_mtx);
257162306a36Sopenharmony_ci
257262306a36Sopenharmony_ci	INIT_WORK(&hdw->workpoll, pvr2_hdw_worker_poll);
257362306a36Sopenharmony_ci
257462306a36Sopenharmony_ci	if (hdw->unit_number == -1)
257562306a36Sopenharmony_ci		goto fail;
257662306a36Sopenharmony_ci
257762306a36Sopenharmony_ci	cnt1 = 0;
257862306a36Sopenharmony_ci	cnt2 = scnprintf(hdw->name+cnt1,sizeof(hdw->name)-cnt1,"pvrusb2");
257962306a36Sopenharmony_ci	cnt1 += cnt2;
258062306a36Sopenharmony_ci	if (hdw->unit_number >= 0) {
258162306a36Sopenharmony_ci		cnt2 = scnprintf(hdw->name+cnt1,sizeof(hdw->name)-cnt1,"_%c",
258262306a36Sopenharmony_ci				 ('a' + hdw->unit_number));
258362306a36Sopenharmony_ci		cnt1 += cnt2;
258462306a36Sopenharmony_ci	}
258562306a36Sopenharmony_ci	if (cnt1 >= sizeof(hdw->name)) cnt1 = sizeof(hdw->name)-1;
258662306a36Sopenharmony_ci	hdw->name[cnt1] = 0;
258762306a36Sopenharmony_ci
258862306a36Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INIT,"Driver unit number is %d, name is %s",
258962306a36Sopenharmony_ci		   hdw->unit_number,hdw->name);
259062306a36Sopenharmony_ci
259162306a36Sopenharmony_ci	hdw->tuner_type = -1;
259262306a36Sopenharmony_ci	hdw->flag_ok = !0;
259362306a36Sopenharmony_ci
259462306a36Sopenharmony_ci	hdw->usb_intf = intf;
259562306a36Sopenharmony_ci	hdw->usb_dev = usb_dev;
259662306a36Sopenharmony_ci
259762306a36Sopenharmony_ci	usb_make_path(hdw->usb_dev, hdw->bus_info, sizeof(hdw->bus_info));
259862306a36Sopenharmony_ci
259962306a36Sopenharmony_ci	ifnum = hdw->usb_intf->cur_altsetting->desc.bInterfaceNumber;
260062306a36Sopenharmony_ci	usb_set_interface(hdw->usb_dev,ifnum,0);
260162306a36Sopenharmony_ci
260262306a36Sopenharmony_ci	mutex_init(&hdw->ctl_lock_mutex);
260362306a36Sopenharmony_ci	mutex_init(&hdw->big_lock_mutex);
260462306a36Sopenharmony_ci
260562306a36Sopenharmony_ci	return hdw;
260662306a36Sopenharmony_ci fail:
260762306a36Sopenharmony_ci	if (hdw) {
260862306a36Sopenharmony_ci		timer_shutdown_sync(&hdw->quiescent_timer);
260962306a36Sopenharmony_ci		timer_shutdown_sync(&hdw->decoder_stabilization_timer);
261062306a36Sopenharmony_ci		timer_shutdown_sync(&hdw->encoder_run_timer);
261162306a36Sopenharmony_ci		timer_shutdown_sync(&hdw->encoder_wait_timer);
261262306a36Sopenharmony_ci		flush_work(&hdw->workpoll);
261362306a36Sopenharmony_ci		v4l2_device_unregister(&hdw->v4l2_dev);
261462306a36Sopenharmony_ci		usb_free_urb(hdw->ctl_read_urb);
261562306a36Sopenharmony_ci		usb_free_urb(hdw->ctl_write_urb);
261662306a36Sopenharmony_ci		kfree(hdw->ctl_read_buffer);
261762306a36Sopenharmony_ci		kfree(hdw->ctl_write_buffer);
261862306a36Sopenharmony_ci		kfree(hdw->controls);
261962306a36Sopenharmony_ci		kfree(hdw->mpeg_ctrl_info);
262062306a36Sopenharmony_ci		kfree(hdw);
262162306a36Sopenharmony_ci	}
262262306a36Sopenharmony_ci	return NULL;
262362306a36Sopenharmony_ci}
262462306a36Sopenharmony_ci
262562306a36Sopenharmony_ci
262662306a36Sopenharmony_ci/* Remove _all_ associations between this driver and the underlying USB
262762306a36Sopenharmony_ci   layer. */
262862306a36Sopenharmony_cistatic void pvr2_hdw_remove_usb_stuff(struct pvr2_hdw *hdw)
262962306a36Sopenharmony_ci{
263062306a36Sopenharmony_ci	if (hdw->flag_disconnected) return;
263162306a36Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_remove_usb_stuff: hdw=%p",hdw);
263262306a36Sopenharmony_ci	if (hdw->ctl_read_urb) {
263362306a36Sopenharmony_ci		usb_kill_urb(hdw->ctl_read_urb);
263462306a36Sopenharmony_ci		usb_free_urb(hdw->ctl_read_urb);
263562306a36Sopenharmony_ci		hdw->ctl_read_urb = NULL;
263662306a36Sopenharmony_ci	}
263762306a36Sopenharmony_ci	if (hdw->ctl_write_urb) {
263862306a36Sopenharmony_ci		usb_kill_urb(hdw->ctl_write_urb);
263962306a36Sopenharmony_ci		usb_free_urb(hdw->ctl_write_urb);
264062306a36Sopenharmony_ci		hdw->ctl_write_urb = NULL;
264162306a36Sopenharmony_ci	}
264262306a36Sopenharmony_ci	if (hdw->ctl_read_buffer) {
264362306a36Sopenharmony_ci		kfree(hdw->ctl_read_buffer);
264462306a36Sopenharmony_ci		hdw->ctl_read_buffer = NULL;
264562306a36Sopenharmony_ci	}
264662306a36Sopenharmony_ci	if (hdw->ctl_write_buffer) {
264762306a36Sopenharmony_ci		kfree(hdw->ctl_write_buffer);
264862306a36Sopenharmony_ci		hdw->ctl_write_buffer = NULL;
264962306a36Sopenharmony_ci	}
265062306a36Sopenharmony_ci	hdw->flag_disconnected = !0;
265162306a36Sopenharmony_ci	/* If we don't do this, then there will be a dangling struct device
265262306a36Sopenharmony_ci	   reference to our disappearing device persisting inside the V4L
265362306a36Sopenharmony_ci	   core... */
265462306a36Sopenharmony_ci	v4l2_device_disconnect(&hdw->v4l2_dev);
265562306a36Sopenharmony_ci	hdw->usb_dev = NULL;
265662306a36Sopenharmony_ci	hdw->usb_intf = NULL;
265762306a36Sopenharmony_ci	pvr2_hdw_render_useless(hdw);
265862306a36Sopenharmony_ci}
265962306a36Sopenharmony_ci
266062306a36Sopenharmony_civoid pvr2_hdw_set_v4l2_dev(struct pvr2_hdw *hdw, struct video_device *vdev)
266162306a36Sopenharmony_ci{
266262306a36Sopenharmony_ci	vdev->v4l2_dev = &hdw->v4l2_dev;
266362306a36Sopenharmony_ci}
266462306a36Sopenharmony_ci
266562306a36Sopenharmony_ci/* Destroy hardware interaction structure */
266662306a36Sopenharmony_civoid pvr2_hdw_destroy(struct pvr2_hdw *hdw)
266762306a36Sopenharmony_ci{
266862306a36Sopenharmony_ci	if (!hdw) return;
266962306a36Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_destroy: hdw=%p",hdw);
267062306a36Sopenharmony_ci	flush_work(&hdw->workpoll);
267162306a36Sopenharmony_ci	timer_shutdown_sync(&hdw->quiescent_timer);
267262306a36Sopenharmony_ci	timer_shutdown_sync(&hdw->decoder_stabilization_timer);
267362306a36Sopenharmony_ci	timer_shutdown_sync(&hdw->encoder_run_timer);
267462306a36Sopenharmony_ci	timer_shutdown_sync(&hdw->encoder_wait_timer);
267562306a36Sopenharmony_ci	if (hdw->fw_buffer) {
267662306a36Sopenharmony_ci		kfree(hdw->fw_buffer);
267762306a36Sopenharmony_ci		hdw->fw_buffer = NULL;
267862306a36Sopenharmony_ci	}
267962306a36Sopenharmony_ci	if (hdw->vid_stream) {
268062306a36Sopenharmony_ci		pvr2_stream_destroy(hdw->vid_stream);
268162306a36Sopenharmony_ci		hdw->vid_stream = NULL;
268262306a36Sopenharmony_ci	}
268362306a36Sopenharmony_ci	v4l2_device_unregister(&hdw->v4l2_dev);
268462306a36Sopenharmony_ci	pvr2_hdw_disconnect(hdw);
268562306a36Sopenharmony_ci	mutex_lock(&pvr2_unit_mtx);
268662306a36Sopenharmony_ci	do {
268762306a36Sopenharmony_ci		if ((hdw->unit_number >= 0) &&
268862306a36Sopenharmony_ci		    (hdw->unit_number < PVR_NUM) &&
268962306a36Sopenharmony_ci		    (unit_pointers[hdw->unit_number] == hdw)) {
269062306a36Sopenharmony_ci			unit_pointers[hdw->unit_number] = NULL;
269162306a36Sopenharmony_ci		}
269262306a36Sopenharmony_ci	} while (0);
269362306a36Sopenharmony_ci	mutex_unlock(&pvr2_unit_mtx);
269462306a36Sopenharmony_ci	kfree(hdw->controls);
269562306a36Sopenharmony_ci	kfree(hdw->mpeg_ctrl_info);
269662306a36Sopenharmony_ci	kfree(hdw);
269762306a36Sopenharmony_ci}
269862306a36Sopenharmony_ci
269962306a36Sopenharmony_ci
270062306a36Sopenharmony_ciint pvr2_hdw_dev_ok(struct pvr2_hdw *hdw)
270162306a36Sopenharmony_ci{
270262306a36Sopenharmony_ci	return (hdw && hdw->flag_ok);
270362306a36Sopenharmony_ci}
270462306a36Sopenharmony_ci
270562306a36Sopenharmony_ci
270662306a36Sopenharmony_ci/* Called when hardware has been unplugged */
270762306a36Sopenharmony_civoid pvr2_hdw_disconnect(struct pvr2_hdw *hdw)
270862306a36Sopenharmony_ci{
270962306a36Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_disconnect(hdw=%p)",hdw);
271062306a36Sopenharmony_ci	LOCK_TAKE(hdw->big_lock);
271162306a36Sopenharmony_ci	pvr2_i2c_core_done(hdw);
271262306a36Sopenharmony_ci	LOCK_TAKE(hdw->ctl_lock);
271362306a36Sopenharmony_ci	pvr2_hdw_remove_usb_stuff(hdw);
271462306a36Sopenharmony_ci	LOCK_GIVE(hdw->ctl_lock);
271562306a36Sopenharmony_ci	LOCK_GIVE(hdw->big_lock);
271662306a36Sopenharmony_ci}
271762306a36Sopenharmony_ci
271862306a36Sopenharmony_ci
271962306a36Sopenharmony_ci/* Get the number of defined controls */
272062306a36Sopenharmony_ciunsigned int pvr2_hdw_get_ctrl_count(struct pvr2_hdw *hdw)
272162306a36Sopenharmony_ci{
272262306a36Sopenharmony_ci	return hdw->control_cnt;
272362306a36Sopenharmony_ci}
272462306a36Sopenharmony_ci
272562306a36Sopenharmony_ci
272662306a36Sopenharmony_ci/* Retrieve a control handle given its index (0..count-1) */
272762306a36Sopenharmony_cistruct pvr2_ctrl *pvr2_hdw_get_ctrl_by_index(struct pvr2_hdw *hdw,
272862306a36Sopenharmony_ci					     unsigned int idx)
272962306a36Sopenharmony_ci{
273062306a36Sopenharmony_ci	if (idx >= hdw->control_cnt) return NULL;
273162306a36Sopenharmony_ci	return hdw->controls + idx;
273262306a36Sopenharmony_ci}
273362306a36Sopenharmony_ci
273462306a36Sopenharmony_ci
273562306a36Sopenharmony_ci/* Retrieve a control handle given its index (0..count-1) */
273662306a36Sopenharmony_cistruct pvr2_ctrl *pvr2_hdw_get_ctrl_by_id(struct pvr2_hdw *hdw,
273762306a36Sopenharmony_ci					  unsigned int ctl_id)
273862306a36Sopenharmony_ci{
273962306a36Sopenharmony_ci	struct pvr2_ctrl *cptr;
274062306a36Sopenharmony_ci	unsigned int idx;
274162306a36Sopenharmony_ci	int i;
274262306a36Sopenharmony_ci
274362306a36Sopenharmony_ci	/* This could be made a lot more efficient, but for now... */
274462306a36Sopenharmony_ci	for (idx = 0; idx < hdw->control_cnt; idx++) {
274562306a36Sopenharmony_ci		cptr = hdw->controls + idx;
274662306a36Sopenharmony_ci		i = cptr->info->internal_id;
274762306a36Sopenharmony_ci		if (i && (i == ctl_id)) return cptr;
274862306a36Sopenharmony_ci	}
274962306a36Sopenharmony_ci	return NULL;
275062306a36Sopenharmony_ci}
275162306a36Sopenharmony_ci
275262306a36Sopenharmony_ci
275362306a36Sopenharmony_ci/* Given a V4L ID, retrieve the control structure associated with it. */
275462306a36Sopenharmony_cistruct pvr2_ctrl *pvr2_hdw_get_ctrl_v4l(struct pvr2_hdw *hdw,unsigned int ctl_id)
275562306a36Sopenharmony_ci{
275662306a36Sopenharmony_ci	struct pvr2_ctrl *cptr;
275762306a36Sopenharmony_ci	unsigned int idx;
275862306a36Sopenharmony_ci	int i;
275962306a36Sopenharmony_ci
276062306a36Sopenharmony_ci	/* This could be made a lot more efficient, but for now... */
276162306a36Sopenharmony_ci	for (idx = 0; idx < hdw->control_cnt; idx++) {
276262306a36Sopenharmony_ci		cptr = hdw->controls + idx;
276362306a36Sopenharmony_ci		i = cptr->info->v4l_id;
276462306a36Sopenharmony_ci		if (i && (i == ctl_id)) return cptr;
276562306a36Sopenharmony_ci	}
276662306a36Sopenharmony_ci	return NULL;
276762306a36Sopenharmony_ci}
276862306a36Sopenharmony_ci
276962306a36Sopenharmony_ci
277062306a36Sopenharmony_ci/* Given a V4L ID for its immediate predecessor, retrieve the control
277162306a36Sopenharmony_ci   structure associated with it. */
277262306a36Sopenharmony_cistruct pvr2_ctrl *pvr2_hdw_get_ctrl_nextv4l(struct pvr2_hdw *hdw,
277362306a36Sopenharmony_ci					    unsigned int ctl_id)
277462306a36Sopenharmony_ci{
277562306a36Sopenharmony_ci	struct pvr2_ctrl *cptr,*cp2;
277662306a36Sopenharmony_ci	unsigned int idx;
277762306a36Sopenharmony_ci	int i;
277862306a36Sopenharmony_ci
277962306a36Sopenharmony_ci	/* This could be made a lot more efficient, but for now... */
278062306a36Sopenharmony_ci	cp2 = NULL;
278162306a36Sopenharmony_ci	for (idx = 0; idx < hdw->control_cnt; idx++) {
278262306a36Sopenharmony_ci		cptr = hdw->controls + idx;
278362306a36Sopenharmony_ci		i = cptr->info->v4l_id;
278462306a36Sopenharmony_ci		if (!i) continue;
278562306a36Sopenharmony_ci		if (i <= ctl_id) continue;
278662306a36Sopenharmony_ci		if (cp2 && (cp2->info->v4l_id < i)) continue;
278762306a36Sopenharmony_ci		cp2 = cptr;
278862306a36Sopenharmony_ci	}
278962306a36Sopenharmony_ci	return cp2;
279062306a36Sopenharmony_ci	return NULL;
279162306a36Sopenharmony_ci}
279262306a36Sopenharmony_ci
279362306a36Sopenharmony_ci
279462306a36Sopenharmony_cistatic const char *get_ctrl_typename(enum pvr2_ctl_type tp)
279562306a36Sopenharmony_ci{
279662306a36Sopenharmony_ci	switch (tp) {
279762306a36Sopenharmony_ci	case pvr2_ctl_int: return "integer";
279862306a36Sopenharmony_ci	case pvr2_ctl_enum: return "enum";
279962306a36Sopenharmony_ci	case pvr2_ctl_bool: return "boolean";
280062306a36Sopenharmony_ci	case pvr2_ctl_bitmask: return "bitmask";
280162306a36Sopenharmony_ci	}
280262306a36Sopenharmony_ci	return "";
280362306a36Sopenharmony_ci}
280462306a36Sopenharmony_ci
280562306a36Sopenharmony_ci
280662306a36Sopenharmony_cistatic void pvr2_subdev_set_control(struct pvr2_hdw *hdw, int id,
280762306a36Sopenharmony_ci				    const char *name, int val)
280862306a36Sopenharmony_ci{
280962306a36Sopenharmony_ci	struct v4l2_control ctrl;
281062306a36Sopenharmony_ci	struct v4l2_subdev *sd;
281162306a36Sopenharmony_ci
281262306a36Sopenharmony_ci	pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 %s=%d", name, val);
281362306a36Sopenharmony_ci	memset(&ctrl, 0, sizeof(ctrl));
281462306a36Sopenharmony_ci	ctrl.id = id;
281562306a36Sopenharmony_ci	ctrl.value = val;
281662306a36Sopenharmony_ci
281762306a36Sopenharmony_ci	v4l2_device_for_each_subdev(sd, &hdw->v4l2_dev)
281862306a36Sopenharmony_ci		v4l2_s_ctrl(NULL, sd->ctrl_handler, &ctrl);
281962306a36Sopenharmony_ci}
282062306a36Sopenharmony_ci
282162306a36Sopenharmony_ci#define PVR2_SUBDEV_SET_CONTROL(hdw, id, lab) \
282262306a36Sopenharmony_ci	if ((hdw)->lab##_dirty || (hdw)->force_dirty) {		\
282362306a36Sopenharmony_ci		pvr2_subdev_set_control(hdw, id, #lab, (hdw)->lab##_val); \
282462306a36Sopenharmony_ci	}
282562306a36Sopenharmony_ci
282662306a36Sopenharmony_cistatic v4l2_std_id pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw)
282762306a36Sopenharmony_ci{
282862306a36Sopenharmony_ci	v4l2_std_id std;
282962306a36Sopenharmony_ci	std = (v4l2_std_id)hdw->std_mask_avail;
283062306a36Sopenharmony_ci	v4l2_device_call_all(&hdw->v4l2_dev, 0,
283162306a36Sopenharmony_ci			     video, querystd, &std);
283262306a36Sopenharmony_ci	return std;
283362306a36Sopenharmony_ci}
283462306a36Sopenharmony_ci
283562306a36Sopenharmony_ci/* Execute whatever commands are required to update the state of all the
283662306a36Sopenharmony_ci   sub-devices so that they match our current control values. */
283762306a36Sopenharmony_cistatic void pvr2_subdev_update(struct pvr2_hdw *hdw)
283862306a36Sopenharmony_ci{
283962306a36Sopenharmony_ci	struct v4l2_subdev *sd;
284062306a36Sopenharmony_ci	unsigned int id;
284162306a36Sopenharmony_ci	pvr2_subdev_update_func fp;
284262306a36Sopenharmony_ci
284362306a36Sopenharmony_ci	pvr2_trace(PVR2_TRACE_CHIPS, "subdev update...");
284462306a36Sopenharmony_ci
284562306a36Sopenharmony_ci	if (hdw->tuner_updated || hdw->force_dirty) {
284662306a36Sopenharmony_ci		struct tuner_setup setup;
284762306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_CHIPS, "subdev tuner set_type(%d)",
284862306a36Sopenharmony_ci			   hdw->tuner_type);
284962306a36Sopenharmony_ci		if (((int)(hdw->tuner_type)) >= 0) {
285062306a36Sopenharmony_ci			memset(&setup, 0, sizeof(setup));
285162306a36Sopenharmony_ci			setup.addr = ADDR_UNSET;
285262306a36Sopenharmony_ci			setup.type = hdw->tuner_type;
285362306a36Sopenharmony_ci			setup.mode_mask = T_RADIO | T_ANALOG_TV;
285462306a36Sopenharmony_ci			v4l2_device_call_all(&hdw->v4l2_dev, 0,
285562306a36Sopenharmony_ci					     tuner, s_type_addr, &setup);
285662306a36Sopenharmony_ci		}
285762306a36Sopenharmony_ci	}
285862306a36Sopenharmony_ci
285962306a36Sopenharmony_ci	if (hdw->input_dirty || hdw->std_dirty || hdw->force_dirty) {
286062306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_standard");
286162306a36Sopenharmony_ci		if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
286262306a36Sopenharmony_ci			v4l2_device_call_all(&hdw->v4l2_dev, 0,
286362306a36Sopenharmony_ci					     tuner, s_radio);
286462306a36Sopenharmony_ci		} else {
286562306a36Sopenharmony_ci			v4l2_std_id vs;
286662306a36Sopenharmony_ci			vs = hdw->std_mask_cur;
286762306a36Sopenharmony_ci			v4l2_device_call_all(&hdw->v4l2_dev, 0,
286862306a36Sopenharmony_ci					     video, s_std, vs);
286962306a36Sopenharmony_ci			pvr2_hdw_cx25840_vbi_hack(hdw);
287062306a36Sopenharmony_ci		}
287162306a36Sopenharmony_ci		hdw->tuner_signal_stale = !0;
287262306a36Sopenharmony_ci		hdw->cropcap_stale = !0;
287362306a36Sopenharmony_ci	}
287462306a36Sopenharmony_ci
287562306a36Sopenharmony_ci	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_BRIGHTNESS, brightness);
287662306a36Sopenharmony_ci	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_CONTRAST, contrast);
287762306a36Sopenharmony_ci	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_SATURATION, saturation);
287862306a36Sopenharmony_ci	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_HUE, hue);
287962306a36Sopenharmony_ci	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_MUTE, mute);
288062306a36Sopenharmony_ci	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_VOLUME, volume);
288162306a36Sopenharmony_ci	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_BALANCE, balance);
288262306a36Sopenharmony_ci	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_BASS, bass);
288362306a36Sopenharmony_ci	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_TREBLE, treble);
288462306a36Sopenharmony_ci
288562306a36Sopenharmony_ci	if (hdw->input_dirty || hdw->audiomode_dirty || hdw->force_dirty) {
288662306a36Sopenharmony_ci		struct v4l2_tuner vt;
288762306a36Sopenharmony_ci		memset(&vt, 0, sizeof(vt));
288862306a36Sopenharmony_ci		vt.type = (hdw->input_val == PVR2_CVAL_INPUT_RADIO) ?
288962306a36Sopenharmony_ci			V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
289062306a36Sopenharmony_ci		vt.audmode = hdw->audiomode_val;
289162306a36Sopenharmony_ci		v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, s_tuner, &vt);
289262306a36Sopenharmony_ci	}
289362306a36Sopenharmony_ci
289462306a36Sopenharmony_ci	if (hdw->freqDirty || hdw->force_dirty) {
289562306a36Sopenharmony_ci		unsigned long fv;
289662306a36Sopenharmony_ci		struct v4l2_frequency freq;
289762306a36Sopenharmony_ci		fv = pvr2_hdw_get_cur_freq(hdw);
289862306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_freq(%lu)", fv);
289962306a36Sopenharmony_ci		if (hdw->tuner_signal_stale) pvr2_hdw_status_poll(hdw);
290062306a36Sopenharmony_ci		memset(&freq, 0, sizeof(freq));
290162306a36Sopenharmony_ci		if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) {
290262306a36Sopenharmony_ci			/* ((fv * 1000) / 62500) */
290362306a36Sopenharmony_ci			freq.frequency = (fv * 2) / 125;
290462306a36Sopenharmony_ci		} else {
290562306a36Sopenharmony_ci			freq.frequency = fv / 62500;
290662306a36Sopenharmony_ci		}
290762306a36Sopenharmony_ci		/* tuner-core currently doesn't seem to care about this, but
290862306a36Sopenharmony_ci		   let's set it anyway for completeness. */
290962306a36Sopenharmony_ci		if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
291062306a36Sopenharmony_ci			freq.type = V4L2_TUNER_RADIO;
291162306a36Sopenharmony_ci		} else {
291262306a36Sopenharmony_ci			freq.type = V4L2_TUNER_ANALOG_TV;
291362306a36Sopenharmony_ci		}
291462306a36Sopenharmony_ci		freq.tuner = 0;
291562306a36Sopenharmony_ci		v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner,
291662306a36Sopenharmony_ci				     s_frequency, &freq);
291762306a36Sopenharmony_ci	}
291862306a36Sopenharmony_ci
291962306a36Sopenharmony_ci	if (hdw->res_hor_dirty || hdw->res_ver_dirty || hdw->force_dirty) {
292062306a36Sopenharmony_ci		struct v4l2_subdev_format format = {
292162306a36Sopenharmony_ci			.which = V4L2_SUBDEV_FORMAT_ACTIVE,
292262306a36Sopenharmony_ci		};
292362306a36Sopenharmony_ci
292462306a36Sopenharmony_ci		format.format.width = hdw->res_hor_val;
292562306a36Sopenharmony_ci		format.format.height = hdw->res_ver_val;
292662306a36Sopenharmony_ci		format.format.code = MEDIA_BUS_FMT_FIXED;
292762306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_size(%dx%d)",
292862306a36Sopenharmony_ci			   format.format.width, format.format.height);
292962306a36Sopenharmony_ci		v4l2_device_call_all(&hdw->v4l2_dev, 0, pad, set_fmt,
293062306a36Sopenharmony_ci				     NULL, &format);
293162306a36Sopenharmony_ci	}
293262306a36Sopenharmony_ci
293362306a36Sopenharmony_ci	if (hdw->srate_dirty || hdw->force_dirty) {
293462306a36Sopenharmony_ci		u32 val;
293562306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_audio %d",
293662306a36Sopenharmony_ci			   hdw->srate_val);
293762306a36Sopenharmony_ci		switch (hdw->srate_val) {
293862306a36Sopenharmony_ci		default:
293962306a36Sopenharmony_ci		case V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000:
294062306a36Sopenharmony_ci			val = 48000;
294162306a36Sopenharmony_ci			break;
294262306a36Sopenharmony_ci		case V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100:
294362306a36Sopenharmony_ci			val = 44100;
294462306a36Sopenharmony_ci			break;
294562306a36Sopenharmony_ci		case V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000:
294662306a36Sopenharmony_ci			val = 32000;
294762306a36Sopenharmony_ci			break;
294862306a36Sopenharmony_ci		}
294962306a36Sopenharmony_ci		v4l2_device_call_all(&hdw->v4l2_dev, 0,
295062306a36Sopenharmony_ci				     audio, s_clock_freq, val);
295162306a36Sopenharmony_ci	}
295262306a36Sopenharmony_ci
295362306a36Sopenharmony_ci	/* Unable to set crop parameters; there is apparently no equivalent
295462306a36Sopenharmony_ci	   for VIDIOC_S_CROP */
295562306a36Sopenharmony_ci
295662306a36Sopenharmony_ci	v4l2_device_for_each_subdev(sd, &hdw->v4l2_dev) {
295762306a36Sopenharmony_ci		id = sd->grp_id;
295862306a36Sopenharmony_ci		if (id >= ARRAY_SIZE(pvr2_module_update_functions)) continue;
295962306a36Sopenharmony_ci		fp = pvr2_module_update_functions[id];
296062306a36Sopenharmony_ci		if (!fp) continue;
296162306a36Sopenharmony_ci		(*fp)(hdw, sd);
296262306a36Sopenharmony_ci	}
296362306a36Sopenharmony_ci
296462306a36Sopenharmony_ci	if (hdw->tuner_signal_stale || hdw->cropcap_stale) {
296562306a36Sopenharmony_ci		pvr2_hdw_status_poll(hdw);
296662306a36Sopenharmony_ci	}
296762306a36Sopenharmony_ci}
296862306a36Sopenharmony_ci
296962306a36Sopenharmony_ci
297062306a36Sopenharmony_ci/* Figure out if we need to commit control changes.  If so, mark internal
297162306a36Sopenharmony_ci   state flags to indicate this fact and return true.  Otherwise do nothing
297262306a36Sopenharmony_ci   else and return false. */
297362306a36Sopenharmony_cistatic int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw)
297462306a36Sopenharmony_ci{
297562306a36Sopenharmony_ci	unsigned int idx;
297662306a36Sopenharmony_ci	struct pvr2_ctrl *cptr;
297762306a36Sopenharmony_ci	int value;
297862306a36Sopenharmony_ci	int commit_flag = hdw->force_dirty;
297962306a36Sopenharmony_ci	char buf[100];
298062306a36Sopenharmony_ci	unsigned int bcnt,ccnt;
298162306a36Sopenharmony_ci
298262306a36Sopenharmony_ci	for (idx = 0; idx < hdw->control_cnt; idx++) {
298362306a36Sopenharmony_ci		cptr = hdw->controls + idx;
298462306a36Sopenharmony_ci		if (!cptr->info->is_dirty) continue;
298562306a36Sopenharmony_ci		if (!cptr->info->is_dirty(cptr)) continue;
298662306a36Sopenharmony_ci		commit_flag = !0;
298762306a36Sopenharmony_ci
298862306a36Sopenharmony_ci		if (!(pvrusb2_debug & PVR2_TRACE_CTL)) continue;
298962306a36Sopenharmony_ci		bcnt = scnprintf(buf,sizeof(buf),"\"%s\" <-- ",
299062306a36Sopenharmony_ci				 cptr->info->name);
299162306a36Sopenharmony_ci		value = 0;
299262306a36Sopenharmony_ci		cptr->info->get_value(cptr,&value);
299362306a36Sopenharmony_ci		pvr2_ctrl_value_to_sym_internal(cptr,~0,value,
299462306a36Sopenharmony_ci						buf+bcnt,
299562306a36Sopenharmony_ci						sizeof(buf)-bcnt,&ccnt);
299662306a36Sopenharmony_ci		bcnt += ccnt;
299762306a36Sopenharmony_ci		bcnt += scnprintf(buf+bcnt,sizeof(buf)-bcnt," <%s>",
299862306a36Sopenharmony_ci				  get_ctrl_typename(cptr->info->type));
299962306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_CTL,
300062306a36Sopenharmony_ci			   "/*--TRACE_COMMIT--*/ %.*s",
300162306a36Sopenharmony_ci			   bcnt,buf);
300262306a36Sopenharmony_ci	}
300362306a36Sopenharmony_ci
300462306a36Sopenharmony_ci	if (!commit_flag) {
300562306a36Sopenharmony_ci		/* Nothing has changed */
300662306a36Sopenharmony_ci		return 0;
300762306a36Sopenharmony_ci	}
300862306a36Sopenharmony_ci
300962306a36Sopenharmony_ci	hdw->state_pipeline_config = 0;
301062306a36Sopenharmony_ci	trace_stbit("state_pipeline_config",hdw->state_pipeline_config);
301162306a36Sopenharmony_ci	pvr2_hdw_state_sched(hdw);
301262306a36Sopenharmony_ci
301362306a36Sopenharmony_ci	return !0;
301462306a36Sopenharmony_ci}
301562306a36Sopenharmony_ci
301662306a36Sopenharmony_ci
301762306a36Sopenharmony_ci/* Perform all operations needed to commit all control changes.  This must
301862306a36Sopenharmony_ci   be performed in synchronization with the pipeline state and is thus
301962306a36Sopenharmony_ci   expected to be called as part of the driver's worker thread.  Return
302062306a36Sopenharmony_ci   true if commit successful, otherwise return false to indicate that
302162306a36Sopenharmony_ci   commit isn't possible at this time. */
302262306a36Sopenharmony_cistatic int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw)
302362306a36Sopenharmony_ci{
302462306a36Sopenharmony_ci	unsigned int idx;
302562306a36Sopenharmony_ci	struct pvr2_ctrl *cptr;
302662306a36Sopenharmony_ci	int disruptive_change;
302762306a36Sopenharmony_ci
302862306a36Sopenharmony_ci	if (hdw->input_dirty && hdw->state_pathway_ok &&
302962306a36Sopenharmony_ci	    (((hdw->input_val == PVR2_CVAL_INPUT_DTV) ?
303062306a36Sopenharmony_ci	      PVR2_PATHWAY_DIGITAL : PVR2_PATHWAY_ANALOG) !=
303162306a36Sopenharmony_ci	     hdw->pathway_state)) {
303262306a36Sopenharmony_ci		/* Change of mode being asked for... */
303362306a36Sopenharmony_ci		hdw->state_pathway_ok = 0;
303462306a36Sopenharmony_ci		trace_stbit("state_pathway_ok", hdw->state_pathway_ok);
303562306a36Sopenharmony_ci	}
303662306a36Sopenharmony_ci	if (!hdw->state_pathway_ok) {
303762306a36Sopenharmony_ci		/* Can't commit anything until pathway is ok. */
303862306a36Sopenharmony_ci		return 0;
303962306a36Sopenharmony_ci	}
304062306a36Sopenharmony_ci
304162306a36Sopenharmony_ci	/* Handle some required side effects when the video standard is
304262306a36Sopenharmony_ci	   changed.... */
304362306a36Sopenharmony_ci	if (hdw->std_dirty) {
304462306a36Sopenharmony_ci		int nvres;
304562306a36Sopenharmony_ci		int gop_size;
304662306a36Sopenharmony_ci		if (hdw->std_mask_cur & V4L2_STD_525_60) {
304762306a36Sopenharmony_ci			nvres = 480;
304862306a36Sopenharmony_ci			gop_size = 15;
304962306a36Sopenharmony_ci		} else {
305062306a36Sopenharmony_ci			nvres = 576;
305162306a36Sopenharmony_ci			gop_size = 12;
305262306a36Sopenharmony_ci		}
305362306a36Sopenharmony_ci		/* Rewrite the vertical resolution to be appropriate to the
305462306a36Sopenharmony_ci		   video standard that has been selected. */
305562306a36Sopenharmony_ci		if (nvres != hdw->res_ver_val) {
305662306a36Sopenharmony_ci			hdw->res_ver_val = nvres;
305762306a36Sopenharmony_ci			hdw->res_ver_dirty = !0;
305862306a36Sopenharmony_ci		}
305962306a36Sopenharmony_ci		/* Rewrite the GOP size to be appropriate to the video
306062306a36Sopenharmony_ci		   standard that has been selected. */
306162306a36Sopenharmony_ci		if (gop_size != hdw->enc_ctl_state.video_gop_size) {
306262306a36Sopenharmony_ci			struct v4l2_ext_controls cs;
306362306a36Sopenharmony_ci			struct v4l2_ext_control c1;
306462306a36Sopenharmony_ci			memset(&cs, 0, sizeof(cs));
306562306a36Sopenharmony_ci			memset(&c1, 0, sizeof(c1));
306662306a36Sopenharmony_ci			cs.controls = &c1;
306762306a36Sopenharmony_ci			cs.count = 1;
306862306a36Sopenharmony_ci			c1.id = V4L2_CID_MPEG_VIDEO_GOP_SIZE;
306962306a36Sopenharmony_ci			c1.value = gop_size;
307062306a36Sopenharmony_ci			cx2341x_ext_ctrls(&hdw->enc_ctl_state, 0, &cs,
307162306a36Sopenharmony_ci					  VIDIOC_S_EXT_CTRLS);
307262306a36Sopenharmony_ci		}
307362306a36Sopenharmony_ci	}
307462306a36Sopenharmony_ci
307562306a36Sopenharmony_ci	/* The broadcast decoder can only scale down, so if
307662306a36Sopenharmony_ci	 * res_*_dirty && crop window < output format ==> enlarge crop.
307762306a36Sopenharmony_ci	 *
307862306a36Sopenharmony_ci	 * The mpeg encoder receives fields of res_hor_val dots and
307962306a36Sopenharmony_ci	 * res_ver_val halflines.  Limits: hor<=720, ver<=576.
308062306a36Sopenharmony_ci	 */
308162306a36Sopenharmony_ci	if (hdw->res_hor_dirty && hdw->cropw_val < hdw->res_hor_val) {
308262306a36Sopenharmony_ci		hdw->cropw_val = hdw->res_hor_val;
308362306a36Sopenharmony_ci		hdw->cropw_dirty = !0;
308462306a36Sopenharmony_ci	} else if (hdw->cropw_dirty) {
308562306a36Sopenharmony_ci		hdw->res_hor_dirty = !0;           /* must rescale */
308662306a36Sopenharmony_ci		hdw->res_hor_val = min(720, hdw->cropw_val);
308762306a36Sopenharmony_ci	}
308862306a36Sopenharmony_ci	if (hdw->res_ver_dirty && hdw->croph_val < hdw->res_ver_val) {
308962306a36Sopenharmony_ci		hdw->croph_val = hdw->res_ver_val;
309062306a36Sopenharmony_ci		hdw->croph_dirty = !0;
309162306a36Sopenharmony_ci	} else if (hdw->croph_dirty) {
309262306a36Sopenharmony_ci		int nvres = hdw->std_mask_cur & V4L2_STD_525_60 ? 480 : 576;
309362306a36Sopenharmony_ci		hdw->res_ver_dirty = !0;
309462306a36Sopenharmony_ci		hdw->res_ver_val = min(nvres, hdw->croph_val);
309562306a36Sopenharmony_ci	}
309662306a36Sopenharmony_ci
309762306a36Sopenharmony_ci	/* If any of the below has changed, then we can't do the update
309862306a36Sopenharmony_ci	   while the pipeline is running.  Pipeline must be paused first
309962306a36Sopenharmony_ci	   and decoder -> encoder connection be made quiescent before we
310062306a36Sopenharmony_ci	   can proceed. */
310162306a36Sopenharmony_ci	disruptive_change =
310262306a36Sopenharmony_ci		(hdw->std_dirty ||
310362306a36Sopenharmony_ci		 hdw->enc_unsafe_stale ||
310462306a36Sopenharmony_ci		 hdw->srate_dirty ||
310562306a36Sopenharmony_ci		 hdw->res_ver_dirty ||
310662306a36Sopenharmony_ci		 hdw->res_hor_dirty ||
310762306a36Sopenharmony_ci		 hdw->cropw_dirty ||
310862306a36Sopenharmony_ci		 hdw->croph_dirty ||
310962306a36Sopenharmony_ci		 hdw->input_dirty ||
311062306a36Sopenharmony_ci		 (hdw->active_stream_type != hdw->desired_stream_type));
311162306a36Sopenharmony_ci	if (disruptive_change && !hdw->state_pipeline_idle) {
311262306a36Sopenharmony_ci		/* Pipeline is not idle; we can't proceed.  Arrange to
311362306a36Sopenharmony_ci		   cause pipeline to stop so that we can try this again
311462306a36Sopenharmony_ci		   later.... */
311562306a36Sopenharmony_ci		hdw->state_pipeline_pause = !0;
311662306a36Sopenharmony_ci		return 0;
311762306a36Sopenharmony_ci	}
311862306a36Sopenharmony_ci
311962306a36Sopenharmony_ci	if (hdw->srate_dirty) {
312062306a36Sopenharmony_ci		/* Write new sample rate into control structure since
312162306a36Sopenharmony_ci		 * the master copy is stale.  We must track srate
312262306a36Sopenharmony_ci		 * separate from the mpeg control structure because
312362306a36Sopenharmony_ci		 * other logic also uses this value. */
312462306a36Sopenharmony_ci		struct v4l2_ext_controls cs;
312562306a36Sopenharmony_ci		struct v4l2_ext_control c1;
312662306a36Sopenharmony_ci		memset(&cs,0,sizeof(cs));
312762306a36Sopenharmony_ci		memset(&c1,0,sizeof(c1));
312862306a36Sopenharmony_ci		cs.controls = &c1;
312962306a36Sopenharmony_ci		cs.count = 1;
313062306a36Sopenharmony_ci		c1.id = V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ;
313162306a36Sopenharmony_ci		c1.value = hdw->srate_val;
313262306a36Sopenharmony_ci		cx2341x_ext_ctrls(&hdw->enc_ctl_state, 0, &cs,VIDIOC_S_EXT_CTRLS);
313362306a36Sopenharmony_ci	}
313462306a36Sopenharmony_ci
313562306a36Sopenharmony_ci	if (hdw->active_stream_type != hdw->desired_stream_type) {
313662306a36Sopenharmony_ci		/* Handle any side effects of stream config here */
313762306a36Sopenharmony_ci		hdw->active_stream_type = hdw->desired_stream_type;
313862306a36Sopenharmony_ci	}
313962306a36Sopenharmony_ci
314062306a36Sopenharmony_ci	if (hdw->hdw_desc->signal_routing_scheme ==
314162306a36Sopenharmony_ci	    PVR2_ROUTING_SCHEME_GOTVIEW) {
314262306a36Sopenharmony_ci		u32 b;
314362306a36Sopenharmony_ci		/* Handle GOTVIEW audio switching */
314462306a36Sopenharmony_ci		pvr2_hdw_gpio_get_out(hdw,&b);
314562306a36Sopenharmony_ci		if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
314662306a36Sopenharmony_ci			/* Set GPIO 11 */
314762306a36Sopenharmony_ci			pvr2_hdw_gpio_chg_out(hdw,(1 << 11),~0);
314862306a36Sopenharmony_ci		} else {
314962306a36Sopenharmony_ci			/* Clear GPIO 11 */
315062306a36Sopenharmony_ci			pvr2_hdw_gpio_chg_out(hdw,(1 << 11),0);
315162306a36Sopenharmony_ci		}
315262306a36Sopenharmony_ci	}
315362306a36Sopenharmony_ci
315462306a36Sopenharmony_ci	/* Check and update state for all sub-devices. */
315562306a36Sopenharmony_ci	pvr2_subdev_update(hdw);
315662306a36Sopenharmony_ci
315762306a36Sopenharmony_ci	hdw->tuner_updated = 0;
315862306a36Sopenharmony_ci	hdw->force_dirty = 0;
315962306a36Sopenharmony_ci	for (idx = 0; idx < hdw->control_cnt; idx++) {
316062306a36Sopenharmony_ci		cptr = hdw->controls + idx;
316162306a36Sopenharmony_ci		if (!cptr->info->clear_dirty) continue;
316262306a36Sopenharmony_ci		cptr->info->clear_dirty(cptr);
316362306a36Sopenharmony_ci	}
316462306a36Sopenharmony_ci
316562306a36Sopenharmony_ci	if ((hdw->pathway_state == PVR2_PATHWAY_ANALOG) &&
316662306a36Sopenharmony_ci	    hdw->state_encoder_run) {
316762306a36Sopenharmony_ci		/* If encoder isn't running or it can't be touched, then
316862306a36Sopenharmony_ci		   this will get worked out later when we start the
316962306a36Sopenharmony_ci		   encoder. */
317062306a36Sopenharmony_ci		if (pvr2_encoder_adjust(hdw) < 0) return !0;
317162306a36Sopenharmony_ci	}
317262306a36Sopenharmony_ci
317362306a36Sopenharmony_ci	hdw->state_pipeline_config = !0;
317462306a36Sopenharmony_ci	/* Hardware state may have changed in a way to cause the cropping
317562306a36Sopenharmony_ci	   capabilities to have changed.  So mark it stale, which will
317662306a36Sopenharmony_ci	   cause a later re-fetch. */
317762306a36Sopenharmony_ci	trace_stbit("state_pipeline_config",hdw->state_pipeline_config);
317862306a36Sopenharmony_ci	return !0;
317962306a36Sopenharmony_ci}
318062306a36Sopenharmony_ci
318162306a36Sopenharmony_ci
318262306a36Sopenharmony_ciint pvr2_hdw_commit_ctl(struct pvr2_hdw *hdw)
318362306a36Sopenharmony_ci{
318462306a36Sopenharmony_ci	int fl;
318562306a36Sopenharmony_ci	LOCK_TAKE(hdw->big_lock);
318662306a36Sopenharmony_ci	fl = pvr2_hdw_commit_setup(hdw);
318762306a36Sopenharmony_ci	LOCK_GIVE(hdw->big_lock);
318862306a36Sopenharmony_ci	if (!fl) return 0;
318962306a36Sopenharmony_ci	return pvr2_hdw_wait(hdw,0);
319062306a36Sopenharmony_ci}
319162306a36Sopenharmony_ci
319262306a36Sopenharmony_ci
319362306a36Sopenharmony_cistatic void pvr2_hdw_worker_poll(struct work_struct *work)
319462306a36Sopenharmony_ci{
319562306a36Sopenharmony_ci	int fl = 0;
319662306a36Sopenharmony_ci	struct pvr2_hdw *hdw = container_of(work,struct pvr2_hdw,workpoll);
319762306a36Sopenharmony_ci	LOCK_TAKE(hdw->big_lock); do {
319862306a36Sopenharmony_ci		fl = pvr2_hdw_state_eval(hdw);
319962306a36Sopenharmony_ci	} while (0); LOCK_GIVE(hdw->big_lock);
320062306a36Sopenharmony_ci	if (fl && hdw->state_func) {
320162306a36Sopenharmony_ci		hdw->state_func(hdw->state_data);
320262306a36Sopenharmony_ci	}
320362306a36Sopenharmony_ci}
320462306a36Sopenharmony_ci
320562306a36Sopenharmony_ci
320662306a36Sopenharmony_cistatic int pvr2_hdw_wait(struct pvr2_hdw *hdw,int state)
320762306a36Sopenharmony_ci{
320862306a36Sopenharmony_ci	return wait_event_interruptible(
320962306a36Sopenharmony_ci		hdw->state_wait_data,
321062306a36Sopenharmony_ci		(hdw->state_stale == 0) &&
321162306a36Sopenharmony_ci		(!state || (hdw->master_state != state)));
321262306a36Sopenharmony_ci}
321362306a36Sopenharmony_ci
321462306a36Sopenharmony_ci
321562306a36Sopenharmony_ci/* Return name for this driver instance */
321662306a36Sopenharmony_ciconst char *pvr2_hdw_get_driver_name(struct pvr2_hdw *hdw)
321762306a36Sopenharmony_ci{
321862306a36Sopenharmony_ci	return hdw->name;
321962306a36Sopenharmony_ci}
322062306a36Sopenharmony_ci
322162306a36Sopenharmony_ci
322262306a36Sopenharmony_ciconst char *pvr2_hdw_get_desc(struct pvr2_hdw *hdw)
322362306a36Sopenharmony_ci{
322462306a36Sopenharmony_ci	return hdw->hdw_desc->description;
322562306a36Sopenharmony_ci}
322662306a36Sopenharmony_ci
322762306a36Sopenharmony_ci
322862306a36Sopenharmony_ciconst char *pvr2_hdw_get_type(struct pvr2_hdw *hdw)
322962306a36Sopenharmony_ci{
323062306a36Sopenharmony_ci	return hdw->hdw_desc->shortname;
323162306a36Sopenharmony_ci}
323262306a36Sopenharmony_ci
323362306a36Sopenharmony_ci
323462306a36Sopenharmony_ciint pvr2_hdw_is_hsm(struct pvr2_hdw *hdw)
323562306a36Sopenharmony_ci{
323662306a36Sopenharmony_ci	int result;
323762306a36Sopenharmony_ci	LOCK_TAKE(hdw->ctl_lock); do {
323862306a36Sopenharmony_ci		hdw->cmd_buffer[0] = FX2CMD_GET_USB_SPEED;
323962306a36Sopenharmony_ci		result = pvr2_send_request(hdw,
324062306a36Sopenharmony_ci					   hdw->cmd_buffer,1,
324162306a36Sopenharmony_ci					   hdw->cmd_buffer,1);
324262306a36Sopenharmony_ci		if (result < 0) break;
324362306a36Sopenharmony_ci		result = (hdw->cmd_buffer[0] != 0);
324462306a36Sopenharmony_ci	} while(0); LOCK_GIVE(hdw->ctl_lock);
324562306a36Sopenharmony_ci	return result;
324662306a36Sopenharmony_ci}
324762306a36Sopenharmony_ci
324862306a36Sopenharmony_ci
324962306a36Sopenharmony_ci/* Execute poll of tuner status */
325062306a36Sopenharmony_civoid pvr2_hdw_execute_tuner_poll(struct pvr2_hdw *hdw)
325162306a36Sopenharmony_ci{
325262306a36Sopenharmony_ci	LOCK_TAKE(hdw->big_lock); do {
325362306a36Sopenharmony_ci		pvr2_hdw_status_poll(hdw);
325462306a36Sopenharmony_ci	} while (0); LOCK_GIVE(hdw->big_lock);
325562306a36Sopenharmony_ci}
325662306a36Sopenharmony_ci
325762306a36Sopenharmony_ci
325862306a36Sopenharmony_cistatic int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw)
325962306a36Sopenharmony_ci{
326062306a36Sopenharmony_ci	if (!hdw->cropcap_stale) {
326162306a36Sopenharmony_ci		return 0;
326262306a36Sopenharmony_ci	}
326362306a36Sopenharmony_ci	pvr2_hdw_status_poll(hdw);
326462306a36Sopenharmony_ci	if (hdw->cropcap_stale) {
326562306a36Sopenharmony_ci		return -EIO;
326662306a36Sopenharmony_ci	}
326762306a36Sopenharmony_ci	return 0;
326862306a36Sopenharmony_ci}
326962306a36Sopenharmony_ci
327062306a36Sopenharmony_ci
327162306a36Sopenharmony_ci/* Return information about cropping capabilities */
327262306a36Sopenharmony_ciint pvr2_hdw_get_cropcap(struct pvr2_hdw *hdw, struct v4l2_cropcap *pp)
327362306a36Sopenharmony_ci{
327462306a36Sopenharmony_ci	int stat = 0;
327562306a36Sopenharmony_ci	LOCK_TAKE(hdw->big_lock);
327662306a36Sopenharmony_ci	stat = pvr2_hdw_check_cropcap(hdw);
327762306a36Sopenharmony_ci	if (!stat) {
327862306a36Sopenharmony_ci		memcpy(pp, &hdw->cropcap_info, sizeof(hdw->cropcap_info));
327962306a36Sopenharmony_ci	}
328062306a36Sopenharmony_ci	LOCK_GIVE(hdw->big_lock);
328162306a36Sopenharmony_ci	return stat;
328262306a36Sopenharmony_ci}
328362306a36Sopenharmony_ci
328462306a36Sopenharmony_ci
328562306a36Sopenharmony_ci/* Return information about the tuner */
328662306a36Sopenharmony_ciint pvr2_hdw_get_tuner_status(struct pvr2_hdw *hdw,struct v4l2_tuner *vtp)
328762306a36Sopenharmony_ci{
328862306a36Sopenharmony_ci	LOCK_TAKE(hdw->big_lock);
328962306a36Sopenharmony_ci	do {
329062306a36Sopenharmony_ci		if (hdw->tuner_signal_stale) {
329162306a36Sopenharmony_ci			pvr2_hdw_status_poll(hdw);
329262306a36Sopenharmony_ci		}
329362306a36Sopenharmony_ci		memcpy(vtp,&hdw->tuner_signal_info,sizeof(struct v4l2_tuner));
329462306a36Sopenharmony_ci	} while (0);
329562306a36Sopenharmony_ci	LOCK_GIVE(hdw->big_lock);
329662306a36Sopenharmony_ci	return 0;
329762306a36Sopenharmony_ci}
329862306a36Sopenharmony_ci
329962306a36Sopenharmony_ci
330062306a36Sopenharmony_ci/* Get handle to video output stream */
330162306a36Sopenharmony_cistruct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *hp)
330262306a36Sopenharmony_ci{
330362306a36Sopenharmony_ci	return hp->vid_stream;
330462306a36Sopenharmony_ci}
330562306a36Sopenharmony_ci
330662306a36Sopenharmony_ci
330762306a36Sopenharmony_civoid pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw)
330862306a36Sopenharmony_ci{
330962306a36Sopenharmony_ci	int nr = pvr2_hdw_get_unit_number(hdw);
331062306a36Sopenharmony_ci	LOCK_TAKE(hdw->big_lock);
331162306a36Sopenharmony_ci	do {
331262306a36Sopenharmony_ci		pr_info("pvrusb2: =================  START STATUS CARD #%d  =================\n", nr);
331362306a36Sopenharmony_ci		v4l2_device_call_all(&hdw->v4l2_dev, 0, core, log_status);
331462306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INFO,"cx2341x config:");
331562306a36Sopenharmony_ci		cx2341x_log_status(&hdw->enc_ctl_state, "pvrusb2");
331662306a36Sopenharmony_ci		pvr2_hdw_state_log_state(hdw);
331762306a36Sopenharmony_ci		pr_info("pvrusb2: ==================  END STATUS CARD #%d  ==================\n", nr);
331862306a36Sopenharmony_ci	} while (0);
331962306a36Sopenharmony_ci	LOCK_GIVE(hdw->big_lock);
332062306a36Sopenharmony_ci}
332162306a36Sopenharmony_ci
332262306a36Sopenharmony_ci
332362306a36Sopenharmony_ci/* Grab EEPROM contents, needed for direct method. */
332462306a36Sopenharmony_ci#define EEPROM_SIZE 8192
332562306a36Sopenharmony_ci#define trace_eeprom(...) pvr2_trace(PVR2_TRACE_EEPROM,__VA_ARGS__)
332662306a36Sopenharmony_cistatic u8 *pvr2_full_eeprom_fetch(struct pvr2_hdw *hdw)
332762306a36Sopenharmony_ci{
332862306a36Sopenharmony_ci	struct i2c_msg msg[2];
332962306a36Sopenharmony_ci	u8 *eeprom;
333062306a36Sopenharmony_ci	u8 iadd[2];
333162306a36Sopenharmony_ci	u8 addr;
333262306a36Sopenharmony_ci	u16 eepromSize;
333362306a36Sopenharmony_ci	unsigned int offs;
333462306a36Sopenharmony_ci	int ret;
333562306a36Sopenharmony_ci	int mode16 = 0;
333662306a36Sopenharmony_ci	unsigned pcnt,tcnt;
333762306a36Sopenharmony_ci	eeprom = kzalloc(EEPROM_SIZE, GFP_KERNEL);
333862306a36Sopenharmony_ci	if (!eeprom) {
333962306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
334062306a36Sopenharmony_ci			   "Failed to allocate memory required to read eeprom");
334162306a36Sopenharmony_ci		return NULL;
334262306a36Sopenharmony_ci	}
334362306a36Sopenharmony_ci
334462306a36Sopenharmony_ci	trace_eeprom("Value for eeprom addr from controller was 0x%x",
334562306a36Sopenharmony_ci		     hdw->eeprom_addr);
334662306a36Sopenharmony_ci	addr = hdw->eeprom_addr;
334762306a36Sopenharmony_ci	/* Seems that if the high bit is set, then the *real* eeprom
334862306a36Sopenharmony_ci	   address is shifted right now bit position (noticed this in
334962306a36Sopenharmony_ci	   newer PVR USB2 hardware) */
335062306a36Sopenharmony_ci	if (addr & 0x80) addr >>= 1;
335162306a36Sopenharmony_ci
335262306a36Sopenharmony_ci	/* FX2 documentation states that a 16bit-addressed eeprom is
335362306a36Sopenharmony_ci	   expected if the I2C address is an odd number (yeah, this is
335462306a36Sopenharmony_ci	   strange but it's what they do) */
335562306a36Sopenharmony_ci	mode16 = (addr & 1);
335662306a36Sopenharmony_ci	eepromSize = (mode16 ? EEPROM_SIZE : 256);
335762306a36Sopenharmony_ci	trace_eeprom("Examining %d byte eeprom at location 0x%x using %d bit addressing",
335862306a36Sopenharmony_ci		     eepromSize, addr,
335962306a36Sopenharmony_ci		     mode16 ? 16 : 8);
336062306a36Sopenharmony_ci
336162306a36Sopenharmony_ci	msg[0].addr = addr;
336262306a36Sopenharmony_ci	msg[0].flags = 0;
336362306a36Sopenharmony_ci	msg[0].len = mode16 ? 2 : 1;
336462306a36Sopenharmony_ci	msg[0].buf = iadd;
336562306a36Sopenharmony_ci	msg[1].addr = addr;
336662306a36Sopenharmony_ci	msg[1].flags = I2C_M_RD;
336762306a36Sopenharmony_ci
336862306a36Sopenharmony_ci	/* We have to do the actual eeprom data fetch ourselves, because
336962306a36Sopenharmony_ci	   (1) we're only fetching part of the eeprom, and (2) if we were
337062306a36Sopenharmony_ci	   getting the whole thing our I2C driver can't grab it in one
337162306a36Sopenharmony_ci	   pass - which is what tveeprom is otherwise going to attempt */
337262306a36Sopenharmony_ci	for (tcnt = 0; tcnt < EEPROM_SIZE; tcnt += pcnt) {
337362306a36Sopenharmony_ci		pcnt = 16;
337462306a36Sopenharmony_ci		if (pcnt + tcnt > EEPROM_SIZE) pcnt = EEPROM_SIZE-tcnt;
337562306a36Sopenharmony_ci		offs = tcnt + (eepromSize - EEPROM_SIZE);
337662306a36Sopenharmony_ci		if (mode16) {
337762306a36Sopenharmony_ci			iadd[0] = offs >> 8;
337862306a36Sopenharmony_ci			iadd[1] = offs;
337962306a36Sopenharmony_ci		} else {
338062306a36Sopenharmony_ci			iadd[0] = offs;
338162306a36Sopenharmony_ci		}
338262306a36Sopenharmony_ci		msg[1].len = pcnt;
338362306a36Sopenharmony_ci		msg[1].buf = eeprom+tcnt;
338462306a36Sopenharmony_ci		if ((ret = i2c_transfer(&hdw->i2c_adap,
338562306a36Sopenharmony_ci					msg,ARRAY_SIZE(msg))) != 2) {
338662306a36Sopenharmony_ci			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
338762306a36Sopenharmony_ci				   "eeprom fetch set offs err=%d",ret);
338862306a36Sopenharmony_ci			kfree(eeprom);
338962306a36Sopenharmony_ci			return NULL;
339062306a36Sopenharmony_ci		}
339162306a36Sopenharmony_ci	}
339262306a36Sopenharmony_ci	return eeprom;
339362306a36Sopenharmony_ci}
339462306a36Sopenharmony_ci
339562306a36Sopenharmony_ci
339662306a36Sopenharmony_civoid pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *hdw,
339762306a36Sopenharmony_ci				int mode,
339862306a36Sopenharmony_ci				int enable_flag)
339962306a36Sopenharmony_ci{
340062306a36Sopenharmony_ci	int ret;
340162306a36Sopenharmony_ci	u16 address;
340262306a36Sopenharmony_ci	unsigned int pipe;
340362306a36Sopenharmony_ci	LOCK_TAKE(hdw->big_lock);
340462306a36Sopenharmony_ci	do {
340562306a36Sopenharmony_ci		if ((hdw->fw_buffer == NULL) == !enable_flag) break;
340662306a36Sopenharmony_ci
340762306a36Sopenharmony_ci		if (!enable_flag) {
340862306a36Sopenharmony_ci			pvr2_trace(PVR2_TRACE_FIRMWARE,
340962306a36Sopenharmony_ci				   "Cleaning up after CPU firmware fetch");
341062306a36Sopenharmony_ci			kfree(hdw->fw_buffer);
341162306a36Sopenharmony_ci			hdw->fw_buffer = NULL;
341262306a36Sopenharmony_ci			hdw->fw_size = 0;
341362306a36Sopenharmony_ci			if (hdw->fw_cpu_flag) {
341462306a36Sopenharmony_ci				/* Now release the CPU.  It will disconnect
341562306a36Sopenharmony_ci				   and reconnect later. */
341662306a36Sopenharmony_ci				pvr2_hdw_cpureset_assert(hdw,0);
341762306a36Sopenharmony_ci			}
341862306a36Sopenharmony_ci			break;
341962306a36Sopenharmony_ci		}
342062306a36Sopenharmony_ci
342162306a36Sopenharmony_ci		hdw->fw_cpu_flag = (mode != 2);
342262306a36Sopenharmony_ci		if (hdw->fw_cpu_flag) {
342362306a36Sopenharmony_ci			hdw->fw_size = (mode == 1) ? 0x4000 : 0x2000;
342462306a36Sopenharmony_ci			pvr2_trace(PVR2_TRACE_FIRMWARE,
342562306a36Sopenharmony_ci				   "Preparing to suck out CPU firmware (size=%u)",
342662306a36Sopenharmony_ci				   hdw->fw_size);
342762306a36Sopenharmony_ci			hdw->fw_buffer = kzalloc(hdw->fw_size,GFP_KERNEL);
342862306a36Sopenharmony_ci			if (!hdw->fw_buffer) {
342962306a36Sopenharmony_ci				hdw->fw_size = 0;
343062306a36Sopenharmony_ci				break;
343162306a36Sopenharmony_ci			}
343262306a36Sopenharmony_ci
343362306a36Sopenharmony_ci			/* We have to hold the CPU during firmware upload. */
343462306a36Sopenharmony_ci			pvr2_hdw_cpureset_assert(hdw,1);
343562306a36Sopenharmony_ci
343662306a36Sopenharmony_ci			/* download the firmware from address 0000-1fff in 2048
343762306a36Sopenharmony_ci			   (=0x800) bytes chunk. */
343862306a36Sopenharmony_ci
343962306a36Sopenharmony_ci			pvr2_trace(PVR2_TRACE_FIRMWARE,
344062306a36Sopenharmony_ci				   "Grabbing CPU firmware");
344162306a36Sopenharmony_ci			pipe = usb_rcvctrlpipe(hdw->usb_dev, 0);
344262306a36Sopenharmony_ci			for(address = 0; address < hdw->fw_size;
344362306a36Sopenharmony_ci			    address += 0x800) {
344462306a36Sopenharmony_ci				ret = usb_control_msg(hdw->usb_dev,pipe,
344562306a36Sopenharmony_ci						      0xa0,0xc0,
344662306a36Sopenharmony_ci						      address,0,
344762306a36Sopenharmony_ci						      hdw->fw_buffer+address,
344862306a36Sopenharmony_ci						      0x800,1000);
344962306a36Sopenharmony_ci				if (ret < 0) break;
345062306a36Sopenharmony_ci			}
345162306a36Sopenharmony_ci
345262306a36Sopenharmony_ci			pvr2_trace(PVR2_TRACE_FIRMWARE,
345362306a36Sopenharmony_ci				   "Done grabbing CPU firmware");
345462306a36Sopenharmony_ci		} else {
345562306a36Sopenharmony_ci			pvr2_trace(PVR2_TRACE_FIRMWARE,
345662306a36Sopenharmony_ci				   "Sucking down EEPROM contents");
345762306a36Sopenharmony_ci			hdw->fw_buffer = pvr2_full_eeprom_fetch(hdw);
345862306a36Sopenharmony_ci			if (!hdw->fw_buffer) {
345962306a36Sopenharmony_ci				pvr2_trace(PVR2_TRACE_FIRMWARE,
346062306a36Sopenharmony_ci					   "EEPROM content suck failed.");
346162306a36Sopenharmony_ci				break;
346262306a36Sopenharmony_ci			}
346362306a36Sopenharmony_ci			hdw->fw_size = EEPROM_SIZE;
346462306a36Sopenharmony_ci			pvr2_trace(PVR2_TRACE_FIRMWARE,
346562306a36Sopenharmony_ci				   "Done sucking down EEPROM contents");
346662306a36Sopenharmony_ci		}
346762306a36Sopenharmony_ci	} while (0);
346862306a36Sopenharmony_ci	LOCK_GIVE(hdw->big_lock);
346962306a36Sopenharmony_ci}
347062306a36Sopenharmony_ci
347162306a36Sopenharmony_ci
347262306a36Sopenharmony_ci/* Return true if we're in a mode for retrieval CPU firmware */
347362306a36Sopenharmony_ciint pvr2_hdw_cpufw_get_enabled(struct pvr2_hdw *hdw)
347462306a36Sopenharmony_ci{
347562306a36Sopenharmony_ci	return hdw->fw_buffer != NULL;
347662306a36Sopenharmony_ci}
347762306a36Sopenharmony_ci
347862306a36Sopenharmony_ci
347962306a36Sopenharmony_ciint pvr2_hdw_cpufw_get(struct pvr2_hdw *hdw,unsigned int offs,
348062306a36Sopenharmony_ci		       char *buf,unsigned int cnt)
348162306a36Sopenharmony_ci{
348262306a36Sopenharmony_ci	int ret = -EINVAL;
348362306a36Sopenharmony_ci	LOCK_TAKE(hdw->big_lock);
348462306a36Sopenharmony_ci	do {
348562306a36Sopenharmony_ci		if (!buf) break;
348662306a36Sopenharmony_ci		if (!cnt) break;
348762306a36Sopenharmony_ci
348862306a36Sopenharmony_ci		if (!hdw->fw_buffer) {
348962306a36Sopenharmony_ci			ret = -EIO;
349062306a36Sopenharmony_ci			break;
349162306a36Sopenharmony_ci		}
349262306a36Sopenharmony_ci
349362306a36Sopenharmony_ci		if (offs >= hdw->fw_size) {
349462306a36Sopenharmony_ci			pvr2_trace(PVR2_TRACE_FIRMWARE,
349562306a36Sopenharmony_ci				   "Read firmware data offs=%d EOF",
349662306a36Sopenharmony_ci				   offs);
349762306a36Sopenharmony_ci			ret = 0;
349862306a36Sopenharmony_ci			break;
349962306a36Sopenharmony_ci		}
350062306a36Sopenharmony_ci
350162306a36Sopenharmony_ci		if (offs + cnt > hdw->fw_size) cnt = hdw->fw_size - offs;
350262306a36Sopenharmony_ci
350362306a36Sopenharmony_ci		memcpy(buf,hdw->fw_buffer+offs,cnt);
350462306a36Sopenharmony_ci
350562306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_FIRMWARE,
350662306a36Sopenharmony_ci			   "Read firmware data offs=%d cnt=%d",
350762306a36Sopenharmony_ci			   offs,cnt);
350862306a36Sopenharmony_ci		ret = cnt;
350962306a36Sopenharmony_ci	} while (0);
351062306a36Sopenharmony_ci	LOCK_GIVE(hdw->big_lock);
351162306a36Sopenharmony_ci
351262306a36Sopenharmony_ci	return ret;
351362306a36Sopenharmony_ci}
351462306a36Sopenharmony_ci
351562306a36Sopenharmony_ci
351662306a36Sopenharmony_ciint pvr2_hdw_v4l_get_minor_number(struct pvr2_hdw *hdw,
351762306a36Sopenharmony_ci				  enum pvr2_v4l_type index)
351862306a36Sopenharmony_ci{
351962306a36Sopenharmony_ci	switch (index) {
352062306a36Sopenharmony_ci	case pvr2_v4l_type_video: return hdw->v4l_minor_number_video;
352162306a36Sopenharmony_ci	case pvr2_v4l_type_vbi: return hdw->v4l_minor_number_vbi;
352262306a36Sopenharmony_ci	case pvr2_v4l_type_radio: return hdw->v4l_minor_number_radio;
352362306a36Sopenharmony_ci	default: return -1;
352462306a36Sopenharmony_ci	}
352562306a36Sopenharmony_ci}
352662306a36Sopenharmony_ci
352762306a36Sopenharmony_ci
352862306a36Sopenharmony_ci/* Store a v4l minor device number */
352962306a36Sopenharmony_civoid pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *hdw,
353062306a36Sopenharmony_ci				     enum pvr2_v4l_type index,int v)
353162306a36Sopenharmony_ci{
353262306a36Sopenharmony_ci	switch (index) {
353362306a36Sopenharmony_ci	case pvr2_v4l_type_video: hdw->v4l_minor_number_video = v;break;
353462306a36Sopenharmony_ci	case pvr2_v4l_type_vbi: hdw->v4l_minor_number_vbi = v;break;
353562306a36Sopenharmony_ci	case pvr2_v4l_type_radio: hdw->v4l_minor_number_radio = v;break;
353662306a36Sopenharmony_ci	default: break;
353762306a36Sopenharmony_ci	}
353862306a36Sopenharmony_ci}
353962306a36Sopenharmony_ci
354062306a36Sopenharmony_ci
354162306a36Sopenharmony_cistatic void pvr2_ctl_write_complete(struct urb *urb)
354262306a36Sopenharmony_ci{
354362306a36Sopenharmony_ci	struct pvr2_hdw *hdw = urb->context;
354462306a36Sopenharmony_ci	hdw->ctl_write_pend_flag = 0;
354562306a36Sopenharmony_ci	if (hdw->ctl_read_pend_flag) return;
354662306a36Sopenharmony_ci	complete(&hdw->ctl_done);
354762306a36Sopenharmony_ci}
354862306a36Sopenharmony_ci
354962306a36Sopenharmony_ci
355062306a36Sopenharmony_cistatic void pvr2_ctl_read_complete(struct urb *urb)
355162306a36Sopenharmony_ci{
355262306a36Sopenharmony_ci	struct pvr2_hdw *hdw = urb->context;
355362306a36Sopenharmony_ci	hdw->ctl_read_pend_flag = 0;
355462306a36Sopenharmony_ci	if (hdw->ctl_write_pend_flag) return;
355562306a36Sopenharmony_ci	complete(&hdw->ctl_done);
355662306a36Sopenharmony_ci}
355762306a36Sopenharmony_ci
355862306a36Sopenharmony_cistruct hdw_timer {
355962306a36Sopenharmony_ci	struct timer_list timer;
356062306a36Sopenharmony_ci	struct pvr2_hdw *hdw;
356162306a36Sopenharmony_ci};
356262306a36Sopenharmony_ci
356362306a36Sopenharmony_cistatic void pvr2_ctl_timeout(struct timer_list *t)
356462306a36Sopenharmony_ci{
356562306a36Sopenharmony_ci	struct hdw_timer *timer = from_timer(timer, t, timer);
356662306a36Sopenharmony_ci	struct pvr2_hdw *hdw = timer->hdw;
356762306a36Sopenharmony_ci
356862306a36Sopenharmony_ci	if (hdw->ctl_write_pend_flag || hdw->ctl_read_pend_flag) {
356962306a36Sopenharmony_ci		hdw->ctl_timeout_flag = !0;
357062306a36Sopenharmony_ci		if (hdw->ctl_write_pend_flag)
357162306a36Sopenharmony_ci			usb_unlink_urb(hdw->ctl_write_urb);
357262306a36Sopenharmony_ci		if (hdw->ctl_read_pend_flag)
357362306a36Sopenharmony_ci			usb_unlink_urb(hdw->ctl_read_urb);
357462306a36Sopenharmony_ci	}
357562306a36Sopenharmony_ci}
357662306a36Sopenharmony_ci
357762306a36Sopenharmony_ci
357862306a36Sopenharmony_ci/* Issue a command and get a response from the device.  This extended
357962306a36Sopenharmony_ci   version includes a probe flag (which if set means that device errors
358062306a36Sopenharmony_ci   should not be logged or treated as fatal) and a timeout in jiffies.
358162306a36Sopenharmony_ci   This can be used to non-lethally probe the health of endpoint 1. */
358262306a36Sopenharmony_cistatic int pvr2_send_request_ex(struct pvr2_hdw *hdw,
358362306a36Sopenharmony_ci				unsigned int timeout,int probe_fl,
358462306a36Sopenharmony_ci				void *write_data,unsigned int write_len,
358562306a36Sopenharmony_ci				void *read_data,unsigned int read_len)
358662306a36Sopenharmony_ci{
358762306a36Sopenharmony_ci	unsigned int idx;
358862306a36Sopenharmony_ci	int status = 0;
358962306a36Sopenharmony_ci	struct hdw_timer timer = {
359062306a36Sopenharmony_ci		.hdw = hdw,
359162306a36Sopenharmony_ci	};
359262306a36Sopenharmony_ci
359362306a36Sopenharmony_ci	if (!hdw->ctl_lock_held) {
359462306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
359562306a36Sopenharmony_ci			   "Attempted to execute control transfer without lock!!");
359662306a36Sopenharmony_ci		return -EDEADLK;
359762306a36Sopenharmony_ci	}
359862306a36Sopenharmony_ci	if (!hdw->flag_ok && !probe_fl) {
359962306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
360062306a36Sopenharmony_ci			   "Attempted to execute control transfer when device not ok");
360162306a36Sopenharmony_ci		return -EIO;
360262306a36Sopenharmony_ci	}
360362306a36Sopenharmony_ci	if (!(hdw->ctl_read_urb && hdw->ctl_write_urb)) {
360462306a36Sopenharmony_ci		if (!probe_fl) {
360562306a36Sopenharmony_ci			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
360662306a36Sopenharmony_ci				   "Attempted to execute control transfer when USB is disconnected");
360762306a36Sopenharmony_ci		}
360862306a36Sopenharmony_ci		return -ENOTTY;
360962306a36Sopenharmony_ci	}
361062306a36Sopenharmony_ci
361162306a36Sopenharmony_ci	/* Ensure that we have sane parameters */
361262306a36Sopenharmony_ci	if (!write_data) write_len = 0;
361362306a36Sopenharmony_ci	if (!read_data) read_len = 0;
361462306a36Sopenharmony_ci	if (write_len > PVR2_CTL_BUFFSIZE) {
361562306a36Sopenharmony_ci		pvr2_trace(
361662306a36Sopenharmony_ci			PVR2_TRACE_ERROR_LEGS,
361762306a36Sopenharmony_ci			"Attempted to execute %d byte control-write transfer (limit=%d)",
361862306a36Sopenharmony_ci			write_len,PVR2_CTL_BUFFSIZE);
361962306a36Sopenharmony_ci		return -EINVAL;
362062306a36Sopenharmony_ci	}
362162306a36Sopenharmony_ci	if (read_len > PVR2_CTL_BUFFSIZE) {
362262306a36Sopenharmony_ci		pvr2_trace(
362362306a36Sopenharmony_ci			PVR2_TRACE_ERROR_LEGS,
362462306a36Sopenharmony_ci			"Attempted to execute %d byte control-read transfer (limit=%d)",
362562306a36Sopenharmony_ci			write_len,PVR2_CTL_BUFFSIZE);
362662306a36Sopenharmony_ci		return -EINVAL;
362762306a36Sopenharmony_ci	}
362862306a36Sopenharmony_ci	if ((!write_len) && (!read_len)) {
362962306a36Sopenharmony_ci		pvr2_trace(
363062306a36Sopenharmony_ci			PVR2_TRACE_ERROR_LEGS,
363162306a36Sopenharmony_ci			"Attempted to execute null control transfer?");
363262306a36Sopenharmony_ci		return -EINVAL;
363362306a36Sopenharmony_ci	}
363462306a36Sopenharmony_ci
363562306a36Sopenharmony_ci
363662306a36Sopenharmony_ci	hdw->cmd_debug_state = 1;
363762306a36Sopenharmony_ci	if (write_len && write_data)
363862306a36Sopenharmony_ci		hdw->cmd_debug_code = ((unsigned char *)write_data)[0];
363962306a36Sopenharmony_ci	else
364062306a36Sopenharmony_ci		hdw->cmd_debug_code = 0;
364162306a36Sopenharmony_ci	hdw->cmd_debug_write_len = write_len;
364262306a36Sopenharmony_ci	hdw->cmd_debug_read_len = read_len;
364362306a36Sopenharmony_ci
364462306a36Sopenharmony_ci	/* Initialize common stuff */
364562306a36Sopenharmony_ci	init_completion(&hdw->ctl_done);
364662306a36Sopenharmony_ci	hdw->ctl_timeout_flag = 0;
364762306a36Sopenharmony_ci	hdw->ctl_write_pend_flag = 0;
364862306a36Sopenharmony_ci	hdw->ctl_read_pend_flag = 0;
364962306a36Sopenharmony_ci	timer_setup_on_stack(&timer.timer, pvr2_ctl_timeout, 0);
365062306a36Sopenharmony_ci	timer.timer.expires = jiffies + timeout;
365162306a36Sopenharmony_ci
365262306a36Sopenharmony_ci	if (write_len && write_data) {
365362306a36Sopenharmony_ci		hdw->cmd_debug_state = 2;
365462306a36Sopenharmony_ci		/* Transfer write data to internal buffer */
365562306a36Sopenharmony_ci		for (idx = 0; idx < write_len; idx++) {
365662306a36Sopenharmony_ci			hdw->ctl_write_buffer[idx] =
365762306a36Sopenharmony_ci				((unsigned char *)write_data)[idx];
365862306a36Sopenharmony_ci		}
365962306a36Sopenharmony_ci		/* Initiate a write request */
366062306a36Sopenharmony_ci		usb_fill_bulk_urb(hdw->ctl_write_urb,
366162306a36Sopenharmony_ci				  hdw->usb_dev,
366262306a36Sopenharmony_ci				  usb_sndbulkpipe(hdw->usb_dev,
366362306a36Sopenharmony_ci						  PVR2_CTL_WRITE_ENDPOINT),
366462306a36Sopenharmony_ci				  hdw->ctl_write_buffer,
366562306a36Sopenharmony_ci				  write_len,
366662306a36Sopenharmony_ci				  pvr2_ctl_write_complete,
366762306a36Sopenharmony_ci				  hdw);
366862306a36Sopenharmony_ci		hdw->ctl_write_urb->actual_length = 0;
366962306a36Sopenharmony_ci		hdw->ctl_write_pend_flag = !0;
367062306a36Sopenharmony_ci		if (usb_urb_ep_type_check(hdw->ctl_write_urb)) {
367162306a36Sopenharmony_ci			pvr2_trace(
367262306a36Sopenharmony_ci				PVR2_TRACE_ERROR_LEGS,
367362306a36Sopenharmony_ci				"Invalid write control endpoint");
367462306a36Sopenharmony_ci			return -EINVAL;
367562306a36Sopenharmony_ci		}
367662306a36Sopenharmony_ci		status = usb_submit_urb(hdw->ctl_write_urb,GFP_KERNEL);
367762306a36Sopenharmony_ci		if (status < 0) {
367862306a36Sopenharmony_ci			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
367962306a36Sopenharmony_ci				   "Failed to submit write-control URB status=%d",
368062306a36Sopenharmony_cistatus);
368162306a36Sopenharmony_ci			hdw->ctl_write_pend_flag = 0;
368262306a36Sopenharmony_ci			goto done;
368362306a36Sopenharmony_ci		}
368462306a36Sopenharmony_ci	}
368562306a36Sopenharmony_ci
368662306a36Sopenharmony_ci	if (read_len) {
368762306a36Sopenharmony_ci		hdw->cmd_debug_state = 3;
368862306a36Sopenharmony_ci		memset(hdw->ctl_read_buffer,0x43,read_len);
368962306a36Sopenharmony_ci		/* Initiate a read request */
369062306a36Sopenharmony_ci		usb_fill_bulk_urb(hdw->ctl_read_urb,
369162306a36Sopenharmony_ci				  hdw->usb_dev,
369262306a36Sopenharmony_ci				  usb_rcvbulkpipe(hdw->usb_dev,
369362306a36Sopenharmony_ci						  PVR2_CTL_READ_ENDPOINT),
369462306a36Sopenharmony_ci				  hdw->ctl_read_buffer,
369562306a36Sopenharmony_ci				  read_len,
369662306a36Sopenharmony_ci				  pvr2_ctl_read_complete,
369762306a36Sopenharmony_ci				  hdw);
369862306a36Sopenharmony_ci		hdw->ctl_read_urb->actual_length = 0;
369962306a36Sopenharmony_ci		hdw->ctl_read_pend_flag = !0;
370062306a36Sopenharmony_ci		if (usb_urb_ep_type_check(hdw->ctl_read_urb)) {
370162306a36Sopenharmony_ci			pvr2_trace(
370262306a36Sopenharmony_ci				PVR2_TRACE_ERROR_LEGS,
370362306a36Sopenharmony_ci				"Invalid read control endpoint");
370462306a36Sopenharmony_ci			return -EINVAL;
370562306a36Sopenharmony_ci		}
370662306a36Sopenharmony_ci		status = usb_submit_urb(hdw->ctl_read_urb,GFP_KERNEL);
370762306a36Sopenharmony_ci		if (status < 0) {
370862306a36Sopenharmony_ci			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
370962306a36Sopenharmony_ci				   "Failed to submit read-control URB status=%d",
371062306a36Sopenharmony_cistatus);
371162306a36Sopenharmony_ci			hdw->ctl_read_pend_flag = 0;
371262306a36Sopenharmony_ci			goto done;
371362306a36Sopenharmony_ci		}
371462306a36Sopenharmony_ci	}
371562306a36Sopenharmony_ci
371662306a36Sopenharmony_ci	/* Start timer */
371762306a36Sopenharmony_ci	add_timer(&timer.timer);
371862306a36Sopenharmony_ci
371962306a36Sopenharmony_ci	/* Now wait for all I/O to complete */
372062306a36Sopenharmony_ci	hdw->cmd_debug_state = 4;
372162306a36Sopenharmony_ci	while (hdw->ctl_write_pend_flag || hdw->ctl_read_pend_flag) {
372262306a36Sopenharmony_ci		wait_for_completion(&hdw->ctl_done);
372362306a36Sopenharmony_ci	}
372462306a36Sopenharmony_ci	hdw->cmd_debug_state = 5;
372562306a36Sopenharmony_ci
372662306a36Sopenharmony_ci	/* Stop timer */
372762306a36Sopenharmony_ci	del_timer_sync(&timer.timer);
372862306a36Sopenharmony_ci
372962306a36Sopenharmony_ci	hdw->cmd_debug_state = 6;
373062306a36Sopenharmony_ci	status = 0;
373162306a36Sopenharmony_ci
373262306a36Sopenharmony_ci	if (hdw->ctl_timeout_flag) {
373362306a36Sopenharmony_ci		status = -ETIMEDOUT;
373462306a36Sopenharmony_ci		if (!probe_fl) {
373562306a36Sopenharmony_ci			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
373662306a36Sopenharmony_ci				   "Timed out control-write");
373762306a36Sopenharmony_ci		}
373862306a36Sopenharmony_ci		goto done;
373962306a36Sopenharmony_ci	}
374062306a36Sopenharmony_ci
374162306a36Sopenharmony_ci	if (write_len) {
374262306a36Sopenharmony_ci		/* Validate results of write request */
374362306a36Sopenharmony_ci		if ((hdw->ctl_write_urb->status != 0) &&
374462306a36Sopenharmony_ci		    (hdw->ctl_write_urb->status != -ENOENT) &&
374562306a36Sopenharmony_ci		    (hdw->ctl_write_urb->status != -ESHUTDOWN) &&
374662306a36Sopenharmony_ci		    (hdw->ctl_write_urb->status != -ECONNRESET)) {
374762306a36Sopenharmony_ci			/* USB subsystem is reporting some kind of failure
374862306a36Sopenharmony_ci			   on the write */
374962306a36Sopenharmony_ci			status = hdw->ctl_write_urb->status;
375062306a36Sopenharmony_ci			if (!probe_fl) {
375162306a36Sopenharmony_ci				pvr2_trace(PVR2_TRACE_ERROR_LEGS,
375262306a36Sopenharmony_ci					   "control-write URB failure, status=%d",
375362306a36Sopenharmony_ci					   status);
375462306a36Sopenharmony_ci			}
375562306a36Sopenharmony_ci			goto done;
375662306a36Sopenharmony_ci		}
375762306a36Sopenharmony_ci		if (hdw->ctl_write_urb->actual_length < write_len) {
375862306a36Sopenharmony_ci			/* Failed to write enough data */
375962306a36Sopenharmony_ci			status = -EIO;
376062306a36Sopenharmony_ci			if (!probe_fl) {
376162306a36Sopenharmony_ci				pvr2_trace(PVR2_TRACE_ERROR_LEGS,
376262306a36Sopenharmony_ci					   "control-write URB short, expected=%d got=%d",
376362306a36Sopenharmony_ci					   write_len,
376462306a36Sopenharmony_ci					   hdw->ctl_write_urb->actual_length);
376562306a36Sopenharmony_ci			}
376662306a36Sopenharmony_ci			goto done;
376762306a36Sopenharmony_ci		}
376862306a36Sopenharmony_ci	}
376962306a36Sopenharmony_ci	if (read_len && read_data) {
377062306a36Sopenharmony_ci		/* Validate results of read request */
377162306a36Sopenharmony_ci		if ((hdw->ctl_read_urb->status != 0) &&
377262306a36Sopenharmony_ci		    (hdw->ctl_read_urb->status != -ENOENT) &&
377362306a36Sopenharmony_ci		    (hdw->ctl_read_urb->status != -ESHUTDOWN) &&
377462306a36Sopenharmony_ci		    (hdw->ctl_read_urb->status != -ECONNRESET)) {
377562306a36Sopenharmony_ci			/* USB subsystem is reporting some kind of failure
377662306a36Sopenharmony_ci			   on the read */
377762306a36Sopenharmony_ci			status = hdw->ctl_read_urb->status;
377862306a36Sopenharmony_ci			if (!probe_fl) {
377962306a36Sopenharmony_ci				pvr2_trace(PVR2_TRACE_ERROR_LEGS,
378062306a36Sopenharmony_ci					   "control-read URB failure, status=%d",
378162306a36Sopenharmony_ci					   status);
378262306a36Sopenharmony_ci			}
378362306a36Sopenharmony_ci			goto done;
378462306a36Sopenharmony_ci		}
378562306a36Sopenharmony_ci		if (hdw->ctl_read_urb->actual_length < read_len) {
378662306a36Sopenharmony_ci			/* Failed to read enough data */
378762306a36Sopenharmony_ci			status = -EIO;
378862306a36Sopenharmony_ci			if (!probe_fl) {
378962306a36Sopenharmony_ci				pvr2_trace(PVR2_TRACE_ERROR_LEGS,
379062306a36Sopenharmony_ci					   "control-read URB short, expected=%d got=%d",
379162306a36Sopenharmony_ci					   read_len,
379262306a36Sopenharmony_ci					   hdw->ctl_read_urb->actual_length);
379362306a36Sopenharmony_ci			}
379462306a36Sopenharmony_ci			goto done;
379562306a36Sopenharmony_ci		}
379662306a36Sopenharmony_ci		/* Transfer retrieved data out from internal buffer */
379762306a36Sopenharmony_ci		for (idx = 0; idx < read_len; idx++) {
379862306a36Sopenharmony_ci			((unsigned char *)read_data)[idx] =
379962306a36Sopenharmony_ci				hdw->ctl_read_buffer[idx];
380062306a36Sopenharmony_ci		}
380162306a36Sopenharmony_ci	}
380262306a36Sopenharmony_ci
380362306a36Sopenharmony_ci done:
380462306a36Sopenharmony_ci
380562306a36Sopenharmony_ci	hdw->cmd_debug_state = 0;
380662306a36Sopenharmony_ci	if ((status < 0) && (!probe_fl)) {
380762306a36Sopenharmony_ci		pvr2_hdw_render_useless(hdw);
380862306a36Sopenharmony_ci	}
380962306a36Sopenharmony_ci	destroy_timer_on_stack(&timer.timer);
381062306a36Sopenharmony_ci
381162306a36Sopenharmony_ci	return status;
381262306a36Sopenharmony_ci}
381362306a36Sopenharmony_ci
381462306a36Sopenharmony_ci
381562306a36Sopenharmony_ciint pvr2_send_request(struct pvr2_hdw *hdw,
381662306a36Sopenharmony_ci		      void *write_data,unsigned int write_len,
381762306a36Sopenharmony_ci		      void *read_data,unsigned int read_len)
381862306a36Sopenharmony_ci{
381962306a36Sopenharmony_ci	return pvr2_send_request_ex(hdw,HZ*4,0,
382062306a36Sopenharmony_ci				    write_data,write_len,
382162306a36Sopenharmony_ci				    read_data,read_len);
382262306a36Sopenharmony_ci}
382362306a36Sopenharmony_ci
382462306a36Sopenharmony_ci
382562306a36Sopenharmony_cistatic int pvr2_issue_simple_cmd(struct pvr2_hdw *hdw,u32 cmdcode)
382662306a36Sopenharmony_ci{
382762306a36Sopenharmony_ci	int ret;
382862306a36Sopenharmony_ci	unsigned int cnt = 1;
382962306a36Sopenharmony_ci	unsigned int args = 0;
383062306a36Sopenharmony_ci	LOCK_TAKE(hdw->ctl_lock);
383162306a36Sopenharmony_ci	hdw->cmd_buffer[0] = cmdcode & 0xffu;
383262306a36Sopenharmony_ci	args = (cmdcode >> 8) & 0xffu;
383362306a36Sopenharmony_ci	args = (args > 2) ? 2 : args;
383462306a36Sopenharmony_ci	if (args) {
383562306a36Sopenharmony_ci		cnt += args;
383662306a36Sopenharmony_ci		hdw->cmd_buffer[1] = (cmdcode >> 16) & 0xffu;
383762306a36Sopenharmony_ci		if (args > 1) {
383862306a36Sopenharmony_ci			hdw->cmd_buffer[2] = (cmdcode >> 24) & 0xffu;
383962306a36Sopenharmony_ci		}
384062306a36Sopenharmony_ci	}
384162306a36Sopenharmony_ci	if (pvrusb2_debug & PVR2_TRACE_INIT) {
384262306a36Sopenharmony_ci		unsigned int idx;
384362306a36Sopenharmony_ci		unsigned int ccnt,bcnt;
384462306a36Sopenharmony_ci		char tbuf[50];
384562306a36Sopenharmony_ci		cmdcode &= 0xffu;
384662306a36Sopenharmony_ci		bcnt = 0;
384762306a36Sopenharmony_ci		ccnt = scnprintf(tbuf+bcnt,
384862306a36Sopenharmony_ci				 sizeof(tbuf)-bcnt,
384962306a36Sopenharmony_ci				 "Sending FX2 command 0x%x",cmdcode);
385062306a36Sopenharmony_ci		bcnt += ccnt;
385162306a36Sopenharmony_ci		for (idx = 0; idx < ARRAY_SIZE(pvr2_fx2cmd_desc); idx++) {
385262306a36Sopenharmony_ci			if (pvr2_fx2cmd_desc[idx].id == cmdcode) {
385362306a36Sopenharmony_ci				ccnt = scnprintf(tbuf+bcnt,
385462306a36Sopenharmony_ci						 sizeof(tbuf)-bcnt,
385562306a36Sopenharmony_ci						 " \"%s\"",
385662306a36Sopenharmony_ci						 pvr2_fx2cmd_desc[idx].desc);
385762306a36Sopenharmony_ci				bcnt += ccnt;
385862306a36Sopenharmony_ci				break;
385962306a36Sopenharmony_ci			}
386062306a36Sopenharmony_ci		}
386162306a36Sopenharmony_ci		if (args) {
386262306a36Sopenharmony_ci			ccnt = scnprintf(tbuf+bcnt,
386362306a36Sopenharmony_ci					 sizeof(tbuf)-bcnt,
386462306a36Sopenharmony_ci					 " (%u",hdw->cmd_buffer[1]);
386562306a36Sopenharmony_ci			bcnt += ccnt;
386662306a36Sopenharmony_ci			if (args > 1) {
386762306a36Sopenharmony_ci				ccnt = scnprintf(tbuf+bcnt,
386862306a36Sopenharmony_ci						 sizeof(tbuf)-bcnt,
386962306a36Sopenharmony_ci						 ",%u",hdw->cmd_buffer[2]);
387062306a36Sopenharmony_ci				bcnt += ccnt;
387162306a36Sopenharmony_ci			}
387262306a36Sopenharmony_ci			ccnt = scnprintf(tbuf+bcnt,
387362306a36Sopenharmony_ci					 sizeof(tbuf)-bcnt,
387462306a36Sopenharmony_ci					 ")");
387562306a36Sopenharmony_ci			bcnt += ccnt;
387662306a36Sopenharmony_ci		}
387762306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INIT,"%.*s",bcnt,tbuf);
387862306a36Sopenharmony_ci	}
387962306a36Sopenharmony_ci	ret = pvr2_send_request(hdw,hdw->cmd_buffer,cnt,NULL,0);
388062306a36Sopenharmony_ci	LOCK_GIVE(hdw->ctl_lock);
388162306a36Sopenharmony_ci	return ret;
388262306a36Sopenharmony_ci}
388362306a36Sopenharmony_ci
388462306a36Sopenharmony_ci
388562306a36Sopenharmony_ciint pvr2_write_register(struct pvr2_hdw *hdw, u16 reg, u32 data)
388662306a36Sopenharmony_ci{
388762306a36Sopenharmony_ci	int ret;
388862306a36Sopenharmony_ci
388962306a36Sopenharmony_ci	LOCK_TAKE(hdw->ctl_lock);
389062306a36Sopenharmony_ci
389162306a36Sopenharmony_ci	hdw->cmd_buffer[0] = FX2CMD_REG_WRITE;  /* write register prefix */
389262306a36Sopenharmony_ci	PVR2_DECOMPOSE_LE(hdw->cmd_buffer,1,data);
389362306a36Sopenharmony_ci	hdw->cmd_buffer[5] = 0;
389462306a36Sopenharmony_ci	hdw->cmd_buffer[6] = (reg >> 8) & 0xff;
389562306a36Sopenharmony_ci	hdw->cmd_buffer[7] = reg & 0xff;
389662306a36Sopenharmony_ci
389762306a36Sopenharmony_ci
389862306a36Sopenharmony_ci	ret = pvr2_send_request(hdw, hdw->cmd_buffer, 8, hdw->cmd_buffer, 0);
389962306a36Sopenharmony_ci
390062306a36Sopenharmony_ci	LOCK_GIVE(hdw->ctl_lock);
390162306a36Sopenharmony_ci
390262306a36Sopenharmony_ci	return ret;
390362306a36Sopenharmony_ci}
390462306a36Sopenharmony_ci
390562306a36Sopenharmony_ci
390662306a36Sopenharmony_cistatic int pvr2_read_register(struct pvr2_hdw *hdw, u16 reg, u32 *data)
390762306a36Sopenharmony_ci{
390862306a36Sopenharmony_ci	int ret = 0;
390962306a36Sopenharmony_ci
391062306a36Sopenharmony_ci	LOCK_TAKE(hdw->ctl_lock);
391162306a36Sopenharmony_ci
391262306a36Sopenharmony_ci	hdw->cmd_buffer[0] = FX2CMD_REG_READ;  /* read register prefix */
391362306a36Sopenharmony_ci	hdw->cmd_buffer[1] = 0;
391462306a36Sopenharmony_ci	hdw->cmd_buffer[2] = 0;
391562306a36Sopenharmony_ci	hdw->cmd_buffer[3] = 0;
391662306a36Sopenharmony_ci	hdw->cmd_buffer[4] = 0;
391762306a36Sopenharmony_ci	hdw->cmd_buffer[5] = 0;
391862306a36Sopenharmony_ci	hdw->cmd_buffer[6] = (reg >> 8) & 0xff;
391962306a36Sopenharmony_ci	hdw->cmd_buffer[7] = reg & 0xff;
392062306a36Sopenharmony_ci
392162306a36Sopenharmony_ci	ret |= pvr2_send_request(hdw, hdw->cmd_buffer, 8, hdw->cmd_buffer, 4);
392262306a36Sopenharmony_ci	*data = PVR2_COMPOSE_LE(hdw->cmd_buffer,0);
392362306a36Sopenharmony_ci
392462306a36Sopenharmony_ci	LOCK_GIVE(hdw->ctl_lock);
392562306a36Sopenharmony_ci
392662306a36Sopenharmony_ci	return ret;
392762306a36Sopenharmony_ci}
392862306a36Sopenharmony_ci
392962306a36Sopenharmony_ci
393062306a36Sopenharmony_civoid pvr2_hdw_render_useless(struct pvr2_hdw *hdw)
393162306a36Sopenharmony_ci{
393262306a36Sopenharmony_ci	if (!hdw->flag_ok) return;
393362306a36Sopenharmony_ci	pvr2_trace(PVR2_TRACE_ERROR_LEGS,
393462306a36Sopenharmony_ci		   "Device being rendered inoperable");
393562306a36Sopenharmony_ci	if (hdw->vid_stream) {
393662306a36Sopenharmony_ci		pvr2_stream_setup(hdw->vid_stream,NULL,0,0);
393762306a36Sopenharmony_ci	}
393862306a36Sopenharmony_ci	hdw->flag_ok = 0;
393962306a36Sopenharmony_ci	trace_stbit("flag_ok",hdw->flag_ok);
394062306a36Sopenharmony_ci	pvr2_hdw_state_sched(hdw);
394162306a36Sopenharmony_ci}
394262306a36Sopenharmony_ci
394362306a36Sopenharmony_ci
394462306a36Sopenharmony_civoid pvr2_hdw_device_reset(struct pvr2_hdw *hdw)
394562306a36Sopenharmony_ci{
394662306a36Sopenharmony_ci	int ret;
394762306a36Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INIT,"Performing a device reset...");
394862306a36Sopenharmony_ci	ret = usb_lock_device_for_reset(hdw->usb_dev,NULL);
394962306a36Sopenharmony_ci	if (ret == 0) {
395062306a36Sopenharmony_ci		ret = usb_reset_device(hdw->usb_dev);
395162306a36Sopenharmony_ci		usb_unlock_device(hdw->usb_dev);
395262306a36Sopenharmony_ci	} else {
395362306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
395462306a36Sopenharmony_ci			   "Failed to lock USB device ret=%d",ret);
395562306a36Sopenharmony_ci	}
395662306a36Sopenharmony_ci	if (init_pause_msec) {
395762306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INFO,
395862306a36Sopenharmony_ci			   "Waiting %u msec for hardware to settle",
395962306a36Sopenharmony_ci			   init_pause_msec);
396062306a36Sopenharmony_ci		msleep(init_pause_msec);
396162306a36Sopenharmony_ci	}
396262306a36Sopenharmony_ci
396362306a36Sopenharmony_ci}
396462306a36Sopenharmony_ci
396562306a36Sopenharmony_ci
396662306a36Sopenharmony_civoid pvr2_hdw_cpureset_assert(struct pvr2_hdw *hdw,int val)
396762306a36Sopenharmony_ci{
396862306a36Sopenharmony_ci	char *da;
396962306a36Sopenharmony_ci	unsigned int pipe;
397062306a36Sopenharmony_ci	int ret;
397162306a36Sopenharmony_ci
397262306a36Sopenharmony_ci	if (!hdw->usb_dev) return;
397362306a36Sopenharmony_ci
397462306a36Sopenharmony_ci	da = kmalloc(16, GFP_KERNEL);
397562306a36Sopenharmony_ci
397662306a36Sopenharmony_ci	if (da == NULL) {
397762306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
397862306a36Sopenharmony_ci			   "Unable to allocate memory to control CPU reset");
397962306a36Sopenharmony_ci		return;
398062306a36Sopenharmony_ci	}
398162306a36Sopenharmony_ci
398262306a36Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INIT,"cpureset_assert(%d)",val);
398362306a36Sopenharmony_ci
398462306a36Sopenharmony_ci	da[0] = val ? 0x01 : 0x00;
398562306a36Sopenharmony_ci
398662306a36Sopenharmony_ci	/* Write the CPUCS register on the 8051.  The lsb of the register
398762306a36Sopenharmony_ci	   is the reset bit; a 1 asserts reset while a 0 clears it. */
398862306a36Sopenharmony_ci	pipe = usb_sndctrlpipe(hdw->usb_dev, 0);
398962306a36Sopenharmony_ci	ret = usb_control_msg(hdw->usb_dev,pipe,0xa0,0x40,0xe600,0,da,1,1000);
399062306a36Sopenharmony_ci	if (ret < 0) {
399162306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
399262306a36Sopenharmony_ci			   "cpureset_assert(%d) error=%d",val,ret);
399362306a36Sopenharmony_ci		pvr2_hdw_render_useless(hdw);
399462306a36Sopenharmony_ci	}
399562306a36Sopenharmony_ci
399662306a36Sopenharmony_ci	kfree(da);
399762306a36Sopenharmony_ci}
399862306a36Sopenharmony_ci
399962306a36Sopenharmony_ci
400062306a36Sopenharmony_ciint pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *hdw)
400162306a36Sopenharmony_ci{
400262306a36Sopenharmony_ci	return pvr2_issue_simple_cmd(hdw,FX2CMD_DEEP_RESET);
400362306a36Sopenharmony_ci}
400462306a36Sopenharmony_ci
400562306a36Sopenharmony_ci
400662306a36Sopenharmony_ciint pvr2_hdw_cmd_powerup(struct pvr2_hdw *hdw)
400762306a36Sopenharmony_ci{
400862306a36Sopenharmony_ci	return pvr2_issue_simple_cmd(hdw,FX2CMD_POWER_ON);
400962306a36Sopenharmony_ci}
401062306a36Sopenharmony_ci
401162306a36Sopenharmony_ci
401262306a36Sopenharmony_ci
401362306a36Sopenharmony_ciint pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw)
401462306a36Sopenharmony_ci{
401562306a36Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INIT,
401662306a36Sopenharmony_ci		   "Requesting decoder reset");
401762306a36Sopenharmony_ci	if (hdw->decoder_client_id) {
401862306a36Sopenharmony_ci		v4l2_device_call_all(&hdw->v4l2_dev, hdw->decoder_client_id,
401962306a36Sopenharmony_ci				     core, reset, 0);
402062306a36Sopenharmony_ci		pvr2_hdw_cx25840_vbi_hack(hdw);
402162306a36Sopenharmony_ci		return 0;
402262306a36Sopenharmony_ci	}
402362306a36Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INIT,
402462306a36Sopenharmony_ci		   "Unable to reset decoder: nothing attached");
402562306a36Sopenharmony_ci	return -ENOTTY;
402662306a36Sopenharmony_ci}
402762306a36Sopenharmony_ci
402862306a36Sopenharmony_ci
402962306a36Sopenharmony_cistatic int pvr2_hdw_cmd_hcw_demod_reset(struct pvr2_hdw *hdw, int onoff)
403062306a36Sopenharmony_ci{
403162306a36Sopenharmony_ci	hdw->flag_ok = !0;
403262306a36Sopenharmony_ci
403362306a36Sopenharmony_ci	/* Use this for Hauppauge 160xxx only */
403462306a36Sopenharmony_ci	if (le16_to_cpu(hdw->usb_dev->descriptor.idVendor) == 0x2040 &&
403562306a36Sopenharmony_ci	    (le16_to_cpu(hdw->usb_dev->descriptor.idProduct) == 0x7502 ||
403662306a36Sopenharmony_ci	     le16_to_cpu(hdw->usb_dev->descriptor.idProduct) == 0x7510)) {
403762306a36Sopenharmony_ci		pr_debug("%s(): resetting demod on Hauppauge 160xxx platform skipped\n",
403862306a36Sopenharmony_ci			 __func__);
403962306a36Sopenharmony_ci		/* Can't reset 160xxx or it will trash Demod tristate */
404062306a36Sopenharmony_ci		return pvr2_issue_simple_cmd(hdw,
404162306a36Sopenharmony_ci					     FX2CMD_HCW_MAKO_SLEEP_PIN |
404262306a36Sopenharmony_ci					     (1 << 8) |
404362306a36Sopenharmony_ci					     ((onoff ? 1 : 0) << 16));
404462306a36Sopenharmony_ci	}
404562306a36Sopenharmony_ci
404662306a36Sopenharmony_ci	return pvr2_issue_simple_cmd(hdw,
404762306a36Sopenharmony_ci				     FX2CMD_HCW_DEMOD_RESETIN |
404862306a36Sopenharmony_ci				     (1 << 8) |
404962306a36Sopenharmony_ci				     ((onoff ? 1 : 0) << 16));
405062306a36Sopenharmony_ci}
405162306a36Sopenharmony_ci
405262306a36Sopenharmony_ci
405362306a36Sopenharmony_cistatic int pvr2_hdw_cmd_onair_fe_power_ctrl(struct pvr2_hdw *hdw, int onoff)
405462306a36Sopenharmony_ci{
405562306a36Sopenharmony_ci	hdw->flag_ok = !0;
405662306a36Sopenharmony_ci	return pvr2_issue_simple_cmd(hdw,(onoff ?
405762306a36Sopenharmony_ci					  FX2CMD_ONAIR_DTV_POWER_ON :
405862306a36Sopenharmony_ci					  FX2CMD_ONAIR_DTV_POWER_OFF));
405962306a36Sopenharmony_ci}
406062306a36Sopenharmony_ci
406162306a36Sopenharmony_ci
406262306a36Sopenharmony_cistatic int pvr2_hdw_cmd_onair_digital_path_ctrl(struct pvr2_hdw *hdw,
406362306a36Sopenharmony_ci						int onoff)
406462306a36Sopenharmony_ci{
406562306a36Sopenharmony_ci	return pvr2_issue_simple_cmd(hdw,(onoff ?
406662306a36Sopenharmony_ci					  FX2CMD_ONAIR_DTV_STREAMING_ON :
406762306a36Sopenharmony_ci					  FX2CMD_ONAIR_DTV_STREAMING_OFF));
406862306a36Sopenharmony_ci}
406962306a36Sopenharmony_ci
407062306a36Sopenharmony_ci
407162306a36Sopenharmony_cistatic void pvr2_hdw_cmd_modeswitch(struct pvr2_hdw *hdw,int digitalFl)
407262306a36Sopenharmony_ci{
407362306a36Sopenharmony_ci	int cmode;
407462306a36Sopenharmony_ci	/* Compare digital/analog desired setting with current setting.  If
407562306a36Sopenharmony_ci	   they don't match, fix it... */
407662306a36Sopenharmony_ci	cmode = (digitalFl ? PVR2_PATHWAY_DIGITAL : PVR2_PATHWAY_ANALOG);
407762306a36Sopenharmony_ci	if (cmode == hdw->pathway_state) {
407862306a36Sopenharmony_ci		/* They match; nothing to do */
407962306a36Sopenharmony_ci		return;
408062306a36Sopenharmony_ci	}
408162306a36Sopenharmony_ci
408262306a36Sopenharmony_ci	switch (hdw->hdw_desc->digital_control_scheme) {
408362306a36Sopenharmony_ci	case PVR2_DIGITAL_SCHEME_HAUPPAUGE:
408462306a36Sopenharmony_ci		pvr2_hdw_cmd_hcw_demod_reset(hdw,digitalFl);
408562306a36Sopenharmony_ci		if (cmode == PVR2_PATHWAY_ANALOG) {
408662306a36Sopenharmony_ci			/* If moving to analog mode, also force the decoder
408762306a36Sopenharmony_ci			   to reset.  If no decoder is attached, then it's
408862306a36Sopenharmony_ci			   ok to ignore this because if/when the decoder
408962306a36Sopenharmony_ci			   attaches, it will reset itself at that time. */
409062306a36Sopenharmony_ci			pvr2_hdw_cmd_decoder_reset(hdw);
409162306a36Sopenharmony_ci		}
409262306a36Sopenharmony_ci		break;
409362306a36Sopenharmony_ci	case PVR2_DIGITAL_SCHEME_ONAIR:
409462306a36Sopenharmony_ci		/* Supposedly we should always have the power on whether in
409562306a36Sopenharmony_ci		   digital or analog mode.  But for now do what appears to
409662306a36Sopenharmony_ci		   work... */
409762306a36Sopenharmony_ci		pvr2_hdw_cmd_onair_fe_power_ctrl(hdw,digitalFl);
409862306a36Sopenharmony_ci		break;
409962306a36Sopenharmony_ci	default: break;
410062306a36Sopenharmony_ci	}
410162306a36Sopenharmony_ci
410262306a36Sopenharmony_ci	pvr2_hdw_untrip_unlocked(hdw);
410362306a36Sopenharmony_ci	hdw->pathway_state = cmode;
410462306a36Sopenharmony_ci}
410562306a36Sopenharmony_ci
410662306a36Sopenharmony_ci
410762306a36Sopenharmony_cistatic void pvr2_led_ctrl_hauppauge(struct pvr2_hdw *hdw, int onoff)
410862306a36Sopenharmony_ci{
410962306a36Sopenharmony_ci	/* change some GPIO data
411062306a36Sopenharmony_ci	 *
411162306a36Sopenharmony_ci	 * note: bit d7 of dir appears to control the LED,
411262306a36Sopenharmony_ci	 * so we shut it off here.
411362306a36Sopenharmony_ci	 *
411462306a36Sopenharmony_ci	 */
411562306a36Sopenharmony_ci	if (onoff) {
411662306a36Sopenharmony_ci		pvr2_hdw_gpio_chg_dir(hdw, 0xffffffff, 0x00000481);
411762306a36Sopenharmony_ci	} else {
411862306a36Sopenharmony_ci		pvr2_hdw_gpio_chg_dir(hdw, 0xffffffff, 0x00000401);
411962306a36Sopenharmony_ci	}
412062306a36Sopenharmony_ci	pvr2_hdw_gpio_chg_out(hdw, 0xffffffff, 0x00000000);
412162306a36Sopenharmony_ci}
412262306a36Sopenharmony_ci
412362306a36Sopenharmony_ci
412462306a36Sopenharmony_citypedef void (*led_method_func)(struct pvr2_hdw *,int);
412562306a36Sopenharmony_ci
412662306a36Sopenharmony_cistatic led_method_func led_methods[] = {
412762306a36Sopenharmony_ci	[PVR2_LED_SCHEME_HAUPPAUGE] = pvr2_led_ctrl_hauppauge,
412862306a36Sopenharmony_ci};
412962306a36Sopenharmony_ci
413062306a36Sopenharmony_ci
413162306a36Sopenharmony_ci/* Toggle LED */
413262306a36Sopenharmony_cistatic void pvr2_led_ctrl(struct pvr2_hdw *hdw,int onoff)
413362306a36Sopenharmony_ci{
413462306a36Sopenharmony_ci	unsigned int scheme_id;
413562306a36Sopenharmony_ci	led_method_func fp;
413662306a36Sopenharmony_ci
413762306a36Sopenharmony_ci	if ((!onoff) == (!hdw->led_on)) return;
413862306a36Sopenharmony_ci
413962306a36Sopenharmony_ci	hdw->led_on = onoff != 0;
414062306a36Sopenharmony_ci
414162306a36Sopenharmony_ci	scheme_id = hdw->hdw_desc->led_scheme;
414262306a36Sopenharmony_ci	if (scheme_id < ARRAY_SIZE(led_methods)) {
414362306a36Sopenharmony_ci		fp = led_methods[scheme_id];
414462306a36Sopenharmony_ci	} else {
414562306a36Sopenharmony_ci		fp = NULL;
414662306a36Sopenharmony_ci	}
414762306a36Sopenharmony_ci
414862306a36Sopenharmony_ci	if (fp) (*fp)(hdw,onoff);
414962306a36Sopenharmony_ci}
415062306a36Sopenharmony_ci
415162306a36Sopenharmony_ci
415262306a36Sopenharmony_ci/* Stop / start video stream transport */
415362306a36Sopenharmony_cistatic int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl)
415462306a36Sopenharmony_ci{
415562306a36Sopenharmony_ci	int ret;
415662306a36Sopenharmony_ci
415762306a36Sopenharmony_ci	/* If we're in analog mode, then just issue the usual analog
415862306a36Sopenharmony_ci	   command. */
415962306a36Sopenharmony_ci	if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) {
416062306a36Sopenharmony_ci		return pvr2_issue_simple_cmd(hdw,
416162306a36Sopenharmony_ci					     (runFl ?
416262306a36Sopenharmony_ci					      FX2CMD_STREAMING_ON :
416362306a36Sopenharmony_ci					      FX2CMD_STREAMING_OFF));
416462306a36Sopenharmony_ci		/*Note: Not reached */
416562306a36Sopenharmony_ci	}
416662306a36Sopenharmony_ci
416762306a36Sopenharmony_ci	if (hdw->pathway_state != PVR2_PATHWAY_DIGITAL) {
416862306a36Sopenharmony_ci		/* Whoops, we don't know what mode we're in... */
416962306a36Sopenharmony_ci		return -EINVAL;
417062306a36Sopenharmony_ci	}
417162306a36Sopenharmony_ci
417262306a36Sopenharmony_ci	/* To get here we have to be in digital mode.  The mechanism here
417362306a36Sopenharmony_ci	   is unfortunately different for different vendors.  So we switch
417462306a36Sopenharmony_ci	   on the device's digital scheme attribute in order to figure out
417562306a36Sopenharmony_ci	   what to do. */
417662306a36Sopenharmony_ci	switch (hdw->hdw_desc->digital_control_scheme) {
417762306a36Sopenharmony_ci	case PVR2_DIGITAL_SCHEME_HAUPPAUGE:
417862306a36Sopenharmony_ci		return pvr2_issue_simple_cmd(hdw,
417962306a36Sopenharmony_ci					     (runFl ?
418062306a36Sopenharmony_ci					      FX2CMD_HCW_DTV_STREAMING_ON :
418162306a36Sopenharmony_ci					      FX2CMD_HCW_DTV_STREAMING_OFF));
418262306a36Sopenharmony_ci	case PVR2_DIGITAL_SCHEME_ONAIR:
418362306a36Sopenharmony_ci		ret = pvr2_issue_simple_cmd(hdw,
418462306a36Sopenharmony_ci					    (runFl ?
418562306a36Sopenharmony_ci					     FX2CMD_STREAMING_ON :
418662306a36Sopenharmony_ci					     FX2CMD_STREAMING_OFF));
418762306a36Sopenharmony_ci		if (ret) return ret;
418862306a36Sopenharmony_ci		return pvr2_hdw_cmd_onair_digital_path_ctrl(hdw,runFl);
418962306a36Sopenharmony_ci	default:
419062306a36Sopenharmony_ci		return -EINVAL;
419162306a36Sopenharmony_ci	}
419262306a36Sopenharmony_ci}
419362306a36Sopenharmony_ci
419462306a36Sopenharmony_ci
419562306a36Sopenharmony_ci/* Evaluate whether or not state_pathway_ok can change */
419662306a36Sopenharmony_cistatic int state_eval_pathway_ok(struct pvr2_hdw *hdw)
419762306a36Sopenharmony_ci{
419862306a36Sopenharmony_ci	if (hdw->state_pathway_ok) {
419962306a36Sopenharmony_ci		/* Nothing to do if pathway is already ok */
420062306a36Sopenharmony_ci		return 0;
420162306a36Sopenharmony_ci	}
420262306a36Sopenharmony_ci	if (!hdw->state_pipeline_idle) {
420362306a36Sopenharmony_ci		/* Not allowed to change anything if pipeline is not idle */
420462306a36Sopenharmony_ci		return 0;
420562306a36Sopenharmony_ci	}
420662306a36Sopenharmony_ci	pvr2_hdw_cmd_modeswitch(hdw,hdw->input_val == PVR2_CVAL_INPUT_DTV);
420762306a36Sopenharmony_ci	hdw->state_pathway_ok = !0;
420862306a36Sopenharmony_ci	trace_stbit("state_pathway_ok",hdw->state_pathway_ok);
420962306a36Sopenharmony_ci	return !0;
421062306a36Sopenharmony_ci}
421162306a36Sopenharmony_ci
421262306a36Sopenharmony_ci
421362306a36Sopenharmony_ci/* Evaluate whether or not state_encoder_ok can change */
421462306a36Sopenharmony_cistatic int state_eval_encoder_ok(struct pvr2_hdw *hdw)
421562306a36Sopenharmony_ci{
421662306a36Sopenharmony_ci	if (hdw->state_encoder_ok) return 0;
421762306a36Sopenharmony_ci	if (hdw->flag_tripped) return 0;
421862306a36Sopenharmony_ci	if (hdw->state_encoder_run) return 0;
421962306a36Sopenharmony_ci	if (hdw->state_encoder_config) return 0;
422062306a36Sopenharmony_ci	if (hdw->state_decoder_run) return 0;
422162306a36Sopenharmony_ci	if (hdw->state_usbstream_run) return 0;
422262306a36Sopenharmony_ci	if (hdw->pathway_state == PVR2_PATHWAY_DIGITAL) {
422362306a36Sopenharmony_ci		if (!hdw->hdw_desc->flag_digital_requires_cx23416) return 0;
422462306a36Sopenharmony_ci	} else if (hdw->pathway_state != PVR2_PATHWAY_ANALOG) {
422562306a36Sopenharmony_ci		return 0;
422662306a36Sopenharmony_ci	}
422762306a36Sopenharmony_ci
422862306a36Sopenharmony_ci	if (pvr2_upload_firmware2(hdw) < 0) {
422962306a36Sopenharmony_ci		hdw->flag_tripped = !0;
423062306a36Sopenharmony_ci		trace_stbit("flag_tripped",hdw->flag_tripped);
423162306a36Sopenharmony_ci		return !0;
423262306a36Sopenharmony_ci	}
423362306a36Sopenharmony_ci	hdw->state_encoder_ok = !0;
423462306a36Sopenharmony_ci	trace_stbit("state_encoder_ok",hdw->state_encoder_ok);
423562306a36Sopenharmony_ci	return !0;
423662306a36Sopenharmony_ci}
423762306a36Sopenharmony_ci
423862306a36Sopenharmony_ci
423962306a36Sopenharmony_ci/* Evaluate whether or not state_encoder_config can change */
424062306a36Sopenharmony_cistatic int state_eval_encoder_config(struct pvr2_hdw *hdw)
424162306a36Sopenharmony_ci{
424262306a36Sopenharmony_ci	if (hdw->state_encoder_config) {
424362306a36Sopenharmony_ci		if (hdw->state_encoder_ok) {
424462306a36Sopenharmony_ci			if (hdw->state_pipeline_req &&
424562306a36Sopenharmony_ci			    !hdw->state_pipeline_pause) return 0;
424662306a36Sopenharmony_ci		}
424762306a36Sopenharmony_ci		hdw->state_encoder_config = 0;
424862306a36Sopenharmony_ci		hdw->state_encoder_waitok = 0;
424962306a36Sopenharmony_ci		trace_stbit("state_encoder_waitok",hdw->state_encoder_waitok);
425062306a36Sopenharmony_ci		/* paranoia - solve race if timer just completed */
425162306a36Sopenharmony_ci		del_timer_sync(&hdw->encoder_wait_timer);
425262306a36Sopenharmony_ci	} else {
425362306a36Sopenharmony_ci		if (!hdw->state_pathway_ok ||
425462306a36Sopenharmony_ci		    (hdw->pathway_state != PVR2_PATHWAY_ANALOG) ||
425562306a36Sopenharmony_ci		    !hdw->state_encoder_ok ||
425662306a36Sopenharmony_ci		    !hdw->state_pipeline_idle ||
425762306a36Sopenharmony_ci		    hdw->state_pipeline_pause ||
425862306a36Sopenharmony_ci		    !hdw->state_pipeline_req ||
425962306a36Sopenharmony_ci		    !hdw->state_pipeline_config) {
426062306a36Sopenharmony_ci			/* We must reset the enforced wait interval if
426162306a36Sopenharmony_ci			   anything has happened that might have disturbed
426262306a36Sopenharmony_ci			   the encoder.  This should be a rare case. */
426362306a36Sopenharmony_ci			if (timer_pending(&hdw->encoder_wait_timer)) {
426462306a36Sopenharmony_ci				del_timer_sync(&hdw->encoder_wait_timer);
426562306a36Sopenharmony_ci			}
426662306a36Sopenharmony_ci			if (hdw->state_encoder_waitok) {
426762306a36Sopenharmony_ci				/* Must clear the state - therefore we did
426862306a36Sopenharmony_ci				   something to a state bit and must also
426962306a36Sopenharmony_ci				   return true. */
427062306a36Sopenharmony_ci				hdw->state_encoder_waitok = 0;
427162306a36Sopenharmony_ci				trace_stbit("state_encoder_waitok",
427262306a36Sopenharmony_ci					    hdw->state_encoder_waitok);
427362306a36Sopenharmony_ci				return !0;
427462306a36Sopenharmony_ci			}
427562306a36Sopenharmony_ci			return 0;
427662306a36Sopenharmony_ci		}
427762306a36Sopenharmony_ci		if (!hdw->state_encoder_waitok) {
427862306a36Sopenharmony_ci			if (!timer_pending(&hdw->encoder_wait_timer)) {
427962306a36Sopenharmony_ci				/* waitok flag wasn't set and timer isn't
428062306a36Sopenharmony_ci				   running.  Check flag once more to avoid
428162306a36Sopenharmony_ci				   a race then start the timer.  This is
428262306a36Sopenharmony_ci				   the point when we measure out a minimal
428362306a36Sopenharmony_ci				   quiet interval before doing something to
428462306a36Sopenharmony_ci				   the encoder. */
428562306a36Sopenharmony_ci				if (!hdw->state_encoder_waitok) {
428662306a36Sopenharmony_ci					hdw->encoder_wait_timer.expires =
428762306a36Sopenharmony_ci						jiffies + msecs_to_jiffies(
428862306a36Sopenharmony_ci						TIME_MSEC_ENCODER_WAIT);
428962306a36Sopenharmony_ci					add_timer(&hdw->encoder_wait_timer);
429062306a36Sopenharmony_ci				}
429162306a36Sopenharmony_ci			}
429262306a36Sopenharmony_ci			/* We can't continue until we know we have been
429362306a36Sopenharmony_ci			   quiet for the interval measured by this
429462306a36Sopenharmony_ci			   timer. */
429562306a36Sopenharmony_ci			return 0;
429662306a36Sopenharmony_ci		}
429762306a36Sopenharmony_ci		pvr2_encoder_configure(hdw);
429862306a36Sopenharmony_ci		if (hdw->state_encoder_ok) hdw->state_encoder_config = !0;
429962306a36Sopenharmony_ci	}
430062306a36Sopenharmony_ci	trace_stbit("state_encoder_config",hdw->state_encoder_config);
430162306a36Sopenharmony_ci	return !0;
430262306a36Sopenharmony_ci}
430362306a36Sopenharmony_ci
430462306a36Sopenharmony_ci
430562306a36Sopenharmony_ci/* Return true if the encoder should not be running. */
430662306a36Sopenharmony_cistatic int state_check_disable_encoder_run(struct pvr2_hdw *hdw)
430762306a36Sopenharmony_ci{
430862306a36Sopenharmony_ci	if (!hdw->state_encoder_ok) {
430962306a36Sopenharmony_ci		/* Encoder isn't healthy at the moment, so stop it. */
431062306a36Sopenharmony_ci		return !0;
431162306a36Sopenharmony_ci	}
431262306a36Sopenharmony_ci	if (!hdw->state_pathway_ok) {
431362306a36Sopenharmony_ci		/* Mode is not understood at the moment (i.e. it wants to
431462306a36Sopenharmony_ci		   change), so encoder must be stopped. */
431562306a36Sopenharmony_ci		return !0;
431662306a36Sopenharmony_ci	}
431762306a36Sopenharmony_ci
431862306a36Sopenharmony_ci	switch (hdw->pathway_state) {
431962306a36Sopenharmony_ci	case PVR2_PATHWAY_ANALOG:
432062306a36Sopenharmony_ci		if (!hdw->state_decoder_run) {
432162306a36Sopenharmony_ci			/* We're in analog mode and the decoder is not
432262306a36Sopenharmony_ci			   running; thus the encoder should be stopped as
432362306a36Sopenharmony_ci			   well. */
432462306a36Sopenharmony_ci			return !0;
432562306a36Sopenharmony_ci		}
432662306a36Sopenharmony_ci		break;
432762306a36Sopenharmony_ci	case PVR2_PATHWAY_DIGITAL:
432862306a36Sopenharmony_ci		if (hdw->state_encoder_runok) {
432962306a36Sopenharmony_ci			/* This is a funny case.  We're in digital mode so
433062306a36Sopenharmony_ci			   really the encoder should be stopped.  However
433162306a36Sopenharmony_ci			   if it really is running, only kill it after
433262306a36Sopenharmony_ci			   runok has been set.  This gives a chance for the
433362306a36Sopenharmony_ci			   onair quirk to function (encoder must run
433462306a36Sopenharmony_ci			   briefly first, at least once, before onair
433562306a36Sopenharmony_ci			   digital streaming can work). */
433662306a36Sopenharmony_ci			return !0;
433762306a36Sopenharmony_ci		}
433862306a36Sopenharmony_ci		break;
433962306a36Sopenharmony_ci	default:
434062306a36Sopenharmony_ci		/* Unknown mode; so encoder should be stopped. */
434162306a36Sopenharmony_ci		return !0;
434262306a36Sopenharmony_ci	}
434362306a36Sopenharmony_ci
434462306a36Sopenharmony_ci	/* If we get here, we haven't found a reason to stop the
434562306a36Sopenharmony_ci	   encoder. */
434662306a36Sopenharmony_ci	return 0;
434762306a36Sopenharmony_ci}
434862306a36Sopenharmony_ci
434962306a36Sopenharmony_ci
435062306a36Sopenharmony_ci/* Return true if the encoder should be running. */
435162306a36Sopenharmony_cistatic int state_check_enable_encoder_run(struct pvr2_hdw *hdw)
435262306a36Sopenharmony_ci{
435362306a36Sopenharmony_ci	if (!hdw->state_encoder_ok) {
435462306a36Sopenharmony_ci		/* Don't run the encoder if it isn't healthy... */
435562306a36Sopenharmony_ci		return 0;
435662306a36Sopenharmony_ci	}
435762306a36Sopenharmony_ci	if (!hdw->state_pathway_ok) {
435862306a36Sopenharmony_ci		/* Don't run the encoder if we don't (yet) know what mode
435962306a36Sopenharmony_ci		   we need to be in... */
436062306a36Sopenharmony_ci		return 0;
436162306a36Sopenharmony_ci	}
436262306a36Sopenharmony_ci
436362306a36Sopenharmony_ci	switch (hdw->pathway_state) {
436462306a36Sopenharmony_ci	case PVR2_PATHWAY_ANALOG:
436562306a36Sopenharmony_ci		if (hdw->state_decoder_run && hdw->state_decoder_ready) {
436662306a36Sopenharmony_ci			/* In analog mode, if the decoder is running, then
436762306a36Sopenharmony_ci			   run the encoder. */
436862306a36Sopenharmony_ci			return !0;
436962306a36Sopenharmony_ci		}
437062306a36Sopenharmony_ci		break;
437162306a36Sopenharmony_ci	case PVR2_PATHWAY_DIGITAL:
437262306a36Sopenharmony_ci		if ((hdw->hdw_desc->digital_control_scheme ==
437362306a36Sopenharmony_ci		     PVR2_DIGITAL_SCHEME_ONAIR) &&
437462306a36Sopenharmony_ci		    !hdw->state_encoder_runok) {
437562306a36Sopenharmony_ci			/* This is a quirk.  OnAir hardware won't stream
437662306a36Sopenharmony_ci			   digital until the encoder has been run at least
437762306a36Sopenharmony_ci			   once, for a minimal period of time (empiricially
437862306a36Sopenharmony_ci			   measured to be 1/4 second).  So if we're on
437962306a36Sopenharmony_ci			   OnAir hardware and the encoder has never been
438062306a36Sopenharmony_ci			   run at all, then start the encoder.  Normal
438162306a36Sopenharmony_ci			   state machine logic in the driver will
438262306a36Sopenharmony_ci			   automatically handle the remaining bits. */
438362306a36Sopenharmony_ci			return !0;
438462306a36Sopenharmony_ci		}
438562306a36Sopenharmony_ci		break;
438662306a36Sopenharmony_ci	default:
438762306a36Sopenharmony_ci		/* For completeness (unknown mode; encoder won't run ever) */
438862306a36Sopenharmony_ci		break;
438962306a36Sopenharmony_ci	}
439062306a36Sopenharmony_ci	/* If we get here, then we haven't found any reason to run the
439162306a36Sopenharmony_ci	   encoder, so don't run it. */
439262306a36Sopenharmony_ci	return 0;
439362306a36Sopenharmony_ci}
439462306a36Sopenharmony_ci
439562306a36Sopenharmony_ci
439662306a36Sopenharmony_ci/* Evaluate whether or not state_encoder_run can change */
439762306a36Sopenharmony_cistatic int state_eval_encoder_run(struct pvr2_hdw *hdw)
439862306a36Sopenharmony_ci{
439962306a36Sopenharmony_ci	if (hdw->state_encoder_run) {
440062306a36Sopenharmony_ci		if (!state_check_disable_encoder_run(hdw)) return 0;
440162306a36Sopenharmony_ci		if (hdw->state_encoder_ok) {
440262306a36Sopenharmony_ci			del_timer_sync(&hdw->encoder_run_timer);
440362306a36Sopenharmony_ci			if (pvr2_encoder_stop(hdw) < 0) return !0;
440462306a36Sopenharmony_ci		}
440562306a36Sopenharmony_ci		hdw->state_encoder_run = 0;
440662306a36Sopenharmony_ci	} else {
440762306a36Sopenharmony_ci		if (!state_check_enable_encoder_run(hdw)) return 0;
440862306a36Sopenharmony_ci		if (pvr2_encoder_start(hdw) < 0) return !0;
440962306a36Sopenharmony_ci		hdw->state_encoder_run = !0;
441062306a36Sopenharmony_ci		if (!hdw->state_encoder_runok) {
441162306a36Sopenharmony_ci			hdw->encoder_run_timer.expires = jiffies +
441262306a36Sopenharmony_ci				 msecs_to_jiffies(TIME_MSEC_ENCODER_OK);
441362306a36Sopenharmony_ci			add_timer(&hdw->encoder_run_timer);
441462306a36Sopenharmony_ci		}
441562306a36Sopenharmony_ci	}
441662306a36Sopenharmony_ci	trace_stbit("state_encoder_run",hdw->state_encoder_run);
441762306a36Sopenharmony_ci	return !0;
441862306a36Sopenharmony_ci}
441962306a36Sopenharmony_ci
442062306a36Sopenharmony_ci
442162306a36Sopenharmony_ci/* Timeout function for quiescent timer. */
442262306a36Sopenharmony_cistatic void pvr2_hdw_quiescent_timeout(struct timer_list *t)
442362306a36Sopenharmony_ci{
442462306a36Sopenharmony_ci	struct pvr2_hdw *hdw = from_timer(hdw, t, quiescent_timer);
442562306a36Sopenharmony_ci	hdw->state_decoder_quiescent = !0;
442662306a36Sopenharmony_ci	trace_stbit("state_decoder_quiescent",hdw->state_decoder_quiescent);
442762306a36Sopenharmony_ci	hdw->state_stale = !0;
442862306a36Sopenharmony_ci	schedule_work(&hdw->workpoll);
442962306a36Sopenharmony_ci}
443062306a36Sopenharmony_ci
443162306a36Sopenharmony_ci
443262306a36Sopenharmony_ci/* Timeout function for decoder stabilization timer. */
443362306a36Sopenharmony_cistatic void pvr2_hdw_decoder_stabilization_timeout(struct timer_list *t)
443462306a36Sopenharmony_ci{
443562306a36Sopenharmony_ci	struct pvr2_hdw *hdw = from_timer(hdw, t, decoder_stabilization_timer);
443662306a36Sopenharmony_ci	hdw->state_decoder_ready = !0;
443762306a36Sopenharmony_ci	trace_stbit("state_decoder_ready", hdw->state_decoder_ready);
443862306a36Sopenharmony_ci	hdw->state_stale = !0;
443962306a36Sopenharmony_ci	schedule_work(&hdw->workpoll);
444062306a36Sopenharmony_ci}
444162306a36Sopenharmony_ci
444262306a36Sopenharmony_ci
444362306a36Sopenharmony_ci/* Timeout function for encoder wait timer. */
444462306a36Sopenharmony_cistatic void pvr2_hdw_encoder_wait_timeout(struct timer_list *t)
444562306a36Sopenharmony_ci{
444662306a36Sopenharmony_ci	struct pvr2_hdw *hdw = from_timer(hdw, t, encoder_wait_timer);
444762306a36Sopenharmony_ci	hdw->state_encoder_waitok = !0;
444862306a36Sopenharmony_ci	trace_stbit("state_encoder_waitok",hdw->state_encoder_waitok);
444962306a36Sopenharmony_ci	hdw->state_stale = !0;
445062306a36Sopenharmony_ci	schedule_work(&hdw->workpoll);
445162306a36Sopenharmony_ci}
445262306a36Sopenharmony_ci
445362306a36Sopenharmony_ci
445462306a36Sopenharmony_ci/* Timeout function for encoder run timer. */
445562306a36Sopenharmony_cistatic void pvr2_hdw_encoder_run_timeout(struct timer_list *t)
445662306a36Sopenharmony_ci{
445762306a36Sopenharmony_ci	struct pvr2_hdw *hdw = from_timer(hdw, t, encoder_run_timer);
445862306a36Sopenharmony_ci	if (!hdw->state_encoder_runok) {
445962306a36Sopenharmony_ci		hdw->state_encoder_runok = !0;
446062306a36Sopenharmony_ci		trace_stbit("state_encoder_runok",hdw->state_encoder_runok);
446162306a36Sopenharmony_ci		hdw->state_stale = !0;
446262306a36Sopenharmony_ci		schedule_work(&hdw->workpoll);
446362306a36Sopenharmony_ci	}
446462306a36Sopenharmony_ci}
446562306a36Sopenharmony_ci
446662306a36Sopenharmony_ci
446762306a36Sopenharmony_ci/* Evaluate whether or not state_decoder_run can change */
446862306a36Sopenharmony_cistatic int state_eval_decoder_run(struct pvr2_hdw *hdw)
446962306a36Sopenharmony_ci{
447062306a36Sopenharmony_ci	if (hdw->state_decoder_run) {
447162306a36Sopenharmony_ci		if (hdw->state_encoder_ok) {
447262306a36Sopenharmony_ci			if (hdw->state_pipeline_req &&
447362306a36Sopenharmony_ci			    !hdw->state_pipeline_pause &&
447462306a36Sopenharmony_ci			    hdw->state_pathway_ok) return 0;
447562306a36Sopenharmony_ci		}
447662306a36Sopenharmony_ci		if (!hdw->flag_decoder_missed) {
447762306a36Sopenharmony_ci			pvr2_decoder_enable(hdw,0);
447862306a36Sopenharmony_ci		}
447962306a36Sopenharmony_ci		hdw->state_decoder_quiescent = 0;
448062306a36Sopenharmony_ci		hdw->state_decoder_run = 0;
448162306a36Sopenharmony_ci		/* paranoia - solve race if timer(s) just completed */
448262306a36Sopenharmony_ci		del_timer_sync(&hdw->quiescent_timer);
448362306a36Sopenharmony_ci		/* Kill the stabilization timer, in case we're killing the
448462306a36Sopenharmony_ci		   encoder before the previous stabilization interval has
448562306a36Sopenharmony_ci		   been properly timed. */
448662306a36Sopenharmony_ci		del_timer_sync(&hdw->decoder_stabilization_timer);
448762306a36Sopenharmony_ci		hdw->state_decoder_ready = 0;
448862306a36Sopenharmony_ci	} else {
448962306a36Sopenharmony_ci		if (!hdw->state_decoder_quiescent) {
449062306a36Sopenharmony_ci			if (!timer_pending(&hdw->quiescent_timer)) {
449162306a36Sopenharmony_ci				/* We don't do something about the
449262306a36Sopenharmony_ci				   quiescent timer until right here because
449362306a36Sopenharmony_ci				   we also want to catch cases where the
449462306a36Sopenharmony_ci				   decoder was already not running (like
449562306a36Sopenharmony_ci				   after initialization) as opposed to
449662306a36Sopenharmony_ci				   knowing that we had just stopped it.
449762306a36Sopenharmony_ci				   The second flag check is here to cover a
449862306a36Sopenharmony_ci				   race - the timer could have run and set
449962306a36Sopenharmony_ci				   this flag just after the previous check
450062306a36Sopenharmony_ci				   but before we did the pending check. */
450162306a36Sopenharmony_ci				if (!hdw->state_decoder_quiescent) {
450262306a36Sopenharmony_ci					hdw->quiescent_timer.expires =
450362306a36Sopenharmony_ci						jiffies + msecs_to_jiffies(
450462306a36Sopenharmony_ci						TIME_MSEC_DECODER_WAIT);
450562306a36Sopenharmony_ci					add_timer(&hdw->quiescent_timer);
450662306a36Sopenharmony_ci				}
450762306a36Sopenharmony_ci			}
450862306a36Sopenharmony_ci			/* Don't allow decoder to start again until it has
450962306a36Sopenharmony_ci			   been quiesced first.  This little detail should
451062306a36Sopenharmony_ci			   hopefully further stabilize the encoder. */
451162306a36Sopenharmony_ci			return 0;
451262306a36Sopenharmony_ci		}
451362306a36Sopenharmony_ci		if (!hdw->state_pathway_ok ||
451462306a36Sopenharmony_ci		    (hdw->pathway_state != PVR2_PATHWAY_ANALOG) ||
451562306a36Sopenharmony_ci		    !hdw->state_pipeline_req ||
451662306a36Sopenharmony_ci		    hdw->state_pipeline_pause ||
451762306a36Sopenharmony_ci		    !hdw->state_pipeline_config ||
451862306a36Sopenharmony_ci		    !hdw->state_encoder_config ||
451962306a36Sopenharmony_ci		    !hdw->state_encoder_ok) return 0;
452062306a36Sopenharmony_ci		del_timer_sync(&hdw->quiescent_timer);
452162306a36Sopenharmony_ci		if (hdw->flag_decoder_missed) return 0;
452262306a36Sopenharmony_ci		if (pvr2_decoder_enable(hdw,!0) < 0) return 0;
452362306a36Sopenharmony_ci		hdw->state_decoder_quiescent = 0;
452462306a36Sopenharmony_ci		hdw->state_decoder_ready = 0;
452562306a36Sopenharmony_ci		hdw->state_decoder_run = !0;
452662306a36Sopenharmony_ci		if (hdw->decoder_client_id == PVR2_CLIENT_ID_SAA7115) {
452762306a36Sopenharmony_ci			hdw->decoder_stabilization_timer.expires =
452862306a36Sopenharmony_ci				jiffies + msecs_to_jiffies(
452962306a36Sopenharmony_ci				TIME_MSEC_DECODER_STABILIZATION_WAIT);
453062306a36Sopenharmony_ci			add_timer(&hdw->decoder_stabilization_timer);
453162306a36Sopenharmony_ci		} else {
453262306a36Sopenharmony_ci			hdw->state_decoder_ready = !0;
453362306a36Sopenharmony_ci		}
453462306a36Sopenharmony_ci	}
453562306a36Sopenharmony_ci	trace_stbit("state_decoder_quiescent",hdw->state_decoder_quiescent);
453662306a36Sopenharmony_ci	trace_stbit("state_decoder_run",hdw->state_decoder_run);
453762306a36Sopenharmony_ci	trace_stbit("state_decoder_ready", hdw->state_decoder_ready);
453862306a36Sopenharmony_ci	return !0;
453962306a36Sopenharmony_ci}
454062306a36Sopenharmony_ci
454162306a36Sopenharmony_ci
454262306a36Sopenharmony_ci/* Evaluate whether or not state_usbstream_run can change */
454362306a36Sopenharmony_cistatic int state_eval_usbstream_run(struct pvr2_hdw *hdw)
454462306a36Sopenharmony_ci{
454562306a36Sopenharmony_ci	if (hdw->state_usbstream_run) {
454662306a36Sopenharmony_ci		int fl = !0;
454762306a36Sopenharmony_ci		if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) {
454862306a36Sopenharmony_ci			fl = (hdw->state_encoder_ok &&
454962306a36Sopenharmony_ci			      hdw->state_encoder_run);
455062306a36Sopenharmony_ci		} else if ((hdw->pathway_state == PVR2_PATHWAY_DIGITAL) &&
455162306a36Sopenharmony_ci			   (hdw->hdw_desc->flag_digital_requires_cx23416)) {
455262306a36Sopenharmony_ci			fl = hdw->state_encoder_ok;
455362306a36Sopenharmony_ci		}
455462306a36Sopenharmony_ci		if (fl &&
455562306a36Sopenharmony_ci		    hdw->state_pipeline_req &&
455662306a36Sopenharmony_ci		    !hdw->state_pipeline_pause &&
455762306a36Sopenharmony_ci		    hdw->state_pathway_ok) {
455862306a36Sopenharmony_ci			return 0;
455962306a36Sopenharmony_ci		}
456062306a36Sopenharmony_ci		pvr2_hdw_cmd_usbstream(hdw,0);
456162306a36Sopenharmony_ci		hdw->state_usbstream_run = 0;
456262306a36Sopenharmony_ci	} else {
456362306a36Sopenharmony_ci		if (!hdw->state_pipeline_req ||
456462306a36Sopenharmony_ci		    hdw->state_pipeline_pause ||
456562306a36Sopenharmony_ci		    !hdw->state_pathway_ok) return 0;
456662306a36Sopenharmony_ci		if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) {
456762306a36Sopenharmony_ci			if (!hdw->state_encoder_ok ||
456862306a36Sopenharmony_ci			    !hdw->state_encoder_run) return 0;
456962306a36Sopenharmony_ci		} else if ((hdw->pathway_state == PVR2_PATHWAY_DIGITAL) &&
457062306a36Sopenharmony_ci			   (hdw->hdw_desc->flag_digital_requires_cx23416)) {
457162306a36Sopenharmony_ci			if (!hdw->state_encoder_ok) return 0;
457262306a36Sopenharmony_ci			if (hdw->state_encoder_run) return 0;
457362306a36Sopenharmony_ci			if (hdw->hdw_desc->digital_control_scheme ==
457462306a36Sopenharmony_ci			    PVR2_DIGITAL_SCHEME_ONAIR) {
457562306a36Sopenharmony_ci				/* OnAir digital receivers won't stream
457662306a36Sopenharmony_ci				   unless the analog encoder has run first.
457762306a36Sopenharmony_ci				   Why?  I have no idea.  But don't even
457862306a36Sopenharmony_ci				   try until we know the analog side is
457962306a36Sopenharmony_ci				   known to have run. */
458062306a36Sopenharmony_ci				if (!hdw->state_encoder_runok) return 0;
458162306a36Sopenharmony_ci			}
458262306a36Sopenharmony_ci		}
458362306a36Sopenharmony_ci		if (pvr2_hdw_cmd_usbstream(hdw,!0) < 0) return 0;
458462306a36Sopenharmony_ci		hdw->state_usbstream_run = !0;
458562306a36Sopenharmony_ci	}
458662306a36Sopenharmony_ci	trace_stbit("state_usbstream_run",hdw->state_usbstream_run);
458762306a36Sopenharmony_ci	return !0;
458862306a36Sopenharmony_ci}
458962306a36Sopenharmony_ci
459062306a36Sopenharmony_ci
459162306a36Sopenharmony_ci/* Attempt to configure pipeline, if needed */
459262306a36Sopenharmony_cistatic int state_eval_pipeline_config(struct pvr2_hdw *hdw)
459362306a36Sopenharmony_ci{
459462306a36Sopenharmony_ci	if (hdw->state_pipeline_config ||
459562306a36Sopenharmony_ci	    hdw->state_pipeline_pause) return 0;
459662306a36Sopenharmony_ci	pvr2_hdw_commit_execute(hdw);
459762306a36Sopenharmony_ci	return !0;
459862306a36Sopenharmony_ci}
459962306a36Sopenharmony_ci
460062306a36Sopenharmony_ci
460162306a36Sopenharmony_ci/* Update pipeline idle and pipeline pause tracking states based on other
460262306a36Sopenharmony_ci   inputs.  This must be called whenever the other relevant inputs have
460362306a36Sopenharmony_ci   changed. */
460462306a36Sopenharmony_cistatic int state_update_pipeline_state(struct pvr2_hdw *hdw)
460562306a36Sopenharmony_ci{
460662306a36Sopenharmony_ci	unsigned int st;
460762306a36Sopenharmony_ci	int updatedFl = 0;
460862306a36Sopenharmony_ci	/* Update pipeline state */
460962306a36Sopenharmony_ci	st = !(hdw->state_encoder_run ||
461062306a36Sopenharmony_ci	       hdw->state_decoder_run ||
461162306a36Sopenharmony_ci	       hdw->state_usbstream_run ||
461262306a36Sopenharmony_ci	       (!hdw->state_decoder_quiescent));
461362306a36Sopenharmony_ci	if (!st != !hdw->state_pipeline_idle) {
461462306a36Sopenharmony_ci		hdw->state_pipeline_idle = st;
461562306a36Sopenharmony_ci		updatedFl = !0;
461662306a36Sopenharmony_ci	}
461762306a36Sopenharmony_ci	if (hdw->state_pipeline_idle && hdw->state_pipeline_pause) {
461862306a36Sopenharmony_ci		hdw->state_pipeline_pause = 0;
461962306a36Sopenharmony_ci		updatedFl = !0;
462062306a36Sopenharmony_ci	}
462162306a36Sopenharmony_ci	return updatedFl;
462262306a36Sopenharmony_ci}
462362306a36Sopenharmony_ci
462462306a36Sopenharmony_ci
462562306a36Sopenharmony_citypedef int (*state_eval_func)(struct pvr2_hdw *);
462662306a36Sopenharmony_ci
462762306a36Sopenharmony_ci/* Set of functions to be run to evaluate various states in the driver. */
462862306a36Sopenharmony_cistatic const state_eval_func eval_funcs[] = {
462962306a36Sopenharmony_ci	state_eval_pathway_ok,
463062306a36Sopenharmony_ci	state_eval_pipeline_config,
463162306a36Sopenharmony_ci	state_eval_encoder_ok,
463262306a36Sopenharmony_ci	state_eval_encoder_config,
463362306a36Sopenharmony_ci	state_eval_decoder_run,
463462306a36Sopenharmony_ci	state_eval_encoder_run,
463562306a36Sopenharmony_ci	state_eval_usbstream_run,
463662306a36Sopenharmony_ci};
463762306a36Sopenharmony_ci
463862306a36Sopenharmony_ci
463962306a36Sopenharmony_ci/* Process various states and return true if we did anything interesting. */
464062306a36Sopenharmony_cistatic int pvr2_hdw_state_update(struct pvr2_hdw *hdw)
464162306a36Sopenharmony_ci{
464262306a36Sopenharmony_ci	unsigned int i;
464362306a36Sopenharmony_ci	int state_updated = 0;
464462306a36Sopenharmony_ci	int check_flag;
464562306a36Sopenharmony_ci
464662306a36Sopenharmony_ci	if (!hdw->state_stale) return 0;
464762306a36Sopenharmony_ci	if ((hdw->fw1_state != FW1_STATE_OK) ||
464862306a36Sopenharmony_ci	    !hdw->flag_ok) {
464962306a36Sopenharmony_ci		hdw->state_stale = 0;
465062306a36Sopenharmony_ci		return !0;
465162306a36Sopenharmony_ci	}
465262306a36Sopenharmony_ci	/* This loop is the heart of the entire driver.  It keeps trying to
465362306a36Sopenharmony_ci	   evaluate various bits of driver state until nothing changes for
465462306a36Sopenharmony_ci	   one full iteration.  Each "bit of state" tracks some global
465562306a36Sopenharmony_ci	   aspect of the driver, e.g. whether decoder should run, if
465662306a36Sopenharmony_ci	   pipeline is configured, usb streaming is on, etc.  We separately
465762306a36Sopenharmony_ci	   evaluate each of those questions based on other driver state to
465862306a36Sopenharmony_ci	   arrive at the correct running configuration. */
465962306a36Sopenharmony_ci	do {
466062306a36Sopenharmony_ci		check_flag = 0;
466162306a36Sopenharmony_ci		state_update_pipeline_state(hdw);
466262306a36Sopenharmony_ci		/* Iterate over each bit of state */
466362306a36Sopenharmony_ci		for (i = 0; (i<ARRAY_SIZE(eval_funcs)) && hdw->flag_ok; i++) {
466462306a36Sopenharmony_ci			if ((*eval_funcs[i])(hdw)) {
466562306a36Sopenharmony_ci				check_flag = !0;
466662306a36Sopenharmony_ci				state_updated = !0;
466762306a36Sopenharmony_ci				state_update_pipeline_state(hdw);
466862306a36Sopenharmony_ci			}
466962306a36Sopenharmony_ci		}
467062306a36Sopenharmony_ci	} while (check_flag && hdw->flag_ok);
467162306a36Sopenharmony_ci	hdw->state_stale = 0;
467262306a36Sopenharmony_ci	trace_stbit("state_stale",hdw->state_stale);
467362306a36Sopenharmony_ci	return state_updated;
467462306a36Sopenharmony_ci}
467562306a36Sopenharmony_ci
467662306a36Sopenharmony_ci
467762306a36Sopenharmony_cistatic unsigned int print_input_mask(unsigned int msk,
467862306a36Sopenharmony_ci				     char *buf,unsigned int acnt)
467962306a36Sopenharmony_ci{
468062306a36Sopenharmony_ci	unsigned int idx,ccnt;
468162306a36Sopenharmony_ci	unsigned int tcnt = 0;
468262306a36Sopenharmony_ci	for (idx = 0; idx < ARRAY_SIZE(control_values_input); idx++) {
468362306a36Sopenharmony_ci		if (!((1UL << idx) & msk)) continue;
468462306a36Sopenharmony_ci		ccnt = scnprintf(buf+tcnt,
468562306a36Sopenharmony_ci				 acnt-tcnt,
468662306a36Sopenharmony_ci				 "%s%s",
468762306a36Sopenharmony_ci				 (tcnt ? ", " : ""),
468862306a36Sopenharmony_ci				 control_values_input[idx]);
468962306a36Sopenharmony_ci		tcnt += ccnt;
469062306a36Sopenharmony_ci	}
469162306a36Sopenharmony_ci	return tcnt;
469262306a36Sopenharmony_ci}
469362306a36Sopenharmony_ci
469462306a36Sopenharmony_ci
469562306a36Sopenharmony_cistatic const char *pvr2_pathway_state_name(int id)
469662306a36Sopenharmony_ci{
469762306a36Sopenharmony_ci	switch (id) {
469862306a36Sopenharmony_ci	case PVR2_PATHWAY_ANALOG: return "analog";
469962306a36Sopenharmony_ci	case PVR2_PATHWAY_DIGITAL: return "digital";
470062306a36Sopenharmony_ci	default: return "unknown";
470162306a36Sopenharmony_ci	}
470262306a36Sopenharmony_ci}
470362306a36Sopenharmony_ci
470462306a36Sopenharmony_ci
470562306a36Sopenharmony_cistatic unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which,
470662306a36Sopenharmony_ci					     char *buf,unsigned int acnt)
470762306a36Sopenharmony_ci{
470862306a36Sopenharmony_ci	switch (which) {
470962306a36Sopenharmony_ci	case 0:
471062306a36Sopenharmony_ci		return scnprintf(
471162306a36Sopenharmony_ci			buf,acnt,
471262306a36Sopenharmony_ci			"driver:%s%s%s%s%s <mode=%s>",
471362306a36Sopenharmony_ci			(hdw->flag_ok ? " <ok>" : " <fail>"),
471462306a36Sopenharmony_ci			(hdw->flag_init_ok ? " <init>" : " <uninitialized>"),
471562306a36Sopenharmony_ci			(hdw->flag_disconnected ? " <disconnected>" :
471662306a36Sopenharmony_ci			 " <connected>"),
471762306a36Sopenharmony_ci			(hdw->flag_tripped ? " <tripped>" : ""),
471862306a36Sopenharmony_ci			(hdw->flag_decoder_missed ? " <no decoder>" : ""),
471962306a36Sopenharmony_ci			pvr2_pathway_state_name(hdw->pathway_state));
472062306a36Sopenharmony_ci
472162306a36Sopenharmony_ci	case 1:
472262306a36Sopenharmony_ci		return scnprintf(
472362306a36Sopenharmony_ci			buf,acnt,
472462306a36Sopenharmony_ci			"pipeline:%s%s%s%s",
472562306a36Sopenharmony_ci			(hdw->state_pipeline_idle ? " <idle>" : ""),
472662306a36Sopenharmony_ci			(hdw->state_pipeline_config ?
472762306a36Sopenharmony_ci			 " <configok>" : " <stale>"),
472862306a36Sopenharmony_ci			(hdw->state_pipeline_req ? " <req>" : ""),
472962306a36Sopenharmony_ci			(hdw->state_pipeline_pause ? " <pause>" : ""));
473062306a36Sopenharmony_ci	case 2:
473162306a36Sopenharmony_ci		return scnprintf(
473262306a36Sopenharmony_ci			buf,acnt,
473362306a36Sopenharmony_ci			"worker:%s%s%s%s%s%s%s",
473462306a36Sopenharmony_ci			(hdw->state_decoder_run ?
473562306a36Sopenharmony_ci			 (hdw->state_decoder_ready ?
473662306a36Sopenharmony_ci			  "<decode:run>" : " <decode:start>") :
473762306a36Sopenharmony_ci			 (hdw->state_decoder_quiescent ?
473862306a36Sopenharmony_ci			  "" : " <decode:stop>")),
473962306a36Sopenharmony_ci			(hdw->state_decoder_quiescent ?
474062306a36Sopenharmony_ci			 " <decode:quiescent>" : ""),
474162306a36Sopenharmony_ci			(hdw->state_encoder_ok ?
474262306a36Sopenharmony_ci			 "" : " <encode:init>"),
474362306a36Sopenharmony_ci			(hdw->state_encoder_run ?
474462306a36Sopenharmony_ci			 (hdw->state_encoder_runok ?
474562306a36Sopenharmony_ci			  " <encode:run>" :
474662306a36Sopenharmony_ci			  " <encode:firstrun>") :
474762306a36Sopenharmony_ci			 (hdw->state_encoder_runok ?
474862306a36Sopenharmony_ci			  " <encode:stop>" :
474962306a36Sopenharmony_ci			  " <encode:virgin>")),
475062306a36Sopenharmony_ci			(hdw->state_encoder_config ?
475162306a36Sopenharmony_ci			 " <encode:configok>" :
475262306a36Sopenharmony_ci			 (hdw->state_encoder_waitok ?
475362306a36Sopenharmony_ci			  "" : " <encode:waitok>")),
475462306a36Sopenharmony_ci			(hdw->state_usbstream_run ?
475562306a36Sopenharmony_ci			 " <usb:run>" : " <usb:stop>"),
475662306a36Sopenharmony_ci			(hdw->state_pathway_ok ?
475762306a36Sopenharmony_ci			 " <pathway:ok>" : ""));
475862306a36Sopenharmony_ci	case 3:
475962306a36Sopenharmony_ci		return scnprintf(
476062306a36Sopenharmony_ci			buf,acnt,
476162306a36Sopenharmony_ci			"state: %s",
476262306a36Sopenharmony_ci			pvr2_get_state_name(hdw->master_state));
476362306a36Sopenharmony_ci	case 4: {
476462306a36Sopenharmony_ci		unsigned int tcnt = 0;
476562306a36Sopenharmony_ci		unsigned int ccnt;
476662306a36Sopenharmony_ci
476762306a36Sopenharmony_ci		ccnt = scnprintf(buf,
476862306a36Sopenharmony_ci				 acnt,
476962306a36Sopenharmony_ci				 "Hardware supported inputs: ");
477062306a36Sopenharmony_ci		tcnt += ccnt;
477162306a36Sopenharmony_ci		tcnt += print_input_mask(hdw->input_avail_mask,
477262306a36Sopenharmony_ci					 buf+tcnt,
477362306a36Sopenharmony_ci					 acnt-tcnt);
477462306a36Sopenharmony_ci		if (hdw->input_avail_mask != hdw->input_allowed_mask) {
477562306a36Sopenharmony_ci			ccnt = scnprintf(buf+tcnt,
477662306a36Sopenharmony_ci					 acnt-tcnt,
477762306a36Sopenharmony_ci					 "; allowed inputs: ");
477862306a36Sopenharmony_ci			tcnt += ccnt;
477962306a36Sopenharmony_ci			tcnt += print_input_mask(hdw->input_allowed_mask,
478062306a36Sopenharmony_ci						 buf+tcnt,
478162306a36Sopenharmony_ci						 acnt-tcnt);
478262306a36Sopenharmony_ci		}
478362306a36Sopenharmony_ci		return tcnt;
478462306a36Sopenharmony_ci	}
478562306a36Sopenharmony_ci	case 5: {
478662306a36Sopenharmony_ci		struct pvr2_stream_stats stats;
478762306a36Sopenharmony_ci		if (!hdw->vid_stream) break;
478862306a36Sopenharmony_ci		pvr2_stream_get_stats(hdw->vid_stream,
478962306a36Sopenharmony_ci				      &stats,
479062306a36Sopenharmony_ci				      0);
479162306a36Sopenharmony_ci		return scnprintf(
479262306a36Sopenharmony_ci			buf,acnt,
479362306a36Sopenharmony_ci			"Bytes streamed=%u URBs: queued=%u idle=%u ready=%u processed=%u failed=%u",
479462306a36Sopenharmony_ci			stats.bytes_processed,
479562306a36Sopenharmony_ci			stats.buffers_in_queue,
479662306a36Sopenharmony_ci			stats.buffers_in_idle,
479762306a36Sopenharmony_ci			stats.buffers_in_ready,
479862306a36Sopenharmony_ci			stats.buffers_processed,
479962306a36Sopenharmony_ci			stats.buffers_failed);
480062306a36Sopenharmony_ci	}
480162306a36Sopenharmony_ci	case 6: {
480262306a36Sopenharmony_ci		unsigned int id = hdw->ir_scheme_active;
480362306a36Sopenharmony_ci		return scnprintf(buf, acnt, "ir scheme: id=%d %s", id,
480462306a36Sopenharmony_ci				 (id >= ARRAY_SIZE(ir_scheme_names) ?
480562306a36Sopenharmony_ci				  "?" : ir_scheme_names[id]));
480662306a36Sopenharmony_ci	}
480762306a36Sopenharmony_ci	default: break;
480862306a36Sopenharmony_ci	}
480962306a36Sopenharmony_ci	return 0;
481062306a36Sopenharmony_ci}
481162306a36Sopenharmony_ci
481262306a36Sopenharmony_ci
481362306a36Sopenharmony_ci/* Generate report containing info about attached sub-devices and attached
481462306a36Sopenharmony_ci   i2c clients, including an indication of which attached i2c clients are
481562306a36Sopenharmony_ci   actually sub-devices. */
481662306a36Sopenharmony_cistatic unsigned int pvr2_hdw_report_clients(struct pvr2_hdw *hdw,
481762306a36Sopenharmony_ci					    char *buf, unsigned int acnt)
481862306a36Sopenharmony_ci{
481962306a36Sopenharmony_ci	struct v4l2_subdev *sd;
482062306a36Sopenharmony_ci	unsigned int tcnt = 0;
482162306a36Sopenharmony_ci	unsigned int ccnt;
482262306a36Sopenharmony_ci	struct i2c_client *client;
482362306a36Sopenharmony_ci	const char *p;
482462306a36Sopenharmony_ci	unsigned int id;
482562306a36Sopenharmony_ci
482662306a36Sopenharmony_ci	ccnt = scnprintf(buf, acnt, "Associated v4l2-subdev drivers and I2C clients:\n");
482762306a36Sopenharmony_ci	tcnt += ccnt;
482862306a36Sopenharmony_ci	v4l2_device_for_each_subdev(sd, &hdw->v4l2_dev) {
482962306a36Sopenharmony_ci		id = sd->grp_id;
483062306a36Sopenharmony_ci		p = NULL;
483162306a36Sopenharmony_ci		if (id < ARRAY_SIZE(module_names)) p = module_names[id];
483262306a36Sopenharmony_ci		if (p) {
483362306a36Sopenharmony_ci			ccnt = scnprintf(buf + tcnt, acnt - tcnt, "  %s:", p);
483462306a36Sopenharmony_ci			tcnt += ccnt;
483562306a36Sopenharmony_ci		} else {
483662306a36Sopenharmony_ci			ccnt = scnprintf(buf + tcnt, acnt - tcnt,
483762306a36Sopenharmony_ci					 "  (unknown id=%u):", id);
483862306a36Sopenharmony_ci			tcnt += ccnt;
483962306a36Sopenharmony_ci		}
484062306a36Sopenharmony_ci		client = v4l2_get_subdevdata(sd);
484162306a36Sopenharmony_ci		if (client) {
484262306a36Sopenharmony_ci			ccnt = scnprintf(buf + tcnt, acnt - tcnt,
484362306a36Sopenharmony_ci					 " %s @ %02x\n", client->name,
484462306a36Sopenharmony_ci					 client->addr);
484562306a36Sopenharmony_ci			tcnt += ccnt;
484662306a36Sopenharmony_ci		} else {
484762306a36Sopenharmony_ci			ccnt = scnprintf(buf + tcnt, acnt - tcnt,
484862306a36Sopenharmony_ci					 " no i2c client\n");
484962306a36Sopenharmony_ci			tcnt += ccnt;
485062306a36Sopenharmony_ci		}
485162306a36Sopenharmony_ci	}
485262306a36Sopenharmony_ci	return tcnt;
485362306a36Sopenharmony_ci}
485462306a36Sopenharmony_ci
485562306a36Sopenharmony_ci
485662306a36Sopenharmony_ciunsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw,
485762306a36Sopenharmony_ci				   char *buf,unsigned int acnt)
485862306a36Sopenharmony_ci{
485962306a36Sopenharmony_ci	unsigned int bcnt,ccnt,idx;
486062306a36Sopenharmony_ci	bcnt = 0;
486162306a36Sopenharmony_ci	LOCK_TAKE(hdw->big_lock);
486262306a36Sopenharmony_ci	for (idx = 0; ; idx++) {
486362306a36Sopenharmony_ci		ccnt = pvr2_hdw_report_unlocked(hdw,idx,buf,acnt);
486462306a36Sopenharmony_ci		if (!ccnt) break;
486562306a36Sopenharmony_ci		bcnt += ccnt; acnt -= ccnt; buf += ccnt;
486662306a36Sopenharmony_ci		if (!acnt) break;
486762306a36Sopenharmony_ci		buf[0] = '\n'; ccnt = 1;
486862306a36Sopenharmony_ci		bcnt += ccnt; acnt -= ccnt; buf += ccnt;
486962306a36Sopenharmony_ci	}
487062306a36Sopenharmony_ci	ccnt = pvr2_hdw_report_clients(hdw, buf, acnt);
487162306a36Sopenharmony_ci	bcnt += ccnt; acnt -= ccnt; buf += ccnt;
487262306a36Sopenharmony_ci	LOCK_GIVE(hdw->big_lock);
487362306a36Sopenharmony_ci	return bcnt;
487462306a36Sopenharmony_ci}
487562306a36Sopenharmony_ci
487662306a36Sopenharmony_ci
487762306a36Sopenharmony_cistatic void pvr2_hdw_state_log_state(struct pvr2_hdw *hdw)
487862306a36Sopenharmony_ci{
487962306a36Sopenharmony_ci	char buf[256];
488062306a36Sopenharmony_ci	unsigned int idx, ccnt;
488162306a36Sopenharmony_ci	unsigned int lcnt, ucnt;
488262306a36Sopenharmony_ci
488362306a36Sopenharmony_ci	for (idx = 0; ; idx++) {
488462306a36Sopenharmony_ci		ccnt = pvr2_hdw_report_unlocked(hdw,idx,buf,sizeof(buf));
488562306a36Sopenharmony_ci		if (!ccnt) break;
488662306a36Sopenharmony_ci		pr_info("%s %.*s\n", hdw->name, ccnt, buf);
488762306a36Sopenharmony_ci	}
488862306a36Sopenharmony_ci	ccnt = pvr2_hdw_report_clients(hdw, buf, sizeof(buf));
488962306a36Sopenharmony_ci	if (ccnt >= sizeof(buf))
489062306a36Sopenharmony_ci		ccnt = sizeof(buf);
489162306a36Sopenharmony_ci
489262306a36Sopenharmony_ci	ucnt = 0;
489362306a36Sopenharmony_ci	while (ucnt < ccnt) {
489462306a36Sopenharmony_ci		lcnt = 0;
489562306a36Sopenharmony_ci		while ((lcnt + ucnt < ccnt) && (buf[lcnt + ucnt] != '\n')) {
489662306a36Sopenharmony_ci			lcnt++;
489762306a36Sopenharmony_ci		}
489862306a36Sopenharmony_ci		pr_info("%s %.*s\n", hdw->name, lcnt, buf + ucnt);
489962306a36Sopenharmony_ci		ucnt += lcnt + 1;
490062306a36Sopenharmony_ci	}
490162306a36Sopenharmony_ci}
490262306a36Sopenharmony_ci
490362306a36Sopenharmony_ci
490462306a36Sopenharmony_ci/* Evaluate and update the driver's current state, taking various actions
490562306a36Sopenharmony_ci   as appropriate for the update. */
490662306a36Sopenharmony_cistatic int pvr2_hdw_state_eval(struct pvr2_hdw *hdw)
490762306a36Sopenharmony_ci{
490862306a36Sopenharmony_ci	unsigned int st;
490962306a36Sopenharmony_ci	int state_updated = 0;
491062306a36Sopenharmony_ci	int callback_flag = 0;
491162306a36Sopenharmony_ci	int analog_mode;
491262306a36Sopenharmony_ci
491362306a36Sopenharmony_ci	pvr2_trace(PVR2_TRACE_STBITS,
491462306a36Sopenharmony_ci		   "Drive state check START");
491562306a36Sopenharmony_ci	if (pvrusb2_debug & PVR2_TRACE_STBITS) {
491662306a36Sopenharmony_ci		pvr2_hdw_state_log_state(hdw);
491762306a36Sopenharmony_ci	}
491862306a36Sopenharmony_ci
491962306a36Sopenharmony_ci	/* Process all state and get back over disposition */
492062306a36Sopenharmony_ci	state_updated = pvr2_hdw_state_update(hdw);
492162306a36Sopenharmony_ci
492262306a36Sopenharmony_ci	analog_mode = (hdw->pathway_state != PVR2_PATHWAY_DIGITAL);
492362306a36Sopenharmony_ci
492462306a36Sopenharmony_ci	/* Update master state based upon all other states. */
492562306a36Sopenharmony_ci	if (!hdw->flag_ok) {
492662306a36Sopenharmony_ci		st = PVR2_STATE_DEAD;
492762306a36Sopenharmony_ci	} else if (hdw->fw1_state != FW1_STATE_OK) {
492862306a36Sopenharmony_ci		st = PVR2_STATE_COLD;
492962306a36Sopenharmony_ci	} else if ((analog_mode ||
493062306a36Sopenharmony_ci		    hdw->hdw_desc->flag_digital_requires_cx23416) &&
493162306a36Sopenharmony_ci		   !hdw->state_encoder_ok) {
493262306a36Sopenharmony_ci		st = PVR2_STATE_WARM;
493362306a36Sopenharmony_ci	} else if (hdw->flag_tripped ||
493462306a36Sopenharmony_ci		   (analog_mode && hdw->flag_decoder_missed)) {
493562306a36Sopenharmony_ci		st = PVR2_STATE_ERROR;
493662306a36Sopenharmony_ci	} else if (hdw->state_usbstream_run &&
493762306a36Sopenharmony_ci		   (!analog_mode ||
493862306a36Sopenharmony_ci		    (hdw->state_encoder_run && hdw->state_decoder_run))) {
493962306a36Sopenharmony_ci		st = PVR2_STATE_RUN;
494062306a36Sopenharmony_ci	} else {
494162306a36Sopenharmony_ci		st = PVR2_STATE_READY;
494262306a36Sopenharmony_ci	}
494362306a36Sopenharmony_ci	if (hdw->master_state != st) {
494462306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_STATE,
494562306a36Sopenharmony_ci			   "Device state change from %s to %s",
494662306a36Sopenharmony_ci			   pvr2_get_state_name(hdw->master_state),
494762306a36Sopenharmony_ci			   pvr2_get_state_name(st));
494862306a36Sopenharmony_ci		pvr2_led_ctrl(hdw,st == PVR2_STATE_RUN);
494962306a36Sopenharmony_ci		hdw->master_state = st;
495062306a36Sopenharmony_ci		state_updated = !0;
495162306a36Sopenharmony_ci		callback_flag = !0;
495262306a36Sopenharmony_ci	}
495362306a36Sopenharmony_ci	if (state_updated) {
495462306a36Sopenharmony_ci		/* Trigger anyone waiting on any state changes here. */
495562306a36Sopenharmony_ci		wake_up(&hdw->state_wait_data);
495662306a36Sopenharmony_ci	}
495762306a36Sopenharmony_ci
495862306a36Sopenharmony_ci	if (pvrusb2_debug & PVR2_TRACE_STBITS) {
495962306a36Sopenharmony_ci		pvr2_hdw_state_log_state(hdw);
496062306a36Sopenharmony_ci	}
496162306a36Sopenharmony_ci	pvr2_trace(PVR2_TRACE_STBITS,
496262306a36Sopenharmony_ci		   "Drive state check DONE callback=%d",callback_flag);
496362306a36Sopenharmony_ci
496462306a36Sopenharmony_ci	return callback_flag;
496562306a36Sopenharmony_ci}
496662306a36Sopenharmony_ci
496762306a36Sopenharmony_ci
496862306a36Sopenharmony_ci/* Cause kernel thread to check / update driver state */
496962306a36Sopenharmony_cistatic void pvr2_hdw_state_sched(struct pvr2_hdw *hdw)
497062306a36Sopenharmony_ci{
497162306a36Sopenharmony_ci	if (hdw->state_stale) return;
497262306a36Sopenharmony_ci	hdw->state_stale = !0;
497362306a36Sopenharmony_ci	trace_stbit("state_stale",hdw->state_stale);
497462306a36Sopenharmony_ci	schedule_work(&hdw->workpoll);
497562306a36Sopenharmony_ci}
497662306a36Sopenharmony_ci
497762306a36Sopenharmony_ci
497862306a36Sopenharmony_ciint pvr2_hdw_gpio_get_dir(struct pvr2_hdw *hdw,u32 *dp)
497962306a36Sopenharmony_ci{
498062306a36Sopenharmony_ci	return pvr2_read_register(hdw,PVR2_GPIO_DIR,dp);
498162306a36Sopenharmony_ci}
498262306a36Sopenharmony_ci
498362306a36Sopenharmony_ci
498462306a36Sopenharmony_ciint pvr2_hdw_gpio_get_out(struct pvr2_hdw *hdw,u32 *dp)
498562306a36Sopenharmony_ci{
498662306a36Sopenharmony_ci	return pvr2_read_register(hdw,PVR2_GPIO_OUT,dp);
498762306a36Sopenharmony_ci}
498862306a36Sopenharmony_ci
498962306a36Sopenharmony_ci
499062306a36Sopenharmony_ciint pvr2_hdw_gpio_get_in(struct pvr2_hdw *hdw,u32 *dp)
499162306a36Sopenharmony_ci{
499262306a36Sopenharmony_ci	return pvr2_read_register(hdw,PVR2_GPIO_IN,dp);
499362306a36Sopenharmony_ci}
499462306a36Sopenharmony_ci
499562306a36Sopenharmony_ci
499662306a36Sopenharmony_ciint pvr2_hdw_gpio_chg_dir(struct pvr2_hdw *hdw,u32 msk,u32 val)
499762306a36Sopenharmony_ci{
499862306a36Sopenharmony_ci	u32 cval,nval;
499962306a36Sopenharmony_ci	int ret;
500062306a36Sopenharmony_ci	if (~msk) {
500162306a36Sopenharmony_ci		ret = pvr2_read_register(hdw,PVR2_GPIO_DIR,&cval);
500262306a36Sopenharmony_ci		if (ret) return ret;
500362306a36Sopenharmony_ci		nval = (cval & ~msk) | (val & msk);
500462306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_GPIO,
500562306a36Sopenharmony_ci			   "GPIO direction changing 0x%x:0x%x from 0x%x to 0x%x",
500662306a36Sopenharmony_ci			   msk,val,cval,nval);
500762306a36Sopenharmony_ci	} else {
500862306a36Sopenharmony_ci		nval = val;
500962306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_GPIO,
501062306a36Sopenharmony_ci			   "GPIO direction changing to 0x%x",nval);
501162306a36Sopenharmony_ci	}
501262306a36Sopenharmony_ci	return pvr2_write_register(hdw,PVR2_GPIO_DIR,nval);
501362306a36Sopenharmony_ci}
501462306a36Sopenharmony_ci
501562306a36Sopenharmony_ci
501662306a36Sopenharmony_ciint pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val)
501762306a36Sopenharmony_ci{
501862306a36Sopenharmony_ci	u32 cval,nval;
501962306a36Sopenharmony_ci	int ret;
502062306a36Sopenharmony_ci	if (~msk) {
502162306a36Sopenharmony_ci		ret = pvr2_read_register(hdw,PVR2_GPIO_OUT,&cval);
502262306a36Sopenharmony_ci		if (ret) return ret;
502362306a36Sopenharmony_ci		nval = (cval & ~msk) | (val & msk);
502462306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_GPIO,
502562306a36Sopenharmony_ci			   "GPIO output changing 0x%x:0x%x from 0x%x to 0x%x",
502662306a36Sopenharmony_ci			   msk,val,cval,nval);
502762306a36Sopenharmony_ci	} else {
502862306a36Sopenharmony_ci		nval = val;
502962306a36Sopenharmony_ci		pvr2_trace(PVR2_TRACE_GPIO,
503062306a36Sopenharmony_ci			   "GPIO output changing to 0x%x",nval);
503162306a36Sopenharmony_ci	}
503262306a36Sopenharmony_ci	return pvr2_write_register(hdw,PVR2_GPIO_OUT,nval);
503362306a36Sopenharmony_ci}
503462306a36Sopenharmony_ci
503562306a36Sopenharmony_ci
503662306a36Sopenharmony_civoid pvr2_hdw_status_poll(struct pvr2_hdw *hdw)
503762306a36Sopenharmony_ci{
503862306a36Sopenharmony_ci	struct v4l2_tuner *vtp = &hdw->tuner_signal_info;
503962306a36Sopenharmony_ci	memset(vtp, 0, sizeof(*vtp));
504062306a36Sopenharmony_ci	vtp->type = (hdw->input_val == PVR2_CVAL_INPUT_RADIO) ?
504162306a36Sopenharmony_ci		V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
504262306a36Sopenharmony_ci	hdw->tuner_signal_stale = 0;
504362306a36Sopenharmony_ci	/* Note: There apparently is no replacement for VIDIOC_CROPCAP
504462306a36Sopenharmony_ci	   using v4l2-subdev - therefore we can't support that AT ALL right
504562306a36Sopenharmony_ci	   now.  (Of course, no sub-drivers seem to implement it either.
504662306a36Sopenharmony_ci	   But now it's a chicken and egg problem...) */
504762306a36Sopenharmony_ci	v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, g_tuner, vtp);
504862306a36Sopenharmony_ci	pvr2_trace(PVR2_TRACE_CHIPS, "subdev status poll type=%u strength=%u audio=0x%x cap=0x%x low=%u hi=%u",
504962306a36Sopenharmony_ci		   vtp->type,
505062306a36Sopenharmony_ci		   vtp->signal, vtp->rxsubchans, vtp->capability,
505162306a36Sopenharmony_ci		   vtp->rangelow, vtp->rangehigh);
505262306a36Sopenharmony_ci
505362306a36Sopenharmony_ci	/* We have to do this to avoid getting into constant polling if
505462306a36Sopenharmony_ci	   there's nobody to answer a poll of cropcap info. */
505562306a36Sopenharmony_ci	hdw->cropcap_stale = 0;
505662306a36Sopenharmony_ci}
505762306a36Sopenharmony_ci
505862306a36Sopenharmony_ci
505962306a36Sopenharmony_ciunsigned int pvr2_hdw_get_input_available(struct pvr2_hdw *hdw)
506062306a36Sopenharmony_ci{
506162306a36Sopenharmony_ci	return hdw->input_avail_mask;
506262306a36Sopenharmony_ci}
506362306a36Sopenharmony_ci
506462306a36Sopenharmony_ci
506562306a36Sopenharmony_ciunsigned int pvr2_hdw_get_input_allowed(struct pvr2_hdw *hdw)
506662306a36Sopenharmony_ci{
506762306a36Sopenharmony_ci	return hdw->input_allowed_mask;
506862306a36Sopenharmony_ci}
506962306a36Sopenharmony_ci
507062306a36Sopenharmony_ci
507162306a36Sopenharmony_cistatic int pvr2_hdw_set_input(struct pvr2_hdw *hdw,int v)
507262306a36Sopenharmony_ci{
507362306a36Sopenharmony_ci	if (hdw->input_val != v) {
507462306a36Sopenharmony_ci		hdw->input_val = v;
507562306a36Sopenharmony_ci		hdw->input_dirty = !0;
507662306a36Sopenharmony_ci	}
507762306a36Sopenharmony_ci
507862306a36Sopenharmony_ci	/* Handle side effects - if we switch to a mode that needs the RF
507962306a36Sopenharmony_ci	   tuner, then select the right frequency choice as well and mark
508062306a36Sopenharmony_ci	   it dirty. */
508162306a36Sopenharmony_ci	if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
508262306a36Sopenharmony_ci		hdw->freqSelector = 0;
508362306a36Sopenharmony_ci		hdw->freqDirty = !0;
508462306a36Sopenharmony_ci	} else if ((hdw->input_val == PVR2_CVAL_INPUT_TV) ||
508562306a36Sopenharmony_ci		   (hdw->input_val == PVR2_CVAL_INPUT_DTV)) {
508662306a36Sopenharmony_ci		hdw->freqSelector = 1;
508762306a36Sopenharmony_ci		hdw->freqDirty = !0;
508862306a36Sopenharmony_ci	}
508962306a36Sopenharmony_ci	return 0;
509062306a36Sopenharmony_ci}
509162306a36Sopenharmony_ci
509262306a36Sopenharmony_ci
509362306a36Sopenharmony_ciint pvr2_hdw_set_input_allowed(struct pvr2_hdw *hdw,
509462306a36Sopenharmony_ci			       unsigned int change_mask,
509562306a36Sopenharmony_ci			       unsigned int change_val)
509662306a36Sopenharmony_ci{
509762306a36Sopenharmony_ci	int ret = 0;
509862306a36Sopenharmony_ci	unsigned int nv,m,idx;
509962306a36Sopenharmony_ci	LOCK_TAKE(hdw->big_lock);
510062306a36Sopenharmony_ci	do {
510162306a36Sopenharmony_ci		nv = hdw->input_allowed_mask & ~change_mask;
510262306a36Sopenharmony_ci		nv |= (change_val & change_mask);
510362306a36Sopenharmony_ci		nv &= hdw->input_avail_mask;
510462306a36Sopenharmony_ci		if (!nv) {
510562306a36Sopenharmony_ci			/* No legal modes left; return error instead. */
510662306a36Sopenharmony_ci			ret = -EPERM;
510762306a36Sopenharmony_ci			break;
510862306a36Sopenharmony_ci		}
510962306a36Sopenharmony_ci		hdw->input_allowed_mask = nv;
511062306a36Sopenharmony_ci		if ((1UL << hdw->input_val) & hdw->input_allowed_mask) {
511162306a36Sopenharmony_ci			/* Current mode is still in the allowed mask, so
511262306a36Sopenharmony_ci			   we're done. */
511362306a36Sopenharmony_ci			break;
511462306a36Sopenharmony_ci		}
511562306a36Sopenharmony_ci		/* Select and switch to a mode that is still in the allowed
511662306a36Sopenharmony_ci		   mask */
511762306a36Sopenharmony_ci		if (!hdw->input_allowed_mask) {
511862306a36Sopenharmony_ci			/* Nothing legal; give up */
511962306a36Sopenharmony_ci			break;
512062306a36Sopenharmony_ci		}
512162306a36Sopenharmony_ci		m = hdw->input_allowed_mask;
512262306a36Sopenharmony_ci		for (idx = 0; idx < (sizeof(m) << 3); idx++) {
512362306a36Sopenharmony_ci			if (!((1UL << idx) & m)) continue;
512462306a36Sopenharmony_ci			pvr2_hdw_set_input(hdw,idx);
512562306a36Sopenharmony_ci			break;
512662306a36Sopenharmony_ci		}
512762306a36Sopenharmony_ci	} while (0);
512862306a36Sopenharmony_ci	LOCK_GIVE(hdw->big_lock);
512962306a36Sopenharmony_ci	return ret;
513062306a36Sopenharmony_ci}
513162306a36Sopenharmony_ci
513262306a36Sopenharmony_ci
513362306a36Sopenharmony_ci/* Find I2C address of eeprom */
513462306a36Sopenharmony_cistatic int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw)
513562306a36Sopenharmony_ci{
513662306a36Sopenharmony_ci	int result;
513762306a36Sopenharmony_ci	LOCK_TAKE(hdw->ctl_lock); do {
513862306a36Sopenharmony_ci		hdw->cmd_buffer[0] = FX2CMD_GET_EEPROM_ADDR;
513962306a36Sopenharmony_ci		result = pvr2_send_request(hdw,
514062306a36Sopenharmony_ci					   hdw->cmd_buffer,1,
514162306a36Sopenharmony_ci					   hdw->cmd_buffer,1);
514262306a36Sopenharmony_ci		if (result < 0) break;
514362306a36Sopenharmony_ci		result = hdw->cmd_buffer[0];
514462306a36Sopenharmony_ci	} while(0); LOCK_GIVE(hdw->ctl_lock);
514562306a36Sopenharmony_ci	return result;
514662306a36Sopenharmony_ci}
5147