18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci *  Copyright (C) 2005 Mike Isely <isely@pobox.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/errno.h>
88c2ecf20Sopenharmony_ci#include <linux/string.h>
98c2ecf20Sopenharmony_ci#include <linux/slab.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/firmware.h>
128c2ecf20Sopenharmony_ci#include <linux/videodev2.h>
138c2ecf20Sopenharmony_ci#include <media/v4l2-common.h>
148c2ecf20Sopenharmony_ci#include <media/tuner.h>
158c2ecf20Sopenharmony_ci#include "pvrusb2.h"
168c2ecf20Sopenharmony_ci#include "pvrusb2-std.h"
178c2ecf20Sopenharmony_ci#include "pvrusb2-util.h"
188c2ecf20Sopenharmony_ci#include "pvrusb2-hdw.h"
198c2ecf20Sopenharmony_ci#include "pvrusb2-i2c-core.h"
208c2ecf20Sopenharmony_ci#include "pvrusb2-eeprom.h"
218c2ecf20Sopenharmony_ci#include "pvrusb2-hdw-internal.h"
228c2ecf20Sopenharmony_ci#include "pvrusb2-encoder.h"
238c2ecf20Sopenharmony_ci#include "pvrusb2-debug.h"
248c2ecf20Sopenharmony_ci#include "pvrusb2-fx2-cmd.h"
258c2ecf20Sopenharmony_ci#include "pvrusb2-wm8775.h"
268c2ecf20Sopenharmony_ci#include "pvrusb2-video-v4l.h"
278c2ecf20Sopenharmony_ci#include "pvrusb2-cx2584x-v4l.h"
288c2ecf20Sopenharmony_ci#include "pvrusb2-cs53l32a.h"
298c2ecf20Sopenharmony_ci#include "pvrusb2-audio.h"
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define TV_MIN_FREQ     55250000L
328c2ecf20Sopenharmony_ci#define TV_MAX_FREQ    850000000L
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/* This defines a minimum interval that the decoder must remain quiet
358c2ecf20Sopenharmony_ci   before we are allowed to start it running. */
368c2ecf20Sopenharmony_ci#define TIME_MSEC_DECODER_WAIT 50
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/* This defines a minimum interval that the decoder must be allowed to run
398c2ecf20Sopenharmony_ci   before we can safely begin using its streaming output. */
408c2ecf20Sopenharmony_ci#define TIME_MSEC_DECODER_STABILIZATION_WAIT 300
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* This defines a minimum interval that the encoder must remain quiet
438c2ecf20Sopenharmony_ci   before we are allowed to configure it. */
448c2ecf20Sopenharmony_ci#define TIME_MSEC_ENCODER_WAIT 50
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/* This defines the minimum interval that the encoder must successfully run
478c2ecf20Sopenharmony_ci   before we consider that the encoder has run at least once since its
488c2ecf20Sopenharmony_ci   firmware has been loaded.  This measurement is in important for cases
498c2ecf20Sopenharmony_ci   where we can't do something until we know that the encoder has been run
508c2ecf20Sopenharmony_ci   at least once. */
518c2ecf20Sopenharmony_ci#define TIME_MSEC_ENCODER_OK 250
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic struct pvr2_hdw *unit_pointers[PVR_NUM] = {[ 0 ... PVR_NUM-1 ] = NULL};
548c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(pvr2_unit_mtx);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic int ctlchg;
578c2ecf20Sopenharmony_cistatic int procreload;
588c2ecf20Sopenharmony_cistatic int tuner[PVR_NUM] = { [0 ... PVR_NUM-1] = -1 };
598c2ecf20Sopenharmony_cistatic int tolerance[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 };
608c2ecf20Sopenharmony_cistatic int video_std[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 };
618c2ecf20Sopenharmony_cistatic int init_pause_msec;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cimodule_param(ctlchg, int, S_IRUGO|S_IWUSR);
648c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ctlchg, "0=optimize ctl change 1=always accept new ctl value");
658c2ecf20Sopenharmony_cimodule_param(init_pause_msec, int, S_IRUGO|S_IWUSR);
668c2ecf20Sopenharmony_ciMODULE_PARM_DESC(init_pause_msec, "hardware initialization settling delay");
678c2ecf20Sopenharmony_cimodule_param(procreload, int, S_IRUGO|S_IWUSR);
688c2ecf20Sopenharmony_ciMODULE_PARM_DESC(procreload,
698c2ecf20Sopenharmony_ci		 "Attempt init failure recovery with firmware reload");
708c2ecf20Sopenharmony_cimodule_param_array(tuner,    int, NULL, 0444);
718c2ecf20Sopenharmony_ciMODULE_PARM_DESC(tuner,"specify installed tuner type");
728c2ecf20Sopenharmony_cimodule_param_array(video_std,    int, NULL, 0444);
738c2ecf20Sopenharmony_ciMODULE_PARM_DESC(video_std,"specify initial video standard");
748c2ecf20Sopenharmony_cimodule_param_array(tolerance,    int, NULL, 0444);
758c2ecf20Sopenharmony_ciMODULE_PARM_DESC(tolerance,"specify stream error tolerance");
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci/* US Broadcast channel 3 (61.25 MHz), to help with testing */
788c2ecf20Sopenharmony_cistatic int default_tv_freq    = 61250000L;
798c2ecf20Sopenharmony_ci/* 104.3 MHz, a usable FM station for my area */
808c2ecf20Sopenharmony_cistatic int default_radio_freq = 104300000L;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cimodule_param_named(tv_freq, default_tv_freq, int, 0444);
838c2ecf20Sopenharmony_ciMODULE_PARM_DESC(tv_freq, "specify initial television frequency");
848c2ecf20Sopenharmony_cimodule_param_named(radio_freq, default_radio_freq, int, 0444);
858c2ecf20Sopenharmony_ciMODULE_PARM_DESC(radio_freq, "specify initial radio frequency");
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci#define PVR2_CTL_WRITE_ENDPOINT  0x01
888c2ecf20Sopenharmony_ci#define PVR2_CTL_READ_ENDPOINT   0x81
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci#define PVR2_GPIO_IN 0x9008
918c2ecf20Sopenharmony_ci#define PVR2_GPIO_OUT 0x900c
928c2ecf20Sopenharmony_ci#define PVR2_GPIO_DIR 0x9020
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci#define trace_firmware(...) pvr2_trace(PVR2_TRACE_FIRMWARE,__VA_ARGS__)
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci#define PVR2_FIRMWARE_ENDPOINT   0x02
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci/* size of a firmware chunk */
998c2ecf20Sopenharmony_ci#define FIRMWARE_CHUNK_SIZE 0x2000
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_citypedef void (*pvr2_subdev_update_func)(struct pvr2_hdw *,
1028c2ecf20Sopenharmony_ci					struct v4l2_subdev *);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic const pvr2_subdev_update_func pvr2_module_update_functions[] = {
1058c2ecf20Sopenharmony_ci	[PVR2_CLIENT_ID_WM8775] = pvr2_wm8775_subdev_update,
1068c2ecf20Sopenharmony_ci	[PVR2_CLIENT_ID_SAA7115] = pvr2_saa7115_subdev_update,
1078c2ecf20Sopenharmony_ci	[PVR2_CLIENT_ID_MSP3400] = pvr2_msp3400_subdev_update,
1088c2ecf20Sopenharmony_ci	[PVR2_CLIENT_ID_CX25840] = pvr2_cx25840_subdev_update,
1098c2ecf20Sopenharmony_ci	[PVR2_CLIENT_ID_CS53L32A] = pvr2_cs53l32a_subdev_update,
1108c2ecf20Sopenharmony_ci};
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic const char *module_names[] = {
1138c2ecf20Sopenharmony_ci	[PVR2_CLIENT_ID_MSP3400] = "msp3400",
1148c2ecf20Sopenharmony_ci	[PVR2_CLIENT_ID_CX25840] = "cx25840",
1158c2ecf20Sopenharmony_ci	[PVR2_CLIENT_ID_SAA7115] = "saa7115",
1168c2ecf20Sopenharmony_ci	[PVR2_CLIENT_ID_TUNER] = "tuner",
1178c2ecf20Sopenharmony_ci	[PVR2_CLIENT_ID_DEMOD] = "tuner",
1188c2ecf20Sopenharmony_ci	[PVR2_CLIENT_ID_CS53L32A] = "cs53l32a",
1198c2ecf20Sopenharmony_ci	[PVR2_CLIENT_ID_WM8775] = "wm8775",
1208c2ecf20Sopenharmony_ci};
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic const unsigned char *module_i2c_addresses[] = {
1248c2ecf20Sopenharmony_ci	[PVR2_CLIENT_ID_TUNER] = "\x60\x61\x62\x63",
1258c2ecf20Sopenharmony_ci	[PVR2_CLIENT_ID_DEMOD] = "\x43",
1268c2ecf20Sopenharmony_ci	[PVR2_CLIENT_ID_MSP3400] = "\x40",
1278c2ecf20Sopenharmony_ci	[PVR2_CLIENT_ID_SAA7115] = "\x21",
1288c2ecf20Sopenharmony_ci	[PVR2_CLIENT_ID_WM8775] = "\x1b",
1298c2ecf20Sopenharmony_ci	[PVR2_CLIENT_ID_CX25840] = "\x44",
1308c2ecf20Sopenharmony_ci	[PVR2_CLIENT_ID_CS53L32A] = "\x11",
1318c2ecf20Sopenharmony_ci};
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic const char *ir_scheme_names[] = {
1358c2ecf20Sopenharmony_ci	[PVR2_IR_SCHEME_NONE] = "none",
1368c2ecf20Sopenharmony_ci	[PVR2_IR_SCHEME_29XXX] = "29xxx",
1378c2ecf20Sopenharmony_ci	[PVR2_IR_SCHEME_24XXX] = "24xxx (29xxx emulation)",
1388c2ecf20Sopenharmony_ci	[PVR2_IR_SCHEME_24XXX_MCE] = "24xxx (MCE device)",
1398c2ecf20Sopenharmony_ci	[PVR2_IR_SCHEME_ZILOG] = "Zilog",
1408c2ecf20Sopenharmony_ci};
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci/* Define the list of additional controls we'll dynamically construct based
1448c2ecf20Sopenharmony_ci   on query of the cx2341x module. */
1458c2ecf20Sopenharmony_cistruct pvr2_mpeg_ids {
1468c2ecf20Sopenharmony_ci	const char *strid;
1478c2ecf20Sopenharmony_ci	int id;
1488c2ecf20Sopenharmony_ci};
1498c2ecf20Sopenharmony_cistatic const struct pvr2_mpeg_ids mpeg_ids[] = {
1508c2ecf20Sopenharmony_ci	{
1518c2ecf20Sopenharmony_ci		.strid = "audio_layer",
1528c2ecf20Sopenharmony_ci		.id = V4L2_CID_MPEG_AUDIO_ENCODING,
1538c2ecf20Sopenharmony_ci	},{
1548c2ecf20Sopenharmony_ci		.strid = "audio_bitrate",
1558c2ecf20Sopenharmony_ci		.id = V4L2_CID_MPEG_AUDIO_L2_BITRATE,
1568c2ecf20Sopenharmony_ci	},{
1578c2ecf20Sopenharmony_ci		/* Already using audio_mode elsewhere :-( */
1588c2ecf20Sopenharmony_ci		.strid = "mpeg_audio_mode",
1598c2ecf20Sopenharmony_ci		.id = V4L2_CID_MPEG_AUDIO_MODE,
1608c2ecf20Sopenharmony_ci	},{
1618c2ecf20Sopenharmony_ci		.strid = "mpeg_audio_mode_extension",
1628c2ecf20Sopenharmony_ci		.id = V4L2_CID_MPEG_AUDIO_MODE_EXTENSION,
1638c2ecf20Sopenharmony_ci	},{
1648c2ecf20Sopenharmony_ci		.strid = "audio_emphasis",
1658c2ecf20Sopenharmony_ci		.id = V4L2_CID_MPEG_AUDIO_EMPHASIS,
1668c2ecf20Sopenharmony_ci	},{
1678c2ecf20Sopenharmony_ci		.strid = "audio_crc",
1688c2ecf20Sopenharmony_ci		.id = V4L2_CID_MPEG_AUDIO_CRC,
1698c2ecf20Sopenharmony_ci	},{
1708c2ecf20Sopenharmony_ci		.strid = "video_aspect",
1718c2ecf20Sopenharmony_ci		.id = V4L2_CID_MPEG_VIDEO_ASPECT,
1728c2ecf20Sopenharmony_ci	},{
1738c2ecf20Sopenharmony_ci		.strid = "video_b_frames",
1748c2ecf20Sopenharmony_ci		.id = V4L2_CID_MPEG_VIDEO_B_FRAMES,
1758c2ecf20Sopenharmony_ci	},{
1768c2ecf20Sopenharmony_ci		.strid = "video_gop_size",
1778c2ecf20Sopenharmony_ci		.id = V4L2_CID_MPEG_VIDEO_GOP_SIZE,
1788c2ecf20Sopenharmony_ci	},{
1798c2ecf20Sopenharmony_ci		.strid = "video_gop_closure",
1808c2ecf20Sopenharmony_ci		.id = V4L2_CID_MPEG_VIDEO_GOP_CLOSURE,
1818c2ecf20Sopenharmony_ci	},{
1828c2ecf20Sopenharmony_ci		.strid = "video_bitrate_mode",
1838c2ecf20Sopenharmony_ci		.id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
1848c2ecf20Sopenharmony_ci	},{
1858c2ecf20Sopenharmony_ci		.strid = "video_bitrate",
1868c2ecf20Sopenharmony_ci		.id = V4L2_CID_MPEG_VIDEO_BITRATE,
1878c2ecf20Sopenharmony_ci	},{
1888c2ecf20Sopenharmony_ci		.strid = "video_bitrate_peak",
1898c2ecf20Sopenharmony_ci		.id = V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
1908c2ecf20Sopenharmony_ci	},{
1918c2ecf20Sopenharmony_ci		.strid = "video_temporal_decimation",
1928c2ecf20Sopenharmony_ci		.id = V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION,
1938c2ecf20Sopenharmony_ci	},{
1948c2ecf20Sopenharmony_ci		.strid = "stream_type",
1958c2ecf20Sopenharmony_ci		.id = V4L2_CID_MPEG_STREAM_TYPE,
1968c2ecf20Sopenharmony_ci	},{
1978c2ecf20Sopenharmony_ci		.strid = "video_spatial_filter_mode",
1988c2ecf20Sopenharmony_ci		.id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE,
1998c2ecf20Sopenharmony_ci	},{
2008c2ecf20Sopenharmony_ci		.strid = "video_spatial_filter",
2018c2ecf20Sopenharmony_ci		.id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER,
2028c2ecf20Sopenharmony_ci	},{
2038c2ecf20Sopenharmony_ci		.strid = "video_luma_spatial_filter_type",
2048c2ecf20Sopenharmony_ci		.id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE,
2058c2ecf20Sopenharmony_ci	},{
2068c2ecf20Sopenharmony_ci		.strid = "video_chroma_spatial_filter_type",
2078c2ecf20Sopenharmony_ci		.id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE,
2088c2ecf20Sopenharmony_ci	},{
2098c2ecf20Sopenharmony_ci		.strid = "video_temporal_filter_mode",
2108c2ecf20Sopenharmony_ci		.id = V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE,
2118c2ecf20Sopenharmony_ci	},{
2128c2ecf20Sopenharmony_ci		.strid = "video_temporal_filter",
2138c2ecf20Sopenharmony_ci		.id = V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER,
2148c2ecf20Sopenharmony_ci	},{
2158c2ecf20Sopenharmony_ci		.strid = "video_median_filter_type",
2168c2ecf20Sopenharmony_ci		.id = V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE,
2178c2ecf20Sopenharmony_ci	},{
2188c2ecf20Sopenharmony_ci		.strid = "video_luma_median_filter_top",
2198c2ecf20Sopenharmony_ci		.id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP,
2208c2ecf20Sopenharmony_ci	},{
2218c2ecf20Sopenharmony_ci		.strid = "video_luma_median_filter_bottom",
2228c2ecf20Sopenharmony_ci		.id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM,
2238c2ecf20Sopenharmony_ci	},{
2248c2ecf20Sopenharmony_ci		.strid = "video_chroma_median_filter_top",
2258c2ecf20Sopenharmony_ci		.id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP,
2268c2ecf20Sopenharmony_ci	},{
2278c2ecf20Sopenharmony_ci		.strid = "video_chroma_median_filter_bottom",
2288c2ecf20Sopenharmony_ci		.id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM,
2298c2ecf20Sopenharmony_ci	}
2308c2ecf20Sopenharmony_ci};
2318c2ecf20Sopenharmony_ci#define MPEGDEF_COUNT ARRAY_SIZE(mpeg_ids)
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistatic const char *control_values_srate[] = {
2358c2ecf20Sopenharmony_ci	[V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100]   = "44.1 kHz",
2368c2ecf20Sopenharmony_ci	[V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000]   = "48 kHz",
2378c2ecf20Sopenharmony_ci	[V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000]   = "32 kHz",
2388c2ecf20Sopenharmony_ci};
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cistatic const char *control_values_input[] = {
2438c2ecf20Sopenharmony_ci	[PVR2_CVAL_INPUT_TV]        = "television",  /*xawtv needs this name*/
2448c2ecf20Sopenharmony_ci	[PVR2_CVAL_INPUT_DTV]       = "dtv",
2458c2ecf20Sopenharmony_ci	[PVR2_CVAL_INPUT_RADIO]     = "radio",
2468c2ecf20Sopenharmony_ci	[PVR2_CVAL_INPUT_SVIDEO]    = "s-video",
2478c2ecf20Sopenharmony_ci	[PVR2_CVAL_INPUT_COMPOSITE] = "composite",
2488c2ecf20Sopenharmony_ci};
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistatic const char *control_values_audiomode[] = {
2528c2ecf20Sopenharmony_ci	[V4L2_TUNER_MODE_MONO]   = "Mono",
2538c2ecf20Sopenharmony_ci	[V4L2_TUNER_MODE_STEREO] = "Stereo",
2548c2ecf20Sopenharmony_ci	[V4L2_TUNER_MODE_LANG1]  = "Lang1",
2558c2ecf20Sopenharmony_ci	[V4L2_TUNER_MODE_LANG2]  = "Lang2",
2568c2ecf20Sopenharmony_ci	[V4L2_TUNER_MODE_LANG1_LANG2] = "Lang1+Lang2",
2578c2ecf20Sopenharmony_ci};
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic const char *control_values_hsm[] = {
2618c2ecf20Sopenharmony_ci	[PVR2_CVAL_HSM_FAIL] = "Fail",
2628c2ecf20Sopenharmony_ci	[PVR2_CVAL_HSM_HIGH] = "High",
2638c2ecf20Sopenharmony_ci	[PVR2_CVAL_HSM_FULL] = "Full",
2648c2ecf20Sopenharmony_ci};
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic const char *pvr2_state_names[] = {
2688c2ecf20Sopenharmony_ci	[PVR2_STATE_NONE] =    "none",
2698c2ecf20Sopenharmony_ci	[PVR2_STATE_DEAD] =    "dead",
2708c2ecf20Sopenharmony_ci	[PVR2_STATE_COLD] =    "cold",
2718c2ecf20Sopenharmony_ci	[PVR2_STATE_WARM] =    "warm",
2728c2ecf20Sopenharmony_ci	[PVR2_STATE_ERROR] =   "error",
2738c2ecf20Sopenharmony_ci	[PVR2_STATE_READY] =   "ready",
2748c2ecf20Sopenharmony_ci	[PVR2_STATE_RUN] =     "run",
2758c2ecf20Sopenharmony_ci};
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistruct pvr2_fx2cmd_descdef {
2798c2ecf20Sopenharmony_ci	unsigned char id;
2808c2ecf20Sopenharmony_ci	unsigned char *desc;
2818c2ecf20Sopenharmony_ci};
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic const struct pvr2_fx2cmd_descdef pvr2_fx2cmd_desc[] = {
2848c2ecf20Sopenharmony_ci	{FX2CMD_MEM_WRITE_DWORD, "write encoder dword"},
2858c2ecf20Sopenharmony_ci	{FX2CMD_MEM_READ_DWORD, "read encoder dword"},
2868c2ecf20Sopenharmony_ci	{FX2CMD_HCW_ZILOG_RESET, "zilog IR reset control"},
2878c2ecf20Sopenharmony_ci	{FX2CMD_MEM_READ_64BYTES, "read encoder 64bytes"},
2888c2ecf20Sopenharmony_ci	{FX2CMD_REG_WRITE, "write encoder register"},
2898c2ecf20Sopenharmony_ci	{FX2CMD_REG_READ, "read encoder register"},
2908c2ecf20Sopenharmony_ci	{FX2CMD_MEMSEL, "encoder memsel"},
2918c2ecf20Sopenharmony_ci	{FX2CMD_I2C_WRITE, "i2c write"},
2928c2ecf20Sopenharmony_ci	{FX2CMD_I2C_READ, "i2c read"},
2938c2ecf20Sopenharmony_ci	{FX2CMD_GET_USB_SPEED, "get USB speed"},
2948c2ecf20Sopenharmony_ci	{FX2CMD_STREAMING_ON, "stream on"},
2958c2ecf20Sopenharmony_ci	{FX2CMD_STREAMING_OFF, "stream off"},
2968c2ecf20Sopenharmony_ci	{FX2CMD_FWPOST1, "fwpost1"},
2978c2ecf20Sopenharmony_ci	{FX2CMD_POWER_OFF, "power off"},
2988c2ecf20Sopenharmony_ci	{FX2CMD_POWER_ON, "power on"},
2998c2ecf20Sopenharmony_ci	{FX2CMD_DEEP_RESET, "deep reset"},
3008c2ecf20Sopenharmony_ci	{FX2CMD_GET_EEPROM_ADDR, "get rom addr"},
3018c2ecf20Sopenharmony_ci	{FX2CMD_GET_IR_CODE, "get IR code"},
3028c2ecf20Sopenharmony_ci	{FX2CMD_HCW_DEMOD_RESETIN, "hcw demod resetin"},
3038c2ecf20Sopenharmony_ci	{FX2CMD_HCW_DTV_STREAMING_ON, "hcw dtv stream on"},
3048c2ecf20Sopenharmony_ci	{FX2CMD_HCW_DTV_STREAMING_OFF, "hcw dtv stream off"},
3058c2ecf20Sopenharmony_ci	{FX2CMD_ONAIR_DTV_STREAMING_ON, "onair dtv stream on"},
3068c2ecf20Sopenharmony_ci	{FX2CMD_ONAIR_DTV_STREAMING_OFF, "onair dtv stream off"},
3078c2ecf20Sopenharmony_ci	{FX2CMD_ONAIR_DTV_POWER_ON, "onair dtv power on"},
3088c2ecf20Sopenharmony_ci	{FX2CMD_ONAIR_DTV_POWER_OFF, "onair dtv power off"},
3098c2ecf20Sopenharmony_ci	{FX2CMD_HCW_DEMOD_RESET_PIN, "hcw demod reset pin"},
3108c2ecf20Sopenharmony_ci	{FX2CMD_HCW_MAKO_SLEEP_PIN, "hcw mako sleep pin"},
3118c2ecf20Sopenharmony_ci};
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic int pvr2_hdw_set_input(struct pvr2_hdw *hdw,int v);
3158c2ecf20Sopenharmony_cistatic void pvr2_hdw_state_sched(struct pvr2_hdw *);
3168c2ecf20Sopenharmony_cistatic int pvr2_hdw_state_eval(struct pvr2_hdw *);
3178c2ecf20Sopenharmony_cistatic void pvr2_hdw_set_cur_freq(struct pvr2_hdw *,unsigned long);
3188c2ecf20Sopenharmony_cistatic void pvr2_hdw_worker_poll(struct work_struct *work);
3198c2ecf20Sopenharmony_cistatic int pvr2_hdw_wait(struct pvr2_hdw *,int state);
3208c2ecf20Sopenharmony_cistatic int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *);
3218c2ecf20Sopenharmony_cistatic void pvr2_hdw_state_log_state(struct pvr2_hdw *);
3228c2ecf20Sopenharmony_cistatic int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl);
3238c2ecf20Sopenharmony_cistatic int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw);
3248c2ecf20Sopenharmony_cistatic int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw);
3258c2ecf20Sopenharmony_cistatic void pvr2_hdw_quiescent_timeout(struct timer_list *);
3268c2ecf20Sopenharmony_cistatic void pvr2_hdw_decoder_stabilization_timeout(struct timer_list *);
3278c2ecf20Sopenharmony_cistatic void pvr2_hdw_encoder_wait_timeout(struct timer_list *);
3288c2ecf20Sopenharmony_cistatic void pvr2_hdw_encoder_run_timeout(struct timer_list *);
3298c2ecf20Sopenharmony_cistatic int pvr2_issue_simple_cmd(struct pvr2_hdw *,u32);
3308c2ecf20Sopenharmony_cistatic int pvr2_send_request_ex(struct pvr2_hdw *hdw,
3318c2ecf20Sopenharmony_ci				unsigned int timeout,int probe_fl,
3328c2ecf20Sopenharmony_ci				void *write_data,unsigned int write_len,
3338c2ecf20Sopenharmony_ci				void *read_data,unsigned int read_len);
3348c2ecf20Sopenharmony_cistatic int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw);
3358c2ecf20Sopenharmony_cistatic v4l2_std_id pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic void trace_stbit(const char *name,int val)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_STBITS,
3408c2ecf20Sopenharmony_ci		   "State bit %s <-- %s",
3418c2ecf20Sopenharmony_ci		   name,(val ? "true" : "false"));
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_cistatic int ctrl_channelfreq_get(struct pvr2_ctrl *cptr,int *vp)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = cptr->hdw;
3478c2ecf20Sopenharmony_ci	if ((hdw->freqProgSlot > 0) && (hdw->freqProgSlot <= FREQTABLE_SIZE)) {
3488c2ecf20Sopenharmony_ci		*vp = hdw->freqTable[hdw->freqProgSlot-1];
3498c2ecf20Sopenharmony_ci	} else {
3508c2ecf20Sopenharmony_ci		*vp = 0;
3518c2ecf20Sopenharmony_ci	}
3528c2ecf20Sopenharmony_ci	return 0;
3538c2ecf20Sopenharmony_ci}
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_cistatic int ctrl_channelfreq_set(struct pvr2_ctrl *cptr,int m,int v)
3568c2ecf20Sopenharmony_ci{
3578c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = cptr->hdw;
3588c2ecf20Sopenharmony_ci	unsigned int slotId = hdw->freqProgSlot;
3598c2ecf20Sopenharmony_ci	if ((slotId > 0) && (slotId <= FREQTABLE_SIZE)) {
3608c2ecf20Sopenharmony_ci		hdw->freqTable[slotId-1] = v;
3618c2ecf20Sopenharmony_ci		/* Handle side effects correctly - if we're tuned to this
3628c2ecf20Sopenharmony_ci		   slot, then forgot the slot id relation since the stored
3638c2ecf20Sopenharmony_ci		   frequency has been changed. */
3648c2ecf20Sopenharmony_ci		if (hdw->freqSelector) {
3658c2ecf20Sopenharmony_ci			if (hdw->freqSlotRadio == slotId) {
3668c2ecf20Sopenharmony_ci				hdw->freqSlotRadio = 0;
3678c2ecf20Sopenharmony_ci			}
3688c2ecf20Sopenharmony_ci		} else {
3698c2ecf20Sopenharmony_ci			if (hdw->freqSlotTelevision == slotId) {
3708c2ecf20Sopenharmony_ci				hdw->freqSlotTelevision = 0;
3718c2ecf20Sopenharmony_ci			}
3728c2ecf20Sopenharmony_ci		}
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci	return 0;
3758c2ecf20Sopenharmony_ci}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_cistatic int ctrl_channelprog_get(struct pvr2_ctrl *cptr,int *vp)
3788c2ecf20Sopenharmony_ci{
3798c2ecf20Sopenharmony_ci	*vp = cptr->hdw->freqProgSlot;
3808c2ecf20Sopenharmony_ci	return 0;
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cistatic int ctrl_channelprog_set(struct pvr2_ctrl *cptr,int m,int v)
3848c2ecf20Sopenharmony_ci{
3858c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = cptr->hdw;
3868c2ecf20Sopenharmony_ci	if ((v >= 0) && (v <= FREQTABLE_SIZE)) {
3878c2ecf20Sopenharmony_ci		hdw->freqProgSlot = v;
3888c2ecf20Sopenharmony_ci	}
3898c2ecf20Sopenharmony_ci	return 0;
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cistatic int ctrl_channel_get(struct pvr2_ctrl *cptr,int *vp)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = cptr->hdw;
3958c2ecf20Sopenharmony_ci	*vp = hdw->freqSelector ? hdw->freqSlotRadio : hdw->freqSlotTelevision;
3968c2ecf20Sopenharmony_ci	return 0;
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_cistatic int ctrl_channel_set(struct pvr2_ctrl *cptr,int m,int slotId)
4008c2ecf20Sopenharmony_ci{
4018c2ecf20Sopenharmony_ci	unsigned freq = 0;
4028c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = cptr->hdw;
4038c2ecf20Sopenharmony_ci	if ((slotId < 0) || (slotId > FREQTABLE_SIZE)) return 0;
4048c2ecf20Sopenharmony_ci	if (slotId > 0) {
4058c2ecf20Sopenharmony_ci		freq = hdw->freqTable[slotId-1];
4068c2ecf20Sopenharmony_ci		if (!freq) return 0;
4078c2ecf20Sopenharmony_ci		pvr2_hdw_set_cur_freq(hdw,freq);
4088c2ecf20Sopenharmony_ci	}
4098c2ecf20Sopenharmony_ci	if (hdw->freqSelector) {
4108c2ecf20Sopenharmony_ci		hdw->freqSlotRadio = slotId;
4118c2ecf20Sopenharmony_ci	} else {
4128c2ecf20Sopenharmony_ci		hdw->freqSlotTelevision = slotId;
4138c2ecf20Sopenharmony_ci	}
4148c2ecf20Sopenharmony_ci	return 0;
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_cistatic int ctrl_freq_get(struct pvr2_ctrl *cptr,int *vp)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci	*vp = pvr2_hdw_get_cur_freq(cptr->hdw);
4208c2ecf20Sopenharmony_ci	return 0;
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_cistatic int ctrl_freq_is_dirty(struct pvr2_ctrl *cptr)
4248c2ecf20Sopenharmony_ci{
4258c2ecf20Sopenharmony_ci	return cptr->hdw->freqDirty != 0;
4268c2ecf20Sopenharmony_ci}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_cistatic void ctrl_freq_clear_dirty(struct pvr2_ctrl *cptr)
4298c2ecf20Sopenharmony_ci{
4308c2ecf20Sopenharmony_ci	cptr->hdw->freqDirty = 0;
4318c2ecf20Sopenharmony_ci}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_cistatic int ctrl_freq_set(struct pvr2_ctrl *cptr,int m,int v)
4348c2ecf20Sopenharmony_ci{
4358c2ecf20Sopenharmony_ci	pvr2_hdw_set_cur_freq(cptr->hdw,v);
4368c2ecf20Sopenharmony_ci	return 0;
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_cistatic int ctrl_cropl_min_get(struct pvr2_ctrl *cptr, int *left)
4408c2ecf20Sopenharmony_ci{
4418c2ecf20Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
4428c2ecf20Sopenharmony_ci	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
4438c2ecf20Sopenharmony_ci	if (stat != 0) {
4448c2ecf20Sopenharmony_ci		return stat;
4458c2ecf20Sopenharmony_ci	}
4468c2ecf20Sopenharmony_ci	*left = cap->bounds.left;
4478c2ecf20Sopenharmony_ci	return 0;
4488c2ecf20Sopenharmony_ci}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_cistatic int ctrl_cropl_max_get(struct pvr2_ctrl *cptr, int *left)
4518c2ecf20Sopenharmony_ci{
4528c2ecf20Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
4538c2ecf20Sopenharmony_ci	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
4548c2ecf20Sopenharmony_ci	if (stat != 0) {
4558c2ecf20Sopenharmony_ci		return stat;
4568c2ecf20Sopenharmony_ci	}
4578c2ecf20Sopenharmony_ci	*left = cap->bounds.left;
4588c2ecf20Sopenharmony_ci	if (cap->bounds.width > cptr->hdw->cropw_val) {
4598c2ecf20Sopenharmony_ci		*left += cap->bounds.width - cptr->hdw->cropw_val;
4608c2ecf20Sopenharmony_ci	}
4618c2ecf20Sopenharmony_ci	return 0;
4628c2ecf20Sopenharmony_ci}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_cistatic int ctrl_cropt_min_get(struct pvr2_ctrl *cptr, int *top)
4658c2ecf20Sopenharmony_ci{
4668c2ecf20Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
4678c2ecf20Sopenharmony_ci	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
4688c2ecf20Sopenharmony_ci	if (stat != 0) {
4698c2ecf20Sopenharmony_ci		return stat;
4708c2ecf20Sopenharmony_ci	}
4718c2ecf20Sopenharmony_ci	*top = cap->bounds.top;
4728c2ecf20Sopenharmony_ci	return 0;
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_cistatic int ctrl_cropt_max_get(struct pvr2_ctrl *cptr, int *top)
4768c2ecf20Sopenharmony_ci{
4778c2ecf20Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
4788c2ecf20Sopenharmony_ci	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
4798c2ecf20Sopenharmony_ci	if (stat != 0) {
4808c2ecf20Sopenharmony_ci		return stat;
4818c2ecf20Sopenharmony_ci	}
4828c2ecf20Sopenharmony_ci	*top = cap->bounds.top;
4838c2ecf20Sopenharmony_ci	if (cap->bounds.height > cptr->hdw->croph_val) {
4848c2ecf20Sopenharmony_ci		*top += cap->bounds.height - cptr->hdw->croph_val;
4858c2ecf20Sopenharmony_ci	}
4868c2ecf20Sopenharmony_ci	return 0;
4878c2ecf20Sopenharmony_ci}
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_cistatic int ctrl_cropw_max_get(struct pvr2_ctrl *cptr, int *width)
4908c2ecf20Sopenharmony_ci{
4918c2ecf20Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
4928c2ecf20Sopenharmony_ci	int stat, bleftend, cleft;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	stat = pvr2_hdw_check_cropcap(cptr->hdw);
4958c2ecf20Sopenharmony_ci	if (stat != 0) {
4968c2ecf20Sopenharmony_ci		return stat;
4978c2ecf20Sopenharmony_ci	}
4988c2ecf20Sopenharmony_ci	bleftend = cap->bounds.left+cap->bounds.width;
4998c2ecf20Sopenharmony_ci	cleft = cptr->hdw->cropl_val;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	*width = cleft < bleftend ? bleftend-cleft : 0;
5028c2ecf20Sopenharmony_ci	return 0;
5038c2ecf20Sopenharmony_ci}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_cistatic int ctrl_croph_max_get(struct pvr2_ctrl *cptr, int *height)
5068c2ecf20Sopenharmony_ci{
5078c2ecf20Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
5088c2ecf20Sopenharmony_ci	int stat, btopend, ctop;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	stat = pvr2_hdw_check_cropcap(cptr->hdw);
5118c2ecf20Sopenharmony_ci	if (stat != 0) {
5128c2ecf20Sopenharmony_ci		return stat;
5138c2ecf20Sopenharmony_ci	}
5148c2ecf20Sopenharmony_ci	btopend = cap->bounds.top+cap->bounds.height;
5158c2ecf20Sopenharmony_ci	ctop = cptr->hdw->cropt_val;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	*height = ctop < btopend ? btopend-ctop : 0;
5188c2ecf20Sopenharmony_ci	return 0;
5198c2ecf20Sopenharmony_ci}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_cistatic int ctrl_get_cropcapbl(struct pvr2_ctrl *cptr, int *val)
5228c2ecf20Sopenharmony_ci{
5238c2ecf20Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
5248c2ecf20Sopenharmony_ci	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
5258c2ecf20Sopenharmony_ci	if (stat != 0) {
5268c2ecf20Sopenharmony_ci		return stat;
5278c2ecf20Sopenharmony_ci	}
5288c2ecf20Sopenharmony_ci	*val = cap->bounds.left;
5298c2ecf20Sopenharmony_ci	return 0;
5308c2ecf20Sopenharmony_ci}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_cistatic int ctrl_get_cropcapbt(struct pvr2_ctrl *cptr, int *val)
5338c2ecf20Sopenharmony_ci{
5348c2ecf20Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
5358c2ecf20Sopenharmony_ci	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
5368c2ecf20Sopenharmony_ci	if (stat != 0) {
5378c2ecf20Sopenharmony_ci		return stat;
5388c2ecf20Sopenharmony_ci	}
5398c2ecf20Sopenharmony_ci	*val = cap->bounds.top;
5408c2ecf20Sopenharmony_ci	return 0;
5418c2ecf20Sopenharmony_ci}
5428c2ecf20Sopenharmony_ci
5438c2ecf20Sopenharmony_cistatic int ctrl_get_cropcapbw(struct pvr2_ctrl *cptr, int *val)
5448c2ecf20Sopenharmony_ci{
5458c2ecf20Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
5468c2ecf20Sopenharmony_ci	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
5478c2ecf20Sopenharmony_ci	if (stat != 0) {
5488c2ecf20Sopenharmony_ci		return stat;
5498c2ecf20Sopenharmony_ci	}
5508c2ecf20Sopenharmony_ci	*val = cap->bounds.width;
5518c2ecf20Sopenharmony_ci	return 0;
5528c2ecf20Sopenharmony_ci}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_cistatic int ctrl_get_cropcapbh(struct pvr2_ctrl *cptr, int *val)
5558c2ecf20Sopenharmony_ci{
5568c2ecf20Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
5578c2ecf20Sopenharmony_ci	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
5588c2ecf20Sopenharmony_ci	if (stat != 0) {
5598c2ecf20Sopenharmony_ci		return stat;
5608c2ecf20Sopenharmony_ci	}
5618c2ecf20Sopenharmony_ci	*val = cap->bounds.height;
5628c2ecf20Sopenharmony_ci	return 0;
5638c2ecf20Sopenharmony_ci}
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_cistatic int ctrl_get_cropcapdl(struct pvr2_ctrl *cptr, int *val)
5668c2ecf20Sopenharmony_ci{
5678c2ecf20Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
5688c2ecf20Sopenharmony_ci	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
5698c2ecf20Sopenharmony_ci	if (stat != 0) {
5708c2ecf20Sopenharmony_ci		return stat;
5718c2ecf20Sopenharmony_ci	}
5728c2ecf20Sopenharmony_ci	*val = cap->defrect.left;
5738c2ecf20Sopenharmony_ci	return 0;
5748c2ecf20Sopenharmony_ci}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_cistatic int ctrl_get_cropcapdt(struct pvr2_ctrl *cptr, int *val)
5778c2ecf20Sopenharmony_ci{
5788c2ecf20Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
5798c2ecf20Sopenharmony_ci	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
5808c2ecf20Sopenharmony_ci	if (stat != 0) {
5818c2ecf20Sopenharmony_ci		return stat;
5828c2ecf20Sopenharmony_ci	}
5838c2ecf20Sopenharmony_ci	*val = cap->defrect.top;
5848c2ecf20Sopenharmony_ci	return 0;
5858c2ecf20Sopenharmony_ci}
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_cistatic int ctrl_get_cropcapdw(struct pvr2_ctrl *cptr, int *val)
5888c2ecf20Sopenharmony_ci{
5898c2ecf20Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
5908c2ecf20Sopenharmony_ci	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
5918c2ecf20Sopenharmony_ci	if (stat != 0) {
5928c2ecf20Sopenharmony_ci		return stat;
5938c2ecf20Sopenharmony_ci	}
5948c2ecf20Sopenharmony_ci	*val = cap->defrect.width;
5958c2ecf20Sopenharmony_ci	return 0;
5968c2ecf20Sopenharmony_ci}
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_cistatic int ctrl_get_cropcapdh(struct pvr2_ctrl *cptr, int *val)
5998c2ecf20Sopenharmony_ci{
6008c2ecf20Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
6018c2ecf20Sopenharmony_ci	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
6028c2ecf20Sopenharmony_ci	if (stat != 0) {
6038c2ecf20Sopenharmony_ci		return stat;
6048c2ecf20Sopenharmony_ci	}
6058c2ecf20Sopenharmony_ci	*val = cap->defrect.height;
6068c2ecf20Sopenharmony_ci	return 0;
6078c2ecf20Sopenharmony_ci}
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_cistatic int ctrl_get_cropcappan(struct pvr2_ctrl *cptr, int *val)
6108c2ecf20Sopenharmony_ci{
6118c2ecf20Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
6128c2ecf20Sopenharmony_ci	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
6138c2ecf20Sopenharmony_ci	if (stat != 0) {
6148c2ecf20Sopenharmony_ci		return stat;
6158c2ecf20Sopenharmony_ci	}
6168c2ecf20Sopenharmony_ci	*val = cap->pixelaspect.numerator;
6178c2ecf20Sopenharmony_ci	return 0;
6188c2ecf20Sopenharmony_ci}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_cistatic int ctrl_get_cropcappad(struct pvr2_ctrl *cptr, int *val)
6218c2ecf20Sopenharmony_ci{
6228c2ecf20Sopenharmony_ci	struct v4l2_cropcap *cap = &cptr->hdw->cropcap_info;
6238c2ecf20Sopenharmony_ci	int stat = pvr2_hdw_check_cropcap(cptr->hdw);
6248c2ecf20Sopenharmony_ci	if (stat != 0) {
6258c2ecf20Sopenharmony_ci		return stat;
6268c2ecf20Sopenharmony_ci	}
6278c2ecf20Sopenharmony_ci	*val = cap->pixelaspect.denominator;
6288c2ecf20Sopenharmony_ci	return 0;
6298c2ecf20Sopenharmony_ci}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_cistatic int ctrl_vres_max_get(struct pvr2_ctrl *cptr,int *vp)
6328c2ecf20Sopenharmony_ci{
6338c2ecf20Sopenharmony_ci	/* Actual maximum depends on the video standard in effect. */
6348c2ecf20Sopenharmony_ci	if (cptr->hdw->std_mask_cur & V4L2_STD_525_60) {
6358c2ecf20Sopenharmony_ci		*vp = 480;
6368c2ecf20Sopenharmony_ci	} else {
6378c2ecf20Sopenharmony_ci		*vp = 576;
6388c2ecf20Sopenharmony_ci	}
6398c2ecf20Sopenharmony_ci	return 0;
6408c2ecf20Sopenharmony_ci}
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_cistatic int ctrl_vres_min_get(struct pvr2_ctrl *cptr,int *vp)
6438c2ecf20Sopenharmony_ci{
6448c2ecf20Sopenharmony_ci	/* Actual minimum depends on device digitizer type. */
6458c2ecf20Sopenharmony_ci	if (cptr->hdw->hdw_desc->flag_has_cx25840) {
6468c2ecf20Sopenharmony_ci		*vp = 75;
6478c2ecf20Sopenharmony_ci	} else {
6488c2ecf20Sopenharmony_ci		*vp = 17;
6498c2ecf20Sopenharmony_ci	}
6508c2ecf20Sopenharmony_ci	return 0;
6518c2ecf20Sopenharmony_ci}
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_cistatic int ctrl_get_input(struct pvr2_ctrl *cptr,int *vp)
6548c2ecf20Sopenharmony_ci{
6558c2ecf20Sopenharmony_ci	*vp = cptr->hdw->input_val;
6568c2ecf20Sopenharmony_ci	return 0;
6578c2ecf20Sopenharmony_ci}
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_cistatic int ctrl_check_input(struct pvr2_ctrl *cptr,int v)
6608c2ecf20Sopenharmony_ci{
6618c2ecf20Sopenharmony_ci	if (v < 0 || v > PVR2_CVAL_INPUT_MAX)
6628c2ecf20Sopenharmony_ci		return 0;
6638c2ecf20Sopenharmony_ci	return ((1UL << v) & cptr->hdw->input_allowed_mask) != 0;
6648c2ecf20Sopenharmony_ci}
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_cistatic int ctrl_set_input(struct pvr2_ctrl *cptr,int m,int v)
6678c2ecf20Sopenharmony_ci{
6688c2ecf20Sopenharmony_ci	return pvr2_hdw_set_input(cptr->hdw,v);
6698c2ecf20Sopenharmony_ci}
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_cistatic int ctrl_isdirty_input(struct pvr2_ctrl *cptr)
6728c2ecf20Sopenharmony_ci{
6738c2ecf20Sopenharmony_ci	return cptr->hdw->input_dirty != 0;
6748c2ecf20Sopenharmony_ci}
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_cistatic void ctrl_cleardirty_input(struct pvr2_ctrl *cptr)
6778c2ecf20Sopenharmony_ci{
6788c2ecf20Sopenharmony_ci	cptr->hdw->input_dirty = 0;
6798c2ecf20Sopenharmony_ci}
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_cistatic int ctrl_freq_max_get(struct pvr2_ctrl *cptr, int *vp)
6838c2ecf20Sopenharmony_ci{
6848c2ecf20Sopenharmony_ci	unsigned long fv;
6858c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = cptr->hdw;
6868c2ecf20Sopenharmony_ci	if (hdw->tuner_signal_stale) {
6878c2ecf20Sopenharmony_ci		pvr2_hdw_status_poll(hdw);
6888c2ecf20Sopenharmony_ci	}
6898c2ecf20Sopenharmony_ci	fv = hdw->tuner_signal_info.rangehigh;
6908c2ecf20Sopenharmony_ci	if (!fv) {
6918c2ecf20Sopenharmony_ci		/* Safety fallback */
6928c2ecf20Sopenharmony_ci		*vp = TV_MAX_FREQ;
6938c2ecf20Sopenharmony_ci		return 0;
6948c2ecf20Sopenharmony_ci	}
6958c2ecf20Sopenharmony_ci	if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) {
6968c2ecf20Sopenharmony_ci		fv = (fv * 125) / 2;
6978c2ecf20Sopenharmony_ci	} else {
6988c2ecf20Sopenharmony_ci		fv = fv * 62500;
6998c2ecf20Sopenharmony_ci	}
7008c2ecf20Sopenharmony_ci	*vp = fv;
7018c2ecf20Sopenharmony_ci	return 0;
7028c2ecf20Sopenharmony_ci}
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_cistatic int ctrl_freq_min_get(struct pvr2_ctrl *cptr, int *vp)
7058c2ecf20Sopenharmony_ci{
7068c2ecf20Sopenharmony_ci	unsigned long fv;
7078c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = cptr->hdw;
7088c2ecf20Sopenharmony_ci	if (hdw->tuner_signal_stale) {
7098c2ecf20Sopenharmony_ci		pvr2_hdw_status_poll(hdw);
7108c2ecf20Sopenharmony_ci	}
7118c2ecf20Sopenharmony_ci	fv = hdw->tuner_signal_info.rangelow;
7128c2ecf20Sopenharmony_ci	if (!fv) {
7138c2ecf20Sopenharmony_ci		/* Safety fallback */
7148c2ecf20Sopenharmony_ci		*vp = TV_MIN_FREQ;
7158c2ecf20Sopenharmony_ci		return 0;
7168c2ecf20Sopenharmony_ci	}
7178c2ecf20Sopenharmony_ci	if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) {
7188c2ecf20Sopenharmony_ci		fv = (fv * 125) / 2;
7198c2ecf20Sopenharmony_ci	} else {
7208c2ecf20Sopenharmony_ci		fv = fv * 62500;
7218c2ecf20Sopenharmony_ci	}
7228c2ecf20Sopenharmony_ci	*vp = fv;
7238c2ecf20Sopenharmony_ci	return 0;
7248c2ecf20Sopenharmony_ci}
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_cistatic int ctrl_cx2341x_is_dirty(struct pvr2_ctrl *cptr)
7278c2ecf20Sopenharmony_ci{
7288c2ecf20Sopenharmony_ci	return cptr->hdw->enc_stale != 0;
7298c2ecf20Sopenharmony_ci}
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_cistatic void ctrl_cx2341x_clear_dirty(struct pvr2_ctrl *cptr)
7328c2ecf20Sopenharmony_ci{
7338c2ecf20Sopenharmony_ci	cptr->hdw->enc_stale = 0;
7348c2ecf20Sopenharmony_ci	cptr->hdw->enc_unsafe_stale = 0;
7358c2ecf20Sopenharmony_ci}
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_cistatic int ctrl_cx2341x_get(struct pvr2_ctrl *cptr,int *vp)
7388c2ecf20Sopenharmony_ci{
7398c2ecf20Sopenharmony_ci	int ret;
7408c2ecf20Sopenharmony_ci	struct v4l2_ext_controls cs;
7418c2ecf20Sopenharmony_ci	struct v4l2_ext_control c1;
7428c2ecf20Sopenharmony_ci	memset(&cs,0,sizeof(cs));
7438c2ecf20Sopenharmony_ci	memset(&c1,0,sizeof(c1));
7448c2ecf20Sopenharmony_ci	cs.controls = &c1;
7458c2ecf20Sopenharmony_ci	cs.count = 1;
7468c2ecf20Sopenharmony_ci	c1.id = cptr->info->v4l_id;
7478c2ecf20Sopenharmony_ci	ret = cx2341x_ext_ctrls(&cptr->hdw->enc_ctl_state, 0, &cs,
7488c2ecf20Sopenharmony_ci				VIDIOC_G_EXT_CTRLS);
7498c2ecf20Sopenharmony_ci	if (ret) return ret;
7508c2ecf20Sopenharmony_ci	*vp = c1.value;
7518c2ecf20Sopenharmony_ci	return 0;
7528c2ecf20Sopenharmony_ci}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_cistatic int ctrl_cx2341x_set(struct pvr2_ctrl *cptr,int m,int v)
7558c2ecf20Sopenharmony_ci{
7568c2ecf20Sopenharmony_ci	int ret;
7578c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = cptr->hdw;
7588c2ecf20Sopenharmony_ci	struct v4l2_ext_controls cs;
7598c2ecf20Sopenharmony_ci	struct v4l2_ext_control c1;
7608c2ecf20Sopenharmony_ci	memset(&cs,0,sizeof(cs));
7618c2ecf20Sopenharmony_ci	memset(&c1,0,sizeof(c1));
7628c2ecf20Sopenharmony_ci	cs.controls = &c1;
7638c2ecf20Sopenharmony_ci	cs.count = 1;
7648c2ecf20Sopenharmony_ci	c1.id = cptr->info->v4l_id;
7658c2ecf20Sopenharmony_ci	c1.value = v;
7668c2ecf20Sopenharmony_ci	ret = cx2341x_ext_ctrls(&hdw->enc_ctl_state,
7678c2ecf20Sopenharmony_ci				hdw->state_encoder_run, &cs,
7688c2ecf20Sopenharmony_ci				VIDIOC_S_EXT_CTRLS);
7698c2ecf20Sopenharmony_ci	if (ret == -EBUSY) {
7708c2ecf20Sopenharmony_ci		/* Oops.  cx2341x is telling us it's not safe to change
7718c2ecf20Sopenharmony_ci		   this control while we're capturing.  Make a note of this
7728c2ecf20Sopenharmony_ci		   fact so that the pipeline will be stopped the next time
7738c2ecf20Sopenharmony_ci		   controls are committed.  Then go on ahead and store this
7748c2ecf20Sopenharmony_ci		   change anyway. */
7758c2ecf20Sopenharmony_ci		ret = cx2341x_ext_ctrls(&hdw->enc_ctl_state,
7768c2ecf20Sopenharmony_ci					0, &cs,
7778c2ecf20Sopenharmony_ci					VIDIOC_S_EXT_CTRLS);
7788c2ecf20Sopenharmony_ci		if (!ret) hdw->enc_unsafe_stale = !0;
7798c2ecf20Sopenharmony_ci	}
7808c2ecf20Sopenharmony_ci	if (ret) return ret;
7818c2ecf20Sopenharmony_ci	hdw->enc_stale = !0;
7828c2ecf20Sopenharmony_ci	return 0;
7838c2ecf20Sopenharmony_ci}
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_cistatic unsigned int ctrl_cx2341x_getv4lflags(struct pvr2_ctrl *cptr)
7868c2ecf20Sopenharmony_ci{
7878c2ecf20Sopenharmony_ci	struct v4l2_queryctrl qctrl = {};
7888c2ecf20Sopenharmony_ci	struct pvr2_ctl_info *info;
7898c2ecf20Sopenharmony_ci	qctrl.id = cptr->info->v4l_id;
7908c2ecf20Sopenharmony_ci	cx2341x_ctrl_query(&cptr->hdw->enc_ctl_state,&qctrl);
7918c2ecf20Sopenharmony_ci	/* Strip out the const so we can adjust a function pointer.  It's
7928c2ecf20Sopenharmony_ci	   OK to do this here because we know this is a dynamically created
7938c2ecf20Sopenharmony_ci	   control, so the underlying storage for the info pointer is (a)
7948c2ecf20Sopenharmony_ci	   private to us, and (b) not in read-only storage.  Either we do
7958c2ecf20Sopenharmony_ci	   this or we significantly complicate the underlying control
7968c2ecf20Sopenharmony_ci	   implementation. */
7978c2ecf20Sopenharmony_ci	info = (struct pvr2_ctl_info *)(cptr->info);
7988c2ecf20Sopenharmony_ci	if (qctrl.flags & V4L2_CTRL_FLAG_READ_ONLY) {
7998c2ecf20Sopenharmony_ci		if (info->set_value) {
8008c2ecf20Sopenharmony_ci			info->set_value = NULL;
8018c2ecf20Sopenharmony_ci		}
8028c2ecf20Sopenharmony_ci	} else {
8038c2ecf20Sopenharmony_ci		if (!(info->set_value)) {
8048c2ecf20Sopenharmony_ci			info->set_value = ctrl_cx2341x_set;
8058c2ecf20Sopenharmony_ci		}
8068c2ecf20Sopenharmony_ci	}
8078c2ecf20Sopenharmony_ci	return qctrl.flags;
8088c2ecf20Sopenharmony_ci}
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_cistatic int ctrl_streamingenabled_get(struct pvr2_ctrl *cptr,int *vp)
8118c2ecf20Sopenharmony_ci{
8128c2ecf20Sopenharmony_ci	*vp = cptr->hdw->state_pipeline_req;
8138c2ecf20Sopenharmony_ci	return 0;
8148c2ecf20Sopenharmony_ci}
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_cistatic int ctrl_masterstate_get(struct pvr2_ctrl *cptr,int *vp)
8178c2ecf20Sopenharmony_ci{
8188c2ecf20Sopenharmony_ci	*vp = cptr->hdw->master_state;
8198c2ecf20Sopenharmony_ci	return 0;
8208c2ecf20Sopenharmony_ci}
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_cistatic int ctrl_hsm_get(struct pvr2_ctrl *cptr,int *vp)
8238c2ecf20Sopenharmony_ci{
8248c2ecf20Sopenharmony_ci	int result = pvr2_hdw_is_hsm(cptr->hdw);
8258c2ecf20Sopenharmony_ci	*vp = PVR2_CVAL_HSM_FULL;
8268c2ecf20Sopenharmony_ci	if (result < 0) *vp = PVR2_CVAL_HSM_FAIL;
8278c2ecf20Sopenharmony_ci	if (result) *vp = PVR2_CVAL_HSM_HIGH;
8288c2ecf20Sopenharmony_ci	return 0;
8298c2ecf20Sopenharmony_ci}
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_cistatic int ctrl_stddetect_get(struct pvr2_ctrl *cptr, int *vp)
8328c2ecf20Sopenharmony_ci{
8338c2ecf20Sopenharmony_ci	*vp = pvr2_hdw_get_detected_std(cptr->hdw);
8348c2ecf20Sopenharmony_ci	return 0;
8358c2ecf20Sopenharmony_ci}
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_cistatic int ctrl_stdavail_get(struct pvr2_ctrl *cptr,int *vp)
8388c2ecf20Sopenharmony_ci{
8398c2ecf20Sopenharmony_ci	*vp = cptr->hdw->std_mask_avail;
8408c2ecf20Sopenharmony_ci	return 0;
8418c2ecf20Sopenharmony_ci}
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_cistatic int ctrl_stdavail_set(struct pvr2_ctrl *cptr,int m,int v)
8448c2ecf20Sopenharmony_ci{
8458c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = cptr->hdw;
8468c2ecf20Sopenharmony_ci	v4l2_std_id ns;
8478c2ecf20Sopenharmony_ci	ns = hdw->std_mask_avail;
8488c2ecf20Sopenharmony_ci	ns = (ns & ~m) | (v & m);
8498c2ecf20Sopenharmony_ci	if (ns == hdw->std_mask_avail) return 0;
8508c2ecf20Sopenharmony_ci	hdw->std_mask_avail = ns;
8518c2ecf20Sopenharmony_ci	hdw->std_info_cur.def.type_bitmask.valid_bits = hdw->std_mask_avail;
8528c2ecf20Sopenharmony_ci	return 0;
8538c2ecf20Sopenharmony_ci}
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_cistatic int ctrl_std_val_to_sym(struct pvr2_ctrl *cptr,int msk,int val,
8568c2ecf20Sopenharmony_ci			       char *bufPtr,unsigned int bufSize,
8578c2ecf20Sopenharmony_ci			       unsigned int *len)
8588c2ecf20Sopenharmony_ci{
8598c2ecf20Sopenharmony_ci	*len = pvr2_std_id_to_str(bufPtr,bufSize,msk & val);
8608c2ecf20Sopenharmony_ci	return 0;
8618c2ecf20Sopenharmony_ci}
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_cistatic int ctrl_std_sym_to_val(struct pvr2_ctrl *cptr,
8648c2ecf20Sopenharmony_ci			       const char *bufPtr,unsigned int bufSize,
8658c2ecf20Sopenharmony_ci			       int *mskp,int *valp)
8668c2ecf20Sopenharmony_ci{
8678c2ecf20Sopenharmony_ci	v4l2_std_id id;
8688c2ecf20Sopenharmony_ci	if (!pvr2_std_str_to_id(&id, bufPtr, bufSize))
8698c2ecf20Sopenharmony_ci		return -EINVAL;
8708c2ecf20Sopenharmony_ci	if (mskp) *mskp = id;
8718c2ecf20Sopenharmony_ci	if (valp) *valp = id;
8728c2ecf20Sopenharmony_ci	return 0;
8738c2ecf20Sopenharmony_ci}
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_cistatic int ctrl_stdcur_get(struct pvr2_ctrl *cptr,int *vp)
8768c2ecf20Sopenharmony_ci{
8778c2ecf20Sopenharmony_ci	*vp = cptr->hdw->std_mask_cur;
8788c2ecf20Sopenharmony_ci	return 0;
8798c2ecf20Sopenharmony_ci}
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_cistatic int ctrl_stdcur_set(struct pvr2_ctrl *cptr,int m,int v)
8828c2ecf20Sopenharmony_ci{
8838c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = cptr->hdw;
8848c2ecf20Sopenharmony_ci	v4l2_std_id ns;
8858c2ecf20Sopenharmony_ci	ns = hdw->std_mask_cur;
8868c2ecf20Sopenharmony_ci	ns = (ns & ~m) | (v & m);
8878c2ecf20Sopenharmony_ci	if (ns == hdw->std_mask_cur) return 0;
8888c2ecf20Sopenharmony_ci	hdw->std_mask_cur = ns;
8898c2ecf20Sopenharmony_ci	hdw->std_dirty = !0;
8908c2ecf20Sopenharmony_ci	return 0;
8918c2ecf20Sopenharmony_ci}
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_cistatic int ctrl_stdcur_is_dirty(struct pvr2_ctrl *cptr)
8948c2ecf20Sopenharmony_ci{
8958c2ecf20Sopenharmony_ci	return cptr->hdw->std_dirty != 0;
8968c2ecf20Sopenharmony_ci}
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_cistatic void ctrl_stdcur_clear_dirty(struct pvr2_ctrl *cptr)
8998c2ecf20Sopenharmony_ci{
9008c2ecf20Sopenharmony_ci	cptr->hdw->std_dirty = 0;
9018c2ecf20Sopenharmony_ci}
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_cistatic int ctrl_signal_get(struct pvr2_ctrl *cptr,int *vp)
9048c2ecf20Sopenharmony_ci{
9058c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = cptr->hdw;
9068c2ecf20Sopenharmony_ci	pvr2_hdw_status_poll(hdw);
9078c2ecf20Sopenharmony_ci	*vp = hdw->tuner_signal_info.signal;
9088c2ecf20Sopenharmony_ci	return 0;
9098c2ecf20Sopenharmony_ci}
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_cistatic int ctrl_audio_modes_present_get(struct pvr2_ctrl *cptr,int *vp)
9128c2ecf20Sopenharmony_ci{
9138c2ecf20Sopenharmony_ci	int val = 0;
9148c2ecf20Sopenharmony_ci	unsigned int subchan;
9158c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = cptr->hdw;
9168c2ecf20Sopenharmony_ci	pvr2_hdw_status_poll(hdw);
9178c2ecf20Sopenharmony_ci	subchan = hdw->tuner_signal_info.rxsubchans;
9188c2ecf20Sopenharmony_ci	if (subchan & V4L2_TUNER_SUB_MONO) {
9198c2ecf20Sopenharmony_ci		val |= (1 << V4L2_TUNER_MODE_MONO);
9208c2ecf20Sopenharmony_ci	}
9218c2ecf20Sopenharmony_ci	if (subchan & V4L2_TUNER_SUB_STEREO) {
9228c2ecf20Sopenharmony_ci		val |= (1 << V4L2_TUNER_MODE_STEREO);
9238c2ecf20Sopenharmony_ci	}
9248c2ecf20Sopenharmony_ci	if (subchan & V4L2_TUNER_SUB_LANG1) {
9258c2ecf20Sopenharmony_ci		val |= (1 << V4L2_TUNER_MODE_LANG1);
9268c2ecf20Sopenharmony_ci	}
9278c2ecf20Sopenharmony_ci	if (subchan & V4L2_TUNER_SUB_LANG2) {
9288c2ecf20Sopenharmony_ci		val |= (1 << V4L2_TUNER_MODE_LANG2);
9298c2ecf20Sopenharmony_ci	}
9308c2ecf20Sopenharmony_ci	*vp = val;
9318c2ecf20Sopenharmony_ci	return 0;
9328c2ecf20Sopenharmony_ci}
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci#define DEFINT(vmin,vmax) \
9368c2ecf20Sopenharmony_ci	.type = pvr2_ctl_int, \
9378c2ecf20Sopenharmony_ci	.def.type_int.min_value = vmin, \
9388c2ecf20Sopenharmony_ci	.def.type_int.max_value = vmax
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci#define DEFENUM(tab) \
9418c2ecf20Sopenharmony_ci	.type = pvr2_ctl_enum, \
9428c2ecf20Sopenharmony_ci	.def.type_enum.count = ARRAY_SIZE(tab), \
9438c2ecf20Sopenharmony_ci	.def.type_enum.value_names = tab
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci#define DEFBOOL \
9468c2ecf20Sopenharmony_ci	.type = pvr2_ctl_bool
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci#define DEFMASK(msk,tab) \
9498c2ecf20Sopenharmony_ci	.type = pvr2_ctl_bitmask, \
9508c2ecf20Sopenharmony_ci	.def.type_bitmask.valid_bits = msk, \
9518c2ecf20Sopenharmony_ci	.def.type_bitmask.bit_names = tab
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci#define DEFREF(vname) \
9548c2ecf20Sopenharmony_ci	.set_value = ctrl_set_##vname, \
9558c2ecf20Sopenharmony_ci	.get_value = ctrl_get_##vname, \
9568c2ecf20Sopenharmony_ci	.is_dirty = ctrl_isdirty_##vname, \
9578c2ecf20Sopenharmony_ci	.clear_dirty = ctrl_cleardirty_##vname
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci#define VCREATE_FUNCS(vname) \
9618c2ecf20Sopenharmony_cistatic int ctrl_get_##vname(struct pvr2_ctrl *cptr,int *vp) \
9628c2ecf20Sopenharmony_ci{*vp = cptr->hdw->vname##_val; return 0;} \
9638c2ecf20Sopenharmony_cistatic int ctrl_set_##vname(struct pvr2_ctrl *cptr,int m,int v) \
9648c2ecf20Sopenharmony_ci{cptr->hdw->vname##_val = v; cptr->hdw->vname##_dirty = !0; return 0;} \
9658c2ecf20Sopenharmony_cistatic int ctrl_isdirty_##vname(struct pvr2_ctrl *cptr) \
9668c2ecf20Sopenharmony_ci{return cptr->hdw->vname##_dirty != 0;} \
9678c2ecf20Sopenharmony_cistatic void ctrl_cleardirty_##vname(struct pvr2_ctrl *cptr) \
9688c2ecf20Sopenharmony_ci{cptr->hdw->vname##_dirty = 0;}
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ciVCREATE_FUNCS(brightness)
9718c2ecf20Sopenharmony_ciVCREATE_FUNCS(contrast)
9728c2ecf20Sopenharmony_ciVCREATE_FUNCS(saturation)
9738c2ecf20Sopenharmony_ciVCREATE_FUNCS(hue)
9748c2ecf20Sopenharmony_ciVCREATE_FUNCS(volume)
9758c2ecf20Sopenharmony_ciVCREATE_FUNCS(balance)
9768c2ecf20Sopenharmony_ciVCREATE_FUNCS(bass)
9778c2ecf20Sopenharmony_ciVCREATE_FUNCS(treble)
9788c2ecf20Sopenharmony_ciVCREATE_FUNCS(mute)
9798c2ecf20Sopenharmony_ciVCREATE_FUNCS(cropl)
9808c2ecf20Sopenharmony_ciVCREATE_FUNCS(cropt)
9818c2ecf20Sopenharmony_ciVCREATE_FUNCS(cropw)
9828c2ecf20Sopenharmony_ciVCREATE_FUNCS(croph)
9838c2ecf20Sopenharmony_ciVCREATE_FUNCS(audiomode)
9848c2ecf20Sopenharmony_ciVCREATE_FUNCS(res_hor)
9858c2ecf20Sopenharmony_ciVCREATE_FUNCS(res_ver)
9868c2ecf20Sopenharmony_ciVCREATE_FUNCS(srate)
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci/* Table definition of all controls which can be manipulated */
9898c2ecf20Sopenharmony_cistatic const struct pvr2_ctl_info control_defs[] = {
9908c2ecf20Sopenharmony_ci	{
9918c2ecf20Sopenharmony_ci		.v4l_id = V4L2_CID_BRIGHTNESS,
9928c2ecf20Sopenharmony_ci		.desc = "Brightness",
9938c2ecf20Sopenharmony_ci		.name = "brightness",
9948c2ecf20Sopenharmony_ci		.default_value = 128,
9958c2ecf20Sopenharmony_ci		DEFREF(brightness),
9968c2ecf20Sopenharmony_ci		DEFINT(0,255),
9978c2ecf20Sopenharmony_ci	},{
9988c2ecf20Sopenharmony_ci		.v4l_id = V4L2_CID_CONTRAST,
9998c2ecf20Sopenharmony_ci		.desc = "Contrast",
10008c2ecf20Sopenharmony_ci		.name = "contrast",
10018c2ecf20Sopenharmony_ci		.default_value = 68,
10028c2ecf20Sopenharmony_ci		DEFREF(contrast),
10038c2ecf20Sopenharmony_ci		DEFINT(0,127),
10048c2ecf20Sopenharmony_ci	},{
10058c2ecf20Sopenharmony_ci		.v4l_id = V4L2_CID_SATURATION,
10068c2ecf20Sopenharmony_ci		.desc = "Saturation",
10078c2ecf20Sopenharmony_ci		.name = "saturation",
10088c2ecf20Sopenharmony_ci		.default_value = 64,
10098c2ecf20Sopenharmony_ci		DEFREF(saturation),
10108c2ecf20Sopenharmony_ci		DEFINT(0,127),
10118c2ecf20Sopenharmony_ci	},{
10128c2ecf20Sopenharmony_ci		.v4l_id = V4L2_CID_HUE,
10138c2ecf20Sopenharmony_ci		.desc = "Hue",
10148c2ecf20Sopenharmony_ci		.name = "hue",
10158c2ecf20Sopenharmony_ci		.default_value = 0,
10168c2ecf20Sopenharmony_ci		DEFREF(hue),
10178c2ecf20Sopenharmony_ci		DEFINT(-128,127),
10188c2ecf20Sopenharmony_ci	},{
10198c2ecf20Sopenharmony_ci		.v4l_id = V4L2_CID_AUDIO_VOLUME,
10208c2ecf20Sopenharmony_ci		.desc = "Volume",
10218c2ecf20Sopenharmony_ci		.name = "volume",
10228c2ecf20Sopenharmony_ci		.default_value = 62000,
10238c2ecf20Sopenharmony_ci		DEFREF(volume),
10248c2ecf20Sopenharmony_ci		DEFINT(0,65535),
10258c2ecf20Sopenharmony_ci	},{
10268c2ecf20Sopenharmony_ci		.v4l_id = V4L2_CID_AUDIO_BALANCE,
10278c2ecf20Sopenharmony_ci		.desc = "Balance",
10288c2ecf20Sopenharmony_ci		.name = "balance",
10298c2ecf20Sopenharmony_ci		.default_value = 0,
10308c2ecf20Sopenharmony_ci		DEFREF(balance),
10318c2ecf20Sopenharmony_ci		DEFINT(-32768,32767),
10328c2ecf20Sopenharmony_ci	},{
10338c2ecf20Sopenharmony_ci		.v4l_id = V4L2_CID_AUDIO_BASS,
10348c2ecf20Sopenharmony_ci		.desc = "Bass",
10358c2ecf20Sopenharmony_ci		.name = "bass",
10368c2ecf20Sopenharmony_ci		.default_value = 0,
10378c2ecf20Sopenharmony_ci		DEFREF(bass),
10388c2ecf20Sopenharmony_ci		DEFINT(-32768,32767),
10398c2ecf20Sopenharmony_ci	},{
10408c2ecf20Sopenharmony_ci		.v4l_id = V4L2_CID_AUDIO_TREBLE,
10418c2ecf20Sopenharmony_ci		.desc = "Treble",
10428c2ecf20Sopenharmony_ci		.name = "treble",
10438c2ecf20Sopenharmony_ci		.default_value = 0,
10448c2ecf20Sopenharmony_ci		DEFREF(treble),
10458c2ecf20Sopenharmony_ci		DEFINT(-32768,32767),
10468c2ecf20Sopenharmony_ci	},{
10478c2ecf20Sopenharmony_ci		.v4l_id = V4L2_CID_AUDIO_MUTE,
10488c2ecf20Sopenharmony_ci		.desc = "Mute",
10498c2ecf20Sopenharmony_ci		.name = "mute",
10508c2ecf20Sopenharmony_ci		.default_value = 0,
10518c2ecf20Sopenharmony_ci		DEFREF(mute),
10528c2ecf20Sopenharmony_ci		DEFBOOL,
10538c2ecf20Sopenharmony_ci	}, {
10548c2ecf20Sopenharmony_ci		.desc = "Capture crop left margin",
10558c2ecf20Sopenharmony_ci		.name = "crop_left",
10568c2ecf20Sopenharmony_ci		.internal_id = PVR2_CID_CROPL,
10578c2ecf20Sopenharmony_ci		.default_value = 0,
10588c2ecf20Sopenharmony_ci		DEFREF(cropl),
10598c2ecf20Sopenharmony_ci		DEFINT(-129, 340),
10608c2ecf20Sopenharmony_ci		.get_min_value = ctrl_cropl_min_get,
10618c2ecf20Sopenharmony_ci		.get_max_value = ctrl_cropl_max_get,
10628c2ecf20Sopenharmony_ci		.get_def_value = ctrl_get_cropcapdl,
10638c2ecf20Sopenharmony_ci	}, {
10648c2ecf20Sopenharmony_ci		.desc = "Capture crop top margin",
10658c2ecf20Sopenharmony_ci		.name = "crop_top",
10668c2ecf20Sopenharmony_ci		.internal_id = PVR2_CID_CROPT,
10678c2ecf20Sopenharmony_ci		.default_value = 0,
10688c2ecf20Sopenharmony_ci		DEFREF(cropt),
10698c2ecf20Sopenharmony_ci		DEFINT(-35, 544),
10708c2ecf20Sopenharmony_ci		.get_min_value = ctrl_cropt_min_get,
10718c2ecf20Sopenharmony_ci		.get_max_value = ctrl_cropt_max_get,
10728c2ecf20Sopenharmony_ci		.get_def_value = ctrl_get_cropcapdt,
10738c2ecf20Sopenharmony_ci	}, {
10748c2ecf20Sopenharmony_ci		.desc = "Capture crop width",
10758c2ecf20Sopenharmony_ci		.name = "crop_width",
10768c2ecf20Sopenharmony_ci		.internal_id = PVR2_CID_CROPW,
10778c2ecf20Sopenharmony_ci		.default_value = 720,
10788c2ecf20Sopenharmony_ci		DEFREF(cropw),
10798c2ecf20Sopenharmony_ci		DEFINT(0, 864),
10808c2ecf20Sopenharmony_ci		.get_max_value = ctrl_cropw_max_get,
10818c2ecf20Sopenharmony_ci		.get_def_value = ctrl_get_cropcapdw,
10828c2ecf20Sopenharmony_ci	}, {
10838c2ecf20Sopenharmony_ci		.desc = "Capture crop height",
10848c2ecf20Sopenharmony_ci		.name = "crop_height",
10858c2ecf20Sopenharmony_ci		.internal_id = PVR2_CID_CROPH,
10868c2ecf20Sopenharmony_ci		.default_value = 480,
10878c2ecf20Sopenharmony_ci		DEFREF(croph),
10888c2ecf20Sopenharmony_ci		DEFINT(0, 576),
10898c2ecf20Sopenharmony_ci		.get_max_value = ctrl_croph_max_get,
10908c2ecf20Sopenharmony_ci		.get_def_value = ctrl_get_cropcapdh,
10918c2ecf20Sopenharmony_ci	}, {
10928c2ecf20Sopenharmony_ci		.desc = "Capture capability pixel aspect numerator",
10938c2ecf20Sopenharmony_ci		.name = "cropcap_pixel_numerator",
10948c2ecf20Sopenharmony_ci		.internal_id = PVR2_CID_CROPCAPPAN,
10958c2ecf20Sopenharmony_ci		.get_value = ctrl_get_cropcappan,
10968c2ecf20Sopenharmony_ci	}, {
10978c2ecf20Sopenharmony_ci		.desc = "Capture capability pixel aspect denominator",
10988c2ecf20Sopenharmony_ci		.name = "cropcap_pixel_denominator",
10998c2ecf20Sopenharmony_ci		.internal_id = PVR2_CID_CROPCAPPAD,
11008c2ecf20Sopenharmony_ci		.get_value = ctrl_get_cropcappad,
11018c2ecf20Sopenharmony_ci	}, {
11028c2ecf20Sopenharmony_ci		.desc = "Capture capability bounds top",
11038c2ecf20Sopenharmony_ci		.name = "cropcap_bounds_top",
11048c2ecf20Sopenharmony_ci		.internal_id = PVR2_CID_CROPCAPBT,
11058c2ecf20Sopenharmony_ci		.get_value = ctrl_get_cropcapbt,
11068c2ecf20Sopenharmony_ci	}, {
11078c2ecf20Sopenharmony_ci		.desc = "Capture capability bounds left",
11088c2ecf20Sopenharmony_ci		.name = "cropcap_bounds_left",
11098c2ecf20Sopenharmony_ci		.internal_id = PVR2_CID_CROPCAPBL,
11108c2ecf20Sopenharmony_ci		.get_value = ctrl_get_cropcapbl,
11118c2ecf20Sopenharmony_ci	}, {
11128c2ecf20Sopenharmony_ci		.desc = "Capture capability bounds width",
11138c2ecf20Sopenharmony_ci		.name = "cropcap_bounds_width",
11148c2ecf20Sopenharmony_ci		.internal_id = PVR2_CID_CROPCAPBW,
11158c2ecf20Sopenharmony_ci		.get_value = ctrl_get_cropcapbw,
11168c2ecf20Sopenharmony_ci	}, {
11178c2ecf20Sopenharmony_ci		.desc = "Capture capability bounds height",
11188c2ecf20Sopenharmony_ci		.name = "cropcap_bounds_height",
11198c2ecf20Sopenharmony_ci		.internal_id = PVR2_CID_CROPCAPBH,
11208c2ecf20Sopenharmony_ci		.get_value = ctrl_get_cropcapbh,
11218c2ecf20Sopenharmony_ci	},{
11228c2ecf20Sopenharmony_ci		.desc = "Video Source",
11238c2ecf20Sopenharmony_ci		.name = "input",
11248c2ecf20Sopenharmony_ci		.internal_id = PVR2_CID_INPUT,
11258c2ecf20Sopenharmony_ci		.default_value = PVR2_CVAL_INPUT_TV,
11268c2ecf20Sopenharmony_ci		.check_value = ctrl_check_input,
11278c2ecf20Sopenharmony_ci		DEFREF(input),
11288c2ecf20Sopenharmony_ci		DEFENUM(control_values_input),
11298c2ecf20Sopenharmony_ci	},{
11308c2ecf20Sopenharmony_ci		.desc = "Audio Mode",
11318c2ecf20Sopenharmony_ci		.name = "audio_mode",
11328c2ecf20Sopenharmony_ci		.internal_id = PVR2_CID_AUDIOMODE,
11338c2ecf20Sopenharmony_ci		.default_value = V4L2_TUNER_MODE_STEREO,
11348c2ecf20Sopenharmony_ci		DEFREF(audiomode),
11358c2ecf20Sopenharmony_ci		DEFENUM(control_values_audiomode),
11368c2ecf20Sopenharmony_ci	},{
11378c2ecf20Sopenharmony_ci		.desc = "Horizontal capture resolution",
11388c2ecf20Sopenharmony_ci		.name = "resolution_hor",
11398c2ecf20Sopenharmony_ci		.internal_id = PVR2_CID_HRES,
11408c2ecf20Sopenharmony_ci		.default_value = 720,
11418c2ecf20Sopenharmony_ci		DEFREF(res_hor),
11428c2ecf20Sopenharmony_ci		DEFINT(19,720),
11438c2ecf20Sopenharmony_ci	},{
11448c2ecf20Sopenharmony_ci		.desc = "Vertical capture resolution",
11458c2ecf20Sopenharmony_ci		.name = "resolution_ver",
11468c2ecf20Sopenharmony_ci		.internal_id = PVR2_CID_VRES,
11478c2ecf20Sopenharmony_ci		.default_value = 480,
11488c2ecf20Sopenharmony_ci		DEFREF(res_ver),
11498c2ecf20Sopenharmony_ci		DEFINT(17,576),
11508c2ecf20Sopenharmony_ci		/* Hook in check for video standard and adjust maximum
11518c2ecf20Sopenharmony_ci		   depending on the standard. */
11528c2ecf20Sopenharmony_ci		.get_max_value = ctrl_vres_max_get,
11538c2ecf20Sopenharmony_ci		.get_min_value = ctrl_vres_min_get,
11548c2ecf20Sopenharmony_ci	},{
11558c2ecf20Sopenharmony_ci		.v4l_id = V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ,
11568c2ecf20Sopenharmony_ci		.default_value = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000,
11578c2ecf20Sopenharmony_ci		.desc = "Audio Sampling Frequency",
11588c2ecf20Sopenharmony_ci		.name = "srate",
11598c2ecf20Sopenharmony_ci		DEFREF(srate),
11608c2ecf20Sopenharmony_ci		DEFENUM(control_values_srate),
11618c2ecf20Sopenharmony_ci	},{
11628c2ecf20Sopenharmony_ci		.desc = "Tuner Frequency (Hz)",
11638c2ecf20Sopenharmony_ci		.name = "frequency",
11648c2ecf20Sopenharmony_ci		.internal_id = PVR2_CID_FREQUENCY,
11658c2ecf20Sopenharmony_ci		.default_value = 0,
11668c2ecf20Sopenharmony_ci		.set_value = ctrl_freq_set,
11678c2ecf20Sopenharmony_ci		.get_value = ctrl_freq_get,
11688c2ecf20Sopenharmony_ci		.is_dirty = ctrl_freq_is_dirty,
11698c2ecf20Sopenharmony_ci		.clear_dirty = ctrl_freq_clear_dirty,
11708c2ecf20Sopenharmony_ci		DEFINT(0,0),
11718c2ecf20Sopenharmony_ci		/* Hook in check for input value (tv/radio) and adjust
11728c2ecf20Sopenharmony_ci		   max/min values accordingly */
11738c2ecf20Sopenharmony_ci		.get_max_value = ctrl_freq_max_get,
11748c2ecf20Sopenharmony_ci		.get_min_value = ctrl_freq_min_get,
11758c2ecf20Sopenharmony_ci	},{
11768c2ecf20Sopenharmony_ci		.desc = "Channel",
11778c2ecf20Sopenharmony_ci		.name = "channel",
11788c2ecf20Sopenharmony_ci		.set_value = ctrl_channel_set,
11798c2ecf20Sopenharmony_ci		.get_value = ctrl_channel_get,
11808c2ecf20Sopenharmony_ci		DEFINT(0,FREQTABLE_SIZE),
11818c2ecf20Sopenharmony_ci	},{
11828c2ecf20Sopenharmony_ci		.desc = "Channel Program Frequency",
11838c2ecf20Sopenharmony_ci		.name = "freq_table_value",
11848c2ecf20Sopenharmony_ci		.set_value = ctrl_channelfreq_set,
11858c2ecf20Sopenharmony_ci		.get_value = ctrl_channelfreq_get,
11868c2ecf20Sopenharmony_ci		DEFINT(0,0),
11878c2ecf20Sopenharmony_ci		/* Hook in check for input value (tv/radio) and adjust
11888c2ecf20Sopenharmony_ci		   max/min values accordingly */
11898c2ecf20Sopenharmony_ci		.get_max_value = ctrl_freq_max_get,
11908c2ecf20Sopenharmony_ci		.get_min_value = ctrl_freq_min_get,
11918c2ecf20Sopenharmony_ci	},{
11928c2ecf20Sopenharmony_ci		.desc = "Channel Program ID",
11938c2ecf20Sopenharmony_ci		.name = "freq_table_channel",
11948c2ecf20Sopenharmony_ci		.set_value = ctrl_channelprog_set,
11958c2ecf20Sopenharmony_ci		.get_value = ctrl_channelprog_get,
11968c2ecf20Sopenharmony_ci		DEFINT(0,FREQTABLE_SIZE),
11978c2ecf20Sopenharmony_ci	},{
11988c2ecf20Sopenharmony_ci		.desc = "Streaming Enabled",
11998c2ecf20Sopenharmony_ci		.name = "streaming_enabled",
12008c2ecf20Sopenharmony_ci		.get_value = ctrl_streamingenabled_get,
12018c2ecf20Sopenharmony_ci		DEFBOOL,
12028c2ecf20Sopenharmony_ci	},{
12038c2ecf20Sopenharmony_ci		.desc = "USB Speed",
12048c2ecf20Sopenharmony_ci		.name = "usb_speed",
12058c2ecf20Sopenharmony_ci		.get_value = ctrl_hsm_get,
12068c2ecf20Sopenharmony_ci		DEFENUM(control_values_hsm),
12078c2ecf20Sopenharmony_ci	},{
12088c2ecf20Sopenharmony_ci		.desc = "Master State",
12098c2ecf20Sopenharmony_ci		.name = "master_state",
12108c2ecf20Sopenharmony_ci		.get_value = ctrl_masterstate_get,
12118c2ecf20Sopenharmony_ci		DEFENUM(pvr2_state_names),
12128c2ecf20Sopenharmony_ci	},{
12138c2ecf20Sopenharmony_ci		.desc = "Signal Present",
12148c2ecf20Sopenharmony_ci		.name = "signal_present",
12158c2ecf20Sopenharmony_ci		.get_value = ctrl_signal_get,
12168c2ecf20Sopenharmony_ci		DEFINT(0,65535),
12178c2ecf20Sopenharmony_ci	},{
12188c2ecf20Sopenharmony_ci		.desc = "Audio Modes Present",
12198c2ecf20Sopenharmony_ci		.name = "audio_modes_present",
12208c2ecf20Sopenharmony_ci		.get_value = ctrl_audio_modes_present_get,
12218c2ecf20Sopenharmony_ci		/* For this type we "borrow" the V4L2_TUNER_MODE enum from
12228c2ecf20Sopenharmony_ci		   v4l.  Nothing outside of this module cares about this,
12238c2ecf20Sopenharmony_ci		   but I reuse it in order to also reuse the
12248c2ecf20Sopenharmony_ci		   control_values_audiomode string table. */
12258c2ecf20Sopenharmony_ci		DEFMASK(((1 << V4L2_TUNER_MODE_MONO)|
12268c2ecf20Sopenharmony_ci			 (1 << V4L2_TUNER_MODE_STEREO)|
12278c2ecf20Sopenharmony_ci			 (1 << V4L2_TUNER_MODE_LANG1)|
12288c2ecf20Sopenharmony_ci			 (1 << V4L2_TUNER_MODE_LANG2)),
12298c2ecf20Sopenharmony_ci			control_values_audiomode),
12308c2ecf20Sopenharmony_ci	},{
12318c2ecf20Sopenharmony_ci		.desc = "Video Standards Available Mask",
12328c2ecf20Sopenharmony_ci		.name = "video_standard_mask_available",
12338c2ecf20Sopenharmony_ci		.internal_id = PVR2_CID_STDAVAIL,
12348c2ecf20Sopenharmony_ci		.skip_init = !0,
12358c2ecf20Sopenharmony_ci		.get_value = ctrl_stdavail_get,
12368c2ecf20Sopenharmony_ci		.set_value = ctrl_stdavail_set,
12378c2ecf20Sopenharmony_ci		.val_to_sym = ctrl_std_val_to_sym,
12388c2ecf20Sopenharmony_ci		.sym_to_val = ctrl_std_sym_to_val,
12398c2ecf20Sopenharmony_ci		.type = pvr2_ctl_bitmask,
12408c2ecf20Sopenharmony_ci	},{
12418c2ecf20Sopenharmony_ci		.desc = "Video Standards In Use Mask",
12428c2ecf20Sopenharmony_ci		.name = "video_standard_mask_active",
12438c2ecf20Sopenharmony_ci		.internal_id = PVR2_CID_STDCUR,
12448c2ecf20Sopenharmony_ci		.skip_init = !0,
12458c2ecf20Sopenharmony_ci		.get_value = ctrl_stdcur_get,
12468c2ecf20Sopenharmony_ci		.set_value = ctrl_stdcur_set,
12478c2ecf20Sopenharmony_ci		.is_dirty = ctrl_stdcur_is_dirty,
12488c2ecf20Sopenharmony_ci		.clear_dirty = ctrl_stdcur_clear_dirty,
12498c2ecf20Sopenharmony_ci		.val_to_sym = ctrl_std_val_to_sym,
12508c2ecf20Sopenharmony_ci		.sym_to_val = ctrl_std_sym_to_val,
12518c2ecf20Sopenharmony_ci		.type = pvr2_ctl_bitmask,
12528c2ecf20Sopenharmony_ci	},{
12538c2ecf20Sopenharmony_ci		.desc = "Video Standards Detected Mask",
12548c2ecf20Sopenharmony_ci		.name = "video_standard_mask_detected",
12558c2ecf20Sopenharmony_ci		.internal_id = PVR2_CID_STDDETECT,
12568c2ecf20Sopenharmony_ci		.skip_init = !0,
12578c2ecf20Sopenharmony_ci		.get_value = ctrl_stddetect_get,
12588c2ecf20Sopenharmony_ci		.val_to_sym = ctrl_std_val_to_sym,
12598c2ecf20Sopenharmony_ci		.sym_to_val = ctrl_std_sym_to_val,
12608c2ecf20Sopenharmony_ci		.type = pvr2_ctl_bitmask,
12618c2ecf20Sopenharmony_ci	}
12628c2ecf20Sopenharmony_ci};
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci#define CTRLDEF_COUNT ARRAY_SIZE(control_defs)
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci
12678c2ecf20Sopenharmony_ciconst char *pvr2_config_get_name(enum pvr2_config cfg)
12688c2ecf20Sopenharmony_ci{
12698c2ecf20Sopenharmony_ci	switch (cfg) {
12708c2ecf20Sopenharmony_ci	case pvr2_config_empty: return "empty";
12718c2ecf20Sopenharmony_ci	case pvr2_config_mpeg: return "mpeg";
12728c2ecf20Sopenharmony_ci	case pvr2_config_vbi: return "vbi";
12738c2ecf20Sopenharmony_ci	case pvr2_config_pcm: return "pcm";
12748c2ecf20Sopenharmony_ci	case pvr2_config_rawvideo: return "raw video";
12758c2ecf20Sopenharmony_ci	}
12768c2ecf20Sopenharmony_ci	return "<unknown>";
12778c2ecf20Sopenharmony_ci}
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_cistruct usb_device *pvr2_hdw_get_dev(struct pvr2_hdw *hdw)
12818c2ecf20Sopenharmony_ci{
12828c2ecf20Sopenharmony_ci	return hdw->usb_dev;
12838c2ecf20Sopenharmony_ci}
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ciunsigned long pvr2_hdw_get_sn(struct pvr2_hdw *hdw)
12878c2ecf20Sopenharmony_ci{
12888c2ecf20Sopenharmony_ci	return hdw->serial_number;
12898c2ecf20Sopenharmony_ci}
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_ciconst char *pvr2_hdw_get_bus_info(struct pvr2_hdw *hdw)
12938c2ecf20Sopenharmony_ci{
12948c2ecf20Sopenharmony_ci	return hdw->bus_info;
12958c2ecf20Sopenharmony_ci}
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_ci
12988c2ecf20Sopenharmony_ciconst char *pvr2_hdw_get_device_identifier(struct pvr2_hdw *hdw)
12998c2ecf20Sopenharmony_ci{
13008c2ecf20Sopenharmony_ci	return hdw->identifier;
13018c2ecf20Sopenharmony_ci}
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_ciunsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *hdw)
13058c2ecf20Sopenharmony_ci{
13068c2ecf20Sopenharmony_ci	return hdw->freqSelector ? hdw->freqValTelevision : hdw->freqValRadio;
13078c2ecf20Sopenharmony_ci}
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci/* Set the currently tuned frequency and account for all possible
13108c2ecf20Sopenharmony_ci   driver-core side effects of this action. */
13118c2ecf20Sopenharmony_cistatic void pvr2_hdw_set_cur_freq(struct pvr2_hdw *hdw,unsigned long val)
13128c2ecf20Sopenharmony_ci{
13138c2ecf20Sopenharmony_ci	if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
13148c2ecf20Sopenharmony_ci		if (hdw->freqSelector) {
13158c2ecf20Sopenharmony_ci			/* Swing over to radio frequency selection */
13168c2ecf20Sopenharmony_ci			hdw->freqSelector = 0;
13178c2ecf20Sopenharmony_ci			hdw->freqDirty = !0;
13188c2ecf20Sopenharmony_ci		}
13198c2ecf20Sopenharmony_ci		if (hdw->freqValRadio != val) {
13208c2ecf20Sopenharmony_ci			hdw->freqValRadio = val;
13218c2ecf20Sopenharmony_ci			hdw->freqSlotRadio = 0;
13228c2ecf20Sopenharmony_ci			hdw->freqDirty = !0;
13238c2ecf20Sopenharmony_ci		}
13248c2ecf20Sopenharmony_ci	} else {
13258c2ecf20Sopenharmony_ci		if (!(hdw->freqSelector)) {
13268c2ecf20Sopenharmony_ci			/* Swing over to television frequency selection */
13278c2ecf20Sopenharmony_ci			hdw->freqSelector = 1;
13288c2ecf20Sopenharmony_ci			hdw->freqDirty = !0;
13298c2ecf20Sopenharmony_ci		}
13308c2ecf20Sopenharmony_ci		if (hdw->freqValTelevision != val) {
13318c2ecf20Sopenharmony_ci			hdw->freqValTelevision = val;
13328c2ecf20Sopenharmony_ci			hdw->freqSlotTelevision = 0;
13338c2ecf20Sopenharmony_ci			hdw->freqDirty = !0;
13348c2ecf20Sopenharmony_ci		}
13358c2ecf20Sopenharmony_ci	}
13368c2ecf20Sopenharmony_ci}
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ciint pvr2_hdw_get_unit_number(struct pvr2_hdw *hdw)
13398c2ecf20Sopenharmony_ci{
13408c2ecf20Sopenharmony_ci	return hdw->unit_number;
13418c2ecf20Sopenharmony_ci}
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_ci/* Attempt to locate one of the given set of files.  Messages are logged
13458c2ecf20Sopenharmony_ci   appropriate to what has been found.  The return value will be 0 or
13468c2ecf20Sopenharmony_ci   greater on success (it will be the index of the file name found) and
13478c2ecf20Sopenharmony_ci   fw_entry will be filled in.  Otherwise a negative error is returned on
13488c2ecf20Sopenharmony_ci   failure.  If the return value is -ENOENT then no viable firmware file
13498c2ecf20Sopenharmony_ci   could be located. */
13508c2ecf20Sopenharmony_cistatic int pvr2_locate_firmware(struct pvr2_hdw *hdw,
13518c2ecf20Sopenharmony_ci				const struct firmware **fw_entry,
13528c2ecf20Sopenharmony_ci				const char *fwtypename,
13538c2ecf20Sopenharmony_ci				unsigned int fwcount,
13548c2ecf20Sopenharmony_ci				const char *fwnames[])
13558c2ecf20Sopenharmony_ci{
13568c2ecf20Sopenharmony_ci	unsigned int idx;
13578c2ecf20Sopenharmony_ci	int ret = -EINVAL;
13588c2ecf20Sopenharmony_ci	for (idx = 0; idx < fwcount; idx++) {
13598c2ecf20Sopenharmony_ci		ret = request_firmware(fw_entry,
13608c2ecf20Sopenharmony_ci				       fwnames[idx],
13618c2ecf20Sopenharmony_ci				       &hdw->usb_dev->dev);
13628c2ecf20Sopenharmony_ci		if (!ret) {
13638c2ecf20Sopenharmony_ci			trace_firmware("Located %s firmware: %s; uploading...",
13648c2ecf20Sopenharmony_ci				       fwtypename,
13658c2ecf20Sopenharmony_ci				       fwnames[idx]);
13668c2ecf20Sopenharmony_ci			return idx;
13678c2ecf20Sopenharmony_ci		}
13688c2ecf20Sopenharmony_ci		if (ret == -ENOENT) continue;
13698c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
13708c2ecf20Sopenharmony_ci			   "request_firmware fatal error with code=%d",ret);
13718c2ecf20Sopenharmony_ci		return ret;
13728c2ecf20Sopenharmony_ci	}
13738c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_ERROR_LEGS,
13748c2ecf20Sopenharmony_ci		   "***WARNING*** Device %s firmware seems to be missing.",
13758c2ecf20Sopenharmony_ci		   fwtypename);
13768c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_ERROR_LEGS,
13778c2ecf20Sopenharmony_ci		   "Did you install the pvrusb2 firmware files in their proper location?");
13788c2ecf20Sopenharmony_ci	if (fwcount == 1) {
13798c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
13808c2ecf20Sopenharmony_ci			   "request_firmware unable to locate %s file %s",
13818c2ecf20Sopenharmony_ci			   fwtypename,fwnames[0]);
13828c2ecf20Sopenharmony_ci	} else {
13838c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
13848c2ecf20Sopenharmony_ci			   "request_firmware unable to locate one of the following %s files:",
13858c2ecf20Sopenharmony_ci			   fwtypename);
13868c2ecf20Sopenharmony_ci		for (idx = 0; idx < fwcount; idx++) {
13878c2ecf20Sopenharmony_ci			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
13888c2ecf20Sopenharmony_ci				   "request_firmware: Failed to find %s",
13898c2ecf20Sopenharmony_ci				   fwnames[idx]);
13908c2ecf20Sopenharmony_ci		}
13918c2ecf20Sopenharmony_ci	}
13928c2ecf20Sopenharmony_ci	return ret;
13938c2ecf20Sopenharmony_ci}
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci/*
13978c2ecf20Sopenharmony_ci * pvr2_upload_firmware1().
13988c2ecf20Sopenharmony_ci *
13998c2ecf20Sopenharmony_ci * Send the 8051 firmware to the device.  After the upload, arrange for
14008c2ecf20Sopenharmony_ci * device to re-enumerate.
14018c2ecf20Sopenharmony_ci *
14028c2ecf20Sopenharmony_ci * NOTE : the pointer to the firmware data given by request_firmware()
14038c2ecf20Sopenharmony_ci * is not suitable for an usb transaction.
14048c2ecf20Sopenharmony_ci *
14058c2ecf20Sopenharmony_ci */
14068c2ecf20Sopenharmony_cistatic int pvr2_upload_firmware1(struct pvr2_hdw *hdw)
14078c2ecf20Sopenharmony_ci{
14088c2ecf20Sopenharmony_ci	const struct firmware *fw_entry = NULL;
14098c2ecf20Sopenharmony_ci	void  *fw_ptr;
14108c2ecf20Sopenharmony_ci	unsigned int pipe;
14118c2ecf20Sopenharmony_ci	unsigned int fwsize;
14128c2ecf20Sopenharmony_ci	int ret;
14138c2ecf20Sopenharmony_ci	u16 address;
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_ci	if (!hdw->hdw_desc->fx2_firmware.cnt) {
14168c2ecf20Sopenharmony_ci		hdw->fw1_state = FW1_STATE_OK;
14178c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
14188c2ecf20Sopenharmony_ci			   "Connected device type defines no firmware to upload; ignoring firmware");
14198c2ecf20Sopenharmony_ci		return -ENOTTY;
14208c2ecf20Sopenharmony_ci	}
14218c2ecf20Sopenharmony_ci
14228c2ecf20Sopenharmony_ci	hdw->fw1_state = FW1_STATE_FAILED; // default result
14238c2ecf20Sopenharmony_ci
14248c2ecf20Sopenharmony_ci	trace_firmware("pvr2_upload_firmware1");
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_ci	ret = pvr2_locate_firmware(hdw,&fw_entry,"fx2 controller",
14278c2ecf20Sopenharmony_ci				   hdw->hdw_desc->fx2_firmware.cnt,
14288c2ecf20Sopenharmony_ci				   hdw->hdw_desc->fx2_firmware.lst);
14298c2ecf20Sopenharmony_ci	if (ret < 0) {
14308c2ecf20Sopenharmony_ci		if (ret == -ENOENT) hdw->fw1_state = FW1_STATE_MISSING;
14318c2ecf20Sopenharmony_ci		return ret;
14328c2ecf20Sopenharmony_ci	}
14338c2ecf20Sopenharmony_ci
14348c2ecf20Sopenharmony_ci	usb_clear_halt(hdw->usb_dev, usb_sndbulkpipe(hdw->usb_dev, 0 & 0x7f));
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci	pipe = usb_sndctrlpipe(hdw->usb_dev, 0);
14378c2ecf20Sopenharmony_ci	fwsize = fw_entry->size;
14388c2ecf20Sopenharmony_ci
14398c2ecf20Sopenharmony_ci	if ((fwsize != 0x2000) &&
14408c2ecf20Sopenharmony_ci	    (!(hdw->hdw_desc->flag_fx2_16kb && (fwsize == 0x4000)))) {
14418c2ecf20Sopenharmony_ci		if (hdw->hdw_desc->flag_fx2_16kb) {
14428c2ecf20Sopenharmony_ci			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
14438c2ecf20Sopenharmony_ci				   "Wrong fx2 firmware size (expected 8192 or 16384, got %u)",
14448c2ecf20Sopenharmony_ci				   fwsize);
14458c2ecf20Sopenharmony_ci		} else {
14468c2ecf20Sopenharmony_ci			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
14478c2ecf20Sopenharmony_ci				   "Wrong fx2 firmware size (expected 8192, got %u)",
14488c2ecf20Sopenharmony_ci				   fwsize);
14498c2ecf20Sopenharmony_ci		}
14508c2ecf20Sopenharmony_ci		release_firmware(fw_entry);
14518c2ecf20Sopenharmony_ci		return -ENOMEM;
14528c2ecf20Sopenharmony_ci	}
14538c2ecf20Sopenharmony_ci
14548c2ecf20Sopenharmony_ci	fw_ptr = kmalloc(0x800, GFP_KERNEL);
14558c2ecf20Sopenharmony_ci	if (fw_ptr == NULL){
14568c2ecf20Sopenharmony_ci		release_firmware(fw_entry);
14578c2ecf20Sopenharmony_ci		return -ENOMEM;
14588c2ecf20Sopenharmony_ci	}
14598c2ecf20Sopenharmony_ci
14608c2ecf20Sopenharmony_ci	/* We have to hold the CPU during firmware upload. */
14618c2ecf20Sopenharmony_ci	pvr2_hdw_cpureset_assert(hdw,1);
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci	/* upload the firmware to address 0000-1fff in 2048 (=0x800) bytes
14648c2ecf20Sopenharmony_ci	   chunk. */
14658c2ecf20Sopenharmony_ci
14668c2ecf20Sopenharmony_ci	ret = 0;
14678c2ecf20Sopenharmony_ci	for (address = 0; address < fwsize; address += 0x800) {
14688c2ecf20Sopenharmony_ci		memcpy(fw_ptr, fw_entry->data + address, 0x800);
14698c2ecf20Sopenharmony_ci		ret += usb_control_msg(hdw->usb_dev, pipe, 0xa0, 0x40, address,
14708c2ecf20Sopenharmony_ci				       0, fw_ptr, 0x800, 1000);
14718c2ecf20Sopenharmony_ci	}
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ci	trace_firmware("Upload done, releasing device's CPU");
14748c2ecf20Sopenharmony_ci
14758c2ecf20Sopenharmony_ci	/* Now release the CPU.  It will disconnect and reconnect later. */
14768c2ecf20Sopenharmony_ci	pvr2_hdw_cpureset_assert(hdw,0);
14778c2ecf20Sopenharmony_ci
14788c2ecf20Sopenharmony_ci	kfree(fw_ptr);
14798c2ecf20Sopenharmony_ci	release_firmware(fw_entry);
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_ci	trace_firmware("Upload done (%d bytes sent)",ret);
14828c2ecf20Sopenharmony_ci
14838c2ecf20Sopenharmony_ci	/* We should have written fwsize bytes */
14848c2ecf20Sopenharmony_ci	if (ret == fwsize) {
14858c2ecf20Sopenharmony_ci		hdw->fw1_state = FW1_STATE_RELOAD;
14868c2ecf20Sopenharmony_ci		return 0;
14878c2ecf20Sopenharmony_ci	}
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ci	return -EIO;
14908c2ecf20Sopenharmony_ci}
14918c2ecf20Sopenharmony_ci
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci/*
14948c2ecf20Sopenharmony_ci * pvr2_upload_firmware2()
14958c2ecf20Sopenharmony_ci *
14968c2ecf20Sopenharmony_ci * This uploads encoder firmware on endpoint 2.
14978c2ecf20Sopenharmony_ci *
14988c2ecf20Sopenharmony_ci */
14998c2ecf20Sopenharmony_ci
15008c2ecf20Sopenharmony_ciint pvr2_upload_firmware2(struct pvr2_hdw *hdw)
15018c2ecf20Sopenharmony_ci{
15028c2ecf20Sopenharmony_ci	const struct firmware *fw_entry = NULL;
15038c2ecf20Sopenharmony_ci	void  *fw_ptr;
15048c2ecf20Sopenharmony_ci	unsigned int pipe, fw_len, fw_done, bcnt, icnt;
15058c2ecf20Sopenharmony_ci	int actual_length;
15068c2ecf20Sopenharmony_ci	int ret = 0;
15078c2ecf20Sopenharmony_ci	int fwidx;
15088c2ecf20Sopenharmony_ci	static const char *fw_files[] = {
15098c2ecf20Sopenharmony_ci		CX2341X_FIRM_ENC_FILENAME,
15108c2ecf20Sopenharmony_ci	};
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci	if (hdw->hdw_desc->flag_skip_cx23416_firmware) {
15138c2ecf20Sopenharmony_ci		return 0;
15148c2ecf20Sopenharmony_ci	}
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_ci	trace_firmware("pvr2_upload_firmware2");
15178c2ecf20Sopenharmony_ci
15188c2ecf20Sopenharmony_ci	ret = pvr2_locate_firmware(hdw,&fw_entry,"encoder",
15198c2ecf20Sopenharmony_ci				   ARRAY_SIZE(fw_files), fw_files);
15208c2ecf20Sopenharmony_ci	if (ret < 0) return ret;
15218c2ecf20Sopenharmony_ci	fwidx = ret;
15228c2ecf20Sopenharmony_ci	ret = 0;
15238c2ecf20Sopenharmony_ci	/* Since we're about to completely reinitialize the encoder,
15248c2ecf20Sopenharmony_ci	   invalidate our cached copy of its configuration state.  Next
15258c2ecf20Sopenharmony_ci	   time we configure the encoder, then we'll fully configure it. */
15268c2ecf20Sopenharmony_ci	hdw->enc_cur_valid = 0;
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_ci	/* Encoder is about to be reset so note that as far as we're
15298c2ecf20Sopenharmony_ci	   concerned now, the encoder has never been run. */
15308c2ecf20Sopenharmony_ci	del_timer_sync(&hdw->encoder_run_timer);
15318c2ecf20Sopenharmony_ci	if (hdw->state_encoder_runok) {
15328c2ecf20Sopenharmony_ci		hdw->state_encoder_runok = 0;
15338c2ecf20Sopenharmony_ci		trace_stbit("state_encoder_runok",hdw->state_encoder_runok);
15348c2ecf20Sopenharmony_ci	}
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_ci	/* First prepare firmware loading */
15378c2ecf20Sopenharmony_ci	ret |= pvr2_write_register(hdw, 0x0048, 0xffffffff); /*interrupt mask*/
15388c2ecf20Sopenharmony_ci	ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000088); /*gpio dir*/
15398c2ecf20Sopenharmony_ci	ret |= pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000008); /*gpio output state*/
15408c2ecf20Sopenharmony_ci	ret |= pvr2_hdw_cmd_deep_reset(hdw);
15418c2ecf20Sopenharmony_ci	ret |= pvr2_write_register(hdw, 0xa064, 0x00000000); /*APU command*/
15428c2ecf20Sopenharmony_ci	ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000408); /*gpio dir*/
15438c2ecf20Sopenharmony_ci	ret |= pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000008); /*gpio output state*/
15448c2ecf20Sopenharmony_ci	ret |= pvr2_write_register(hdw, 0x9058, 0xffffffed); /*VPU ctrl*/
15458c2ecf20Sopenharmony_ci	ret |= pvr2_write_register(hdw, 0x9054, 0xfffffffd); /*reset hw blocks*/
15468c2ecf20Sopenharmony_ci	ret |= pvr2_write_register(hdw, 0x07f8, 0x80000800); /*encoder SDRAM refresh*/
15478c2ecf20Sopenharmony_ci	ret |= pvr2_write_register(hdw, 0x07fc, 0x0000001a); /*encoder SDRAM pre-charge*/
15488c2ecf20Sopenharmony_ci	ret |= pvr2_write_register(hdw, 0x0700, 0x00000000); /*I2C clock*/
15498c2ecf20Sopenharmony_ci	ret |= pvr2_write_register(hdw, 0xaa00, 0x00000000); /*unknown*/
15508c2ecf20Sopenharmony_ci	ret |= pvr2_write_register(hdw, 0xaa04, 0x00057810); /*unknown*/
15518c2ecf20Sopenharmony_ci	ret |= pvr2_write_register(hdw, 0xaa10, 0x00148500); /*unknown*/
15528c2ecf20Sopenharmony_ci	ret |= pvr2_write_register(hdw, 0xaa18, 0x00840000); /*unknown*/
15538c2ecf20Sopenharmony_ci	ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_FWPOST1);
15548c2ecf20Sopenharmony_ci	ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_MEMSEL | (1 << 8) | (0 << 16));
15558c2ecf20Sopenharmony_ci
15568c2ecf20Sopenharmony_ci	if (ret) {
15578c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
15588c2ecf20Sopenharmony_ci			   "firmware2 upload prep failed, ret=%d",ret);
15598c2ecf20Sopenharmony_ci		release_firmware(fw_entry);
15608c2ecf20Sopenharmony_ci		goto done;
15618c2ecf20Sopenharmony_ci	}
15628c2ecf20Sopenharmony_ci
15638c2ecf20Sopenharmony_ci	/* Now send firmware */
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_ci	fw_len = fw_entry->size;
15668c2ecf20Sopenharmony_ci
15678c2ecf20Sopenharmony_ci	if (fw_len % sizeof(u32)) {
15688c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
15698c2ecf20Sopenharmony_ci			   "size of %s firmware must be a multiple of %zu bytes",
15708c2ecf20Sopenharmony_ci			   fw_files[fwidx],sizeof(u32));
15718c2ecf20Sopenharmony_ci		release_firmware(fw_entry);
15728c2ecf20Sopenharmony_ci		ret = -EINVAL;
15738c2ecf20Sopenharmony_ci		goto done;
15748c2ecf20Sopenharmony_ci	}
15758c2ecf20Sopenharmony_ci
15768c2ecf20Sopenharmony_ci	fw_ptr = kmalloc(FIRMWARE_CHUNK_SIZE, GFP_KERNEL);
15778c2ecf20Sopenharmony_ci	if (fw_ptr == NULL){
15788c2ecf20Sopenharmony_ci		release_firmware(fw_entry);
15798c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
15808c2ecf20Sopenharmony_ci			   "failed to allocate memory for firmware2 upload");
15818c2ecf20Sopenharmony_ci		ret = -ENOMEM;
15828c2ecf20Sopenharmony_ci		goto done;
15838c2ecf20Sopenharmony_ci	}
15848c2ecf20Sopenharmony_ci
15858c2ecf20Sopenharmony_ci	pipe = usb_sndbulkpipe(hdw->usb_dev, PVR2_FIRMWARE_ENDPOINT);
15868c2ecf20Sopenharmony_ci
15878c2ecf20Sopenharmony_ci	fw_done = 0;
15888c2ecf20Sopenharmony_ci	for (fw_done = 0; fw_done < fw_len;) {
15898c2ecf20Sopenharmony_ci		bcnt = fw_len - fw_done;
15908c2ecf20Sopenharmony_ci		if (bcnt > FIRMWARE_CHUNK_SIZE) bcnt = FIRMWARE_CHUNK_SIZE;
15918c2ecf20Sopenharmony_ci		memcpy(fw_ptr, fw_entry->data + fw_done, bcnt);
15928c2ecf20Sopenharmony_ci		/* Usbsnoop log shows that we must swap bytes... */
15938c2ecf20Sopenharmony_ci		/* Some background info: The data being swapped here is a
15948c2ecf20Sopenharmony_ci		   firmware image destined for the mpeg encoder chip that
15958c2ecf20Sopenharmony_ci		   lives at the other end of a USB endpoint.  The encoder
15968c2ecf20Sopenharmony_ci		   chip always talks in 32 bit chunks and its storage is
15978c2ecf20Sopenharmony_ci		   organized into 32 bit words.  However from the file
15988c2ecf20Sopenharmony_ci		   system to the encoder chip everything is purely a byte
15998c2ecf20Sopenharmony_ci		   stream.  The firmware file's contents are always 32 bit
16008c2ecf20Sopenharmony_ci		   swapped from what the encoder expects.  Thus the need
16018c2ecf20Sopenharmony_ci		   always exists to swap the bytes regardless of the endian
16028c2ecf20Sopenharmony_ci		   type of the host processor and therefore swab32() makes
16038c2ecf20Sopenharmony_ci		   the most sense. */
16048c2ecf20Sopenharmony_ci		for (icnt = 0; icnt < bcnt/4 ; icnt++)
16058c2ecf20Sopenharmony_ci			((u32 *)fw_ptr)[icnt] = swab32(((u32 *)fw_ptr)[icnt]);
16068c2ecf20Sopenharmony_ci
16078c2ecf20Sopenharmony_ci		ret |= usb_bulk_msg(hdw->usb_dev, pipe, fw_ptr,bcnt,
16088c2ecf20Sopenharmony_ci				    &actual_length, 1000);
16098c2ecf20Sopenharmony_ci		ret |= (actual_length != bcnt);
16108c2ecf20Sopenharmony_ci		if (ret) break;
16118c2ecf20Sopenharmony_ci		fw_done += bcnt;
16128c2ecf20Sopenharmony_ci	}
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_ci	trace_firmware("upload of %s : %i / %i ",
16158c2ecf20Sopenharmony_ci		       fw_files[fwidx],fw_done,fw_len);
16168c2ecf20Sopenharmony_ci
16178c2ecf20Sopenharmony_ci	kfree(fw_ptr);
16188c2ecf20Sopenharmony_ci	release_firmware(fw_entry);
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_ci	if (ret) {
16218c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
16228c2ecf20Sopenharmony_ci			   "firmware2 upload transfer failure");
16238c2ecf20Sopenharmony_ci		goto done;
16248c2ecf20Sopenharmony_ci	}
16258c2ecf20Sopenharmony_ci
16268c2ecf20Sopenharmony_ci	/* Finish upload */
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ci	ret |= pvr2_write_register(hdw, 0x9054, 0xffffffff); /*reset hw blocks*/
16298c2ecf20Sopenharmony_ci	ret |= pvr2_write_register(hdw, 0x9058, 0xffffffe8); /*VPU ctrl*/
16308c2ecf20Sopenharmony_ci	ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_MEMSEL | (1 << 8) | (0 << 16));
16318c2ecf20Sopenharmony_ci
16328c2ecf20Sopenharmony_ci	if (ret) {
16338c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
16348c2ecf20Sopenharmony_ci			   "firmware2 upload post-proc failure");
16358c2ecf20Sopenharmony_ci	}
16368c2ecf20Sopenharmony_ci
16378c2ecf20Sopenharmony_ci done:
16388c2ecf20Sopenharmony_ci	if (hdw->hdw_desc->signal_routing_scheme ==
16398c2ecf20Sopenharmony_ci	    PVR2_ROUTING_SCHEME_GOTVIEW) {
16408c2ecf20Sopenharmony_ci		/* Ensure that GPIO 11 is set to output for GOTVIEW
16418c2ecf20Sopenharmony_ci		   hardware. */
16428c2ecf20Sopenharmony_ci		pvr2_hdw_gpio_chg_dir(hdw,(1 << 11),~0);
16438c2ecf20Sopenharmony_ci	}
16448c2ecf20Sopenharmony_ci	return ret;
16458c2ecf20Sopenharmony_ci}
16468c2ecf20Sopenharmony_ci
16478c2ecf20Sopenharmony_ci
16488c2ecf20Sopenharmony_cistatic const char *pvr2_get_state_name(unsigned int st)
16498c2ecf20Sopenharmony_ci{
16508c2ecf20Sopenharmony_ci	if (st < ARRAY_SIZE(pvr2_state_names)) {
16518c2ecf20Sopenharmony_ci		return pvr2_state_names[st];
16528c2ecf20Sopenharmony_ci	}
16538c2ecf20Sopenharmony_ci	return "???";
16548c2ecf20Sopenharmony_ci}
16558c2ecf20Sopenharmony_ci
16568c2ecf20Sopenharmony_cistatic int pvr2_decoder_enable(struct pvr2_hdw *hdw,int enablefl)
16578c2ecf20Sopenharmony_ci{
16588c2ecf20Sopenharmony_ci	/* Even though we really only care about the video decoder chip at
16598c2ecf20Sopenharmony_ci	   this point, we'll broadcast stream on/off to all sub-devices
16608c2ecf20Sopenharmony_ci	   anyway, just in case somebody else wants to hear the
16618c2ecf20Sopenharmony_ci	   command... */
16628c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 stream=%s",
16638c2ecf20Sopenharmony_ci		   (enablefl ? "on" : "off"));
16648c2ecf20Sopenharmony_ci	v4l2_device_call_all(&hdw->v4l2_dev, 0, video, s_stream, enablefl);
16658c2ecf20Sopenharmony_ci	v4l2_device_call_all(&hdw->v4l2_dev, 0, audio, s_stream, enablefl);
16668c2ecf20Sopenharmony_ci	if (hdw->decoder_client_id) {
16678c2ecf20Sopenharmony_ci		/* We get here if the encoder has been noticed.  Otherwise
16688c2ecf20Sopenharmony_ci		   we'll issue a warning to the user (which should
16698c2ecf20Sopenharmony_ci		   normally never happen). */
16708c2ecf20Sopenharmony_ci		return 0;
16718c2ecf20Sopenharmony_ci	}
16728c2ecf20Sopenharmony_ci	if (!hdw->flag_decoder_missed) {
16738c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
16748c2ecf20Sopenharmony_ci			   "***WARNING*** No decoder present");
16758c2ecf20Sopenharmony_ci		hdw->flag_decoder_missed = !0;
16768c2ecf20Sopenharmony_ci		trace_stbit("flag_decoder_missed",
16778c2ecf20Sopenharmony_ci			    hdw->flag_decoder_missed);
16788c2ecf20Sopenharmony_ci	}
16798c2ecf20Sopenharmony_ci	return -EIO;
16808c2ecf20Sopenharmony_ci}
16818c2ecf20Sopenharmony_ci
16828c2ecf20Sopenharmony_ci
16838c2ecf20Sopenharmony_ciint pvr2_hdw_get_state(struct pvr2_hdw *hdw)
16848c2ecf20Sopenharmony_ci{
16858c2ecf20Sopenharmony_ci	return hdw->master_state;
16868c2ecf20Sopenharmony_ci}
16878c2ecf20Sopenharmony_ci
16888c2ecf20Sopenharmony_ci
16898c2ecf20Sopenharmony_cistatic int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *hdw)
16908c2ecf20Sopenharmony_ci{
16918c2ecf20Sopenharmony_ci	if (!hdw->flag_tripped) return 0;
16928c2ecf20Sopenharmony_ci	hdw->flag_tripped = 0;
16938c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_ERROR_LEGS,
16948c2ecf20Sopenharmony_ci		   "Clearing driver error status");
16958c2ecf20Sopenharmony_ci	return !0;
16968c2ecf20Sopenharmony_ci}
16978c2ecf20Sopenharmony_ci
16988c2ecf20Sopenharmony_ci
16998c2ecf20Sopenharmony_ciint pvr2_hdw_untrip(struct pvr2_hdw *hdw)
17008c2ecf20Sopenharmony_ci{
17018c2ecf20Sopenharmony_ci	int fl;
17028c2ecf20Sopenharmony_ci	LOCK_TAKE(hdw->big_lock); do {
17038c2ecf20Sopenharmony_ci		fl = pvr2_hdw_untrip_unlocked(hdw);
17048c2ecf20Sopenharmony_ci	} while (0); LOCK_GIVE(hdw->big_lock);
17058c2ecf20Sopenharmony_ci	if (fl) pvr2_hdw_state_sched(hdw);
17068c2ecf20Sopenharmony_ci	return 0;
17078c2ecf20Sopenharmony_ci}
17088c2ecf20Sopenharmony_ci
17098c2ecf20Sopenharmony_ci
17108c2ecf20Sopenharmony_ci
17118c2ecf20Sopenharmony_ci
17128c2ecf20Sopenharmony_ciint pvr2_hdw_get_streaming(struct pvr2_hdw *hdw)
17138c2ecf20Sopenharmony_ci{
17148c2ecf20Sopenharmony_ci	return hdw->state_pipeline_req != 0;
17158c2ecf20Sopenharmony_ci}
17168c2ecf20Sopenharmony_ci
17178c2ecf20Sopenharmony_ci
17188c2ecf20Sopenharmony_ciint pvr2_hdw_set_streaming(struct pvr2_hdw *hdw,int enable_flag)
17198c2ecf20Sopenharmony_ci{
17208c2ecf20Sopenharmony_ci	int ret,st;
17218c2ecf20Sopenharmony_ci	LOCK_TAKE(hdw->big_lock); do {
17228c2ecf20Sopenharmony_ci		pvr2_hdw_untrip_unlocked(hdw);
17238c2ecf20Sopenharmony_ci		if ((!enable_flag) != !(hdw->state_pipeline_req)) {
17248c2ecf20Sopenharmony_ci			hdw->state_pipeline_req = enable_flag != 0;
17258c2ecf20Sopenharmony_ci			pvr2_trace(PVR2_TRACE_START_STOP,
17268c2ecf20Sopenharmony_ci				   "/*--TRACE_STREAM--*/ %s",
17278c2ecf20Sopenharmony_ci				   enable_flag ? "enable" : "disable");
17288c2ecf20Sopenharmony_ci		}
17298c2ecf20Sopenharmony_ci		pvr2_hdw_state_sched(hdw);
17308c2ecf20Sopenharmony_ci	} while (0); LOCK_GIVE(hdw->big_lock);
17318c2ecf20Sopenharmony_ci	if ((ret = pvr2_hdw_wait(hdw,0)) < 0) return ret;
17328c2ecf20Sopenharmony_ci	if (enable_flag) {
17338c2ecf20Sopenharmony_ci		while ((st = hdw->master_state) != PVR2_STATE_RUN) {
17348c2ecf20Sopenharmony_ci			if (st != PVR2_STATE_READY) return -EIO;
17358c2ecf20Sopenharmony_ci			if ((ret = pvr2_hdw_wait(hdw,st)) < 0) return ret;
17368c2ecf20Sopenharmony_ci		}
17378c2ecf20Sopenharmony_ci	}
17388c2ecf20Sopenharmony_ci	return 0;
17398c2ecf20Sopenharmony_ci}
17408c2ecf20Sopenharmony_ci
17418c2ecf20Sopenharmony_ci
17428c2ecf20Sopenharmony_ciint pvr2_hdw_set_stream_type(struct pvr2_hdw *hdw,enum pvr2_config config)
17438c2ecf20Sopenharmony_ci{
17448c2ecf20Sopenharmony_ci	int fl;
17458c2ecf20Sopenharmony_ci	LOCK_TAKE(hdw->big_lock);
17468c2ecf20Sopenharmony_ci	if ((fl = (hdw->desired_stream_type != config)) != 0) {
17478c2ecf20Sopenharmony_ci		hdw->desired_stream_type = config;
17488c2ecf20Sopenharmony_ci		hdw->state_pipeline_config = 0;
17498c2ecf20Sopenharmony_ci		trace_stbit("state_pipeline_config",
17508c2ecf20Sopenharmony_ci			    hdw->state_pipeline_config);
17518c2ecf20Sopenharmony_ci		pvr2_hdw_state_sched(hdw);
17528c2ecf20Sopenharmony_ci	}
17538c2ecf20Sopenharmony_ci	LOCK_GIVE(hdw->big_lock);
17548c2ecf20Sopenharmony_ci	if (fl) return 0;
17558c2ecf20Sopenharmony_ci	return pvr2_hdw_wait(hdw,0);
17568c2ecf20Sopenharmony_ci}
17578c2ecf20Sopenharmony_ci
17588c2ecf20Sopenharmony_ci
17598c2ecf20Sopenharmony_cistatic int get_default_tuner_type(struct pvr2_hdw *hdw)
17608c2ecf20Sopenharmony_ci{
17618c2ecf20Sopenharmony_ci	int unit_number = hdw->unit_number;
17628c2ecf20Sopenharmony_ci	int tp = -1;
17638c2ecf20Sopenharmony_ci	if ((unit_number >= 0) && (unit_number < PVR_NUM)) {
17648c2ecf20Sopenharmony_ci		tp = tuner[unit_number];
17658c2ecf20Sopenharmony_ci	}
17668c2ecf20Sopenharmony_ci	if (tp < 0) return -EINVAL;
17678c2ecf20Sopenharmony_ci	hdw->tuner_type = tp;
17688c2ecf20Sopenharmony_ci	hdw->tuner_updated = !0;
17698c2ecf20Sopenharmony_ci	return 0;
17708c2ecf20Sopenharmony_ci}
17718c2ecf20Sopenharmony_ci
17728c2ecf20Sopenharmony_ci
17738c2ecf20Sopenharmony_cistatic v4l2_std_id get_default_standard(struct pvr2_hdw *hdw)
17748c2ecf20Sopenharmony_ci{
17758c2ecf20Sopenharmony_ci	int unit_number = hdw->unit_number;
17768c2ecf20Sopenharmony_ci	int tp = 0;
17778c2ecf20Sopenharmony_ci	if ((unit_number >= 0) && (unit_number < PVR_NUM)) {
17788c2ecf20Sopenharmony_ci		tp = video_std[unit_number];
17798c2ecf20Sopenharmony_ci		if (tp) return tp;
17808c2ecf20Sopenharmony_ci	}
17818c2ecf20Sopenharmony_ci	return 0;
17828c2ecf20Sopenharmony_ci}
17838c2ecf20Sopenharmony_ci
17848c2ecf20Sopenharmony_ci
17858c2ecf20Sopenharmony_cistatic unsigned int get_default_error_tolerance(struct pvr2_hdw *hdw)
17868c2ecf20Sopenharmony_ci{
17878c2ecf20Sopenharmony_ci	int unit_number = hdw->unit_number;
17888c2ecf20Sopenharmony_ci	int tp = 0;
17898c2ecf20Sopenharmony_ci	if ((unit_number >= 0) && (unit_number < PVR_NUM)) {
17908c2ecf20Sopenharmony_ci		tp = tolerance[unit_number];
17918c2ecf20Sopenharmony_ci	}
17928c2ecf20Sopenharmony_ci	return tp;
17938c2ecf20Sopenharmony_ci}
17948c2ecf20Sopenharmony_ci
17958c2ecf20Sopenharmony_ci
17968c2ecf20Sopenharmony_cistatic int pvr2_hdw_check_firmware(struct pvr2_hdw *hdw)
17978c2ecf20Sopenharmony_ci{
17988c2ecf20Sopenharmony_ci	/* Try a harmless request to fetch the eeprom's address over
17998c2ecf20Sopenharmony_ci	   endpoint 1.  See what happens.  Only the full FX2 image can
18008c2ecf20Sopenharmony_ci	   respond to this.  If this probe fails then likely the FX2
18018c2ecf20Sopenharmony_ci	   firmware needs be loaded. */
18028c2ecf20Sopenharmony_ci	int result;
18038c2ecf20Sopenharmony_ci	LOCK_TAKE(hdw->ctl_lock); do {
18048c2ecf20Sopenharmony_ci		hdw->cmd_buffer[0] = FX2CMD_GET_EEPROM_ADDR;
18058c2ecf20Sopenharmony_ci		result = pvr2_send_request_ex(hdw,HZ*1,!0,
18068c2ecf20Sopenharmony_ci					   hdw->cmd_buffer,1,
18078c2ecf20Sopenharmony_ci					   hdw->cmd_buffer,1);
18088c2ecf20Sopenharmony_ci		if (result < 0) break;
18098c2ecf20Sopenharmony_ci	} while(0); LOCK_GIVE(hdw->ctl_lock);
18108c2ecf20Sopenharmony_ci	if (result) {
18118c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INIT,
18128c2ecf20Sopenharmony_ci			   "Probe of device endpoint 1 result status %d",
18138c2ecf20Sopenharmony_ci			   result);
18148c2ecf20Sopenharmony_ci	} else {
18158c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INIT,
18168c2ecf20Sopenharmony_ci			   "Probe of device endpoint 1 succeeded");
18178c2ecf20Sopenharmony_ci	}
18188c2ecf20Sopenharmony_ci	return result == 0;
18198c2ecf20Sopenharmony_ci}
18208c2ecf20Sopenharmony_ci
18218c2ecf20Sopenharmony_cistruct pvr2_std_hack {
18228c2ecf20Sopenharmony_ci	v4l2_std_id pat;  /* Pattern to match */
18238c2ecf20Sopenharmony_ci	v4l2_std_id msk;  /* Which bits we care about */
18248c2ecf20Sopenharmony_ci	v4l2_std_id std;  /* What additional standards or default to set */
18258c2ecf20Sopenharmony_ci};
18268c2ecf20Sopenharmony_ci
18278c2ecf20Sopenharmony_ci/* This data structure labels specific combinations of standards from
18288c2ecf20Sopenharmony_ci   tveeprom that we'll try to recognize.  If we recognize one, then assume
18298c2ecf20Sopenharmony_ci   a specified default standard to use.  This is here because tveeprom only
18308c2ecf20Sopenharmony_ci   tells us about available standards not the intended default standard (if
18318c2ecf20Sopenharmony_ci   any) for the device in question.  We guess the default based on what has
18328c2ecf20Sopenharmony_ci   been reported as available.  Note that this is only for guessing a
18338c2ecf20Sopenharmony_ci   default - which can always be overridden explicitly - and if the user
18348c2ecf20Sopenharmony_ci   has otherwise named a default then that default will always be used in
18358c2ecf20Sopenharmony_ci   place of this table. */
18368c2ecf20Sopenharmony_cistatic const struct pvr2_std_hack std_eeprom_maps[] = {
18378c2ecf20Sopenharmony_ci	{	/* PAL(B/G) */
18388c2ecf20Sopenharmony_ci		.pat = V4L2_STD_B|V4L2_STD_GH,
18398c2ecf20Sopenharmony_ci		.std = V4L2_STD_PAL_B|V4L2_STD_PAL_B1|V4L2_STD_PAL_G,
18408c2ecf20Sopenharmony_ci	},
18418c2ecf20Sopenharmony_ci	{	/* NTSC(M) */
18428c2ecf20Sopenharmony_ci		.pat = V4L2_STD_MN,
18438c2ecf20Sopenharmony_ci		.std = V4L2_STD_NTSC_M,
18448c2ecf20Sopenharmony_ci	},
18458c2ecf20Sopenharmony_ci	{	/* PAL(I) */
18468c2ecf20Sopenharmony_ci		.pat = V4L2_STD_PAL_I,
18478c2ecf20Sopenharmony_ci		.std = V4L2_STD_PAL_I,
18488c2ecf20Sopenharmony_ci	},
18498c2ecf20Sopenharmony_ci	{	/* SECAM(L/L') */
18508c2ecf20Sopenharmony_ci		.pat = V4L2_STD_SECAM_L|V4L2_STD_SECAM_LC,
18518c2ecf20Sopenharmony_ci		.std = V4L2_STD_SECAM_L|V4L2_STD_SECAM_LC,
18528c2ecf20Sopenharmony_ci	},
18538c2ecf20Sopenharmony_ci	{	/* PAL(D/D1/K) */
18548c2ecf20Sopenharmony_ci		.pat = V4L2_STD_DK,
18558c2ecf20Sopenharmony_ci		.std = V4L2_STD_PAL_D|V4L2_STD_PAL_D1|V4L2_STD_PAL_K,
18568c2ecf20Sopenharmony_ci	},
18578c2ecf20Sopenharmony_ci};
18588c2ecf20Sopenharmony_ci
18598c2ecf20Sopenharmony_cistatic void pvr2_hdw_setup_std(struct pvr2_hdw *hdw)
18608c2ecf20Sopenharmony_ci{
18618c2ecf20Sopenharmony_ci	char buf[40];
18628c2ecf20Sopenharmony_ci	unsigned int bcnt;
18638c2ecf20Sopenharmony_ci	v4l2_std_id std1,std2,std3;
18648c2ecf20Sopenharmony_ci
18658c2ecf20Sopenharmony_ci	std1 = get_default_standard(hdw);
18668c2ecf20Sopenharmony_ci	std3 = std1 ? 0 : hdw->hdw_desc->default_std_mask;
18678c2ecf20Sopenharmony_ci
18688c2ecf20Sopenharmony_ci	bcnt = pvr2_std_id_to_str(buf,sizeof(buf),hdw->std_mask_eeprom);
18698c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_STD,
18708c2ecf20Sopenharmony_ci		   "Supported video standard(s) reported available in hardware: %.*s",
18718c2ecf20Sopenharmony_ci		   bcnt,buf);
18728c2ecf20Sopenharmony_ci
18738c2ecf20Sopenharmony_ci	hdw->std_mask_avail = hdw->std_mask_eeprom;
18748c2ecf20Sopenharmony_ci
18758c2ecf20Sopenharmony_ci	std2 = (std1|std3) & ~hdw->std_mask_avail;
18768c2ecf20Sopenharmony_ci	if (std2) {
18778c2ecf20Sopenharmony_ci		bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std2);
18788c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_STD,
18798c2ecf20Sopenharmony_ci			   "Expanding supported video standards to include: %.*s",
18808c2ecf20Sopenharmony_ci			   bcnt,buf);
18818c2ecf20Sopenharmony_ci		hdw->std_mask_avail |= std2;
18828c2ecf20Sopenharmony_ci	}
18838c2ecf20Sopenharmony_ci
18848c2ecf20Sopenharmony_ci	hdw->std_info_cur.def.type_bitmask.valid_bits = hdw->std_mask_avail;
18858c2ecf20Sopenharmony_ci
18868c2ecf20Sopenharmony_ci	if (std1) {
18878c2ecf20Sopenharmony_ci		bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std1);
18888c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_STD,
18898c2ecf20Sopenharmony_ci			   "Initial video standard forced to %.*s",
18908c2ecf20Sopenharmony_ci			   bcnt,buf);
18918c2ecf20Sopenharmony_ci		hdw->std_mask_cur = std1;
18928c2ecf20Sopenharmony_ci		hdw->std_dirty = !0;
18938c2ecf20Sopenharmony_ci		return;
18948c2ecf20Sopenharmony_ci	}
18958c2ecf20Sopenharmony_ci	if (std3) {
18968c2ecf20Sopenharmony_ci		bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std3);
18978c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_STD,
18988c2ecf20Sopenharmony_ci			   "Initial video standard (determined by device type): %.*s",
18998c2ecf20Sopenharmony_ci			   bcnt, buf);
19008c2ecf20Sopenharmony_ci		hdw->std_mask_cur = std3;
19018c2ecf20Sopenharmony_ci		hdw->std_dirty = !0;
19028c2ecf20Sopenharmony_ci		return;
19038c2ecf20Sopenharmony_ci	}
19048c2ecf20Sopenharmony_ci
19058c2ecf20Sopenharmony_ci	{
19068c2ecf20Sopenharmony_ci		unsigned int idx;
19078c2ecf20Sopenharmony_ci		for (idx = 0; idx < ARRAY_SIZE(std_eeprom_maps); idx++) {
19088c2ecf20Sopenharmony_ci			if (std_eeprom_maps[idx].msk ?
19098c2ecf20Sopenharmony_ci			    ((std_eeprom_maps[idx].pat ^
19108c2ecf20Sopenharmony_ci			     hdw->std_mask_eeprom) &
19118c2ecf20Sopenharmony_ci			     std_eeprom_maps[idx].msk) :
19128c2ecf20Sopenharmony_ci			    (std_eeprom_maps[idx].pat !=
19138c2ecf20Sopenharmony_ci			     hdw->std_mask_eeprom)) continue;
19148c2ecf20Sopenharmony_ci			bcnt = pvr2_std_id_to_str(buf,sizeof(buf),
19158c2ecf20Sopenharmony_ci						  std_eeprom_maps[idx].std);
19168c2ecf20Sopenharmony_ci			pvr2_trace(PVR2_TRACE_STD,
19178c2ecf20Sopenharmony_ci				   "Initial video standard guessed as %.*s",
19188c2ecf20Sopenharmony_ci				   bcnt,buf);
19198c2ecf20Sopenharmony_ci			hdw->std_mask_cur = std_eeprom_maps[idx].std;
19208c2ecf20Sopenharmony_ci			hdw->std_dirty = !0;
19218c2ecf20Sopenharmony_ci			return;
19228c2ecf20Sopenharmony_ci		}
19238c2ecf20Sopenharmony_ci	}
19248c2ecf20Sopenharmony_ci
19258c2ecf20Sopenharmony_ci}
19268c2ecf20Sopenharmony_ci
19278c2ecf20Sopenharmony_ci
19288c2ecf20Sopenharmony_cistatic unsigned int pvr2_copy_i2c_addr_list(
19298c2ecf20Sopenharmony_ci	unsigned short *dst, const unsigned char *src,
19308c2ecf20Sopenharmony_ci	unsigned int dst_max)
19318c2ecf20Sopenharmony_ci{
19328c2ecf20Sopenharmony_ci	unsigned int cnt = 0;
19338c2ecf20Sopenharmony_ci	if (!src) return 0;
19348c2ecf20Sopenharmony_ci	while (src[cnt] && (cnt + 1) < dst_max) {
19358c2ecf20Sopenharmony_ci		dst[cnt] = src[cnt];
19368c2ecf20Sopenharmony_ci		cnt++;
19378c2ecf20Sopenharmony_ci	}
19388c2ecf20Sopenharmony_ci	dst[cnt] = I2C_CLIENT_END;
19398c2ecf20Sopenharmony_ci	return cnt;
19408c2ecf20Sopenharmony_ci}
19418c2ecf20Sopenharmony_ci
19428c2ecf20Sopenharmony_ci
19438c2ecf20Sopenharmony_cistatic void pvr2_hdw_cx25840_vbi_hack(struct pvr2_hdw *hdw)
19448c2ecf20Sopenharmony_ci{
19458c2ecf20Sopenharmony_ci	/*
19468c2ecf20Sopenharmony_ci	  Mike Isely <isely@pobox.com> 19-Nov-2006 - This bit of nuttiness
19478c2ecf20Sopenharmony_ci	  for cx25840 causes that module to correctly set up its video
19488c2ecf20Sopenharmony_ci	  scaling.  This is really a problem in the cx25840 module itself,
19498c2ecf20Sopenharmony_ci	  but we work around it here.  The problem has not been seen in
19508c2ecf20Sopenharmony_ci	  ivtv because there VBI is supported and set up.  We don't do VBI
19518c2ecf20Sopenharmony_ci	  here (at least not yet) and thus we never attempted to even set
19528c2ecf20Sopenharmony_ci	  it up.
19538c2ecf20Sopenharmony_ci	*/
19548c2ecf20Sopenharmony_ci	struct v4l2_format fmt;
19558c2ecf20Sopenharmony_ci	if (hdw->decoder_client_id != PVR2_CLIENT_ID_CX25840) {
19568c2ecf20Sopenharmony_ci		/* We're not using a cx25840 so don't enable the hack */
19578c2ecf20Sopenharmony_ci		return;
19588c2ecf20Sopenharmony_ci	}
19598c2ecf20Sopenharmony_ci
19608c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INIT,
19618c2ecf20Sopenharmony_ci		   "Module ID %u: Executing cx25840 VBI hack",
19628c2ecf20Sopenharmony_ci		   hdw->decoder_client_id);
19638c2ecf20Sopenharmony_ci	memset(&fmt, 0, sizeof(fmt));
19648c2ecf20Sopenharmony_ci	fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
19658c2ecf20Sopenharmony_ci	fmt.fmt.sliced.service_lines[0][21] = V4L2_SLICED_CAPTION_525;
19668c2ecf20Sopenharmony_ci	fmt.fmt.sliced.service_lines[1][21] = V4L2_SLICED_CAPTION_525;
19678c2ecf20Sopenharmony_ci	v4l2_device_call_all(&hdw->v4l2_dev, hdw->decoder_client_id,
19688c2ecf20Sopenharmony_ci			     vbi, s_sliced_fmt, &fmt.fmt.sliced);
19698c2ecf20Sopenharmony_ci}
19708c2ecf20Sopenharmony_ci
19718c2ecf20Sopenharmony_ci
19728c2ecf20Sopenharmony_cistatic int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw,
19738c2ecf20Sopenharmony_ci				const struct pvr2_device_client_desc *cd)
19748c2ecf20Sopenharmony_ci{
19758c2ecf20Sopenharmony_ci	const char *fname;
19768c2ecf20Sopenharmony_ci	unsigned char mid;
19778c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd;
19788c2ecf20Sopenharmony_ci	unsigned int i2ccnt;
19798c2ecf20Sopenharmony_ci	const unsigned char *p;
19808c2ecf20Sopenharmony_ci	/* Arbitrary count - max # i2c addresses we will probe */
19818c2ecf20Sopenharmony_ci	unsigned short i2caddr[25];
19828c2ecf20Sopenharmony_ci
19838c2ecf20Sopenharmony_ci	mid = cd->module_id;
19848c2ecf20Sopenharmony_ci	fname = (mid < ARRAY_SIZE(module_names)) ? module_names[mid] : NULL;
19858c2ecf20Sopenharmony_ci	if (!fname) {
19868c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
19878c2ecf20Sopenharmony_ci			   "Module ID %u for device %s has no name?  The driver might have a configuration problem.",
19888c2ecf20Sopenharmony_ci			   mid,
19898c2ecf20Sopenharmony_ci			   hdw->hdw_desc->description);
19908c2ecf20Sopenharmony_ci		return -EINVAL;
19918c2ecf20Sopenharmony_ci	}
19928c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INIT,
19938c2ecf20Sopenharmony_ci		   "Module ID %u (%s) for device %s being loaded...",
19948c2ecf20Sopenharmony_ci		   mid, fname,
19958c2ecf20Sopenharmony_ci		   hdw->hdw_desc->description);
19968c2ecf20Sopenharmony_ci
19978c2ecf20Sopenharmony_ci	i2ccnt = pvr2_copy_i2c_addr_list(i2caddr, cd->i2c_address_list,
19988c2ecf20Sopenharmony_ci					 ARRAY_SIZE(i2caddr));
19998c2ecf20Sopenharmony_ci	if (!i2ccnt && ((p = (mid < ARRAY_SIZE(module_i2c_addresses)) ?
20008c2ecf20Sopenharmony_ci			 module_i2c_addresses[mid] : NULL) != NULL)) {
20018c2ecf20Sopenharmony_ci		/* Second chance: Try default i2c address list */
20028c2ecf20Sopenharmony_ci		i2ccnt = pvr2_copy_i2c_addr_list(i2caddr, p,
20038c2ecf20Sopenharmony_ci						 ARRAY_SIZE(i2caddr));
20048c2ecf20Sopenharmony_ci		if (i2ccnt) {
20058c2ecf20Sopenharmony_ci			pvr2_trace(PVR2_TRACE_INIT,
20068c2ecf20Sopenharmony_ci				   "Module ID %u: Using default i2c address list",
20078c2ecf20Sopenharmony_ci				   mid);
20088c2ecf20Sopenharmony_ci		}
20098c2ecf20Sopenharmony_ci	}
20108c2ecf20Sopenharmony_ci
20118c2ecf20Sopenharmony_ci	if (!i2ccnt) {
20128c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
20138c2ecf20Sopenharmony_ci			   "Module ID %u (%s) for device %s: No i2c addresses.	The driver might have a configuration problem.",
20148c2ecf20Sopenharmony_ci			   mid, fname, hdw->hdw_desc->description);
20158c2ecf20Sopenharmony_ci		return -EINVAL;
20168c2ecf20Sopenharmony_ci	}
20178c2ecf20Sopenharmony_ci
20188c2ecf20Sopenharmony_ci	if (i2ccnt == 1) {
20198c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INIT,
20208c2ecf20Sopenharmony_ci			   "Module ID %u: Setting up with specified i2c address 0x%x",
20218c2ecf20Sopenharmony_ci			   mid, i2caddr[0]);
20228c2ecf20Sopenharmony_ci		sd = v4l2_i2c_new_subdev(&hdw->v4l2_dev, &hdw->i2c_adap,
20238c2ecf20Sopenharmony_ci					 fname, i2caddr[0], NULL);
20248c2ecf20Sopenharmony_ci	} else {
20258c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INIT,
20268c2ecf20Sopenharmony_ci			   "Module ID %u: Setting up with address probe list",
20278c2ecf20Sopenharmony_ci			   mid);
20288c2ecf20Sopenharmony_ci		sd = v4l2_i2c_new_subdev(&hdw->v4l2_dev, &hdw->i2c_adap,
20298c2ecf20Sopenharmony_ci					 fname, 0, i2caddr);
20308c2ecf20Sopenharmony_ci	}
20318c2ecf20Sopenharmony_ci
20328c2ecf20Sopenharmony_ci	if (!sd) {
20338c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
20348c2ecf20Sopenharmony_ci			   "Module ID %u (%s) for device %s failed to load.  Possible missing sub-device kernel module or initialization failure within module.",
20358c2ecf20Sopenharmony_ci			   mid, fname, hdw->hdw_desc->description);
20368c2ecf20Sopenharmony_ci		return -EIO;
20378c2ecf20Sopenharmony_ci	}
20388c2ecf20Sopenharmony_ci
20398c2ecf20Sopenharmony_ci	/* Tag this sub-device instance with the module ID we know about.
20408c2ecf20Sopenharmony_ci	   In other places we'll use that tag to determine if the instance
20418c2ecf20Sopenharmony_ci	   requires special handling. */
20428c2ecf20Sopenharmony_ci	sd->grp_id = mid;
20438c2ecf20Sopenharmony_ci
20448c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INFO, "Attached sub-driver %s", fname);
20458c2ecf20Sopenharmony_ci
20468c2ecf20Sopenharmony_ci
20478c2ecf20Sopenharmony_ci	/* client-specific setup... */
20488c2ecf20Sopenharmony_ci	switch (mid) {
20498c2ecf20Sopenharmony_ci	case PVR2_CLIENT_ID_CX25840:
20508c2ecf20Sopenharmony_ci	case PVR2_CLIENT_ID_SAA7115:
20518c2ecf20Sopenharmony_ci		hdw->decoder_client_id = mid;
20528c2ecf20Sopenharmony_ci		break;
20538c2ecf20Sopenharmony_ci	default: break;
20548c2ecf20Sopenharmony_ci	}
20558c2ecf20Sopenharmony_ci
20568c2ecf20Sopenharmony_ci	return 0;
20578c2ecf20Sopenharmony_ci}
20588c2ecf20Sopenharmony_ci
20598c2ecf20Sopenharmony_ci
20608c2ecf20Sopenharmony_cistatic void pvr2_hdw_load_modules(struct pvr2_hdw *hdw)
20618c2ecf20Sopenharmony_ci{
20628c2ecf20Sopenharmony_ci	unsigned int idx;
20638c2ecf20Sopenharmony_ci	const struct pvr2_string_table *cm;
20648c2ecf20Sopenharmony_ci	const struct pvr2_device_client_table *ct;
20658c2ecf20Sopenharmony_ci	int okFl = !0;
20668c2ecf20Sopenharmony_ci
20678c2ecf20Sopenharmony_ci	cm = &hdw->hdw_desc->client_modules;
20688c2ecf20Sopenharmony_ci	for (idx = 0; idx < cm->cnt; idx++) {
20698c2ecf20Sopenharmony_ci		request_module(cm->lst[idx]);
20708c2ecf20Sopenharmony_ci	}
20718c2ecf20Sopenharmony_ci
20728c2ecf20Sopenharmony_ci	ct = &hdw->hdw_desc->client_table;
20738c2ecf20Sopenharmony_ci	for (idx = 0; idx < ct->cnt; idx++) {
20748c2ecf20Sopenharmony_ci		if (pvr2_hdw_load_subdev(hdw, &ct->lst[idx]) < 0) okFl = 0;
20758c2ecf20Sopenharmony_ci	}
20768c2ecf20Sopenharmony_ci	if (!okFl) {
20778c2ecf20Sopenharmony_ci		hdw->flag_modulefail = !0;
20788c2ecf20Sopenharmony_ci		pvr2_hdw_render_useless(hdw);
20798c2ecf20Sopenharmony_ci	}
20808c2ecf20Sopenharmony_ci}
20818c2ecf20Sopenharmony_ci
20828c2ecf20Sopenharmony_ci
20838c2ecf20Sopenharmony_cistatic void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
20848c2ecf20Sopenharmony_ci{
20858c2ecf20Sopenharmony_ci	int ret;
20868c2ecf20Sopenharmony_ci	unsigned int idx;
20878c2ecf20Sopenharmony_ci	struct pvr2_ctrl *cptr;
20888c2ecf20Sopenharmony_ci	int reloadFl = 0;
20898c2ecf20Sopenharmony_ci	if (hdw->hdw_desc->fx2_firmware.cnt) {
20908c2ecf20Sopenharmony_ci		if (!reloadFl) {
20918c2ecf20Sopenharmony_ci			reloadFl =
20928c2ecf20Sopenharmony_ci				(hdw->usb_intf->cur_altsetting->desc.bNumEndpoints
20938c2ecf20Sopenharmony_ci				 == 0);
20948c2ecf20Sopenharmony_ci			if (reloadFl) {
20958c2ecf20Sopenharmony_ci				pvr2_trace(PVR2_TRACE_INIT,
20968c2ecf20Sopenharmony_ci					   "USB endpoint config looks strange; possibly firmware needs to be loaded");
20978c2ecf20Sopenharmony_ci			}
20988c2ecf20Sopenharmony_ci		}
20998c2ecf20Sopenharmony_ci		if (!reloadFl) {
21008c2ecf20Sopenharmony_ci			reloadFl = !pvr2_hdw_check_firmware(hdw);
21018c2ecf20Sopenharmony_ci			if (reloadFl) {
21028c2ecf20Sopenharmony_ci				pvr2_trace(PVR2_TRACE_INIT,
21038c2ecf20Sopenharmony_ci					   "Check for FX2 firmware failed; possibly firmware needs to be loaded");
21048c2ecf20Sopenharmony_ci			}
21058c2ecf20Sopenharmony_ci		}
21068c2ecf20Sopenharmony_ci		if (reloadFl) {
21078c2ecf20Sopenharmony_ci			if (pvr2_upload_firmware1(hdw) != 0) {
21088c2ecf20Sopenharmony_ci				pvr2_trace(PVR2_TRACE_ERROR_LEGS,
21098c2ecf20Sopenharmony_ci					   "Failure uploading firmware1");
21108c2ecf20Sopenharmony_ci			}
21118c2ecf20Sopenharmony_ci			return;
21128c2ecf20Sopenharmony_ci		}
21138c2ecf20Sopenharmony_ci	}
21148c2ecf20Sopenharmony_ci	hdw->fw1_state = FW1_STATE_OK;
21158c2ecf20Sopenharmony_ci
21168c2ecf20Sopenharmony_ci	if (!pvr2_hdw_dev_ok(hdw)) return;
21178c2ecf20Sopenharmony_ci
21188c2ecf20Sopenharmony_ci	hdw->force_dirty = !0;
21198c2ecf20Sopenharmony_ci
21208c2ecf20Sopenharmony_ci	if (!hdw->hdw_desc->flag_no_powerup) {
21218c2ecf20Sopenharmony_ci		pvr2_hdw_cmd_powerup(hdw);
21228c2ecf20Sopenharmony_ci		if (!pvr2_hdw_dev_ok(hdw)) return;
21238c2ecf20Sopenharmony_ci	}
21248c2ecf20Sopenharmony_ci
21258c2ecf20Sopenharmony_ci	/* Take the IR chip out of reset, if appropriate */
21268c2ecf20Sopenharmony_ci	if (hdw->ir_scheme_active == PVR2_IR_SCHEME_ZILOG) {
21278c2ecf20Sopenharmony_ci		pvr2_issue_simple_cmd(hdw,
21288c2ecf20Sopenharmony_ci				      FX2CMD_HCW_ZILOG_RESET |
21298c2ecf20Sopenharmony_ci				      (1 << 8) |
21308c2ecf20Sopenharmony_ci				      ((0) << 16));
21318c2ecf20Sopenharmony_ci	}
21328c2ecf20Sopenharmony_ci
21338c2ecf20Sopenharmony_ci	/* This step MUST happen after the earlier powerup step */
21348c2ecf20Sopenharmony_ci	pvr2_i2c_core_init(hdw);
21358c2ecf20Sopenharmony_ci	if (!pvr2_hdw_dev_ok(hdw)) return;
21368c2ecf20Sopenharmony_ci
21378c2ecf20Sopenharmony_ci	/* Reset demod only on Hauppauge 160xxx platform */
21388c2ecf20Sopenharmony_ci	if (le16_to_cpu(hdw->usb_dev->descriptor.idVendor) == 0x2040 &&
21398c2ecf20Sopenharmony_ci	    (le16_to_cpu(hdw->usb_dev->descriptor.idProduct) == 0x7502 ||
21408c2ecf20Sopenharmony_ci	     le16_to_cpu(hdw->usb_dev->descriptor.idProduct) == 0x7510)) {
21418c2ecf20Sopenharmony_ci		pr_info("%s(): resetting 160xxx demod\n", __func__);
21428c2ecf20Sopenharmony_ci		/* TODO: not sure this is proper place to reset once only */
21438c2ecf20Sopenharmony_ci		pvr2_issue_simple_cmd(hdw,
21448c2ecf20Sopenharmony_ci				      FX2CMD_HCW_DEMOD_RESET_PIN |
21458c2ecf20Sopenharmony_ci				      (1 << 8) |
21468c2ecf20Sopenharmony_ci				      ((0) << 16));
21478c2ecf20Sopenharmony_ci		usleep_range(10000, 10500);
21488c2ecf20Sopenharmony_ci		pvr2_issue_simple_cmd(hdw,
21498c2ecf20Sopenharmony_ci				      FX2CMD_HCW_DEMOD_RESET_PIN |
21508c2ecf20Sopenharmony_ci				      (1 << 8) |
21518c2ecf20Sopenharmony_ci				      ((1) << 16));
21528c2ecf20Sopenharmony_ci		usleep_range(10000, 10500);
21538c2ecf20Sopenharmony_ci	}
21548c2ecf20Sopenharmony_ci
21558c2ecf20Sopenharmony_ci	pvr2_hdw_load_modules(hdw);
21568c2ecf20Sopenharmony_ci	if (!pvr2_hdw_dev_ok(hdw)) return;
21578c2ecf20Sopenharmony_ci
21588c2ecf20Sopenharmony_ci	v4l2_device_call_all(&hdw->v4l2_dev, 0, core, load_fw);
21598c2ecf20Sopenharmony_ci
21608c2ecf20Sopenharmony_ci	for (idx = 0; idx < CTRLDEF_COUNT; idx++) {
21618c2ecf20Sopenharmony_ci		cptr = hdw->controls + idx;
21628c2ecf20Sopenharmony_ci		if (cptr->info->skip_init) continue;
21638c2ecf20Sopenharmony_ci		if (!cptr->info->set_value) continue;
21648c2ecf20Sopenharmony_ci		cptr->info->set_value(cptr,~0,cptr->info->default_value);
21658c2ecf20Sopenharmony_ci	}
21668c2ecf20Sopenharmony_ci
21678c2ecf20Sopenharmony_ci	pvr2_hdw_cx25840_vbi_hack(hdw);
21688c2ecf20Sopenharmony_ci
21698c2ecf20Sopenharmony_ci	/* Set up special default values for the television and radio
21708c2ecf20Sopenharmony_ci	   frequencies here.  It's not really important what these defaults
21718c2ecf20Sopenharmony_ci	   are, but I set them to something usable in the Chicago area just
21728c2ecf20Sopenharmony_ci	   to make driver testing a little easier. */
21738c2ecf20Sopenharmony_ci
21748c2ecf20Sopenharmony_ci	hdw->freqValTelevision = default_tv_freq;
21758c2ecf20Sopenharmony_ci	hdw->freqValRadio = default_radio_freq;
21768c2ecf20Sopenharmony_ci
21778c2ecf20Sopenharmony_ci	// Do not use pvr2_reset_ctl_endpoints() here.  It is not
21788c2ecf20Sopenharmony_ci	// thread-safe against the normal pvr2_send_request() mechanism.
21798c2ecf20Sopenharmony_ci	// (We should make it thread safe).
21808c2ecf20Sopenharmony_ci
21818c2ecf20Sopenharmony_ci	if (hdw->hdw_desc->flag_has_hauppauge_rom) {
21828c2ecf20Sopenharmony_ci		ret = pvr2_hdw_get_eeprom_addr(hdw);
21838c2ecf20Sopenharmony_ci		if (!pvr2_hdw_dev_ok(hdw)) return;
21848c2ecf20Sopenharmony_ci		if (ret < 0) {
21858c2ecf20Sopenharmony_ci			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
21868c2ecf20Sopenharmony_ci				   "Unable to determine location of eeprom, skipping");
21878c2ecf20Sopenharmony_ci		} else {
21888c2ecf20Sopenharmony_ci			hdw->eeprom_addr = ret;
21898c2ecf20Sopenharmony_ci			pvr2_eeprom_analyze(hdw);
21908c2ecf20Sopenharmony_ci			if (!pvr2_hdw_dev_ok(hdw)) return;
21918c2ecf20Sopenharmony_ci		}
21928c2ecf20Sopenharmony_ci	} else {
21938c2ecf20Sopenharmony_ci		hdw->tuner_type = hdw->hdw_desc->default_tuner_type;
21948c2ecf20Sopenharmony_ci		hdw->tuner_updated = !0;
21958c2ecf20Sopenharmony_ci		hdw->std_mask_eeprom = V4L2_STD_ALL;
21968c2ecf20Sopenharmony_ci	}
21978c2ecf20Sopenharmony_ci
21988c2ecf20Sopenharmony_ci	if (hdw->serial_number) {
21998c2ecf20Sopenharmony_ci		idx = scnprintf(hdw->identifier, sizeof(hdw->identifier) - 1,
22008c2ecf20Sopenharmony_ci				"sn-%lu", hdw->serial_number);
22018c2ecf20Sopenharmony_ci	} else if (hdw->unit_number >= 0) {
22028c2ecf20Sopenharmony_ci		idx = scnprintf(hdw->identifier, sizeof(hdw->identifier) - 1,
22038c2ecf20Sopenharmony_ci				"unit-%c",
22048c2ecf20Sopenharmony_ci				hdw->unit_number + 'a');
22058c2ecf20Sopenharmony_ci	} else {
22068c2ecf20Sopenharmony_ci		idx = scnprintf(hdw->identifier, sizeof(hdw->identifier) - 1,
22078c2ecf20Sopenharmony_ci				"unit-??");
22088c2ecf20Sopenharmony_ci	}
22098c2ecf20Sopenharmony_ci	hdw->identifier[idx] = 0;
22108c2ecf20Sopenharmony_ci
22118c2ecf20Sopenharmony_ci	pvr2_hdw_setup_std(hdw);
22128c2ecf20Sopenharmony_ci
22138c2ecf20Sopenharmony_ci	if (!get_default_tuner_type(hdw)) {
22148c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INIT,
22158c2ecf20Sopenharmony_ci			   "pvr2_hdw_setup: Tuner type overridden to %d",
22168c2ecf20Sopenharmony_ci			   hdw->tuner_type);
22178c2ecf20Sopenharmony_ci	}
22188c2ecf20Sopenharmony_ci
22198c2ecf20Sopenharmony_ci
22208c2ecf20Sopenharmony_ci	if (!pvr2_hdw_dev_ok(hdw)) return;
22218c2ecf20Sopenharmony_ci
22228c2ecf20Sopenharmony_ci	if (hdw->hdw_desc->signal_routing_scheme ==
22238c2ecf20Sopenharmony_ci	    PVR2_ROUTING_SCHEME_GOTVIEW) {
22248c2ecf20Sopenharmony_ci		/* Ensure that GPIO 11 is set to output for GOTVIEW
22258c2ecf20Sopenharmony_ci		   hardware. */
22268c2ecf20Sopenharmony_ci		pvr2_hdw_gpio_chg_dir(hdw,(1 << 11),~0);
22278c2ecf20Sopenharmony_ci	}
22288c2ecf20Sopenharmony_ci
22298c2ecf20Sopenharmony_ci	pvr2_hdw_commit_setup(hdw);
22308c2ecf20Sopenharmony_ci
22318c2ecf20Sopenharmony_ci	hdw->vid_stream = pvr2_stream_create();
22328c2ecf20Sopenharmony_ci	if (!pvr2_hdw_dev_ok(hdw)) return;
22338c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INIT,
22348c2ecf20Sopenharmony_ci		   "pvr2_hdw_setup: video stream is %p",hdw->vid_stream);
22358c2ecf20Sopenharmony_ci	if (hdw->vid_stream) {
22368c2ecf20Sopenharmony_ci		idx = get_default_error_tolerance(hdw);
22378c2ecf20Sopenharmony_ci		if (idx) {
22388c2ecf20Sopenharmony_ci			pvr2_trace(PVR2_TRACE_INIT,
22398c2ecf20Sopenharmony_ci				   "pvr2_hdw_setup: video stream %p setting tolerance %u",
22408c2ecf20Sopenharmony_ci				   hdw->vid_stream,idx);
22418c2ecf20Sopenharmony_ci		}
22428c2ecf20Sopenharmony_ci		pvr2_stream_setup(hdw->vid_stream,hdw->usb_dev,
22438c2ecf20Sopenharmony_ci				  PVR2_VID_ENDPOINT,idx);
22448c2ecf20Sopenharmony_ci	}
22458c2ecf20Sopenharmony_ci
22468c2ecf20Sopenharmony_ci	if (!pvr2_hdw_dev_ok(hdw)) return;
22478c2ecf20Sopenharmony_ci
22488c2ecf20Sopenharmony_ci	hdw->flag_init_ok = !0;
22498c2ecf20Sopenharmony_ci
22508c2ecf20Sopenharmony_ci	pvr2_hdw_state_sched(hdw);
22518c2ecf20Sopenharmony_ci}
22528c2ecf20Sopenharmony_ci
22538c2ecf20Sopenharmony_ci
22548c2ecf20Sopenharmony_ci/* Set up the structure and attempt to put the device into a usable state.
22558c2ecf20Sopenharmony_ci   This can be a time-consuming operation, which is why it is not done
22568c2ecf20Sopenharmony_ci   internally as part of the create() step. */
22578c2ecf20Sopenharmony_cistatic void pvr2_hdw_setup(struct pvr2_hdw *hdw)
22588c2ecf20Sopenharmony_ci{
22598c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_setup(hdw=%p) begin",hdw);
22608c2ecf20Sopenharmony_ci	do {
22618c2ecf20Sopenharmony_ci		pvr2_hdw_setup_low(hdw);
22628c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INIT,
22638c2ecf20Sopenharmony_ci			   "pvr2_hdw_setup(hdw=%p) done, ok=%d init_ok=%d",
22648c2ecf20Sopenharmony_ci			   hdw,pvr2_hdw_dev_ok(hdw),hdw->flag_init_ok);
22658c2ecf20Sopenharmony_ci		if (pvr2_hdw_dev_ok(hdw)) {
22668c2ecf20Sopenharmony_ci			if (hdw->flag_init_ok) {
22678c2ecf20Sopenharmony_ci				pvr2_trace(
22688c2ecf20Sopenharmony_ci					PVR2_TRACE_INFO,
22698c2ecf20Sopenharmony_ci					"Device initialization completed successfully.");
22708c2ecf20Sopenharmony_ci				break;
22718c2ecf20Sopenharmony_ci			}
22728c2ecf20Sopenharmony_ci			if (hdw->fw1_state == FW1_STATE_RELOAD) {
22738c2ecf20Sopenharmony_ci				pvr2_trace(
22748c2ecf20Sopenharmony_ci					PVR2_TRACE_INFO,
22758c2ecf20Sopenharmony_ci					"Device microcontroller firmware (re)loaded; it should now reset and reconnect.");
22768c2ecf20Sopenharmony_ci				break;
22778c2ecf20Sopenharmony_ci			}
22788c2ecf20Sopenharmony_ci			pvr2_trace(
22798c2ecf20Sopenharmony_ci				PVR2_TRACE_ERROR_LEGS,
22808c2ecf20Sopenharmony_ci				"Device initialization was not successful.");
22818c2ecf20Sopenharmony_ci			if (hdw->fw1_state == FW1_STATE_MISSING) {
22828c2ecf20Sopenharmony_ci				pvr2_trace(
22838c2ecf20Sopenharmony_ci					PVR2_TRACE_ERROR_LEGS,
22848c2ecf20Sopenharmony_ci					"Giving up since device microcontroller firmware appears to be missing.");
22858c2ecf20Sopenharmony_ci				break;
22868c2ecf20Sopenharmony_ci			}
22878c2ecf20Sopenharmony_ci		}
22888c2ecf20Sopenharmony_ci		if (hdw->flag_modulefail) {
22898c2ecf20Sopenharmony_ci			pvr2_trace(
22908c2ecf20Sopenharmony_ci				PVR2_TRACE_ERROR_LEGS,
22918c2ecf20Sopenharmony_ci				"***WARNING*** pvrusb2 driver initialization failed due to the failure of one or more sub-device kernel modules.");
22928c2ecf20Sopenharmony_ci			pvr2_trace(
22938c2ecf20Sopenharmony_ci				PVR2_TRACE_ERROR_LEGS,
22948c2ecf20Sopenharmony_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.");
22958c2ecf20Sopenharmony_ci			break;
22968c2ecf20Sopenharmony_ci		}
22978c2ecf20Sopenharmony_ci		if (procreload) {
22988c2ecf20Sopenharmony_ci			pvr2_trace(
22998c2ecf20Sopenharmony_ci				PVR2_TRACE_ERROR_LEGS,
23008c2ecf20Sopenharmony_ci				"Attempting pvrusb2 recovery by reloading primary firmware.");
23018c2ecf20Sopenharmony_ci			pvr2_trace(
23028c2ecf20Sopenharmony_ci				PVR2_TRACE_ERROR_LEGS,
23038c2ecf20Sopenharmony_ci				"If this works, device should disconnect and reconnect in a sane state.");
23048c2ecf20Sopenharmony_ci			hdw->fw1_state = FW1_STATE_UNKNOWN;
23058c2ecf20Sopenharmony_ci			pvr2_upload_firmware1(hdw);
23068c2ecf20Sopenharmony_ci		} else {
23078c2ecf20Sopenharmony_ci			pvr2_trace(
23088c2ecf20Sopenharmony_ci				PVR2_TRACE_ERROR_LEGS,
23098c2ecf20Sopenharmony_ci				"***WARNING*** pvrusb2 device hardware appears to be jammed and I can't clear it.");
23108c2ecf20Sopenharmony_ci			pvr2_trace(
23118c2ecf20Sopenharmony_ci				PVR2_TRACE_ERROR_LEGS,
23128c2ecf20Sopenharmony_ci				"You might need to power cycle the pvrusb2 device in order to recover.");
23138c2ecf20Sopenharmony_ci		}
23148c2ecf20Sopenharmony_ci	} while (0);
23158c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_setup(hdw=%p) end",hdw);
23168c2ecf20Sopenharmony_ci}
23178c2ecf20Sopenharmony_ci
23188c2ecf20Sopenharmony_ci
23198c2ecf20Sopenharmony_ci/* Perform second stage initialization.  Set callback pointer first so that
23208c2ecf20Sopenharmony_ci   we can avoid a possible initialization race (if the kernel thread runs
23218c2ecf20Sopenharmony_ci   before the callback has been set). */
23228c2ecf20Sopenharmony_ciint pvr2_hdw_initialize(struct pvr2_hdw *hdw,
23238c2ecf20Sopenharmony_ci			void (*callback_func)(void *),
23248c2ecf20Sopenharmony_ci			void *callback_data)
23258c2ecf20Sopenharmony_ci{
23268c2ecf20Sopenharmony_ci	LOCK_TAKE(hdw->big_lock); do {
23278c2ecf20Sopenharmony_ci		if (hdw->flag_disconnected) {
23288c2ecf20Sopenharmony_ci			/* Handle a race here: If we're already
23298c2ecf20Sopenharmony_ci			   disconnected by this point, then give up.  If we
23308c2ecf20Sopenharmony_ci			   get past this then we'll remain connected for
23318c2ecf20Sopenharmony_ci			   the duration of initialization since the entire
23328c2ecf20Sopenharmony_ci			   initialization sequence is now protected by the
23338c2ecf20Sopenharmony_ci			   big_lock. */
23348c2ecf20Sopenharmony_ci			break;
23358c2ecf20Sopenharmony_ci		}
23368c2ecf20Sopenharmony_ci		hdw->state_data = callback_data;
23378c2ecf20Sopenharmony_ci		hdw->state_func = callback_func;
23388c2ecf20Sopenharmony_ci		pvr2_hdw_setup(hdw);
23398c2ecf20Sopenharmony_ci	} while (0); LOCK_GIVE(hdw->big_lock);
23408c2ecf20Sopenharmony_ci	return hdw->flag_init_ok;
23418c2ecf20Sopenharmony_ci}
23428c2ecf20Sopenharmony_ci
23438c2ecf20Sopenharmony_ci
23448c2ecf20Sopenharmony_ci/* Create, set up, and return a structure for interacting with the
23458c2ecf20Sopenharmony_ci   underlying hardware.  */
23468c2ecf20Sopenharmony_cistruct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
23478c2ecf20Sopenharmony_ci				 const struct usb_device_id *devid)
23488c2ecf20Sopenharmony_ci{
23498c2ecf20Sopenharmony_ci	unsigned int idx,cnt1,cnt2,m;
23508c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = NULL;
23518c2ecf20Sopenharmony_ci	int valid_std_mask;
23528c2ecf20Sopenharmony_ci	struct pvr2_ctrl *cptr;
23538c2ecf20Sopenharmony_ci	struct usb_device *usb_dev;
23548c2ecf20Sopenharmony_ci	const struct pvr2_device_desc *hdw_desc;
23558c2ecf20Sopenharmony_ci	__u8 ifnum;
23568c2ecf20Sopenharmony_ci	struct v4l2_queryctrl qctrl;
23578c2ecf20Sopenharmony_ci	struct pvr2_ctl_info *ciptr;
23588c2ecf20Sopenharmony_ci
23598c2ecf20Sopenharmony_ci	usb_dev = interface_to_usbdev(intf);
23608c2ecf20Sopenharmony_ci
23618c2ecf20Sopenharmony_ci	hdw_desc = (const struct pvr2_device_desc *)(devid->driver_info);
23628c2ecf20Sopenharmony_ci
23638c2ecf20Sopenharmony_ci	if (hdw_desc == NULL) {
23648c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INIT, "pvr2_hdw_create: No device description pointer, unable to continue.");
23658c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INIT,
23668c2ecf20Sopenharmony_ci			   "If you have a new device type, please contact Mike Isely <isely@pobox.com> to get it included in the driver");
23678c2ecf20Sopenharmony_ci		goto fail;
23688c2ecf20Sopenharmony_ci	}
23698c2ecf20Sopenharmony_ci
23708c2ecf20Sopenharmony_ci	hdw = kzalloc(sizeof(*hdw),GFP_KERNEL);
23718c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_create: hdw=%p, type \"%s\"",
23728c2ecf20Sopenharmony_ci		   hdw,hdw_desc->description);
23738c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INFO, "Hardware description: %s",
23748c2ecf20Sopenharmony_ci		hdw_desc->description);
23758c2ecf20Sopenharmony_ci	if (hdw_desc->flag_is_experimental) {
23768c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INFO, "**********");
23778c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INFO,
23788c2ecf20Sopenharmony_ci			   "***WARNING*** Support for this device (%s) is experimental.",
23798c2ecf20Sopenharmony_ci							      hdw_desc->description);
23808c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INFO,
23818c2ecf20Sopenharmony_ci			   "Important functionality might not be entirely working.");
23828c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INFO,
23838c2ecf20Sopenharmony_ci			   "Please consider contacting the driver author to help with further stabilization of the driver.");
23848c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INFO, "**********");
23858c2ecf20Sopenharmony_ci	}
23868c2ecf20Sopenharmony_ci	if (!hdw) goto fail;
23878c2ecf20Sopenharmony_ci
23888c2ecf20Sopenharmony_ci	timer_setup(&hdw->quiescent_timer, pvr2_hdw_quiescent_timeout, 0);
23898c2ecf20Sopenharmony_ci
23908c2ecf20Sopenharmony_ci	timer_setup(&hdw->decoder_stabilization_timer,
23918c2ecf20Sopenharmony_ci		    pvr2_hdw_decoder_stabilization_timeout, 0);
23928c2ecf20Sopenharmony_ci
23938c2ecf20Sopenharmony_ci	timer_setup(&hdw->encoder_wait_timer, pvr2_hdw_encoder_wait_timeout,
23948c2ecf20Sopenharmony_ci		    0);
23958c2ecf20Sopenharmony_ci
23968c2ecf20Sopenharmony_ci	timer_setup(&hdw->encoder_run_timer, pvr2_hdw_encoder_run_timeout, 0);
23978c2ecf20Sopenharmony_ci
23988c2ecf20Sopenharmony_ci	hdw->master_state = PVR2_STATE_DEAD;
23998c2ecf20Sopenharmony_ci
24008c2ecf20Sopenharmony_ci	init_waitqueue_head(&hdw->state_wait_data);
24018c2ecf20Sopenharmony_ci
24028c2ecf20Sopenharmony_ci	hdw->tuner_signal_stale = !0;
24038c2ecf20Sopenharmony_ci	cx2341x_fill_defaults(&hdw->enc_ctl_state);
24048c2ecf20Sopenharmony_ci
24058c2ecf20Sopenharmony_ci	/* Calculate which inputs are OK */
24068c2ecf20Sopenharmony_ci	m = 0;
24078c2ecf20Sopenharmony_ci	if (hdw_desc->flag_has_analogtuner) m |= 1 << PVR2_CVAL_INPUT_TV;
24088c2ecf20Sopenharmony_ci	if (hdw_desc->digital_control_scheme != PVR2_DIGITAL_SCHEME_NONE) {
24098c2ecf20Sopenharmony_ci		m |= 1 << PVR2_CVAL_INPUT_DTV;
24108c2ecf20Sopenharmony_ci	}
24118c2ecf20Sopenharmony_ci	if (hdw_desc->flag_has_svideo) m |= 1 << PVR2_CVAL_INPUT_SVIDEO;
24128c2ecf20Sopenharmony_ci	if (hdw_desc->flag_has_composite) m |= 1 << PVR2_CVAL_INPUT_COMPOSITE;
24138c2ecf20Sopenharmony_ci	if (hdw_desc->flag_has_fmradio) m |= 1 << PVR2_CVAL_INPUT_RADIO;
24148c2ecf20Sopenharmony_ci	hdw->input_avail_mask = m;
24158c2ecf20Sopenharmony_ci	hdw->input_allowed_mask = hdw->input_avail_mask;
24168c2ecf20Sopenharmony_ci
24178c2ecf20Sopenharmony_ci	/* If not a hybrid device, pathway_state never changes.  So
24188c2ecf20Sopenharmony_ci	   initialize it here to what it should forever be. */
24198c2ecf20Sopenharmony_ci	if (!(hdw->input_avail_mask & (1 << PVR2_CVAL_INPUT_DTV))) {
24208c2ecf20Sopenharmony_ci		hdw->pathway_state = PVR2_PATHWAY_ANALOG;
24218c2ecf20Sopenharmony_ci	} else if (!(hdw->input_avail_mask & (1 << PVR2_CVAL_INPUT_TV))) {
24228c2ecf20Sopenharmony_ci		hdw->pathway_state = PVR2_PATHWAY_DIGITAL;
24238c2ecf20Sopenharmony_ci	}
24248c2ecf20Sopenharmony_ci
24258c2ecf20Sopenharmony_ci	hdw->control_cnt = CTRLDEF_COUNT;
24268c2ecf20Sopenharmony_ci	hdw->control_cnt += MPEGDEF_COUNT;
24278c2ecf20Sopenharmony_ci	hdw->controls = kcalloc(hdw->control_cnt, sizeof(struct pvr2_ctrl),
24288c2ecf20Sopenharmony_ci				GFP_KERNEL);
24298c2ecf20Sopenharmony_ci	if (!hdw->controls) goto fail;
24308c2ecf20Sopenharmony_ci	hdw->hdw_desc = hdw_desc;
24318c2ecf20Sopenharmony_ci	hdw->ir_scheme_active = hdw->hdw_desc->ir_scheme;
24328c2ecf20Sopenharmony_ci	for (idx = 0; idx < hdw->control_cnt; idx++) {
24338c2ecf20Sopenharmony_ci		cptr = hdw->controls + idx;
24348c2ecf20Sopenharmony_ci		cptr->hdw = hdw;
24358c2ecf20Sopenharmony_ci	}
24368c2ecf20Sopenharmony_ci	for (idx = 0; idx < 32; idx++) {
24378c2ecf20Sopenharmony_ci		hdw->std_mask_ptrs[idx] = hdw->std_mask_names[idx];
24388c2ecf20Sopenharmony_ci	}
24398c2ecf20Sopenharmony_ci	for (idx = 0; idx < CTRLDEF_COUNT; idx++) {
24408c2ecf20Sopenharmony_ci		cptr = hdw->controls + idx;
24418c2ecf20Sopenharmony_ci		cptr->info = control_defs+idx;
24428c2ecf20Sopenharmony_ci	}
24438c2ecf20Sopenharmony_ci
24448c2ecf20Sopenharmony_ci	/* Ensure that default input choice is a valid one. */
24458c2ecf20Sopenharmony_ci	m = hdw->input_avail_mask;
24468c2ecf20Sopenharmony_ci	if (m) for (idx = 0; idx < (sizeof(m) << 3); idx++) {
24478c2ecf20Sopenharmony_ci		if (!((1UL << idx) & m)) continue;
24488c2ecf20Sopenharmony_ci		hdw->input_val = idx;
24498c2ecf20Sopenharmony_ci		break;
24508c2ecf20Sopenharmony_ci	}
24518c2ecf20Sopenharmony_ci
24528c2ecf20Sopenharmony_ci	/* Define and configure additional controls from cx2341x module. */
24538c2ecf20Sopenharmony_ci	hdw->mpeg_ctrl_info = kcalloc(MPEGDEF_COUNT,
24548c2ecf20Sopenharmony_ci				      sizeof(*(hdw->mpeg_ctrl_info)),
24558c2ecf20Sopenharmony_ci				      GFP_KERNEL);
24568c2ecf20Sopenharmony_ci	if (!hdw->mpeg_ctrl_info) goto fail;
24578c2ecf20Sopenharmony_ci	for (idx = 0; idx < MPEGDEF_COUNT; idx++) {
24588c2ecf20Sopenharmony_ci		cptr = hdw->controls + idx + CTRLDEF_COUNT;
24598c2ecf20Sopenharmony_ci		ciptr = &(hdw->mpeg_ctrl_info[idx].info);
24608c2ecf20Sopenharmony_ci		ciptr->desc = hdw->mpeg_ctrl_info[idx].desc;
24618c2ecf20Sopenharmony_ci		ciptr->name = mpeg_ids[idx].strid;
24628c2ecf20Sopenharmony_ci		ciptr->v4l_id = mpeg_ids[idx].id;
24638c2ecf20Sopenharmony_ci		ciptr->skip_init = !0;
24648c2ecf20Sopenharmony_ci		ciptr->get_value = ctrl_cx2341x_get;
24658c2ecf20Sopenharmony_ci		ciptr->get_v4lflags = ctrl_cx2341x_getv4lflags;
24668c2ecf20Sopenharmony_ci		ciptr->is_dirty = ctrl_cx2341x_is_dirty;
24678c2ecf20Sopenharmony_ci		if (!idx) ciptr->clear_dirty = ctrl_cx2341x_clear_dirty;
24688c2ecf20Sopenharmony_ci		qctrl.id = ciptr->v4l_id;
24698c2ecf20Sopenharmony_ci		cx2341x_ctrl_query(&hdw->enc_ctl_state,&qctrl);
24708c2ecf20Sopenharmony_ci		if (!(qctrl.flags & V4L2_CTRL_FLAG_READ_ONLY)) {
24718c2ecf20Sopenharmony_ci			ciptr->set_value = ctrl_cx2341x_set;
24728c2ecf20Sopenharmony_ci		}
24738c2ecf20Sopenharmony_ci		strscpy(hdw->mpeg_ctrl_info[idx].desc, qctrl.name,
24748c2ecf20Sopenharmony_ci			sizeof(hdw->mpeg_ctrl_info[idx].desc));
24758c2ecf20Sopenharmony_ci		ciptr->default_value = qctrl.default_value;
24768c2ecf20Sopenharmony_ci		switch (qctrl.type) {
24778c2ecf20Sopenharmony_ci		default:
24788c2ecf20Sopenharmony_ci		case V4L2_CTRL_TYPE_INTEGER:
24798c2ecf20Sopenharmony_ci			ciptr->type = pvr2_ctl_int;
24808c2ecf20Sopenharmony_ci			ciptr->def.type_int.min_value = qctrl.minimum;
24818c2ecf20Sopenharmony_ci			ciptr->def.type_int.max_value = qctrl.maximum;
24828c2ecf20Sopenharmony_ci			break;
24838c2ecf20Sopenharmony_ci		case V4L2_CTRL_TYPE_BOOLEAN:
24848c2ecf20Sopenharmony_ci			ciptr->type = pvr2_ctl_bool;
24858c2ecf20Sopenharmony_ci			break;
24868c2ecf20Sopenharmony_ci		case V4L2_CTRL_TYPE_MENU:
24878c2ecf20Sopenharmony_ci			ciptr->type = pvr2_ctl_enum;
24888c2ecf20Sopenharmony_ci			ciptr->def.type_enum.value_names =
24898c2ecf20Sopenharmony_ci				cx2341x_ctrl_get_menu(&hdw->enc_ctl_state,
24908c2ecf20Sopenharmony_ci								ciptr->v4l_id);
24918c2ecf20Sopenharmony_ci			for (cnt1 = 0;
24928c2ecf20Sopenharmony_ci			     ciptr->def.type_enum.value_names[cnt1] != NULL;
24938c2ecf20Sopenharmony_ci			     cnt1++) { }
24948c2ecf20Sopenharmony_ci			ciptr->def.type_enum.count = cnt1;
24958c2ecf20Sopenharmony_ci			break;
24968c2ecf20Sopenharmony_ci		}
24978c2ecf20Sopenharmony_ci		cptr->info = ciptr;
24988c2ecf20Sopenharmony_ci	}
24998c2ecf20Sopenharmony_ci
25008c2ecf20Sopenharmony_ci	// Initialize control data regarding video standard masks
25018c2ecf20Sopenharmony_ci	valid_std_mask = pvr2_std_get_usable();
25028c2ecf20Sopenharmony_ci	for (idx = 0; idx < 32; idx++) {
25038c2ecf20Sopenharmony_ci		if (!(valid_std_mask & (1UL << idx))) continue;
25048c2ecf20Sopenharmony_ci		cnt1 = pvr2_std_id_to_str(
25058c2ecf20Sopenharmony_ci			hdw->std_mask_names[idx],
25068c2ecf20Sopenharmony_ci			sizeof(hdw->std_mask_names[idx])-1,
25078c2ecf20Sopenharmony_ci			1UL << idx);
25088c2ecf20Sopenharmony_ci		hdw->std_mask_names[idx][cnt1] = 0;
25098c2ecf20Sopenharmony_ci	}
25108c2ecf20Sopenharmony_ci	cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDAVAIL);
25118c2ecf20Sopenharmony_ci	if (cptr) {
25128c2ecf20Sopenharmony_ci		memcpy(&hdw->std_info_avail,cptr->info,
25138c2ecf20Sopenharmony_ci		       sizeof(hdw->std_info_avail));
25148c2ecf20Sopenharmony_ci		cptr->info = &hdw->std_info_avail;
25158c2ecf20Sopenharmony_ci		hdw->std_info_avail.def.type_bitmask.bit_names =
25168c2ecf20Sopenharmony_ci			hdw->std_mask_ptrs;
25178c2ecf20Sopenharmony_ci		hdw->std_info_avail.def.type_bitmask.valid_bits =
25188c2ecf20Sopenharmony_ci			valid_std_mask;
25198c2ecf20Sopenharmony_ci	}
25208c2ecf20Sopenharmony_ci	cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR);
25218c2ecf20Sopenharmony_ci	if (cptr) {
25228c2ecf20Sopenharmony_ci		memcpy(&hdw->std_info_cur,cptr->info,
25238c2ecf20Sopenharmony_ci		       sizeof(hdw->std_info_cur));
25248c2ecf20Sopenharmony_ci		cptr->info = &hdw->std_info_cur;
25258c2ecf20Sopenharmony_ci		hdw->std_info_cur.def.type_bitmask.bit_names =
25268c2ecf20Sopenharmony_ci			hdw->std_mask_ptrs;
25278c2ecf20Sopenharmony_ci		hdw->std_info_cur.def.type_bitmask.valid_bits =
25288c2ecf20Sopenharmony_ci			valid_std_mask;
25298c2ecf20Sopenharmony_ci	}
25308c2ecf20Sopenharmony_ci	cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDDETECT);
25318c2ecf20Sopenharmony_ci	if (cptr) {
25328c2ecf20Sopenharmony_ci		memcpy(&hdw->std_info_detect,cptr->info,
25338c2ecf20Sopenharmony_ci		       sizeof(hdw->std_info_detect));
25348c2ecf20Sopenharmony_ci		cptr->info = &hdw->std_info_detect;
25358c2ecf20Sopenharmony_ci		hdw->std_info_detect.def.type_bitmask.bit_names =
25368c2ecf20Sopenharmony_ci			hdw->std_mask_ptrs;
25378c2ecf20Sopenharmony_ci		hdw->std_info_detect.def.type_bitmask.valid_bits =
25388c2ecf20Sopenharmony_ci			valid_std_mask;
25398c2ecf20Sopenharmony_ci	}
25408c2ecf20Sopenharmony_ci
25418c2ecf20Sopenharmony_ci	hdw->cropcap_stale = !0;
25428c2ecf20Sopenharmony_ci	hdw->eeprom_addr = -1;
25438c2ecf20Sopenharmony_ci	hdw->unit_number = -1;
25448c2ecf20Sopenharmony_ci	hdw->v4l_minor_number_video = -1;
25458c2ecf20Sopenharmony_ci	hdw->v4l_minor_number_vbi = -1;
25468c2ecf20Sopenharmony_ci	hdw->v4l_minor_number_radio = -1;
25478c2ecf20Sopenharmony_ci	hdw->ctl_write_buffer = kmalloc(PVR2_CTL_BUFFSIZE,GFP_KERNEL);
25488c2ecf20Sopenharmony_ci	if (!hdw->ctl_write_buffer) goto fail;
25498c2ecf20Sopenharmony_ci	hdw->ctl_read_buffer = kmalloc(PVR2_CTL_BUFFSIZE,GFP_KERNEL);
25508c2ecf20Sopenharmony_ci	if (!hdw->ctl_read_buffer) goto fail;
25518c2ecf20Sopenharmony_ci	hdw->ctl_write_urb = usb_alloc_urb(0,GFP_KERNEL);
25528c2ecf20Sopenharmony_ci	if (!hdw->ctl_write_urb) goto fail;
25538c2ecf20Sopenharmony_ci	hdw->ctl_read_urb = usb_alloc_urb(0,GFP_KERNEL);
25548c2ecf20Sopenharmony_ci	if (!hdw->ctl_read_urb) goto fail;
25558c2ecf20Sopenharmony_ci
25568c2ecf20Sopenharmony_ci	if (v4l2_device_register(&intf->dev, &hdw->v4l2_dev) != 0) {
25578c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
25588c2ecf20Sopenharmony_ci			   "Error registering with v4l core, giving up");
25598c2ecf20Sopenharmony_ci		goto fail;
25608c2ecf20Sopenharmony_ci	}
25618c2ecf20Sopenharmony_ci	mutex_lock(&pvr2_unit_mtx);
25628c2ecf20Sopenharmony_ci	do {
25638c2ecf20Sopenharmony_ci		for (idx = 0; idx < PVR_NUM; idx++) {
25648c2ecf20Sopenharmony_ci			if (unit_pointers[idx]) continue;
25658c2ecf20Sopenharmony_ci			hdw->unit_number = idx;
25668c2ecf20Sopenharmony_ci			unit_pointers[idx] = hdw;
25678c2ecf20Sopenharmony_ci			break;
25688c2ecf20Sopenharmony_ci		}
25698c2ecf20Sopenharmony_ci	} while (0);
25708c2ecf20Sopenharmony_ci	mutex_unlock(&pvr2_unit_mtx);
25718c2ecf20Sopenharmony_ci
25728c2ecf20Sopenharmony_ci	INIT_WORK(&hdw->workpoll, pvr2_hdw_worker_poll);
25738c2ecf20Sopenharmony_ci
25748c2ecf20Sopenharmony_ci	if (hdw->unit_number == -1)
25758c2ecf20Sopenharmony_ci		goto fail;
25768c2ecf20Sopenharmony_ci
25778c2ecf20Sopenharmony_ci	cnt1 = 0;
25788c2ecf20Sopenharmony_ci	cnt2 = scnprintf(hdw->name+cnt1,sizeof(hdw->name)-cnt1,"pvrusb2");
25798c2ecf20Sopenharmony_ci	cnt1 += cnt2;
25808c2ecf20Sopenharmony_ci	if (hdw->unit_number >= 0) {
25818c2ecf20Sopenharmony_ci		cnt2 = scnprintf(hdw->name+cnt1,sizeof(hdw->name)-cnt1,"_%c",
25828c2ecf20Sopenharmony_ci				 ('a' + hdw->unit_number));
25838c2ecf20Sopenharmony_ci		cnt1 += cnt2;
25848c2ecf20Sopenharmony_ci	}
25858c2ecf20Sopenharmony_ci	if (cnt1 >= sizeof(hdw->name)) cnt1 = sizeof(hdw->name)-1;
25868c2ecf20Sopenharmony_ci	hdw->name[cnt1] = 0;
25878c2ecf20Sopenharmony_ci
25888c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INIT,"Driver unit number is %d, name is %s",
25898c2ecf20Sopenharmony_ci		   hdw->unit_number,hdw->name);
25908c2ecf20Sopenharmony_ci
25918c2ecf20Sopenharmony_ci	hdw->tuner_type = -1;
25928c2ecf20Sopenharmony_ci	hdw->flag_ok = !0;
25938c2ecf20Sopenharmony_ci
25948c2ecf20Sopenharmony_ci	hdw->usb_intf = intf;
25958c2ecf20Sopenharmony_ci	hdw->usb_dev = usb_dev;
25968c2ecf20Sopenharmony_ci
25978c2ecf20Sopenharmony_ci	usb_make_path(hdw->usb_dev, hdw->bus_info, sizeof(hdw->bus_info));
25988c2ecf20Sopenharmony_ci
25998c2ecf20Sopenharmony_ci	ifnum = hdw->usb_intf->cur_altsetting->desc.bInterfaceNumber;
26008c2ecf20Sopenharmony_ci	usb_set_interface(hdw->usb_dev,ifnum,0);
26018c2ecf20Sopenharmony_ci
26028c2ecf20Sopenharmony_ci	mutex_init(&hdw->ctl_lock_mutex);
26038c2ecf20Sopenharmony_ci	mutex_init(&hdw->big_lock_mutex);
26048c2ecf20Sopenharmony_ci
26058c2ecf20Sopenharmony_ci	return hdw;
26068c2ecf20Sopenharmony_ci fail:
26078c2ecf20Sopenharmony_ci	if (hdw) {
26088c2ecf20Sopenharmony_ci		del_timer_sync(&hdw->quiescent_timer);
26098c2ecf20Sopenharmony_ci		del_timer_sync(&hdw->decoder_stabilization_timer);
26108c2ecf20Sopenharmony_ci		del_timer_sync(&hdw->encoder_run_timer);
26118c2ecf20Sopenharmony_ci		del_timer_sync(&hdw->encoder_wait_timer);
26128c2ecf20Sopenharmony_ci		flush_work(&hdw->workpoll);
26138c2ecf20Sopenharmony_ci		v4l2_device_unregister(&hdw->v4l2_dev);
26148c2ecf20Sopenharmony_ci		usb_free_urb(hdw->ctl_read_urb);
26158c2ecf20Sopenharmony_ci		usb_free_urb(hdw->ctl_write_urb);
26168c2ecf20Sopenharmony_ci		kfree(hdw->ctl_read_buffer);
26178c2ecf20Sopenharmony_ci		kfree(hdw->ctl_write_buffer);
26188c2ecf20Sopenharmony_ci		kfree(hdw->controls);
26198c2ecf20Sopenharmony_ci		kfree(hdw->mpeg_ctrl_info);
26208c2ecf20Sopenharmony_ci		kfree(hdw);
26218c2ecf20Sopenharmony_ci	}
26228c2ecf20Sopenharmony_ci	return NULL;
26238c2ecf20Sopenharmony_ci}
26248c2ecf20Sopenharmony_ci
26258c2ecf20Sopenharmony_ci
26268c2ecf20Sopenharmony_ci/* Remove _all_ associations between this driver and the underlying USB
26278c2ecf20Sopenharmony_ci   layer. */
26288c2ecf20Sopenharmony_cistatic void pvr2_hdw_remove_usb_stuff(struct pvr2_hdw *hdw)
26298c2ecf20Sopenharmony_ci{
26308c2ecf20Sopenharmony_ci	if (hdw->flag_disconnected) return;
26318c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_remove_usb_stuff: hdw=%p",hdw);
26328c2ecf20Sopenharmony_ci	if (hdw->ctl_read_urb) {
26338c2ecf20Sopenharmony_ci		usb_kill_urb(hdw->ctl_read_urb);
26348c2ecf20Sopenharmony_ci		usb_free_urb(hdw->ctl_read_urb);
26358c2ecf20Sopenharmony_ci		hdw->ctl_read_urb = NULL;
26368c2ecf20Sopenharmony_ci	}
26378c2ecf20Sopenharmony_ci	if (hdw->ctl_write_urb) {
26388c2ecf20Sopenharmony_ci		usb_kill_urb(hdw->ctl_write_urb);
26398c2ecf20Sopenharmony_ci		usb_free_urb(hdw->ctl_write_urb);
26408c2ecf20Sopenharmony_ci		hdw->ctl_write_urb = NULL;
26418c2ecf20Sopenharmony_ci	}
26428c2ecf20Sopenharmony_ci	if (hdw->ctl_read_buffer) {
26438c2ecf20Sopenharmony_ci		kfree(hdw->ctl_read_buffer);
26448c2ecf20Sopenharmony_ci		hdw->ctl_read_buffer = NULL;
26458c2ecf20Sopenharmony_ci	}
26468c2ecf20Sopenharmony_ci	if (hdw->ctl_write_buffer) {
26478c2ecf20Sopenharmony_ci		kfree(hdw->ctl_write_buffer);
26488c2ecf20Sopenharmony_ci		hdw->ctl_write_buffer = NULL;
26498c2ecf20Sopenharmony_ci	}
26508c2ecf20Sopenharmony_ci	hdw->flag_disconnected = !0;
26518c2ecf20Sopenharmony_ci	/* If we don't do this, then there will be a dangling struct device
26528c2ecf20Sopenharmony_ci	   reference to our disappearing device persisting inside the V4L
26538c2ecf20Sopenharmony_ci	   core... */
26548c2ecf20Sopenharmony_ci	v4l2_device_disconnect(&hdw->v4l2_dev);
26558c2ecf20Sopenharmony_ci	hdw->usb_dev = NULL;
26568c2ecf20Sopenharmony_ci	hdw->usb_intf = NULL;
26578c2ecf20Sopenharmony_ci	pvr2_hdw_render_useless(hdw);
26588c2ecf20Sopenharmony_ci}
26598c2ecf20Sopenharmony_ci
26608c2ecf20Sopenharmony_civoid pvr2_hdw_set_v4l2_dev(struct pvr2_hdw *hdw, struct video_device *vdev)
26618c2ecf20Sopenharmony_ci{
26628c2ecf20Sopenharmony_ci	vdev->v4l2_dev = &hdw->v4l2_dev;
26638c2ecf20Sopenharmony_ci}
26648c2ecf20Sopenharmony_ci
26658c2ecf20Sopenharmony_ci/* Destroy hardware interaction structure */
26668c2ecf20Sopenharmony_civoid pvr2_hdw_destroy(struct pvr2_hdw *hdw)
26678c2ecf20Sopenharmony_ci{
26688c2ecf20Sopenharmony_ci	if (!hdw) return;
26698c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_destroy: hdw=%p",hdw);
26708c2ecf20Sopenharmony_ci	flush_work(&hdw->workpoll);
26718c2ecf20Sopenharmony_ci	del_timer_sync(&hdw->quiescent_timer);
26728c2ecf20Sopenharmony_ci	del_timer_sync(&hdw->decoder_stabilization_timer);
26738c2ecf20Sopenharmony_ci	del_timer_sync(&hdw->encoder_run_timer);
26748c2ecf20Sopenharmony_ci	del_timer_sync(&hdw->encoder_wait_timer);
26758c2ecf20Sopenharmony_ci	if (hdw->fw_buffer) {
26768c2ecf20Sopenharmony_ci		kfree(hdw->fw_buffer);
26778c2ecf20Sopenharmony_ci		hdw->fw_buffer = NULL;
26788c2ecf20Sopenharmony_ci	}
26798c2ecf20Sopenharmony_ci	if (hdw->vid_stream) {
26808c2ecf20Sopenharmony_ci		pvr2_stream_destroy(hdw->vid_stream);
26818c2ecf20Sopenharmony_ci		hdw->vid_stream = NULL;
26828c2ecf20Sopenharmony_ci	}
26838c2ecf20Sopenharmony_ci	v4l2_device_unregister(&hdw->v4l2_dev);
26848c2ecf20Sopenharmony_ci	pvr2_hdw_disconnect(hdw);
26858c2ecf20Sopenharmony_ci	mutex_lock(&pvr2_unit_mtx);
26868c2ecf20Sopenharmony_ci	do {
26878c2ecf20Sopenharmony_ci		if ((hdw->unit_number >= 0) &&
26888c2ecf20Sopenharmony_ci		    (hdw->unit_number < PVR_NUM) &&
26898c2ecf20Sopenharmony_ci		    (unit_pointers[hdw->unit_number] == hdw)) {
26908c2ecf20Sopenharmony_ci			unit_pointers[hdw->unit_number] = NULL;
26918c2ecf20Sopenharmony_ci		}
26928c2ecf20Sopenharmony_ci	} while (0);
26938c2ecf20Sopenharmony_ci	mutex_unlock(&pvr2_unit_mtx);
26948c2ecf20Sopenharmony_ci	kfree(hdw->controls);
26958c2ecf20Sopenharmony_ci	kfree(hdw->mpeg_ctrl_info);
26968c2ecf20Sopenharmony_ci	kfree(hdw);
26978c2ecf20Sopenharmony_ci}
26988c2ecf20Sopenharmony_ci
26998c2ecf20Sopenharmony_ci
27008c2ecf20Sopenharmony_ciint pvr2_hdw_dev_ok(struct pvr2_hdw *hdw)
27018c2ecf20Sopenharmony_ci{
27028c2ecf20Sopenharmony_ci	return (hdw && hdw->flag_ok);
27038c2ecf20Sopenharmony_ci}
27048c2ecf20Sopenharmony_ci
27058c2ecf20Sopenharmony_ci
27068c2ecf20Sopenharmony_ci/* Called when hardware has been unplugged */
27078c2ecf20Sopenharmony_civoid pvr2_hdw_disconnect(struct pvr2_hdw *hdw)
27088c2ecf20Sopenharmony_ci{
27098c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_disconnect(hdw=%p)",hdw);
27108c2ecf20Sopenharmony_ci	LOCK_TAKE(hdw->big_lock);
27118c2ecf20Sopenharmony_ci	pvr2_i2c_core_done(hdw);
27128c2ecf20Sopenharmony_ci	LOCK_TAKE(hdw->ctl_lock);
27138c2ecf20Sopenharmony_ci	pvr2_hdw_remove_usb_stuff(hdw);
27148c2ecf20Sopenharmony_ci	LOCK_GIVE(hdw->ctl_lock);
27158c2ecf20Sopenharmony_ci	LOCK_GIVE(hdw->big_lock);
27168c2ecf20Sopenharmony_ci}
27178c2ecf20Sopenharmony_ci
27188c2ecf20Sopenharmony_ci
27198c2ecf20Sopenharmony_ci/* Get the number of defined controls */
27208c2ecf20Sopenharmony_ciunsigned int pvr2_hdw_get_ctrl_count(struct pvr2_hdw *hdw)
27218c2ecf20Sopenharmony_ci{
27228c2ecf20Sopenharmony_ci	return hdw->control_cnt;
27238c2ecf20Sopenharmony_ci}
27248c2ecf20Sopenharmony_ci
27258c2ecf20Sopenharmony_ci
27268c2ecf20Sopenharmony_ci/* Retrieve a control handle given its index (0..count-1) */
27278c2ecf20Sopenharmony_cistruct pvr2_ctrl *pvr2_hdw_get_ctrl_by_index(struct pvr2_hdw *hdw,
27288c2ecf20Sopenharmony_ci					     unsigned int idx)
27298c2ecf20Sopenharmony_ci{
27308c2ecf20Sopenharmony_ci	if (idx >= hdw->control_cnt) return NULL;
27318c2ecf20Sopenharmony_ci	return hdw->controls + idx;
27328c2ecf20Sopenharmony_ci}
27338c2ecf20Sopenharmony_ci
27348c2ecf20Sopenharmony_ci
27358c2ecf20Sopenharmony_ci/* Retrieve a control handle given its index (0..count-1) */
27368c2ecf20Sopenharmony_cistruct pvr2_ctrl *pvr2_hdw_get_ctrl_by_id(struct pvr2_hdw *hdw,
27378c2ecf20Sopenharmony_ci					  unsigned int ctl_id)
27388c2ecf20Sopenharmony_ci{
27398c2ecf20Sopenharmony_ci	struct pvr2_ctrl *cptr;
27408c2ecf20Sopenharmony_ci	unsigned int idx;
27418c2ecf20Sopenharmony_ci	int i;
27428c2ecf20Sopenharmony_ci
27438c2ecf20Sopenharmony_ci	/* This could be made a lot more efficient, but for now... */
27448c2ecf20Sopenharmony_ci	for (idx = 0; idx < hdw->control_cnt; idx++) {
27458c2ecf20Sopenharmony_ci		cptr = hdw->controls + idx;
27468c2ecf20Sopenharmony_ci		i = cptr->info->internal_id;
27478c2ecf20Sopenharmony_ci		if (i && (i == ctl_id)) return cptr;
27488c2ecf20Sopenharmony_ci	}
27498c2ecf20Sopenharmony_ci	return NULL;
27508c2ecf20Sopenharmony_ci}
27518c2ecf20Sopenharmony_ci
27528c2ecf20Sopenharmony_ci
27538c2ecf20Sopenharmony_ci/* Given a V4L ID, retrieve the control structure associated with it. */
27548c2ecf20Sopenharmony_cistruct pvr2_ctrl *pvr2_hdw_get_ctrl_v4l(struct pvr2_hdw *hdw,unsigned int ctl_id)
27558c2ecf20Sopenharmony_ci{
27568c2ecf20Sopenharmony_ci	struct pvr2_ctrl *cptr;
27578c2ecf20Sopenharmony_ci	unsigned int idx;
27588c2ecf20Sopenharmony_ci	int i;
27598c2ecf20Sopenharmony_ci
27608c2ecf20Sopenharmony_ci	/* This could be made a lot more efficient, but for now... */
27618c2ecf20Sopenharmony_ci	for (idx = 0; idx < hdw->control_cnt; idx++) {
27628c2ecf20Sopenharmony_ci		cptr = hdw->controls + idx;
27638c2ecf20Sopenharmony_ci		i = cptr->info->v4l_id;
27648c2ecf20Sopenharmony_ci		if (i && (i == ctl_id)) return cptr;
27658c2ecf20Sopenharmony_ci	}
27668c2ecf20Sopenharmony_ci	return NULL;
27678c2ecf20Sopenharmony_ci}
27688c2ecf20Sopenharmony_ci
27698c2ecf20Sopenharmony_ci
27708c2ecf20Sopenharmony_ci/* Given a V4L ID for its immediate predecessor, retrieve the control
27718c2ecf20Sopenharmony_ci   structure associated with it. */
27728c2ecf20Sopenharmony_cistruct pvr2_ctrl *pvr2_hdw_get_ctrl_nextv4l(struct pvr2_hdw *hdw,
27738c2ecf20Sopenharmony_ci					    unsigned int ctl_id)
27748c2ecf20Sopenharmony_ci{
27758c2ecf20Sopenharmony_ci	struct pvr2_ctrl *cptr,*cp2;
27768c2ecf20Sopenharmony_ci	unsigned int idx;
27778c2ecf20Sopenharmony_ci	int i;
27788c2ecf20Sopenharmony_ci
27798c2ecf20Sopenharmony_ci	/* This could be made a lot more efficient, but for now... */
27808c2ecf20Sopenharmony_ci	cp2 = NULL;
27818c2ecf20Sopenharmony_ci	for (idx = 0; idx < hdw->control_cnt; idx++) {
27828c2ecf20Sopenharmony_ci		cptr = hdw->controls + idx;
27838c2ecf20Sopenharmony_ci		i = cptr->info->v4l_id;
27848c2ecf20Sopenharmony_ci		if (!i) continue;
27858c2ecf20Sopenharmony_ci		if (i <= ctl_id) continue;
27868c2ecf20Sopenharmony_ci		if (cp2 && (cp2->info->v4l_id < i)) continue;
27878c2ecf20Sopenharmony_ci		cp2 = cptr;
27888c2ecf20Sopenharmony_ci	}
27898c2ecf20Sopenharmony_ci	return cp2;
27908c2ecf20Sopenharmony_ci	return NULL;
27918c2ecf20Sopenharmony_ci}
27928c2ecf20Sopenharmony_ci
27938c2ecf20Sopenharmony_ci
27948c2ecf20Sopenharmony_cistatic const char *get_ctrl_typename(enum pvr2_ctl_type tp)
27958c2ecf20Sopenharmony_ci{
27968c2ecf20Sopenharmony_ci	switch (tp) {
27978c2ecf20Sopenharmony_ci	case pvr2_ctl_int: return "integer";
27988c2ecf20Sopenharmony_ci	case pvr2_ctl_enum: return "enum";
27998c2ecf20Sopenharmony_ci	case pvr2_ctl_bool: return "boolean";
28008c2ecf20Sopenharmony_ci	case pvr2_ctl_bitmask: return "bitmask";
28018c2ecf20Sopenharmony_ci	}
28028c2ecf20Sopenharmony_ci	return "";
28038c2ecf20Sopenharmony_ci}
28048c2ecf20Sopenharmony_ci
28058c2ecf20Sopenharmony_ci
28068c2ecf20Sopenharmony_cistatic void pvr2_subdev_set_control(struct pvr2_hdw *hdw, int id,
28078c2ecf20Sopenharmony_ci				    const char *name, int val)
28088c2ecf20Sopenharmony_ci{
28098c2ecf20Sopenharmony_ci	struct v4l2_control ctrl;
28108c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd;
28118c2ecf20Sopenharmony_ci
28128c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 %s=%d", name, val);
28138c2ecf20Sopenharmony_ci	memset(&ctrl, 0, sizeof(ctrl));
28148c2ecf20Sopenharmony_ci	ctrl.id = id;
28158c2ecf20Sopenharmony_ci	ctrl.value = val;
28168c2ecf20Sopenharmony_ci
28178c2ecf20Sopenharmony_ci	v4l2_device_for_each_subdev(sd, &hdw->v4l2_dev)
28188c2ecf20Sopenharmony_ci		v4l2_s_ctrl(NULL, sd->ctrl_handler, &ctrl);
28198c2ecf20Sopenharmony_ci}
28208c2ecf20Sopenharmony_ci
28218c2ecf20Sopenharmony_ci#define PVR2_SUBDEV_SET_CONTROL(hdw, id, lab) \
28228c2ecf20Sopenharmony_ci	if ((hdw)->lab##_dirty || (hdw)->force_dirty) {		\
28238c2ecf20Sopenharmony_ci		pvr2_subdev_set_control(hdw, id, #lab, (hdw)->lab##_val); \
28248c2ecf20Sopenharmony_ci	}
28258c2ecf20Sopenharmony_ci
28268c2ecf20Sopenharmony_cistatic v4l2_std_id pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw)
28278c2ecf20Sopenharmony_ci{
28288c2ecf20Sopenharmony_ci	v4l2_std_id std;
28298c2ecf20Sopenharmony_ci	std = (v4l2_std_id)hdw->std_mask_avail;
28308c2ecf20Sopenharmony_ci	v4l2_device_call_all(&hdw->v4l2_dev, 0,
28318c2ecf20Sopenharmony_ci			     video, querystd, &std);
28328c2ecf20Sopenharmony_ci	return std;
28338c2ecf20Sopenharmony_ci}
28348c2ecf20Sopenharmony_ci
28358c2ecf20Sopenharmony_ci/* Execute whatever commands are required to update the state of all the
28368c2ecf20Sopenharmony_ci   sub-devices so that they match our current control values. */
28378c2ecf20Sopenharmony_cistatic void pvr2_subdev_update(struct pvr2_hdw *hdw)
28388c2ecf20Sopenharmony_ci{
28398c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd;
28408c2ecf20Sopenharmony_ci	unsigned int id;
28418c2ecf20Sopenharmony_ci	pvr2_subdev_update_func fp;
28428c2ecf20Sopenharmony_ci
28438c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_CHIPS, "subdev update...");
28448c2ecf20Sopenharmony_ci
28458c2ecf20Sopenharmony_ci	if (hdw->tuner_updated || hdw->force_dirty) {
28468c2ecf20Sopenharmony_ci		struct tuner_setup setup;
28478c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_CHIPS, "subdev tuner set_type(%d)",
28488c2ecf20Sopenharmony_ci			   hdw->tuner_type);
28498c2ecf20Sopenharmony_ci		if (((int)(hdw->tuner_type)) >= 0) {
28508c2ecf20Sopenharmony_ci			memset(&setup, 0, sizeof(setup));
28518c2ecf20Sopenharmony_ci			setup.addr = ADDR_UNSET;
28528c2ecf20Sopenharmony_ci			setup.type = hdw->tuner_type;
28538c2ecf20Sopenharmony_ci			setup.mode_mask = T_RADIO | T_ANALOG_TV;
28548c2ecf20Sopenharmony_ci			v4l2_device_call_all(&hdw->v4l2_dev, 0,
28558c2ecf20Sopenharmony_ci					     tuner, s_type_addr, &setup);
28568c2ecf20Sopenharmony_ci		}
28578c2ecf20Sopenharmony_ci	}
28588c2ecf20Sopenharmony_ci
28598c2ecf20Sopenharmony_ci	if (hdw->input_dirty || hdw->std_dirty || hdw->force_dirty) {
28608c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_standard");
28618c2ecf20Sopenharmony_ci		if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
28628c2ecf20Sopenharmony_ci			v4l2_device_call_all(&hdw->v4l2_dev, 0,
28638c2ecf20Sopenharmony_ci					     tuner, s_radio);
28648c2ecf20Sopenharmony_ci		} else {
28658c2ecf20Sopenharmony_ci			v4l2_std_id vs;
28668c2ecf20Sopenharmony_ci			vs = hdw->std_mask_cur;
28678c2ecf20Sopenharmony_ci			v4l2_device_call_all(&hdw->v4l2_dev, 0,
28688c2ecf20Sopenharmony_ci					     video, s_std, vs);
28698c2ecf20Sopenharmony_ci			pvr2_hdw_cx25840_vbi_hack(hdw);
28708c2ecf20Sopenharmony_ci		}
28718c2ecf20Sopenharmony_ci		hdw->tuner_signal_stale = !0;
28728c2ecf20Sopenharmony_ci		hdw->cropcap_stale = !0;
28738c2ecf20Sopenharmony_ci	}
28748c2ecf20Sopenharmony_ci
28758c2ecf20Sopenharmony_ci	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_BRIGHTNESS, brightness);
28768c2ecf20Sopenharmony_ci	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_CONTRAST, contrast);
28778c2ecf20Sopenharmony_ci	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_SATURATION, saturation);
28788c2ecf20Sopenharmony_ci	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_HUE, hue);
28798c2ecf20Sopenharmony_ci	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_MUTE, mute);
28808c2ecf20Sopenharmony_ci	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_VOLUME, volume);
28818c2ecf20Sopenharmony_ci	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_BALANCE, balance);
28828c2ecf20Sopenharmony_ci	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_BASS, bass);
28838c2ecf20Sopenharmony_ci	PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_TREBLE, treble);
28848c2ecf20Sopenharmony_ci
28858c2ecf20Sopenharmony_ci	if (hdw->input_dirty || hdw->audiomode_dirty || hdw->force_dirty) {
28868c2ecf20Sopenharmony_ci		struct v4l2_tuner vt;
28878c2ecf20Sopenharmony_ci		memset(&vt, 0, sizeof(vt));
28888c2ecf20Sopenharmony_ci		vt.type = (hdw->input_val == PVR2_CVAL_INPUT_RADIO) ?
28898c2ecf20Sopenharmony_ci			V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
28908c2ecf20Sopenharmony_ci		vt.audmode = hdw->audiomode_val;
28918c2ecf20Sopenharmony_ci		v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, s_tuner, &vt);
28928c2ecf20Sopenharmony_ci	}
28938c2ecf20Sopenharmony_ci
28948c2ecf20Sopenharmony_ci	if (hdw->freqDirty || hdw->force_dirty) {
28958c2ecf20Sopenharmony_ci		unsigned long fv;
28968c2ecf20Sopenharmony_ci		struct v4l2_frequency freq;
28978c2ecf20Sopenharmony_ci		fv = pvr2_hdw_get_cur_freq(hdw);
28988c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_freq(%lu)", fv);
28998c2ecf20Sopenharmony_ci		if (hdw->tuner_signal_stale) pvr2_hdw_status_poll(hdw);
29008c2ecf20Sopenharmony_ci		memset(&freq, 0, sizeof(freq));
29018c2ecf20Sopenharmony_ci		if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) {
29028c2ecf20Sopenharmony_ci			/* ((fv * 1000) / 62500) */
29038c2ecf20Sopenharmony_ci			freq.frequency = (fv * 2) / 125;
29048c2ecf20Sopenharmony_ci		} else {
29058c2ecf20Sopenharmony_ci			freq.frequency = fv / 62500;
29068c2ecf20Sopenharmony_ci		}
29078c2ecf20Sopenharmony_ci		/* tuner-core currently doesn't seem to care about this, but
29088c2ecf20Sopenharmony_ci		   let's set it anyway for completeness. */
29098c2ecf20Sopenharmony_ci		if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
29108c2ecf20Sopenharmony_ci			freq.type = V4L2_TUNER_RADIO;
29118c2ecf20Sopenharmony_ci		} else {
29128c2ecf20Sopenharmony_ci			freq.type = V4L2_TUNER_ANALOG_TV;
29138c2ecf20Sopenharmony_ci		}
29148c2ecf20Sopenharmony_ci		freq.tuner = 0;
29158c2ecf20Sopenharmony_ci		v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner,
29168c2ecf20Sopenharmony_ci				     s_frequency, &freq);
29178c2ecf20Sopenharmony_ci	}
29188c2ecf20Sopenharmony_ci
29198c2ecf20Sopenharmony_ci	if (hdw->res_hor_dirty || hdw->res_ver_dirty || hdw->force_dirty) {
29208c2ecf20Sopenharmony_ci		struct v4l2_subdev_format format = {
29218c2ecf20Sopenharmony_ci			.which = V4L2_SUBDEV_FORMAT_ACTIVE,
29228c2ecf20Sopenharmony_ci		};
29238c2ecf20Sopenharmony_ci
29248c2ecf20Sopenharmony_ci		format.format.width = hdw->res_hor_val;
29258c2ecf20Sopenharmony_ci		format.format.height = hdw->res_ver_val;
29268c2ecf20Sopenharmony_ci		format.format.code = MEDIA_BUS_FMT_FIXED;
29278c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_size(%dx%d)",
29288c2ecf20Sopenharmony_ci			   format.format.width, format.format.height);
29298c2ecf20Sopenharmony_ci		v4l2_device_call_all(&hdw->v4l2_dev, 0, pad, set_fmt,
29308c2ecf20Sopenharmony_ci				     NULL, &format);
29318c2ecf20Sopenharmony_ci	}
29328c2ecf20Sopenharmony_ci
29338c2ecf20Sopenharmony_ci	if (hdw->srate_dirty || hdw->force_dirty) {
29348c2ecf20Sopenharmony_ci		u32 val;
29358c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_audio %d",
29368c2ecf20Sopenharmony_ci			   hdw->srate_val);
29378c2ecf20Sopenharmony_ci		switch (hdw->srate_val) {
29388c2ecf20Sopenharmony_ci		default:
29398c2ecf20Sopenharmony_ci		case V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000:
29408c2ecf20Sopenharmony_ci			val = 48000;
29418c2ecf20Sopenharmony_ci			break;
29428c2ecf20Sopenharmony_ci		case V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100:
29438c2ecf20Sopenharmony_ci			val = 44100;
29448c2ecf20Sopenharmony_ci			break;
29458c2ecf20Sopenharmony_ci		case V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000:
29468c2ecf20Sopenharmony_ci			val = 32000;
29478c2ecf20Sopenharmony_ci			break;
29488c2ecf20Sopenharmony_ci		}
29498c2ecf20Sopenharmony_ci		v4l2_device_call_all(&hdw->v4l2_dev, 0,
29508c2ecf20Sopenharmony_ci				     audio, s_clock_freq, val);
29518c2ecf20Sopenharmony_ci	}
29528c2ecf20Sopenharmony_ci
29538c2ecf20Sopenharmony_ci	/* Unable to set crop parameters; there is apparently no equivalent
29548c2ecf20Sopenharmony_ci	   for VIDIOC_S_CROP */
29558c2ecf20Sopenharmony_ci
29568c2ecf20Sopenharmony_ci	v4l2_device_for_each_subdev(sd, &hdw->v4l2_dev) {
29578c2ecf20Sopenharmony_ci		id = sd->grp_id;
29588c2ecf20Sopenharmony_ci		if (id >= ARRAY_SIZE(pvr2_module_update_functions)) continue;
29598c2ecf20Sopenharmony_ci		fp = pvr2_module_update_functions[id];
29608c2ecf20Sopenharmony_ci		if (!fp) continue;
29618c2ecf20Sopenharmony_ci		(*fp)(hdw, sd);
29628c2ecf20Sopenharmony_ci	}
29638c2ecf20Sopenharmony_ci
29648c2ecf20Sopenharmony_ci	if (hdw->tuner_signal_stale || hdw->cropcap_stale) {
29658c2ecf20Sopenharmony_ci		pvr2_hdw_status_poll(hdw);
29668c2ecf20Sopenharmony_ci	}
29678c2ecf20Sopenharmony_ci}
29688c2ecf20Sopenharmony_ci
29698c2ecf20Sopenharmony_ci
29708c2ecf20Sopenharmony_ci/* Figure out if we need to commit control changes.  If so, mark internal
29718c2ecf20Sopenharmony_ci   state flags to indicate this fact and return true.  Otherwise do nothing
29728c2ecf20Sopenharmony_ci   else and return false. */
29738c2ecf20Sopenharmony_cistatic int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw)
29748c2ecf20Sopenharmony_ci{
29758c2ecf20Sopenharmony_ci	unsigned int idx;
29768c2ecf20Sopenharmony_ci	struct pvr2_ctrl *cptr;
29778c2ecf20Sopenharmony_ci	int value;
29788c2ecf20Sopenharmony_ci	int commit_flag = hdw->force_dirty;
29798c2ecf20Sopenharmony_ci	char buf[100];
29808c2ecf20Sopenharmony_ci	unsigned int bcnt,ccnt;
29818c2ecf20Sopenharmony_ci
29828c2ecf20Sopenharmony_ci	for (idx = 0; idx < hdw->control_cnt; idx++) {
29838c2ecf20Sopenharmony_ci		cptr = hdw->controls + idx;
29848c2ecf20Sopenharmony_ci		if (!cptr->info->is_dirty) continue;
29858c2ecf20Sopenharmony_ci		if (!cptr->info->is_dirty(cptr)) continue;
29868c2ecf20Sopenharmony_ci		commit_flag = !0;
29878c2ecf20Sopenharmony_ci
29888c2ecf20Sopenharmony_ci		if (!(pvrusb2_debug & PVR2_TRACE_CTL)) continue;
29898c2ecf20Sopenharmony_ci		bcnt = scnprintf(buf,sizeof(buf),"\"%s\" <-- ",
29908c2ecf20Sopenharmony_ci				 cptr->info->name);
29918c2ecf20Sopenharmony_ci		value = 0;
29928c2ecf20Sopenharmony_ci		cptr->info->get_value(cptr,&value);
29938c2ecf20Sopenharmony_ci		pvr2_ctrl_value_to_sym_internal(cptr,~0,value,
29948c2ecf20Sopenharmony_ci						buf+bcnt,
29958c2ecf20Sopenharmony_ci						sizeof(buf)-bcnt,&ccnt);
29968c2ecf20Sopenharmony_ci		bcnt += ccnt;
29978c2ecf20Sopenharmony_ci		bcnt += scnprintf(buf+bcnt,sizeof(buf)-bcnt," <%s>",
29988c2ecf20Sopenharmony_ci				  get_ctrl_typename(cptr->info->type));
29998c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_CTL,
30008c2ecf20Sopenharmony_ci			   "/*--TRACE_COMMIT--*/ %.*s",
30018c2ecf20Sopenharmony_ci			   bcnt,buf);
30028c2ecf20Sopenharmony_ci	}
30038c2ecf20Sopenharmony_ci
30048c2ecf20Sopenharmony_ci	if (!commit_flag) {
30058c2ecf20Sopenharmony_ci		/* Nothing has changed */
30068c2ecf20Sopenharmony_ci		return 0;
30078c2ecf20Sopenharmony_ci	}
30088c2ecf20Sopenharmony_ci
30098c2ecf20Sopenharmony_ci	hdw->state_pipeline_config = 0;
30108c2ecf20Sopenharmony_ci	trace_stbit("state_pipeline_config",hdw->state_pipeline_config);
30118c2ecf20Sopenharmony_ci	pvr2_hdw_state_sched(hdw);
30128c2ecf20Sopenharmony_ci
30138c2ecf20Sopenharmony_ci	return !0;
30148c2ecf20Sopenharmony_ci}
30158c2ecf20Sopenharmony_ci
30168c2ecf20Sopenharmony_ci
30178c2ecf20Sopenharmony_ci/* Perform all operations needed to commit all control changes.  This must
30188c2ecf20Sopenharmony_ci   be performed in synchronization with the pipeline state and is thus
30198c2ecf20Sopenharmony_ci   expected to be called as part of the driver's worker thread.  Return
30208c2ecf20Sopenharmony_ci   true if commit successful, otherwise return false to indicate that
30218c2ecf20Sopenharmony_ci   commit isn't possible at this time. */
30228c2ecf20Sopenharmony_cistatic int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw)
30238c2ecf20Sopenharmony_ci{
30248c2ecf20Sopenharmony_ci	unsigned int idx;
30258c2ecf20Sopenharmony_ci	struct pvr2_ctrl *cptr;
30268c2ecf20Sopenharmony_ci	int disruptive_change;
30278c2ecf20Sopenharmony_ci
30288c2ecf20Sopenharmony_ci	if (hdw->input_dirty && hdw->state_pathway_ok &&
30298c2ecf20Sopenharmony_ci	    (((hdw->input_val == PVR2_CVAL_INPUT_DTV) ?
30308c2ecf20Sopenharmony_ci	      PVR2_PATHWAY_DIGITAL : PVR2_PATHWAY_ANALOG) !=
30318c2ecf20Sopenharmony_ci	     hdw->pathway_state)) {
30328c2ecf20Sopenharmony_ci		/* Change of mode being asked for... */
30338c2ecf20Sopenharmony_ci		hdw->state_pathway_ok = 0;
30348c2ecf20Sopenharmony_ci		trace_stbit("state_pathway_ok", hdw->state_pathway_ok);
30358c2ecf20Sopenharmony_ci	}
30368c2ecf20Sopenharmony_ci	if (!hdw->state_pathway_ok) {
30378c2ecf20Sopenharmony_ci		/* Can't commit anything until pathway is ok. */
30388c2ecf20Sopenharmony_ci		return 0;
30398c2ecf20Sopenharmony_ci	}
30408c2ecf20Sopenharmony_ci
30418c2ecf20Sopenharmony_ci	/* Handle some required side effects when the video standard is
30428c2ecf20Sopenharmony_ci	   changed.... */
30438c2ecf20Sopenharmony_ci	if (hdw->std_dirty) {
30448c2ecf20Sopenharmony_ci		int nvres;
30458c2ecf20Sopenharmony_ci		int gop_size;
30468c2ecf20Sopenharmony_ci		if (hdw->std_mask_cur & V4L2_STD_525_60) {
30478c2ecf20Sopenharmony_ci			nvres = 480;
30488c2ecf20Sopenharmony_ci			gop_size = 15;
30498c2ecf20Sopenharmony_ci		} else {
30508c2ecf20Sopenharmony_ci			nvres = 576;
30518c2ecf20Sopenharmony_ci			gop_size = 12;
30528c2ecf20Sopenharmony_ci		}
30538c2ecf20Sopenharmony_ci		/* Rewrite the vertical resolution to be appropriate to the
30548c2ecf20Sopenharmony_ci		   video standard that has been selected. */
30558c2ecf20Sopenharmony_ci		if (nvres != hdw->res_ver_val) {
30568c2ecf20Sopenharmony_ci			hdw->res_ver_val = nvres;
30578c2ecf20Sopenharmony_ci			hdw->res_ver_dirty = !0;
30588c2ecf20Sopenharmony_ci		}
30598c2ecf20Sopenharmony_ci		/* Rewrite the GOP size to be appropriate to the video
30608c2ecf20Sopenharmony_ci		   standard that has been selected. */
30618c2ecf20Sopenharmony_ci		if (gop_size != hdw->enc_ctl_state.video_gop_size) {
30628c2ecf20Sopenharmony_ci			struct v4l2_ext_controls cs;
30638c2ecf20Sopenharmony_ci			struct v4l2_ext_control c1;
30648c2ecf20Sopenharmony_ci			memset(&cs, 0, sizeof(cs));
30658c2ecf20Sopenharmony_ci			memset(&c1, 0, sizeof(c1));
30668c2ecf20Sopenharmony_ci			cs.controls = &c1;
30678c2ecf20Sopenharmony_ci			cs.count = 1;
30688c2ecf20Sopenharmony_ci			c1.id = V4L2_CID_MPEG_VIDEO_GOP_SIZE;
30698c2ecf20Sopenharmony_ci			c1.value = gop_size;
30708c2ecf20Sopenharmony_ci			cx2341x_ext_ctrls(&hdw->enc_ctl_state, 0, &cs,
30718c2ecf20Sopenharmony_ci					  VIDIOC_S_EXT_CTRLS);
30728c2ecf20Sopenharmony_ci		}
30738c2ecf20Sopenharmony_ci	}
30748c2ecf20Sopenharmony_ci
30758c2ecf20Sopenharmony_ci	/* The broadcast decoder can only scale down, so if
30768c2ecf20Sopenharmony_ci	 * res_*_dirty && crop window < output format ==> enlarge crop.
30778c2ecf20Sopenharmony_ci	 *
30788c2ecf20Sopenharmony_ci	 * The mpeg encoder receives fields of res_hor_val dots and
30798c2ecf20Sopenharmony_ci	 * res_ver_val halflines.  Limits: hor<=720, ver<=576.
30808c2ecf20Sopenharmony_ci	 */
30818c2ecf20Sopenharmony_ci	if (hdw->res_hor_dirty && hdw->cropw_val < hdw->res_hor_val) {
30828c2ecf20Sopenharmony_ci		hdw->cropw_val = hdw->res_hor_val;
30838c2ecf20Sopenharmony_ci		hdw->cropw_dirty = !0;
30848c2ecf20Sopenharmony_ci	} else if (hdw->cropw_dirty) {
30858c2ecf20Sopenharmony_ci		hdw->res_hor_dirty = !0;           /* must rescale */
30868c2ecf20Sopenharmony_ci		hdw->res_hor_val = min(720, hdw->cropw_val);
30878c2ecf20Sopenharmony_ci	}
30888c2ecf20Sopenharmony_ci	if (hdw->res_ver_dirty && hdw->croph_val < hdw->res_ver_val) {
30898c2ecf20Sopenharmony_ci		hdw->croph_val = hdw->res_ver_val;
30908c2ecf20Sopenharmony_ci		hdw->croph_dirty = !0;
30918c2ecf20Sopenharmony_ci	} else if (hdw->croph_dirty) {
30928c2ecf20Sopenharmony_ci		int nvres = hdw->std_mask_cur & V4L2_STD_525_60 ? 480 : 576;
30938c2ecf20Sopenharmony_ci		hdw->res_ver_dirty = !0;
30948c2ecf20Sopenharmony_ci		hdw->res_ver_val = min(nvres, hdw->croph_val);
30958c2ecf20Sopenharmony_ci	}
30968c2ecf20Sopenharmony_ci
30978c2ecf20Sopenharmony_ci	/* If any of the below has changed, then we can't do the update
30988c2ecf20Sopenharmony_ci	   while the pipeline is running.  Pipeline must be paused first
30998c2ecf20Sopenharmony_ci	   and decoder -> encoder connection be made quiescent before we
31008c2ecf20Sopenharmony_ci	   can proceed. */
31018c2ecf20Sopenharmony_ci	disruptive_change =
31028c2ecf20Sopenharmony_ci		(hdw->std_dirty ||
31038c2ecf20Sopenharmony_ci		 hdw->enc_unsafe_stale ||
31048c2ecf20Sopenharmony_ci		 hdw->srate_dirty ||
31058c2ecf20Sopenharmony_ci		 hdw->res_ver_dirty ||
31068c2ecf20Sopenharmony_ci		 hdw->res_hor_dirty ||
31078c2ecf20Sopenharmony_ci		 hdw->cropw_dirty ||
31088c2ecf20Sopenharmony_ci		 hdw->croph_dirty ||
31098c2ecf20Sopenharmony_ci		 hdw->input_dirty ||
31108c2ecf20Sopenharmony_ci		 (hdw->active_stream_type != hdw->desired_stream_type));
31118c2ecf20Sopenharmony_ci	if (disruptive_change && !hdw->state_pipeline_idle) {
31128c2ecf20Sopenharmony_ci		/* Pipeline is not idle; we can't proceed.  Arrange to
31138c2ecf20Sopenharmony_ci		   cause pipeline to stop so that we can try this again
31148c2ecf20Sopenharmony_ci		   later.... */
31158c2ecf20Sopenharmony_ci		hdw->state_pipeline_pause = !0;
31168c2ecf20Sopenharmony_ci		return 0;
31178c2ecf20Sopenharmony_ci	}
31188c2ecf20Sopenharmony_ci
31198c2ecf20Sopenharmony_ci	if (hdw->srate_dirty) {
31208c2ecf20Sopenharmony_ci		/* Write new sample rate into control structure since
31218c2ecf20Sopenharmony_ci		 * the master copy is stale.  We must track srate
31228c2ecf20Sopenharmony_ci		 * separate from the mpeg control structure because
31238c2ecf20Sopenharmony_ci		 * other logic also uses this value. */
31248c2ecf20Sopenharmony_ci		struct v4l2_ext_controls cs;
31258c2ecf20Sopenharmony_ci		struct v4l2_ext_control c1;
31268c2ecf20Sopenharmony_ci		memset(&cs,0,sizeof(cs));
31278c2ecf20Sopenharmony_ci		memset(&c1,0,sizeof(c1));
31288c2ecf20Sopenharmony_ci		cs.controls = &c1;
31298c2ecf20Sopenharmony_ci		cs.count = 1;
31308c2ecf20Sopenharmony_ci		c1.id = V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ;
31318c2ecf20Sopenharmony_ci		c1.value = hdw->srate_val;
31328c2ecf20Sopenharmony_ci		cx2341x_ext_ctrls(&hdw->enc_ctl_state, 0, &cs,VIDIOC_S_EXT_CTRLS);
31338c2ecf20Sopenharmony_ci	}
31348c2ecf20Sopenharmony_ci
31358c2ecf20Sopenharmony_ci	if (hdw->active_stream_type != hdw->desired_stream_type) {
31368c2ecf20Sopenharmony_ci		/* Handle any side effects of stream config here */
31378c2ecf20Sopenharmony_ci		hdw->active_stream_type = hdw->desired_stream_type;
31388c2ecf20Sopenharmony_ci	}
31398c2ecf20Sopenharmony_ci
31408c2ecf20Sopenharmony_ci	if (hdw->hdw_desc->signal_routing_scheme ==
31418c2ecf20Sopenharmony_ci	    PVR2_ROUTING_SCHEME_GOTVIEW) {
31428c2ecf20Sopenharmony_ci		u32 b;
31438c2ecf20Sopenharmony_ci		/* Handle GOTVIEW audio switching */
31448c2ecf20Sopenharmony_ci		pvr2_hdw_gpio_get_out(hdw,&b);
31458c2ecf20Sopenharmony_ci		if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
31468c2ecf20Sopenharmony_ci			/* Set GPIO 11 */
31478c2ecf20Sopenharmony_ci			pvr2_hdw_gpio_chg_out(hdw,(1 << 11),~0);
31488c2ecf20Sopenharmony_ci		} else {
31498c2ecf20Sopenharmony_ci			/* Clear GPIO 11 */
31508c2ecf20Sopenharmony_ci			pvr2_hdw_gpio_chg_out(hdw,(1 << 11),0);
31518c2ecf20Sopenharmony_ci		}
31528c2ecf20Sopenharmony_ci	}
31538c2ecf20Sopenharmony_ci
31548c2ecf20Sopenharmony_ci	/* Check and update state for all sub-devices. */
31558c2ecf20Sopenharmony_ci	pvr2_subdev_update(hdw);
31568c2ecf20Sopenharmony_ci
31578c2ecf20Sopenharmony_ci	hdw->tuner_updated = 0;
31588c2ecf20Sopenharmony_ci	hdw->force_dirty = 0;
31598c2ecf20Sopenharmony_ci	for (idx = 0; idx < hdw->control_cnt; idx++) {
31608c2ecf20Sopenharmony_ci		cptr = hdw->controls + idx;
31618c2ecf20Sopenharmony_ci		if (!cptr->info->clear_dirty) continue;
31628c2ecf20Sopenharmony_ci		cptr->info->clear_dirty(cptr);
31638c2ecf20Sopenharmony_ci	}
31648c2ecf20Sopenharmony_ci
31658c2ecf20Sopenharmony_ci	if ((hdw->pathway_state == PVR2_PATHWAY_ANALOG) &&
31668c2ecf20Sopenharmony_ci	    hdw->state_encoder_run) {
31678c2ecf20Sopenharmony_ci		/* If encoder isn't running or it can't be touched, then
31688c2ecf20Sopenharmony_ci		   this will get worked out later when we start the
31698c2ecf20Sopenharmony_ci		   encoder. */
31708c2ecf20Sopenharmony_ci		if (pvr2_encoder_adjust(hdw) < 0) return !0;
31718c2ecf20Sopenharmony_ci	}
31728c2ecf20Sopenharmony_ci
31738c2ecf20Sopenharmony_ci	hdw->state_pipeline_config = !0;
31748c2ecf20Sopenharmony_ci	/* Hardware state may have changed in a way to cause the cropping
31758c2ecf20Sopenharmony_ci	   capabilities to have changed.  So mark it stale, which will
31768c2ecf20Sopenharmony_ci	   cause a later re-fetch. */
31778c2ecf20Sopenharmony_ci	trace_stbit("state_pipeline_config",hdw->state_pipeline_config);
31788c2ecf20Sopenharmony_ci	return !0;
31798c2ecf20Sopenharmony_ci}
31808c2ecf20Sopenharmony_ci
31818c2ecf20Sopenharmony_ci
31828c2ecf20Sopenharmony_ciint pvr2_hdw_commit_ctl(struct pvr2_hdw *hdw)
31838c2ecf20Sopenharmony_ci{
31848c2ecf20Sopenharmony_ci	int fl;
31858c2ecf20Sopenharmony_ci	LOCK_TAKE(hdw->big_lock);
31868c2ecf20Sopenharmony_ci	fl = pvr2_hdw_commit_setup(hdw);
31878c2ecf20Sopenharmony_ci	LOCK_GIVE(hdw->big_lock);
31888c2ecf20Sopenharmony_ci	if (!fl) return 0;
31898c2ecf20Sopenharmony_ci	return pvr2_hdw_wait(hdw,0);
31908c2ecf20Sopenharmony_ci}
31918c2ecf20Sopenharmony_ci
31928c2ecf20Sopenharmony_ci
31938c2ecf20Sopenharmony_cistatic void pvr2_hdw_worker_poll(struct work_struct *work)
31948c2ecf20Sopenharmony_ci{
31958c2ecf20Sopenharmony_ci	int fl = 0;
31968c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = container_of(work,struct pvr2_hdw,workpoll);
31978c2ecf20Sopenharmony_ci	LOCK_TAKE(hdw->big_lock); do {
31988c2ecf20Sopenharmony_ci		fl = pvr2_hdw_state_eval(hdw);
31998c2ecf20Sopenharmony_ci	} while (0); LOCK_GIVE(hdw->big_lock);
32008c2ecf20Sopenharmony_ci	if (fl && hdw->state_func) {
32018c2ecf20Sopenharmony_ci		hdw->state_func(hdw->state_data);
32028c2ecf20Sopenharmony_ci	}
32038c2ecf20Sopenharmony_ci}
32048c2ecf20Sopenharmony_ci
32058c2ecf20Sopenharmony_ci
32068c2ecf20Sopenharmony_cistatic int pvr2_hdw_wait(struct pvr2_hdw *hdw,int state)
32078c2ecf20Sopenharmony_ci{
32088c2ecf20Sopenharmony_ci	return wait_event_interruptible(
32098c2ecf20Sopenharmony_ci		hdw->state_wait_data,
32108c2ecf20Sopenharmony_ci		(hdw->state_stale == 0) &&
32118c2ecf20Sopenharmony_ci		(!state || (hdw->master_state != state)));
32128c2ecf20Sopenharmony_ci}
32138c2ecf20Sopenharmony_ci
32148c2ecf20Sopenharmony_ci
32158c2ecf20Sopenharmony_ci/* Return name for this driver instance */
32168c2ecf20Sopenharmony_ciconst char *pvr2_hdw_get_driver_name(struct pvr2_hdw *hdw)
32178c2ecf20Sopenharmony_ci{
32188c2ecf20Sopenharmony_ci	return hdw->name;
32198c2ecf20Sopenharmony_ci}
32208c2ecf20Sopenharmony_ci
32218c2ecf20Sopenharmony_ci
32228c2ecf20Sopenharmony_ciconst char *pvr2_hdw_get_desc(struct pvr2_hdw *hdw)
32238c2ecf20Sopenharmony_ci{
32248c2ecf20Sopenharmony_ci	return hdw->hdw_desc->description;
32258c2ecf20Sopenharmony_ci}
32268c2ecf20Sopenharmony_ci
32278c2ecf20Sopenharmony_ci
32288c2ecf20Sopenharmony_ciconst char *pvr2_hdw_get_type(struct pvr2_hdw *hdw)
32298c2ecf20Sopenharmony_ci{
32308c2ecf20Sopenharmony_ci	return hdw->hdw_desc->shortname;
32318c2ecf20Sopenharmony_ci}
32328c2ecf20Sopenharmony_ci
32338c2ecf20Sopenharmony_ci
32348c2ecf20Sopenharmony_ciint pvr2_hdw_is_hsm(struct pvr2_hdw *hdw)
32358c2ecf20Sopenharmony_ci{
32368c2ecf20Sopenharmony_ci	int result;
32378c2ecf20Sopenharmony_ci	LOCK_TAKE(hdw->ctl_lock); do {
32388c2ecf20Sopenharmony_ci		hdw->cmd_buffer[0] = FX2CMD_GET_USB_SPEED;
32398c2ecf20Sopenharmony_ci		result = pvr2_send_request(hdw,
32408c2ecf20Sopenharmony_ci					   hdw->cmd_buffer,1,
32418c2ecf20Sopenharmony_ci					   hdw->cmd_buffer,1);
32428c2ecf20Sopenharmony_ci		if (result < 0) break;
32438c2ecf20Sopenharmony_ci		result = (hdw->cmd_buffer[0] != 0);
32448c2ecf20Sopenharmony_ci	} while(0); LOCK_GIVE(hdw->ctl_lock);
32458c2ecf20Sopenharmony_ci	return result;
32468c2ecf20Sopenharmony_ci}
32478c2ecf20Sopenharmony_ci
32488c2ecf20Sopenharmony_ci
32498c2ecf20Sopenharmony_ci/* Execute poll of tuner status */
32508c2ecf20Sopenharmony_civoid pvr2_hdw_execute_tuner_poll(struct pvr2_hdw *hdw)
32518c2ecf20Sopenharmony_ci{
32528c2ecf20Sopenharmony_ci	LOCK_TAKE(hdw->big_lock); do {
32538c2ecf20Sopenharmony_ci		pvr2_hdw_status_poll(hdw);
32548c2ecf20Sopenharmony_ci	} while (0); LOCK_GIVE(hdw->big_lock);
32558c2ecf20Sopenharmony_ci}
32568c2ecf20Sopenharmony_ci
32578c2ecf20Sopenharmony_ci
32588c2ecf20Sopenharmony_cistatic int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw)
32598c2ecf20Sopenharmony_ci{
32608c2ecf20Sopenharmony_ci	if (!hdw->cropcap_stale) {
32618c2ecf20Sopenharmony_ci		return 0;
32628c2ecf20Sopenharmony_ci	}
32638c2ecf20Sopenharmony_ci	pvr2_hdw_status_poll(hdw);
32648c2ecf20Sopenharmony_ci	if (hdw->cropcap_stale) {
32658c2ecf20Sopenharmony_ci		return -EIO;
32668c2ecf20Sopenharmony_ci	}
32678c2ecf20Sopenharmony_ci	return 0;
32688c2ecf20Sopenharmony_ci}
32698c2ecf20Sopenharmony_ci
32708c2ecf20Sopenharmony_ci
32718c2ecf20Sopenharmony_ci/* Return information about cropping capabilities */
32728c2ecf20Sopenharmony_ciint pvr2_hdw_get_cropcap(struct pvr2_hdw *hdw, struct v4l2_cropcap *pp)
32738c2ecf20Sopenharmony_ci{
32748c2ecf20Sopenharmony_ci	int stat = 0;
32758c2ecf20Sopenharmony_ci	LOCK_TAKE(hdw->big_lock);
32768c2ecf20Sopenharmony_ci	stat = pvr2_hdw_check_cropcap(hdw);
32778c2ecf20Sopenharmony_ci	if (!stat) {
32788c2ecf20Sopenharmony_ci		memcpy(pp, &hdw->cropcap_info, sizeof(hdw->cropcap_info));
32798c2ecf20Sopenharmony_ci	}
32808c2ecf20Sopenharmony_ci	LOCK_GIVE(hdw->big_lock);
32818c2ecf20Sopenharmony_ci	return stat;
32828c2ecf20Sopenharmony_ci}
32838c2ecf20Sopenharmony_ci
32848c2ecf20Sopenharmony_ci
32858c2ecf20Sopenharmony_ci/* Return information about the tuner */
32868c2ecf20Sopenharmony_ciint pvr2_hdw_get_tuner_status(struct pvr2_hdw *hdw,struct v4l2_tuner *vtp)
32878c2ecf20Sopenharmony_ci{
32888c2ecf20Sopenharmony_ci	LOCK_TAKE(hdw->big_lock); do {
32898c2ecf20Sopenharmony_ci		if (hdw->tuner_signal_stale) {
32908c2ecf20Sopenharmony_ci			pvr2_hdw_status_poll(hdw);
32918c2ecf20Sopenharmony_ci		}
32928c2ecf20Sopenharmony_ci		memcpy(vtp,&hdw->tuner_signal_info,sizeof(struct v4l2_tuner));
32938c2ecf20Sopenharmony_ci	} while (0); LOCK_GIVE(hdw->big_lock);
32948c2ecf20Sopenharmony_ci	return 0;
32958c2ecf20Sopenharmony_ci}
32968c2ecf20Sopenharmony_ci
32978c2ecf20Sopenharmony_ci
32988c2ecf20Sopenharmony_ci/* Get handle to video output stream */
32998c2ecf20Sopenharmony_cistruct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *hp)
33008c2ecf20Sopenharmony_ci{
33018c2ecf20Sopenharmony_ci	return hp->vid_stream;
33028c2ecf20Sopenharmony_ci}
33038c2ecf20Sopenharmony_ci
33048c2ecf20Sopenharmony_ci
33058c2ecf20Sopenharmony_civoid pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw)
33068c2ecf20Sopenharmony_ci{
33078c2ecf20Sopenharmony_ci	int nr = pvr2_hdw_get_unit_number(hdw);
33088c2ecf20Sopenharmony_ci	LOCK_TAKE(hdw->big_lock);
33098c2ecf20Sopenharmony_ci	do {
33108c2ecf20Sopenharmony_ci		pr_info("pvrusb2: =================  START STATUS CARD #%d  =================\n", nr);
33118c2ecf20Sopenharmony_ci		v4l2_device_call_all(&hdw->v4l2_dev, 0, core, log_status);
33128c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INFO,"cx2341x config:");
33138c2ecf20Sopenharmony_ci		cx2341x_log_status(&hdw->enc_ctl_state, "pvrusb2");
33148c2ecf20Sopenharmony_ci		pvr2_hdw_state_log_state(hdw);
33158c2ecf20Sopenharmony_ci		pr_info("pvrusb2: ==================  END STATUS CARD #%d  ==================\n", nr);
33168c2ecf20Sopenharmony_ci	} while (0);
33178c2ecf20Sopenharmony_ci	LOCK_GIVE(hdw->big_lock);
33188c2ecf20Sopenharmony_ci}
33198c2ecf20Sopenharmony_ci
33208c2ecf20Sopenharmony_ci
33218c2ecf20Sopenharmony_ci/* Grab EEPROM contents, needed for direct method. */
33228c2ecf20Sopenharmony_ci#define EEPROM_SIZE 8192
33238c2ecf20Sopenharmony_ci#define trace_eeprom(...) pvr2_trace(PVR2_TRACE_EEPROM,__VA_ARGS__)
33248c2ecf20Sopenharmony_cistatic u8 *pvr2_full_eeprom_fetch(struct pvr2_hdw *hdw)
33258c2ecf20Sopenharmony_ci{
33268c2ecf20Sopenharmony_ci	struct i2c_msg msg[2];
33278c2ecf20Sopenharmony_ci	u8 *eeprom;
33288c2ecf20Sopenharmony_ci	u8 iadd[2];
33298c2ecf20Sopenharmony_ci	u8 addr;
33308c2ecf20Sopenharmony_ci	u16 eepromSize;
33318c2ecf20Sopenharmony_ci	unsigned int offs;
33328c2ecf20Sopenharmony_ci	int ret;
33338c2ecf20Sopenharmony_ci	int mode16 = 0;
33348c2ecf20Sopenharmony_ci	unsigned pcnt,tcnt;
33358c2ecf20Sopenharmony_ci	eeprom = kzalloc(EEPROM_SIZE, GFP_KERNEL);
33368c2ecf20Sopenharmony_ci	if (!eeprom) {
33378c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
33388c2ecf20Sopenharmony_ci			   "Failed to allocate memory required to read eeprom");
33398c2ecf20Sopenharmony_ci		return NULL;
33408c2ecf20Sopenharmony_ci	}
33418c2ecf20Sopenharmony_ci
33428c2ecf20Sopenharmony_ci	trace_eeprom("Value for eeprom addr from controller was 0x%x",
33438c2ecf20Sopenharmony_ci		     hdw->eeprom_addr);
33448c2ecf20Sopenharmony_ci	addr = hdw->eeprom_addr;
33458c2ecf20Sopenharmony_ci	/* Seems that if the high bit is set, then the *real* eeprom
33468c2ecf20Sopenharmony_ci	   address is shifted right now bit position (noticed this in
33478c2ecf20Sopenharmony_ci	   newer PVR USB2 hardware) */
33488c2ecf20Sopenharmony_ci	if (addr & 0x80) addr >>= 1;
33498c2ecf20Sopenharmony_ci
33508c2ecf20Sopenharmony_ci	/* FX2 documentation states that a 16bit-addressed eeprom is
33518c2ecf20Sopenharmony_ci	   expected if the I2C address is an odd number (yeah, this is
33528c2ecf20Sopenharmony_ci	   strange but it's what they do) */
33538c2ecf20Sopenharmony_ci	mode16 = (addr & 1);
33548c2ecf20Sopenharmony_ci	eepromSize = (mode16 ? EEPROM_SIZE : 256);
33558c2ecf20Sopenharmony_ci	trace_eeprom("Examining %d byte eeprom at location 0x%x using %d bit addressing",
33568c2ecf20Sopenharmony_ci		     eepromSize, addr,
33578c2ecf20Sopenharmony_ci		     mode16 ? 16 : 8);
33588c2ecf20Sopenharmony_ci
33598c2ecf20Sopenharmony_ci	msg[0].addr = addr;
33608c2ecf20Sopenharmony_ci	msg[0].flags = 0;
33618c2ecf20Sopenharmony_ci	msg[0].len = mode16 ? 2 : 1;
33628c2ecf20Sopenharmony_ci	msg[0].buf = iadd;
33638c2ecf20Sopenharmony_ci	msg[1].addr = addr;
33648c2ecf20Sopenharmony_ci	msg[1].flags = I2C_M_RD;
33658c2ecf20Sopenharmony_ci
33668c2ecf20Sopenharmony_ci	/* We have to do the actual eeprom data fetch ourselves, because
33678c2ecf20Sopenharmony_ci	   (1) we're only fetching part of the eeprom, and (2) if we were
33688c2ecf20Sopenharmony_ci	   getting the whole thing our I2C driver can't grab it in one
33698c2ecf20Sopenharmony_ci	   pass - which is what tveeprom is otherwise going to attempt */
33708c2ecf20Sopenharmony_ci	for (tcnt = 0; tcnt < EEPROM_SIZE; tcnt += pcnt) {
33718c2ecf20Sopenharmony_ci		pcnt = 16;
33728c2ecf20Sopenharmony_ci		if (pcnt + tcnt > EEPROM_SIZE) pcnt = EEPROM_SIZE-tcnt;
33738c2ecf20Sopenharmony_ci		offs = tcnt + (eepromSize - EEPROM_SIZE);
33748c2ecf20Sopenharmony_ci		if (mode16) {
33758c2ecf20Sopenharmony_ci			iadd[0] = offs >> 8;
33768c2ecf20Sopenharmony_ci			iadd[1] = offs;
33778c2ecf20Sopenharmony_ci		} else {
33788c2ecf20Sopenharmony_ci			iadd[0] = offs;
33798c2ecf20Sopenharmony_ci		}
33808c2ecf20Sopenharmony_ci		msg[1].len = pcnt;
33818c2ecf20Sopenharmony_ci		msg[1].buf = eeprom+tcnt;
33828c2ecf20Sopenharmony_ci		if ((ret = i2c_transfer(&hdw->i2c_adap,
33838c2ecf20Sopenharmony_ci					msg,ARRAY_SIZE(msg))) != 2) {
33848c2ecf20Sopenharmony_ci			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
33858c2ecf20Sopenharmony_ci				   "eeprom fetch set offs err=%d",ret);
33868c2ecf20Sopenharmony_ci			kfree(eeprom);
33878c2ecf20Sopenharmony_ci			return NULL;
33888c2ecf20Sopenharmony_ci		}
33898c2ecf20Sopenharmony_ci	}
33908c2ecf20Sopenharmony_ci	return eeprom;
33918c2ecf20Sopenharmony_ci}
33928c2ecf20Sopenharmony_ci
33938c2ecf20Sopenharmony_ci
33948c2ecf20Sopenharmony_civoid pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *hdw,
33958c2ecf20Sopenharmony_ci				int mode,
33968c2ecf20Sopenharmony_ci				int enable_flag)
33978c2ecf20Sopenharmony_ci{
33988c2ecf20Sopenharmony_ci	int ret;
33998c2ecf20Sopenharmony_ci	u16 address;
34008c2ecf20Sopenharmony_ci	unsigned int pipe;
34018c2ecf20Sopenharmony_ci	LOCK_TAKE(hdw->big_lock); do {
34028c2ecf20Sopenharmony_ci		if ((hdw->fw_buffer == NULL) == !enable_flag) break;
34038c2ecf20Sopenharmony_ci
34048c2ecf20Sopenharmony_ci		if (!enable_flag) {
34058c2ecf20Sopenharmony_ci			pvr2_trace(PVR2_TRACE_FIRMWARE,
34068c2ecf20Sopenharmony_ci				   "Cleaning up after CPU firmware fetch");
34078c2ecf20Sopenharmony_ci			kfree(hdw->fw_buffer);
34088c2ecf20Sopenharmony_ci			hdw->fw_buffer = NULL;
34098c2ecf20Sopenharmony_ci			hdw->fw_size = 0;
34108c2ecf20Sopenharmony_ci			if (hdw->fw_cpu_flag) {
34118c2ecf20Sopenharmony_ci				/* Now release the CPU.  It will disconnect
34128c2ecf20Sopenharmony_ci				   and reconnect later. */
34138c2ecf20Sopenharmony_ci				pvr2_hdw_cpureset_assert(hdw,0);
34148c2ecf20Sopenharmony_ci			}
34158c2ecf20Sopenharmony_ci			break;
34168c2ecf20Sopenharmony_ci		}
34178c2ecf20Sopenharmony_ci
34188c2ecf20Sopenharmony_ci		hdw->fw_cpu_flag = (mode != 2);
34198c2ecf20Sopenharmony_ci		if (hdw->fw_cpu_flag) {
34208c2ecf20Sopenharmony_ci			hdw->fw_size = (mode == 1) ? 0x4000 : 0x2000;
34218c2ecf20Sopenharmony_ci			pvr2_trace(PVR2_TRACE_FIRMWARE,
34228c2ecf20Sopenharmony_ci				   "Preparing to suck out CPU firmware (size=%u)",
34238c2ecf20Sopenharmony_ci				   hdw->fw_size);
34248c2ecf20Sopenharmony_ci			hdw->fw_buffer = kzalloc(hdw->fw_size,GFP_KERNEL);
34258c2ecf20Sopenharmony_ci			if (!hdw->fw_buffer) {
34268c2ecf20Sopenharmony_ci				hdw->fw_size = 0;
34278c2ecf20Sopenharmony_ci				break;
34288c2ecf20Sopenharmony_ci			}
34298c2ecf20Sopenharmony_ci
34308c2ecf20Sopenharmony_ci			/* We have to hold the CPU during firmware upload. */
34318c2ecf20Sopenharmony_ci			pvr2_hdw_cpureset_assert(hdw,1);
34328c2ecf20Sopenharmony_ci
34338c2ecf20Sopenharmony_ci			/* download the firmware from address 0000-1fff in 2048
34348c2ecf20Sopenharmony_ci			   (=0x800) bytes chunk. */
34358c2ecf20Sopenharmony_ci
34368c2ecf20Sopenharmony_ci			pvr2_trace(PVR2_TRACE_FIRMWARE,
34378c2ecf20Sopenharmony_ci				   "Grabbing CPU firmware");
34388c2ecf20Sopenharmony_ci			pipe = usb_rcvctrlpipe(hdw->usb_dev, 0);
34398c2ecf20Sopenharmony_ci			for(address = 0; address < hdw->fw_size;
34408c2ecf20Sopenharmony_ci			    address += 0x800) {
34418c2ecf20Sopenharmony_ci				ret = usb_control_msg(hdw->usb_dev,pipe,
34428c2ecf20Sopenharmony_ci						      0xa0,0xc0,
34438c2ecf20Sopenharmony_ci						      address,0,
34448c2ecf20Sopenharmony_ci						      hdw->fw_buffer+address,
34458c2ecf20Sopenharmony_ci						      0x800,1000);
34468c2ecf20Sopenharmony_ci				if (ret < 0) break;
34478c2ecf20Sopenharmony_ci			}
34488c2ecf20Sopenharmony_ci
34498c2ecf20Sopenharmony_ci			pvr2_trace(PVR2_TRACE_FIRMWARE,
34508c2ecf20Sopenharmony_ci				   "Done grabbing CPU firmware");
34518c2ecf20Sopenharmony_ci		} else {
34528c2ecf20Sopenharmony_ci			pvr2_trace(PVR2_TRACE_FIRMWARE,
34538c2ecf20Sopenharmony_ci				   "Sucking down EEPROM contents");
34548c2ecf20Sopenharmony_ci			hdw->fw_buffer = pvr2_full_eeprom_fetch(hdw);
34558c2ecf20Sopenharmony_ci			if (!hdw->fw_buffer) {
34568c2ecf20Sopenharmony_ci				pvr2_trace(PVR2_TRACE_FIRMWARE,
34578c2ecf20Sopenharmony_ci					   "EEPROM content suck failed.");
34588c2ecf20Sopenharmony_ci				break;
34598c2ecf20Sopenharmony_ci			}
34608c2ecf20Sopenharmony_ci			hdw->fw_size = EEPROM_SIZE;
34618c2ecf20Sopenharmony_ci			pvr2_trace(PVR2_TRACE_FIRMWARE,
34628c2ecf20Sopenharmony_ci				   "Done sucking down EEPROM contents");
34638c2ecf20Sopenharmony_ci		}
34648c2ecf20Sopenharmony_ci
34658c2ecf20Sopenharmony_ci	} while (0); LOCK_GIVE(hdw->big_lock);
34668c2ecf20Sopenharmony_ci}
34678c2ecf20Sopenharmony_ci
34688c2ecf20Sopenharmony_ci
34698c2ecf20Sopenharmony_ci/* Return true if we're in a mode for retrieval CPU firmware */
34708c2ecf20Sopenharmony_ciint pvr2_hdw_cpufw_get_enabled(struct pvr2_hdw *hdw)
34718c2ecf20Sopenharmony_ci{
34728c2ecf20Sopenharmony_ci	return hdw->fw_buffer != NULL;
34738c2ecf20Sopenharmony_ci}
34748c2ecf20Sopenharmony_ci
34758c2ecf20Sopenharmony_ci
34768c2ecf20Sopenharmony_ciint pvr2_hdw_cpufw_get(struct pvr2_hdw *hdw,unsigned int offs,
34778c2ecf20Sopenharmony_ci		       char *buf,unsigned int cnt)
34788c2ecf20Sopenharmony_ci{
34798c2ecf20Sopenharmony_ci	int ret = -EINVAL;
34808c2ecf20Sopenharmony_ci	LOCK_TAKE(hdw->big_lock); do {
34818c2ecf20Sopenharmony_ci		if (!buf) break;
34828c2ecf20Sopenharmony_ci		if (!cnt) break;
34838c2ecf20Sopenharmony_ci
34848c2ecf20Sopenharmony_ci		if (!hdw->fw_buffer) {
34858c2ecf20Sopenharmony_ci			ret = -EIO;
34868c2ecf20Sopenharmony_ci			break;
34878c2ecf20Sopenharmony_ci		}
34888c2ecf20Sopenharmony_ci
34898c2ecf20Sopenharmony_ci		if (offs >= hdw->fw_size) {
34908c2ecf20Sopenharmony_ci			pvr2_trace(PVR2_TRACE_FIRMWARE,
34918c2ecf20Sopenharmony_ci				   "Read firmware data offs=%d EOF",
34928c2ecf20Sopenharmony_ci				   offs);
34938c2ecf20Sopenharmony_ci			ret = 0;
34948c2ecf20Sopenharmony_ci			break;
34958c2ecf20Sopenharmony_ci		}
34968c2ecf20Sopenharmony_ci
34978c2ecf20Sopenharmony_ci		if (offs + cnt > hdw->fw_size) cnt = hdw->fw_size - offs;
34988c2ecf20Sopenharmony_ci
34998c2ecf20Sopenharmony_ci		memcpy(buf,hdw->fw_buffer+offs,cnt);
35008c2ecf20Sopenharmony_ci
35018c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_FIRMWARE,
35028c2ecf20Sopenharmony_ci			   "Read firmware data offs=%d cnt=%d",
35038c2ecf20Sopenharmony_ci			   offs,cnt);
35048c2ecf20Sopenharmony_ci		ret = cnt;
35058c2ecf20Sopenharmony_ci	} while (0); LOCK_GIVE(hdw->big_lock);
35068c2ecf20Sopenharmony_ci
35078c2ecf20Sopenharmony_ci	return ret;
35088c2ecf20Sopenharmony_ci}
35098c2ecf20Sopenharmony_ci
35108c2ecf20Sopenharmony_ci
35118c2ecf20Sopenharmony_ciint pvr2_hdw_v4l_get_minor_number(struct pvr2_hdw *hdw,
35128c2ecf20Sopenharmony_ci				  enum pvr2_v4l_type index)
35138c2ecf20Sopenharmony_ci{
35148c2ecf20Sopenharmony_ci	switch (index) {
35158c2ecf20Sopenharmony_ci	case pvr2_v4l_type_video: return hdw->v4l_minor_number_video;
35168c2ecf20Sopenharmony_ci	case pvr2_v4l_type_vbi: return hdw->v4l_minor_number_vbi;
35178c2ecf20Sopenharmony_ci	case pvr2_v4l_type_radio: return hdw->v4l_minor_number_radio;
35188c2ecf20Sopenharmony_ci	default: return -1;
35198c2ecf20Sopenharmony_ci	}
35208c2ecf20Sopenharmony_ci}
35218c2ecf20Sopenharmony_ci
35228c2ecf20Sopenharmony_ci
35238c2ecf20Sopenharmony_ci/* Store a v4l minor device number */
35248c2ecf20Sopenharmony_civoid pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *hdw,
35258c2ecf20Sopenharmony_ci				     enum pvr2_v4l_type index,int v)
35268c2ecf20Sopenharmony_ci{
35278c2ecf20Sopenharmony_ci	switch (index) {
35288c2ecf20Sopenharmony_ci	case pvr2_v4l_type_video: hdw->v4l_minor_number_video = v;break;
35298c2ecf20Sopenharmony_ci	case pvr2_v4l_type_vbi: hdw->v4l_minor_number_vbi = v;break;
35308c2ecf20Sopenharmony_ci	case pvr2_v4l_type_radio: hdw->v4l_minor_number_radio = v;break;
35318c2ecf20Sopenharmony_ci	default: break;
35328c2ecf20Sopenharmony_ci	}
35338c2ecf20Sopenharmony_ci}
35348c2ecf20Sopenharmony_ci
35358c2ecf20Sopenharmony_ci
35368c2ecf20Sopenharmony_cistatic void pvr2_ctl_write_complete(struct urb *urb)
35378c2ecf20Sopenharmony_ci{
35388c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = urb->context;
35398c2ecf20Sopenharmony_ci	hdw->ctl_write_pend_flag = 0;
35408c2ecf20Sopenharmony_ci	if (hdw->ctl_read_pend_flag) return;
35418c2ecf20Sopenharmony_ci	complete(&hdw->ctl_done);
35428c2ecf20Sopenharmony_ci}
35438c2ecf20Sopenharmony_ci
35448c2ecf20Sopenharmony_ci
35458c2ecf20Sopenharmony_cistatic void pvr2_ctl_read_complete(struct urb *urb)
35468c2ecf20Sopenharmony_ci{
35478c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = urb->context;
35488c2ecf20Sopenharmony_ci	hdw->ctl_read_pend_flag = 0;
35498c2ecf20Sopenharmony_ci	if (hdw->ctl_write_pend_flag) return;
35508c2ecf20Sopenharmony_ci	complete(&hdw->ctl_done);
35518c2ecf20Sopenharmony_ci}
35528c2ecf20Sopenharmony_ci
35538c2ecf20Sopenharmony_cistruct hdw_timer {
35548c2ecf20Sopenharmony_ci	struct timer_list timer;
35558c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw;
35568c2ecf20Sopenharmony_ci};
35578c2ecf20Sopenharmony_ci
35588c2ecf20Sopenharmony_cistatic void pvr2_ctl_timeout(struct timer_list *t)
35598c2ecf20Sopenharmony_ci{
35608c2ecf20Sopenharmony_ci	struct hdw_timer *timer = from_timer(timer, t, timer);
35618c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = timer->hdw;
35628c2ecf20Sopenharmony_ci
35638c2ecf20Sopenharmony_ci	if (hdw->ctl_write_pend_flag || hdw->ctl_read_pend_flag) {
35648c2ecf20Sopenharmony_ci		hdw->ctl_timeout_flag = !0;
35658c2ecf20Sopenharmony_ci		if (hdw->ctl_write_pend_flag)
35668c2ecf20Sopenharmony_ci			usb_unlink_urb(hdw->ctl_write_urb);
35678c2ecf20Sopenharmony_ci		if (hdw->ctl_read_pend_flag)
35688c2ecf20Sopenharmony_ci			usb_unlink_urb(hdw->ctl_read_urb);
35698c2ecf20Sopenharmony_ci	}
35708c2ecf20Sopenharmony_ci}
35718c2ecf20Sopenharmony_ci
35728c2ecf20Sopenharmony_ci
35738c2ecf20Sopenharmony_ci/* Issue a command and get a response from the device.  This extended
35748c2ecf20Sopenharmony_ci   version includes a probe flag (which if set means that device errors
35758c2ecf20Sopenharmony_ci   should not be logged or treated as fatal) and a timeout in jiffies.
35768c2ecf20Sopenharmony_ci   This can be used to non-lethally probe the health of endpoint 1. */
35778c2ecf20Sopenharmony_cistatic int pvr2_send_request_ex(struct pvr2_hdw *hdw,
35788c2ecf20Sopenharmony_ci				unsigned int timeout,int probe_fl,
35798c2ecf20Sopenharmony_ci				void *write_data,unsigned int write_len,
35808c2ecf20Sopenharmony_ci				void *read_data,unsigned int read_len)
35818c2ecf20Sopenharmony_ci{
35828c2ecf20Sopenharmony_ci	unsigned int idx;
35838c2ecf20Sopenharmony_ci	int status = 0;
35848c2ecf20Sopenharmony_ci	struct hdw_timer timer = {
35858c2ecf20Sopenharmony_ci		.hdw = hdw,
35868c2ecf20Sopenharmony_ci	};
35878c2ecf20Sopenharmony_ci
35888c2ecf20Sopenharmony_ci	if (!hdw->ctl_lock_held) {
35898c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
35908c2ecf20Sopenharmony_ci			   "Attempted to execute control transfer without lock!!");
35918c2ecf20Sopenharmony_ci		return -EDEADLK;
35928c2ecf20Sopenharmony_ci	}
35938c2ecf20Sopenharmony_ci	if (!hdw->flag_ok && !probe_fl) {
35948c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
35958c2ecf20Sopenharmony_ci			   "Attempted to execute control transfer when device not ok");
35968c2ecf20Sopenharmony_ci		return -EIO;
35978c2ecf20Sopenharmony_ci	}
35988c2ecf20Sopenharmony_ci	if (!(hdw->ctl_read_urb && hdw->ctl_write_urb)) {
35998c2ecf20Sopenharmony_ci		if (!probe_fl) {
36008c2ecf20Sopenharmony_ci			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
36018c2ecf20Sopenharmony_ci				   "Attempted to execute control transfer when USB is disconnected");
36028c2ecf20Sopenharmony_ci		}
36038c2ecf20Sopenharmony_ci		return -ENOTTY;
36048c2ecf20Sopenharmony_ci	}
36058c2ecf20Sopenharmony_ci
36068c2ecf20Sopenharmony_ci	/* Ensure that we have sane parameters */
36078c2ecf20Sopenharmony_ci	if (!write_data) write_len = 0;
36088c2ecf20Sopenharmony_ci	if (!read_data) read_len = 0;
36098c2ecf20Sopenharmony_ci	if (write_len > PVR2_CTL_BUFFSIZE) {
36108c2ecf20Sopenharmony_ci		pvr2_trace(
36118c2ecf20Sopenharmony_ci			PVR2_TRACE_ERROR_LEGS,
36128c2ecf20Sopenharmony_ci			"Attempted to execute %d byte control-write transfer (limit=%d)",
36138c2ecf20Sopenharmony_ci			write_len,PVR2_CTL_BUFFSIZE);
36148c2ecf20Sopenharmony_ci		return -EINVAL;
36158c2ecf20Sopenharmony_ci	}
36168c2ecf20Sopenharmony_ci	if (read_len > PVR2_CTL_BUFFSIZE) {
36178c2ecf20Sopenharmony_ci		pvr2_trace(
36188c2ecf20Sopenharmony_ci			PVR2_TRACE_ERROR_LEGS,
36198c2ecf20Sopenharmony_ci			"Attempted to execute %d byte control-read transfer (limit=%d)",
36208c2ecf20Sopenharmony_ci			write_len,PVR2_CTL_BUFFSIZE);
36218c2ecf20Sopenharmony_ci		return -EINVAL;
36228c2ecf20Sopenharmony_ci	}
36238c2ecf20Sopenharmony_ci	if ((!write_len) && (!read_len)) {
36248c2ecf20Sopenharmony_ci		pvr2_trace(
36258c2ecf20Sopenharmony_ci			PVR2_TRACE_ERROR_LEGS,
36268c2ecf20Sopenharmony_ci			"Attempted to execute null control transfer?");
36278c2ecf20Sopenharmony_ci		return -EINVAL;
36288c2ecf20Sopenharmony_ci	}
36298c2ecf20Sopenharmony_ci
36308c2ecf20Sopenharmony_ci
36318c2ecf20Sopenharmony_ci	hdw->cmd_debug_state = 1;
36328c2ecf20Sopenharmony_ci	if (write_len && write_data)
36338c2ecf20Sopenharmony_ci		hdw->cmd_debug_code = ((unsigned char *)write_data)[0];
36348c2ecf20Sopenharmony_ci	else
36358c2ecf20Sopenharmony_ci		hdw->cmd_debug_code = 0;
36368c2ecf20Sopenharmony_ci	hdw->cmd_debug_write_len = write_len;
36378c2ecf20Sopenharmony_ci	hdw->cmd_debug_read_len = read_len;
36388c2ecf20Sopenharmony_ci
36398c2ecf20Sopenharmony_ci	/* Initialize common stuff */
36408c2ecf20Sopenharmony_ci	init_completion(&hdw->ctl_done);
36418c2ecf20Sopenharmony_ci	hdw->ctl_timeout_flag = 0;
36428c2ecf20Sopenharmony_ci	hdw->ctl_write_pend_flag = 0;
36438c2ecf20Sopenharmony_ci	hdw->ctl_read_pend_flag = 0;
36448c2ecf20Sopenharmony_ci	timer_setup_on_stack(&timer.timer, pvr2_ctl_timeout, 0);
36458c2ecf20Sopenharmony_ci	timer.timer.expires = jiffies + timeout;
36468c2ecf20Sopenharmony_ci
36478c2ecf20Sopenharmony_ci	if (write_len && write_data) {
36488c2ecf20Sopenharmony_ci		hdw->cmd_debug_state = 2;
36498c2ecf20Sopenharmony_ci		/* Transfer write data to internal buffer */
36508c2ecf20Sopenharmony_ci		for (idx = 0; idx < write_len; idx++) {
36518c2ecf20Sopenharmony_ci			hdw->ctl_write_buffer[idx] =
36528c2ecf20Sopenharmony_ci				((unsigned char *)write_data)[idx];
36538c2ecf20Sopenharmony_ci		}
36548c2ecf20Sopenharmony_ci		/* Initiate a write request */
36558c2ecf20Sopenharmony_ci		usb_fill_bulk_urb(hdw->ctl_write_urb,
36568c2ecf20Sopenharmony_ci				  hdw->usb_dev,
36578c2ecf20Sopenharmony_ci				  usb_sndbulkpipe(hdw->usb_dev,
36588c2ecf20Sopenharmony_ci						  PVR2_CTL_WRITE_ENDPOINT),
36598c2ecf20Sopenharmony_ci				  hdw->ctl_write_buffer,
36608c2ecf20Sopenharmony_ci				  write_len,
36618c2ecf20Sopenharmony_ci				  pvr2_ctl_write_complete,
36628c2ecf20Sopenharmony_ci				  hdw);
36638c2ecf20Sopenharmony_ci		hdw->ctl_write_urb->actual_length = 0;
36648c2ecf20Sopenharmony_ci		hdw->ctl_write_pend_flag = !0;
36658c2ecf20Sopenharmony_ci		if (usb_urb_ep_type_check(hdw->ctl_write_urb)) {
36668c2ecf20Sopenharmony_ci			pvr2_trace(
36678c2ecf20Sopenharmony_ci				PVR2_TRACE_ERROR_LEGS,
36688c2ecf20Sopenharmony_ci				"Invalid write control endpoint");
36698c2ecf20Sopenharmony_ci			return -EINVAL;
36708c2ecf20Sopenharmony_ci		}
36718c2ecf20Sopenharmony_ci		status = usb_submit_urb(hdw->ctl_write_urb,GFP_KERNEL);
36728c2ecf20Sopenharmony_ci		if (status < 0) {
36738c2ecf20Sopenharmony_ci			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
36748c2ecf20Sopenharmony_ci				   "Failed to submit write-control URB status=%d",
36758c2ecf20Sopenharmony_cistatus);
36768c2ecf20Sopenharmony_ci			hdw->ctl_write_pend_flag = 0;
36778c2ecf20Sopenharmony_ci			goto done;
36788c2ecf20Sopenharmony_ci		}
36798c2ecf20Sopenharmony_ci	}
36808c2ecf20Sopenharmony_ci
36818c2ecf20Sopenharmony_ci	if (read_len) {
36828c2ecf20Sopenharmony_ci		hdw->cmd_debug_state = 3;
36838c2ecf20Sopenharmony_ci		memset(hdw->ctl_read_buffer,0x43,read_len);
36848c2ecf20Sopenharmony_ci		/* Initiate a read request */
36858c2ecf20Sopenharmony_ci		usb_fill_bulk_urb(hdw->ctl_read_urb,
36868c2ecf20Sopenharmony_ci				  hdw->usb_dev,
36878c2ecf20Sopenharmony_ci				  usb_rcvbulkpipe(hdw->usb_dev,
36888c2ecf20Sopenharmony_ci						  PVR2_CTL_READ_ENDPOINT),
36898c2ecf20Sopenharmony_ci				  hdw->ctl_read_buffer,
36908c2ecf20Sopenharmony_ci				  read_len,
36918c2ecf20Sopenharmony_ci				  pvr2_ctl_read_complete,
36928c2ecf20Sopenharmony_ci				  hdw);
36938c2ecf20Sopenharmony_ci		hdw->ctl_read_urb->actual_length = 0;
36948c2ecf20Sopenharmony_ci		hdw->ctl_read_pend_flag = !0;
36958c2ecf20Sopenharmony_ci		if (usb_urb_ep_type_check(hdw->ctl_read_urb)) {
36968c2ecf20Sopenharmony_ci			pvr2_trace(
36978c2ecf20Sopenharmony_ci				PVR2_TRACE_ERROR_LEGS,
36988c2ecf20Sopenharmony_ci				"Invalid read control endpoint");
36998c2ecf20Sopenharmony_ci			return -EINVAL;
37008c2ecf20Sopenharmony_ci		}
37018c2ecf20Sopenharmony_ci		status = usb_submit_urb(hdw->ctl_read_urb,GFP_KERNEL);
37028c2ecf20Sopenharmony_ci		if (status < 0) {
37038c2ecf20Sopenharmony_ci			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
37048c2ecf20Sopenharmony_ci				   "Failed to submit read-control URB status=%d",
37058c2ecf20Sopenharmony_cistatus);
37068c2ecf20Sopenharmony_ci			hdw->ctl_read_pend_flag = 0;
37078c2ecf20Sopenharmony_ci			goto done;
37088c2ecf20Sopenharmony_ci		}
37098c2ecf20Sopenharmony_ci	}
37108c2ecf20Sopenharmony_ci
37118c2ecf20Sopenharmony_ci	/* Start timer */
37128c2ecf20Sopenharmony_ci	add_timer(&timer.timer);
37138c2ecf20Sopenharmony_ci
37148c2ecf20Sopenharmony_ci	/* Now wait for all I/O to complete */
37158c2ecf20Sopenharmony_ci	hdw->cmd_debug_state = 4;
37168c2ecf20Sopenharmony_ci	while (hdw->ctl_write_pend_flag || hdw->ctl_read_pend_flag) {
37178c2ecf20Sopenharmony_ci		wait_for_completion(&hdw->ctl_done);
37188c2ecf20Sopenharmony_ci	}
37198c2ecf20Sopenharmony_ci	hdw->cmd_debug_state = 5;
37208c2ecf20Sopenharmony_ci
37218c2ecf20Sopenharmony_ci	/* Stop timer */
37228c2ecf20Sopenharmony_ci	del_timer_sync(&timer.timer);
37238c2ecf20Sopenharmony_ci
37248c2ecf20Sopenharmony_ci	hdw->cmd_debug_state = 6;
37258c2ecf20Sopenharmony_ci	status = 0;
37268c2ecf20Sopenharmony_ci
37278c2ecf20Sopenharmony_ci	if (hdw->ctl_timeout_flag) {
37288c2ecf20Sopenharmony_ci		status = -ETIMEDOUT;
37298c2ecf20Sopenharmony_ci		if (!probe_fl) {
37308c2ecf20Sopenharmony_ci			pvr2_trace(PVR2_TRACE_ERROR_LEGS,
37318c2ecf20Sopenharmony_ci				   "Timed out control-write");
37328c2ecf20Sopenharmony_ci		}
37338c2ecf20Sopenharmony_ci		goto done;
37348c2ecf20Sopenharmony_ci	}
37358c2ecf20Sopenharmony_ci
37368c2ecf20Sopenharmony_ci	if (write_len) {
37378c2ecf20Sopenharmony_ci		/* Validate results of write request */
37388c2ecf20Sopenharmony_ci		if ((hdw->ctl_write_urb->status != 0) &&
37398c2ecf20Sopenharmony_ci		    (hdw->ctl_write_urb->status != -ENOENT) &&
37408c2ecf20Sopenharmony_ci		    (hdw->ctl_write_urb->status != -ESHUTDOWN) &&
37418c2ecf20Sopenharmony_ci		    (hdw->ctl_write_urb->status != -ECONNRESET)) {
37428c2ecf20Sopenharmony_ci			/* USB subsystem is reporting some kind of failure
37438c2ecf20Sopenharmony_ci			   on the write */
37448c2ecf20Sopenharmony_ci			status = hdw->ctl_write_urb->status;
37458c2ecf20Sopenharmony_ci			if (!probe_fl) {
37468c2ecf20Sopenharmony_ci				pvr2_trace(PVR2_TRACE_ERROR_LEGS,
37478c2ecf20Sopenharmony_ci					   "control-write URB failure, status=%d",
37488c2ecf20Sopenharmony_ci					   status);
37498c2ecf20Sopenharmony_ci			}
37508c2ecf20Sopenharmony_ci			goto done;
37518c2ecf20Sopenharmony_ci		}
37528c2ecf20Sopenharmony_ci		if (hdw->ctl_write_urb->actual_length < write_len) {
37538c2ecf20Sopenharmony_ci			/* Failed to write enough data */
37548c2ecf20Sopenharmony_ci			status = -EIO;
37558c2ecf20Sopenharmony_ci			if (!probe_fl) {
37568c2ecf20Sopenharmony_ci				pvr2_trace(PVR2_TRACE_ERROR_LEGS,
37578c2ecf20Sopenharmony_ci					   "control-write URB short, expected=%d got=%d",
37588c2ecf20Sopenharmony_ci					   write_len,
37598c2ecf20Sopenharmony_ci					   hdw->ctl_write_urb->actual_length);
37608c2ecf20Sopenharmony_ci			}
37618c2ecf20Sopenharmony_ci			goto done;
37628c2ecf20Sopenharmony_ci		}
37638c2ecf20Sopenharmony_ci	}
37648c2ecf20Sopenharmony_ci	if (read_len && read_data) {
37658c2ecf20Sopenharmony_ci		/* Validate results of read request */
37668c2ecf20Sopenharmony_ci		if ((hdw->ctl_read_urb->status != 0) &&
37678c2ecf20Sopenharmony_ci		    (hdw->ctl_read_urb->status != -ENOENT) &&
37688c2ecf20Sopenharmony_ci		    (hdw->ctl_read_urb->status != -ESHUTDOWN) &&
37698c2ecf20Sopenharmony_ci		    (hdw->ctl_read_urb->status != -ECONNRESET)) {
37708c2ecf20Sopenharmony_ci			/* USB subsystem is reporting some kind of failure
37718c2ecf20Sopenharmony_ci			   on the read */
37728c2ecf20Sopenharmony_ci			status = hdw->ctl_read_urb->status;
37738c2ecf20Sopenharmony_ci			if (!probe_fl) {
37748c2ecf20Sopenharmony_ci				pvr2_trace(PVR2_TRACE_ERROR_LEGS,
37758c2ecf20Sopenharmony_ci					   "control-read URB failure, status=%d",
37768c2ecf20Sopenharmony_ci					   status);
37778c2ecf20Sopenharmony_ci			}
37788c2ecf20Sopenharmony_ci			goto done;
37798c2ecf20Sopenharmony_ci		}
37808c2ecf20Sopenharmony_ci		if (hdw->ctl_read_urb->actual_length < read_len) {
37818c2ecf20Sopenharmony_ci			/* Failed to read enough data */
37828c2ecf20Sopenharmony_ci			status = -EIO;
37838c2ecf20Sopenharmony_ci			if (!probe_fl) {
37848c2ecf20Sopenharmony_ci				pvr2_trace(PVR2_TRACE_ERROR_LEGS,
37858c2ecf20Sopenharmony_ci					   "control-read URB short, expected=%d got=%d",
37868c2ecf20Sopenharmony_ci					   read_len,
37878c2ecf20Sopenharmony_ci					   hdw->ctl_read_urb->actual_length);
37888c2ecf20Sopenharmony_ci			}
37898c2ecf20Sopenharmony_ci			goto done;
37908c2ecf20Sopenharmony_ci		}
37918c2ecf20Sopenharmony_ci		/* Transfer retrieved data out from internal buffer */
37928c2ecf20Sopenharmony_ci		for (idx = 0; idx < read_len; idx++) {
37938c2ecf20Sopenharmony_ci			((unsigned char *)read_data)[idx] =
37948c2ecf20Sopenharmony_ci				hdw->ctl_read_buffer[idx];
37958c2ecf20Sopenharmony_ci		}
37968c2ecf20Sopenharmony_ci	}
37978c2ecf20Sopenharmony_ci
37988c2ecf20Sopenharmony_ci done:
37998c2ecf20Sopenharmony_ci
38008c2ecf20Sopenharmony_ci	hdw->cmd_debug_state = 0;
38018c2ecf20Sopenharmony_ci	if ((status < 0) && (!probe_fl)) {
38028c2ecf20Sopenharmony_ci		pvr2_hdw_render_useless(hdw);
38038c2ecf20Sopenharmony_ci	}
38048c2ecf20Sopenharmony_ci	destroy_timer_on_stack(&timer.timer);
38058c2ecf20Sopenharmony_ci
38068c2ecf20Sopenharmony_ci	return status;
38078c2ecf20Sopenharmony_ci}
38088c2ecf20Sopenharmony_ci
38098c2ecf20Sopenharmony_ci
38108c2ecf20Sopenharmony_ciint pvr2_send_request(struct pvr2_hdw *hdw,
38118c2ecf20Sopenharmony_ci		      void *write_data,unsigned int write_len,
38128c2ecf20Sopenharmony_ci		      void *read_data,unsigned int read_len)
38138c2ecf20Sopenharmony_ci{
38148c2ecf20Sopenharmony_ci	return pvr2_send_request_ex(hdw,HZ*4,0,
38158c2ecf20Sopenharmony_ci				    write_data,write_len,
38168c2ecf20Sopenharmony_ci				    read_data,read_len);
38178c2ecf20Sopenharmony_ci}
38188c2ecf20Sopenharmony_ci
38198c2ecf20Sopenharmony_ci
38208c2ecf20Sopenharmony_cistatic int pvr2_issue_simple_cmd(struct pvr2_hdw *hdw,u32 cmdcode)
38218c2ecf20Sopenharmony_ci{
38228c2ecf20Sopenharmony_ci	int ret;
38238c2ecf20Sopenharmony_ci	unsigned int cnt = 1;
38248c2ecf20Sopenharmony_ci	unsigned int args = 0;
38258c2ecf20Sopenharmony_ci	LOCK_TAKE(hdw->ctl_lock);
38268c2ecf20Sopenharmony_ci	hdw->cmd_buffer[0] = cmdcode & 0xffu;
38278c2ecf20Sopenharmony_ci	args = (cmdcode >> 8) & 0xffu;
38288c2ecf20Sopenharmony_ci	args = (args > 2) ? 2 : args;
38298c2ecf20Sopenharmony_ci	if (args) {
38308c2ecf20Sopenharmony_ci		cnt += args;
38318c2ecf20Sopenharmony_ci		hdw->cmd_buffer[1] = (cmdcode >> 16) & 0xffu;
38328c2ecf20Sopenharmony_ci		if (args > 1) {
38338c2ecf20Sopenharmony_ci			hdw->cmd_buffer[2] = (cmdcode >> 24) & 0xffu;
38348c2ecf20Sopenharmony_ci		}
38358c2ecf20Sopenharmony_ci	}
38368c2ecf20Sopenharmony_ci	if (pvrusb2_debug & PVR2_TRACE_INIT) {
38378c2ecf20Sopenharmony_ci		unsigned int idx;
38388c2ecf20Sopenharmony_ci		unsigned int ccnt,bcnt;
38398c2ecf20Sopenharmony_ci		char tbuf[50];
38408c2ecf20Sopenharmony_ci		cmdcode &= 0xffu;
38418c2ecf20Sopenharmony_ci		bcnt = 0;
38428c2ecf20Sopenharmony_ci		ccnt = scnprintf(tbuf+bcnt,
38438c2ecf20Sopenharmony_ci				 sizeof(tbuf)-bcnt,
38448c2ecf20Sopenharmony_ci				 "Sending FX2 command 0x%x",cmdcode);
38458c2ecf20Sopenharmony_ci		bcnt += ccnt;
38468c2ecf20Sopenharmony_ci		for (idx = 0; idx < ARRAY_SIZE(pvr2_fx2cmd_desc); idx++) {
38478c2ecf20Sopenharmony_ci			if (pvr2_fx2cmd_desc[idx].id == cmdcode) {
38488c2ecf20Sopenharmony_ci				ccnt = scnprintf(tbuf+bcnt,
38498c2ecf20Sopenharmony_ci						 sizeof(tbuf)-bcnt,
38508c2ecf20Sopenharmony_ci						 " \"%s\"",
38518c2ecf20Sopenharmony_ci						 pvr2_fx2cmd_desc[idx].desc);
38528c2ecf20Sopenharmony_ci				bcnt += ccnt;
38538c2ecf20Sopenharmony_ci				break;
38548c2ecf20Sopenharmony_ci			}
38558c2ecf20Sopenharmony_ci		}
38568c2ecf20Sopenharmony_ci		if (args) {
38578c2ecf20Sopenharmony_ci			ccnt = scnprintf(tbuf+bcnt,
38588c2ecf20Sopenharmony_ci					 sizeof(tbuf)-bcnt,
38598c2ecf20Sopenharmony_ci					 " (%u",hdw->cmd_buffer[1]);
38608c2ecf20Sopenharmony_ci			bcnt += ccnt;
38618c2ecf20Sopenharmony_ci			if (args > 1) {
38628c2ecf20Sopenharmony_ci				ccnt = scnprintf(tbuf+bcnt,
38638c2ecf20Sopenharmony_ci						 sizeof(tbuf)-bcnt,
38648c2ecf20Sopenharmony_ci						 ",%u",hdw->cmd_buffer[2]);
38658c2ecf20Sopenharmony_ci				bcnt += ccnt;
38668c2ecf20Sopenharmony_ci			}
38678c2ecf20Sopenharmony_ci			ccnt = scnprintf(tbuf+bcnt,
38688c2ecf20Sopenharmony_ci					 sizeof(tbuf)-bcnt,
38698c2ecf20Sopenharmony_ci					 ")");
38708c2ecf20Sopenharmony_ci			bcnt += ccnt;
38718c2ecf20Sopenharmony_ci		}
38728c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INIT,"%.*s",bcnt,tbuf);
38738c2ecf20Sopenharmony_ci	}
38748c2ecf20Sopenharmony_ci	ret = pvr2_send_request(hdw,hdw->cmd_buffer,cnt,NULL,0);
38758c2ecf20Sopenharmony_ci	LOCK_GIVE(hdw->ctl_lock);
38768c2ecf20Sopenharmony_ci	return ret;
38778c2ecf20Sopenharmony_ci}
38788c2ecf20Sopenharmony_ci
38798c2ecf20Sopenharmony_ci
38808c2ecf20Sopenharmony_ciint pvr2_write_register(struct pvr2_hdw *hdw, u16 reg, u32 data)
38818c2ecf20Sopenharmony_ci{
38828c2ecf20Sopenharmony_ci	int ret;
38838c2ecf20Sopenharmony_ci
38848c2ecf20Sopenharmony_ci	LOCK_TAKE(hdw->ctl_lock);
38858c2ecf20Sopenharmony_ci
38868c2ecf20Sopenharmony_ci	hdw->cmd_buffer[0] = FX2CMD_REG_WRITE;  /* write register prefix */
38878c2ecf20Sopenharmony_ci	PVR2_DECOMPOSE_LE(hdw->cmd_buffer,1,data);
38888c2ecf20Sopenharmony_ci	hdw->cmd_buffer[5] = 0;
38898c2ecf20Sopenharmony_ci	hdw->cmd_buffer[6] = (reg >> 8) & 0xff;
38908c2ecf20Sopenharmony_ci	hdw->cmd_buffer[7] = reg & 0xff;
38918c2ecf20Sopenharmony_ci
38928c2ecf20Sopenharmony_ci
38938c2ecf20Sopenharmony_ci	ret = pvr2_send_request(hdw, hdw->cmd_buffer, 8, hdw->cmd_buffer, 0);
38948c2ecf20Sopenharmony_ci
38958c2ecf20Sopenharmony_ci	LOCK_GIVE(hdw->ctl_lock);
38968c2ecf20Sopenharmony_ci
38978c2ecf20Sopenharmony_ci	return ret;
38988c2ecf20Sopenharmony_ci}
38998c2ecf20Sopenharmony_ci
39008c2ecf20Sopenharmony_ci
39018c2ecf20Sopenharmony_cistatic int pvr2_read_register(struct pvr2_hdw *hdw, u16 reg, u32 *data)
39028c2ecf20Sopenharmony_ci{
39038c2ecf20Sopenharmony_ci	int ret = 0;
39048c2ecf20Sopenharmony_ci
39058c2ecf20Sopenharmony_ci	LOCK_TAKE(hdw->ctl_lock);
39068c2ecf20Sopenharmony_ci
39078c2ecf20Sopenharmony_ci	hdw->cmd_buffer[0] = FX2CMD_REG_READ;  /* read register prefix */
39088c2ecf20Sopenharmony_ci	hdw->cmd_buffer[1] = 0;
39098c2ecf20Sopenharmony_ci	hdw->cmd_buffer[2] = 0;
39108c2ecf20Sopenharmony_ci	hdw->cmd_buffer[3] = 0;
39118c2ecf20Sopenharmony_ci	hdw->cmd_buffer[4] = 0;
39128c2ecf20Sopenharmony_ci	hdw->cmd_buffer[5] = 0;
39138c2ecf20Sopenharmony_ci	hdw->cmd_buffer[6] = (reg >> 8) & 0xff;
39148c2ecf20Sopenharmony_ci	hdw->cmd_buffer[7] = reg & 0xff;
39158c2ecf20Sopenharmony_ci
39168c2ecf20Sopenharmony_ci	ret |= pvr2_send_request(hdw, hdw->cmd_buffer, 8, hdw->cmd_buffer, 4);
39178c2ecf20Sopenharmony_ci	*data = PVR2_COMPOSE_LE(hdw->cmd_buffer,0);
39188c2ecf20Sopenharmony_ci
39198c2ecf20Sopenharmony_ci	LOCK_GIVE(hdw->ctl_lock);
39208c2ecf20Sopenharmony_ci
39218c2ecf20Sopenharmony_ci	return ret;
39228c2ecf20Sopenharmony_ci}
39238c2ecf20Sopenharmony_ci
39248c2ecf20Sopenharmony_ci
39258c2ecf20Sopenharmony_civoid pvr2_hdw_render_useless(struct pvr2_hdw *hdw)
39268c2ecf20Sopenharmony_ci{
39278c2ecf20Sopenharmony_ci	if (!hdw->flag_ok) return;
39288c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_ERROR_LEGS,
39298c2ecf20Sopenharmony_ci		   "Device being rendered inoperable");
39308c2ecf20Sopenharmony_ci	if (hdw->vid_stream) {
39318c2ecf20Sopenharmony_ci		pvr2_stream_setup(hdw->vid_stream,NULL,0,0);
39328c2ecf20Sopenharmony_ci	}
39338c2ecf20Sopenharmony_ci	hdw->flag_ok = 0;
39348c2ecf20Sopenharmony_ci	trace_stbit("flag_ok",hdw->flag_ok);
39358c2ecf20Sopenharmony_ci	pvr2_hdw_state_sched(hdw);
39368c2ecf20Sopenharmony_ci}
39378c2ecf20Sopenharmony_ci
39388c2ecf20Sopenharmony_ci
39398c2ecf20Sopenharmony_civoid pvr2_hdw_device_reset(struct pvr2_hdw *hdw)
39408c2ecf20Sopenharmony_ci{
39418c2ecf20Sopenharmony_ci	int ret;
39428c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INIT,"Performing a device reset...");
39438c2ecf20Sopenharmony_ci	ret = usb_lock_device_for_reset(hdw->usb_dev,NULL);
39448c2ecf20Sopenharmony_ci	if (ret == 0) {
39458c2ecf20Sopenharmony_ci		ret = usb_reset_device(hdw->usb_dev);
39468c2ecf20Sopenharmony_ci		usb_unlock_device(hdw->usb_dev);
39478c2ecf20Sopenharmony_ci	} else {
39488c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
39498c2ecf20Sopenharmony_ci			   "Failed to lock USB device ret=%d",ret);
39508c2ecf20Sopenharmony_ci	}
39518c2ecf20Sopenharmony_ci	if (init_pause_msec) {
39528c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_INFO,
39538c2ecf20Sopenharmony_ci			   "Waiting %u msec for hardware to settle",
39548c2ecf20Sopenharmony_ci			   init_pause_msec);
39558c2ecf20Sopenharmony_ci		msleep(init_pause_msec);
39568c2ecf20Sopenharmony_ci	}
39578c2ecf20Sopenharmony_ci
39588c2ecf20Sopenharmony_ci}
39598c2ecf20Sopenharmony_ci
39608c2ecf20Sopenharmony_ci
39618c2ecf20Sopenharmony_civoid pvr2_hdw_cpureset_assert(struct pvr2_hdw *hdw,int val)
39628c2ecf20Sopenharmony_ci{
39638c2ecf20Sopenharmony_ci	char *da;
39648c2ecf20Sopenharmony_ci	unsigned int pipe;
39658c2ecf20Sopenharmony_ci	int ret;
39668c2ecf20Sopenharmony_ci
39678c2ecf20Sopenharmony_ci	if (!hdw->usb_dev) return;
39688c2ecf20Sopenharmony_ci
39698c2ecf20Sopenharmony_ci	da = kmalloc(16, GFP_KERNEL);
39708c2ecf20Sopenharmony_ci
39718c2ecf20Sopenharmony_ci	if (da == NULL) {
39728c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
39738c2ecf20Sopenharmony_ci			   "Unable to allocate memory to control CPU reset");
39748c2ecf20Sopenharmony_ci		return;
39758c2ecf20Sopenharmony_ci	}
39768c2ecf20Sopenharmony_ci
39778c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INIT,"cpureset_assert(%d)",val);
39788c2ecf20Sopenharmony_ci
39798c2ecf20Sopenharmony_ci	da[0] = val ? 0x01 : 0x00;
39808c2ecf20Sopenharmony_ci
39818c2ecf20Sopenharmony_ci	/* Write the CPUCS register on the 8051.  The lsb of the register
39828c2ecf20Sopenharmony_ci	   is the reset bit; a 1 asserts reset while a 0 clears it. */
39838c2ecf20Sopenharmony_ci	pipe = usb_sndctrlpipe(hdw->usb_dev, 0);
39848c2ecf20Sopenharmony_ci	ret = usb_control_msg(hdw->usb_dev,pipe,0xa0,0x40,0xe600,0,da,1,1000);
39858c2ecf20Sopenharmony_ci	if (ret < 0) {
39868c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_ERROR_LEGS,
39878c2ecf20Sopenharmony_ci			   "cpureset_assert(%d) error=%d",val,ret);
39888c2ecf20Sopenharmony_ci		pvr2_hdw_render_useless(hdw);
39898c2ecf20Sopenharmony_ci	}
39908c2ecf20Sopenharmony_ci
39918c2ecf20Sopenharmony_ci	kfree(da);
39928c2ecf20Sopenharmony_ci}
39938c2ecf20Sopenharmony_ci
39948c2ecf20Sopenharmony_ci
39958c2ecf20Sopenharmony_ciint pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *hdw)
39968c2ecf20Sopenharmony_ci{
39978c2ecf20Sopenharmony_ci	return pvr2_issue_simple_cmd(hdw,FX2CMD_DEEP_RESET);
39988c2ecf20Sopenharmony_ci}
39998c2ecf20Sopenharmony_ci
40008c2ecf20Sopenharmony_ci
40018c2ecf20Sopenharmony_ciint pvr2_hdw_cmd_powerup(struct pvr2_hdw *hdw)
40028c2ecf20Sopenharmony_ci{
40038c2ecf20Sopenharmony_ci	return pvr2_issue_simple_cmd(hdw,FX2CMD_POWER_ON);
40048c2ecf20Sopenharmony_ci}
40058c2ecf20Sopenharmony_ci
40068c2ecf20Sopenharmony_ci
40078c2ecf20Sopenharmony_ci
40088c2ecf20Sopenharmony_ciint pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw)
40098c2ecf20Sopenharmony_ci{
40108c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INIT,
40118c2ecf20Sopenharmony_ci		   "Requesting decoder reset");
40128c2ecf20Sopenharmony_ci	if (hdw->decoder_client_id) {
40138c2ecf20Sopenharmony_ci		v4l2_device_call_all(&hdw->v4l2_dev, hdw->decoder_client_id,
40148c2ecf20Sopenharmony_ci				     core, reset, 0);
40158c2ecf20Sopenharmony_ci		pvr2_hdw_cx25840_vbi_hack(hdw);
40168c2ecf20Sopenharmony_ci		return 0;
40178c2ecf20Sopenharmony_ci	}
40188c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_INIT,
40198c2ecf20Sopenharmony_ci		   "Unable to reset decoder: nothing attached");
40208c2ecf20Sopenharmony_ci	return -ENOTTY;
40218c2ecf20Sopenharmony_ci}
40228c2ecf20Sopenharmony_ci
40238c2ecf20Sopenharmony_ci
40248c2ecf20Sopenharmony_cistatic int pvr2_hdw_cmd_hcw_demod_reset(struct pvr2_hdw *hdw, int onoff)
40258c2ecf20Sopenharmony_ci{
40268c2ecf20Sopenharmony_ci	hdw->flag_ok = !0;
40278c2ecf20Sopenharmony_ci
40288c2ecf20Sopenharmony_ci	/* Use this for Hauppauge 160xxx only */
40298c2ecf20Sopenharmony_ci	if (le16_to_cpu(hdw->usb_dev->descriptor.idVendor) == 0x2040 &&
40308c2ecf20Sopenharmony_ci	    (le16_to_cpu(hdw->usb_dev->descriptor.idProduct) == 0x7502 ||
40318c2ecf20Sopenharmony_ci	     le16_to_cpu(hdw->usb_dev->descriptor.idProduct) == 0x7510)) {
40328c2ecf20Sopenharmony_ci		pr_debug("%s(): resetting demod on Hauppauge 160xxx platform skipped\n",
40338c2ecf20Sopenharmony_ci			 __func__);
40348c2ecf20Sopenharmony_ci		/* Can't reset 160xxx or it will trash Demod tristate */
40358c2ecf20Sopenharmony_ci		return pvr2_issue_simple_cmd(hdw,
40368c2ecf20Sopenharmony_ci					     FX2CMD_HCW_MAKO_SLEEP_PIN |
40378c2ecf20Sopenharmony_ci					     (1 << 8) |
40388c2ecf20Sopenharmony_ci					     ((onoff ? 1 : 0) << 16));
40398c2ecf20Sopenharmony_ci	}
40408c2ecf20Sopenharmony_ci
40418c2ecf20Sopenharmony_ci	return pvr2_issue_simple_cmd(hdw,
40428c2ecf20Sopenharmony_ci				     FX2CMD_HCW_DEMOD_RESETIN |
40438c2ecf20Sopenharmony_ci				     (1 << 8) |
40448c2ecf20Sopenharmony_ci				     ((onoff ? 1 : 0) << 16));
40458c2ecf20Sopenharmony_ci}
40468c2ecf20Sopenharmony_ci
40478c2ecf20Sopenharmony_ci
40488c2ecf20Sopenharmony_cistatic int pvr2_hdw_cmd_onair_fe_power_ctrl(struct pvr2_hdw *hdw, int onoff)
40498c2ecf20Sopenharmony_ci{
40508c2ecf20Sopenharmony_ci	hdw->flag_ok = !0;
40518c2ecf20Sopenharmony_ci	return pvr2_issue_simple_cmd(hdw,(onoff ?
40528c2ecf20Sopenharmony_ci					  FX2CMD_ONAIR_DTV_POWER_ON :
40538c2ecf20Sopenharmony_ci					  FX2CMD_ONAIR_DTV_POWER_OFF));
40548c2ecf20Sopenharmony_ci}
40558c2ecf20Sopenharmony_ci
40568c2ecf20Sopenharmony_ci
40578c2ecf20Sopenharmony_cistatic int pvr2_hdw_cmd_onair_digital_path_ctrl(struct pvr2_hdw *hdw,
40588c2ecf20Sopenharmony_ci						int onoff)
40598c2ecf20Sopenharmony_ci{
40608c2ecf20Sopenharmony_ci	return pvr2_issue_simple_cmd(hdw,(onoff ?
40618c2ecf20Sopenharmony_ci					  FX2CMD_ONAIR_DTV_STREAMING_ON :
40628c2ecf20Sopenharmony_ci					  FX2CMD_ONAIR_DTV_STREAMING_OFF));
40638c2ecf20Sopenharmony_ci}
40648c2ecf20Sopenharmony_ci
40658c2ecf20Sopenharmony_ci
40668c2ecf20Sopenharmony_cistatic void pvr2_hdw_cmd_modeswitch(struct pvr2_hdw *hdw,int digitalFl)
40678c2ecf20Sopenharmony_ci{
40688c2ecf20Sopenharmony_ci	int cmode;
40698c2ecf20Sopenharmony_ci	/* Compare digital/analog desired setting with current setting.  If
40708c2ecf20Sopenharmony_ci	   they don't match, fix it... */
40718c2ecf20Sopenharmony_ci	cmode = (digitalFl ? PVR2_PATHWAY_DIGITAL : PVR2_PATHWAY_ANALOG);
40728c2ecf20Sopenharmony_ci	if (cmode == hdw->pathway_state) {
40738c2ecf20Sopenharmony_ci		/* They match; nothing to do */
40748c2ecf20Sopenharmony_ci		return;
40758c2ecf20Sopenharmony_ci	}
40768c2ecf20Sopenharmony_ci
40778c2ecf20Sopenharmony_ci	switch (hdw->hdw_desc->digital_control_scheme) {
40788c2ecf20Sopenharmony_ci	case PVR2_DIGITAL_SCHEME_HAUPPAUGE:
40798c2ecf20Sopenharmony_ci		pvr2_hdw_cmd_hcw_demod_reset(hdw,digitalFl);
40808c2ecf20Sopenharmony_ci		if (cmode == PVR2_PATHWAY_ANALOG) {
40818c2ecf20Sopenharmony_ci			/* If moving to analog mode, also force the decoder
40828c2ecf20Sopenharmony_ci			   to reset.  If no decoder is attached, then it's
40838c2ecf20Sopenharmony_ci			   ok to ignore this because if/when the decoder
40848c2ecf20Sopenharmony_ci			   attaches, it will reset itself at that time. */
40858c2ecf20Sopenharmony_ci			pvr2_hdw_cmd_decoder_reset(hdw);
40868c2ecf20Sopenharmony_ci		}
40878c2ecf20Sopenharmony_ci		break;
40888c2ecf20Sopenharmony_ci	case PVR2_DIGITAL_SCHEME_ONAIR:
40898c2ecf20Sopenharmony_ci		/* Supposedly we should always have the power on whether in
40908c2ecf20Sopenharmony_ci		   digital or analog mode.  But for now do what appears to
40918c2ecf20Sopenharmony_ci		   work... */
40928c2ecf20Sopenharmony_ci		pvr2_hdw_cmd_onair_fe_power_ctrl(hdw,digitalFl);
40938c2ecf20Sopenharmony_ci		break;
40948c2ecf20Sopenharmony_ci	default: break;
40958c2ecf20Sopenharmony_ci	}
40968c2ecf20Sopenharmony_ci
40978c2ecf20Sopenharmony_ci	pvr2_hdw_untrip_unlocked(hdw);
40988c2ecf20Sopenharmony_ci	hdw->pathway_state = cmode;
40998c2ecf20Sopenharmony_ci}
41008c2ecf20Sopenharmony_ci
41018c2ecf20Sopenharmony_ci
41028c2ecf20Sopenharmony_cistatic void pvr2_led_ctrl_hauppauge(struct pvr2_hdw *hdw, int onoff)
41038c2ecf20Sopenharmony_ci{
41048c2ecf20Sopenharmony_ci	/* change some GPIO data
41058c2ecf20Sopenharmony_ci	 *
41068c2ecf20Sopenharmony_ci	 * note: bit d7 of dir appears to control the LED,
41078c2ecf20Sopenharmony_ci	 * so we shut it off here.
41088c2ecf20Sopenharmony_ci	 *
41098c2ecf20Sopenharmony_ci	 */
41108c2ecf20Sopenharmony_ci	if (onoff) {
41118c2ecf20Sopenharmony_ci		pvr2_hdw_gpio_chg_dir(hdw, 0xffffffff, 0x00000481);
41128c2ecf20Sopenharmony_ci	} else {
41138c2ecf20Sopenharmony_ci		pvr2_hdw_gpio_chg_dir(hdw, 0xffffffff, 0x00000401);
41148c2ecf20Sopenharmony_ci	}
41158c2ecf20Sopenharmony_ci	pvr2_hdw_gpio_chg_out(hdw, 0xffffffff, 0x00000000);
41168c2ecf20Sopenharmony_ci}
41178c2ecf20Sopenharmony_ci
41188c2ecf20Sopenharmony_ci
41198c2ecf20Sopenharmony_citypedef void (*led_method_func)(struct pvr2_hdw *,int);
41208c2ecf20Sopenharmony_ci
41218c2ecf20Sopenharmony_cistatic led_method_func led_methods[] = {
41228c2ecf20Sopenharmony_ci	[PVR2_LED_SCHEME_HAUPPAUGE] = pvr2_led_ctrl_hauppauge,
41238c2ecf20Sopenharmony_ci};
41248c2ecf20Sopenharmony_ci
41258c2ecf20Sopenharmony_ci
41268c2ecf20Sopenharmony_ci/* Toggle LED */
41278c2ecf20Sopenharmony_cistatic void pvr2_led_ctrl(struct pvr2_hdw *hdw,int onoff)
41288c2ecf20Sopenharmony_ci{
41298c2ecf20Sopenharmony_ci	unsigned int scheme_id;
41308c2ecf20Sopenharmony_ci	led_method_func fp;
41318c2ecf20Sopenharmony_ci
41328c2ecf20Sopenharmony_ci	if ((!onoff) == (!hdw->led_on)) return;
41338c2ecf20Sopenharmony_ci
41348c2ecf20Sopenharmony_ci	hdw->led_on = onoff != 0;
41358c2ecf20Sopenharmony_ci
41368c2ecf20Sopenharmony_ci	scheme_id = hdw->hdw_desc->led_scheme;
41378c2ecf20Sopenharmony_ci	if (scheme_id < ARRAY_SIZE(led_methods)) {
41388c2ecf20Sopenharmony_ci		fp = led_methods[scheme_id];
41398c2ecf20Sopenharmony_ci	} else {
41408c2ecf20Sopenharmony_ci		fp = NULL;
41418c2ecf20Sopenharmony_ci	}
41428c2ecf20Sopenharmony_ci
41438c2ecf20Sopenharmony_ci	if (fp) (*fp)(hdw,onoff);
41448c2ecf20Sopenharmony_ci}
41458c2ecf20Sopenharmony_ci
41468c2ecf20Sopenharmony_ci
41478c2ecf20Sopenharmony_ci/* Stop / start video stream transport */
41488c2ecf20Sopenharmony_cistatic int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl)
41498c2ecf20Sopenharmony_ci{
41508c2ecf20Sopenharmony_ci	int ret;
41518c2ecf20Sopenharmony_ci
41528c2ecf20Sopenharmony_ci	/* If we're in analog mode, then just issue the usual analog
41538c2ecf20Sopenharmony_ci	   command. */
41548c2ecf20Sopenharmony_ci	if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) {
41558c2ecf20Sopenharmony_ci		return pvr2_issue_simple_cmd(hdw,
41568c2ecf20Sopenharmony_ci					     (runFl ?
41578c2ecf20Sopenharmony_ci					      FX2CMD_STREAMING_ON :
41588c2ecf20Sopenharmony_ci					      FX2CMD_STREAMING_OFF));
41598c2ecf20Sopenharmony_ci		/*Note: Not reached */
41608c2ecf20Sopenharmony_ci	}
41618c2ecf20Sopenharmony_ci
41628c2ecf20Sopenharmony_ci	if (hdw->pathway_state != PVR2_PATHWAY_DIGITAL) {
41638c2ecf20Sopenharmony_ci		/* Whoops, we don't know what mode we're in... */
41648c2ecf20Sopenharmony_ci		return -EINVAL;
41658c2ecf20Sopenharmony_ci	}
41668c2ecf20Sopenharmony_ci
41678c2ecf20Sopenharmony_ci	/* To get here we have to be in digital mode.  The mechanism here
41688c2ecf20Sopenharmony_ci	   is unfortunately different for different vendors.  So we switch
41698c2ecf20Sopenharmony_ci	   on the device's digital scheme attribute in order to figure out
41708c2ecf20Sopenharmony_ci	   what to do. */
41718c2ecf20Sopenharmony_ci	switch (hdw->hdw_desc->digital_control_scheme) {
41728c2ecf20Sopenharmony_ci	case PVR2_DIGITAL_SCHEME_HAUPPAUGE:
41738c2ecf20Sopenharmony_ci		return pvr2_issue_simple_cmd(hdw,
41748c2ecf20Sopenharmony_ci					     (runFl ?
41758c2ecf20Sopenharmony_ci					      FX2CMD_HCW_DTV_STREAMING_ON :
41768c2ecf20Sopenharmony_ci					      FX2CMD_HCW_DTV_STREAMING_OFF));
41778c2ecf20Sopenharmony_ci	case PVR2_DIGITAL_SCHEME_ONAIR:
41788c2ecf20Sopenharmony_ci		ret = pvr2_issue_simple_cmd(hdw,
41798c2ecf20Sopenharmony_ci					    (runFl ?
41808c2ecf20Sopenharmony_ci					     FX2CMD_STREAMING_ON :
41818c2ecf20Sopenharmony_ci					     FX2CMD_STREAMING_OFF));
41828c2ecf20Sopenharmony_ci		if (ret) return ret;
41838c2ecf20Sopenharmony_ci		return pvr2_hdw_cmd_onair_digital_path_ctrl(hdw,runFl);
41848c2ecf20Sopenharmony_ci	default:
41858c2ecf20Sopenharmony_ci		return -EINVAL;
41868c2ecf20Sopenharmony_ci	}
41878c2ecf20Sopenharmony_ci}
41888c2ecf20Sopenharmony_ci
41898c2ecf20Sopenharmony_ci
41908c2ecf20Sopenharmony_ci/* Evaluate whether or not state_pathway_ok can change */
41918c2ecf20Sopenharmony_cistatic int state_eval_pathway_ok(struct pvr2_hdw *hdw)
41928c2ecf20Sopenharmony_ci{
41938c2ecf20Sopenharmony_ci	if (hdw->state_pathway_ok) {
41948c2ecf20Sopenharmony_ci		/* Nothing to do if pathway is already ok */
41958c2ecf20Sopenharmony_ci		return 0;
41968c2ecf20Sopenharmony_ci	}
41978c2ecf20Sopenharmony_ci	if (!hdw->state_pipeline_idle) {
41988c2ecf20Sopenharmony_ci		/* Not allowed to change anything if pipeline is not idle */
41998c2ecf20Sopenharmony_ci		return 0;
42008c2ecf20Sopenharmony_ci	}
42018c2ecf20Sopenharmony_ci	pvr2_hdw_cmd_modeswitch(hdw,hdw->input_val == PVR2_CVAL_INPUT_DTV);
42028c2ecf20Sopenharmony_ci	hdw->state_pathway_ok = !0;
42038c2ecf20Sopenharmony_ci	trace_stbit("state_pathway_ok",hdw->state_pathway_ok);
42048c2ecf20Sopenharmony_ci	return !0;
42058c2ecf20Sopenharmony_ci}
42068c2ecf20Sopenharmony_ci
42078c2ecf20Sopenharmony_ci
42088c2ecf20Sopenharmony_ci/* Evaluate whether or not state_encoder_ok can change */
42098c2ecf20Sopenharmony_cistatic int state_eval_encoder_ok(struct pvr2_hdw *hdw)
42108c2ecf20Sopenharmony_ci{
42118c2ecf20Sopenharmony_ci	if (hdw->state_encoder_ok) return 0;
42128c2ecf20Sopenharmony_ci	if (hdw->flag_tripped) return 0;
42138c2ecf20Sopenharmony_ci	if (hdw->state_encoder_run) return 0;
42148c2ecf20Sopenharmony_ci	if (hdw->state_encoder_config) return 0;
42158c2ecf20Sopenharmony_ci	if (hdw->state_decoder_run) return 0;
42168c2ecf20Sopenharmony_ci	if (hdw->state_usbstream_run) return 0;
42178c2ecf20Sopenharmony_ci	if (hdw->pathway_state == PVR2_PATHWAY_DIGITAL) {
42188c2ecf20Sopenharmony_ci		if (!hdw->hdw_desc->flag_digital_requires_cx23416) return 0;
42198c2ecf20Sopenharmony_ci	} else if (hdw->pathway_state != PVR2_PATHWAY_ANALOG) {
42208c2ecf20Sopenharmony_ci		return 0;
42218c2ecf20Sopenharmony_ci	}
42228c2ecf20Sopenharmony_ci
42238c2ecf20Sopenharmony_ci	if (pvr2_upload_firmware2(hdw) < 0) {
42248c2ecf20Sopenharmony_ci		hdw->flag_tripped = !0;
42258c2ecf20Sopenharmony_ci		trace_stbit("flag_tripped",hdw->flag_tripped);
42268c2ecf20Sopenharmony_ci		return !0;
42278c2ecf20Sopenharmony_ci	}
42288c2ecf20Sopenharmony_ci	hdw->state_encoder_ok = !0;
42298c2ecf20Sopenharmony_ci	trace_stbit("state_encoder_ok",hdw->state_encoder_ok);
42308c2ecf20Sopenharmony_ci	return !0;
42318c2ecf20Sopenharmony_ci}
42328c2ecf20Sopenharmony_ci
42338c2ecf20Sopenharmony_ci
42348c2ecf20Sopenharmony_ci/* Evaluate whether or not state_encoder_config can change */
42358c2ecf20Sopenharmony_cistatic int state_eval_encoder_config(struct pvr2_hdw *hdw)
42368c2ecf20Sopenharmony_ci{
42378c2ecf20Sopenharmony_ci	if (hdw->state_encoder_config) {
42388c2ecf20Sopenharmony_ci		if (hdw->state_encoder_ok) {
42398c2ecf20Sopenharmony_ci			if (hdw->state_pipeline_req &&
42408c2ecf20Sopenharmony_ci			    !hdw->state_pipeline_pause) return 0;
42418c2ecf20Sopenharmony_ci		}
42428c2ecf20Sopenharmony_ci		hdw->state_encoder_config = 0;
42438c2ecf20Sopenharmony_ci		hdw->state_encoder_waitok = 0;
42448c2ecf20Sopenharmony_ci		trace_stbit("state_encoder_waitok",hdw->state_encoder_waitok);
42458c2ecf20Sopenharmony_ci		/* paranoia - solve race if timer just completed */
42468c2ecf20Sopenharmony_ci		del_timer_sync(&hdw->encoder_wait_timer);
42478c2ecf20Sopenharmony_ci	} else {
42488c2ecf20Sopenharmony_ci		if (!hdw->state_pathway_ok ||
42498c2ecf20Sopenharmony_ci		    (hdw->pathway_state != PVR2_PATHWAY_ANALOG) ||
42508c2ecf20Sopenharmony_ci		    !hdw->state_encoder_ok ||
42518c2ecf20Sopenharmony_ci		    !hdw->state_pipeline_idle ||
42528c2ecf20Sopenharmony_ci		    hdw->state_pipeline_pause ||
42538c2ecf20Sopenharmony_ci		    !hdw->state_pipeline_req ||
42548c2ecf20Sopenharmony_ci		    !hdw->state_pipeline_config) {
42558c2ecf20Sopenharmony_ci			/* We must reset the enforced wait interval if
42568c2ecf20Sopenharmony_ci			   anything has happened that might have disturbed
42578c2ecf20Sopenharmony_ci			   the encoder.  This should be a rare case. */
42588c2ecf20Sopenharmony_ci			if (timer_pending(&hdw->encoder_wait_timer)) {
42598c2ecf20Sopenharmony_ci				del_timer_sync(&hdw->encoder_wait_timer);
42608c2ecf20Sopenharmony_ci			}
42618c2ecf20Sopenharmony_ci			if (hdw->state_encoder_waitok) {
42628c2ecf20Sopenharmony_ci				/* Must clear the state - therefore we did
42638c2ecf20Sopenharmony_ci				   something to a state bit and must also
42648c2ecf20Sopenharmony_ci				   return true. */
42658c2ecf20Sopenharmony_ci				hdw->state_encoder_waitok = 0;
42668c2ecf20Sopenharmony_ci				trace_stbit("state_encoder_waitok",
42678c2ecf20Sopenharmony_ci					    hdw->state_encoder_waitok);
42688c2ecf20Sopenharmony_ci				return !0;
42698c2ecf20Sopenharmony_ci			}
42708c2ecf20Sopenharmony_ci			return 0;
42718c2ecf20Sopenharmony_ci		}
42728c2ecf20Sopenharmony_ci		if (!hdw->state_encoder_waitok) {
42738c2ecf20Sopenharmony_ci			if (!timer_pending(&hdw->encoder_wait_timer)) {
42748c2ecf20Sopenharmony_ci				/* waitok flag wasn't set and timer isn't
42758c2ecf20Sopenharmony_ci				   running.  Check flag once more to avoid
42768c2ecf20Sopenharmony_ci				   a race then start the timer.  This is
42778c2ecf20Sopenharmony_ci				   the point when we measure out a minimal
42788c2ecf20Sopenharmony_ci				   quiet interval before doing something to
42798c2ecf20Sopenharmony_ci				   the encoder. */
42808c2ecf20Sopenharmony_ci				if (!hdw->state_encoder_waitok) {
42818c2ecf20Sopenharmony_ci					hdw->encoder_wait_timer.expires =
42828c2ecf20Sopenharmony_ci						jiffies + msecs_to_jiffies(
42838c2ecf20Sopenharmony_ci						TIME_MSEC_ENCODER_WAIT);
42848c2ecf20Sopenharmony_ci					add_timer(&hdw->encoder_wait_timer);
42858c2ecf20Sopenharmony_ci				}
42868c2ecf20Sopenharmony_ci			}
42878c2ecf20Sopenharmony_ci			/* We can't continue until we know we have been
42888c2ecf20Sopenharmony_ci			   quiet for the interval measured by this
42898c2ecf20Sopenharmony_ci			   timer. */
42908c2ecf20Sopenharmony_ci			return 0;
42918c2ecf20Sopenharmony_ci		}
42928c2ecf20Sopenharmony_ci		pvr2_encoder_configure(hdw);
42938c2ecf20Sopenharmony_ci		if (hdw->state_encoder_ok) hdw->state_encoder_config = !0;
42948c2ecf20Sopenharmony_ci	}
42958c2ecf20Sopenharmony_ci	trace_stbit("state_encoder_config",hdw->state_encoder_config);
42968c2ecf20Sopenharmony_ci	return !0;
42978c2ecf20Sopenharmony_ci}
42988c2ecf20Sopenharmony_ci
42998c2ecf20Sopenharmony_ci
43008c2ecf20Sopenharmony_ci/* Return true if the encoder should not be running. */
43018c2ecf20Sopenharmony_cistatic int state_check_disable_encoder_run(struct pvr2_hdw *hdw)
43028c2ecf20Sopenharmony_ci{
43038c2ecf20Sopenharmony_ci	if (!hdw->state_encoder_ok) {
43048c2ecf20Sopenharmony_ci		/* Encoder isn't healthy at the moment, so stop it. */
43058c2ecf20Sopenharmony_ci		return !0;
43068c2ecf20Sopenharmony_ci	}
43078c2ecf20Sopenharmony_ci	if (!hdw->state_pathway_ok) {
43088c2ecf20Sopenharmony_ci		/* Mode is not understood at the moment (i.e. it wants to
43098c2ecf20Sopenharmony_ci		   change), so encoder must be stopped. */
43108c2ecf20Sopenharmony_ci		return !0;
43118c2ecf20Sopenharmony_ci	}
43128c2ecf20Sopenharmony_ci
43138c2ecf20Sopenharmony_ci	switch (hdw->pathway_state) {
43148c2ecf20Sopenharmony_ci	case PVR2_PATHWAY_ANALOG:
43158c2ecf20Sopenharmony_ci		if (!hdw->state_decoder_run) {
43168c2ecf20Sopenharmony_ci			/* We're in analog mode and the decoder is not
43178c2ecf20Sopenharmony_ci			   running; thus the encoder should be stopped as
43188c2ecf20Sopenharmony_ci			   well. */
43198c2ecf20Sopenharmony_ci			return !0;
43208c2ecf20Sopenharmony_ci		}
43218c2ecf20Sopenharmony_ci		break;
43228c2ecf20Sopenharmony_ci	case PVR2_PATHWAY_DIGITAL:
43238c2ecf20Sopenharmony_ci		if (hdw->state_encoder_runok) {
43248c2ecf20Sopenharmony_ci			/* This is a funny case.  We're in digital mode so
43258c2ecf20Sopenharmony_ci			   really the encoder should be stopped.  However
43268c2ecf20Sopenharmony_ci			   if it really is running, only kill it after
43278c2ecf20Sopenharmony_ci			   runok has been set.  This gives a chance for the
43288c2ecf20Sopenharmony_ci			   onair quirk to function (encoder must run
43298c2ecf20Sopenharmony_ci			   briefly first, at least once, before onair
43308c2ecf20Sopenharmony_ci			   digital streaming can work). */
43318c2ecf20Sopenharmony_ci			return !0;
43328c2ecf20Sopenharmony_ci		}
43338c2ecf20Sopenharmony_ci		break;
43348c2ecf20Sopenharmony_ci	default:
43358c2ecf20Sopenharmony_ci		/* Unknown mode; so encoder should be stopped. */
43368c2ecf20Sopenharmony_ci		return !0;
43378c2ecf20Sopenharmony_ci	}
43388c2ecf20Sopenharmony_ci
43398c2ecf20Sopenharmony_ci	/* If we get here, we haven't found a reason to stop the
43408c2ecf20Sopenharmony_ci	   encoder. */
43418c2ecf20Sopenharmony_ci	return 0;
43428c2ecf20Sopenharmony_ci}
43438c2ecf20Sopenharmony_ci
43448c2ecf20Sopenharmony_ci
43458c2ecf20Sopenharmony_ci/* Return true if the encoder should be running. */
43468c2ecf20Sopenharmony_cistatic int state_check_enable_encoder_run(struct pvr2_hdw *hdw)
43478c2ecf20Sopenharmony_ci{
43488c2ecf20Sopenharmony_ci	if (!hdw->state_encoder_ok) {
43498c2ecf20Sopenharmony_ci		/* Don't run the encoder if it isn't healthy... */
43508c2ecf20Sopenharmony_ci		return 0;
43518c2ecf20Sopenharmony_ci	}
43528c2ecf20Sopenharmony_ci	if (!hdw->state_pathway_ok) {
43538c2ecf20Sopenharmony_ci		/* Don't run the encoder if we don't (yet) know what mode
43548c2ecf20Sopenharmony_ci		   we need to be in... */
43558c2ecf20Sopenharmony_ci		return 0;
43568c2ecf20Sopenharmony_ci	}
43578c2ecf20Sopenharmony_ci
43588c2ecf20Sopenharmony_ci	switch (hdw->pathway_state) {
43598c2ecf20Sopenharmony_ci	case PVR2_PATHWAY_ANALOG:
43608c2ecf20Sopenharmony_ci		if (hdw->state_decoder_run && hdw->state_decoder_ready) {
43618c2ecf20Sopenharmony_ci			/* In analog mode, if the decoder is running, then
43628c2ecf20Sopenharmony_ci			   run the encoder. */
43638c2ecf20Sopenharmony_ci			return !0;
43648c2ecf20Sopenharmony_ci		}
43658c2ecf20Sopenharmony_ci		break;
43668c2ecf20Sopenharmony_ci	case PVR2_PATHWAY_DIGITAL:
43678c2ecf20Sopenharmony_ci		if ((hdw->hdw_desc->digital_control_scheme ==
43688c2ecf20Sopenharmony_ci		     PVR2_DIGITAL_SCHEME_ONAIR) &&
43698c2ecf20Sopenharmony_ci		    !hdw->state_encoder_runok) {
43708c2ecf20Sopenharmony_ci			/* This is a quirk.  OnAir hardware won't stream
43718c2ecf20Sopenharmony_ci			   digital until the encoder has been run at least
43728c2ecf20Sopenharmony_ci			   once, for a minimal period of time (empiricially
43738c2ecf20Sopenharmony_ci			   measured to be 1/4 second).  So if we're on
43748c2ecf20Sopenharmony_ci			   OnAir hardware and the encoder has never been
43758c2ecf20Sopenharmony_ci			   run at all, then start the encoder.  Normal
43768c2ecf20Sopenharmony_ci			   state machine logic in the driver will
43778c2ecf20Sopenharmony_ci			   automatically handle the remaining bits. */
43788c2ecf20Sopenharmony_ci			return !0;
43798c2ecf20Sopenharmony_ci		}
43808c2ecf20Sopenharmony_ci		break;
43818c2ecf20Sopenharmony_ci	default:
43828c2ecf20Sopenharmony_ci		/* For completeness (unknown mode; encoder won't run ever) */
43838c2ecf20Sopenharmony_ci		break;
43848c2ecf20Sopenharmony_ci	}
43858c2ecf20Sopenharmony_ci	/* If we get here, then we haven't found any reason to run the
43868c2ecf20Sopenharmony_ci	   encoder, so don't run it. */
43878c2ecf20Sopenharmony_ci	return 0;
43888c2ecf20Sopenharmony_ci}
43898c2ecf20Sopenharmony_ci
43908c2ecf20Sopenharmony_ci
43918c2ecf20Sopenharmony_ci/* Evaluate whether or not state_encoder_run can change */
43928c2ecf20Sopenharmony_cistatic int state_eval_encoder_run(struct pvr2_hdw *hdw)
43938c2ecf20Sopenharmony_ci{
43948c2ecf20Sopenharmony_ci	if (hdw->state_encoder_run) {
43958c2ecf20Sopenharmony_ci		if (!state_check_disable_encoder_run(hdw)) return 0;
43968c2ecf20Sopenharmony_ci		if (hdw->state_encoder_ok) {
43978c2ecf20Sopenharmony_ci			del_timer_sync(&hdw->encoder_run_timer);
43988c2ecf20Sopenharmony_ci			if (pvr2_encoder_stop(hdw) < 0) return !0;
43998c2ecf20Sopenharmony_ci		}
44008c2ecf20Sopenharmony_ci		hdw->state_encoder_run = 0;
44018c2ecf20Sopenharmony_ci	} else {
44028c2ecf20Sopenharmony_ci		if (!state_check_enable_encoder_run(hdw)) return 0;
44038c2ecf20Sopenharmony_ci		if (pvr2_encoder_start(hdw) < 0) return !0;
44048c2ecf20Sopenharmony_ci		hdw->state_encoder_run = !0;
44058c2ecf20Sopenharmony_ci		if (!hdw->state_encoder_runok) {
44068c2ecf20Sopenharmony_ci			hdw->encoder_run_timer.expires = jiffies +
44078c2ecf20Sopenharmony_ci				 msecs_to_jiffies(TIME_MSEC_ENCODER_OK);
44088c2ecf20Sopenharmony_ci			add_timer(&hdw->encoder_run_timer);
44098c2ecf20Sopenharmony_ci		}
44108c2ecf20Sopenharmony_ci	}
44118c2ecf20Sopenharmony_ci	trace_stbit("state_encoder_run",hdw->state_encoder_run);
44128c2ecf20Sopenharmony_ci	return !0;
44138c2ecf20Sopenharmony_ci}
44148c2ecf20Sopenharmony_ci
44158c2ecf20Sopenharmony_ci
44168c2ecf20Sopenharmony_ci/* Timeout function for quiescent timer. */
44178c2ecf20Sopenharmony_cistatic void pvr2_hdw_quiescent_timeout(struct timer_list *t)
44188c2ecf20Sopenharmony_ci{
44198c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = from_timer(hdw, t, quiescent_timer);
44208c2ecf20Sopenharmony_ci	hdw->state_decoder_quiescent = !0;
44218c2ecf20Sopenharmony_ci	trace_stbit("state_decoder_quiescent",hdw->state_decoder_quiescent);
44228c2ecf20Sopenharmony_ci	hdw->state_stale = !0;
44238c2ecf20Sopenharmony_ci	schedule_work(&hdw->workpoll);
44248c2ecf20Sopenharmony_ci}
44258c2ecf20Sopenharmony_ci
44268c2ecf20Sopenharmony_ci
44278c2ecf20Sopenharmony_ci/* Timeout function for decoder stabilization timer. */
44288c2ecf20Sopenharmony_cistatic void pvr2_hdw_decoder_stabilization_timeout(struct timer_list *t)
44298c2ecf20Sopenharmony_ci{
44308c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = from_timer(hdw, t, decoder_stabilization_timer);
44318c2ecf20Sopenharmony_ci	hdw->state_decoder_ready = !0;
44328c2ecf20Sopenharmony_ci	trace_stbit("state_decoder_ready", hdw->state_decoder_ready);
44338c2ecf20Sopenharmony_ci	hdw->state_stale = !0;
44348c2ecf20Sopenharmony_ci	schedule_work(&hdw->workpoll);
44358c2ecf20Sopenharmony_ci}
44368c2ecf20Sopenharmony_ci
44378c2ecf20Sopenharmony_ci
44388c2ecf20Sopenharmony_ci/* Timeout function for encoder wait timer. */
44398c2ecf20Sopenharmony_cistatic void pvr2_hdw_encoder_wait_timeout(struct timer_list *t)
44408c2ecf20Sopenharmony_ci{
44418c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = from_timer(hdw, t, encoder_wait_timer);
44428c2ecf20Sopenharmony_ci	hdw->state_encoder_waitok = !0;
44438c2ecf20Sopenharmony_ci	trace_stbit("state_encoder_waitok",hdw->state_encoder_waitok);
44448c2ecf20Sopenharmony_ci	hdw->state_stale = !0;
44458c2ecf20Sopenharmony_ci	schedule_work(&hdw->workpoll);
44468c2ecf20Sopenharmony_ci}
44478c2ecf20Sopenharmony_ci
44488c2ecf20Sopenharmony_ci
44498c2ecf20Sopenharmony_ci/* Timeout function for encoder run timer. */
44508c2ecf20Sopenharmony_cistatic void pvr2_hdw_encoder_run_timeout(struct timer_list *t)
44518c2ecf20Sopenharmony_ci{
44528c2ecf20Sopenharmony_ci	struct pvr2_hdw *hdw = from_timer(hdw, t, encoder_run_timer);
44538c2ecf20Sopenharmony_ci	if (!hdw->state_encoder_runok) {
44548c2ecf20Sopenharmony_ci		hdw->state_encoder_runok = !0;
44558c2ecf20Sopenharmony_ci		trace_stbit("state_encoder_runok",hdw->state_encoder_runok);
44568c2ecf20Sopenharmony_ci		hdw->state_stale = !0;
44578c2ecf20Sopenharmony_ci		schedule_work(&hdw->workpoll);
44588c2ecf20Sopenharmony_ci	}
44598c2ecf20Sopenharmony_ci}
44608c2ecf20Sopenharmony_ci
44618c2ecf20Sopenharmony_ci
44628c2ecf20Sopenharmony_ci/* Evaluate whether or not state_decoder_run can change */
44638c2ecf20Sopenharmony_cistatic int state_eval_decoder_run(struct pvr2_hdw *hdw)
44648c2ecf20Sopenharmony_ci{
44658c2ecf20Sopenharmony_ci	if (hdw->state_decoder_run) {
44668c2ecf20Sopenharmony_ci		if (hdw->state_encoder_ok) {
44678c2ecf20Sopenharmony_ci			if (hdw->state_pipeline_req &&
44688c2ecf20Sopenharmony_ci			    !hdw->state_pipeline_pause &&
44698c2ecf20Sopenharmony_ci			    hdw->state_pathway_ok) return 0;
44708c2ecf20Sopenharmony_ci		}
44718c2ecf20Sopenharmony_ci		if (!hdw->flag_decoder_missed) {
44728c2ecf20Sopenharmony_ci			pvr2_decoder_enable(hdw,0);
44738c2ecf20Sopenharmony_ci		}
44748c2ecf20Sopenharmony_ci		hdw->state_decoder_quiescent = 0;
44758c2ecf20Sopenharmony_ci		hdw->state_decoder_run = 0;
44768c2ecf20Sopenharmony_ci		/* paranoia - solve race if timer(s) just completed */
44778c2ecf20Sopenharmony_ci		del_timer_sync(&hdw->quiescent_timer);
44788c2ecf20Sopenharmony_ci		/* Kill the stabilization timer, in case we're killing the
44798c2ecf20Sopenharmony_ci		   encoder before the previous stabilization interval has
44808c2ecf20Sopenharmony_ci		   been properly timed. */
44818c2ecf20Sopenharmony_ci		del_timer_sync(&hdw->decoder_stabilization_timer);
44828c2ecf20Sopenharmony_ci		hdw->state_decoder_ready = 0;
44838c2ecf20Sopenharmony_ci	} else {
44848c2ecf20Sopenharmony_ci		if (!hdw->state_decoder_quiescent) {
44858c2ecf20Sopenharmony_ci			if (!timer_pending(&hdw->quiescent_timer)) {
44868c2ecf20Sopenharmony_ci				/* We don't do something about the
44878c2ecf20Sopenharmony_ci				   quiescent timer until right here because
44888c2ecf20Sopenharmony_ci				   we also want to catch cases where the
44898c2ecf20Sopenharmony_ci				   decoder was already not running (like
44908c2ecf20Sopenharmony_ci				   after initialization) as opposed to
44918c2ecf20Sopenharmony_ci				   knowing that we had just stopped it.
44928c2ecf20Sopenharmony_ci				   The second flag check is here to cover a
44938c2ecf20Sopenharmony_ci				   race - the timer could have run and set
44948c2ecf20Sopenharmony_ci				   this flag just after the previous check
44958c2ecf20Sopenharmony_ci				   but before we did the pending check. */
44968c2ecf20Sopenharmony_ci				if (!hdw->state_decoder_quiescent) {
44978c2ecf20Sopenharmony_ci					hdw->quiescent_timer.expires =
44988c2ecf20Sopenharmony_ci						jiffies + msecs_to_jiffies(
44998c2ecf20Sopenharmony_ci						TIME_MSEC_DECODER_WAIT);
45008c2ecf20Sopenharmony_ci					add_timer(&hdw->quiescent_timer);
45018c2ecf20Sopenharmony_ci				}
45028c2ecf20Sopenharmony_ci			}
45038c2ecf20Sopenharmony_ci			/* Don't allow decoder to start again until it has
45048c2ecf20Sopenharmony_ci			   been quiesced first.  This little detail should
45058c2ecf20Sopenharmony_ci			   hopefully further stabilize the encoder. */
45068c2ecf20Sopenharmony_ci			return 0;
45078c2ecf20Sopenharmony_ci		}
45088c2ecf20Sopenharmony_ci		if (!hdw->state_pathway_ok ||
45098c2ecf20Sopenharmony_ci		    (hdw->pathway_state != PVR2_PATHWAY_ANALOG) ||
45108c2ecf20Sopenharmony_ci		    !hdw->state_pipeline_req ||
45118c2ecf20Sopenharmony_ci		    hdw->state_pipeline_pause ||
45128c2ecf20Sopenharmony_ci		    !hdw->state_pipeline_config ||
45138c2ecf20Sopenharmony_ci		    !hdw->state_encoder_config ||
45148c2ecf20Sopenharmony_ci		    !hdw->state_encoder_ok) return 0;
45158c2ecf20Sopenharmony_ci		del_timer_sync(&hdw->quiescent_timer);
45168c2ecf20Sopenharmony_ci		if (hdw->flag_decoder_missed) return 0;
45178c2ecf20Sopenharmony_ci		if (pvr2_decoder_enable(hdw,!0) < 0) return 0;
45188c2ecf20Sopenharmony_ci		hdw->state_decoder_quiescent = 0;
45198c2ecf20Sopenharmony_ci		hdw->state_decoder_ready = 0;
45208c2ecf20Sopenharmony_ci		hdw->state_decoder_run = !0;
45218c2ecf20Sopenharmony_ci		if (hdw->decoder_client_id == PVR2_CLIENT_ID_SAA7115) {
45228c2ecf20Sopenharmony_ci			hdw->decoder_stabilization_timer.expires =
45238c2ecf20Sopenharmony_ci				jiffies + msecs_to_jiffies(
45248c2ecf20Sopenharmony_ci				TIME_MSEC_DECODER_STABILIZATION_WAIT);
45258c2ecf20Sopenharmony_ci			add_timer(&hdw->decoder_stabilization_timer);
45268c2ecf20Sopenharmony_ci		} else {
45278c2ecf20Sopenharmony_ci			hdw->state_decoder_ready = !0;
45288c2ecf20Sopenharmony_ci		}
45298c2ecf20Sopenharmony_ci	}
45308c2ecf20Sopenharmony_ci	trace_stbit("state_decoder_quiescent",hdw->state_decoder_quiescent);
45318c2ecf20Sopenharmony_ci	trace_stbit("state_decoder_run",hdw->state_decoder_run);
45328c2ecf20Sopenharmony_ci	trace_stbit("state_decoder_ready", hdw->state_decoder_ready);
45338c2ecf20Sopenharmony_ci	return !0;
45348c2ecf20Sopenharmony_ci}
45358c2ecf20Sopenharmony_ci
45368c2ecf20Sopenharmony_ci
45378c2ecf20Sopenharmony_ci/* Evaluate whether or not state_usbstream_run can change */
45388c2ecf20Sopenharmony_cistatic int state_eval_usbstream_run(struct pvr2_hdw *hdw)
45398c2ecf20Sopenharmony_ci{
45408c2ecf20Sopenharmony_ci	if (hdw->state_usbstream_run) {
45418c2ecf20Sopenharmony_ci		int fl = !0;
45428c2ecf20Sopenharmony_ci		if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) {
45438c2ecf20Sopenharmony_ci			fl = (hdw->state_encoder_ok &&
45448c2ecf20Sopenharmony_ci			      hdw->state_encoder_run);
45458c2ecf20Sopenharmony_ci		} else if ((hdw->pathway_state == PVR2_PATHWAY_DIGITAL) &&
45468c2ecf20Sopenharmony_ci			   (hdw->hdw_desc->flag_digital_requires_cx23416)) {
45478c2ecf20Sopenharmony_ci			fl = hdw->state_encoder_ok;
45488c2ecf20Sopenharmony_ci		}
45498c2ecf20Sopenharmony_ci		if (fl &&
45508c2ecf20Sopenharmony_ci		    hdw->state_pipeline_req &&
45518c2ecf20Sopenharmony_ci		    !hdw->state_pipeline_pause &&
45528c2ecf20Sopenharmony_ci		    hdw->state_pathway_ok) {
45538c2ecf20Sopenharmony_ci			return 0;
45548c2ecf20Sopenharmony_ci		}
45558c2ecf20Sopenharmony_ci		pvr2_hdw_cmd_usbstream(hdw,0);
45568c2ecf20Sopenharmony_ci		hdw->state_usbstream_run = 0;
45578c2ecf20Sopenharmony_ci	} else {
45588c2ecf20Sopenharmony_ci		if (!hdw->state_pipeline_req ||
45598c2ecf20Sopenharmony_ci		    hdw->state_pipeline_pause ||
45608c2ecf20Sopenharmony_ci		    !hdw->state_pathway_ok) return 0;
45618c2ecf20Sopenharmony_ci		if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) {
45628c2ecf20Sopenharmony_ci			if (!hdw->state_encoder_ok ||
45638c2ecf20Sopenharmony_ci			    !hdw->state_encoder_run) return 0;
45648c2ecf20Sopenharmony_ci		} else if ((hdw->pathway_state == PVR2_PATHWAY_DIGITAL) &&
45658c2ecf20Sopenharmony_ci			   (hdw->hdw_desc->flag_digital_requires_cx23416)) {
45668c2ecf20Sopenharmony_ci			if (!hdw->state_encoder_ok) return 0;
45678c2ecf20Sopenharmony_ci			if (hdw->state_encoder_run) return 0;
45688c2ecf20Sopenharmony_ci			if (hdw->hdw_desc->digital_control_scheme ==
45698c2ecf20Sopenharmony_ci			    PVR2_DIGITAL_SCHEME_ONAIR) {
45708c2ecf20Sopenharmony_ci				/* OnAir digital receivers won't stream
45718c2ecf20Sopenharmony_ci				   unless the analog encoder has run first.
45728c2ecf20Sopenharmony_ci				   Why?  I have no idea.  But don't even
45738c2ecf20Sopenharmony_ci				   try until we know the analog side is
45748c2ecf20Sopenharmony_ci				   known to have run. */
45758c2ecf20Sopenharmony_ci				if (!hdw->state_encoder_runok) return 0;
45768c2ecf20Sopenharmony_ci			}
45778c2ecf20Sopenharmony_ci		}
45788c2ecf20Sopenharmony_ci		if (pvr2_hdw_cmd_usbstream(hdw,!0) < 0) return 0;
45798c2ecf20Sopenharmony_ci		hdw->state_usbstream_run = !0;
45808c2ecf20Sopenharmony_ci	}
45818c2ecf20Sopenharmony_ci	trace_stbit("state_usbstream_run",hdw->state_usbstream_run);
45828c2ecf20Sopenharmony_ci	return !0;
45838c2ecf20Sopenharmony_ci}
45848c2ecf20Sopenharmony_ci
45858c2ecf20Sopenharmony_ci
45868c2ecf20Sopenharmony_ci/* Attempt to configure pipeline, if needed */
45878c2ecf20Sopenharmony_cistatic int state_eval_pipeline_config(struct pvr2_hdw *hdw)
45888c2ecf20Sopenharmony_ci{
45898c2ecf20Sopenharmony_ci	if (hdw->state_pipeline_config ||
45908c2ecf20Sopenharmony_ci	    hdw->state_pipeline_pause) return 0;
45918c2ecf20Sopenharmony_ci	pvr2_hdw_commit_execute(hdw);
45928c2ecf20Sopenharmony_ci	return !0;
45938c2ecf20Sopenharmony_ci}
45948c2ecf20Sopenharmony_ci
45958c2ecf20Sopenharmony_ci
45968c2ecf20Sopenharmony_ci/* Update pipeline idle and pipeline pause tracking states based on other
45978c2ecf20Sopenharmony_ci   inputs.  This must be called whenever the other relevant inputs have
45988c2ecf20Sopenharmony_ci   changed. */
45998c2ecf20Sopenharmony_cistatic int state_update_pipeline_state(struct pvr2_hdw *hdw)
46008c2ecf20Sopenharmony_ci{
46018c2ecf20Sopenharmony_ci	unsigned int st;
46028c2ecf20Sopenharmony_ci	int updatedFl = 0;
46038c2ecf20Sopenharmony_ci	/* Update pipeline state */
46048c2ecf20Sopenharmony_ci	st = !(hdw->state_encoder_run ||
46058c2ecf20Sopenharmony_ci	       hdw->state_decoder_run ||
46068c2ecf20Sopenharmony_ci	       hdw->state_usbstream_run ||
46078c2ecf20Sopenharmony_ci	       (!hdw->state_decoder_quiescent));
46088c2ecf20Sopenharmony_ci	if (!st != !hdw->state_pipeline_idle) {
46098c2ecf20Sopenharmony_ci		hdw->state_pipeline_idle = st;
46108c2ecf20Sopenharmony_ci		updatedFl = !0;
46118c2ecf20Sopenharmony_ci	}
46128c2ecf20Sopenharmony_ci	if (hdw->state_pipeline_idle && hdw->state_pipeline_pause) {
46138c2ecf20Sopenharmony_ci		hdw->state_pipeline_pause = 0;
46148c2ecf20Sopenharmony_ci		updatedFl = !0;
46158c2ecf20Sopenharmony_ci	}
46168c2ecf20Sopenharmony_ci	return updatedFl;
46178c2ecf20Sopenharmony_ci}
46188c2ecf20Sopenharmony_ci
46198c2ecf20Sopenharmony_ci
46208c2ecf20Sopenharmony_citypedef int (*state_eval_func)(struct pvr2_hdw *);
46218c2ecf20Sopenharmony_ci
46228c2ecf20Sopenharmony_ci/* Set of functions to be run to evaluate various states in the driver. */
46238c2ecf20Sopenharmony_cistatic const state_eval_func eval_funcs[] = {
46248c2ecf20Sopenharmony_ci	state_eval_pathway_ok,
46258c2ecf20Sopenharmony_ci	state_eval_pipeline_config,
46268c2ecf20Sopenharmony_ci	state_eval_encoder_ok,
46278c2ecf20Sopenharmony_ci	state_eval_encoder_config,
46288c2ecf20Sopenharmony_ci	state_eval_decoder_run,
46298c2ecf20Sopenharmony_ci	state_eval_encoder_run,
46308c2ecf20Sopenharmony_ci	state_eval_usbstream_run,
46318c2ecf20Sopenharmony_ci};
46328c2ecf20Sopenharmony_ci
46338c2ecf20Sopenharmony_ci
46348c2ecf20Sopenharmony_ci/* Process various states and return true if we did anything interesting. */
46358c2ecf20Sopenharmony_cistatic int pvr2_hdw_state_update(struct pvr2_hdw *hdw)
46368c2ecf20Sopenharmony_ci{
46378c2ecf20Sopenharmony_ci	unsigned int i;
46388c2ecf20Sopenharmony_ci	int state_updated = 0;
46398c2ecf20Sopenharmony_ci	int check_flag;
46408c2ecf20Sopenharmony_ci
46418c2ecf20Sopenharmony_ci	if (!hdw->state_stale) return 0;
46428c2ecf20Sopenharmony_ci	if ((hdw->fw1_state != FW1_STATE_OK) ||
46438c2ecf20Sopenharmony_ci	    !hdw->flag_ok) {
46448c2ecf20Sopenharmony_ci		hdw->state_stale = 0;
46458c2ecf20Sopenharmony_ci		return !0;
46468c2ecf20Sopenharmony_ci	}
46478c2ecf20Sopenharmony_ci	/* This loop is the heart of the entire driver.  It keeps trying to
46488c2ecf20Sopenharmony_ci	   evaluate various bits of driver state until nothing changes for
46498c2ecf20Sopenharmony_ci	   one full iteration.  Each "bit of state" tracks some global
46508c2ecf20Sopenharmony_ci	   aspect of the driver, e.g. whether decoder should run, if
46518c2ecf20Sopenharmony_ci	   pipeline is configured, usb streaming is on, etc.  We separately
46528c2ecf20Sopenharmony_ci	   evaluate each of those questions based on other driver state to
46538c2ecf20Sopenharmony_ci	   arrive at the correct running configuration. */
46548c2ecf20Sopenharmony_ci	do {
46558c2ecf20Sopenharmony_ci		check_flag = 0;
46568c2ecf20Sopenharmony_ci		state_update_pipeline_state(hdw);
46578c2ecf20Sopenharmony_ci		/* Iterate over each bit of state */
46588c2ecf20Sopenharmony_ci		for (i = 0; (i<ARRAY_SIZE(eval_funcs)) && hdw->flag_ok; i++) {
46598c2ecf20Sopenharmony_ci			if ((*eval_funcs[i])(hdw)) {
46608c2ecf20Sopenharmony_ci				check_flag = !0;
46618c2ecf20Sopenharmony_ci				state_updated = !0;
46628c2ecf20Sopenharmony_ci				state_update_pipeline_state(hdw);
46638c2ecf20Sopenharmony_ci			}
46648c2ecf20Sopenharmony_ci		}
46658c2ecf20Sopenharmony_ci	} while (check_flag && hdw->flag_ok);
46668c2ecf20Sopenharmony_ci	hdw->state_stale = 0;
46678c2ecf20Sopenharmony_ci	trace_stbit("state_stale",hdw->state_stale);
46688c2ecf20Sopenharmony_ci	return state_updated;
46698c2ecf20Sopenharmony_ci}
46708c2ecf20Sopenharmony_ci
46718c2ecf20Sopenharmony_ci
46728c2ecf20Sopenharmony_cistatic unsigned int print_input_mask(unsigned int msk,
46738c2ecf20Sopenharmony_ci				     char *buf,unsigned int acnt)
46748c2ecf20Sopenharmony_ci{
46758c2ecf20Sopenharmony_ci	unsigned int idx,ccnt;
46768c2ecf20Sopenharmony_ci	unsigned int tcnt = 0;
46778c2ecf20Sopenharmony_ci	for (idx = 0; idx < ARRAY_SIZE(control_values_input); idx++) {
46788c2ecf20Sopenharmony_ci		if (!((1UL << idx) & msk)) continue;
46798c2ecf20Sopenharmony_ci		ccnt = scnprintf(buf+tcnt,
46808c2ecf20Sopenharmony_ci				 acnt-tcnt,
46818c2ecf20Sopenharmony_ci				 "%s%s",
46828c2ecf20Sopenharmony_ci				 (tcnt ? ", " : ""),
46838c2ecf20Sopenharmony_ci				 control_values_input[idx]);
46848c2ecf20Sopenharmony_ci		tcnt += ccnt;
46858c2ecf20Sopenharmony_ci	}
46868c2ecf20Sopenharmony_ci	return tcnt;
46878c2ecf20Sopenharmony_ci}
46888c2ecf20Sopenharmony_ci
46898c2ecf20Sopenharmony_ci
46908c2ecf20Sopenharmony_cistatic const char *pvr2_pathway_state_name(int id)
46918c2ecf20Sopenharmony_ci{
46928c2ecf20Sopenharmony_ci	switch (id) {
46938c2ecf20Sopenharmony_ci	case PVR2_PATHWAY_ANALOG: return "analog";
46948c2ecf20Sopenharmony_ci	case PVR2_PATHWAY_DIGITAL: return "digital";
46958c2ecf20Sopenharmony_ci	default: return "unknown";
46968c2ecf20Sopenharmony_ci	}
46978c2ecf20Sopenharmony_ci}
46988c2ecf20Sopenharmony_ci
46998c2ecf20Sopenharmony_ci
47008c2ecf20Sopenharmony_cistatic unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which,
47018c2ecf20Sopenharmony_ci					     char *buf,unsigned int acnt)
47028c2ecf20Sopenharmony_ci{
47038c2ecf20Sopenharmony_ci	switch (which) {
47048c2ecf20Sopenharmony_ci	case 0:
47058c2ecf20Sopenharmony_ci		return scnprintf(
47068c2ecf20Sopenharmony_ci			buf,acnt,
47078c2ecf20Sopenharmony_ci			"driver:%s%s%s%s%s <mode=%s>",
47088c2ecf20Sopenharmony_ci			(hdw->flag_ok ? " <ok>" : " <fail>"),
47098c2ecf20Sopenharmony_ci			(hdw->flag_init_ok ? " <init>" : " <uninitialized>"),
47108c2ecf20Sopenharmony_ci			(hdw->flag_disconnected ? " <disconnected>" :
47118c2ecf20Sopenharmony_ci			 " <connected>"),
47128c2ecf20Sopenharmony_ci			(hdw->flag_tripped ? " <tripped>" : ""),
47138c2ecf20Sopenharmony_ci			(hdw->flag_decoder_missed ? " <no decoder>" : ""),
47148c2ecf20Sopenharmony_ci			pvr2_pathway_state_name(hdw->pathway_state));
47158c2ecf20Sopenharmony_ci
47168c2ecf20Sopenharmony_ci	case 1:
47178c2ecf20Sopenharmony_ci		return scnprintf(
47188c2ecf20Sopenharmony_ci			buf,acnt,
47198c2ecf20Sopenharmony_ci			"pipeline:%s%s%s%s",
47208c2ecf20Sopenharmony_ci			(hdw->state_pipeline_idle ? " <idle>" : ""),
47218c2ecf20Sopenharmony_ci			(hdw->state_pipeline_config ?
47228c2ecf20Sopenharmony_ci			 " <configok>" : " <stale>"),
47238c2ecf20Sopenharmony_ci			(hdw->state_pipeline_req ? " <req>" : ""),
47248c2ecf20Sopenharmony_ci			(hdw->state_pipeline_pause ? " <pause>" : ""));
47258c2ecf20Sopenharmony_ci	case 2:
47268c2ecf20Sopenharmony_ci		return scnprintf(
47278c2ecf20Sopenharmony_ci			buf,acnt,
47288c2ecf20Sopenharmony_ci			"worker:%s%s%s%s%s%s%s",
47298c2ecf20Sopenharmony_ci			(hdw->state_decoder_run ?
47308c2ecf20Sopenharmony_ci			 (hdw->state_decoder_ready ?
47318c2ecf20Sopenharmony_ci			  "<decode:run>" : " <decode:start>") :
47328c2ecf20Sopenharmony_ci			 (hdw->state_decoder_quiescent ?
47338c2ecf20Sopenharmony_ci			  "" : " <decode:stop>")),
47348c2ecf20Sopenharmony_ci			(hdw->state_decoder_quiescent ?
47358c2ecf20Sopenharmony_ci			 " <decode:quiescent>" : ""),
47368c2ecf20Sopenharmony_ci			(hdw->state_encoder_ok ?
47378c2ecf20Sopenharmony_ci			 "" : " <encode:init>"),
47388c2ecf20Sopenharmony_ci			(hdw->state_encoder_run ?
47398c2ecf20Sopenharmony_ci			 (hdw->state_encoder_runok ?
47408c2ecf20Sopenharmony_ci			  " <encode:run>" :
47418c2ecf20Sopenharmony_ci			  " <encode:firstrun>") :
47428c2ecf20Sopenharmony_ci			 (hdw->state_encoder_runok ?
47438c2ecf20Sopenharmony_ci			  " <encode:stop>" :
47448c2ecf20Sopenharmony_ci			  " <encode:virgin>")),
47458c2ecf20Sopenharmony_ci			(hdw->state_encoder_config ?
47468c2ecf20Sopenharmony_ci			 " <encode:configok>" :
47478c2ecf20Sopenharmony_ci			 (hdw->state_encoder_waitok ?
47488c2ecf20Sopenharmony_ci			  "" : " <encode:waitok>")),
47498c2ecf20Sopenharmony_ci			(hdw->state_usbstream_run ?
47508c2ecf20Sopenharmony_ci			 " <usb:run>" : " <usb:stop>"),
47518c2ecf20Sopenharmony_ci			(hdw->state_pathway_ok ?
47528c2ecf20Sopenharmony_ci			 " <pathway:ok>" : ""));
47538c2ecf20Sopenharmony_ci	case 3:
47548c2ecf20Sopenharmony_ci		return scnprintf(
47558c2ecf20Sopenharmony_ci			buf,acnt,
47568c2ecf20Sopenharmony_ci			"state: %s",
47578c2ecf20Sopenharmony_ci			pvr2_get_state_name(hdw->master_state));
47588c2ecf20Sopenharmony_ci	case 4: {
47598c2ecf20Sopenharmony_ci		unsigned int tcnt = 0;
47608c2ecf20Sopenharmony_ci		unsigned int ccnt;
47618c2ecf20Sopenharmony_ci
47628c2ecf20Sopenharmony_ci		ccnt = scnprintf(buf,
47638c2ecf20Sopenharmony_ci				 acnt,
47648c2ecf20Sopenharmony_ci				 "Hardware supported inputs: ");
47658c2ecf20Sopenharmony_ci		tcnt += ccnt;
47668c2ecf20Sopenharmony_ci		tcnt += print_input_mask(hdw->input_avail_mask,
47678c2ecf20Sopenharmony_ci					 buf+tcnt,
47688c2ecf20Sopenharmony_ci					 acnt-tcnt);
47698c2ecf20Sopenharmony_ci		if (hdw->input_avail_mask != hdw->input_allowed_mask) {
47708c2ecf20Sopenharmony_ci			ccnt = scnprintf(buf+tcnt,
47718c2ecf20Sopenharmony_ci					 acnt-tcnt,
47728c2ecf20Sopenharmony_ci					 "; allowed inputs: ");
47738c2ecf20Sopenharmony_ci			tcnt += ccnt;
47748c2ecf20Sopenharmony_ci			tcnt += print_input_mask(hdw->input_allowed_mask,
47758c2ecf20Sopenharmony_ci						 buf+tcnt,
47768c2ecf20Sopenharmony_ci						 acnt-tcnt);
47778c2ecf20Sopenharmony_ci		}
47788c2ecf20Sopenharmony_ci		return tcnt;
47798c2ecf20Sopenharmony_ci	}
47808c2ecf20Sopenharmony_ci	case 5: {
47818c2ecf20Sopenharmony_ci		struct pvr2_stream_stats stats;
47828c2ecf20Sopenharmony_ci		if (!hdw->vid_stream) break;
47838c2ecf20Sopenharmony_ci		pvr2_stream_get_stats(hdw->vid_stream,
47848c2ecf20Sopenharmony_ci				      &stats,
47858c2ecf20Sopenharmony_ci				      0);
47868c2ecf20Sopenharmony_ci		return scnprintf(
47878c2ecf20Sopenharmony_ci			buf,acnt,
47888c2ecf20Sopenharmony_ci			"Bytes streamed=%u URBs: queued=%u idle=%u ready=%u processed=%u failed=%u",
47898c2ecf20Sopenharmony_ci			stats.bytes_processed,
47908c2ecf20Sopenharmony_ci			stats.buffers_in_queue,
47918c2ecf20Sopenharmony_ci			stats.buffers_in_idle,
47928c2ecf20Sopenharmony_ci			stats.buffers_in_ready,
47938c2ecf20Sopenharmony_ci			stats.buffers_processed,
47948c2ecf20Sopenharmony_ci			stats.buffers_failed);
47958c2ecf20Sopenharmony_ci	}
47968c2ecf20Sopenharmony_ci	case 6: {
47978c2ecf20Sopenharmony_ci		unsigned int id = hdw->ir_scheme_active;
47988c2ecf20Sopenharmony_ci		return scnprintf(buf, acnt, "ir scheme: id=%d %s", id,
47998c2ecf20Sopenharmony_ci				 (id >= ARRAY_SIZE(ir_scheme_names) ?
48008c2ecf20Sopenharmony_ci				  "?" : ir_scheme_names[id]));
48018c2ecf20Sopenharmony_ci	}
48028c2ecf20Sopenharmony_ci	default: break;
48038c2ecf20Sopenharmony_ci	}
48048c2ecf20Sopenharmony_ci	return 0;
48058c2ecf20Sopenharmony_ci}
48068c2ecf20Sopenharmony_ci
48078c2ecf20Sopenharmony_ci
48088c2ecf20Sopenharmony_ci/* Generate report containing info about attached sub-devices and attached
48098c2ecf20Sopenharmony_ci   i2c clients, including an indication of which attached i2c clients are
48108c2ecf20Sopenharmony_ci   actually sub-devices. */
48118c2ecf20Sopenharmony_cistatic unsigned int pvr2_hdw_report_clients(struct pvr2_hdw *hdw,
48128c2ecf20Sopenharmony_ci					    char *buf, unsigned int acnt)
48138c2ecf20Sopenharmony_ci{
48148c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd;
48158c2ecf20Sopenharmony_ci	unsigned int tcnt = 0;
48168c2ecf20Sopenharmony_ci	unsigned int ccnt;
48178c2ecf20Sopenharmony_ci	struct i2c_client *client;
48188c2ecf20Sopenharmony_ci	const char *p;
48198c2ecf20Sopenharmony_ci	unsigned int id;
48208c2ecf20Sopenharmony_ci
48218c2ecf20Sopenharmony_ci	ccnt = scnprintf(buf, acnt, "Associated v4l2-subdev drivers and I2C clients:\n");
48228c2ecf20Sopenharmony_ci	tcnt += ccnt;
48238c2ecf20Sopenharmony_ci	v4l2_device_for_each_subdev(sd, &hdw->v4l2_dev) {
48248c2ecf20Sopenharmony_ci		id = sd->grp_id;
48258c2ecf20Sopenharmony_ci		p = NULL;
48268c2ecf20Sopenharmony_ci		if (id < ARRAY_SIZE(module_names)) p = module_names[id];
48278c2ecf20Sopenharmony_ci		if (p) {
48288c2ecf20Sopenharmony_ci			ccnt = scnprintf(buf + tcnt, acnt - tcnt, "  %s:", p);
48298c2ecf20Sopenharmony_ci			tcnt += ccnt;
48308c2ecf20Sopenharmony_ci		} else {
48318c2ecf20Sopenharmony_ci			ccnt = scnprintf(buf + tcnt, acnt - tcnt,
48328c2ecf20Sopenharmony_ci					 "  (unknown id=%u):", id);
48338c2ecf20Sopenharmony_ci			tcnt += ccnt;
48348c2ecf20Sopenharmony_ci		}
48358c2ecf20Sopenharmony_ci		client = v4l2_get_subdevdata(sd);
48368c2ecf20Sopenharmony_ci		if (client) {
48378c2ecf20Sopenharmony_ci			ccnt = scnprintf(buf + tcnt, acnt - tcnt,
48388c2ecf20Sopenharmony_ci					 " %s @ %02x\n", client->name,
48398c2ecf20Sopenharmony_ci					 client->addr);
48408c2ecf20Sopenharmony_ci			tcnt += ccnt;
48418c2ecf20Sopenharmony_ci		} else {
48428c2ecf20Sopenharmony_ci			ccnt = scnprintf(buf + tcnt, acnt - tcnt,
48438c2ecf20Sopenharmony_ci					 " no i2c client\n");
48448c2ecf20Sopenharmony_ci			tcnt += ccnt;
48458c2ecf20Sopenharmony_ci		}
48468c2ecf20Sopenharmony_ci	}
48478c2ecf20Sopenharmony_ci	return tcnt;
48488c2ecf20Sopenharmony_ci}
48498c2ecf20Sopenharmony_ci
48508c2ecf20Sopenharmony_ci
48518c2ecf20Sopenharmony_ciunsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw,
48528c2ecf20Sopenharmony_ci				   char *buf,unsigned int acnt)
48538c2ecf20Sopenharmony_ci{
48548c2ecf20Sopenharmony_ci	unsigned int bcnt,ccnt,idx;
48558c2ecf20Sopenharmony_ci	bcnt = 0;
48568c2ecf20Sopenharmony_ci	LOCK_TAKE(hdw->big_lock);
48578c2ecf20Sopenharmony_ci	for (idx = 0; ; idx++) {
48588c2ecf20Sopenharmony_ci		ccnt = pvr2_hdw_report_unlocked(hdw,idx,buf,acnt);
48598c2ecf20Sopenharmony_ci		if (!ccnt) break;
48608c2ecf20Sopenharmony_ci		bcnt += ccnt; acnt -= ccnt; buf += ccnt;
48618c2ecf20Sopenharmony_ci		if (!acnt) break;
48628c2ecf20Sopenharmony_ci		buf[0] = '\n'; ccnt = 1;
48638c2ecf20Sopenharmony_ci		bcnt += ccnt; acnt -= ccnt; buf += ccnt;
48648c2ecf20Sopenharmony_ci	}
48658c2ecf20Sopenharmony_ci	ccnt = pvr2_hdw_report_clients(hdw, buf, acnt);
48668c2ecf20Sopenharmony_ci	bcnt += ccnt; acnt -= ccnt; buf += ccnt;
48678c2ecf20Sopenharmony_ci	LOCK_GIVE(hdw->big_lock);
48688c2ecf20Sopenharmony_ci	return bcnt;
48698c2ecf20Sopenharmony_ci}
48708c2ecf20Sopenharmony_ci
48718c2ecf20Sopenharmony_ci
48728c2ecf20Sopenharmony_cistatic void pvr2_hdw_state_log_state(struct pvr2_hdw *hdw)
48738c2ecf20Sopenharmony_ci{
48748c2ecf20Sopenharmony_ci	char buf[256];
48758c2ecf20Sopenharmony_ci	unsigned int idx, ccnt;
48768c2ecf20Sopenharmony_ci	unsigned int lcnt, ucnt;
48778c2ecf20Sopenharmony_ci
48788c2ecf20Sopenharmony_ci	for (idx = 0; ; idx++) {
48798c2ecf20Sopenharmony_ci		ccnt = pvr2_hdw_report_unlocked(hdw,idx,buf,sizeof(buf));
48808c2ecf20Sopenharmony_ci		if (!ccnt) break;
48818c2ecf20Sopenharmony_ci		pr_info("%s %.*s\n", hdw->name, ccnt, buf);
48828c2ecf20Sopenharmony_ci	}
48838c2ecf20Sopenharmony_ci	ccnt = pvr2_hdw_report_clients(hdw, buf, sizeof(buf));
48848c2ecf20Sopenharmony_ci	if (ccnt >= sizeof(buf))
48858c2ecf20Sopenharmony_ci		ccnt = sizeof(buf);
48868c2ecf20Sopenharmony_ci
48878c2ecf20Sopenharmony_ci	ucnt = 0;
48888c2ecf20Sopenharmony_ci	while (ucnt < ccnt) {
48898c2ecf20Sopenharmony_ci		lcnt = 0;
48908c2ecf20Sopenharmony_ci		while ((lcnt + ucnt < ccnt) && (buf[lcnt + ucnt] != '\n')) {
48918c2ecf20Sopenharmony_ci			lcnt++;
48928c2ecf20Sopenharmony_ci		}
48938c2ecf20Sopenharmony_ci		pr_info("%s %.*s\n", hdw->name, lcnt, buf + ucnt);
48948c2ecf20Sopenharmony_ci		ucnt += lcnt + 1;
48958c2ecf20Sopenharmony_ci	}
48968c2ecf20Sopenharmony_ci}
48978c2ecf20Sopenharmony_ci
48988c2ecf20Sopenharmony_ci
48998c2ecf20Sopenharmony_ci/* Evaluate and update the driver's current state, taking various actions
49008c2ecf20Sopenharmony_ci   as appropriate for the update. */
49018c2ecf20Sopenharmony_cistatic int pvr2_hdw_state_eval(struct pvr2_hdw *hdw)
49028c2ecf20Sopenharmony_ci{
49038c2ecf20Sopenharmony_ci	unsigned int st;
49048c2ecf20Sopenharmony_ci	int state_updated = 0;
49058c2ecf20Sopenharmony_ci	int callback_flag = 0;
49068c2ecf20Sopenharmony_ci	int analog_mode;
49078c2ecf20Sopenharmony_ci
49088c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_STBITS,
49098c2ecf20Sopenharmony_ci		   "Drive state check START");
49108c2ecf20Sopenharmony_ci	if (pvrusb2_debug & PVR2_TRACE_STBITS) {
49118c2ecf20Sopenharmony_ci		pvr2_hdw_state_log_state(hdw);
49128c2ecf20Sopenharmony_ci	}
49138c2ecf20Sopenharmony_ci
49148c2ecf20Sopenharmony_ci	/* Process all state and get back over disposition */
49158c2ecf20Sopenharmony_ci	state_updated = pvr2_hdw_state_update(hdw);
49168c2ecf20Sopenharmony_ci
49178c2ecf20Sopenharmony_ci	analog_mode = (hdw->pathway_state != PVR2_PATHWAY_DIGITAL);
49188c2ecf20Sopenharmony_ci
49198c2ecf20Sopenharmony_ci	/* Update master state based upon all other states. */
49208c2ecf20Sopenharmony_ci	if (!hdw->flag_ok) {
49218c2ecf20Sopenharmony_ci		st = PVR2_STATE_DEAD;
49228c2ecf20Sopenharmony_ci	} else if (hdw->fw1_state != FW1_STATE_OK) {
49238c2ecf20Sopenharmony_ci		st = PVR2_STATE_COLD;
49248c2ecf20Sopenharmony_ci	} else if ((analog_mode ||
49258c2ecf20Sopenharmony_ci		    hdw->hdw_desc->flag_digital_requires_cx23416) &&
49268c2ecf20Sopenharmony_ci		   !hdw->state_encoder_ok) {
49278c2ecf20Sopenharmony_ci		st = PVR2_STATE_WARM;
49288c2ecf20Sopenharmony_ci	} else if (hdw->flag_tripped ||
49298c2ecf20Sopenharmony_ci		   (analog_mode && hdw->flag_decoder_missed)) {
49308c2ecf20Sopenharmony_ci		st = PVR2_STATE_ERROR;
49318c2ecf20Sopenharmony_ci	} else if (hdw->state_usbstream_run &&
49328c2ecf20Sopenharmony_ci		   (!analog_mode ||
49338c2ecf20Sopenharmony_ci		    (hdw->state_encoder_run && hdw->state_decoder_run))) {
49348c2ecf20Sopenharmony_ci		st = PVR2_STATE_RUN;
49358c2ecf20Sopenharmony_ci	} else {
49368c2ecf20Sopenharmony_ci		st = PVR2_STATE_READY;
49378c2ecf20Sopenharmony_ci	}
49388c2ecf20Sopenharmony_ci	if (hdw->master_state != st) {
49398c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_STATE,
49408c2ecf20Sopenharmony_ci			   "Device state change from %s to %s",
49418c2ecf20Sopenharmony_ci			   pvr2_get_state_name(hdw->master_state),
49428c2ecf20Sopenharmony_ci			   pvr2_get_state_name(st));
49438c2ecf20Sopenharmony_ci		pvr2_led_ctrl(hdw,st == PVR2_STATE_RUN);
49448c2ecf20Sopenharmony_ci		hdw->master_state = st;
49458c2ecf20Sopenharmony_ci		state_updated = !0;
49468c2ecf20Sopenharmony_ci		callback_flag = !0;
49478c2ecf20Sopenharmony_ci	}
49488c2ecf20Sopenharmony_ci	if (state_updated) {
49498c2ecf20Sopenharmony_ci		/* Trigger anyone waiting on any state changes here. */
49508c2ecf20Sopenharmony_ci		wake_up(&hdw->state_wait_data);
49518c2ecf20Sopenharmony_ci	}
49528c2ecf20Sopenharmony_ci
49538c2ecf20Sopenharmony_ci	if (pvrusb2_debug & PVR2_TRACE_STBITS) {
49548c2ecf20Sopenharmony_ci		pvr2_hdw_state_log_state(hdw);
49558c2ecf20Sopenharmony_ci	}
49568c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_STBITS,
49578c2ecf20Sopenharmony_ci		   "Drive state check DONE callback=%d",callback_flag);
49588c2ecf20Sopenharmony_ci
49598c2ecf20Sopenharmony_ci	return callback_flag;
49608c2ecf20Sopenharmony_ci}
49618c2ecf20Sopenharmony_ci
49628c2ecf20Sopenharmony_ci
49638c2ecf20Sopenharmony_ci/* Cause kernel thread to check / update driver state */
49648c2ecf20Sopenharmony_cistatic void pvr2_hdw_state_sched(struct pvr2_hdw *hdw)
49658c2ecf20Sopenharmony_ci{
49668c2ecf20Sopenharmony_ci	if (hdw->state_stale) return;
49678c2ecf20Sopenharmony_ci	hdw->state_stale = !0;
49688c2ecf20Sopenharmony_ci	trace_stbit("state_stale",hdw->state_stale);
49698c2ecf20Sopenharmony_ci	schedule_work(&hdw->workpoll);
49708c2ecf20Sopenharmony_ci}
49718c2ecf20Sopenharmony_ci
49728c2ecf20Sopenharmony_ci
49738c2ecf20Sopenharmony_ciint pvr2_hdw_gpio_get_dir(struct pvr2_hdw *hdw,u32 *dp)
49748c2ecf20Sopenharmony_ci{
49758c2ecf20Sopenharmony_ci	return pvr2_read_register(hdw,PVR2_GPIO_DIR,dp);
49768c2ecf20Sopenharmony_ci}
49778c2ecf20Sopenharmony_ci
49788c2ecf20Sopenharmony_ci
49798c2ecf20Sopenharmony_ciint pvr2_hdw_gpio_get_out(struct pvr2_hdw *hdw,u32 *dp)
49808c2ecf20Sopenharmony_ci{
49818c2ecf20Sopenharmony_ci	return pvr2_read_register(hdw,PVR2_GPIO_OUT,dp);
49828c2ecf20Sopenharmony_ci}
49838c2ecf20Sopenharmony_ci
49848c2ecf20Sopenharmony_ci
49858c2ecf20Sopenharmony_ciint pvr2_hdw_gpio_get_in(struct pvr2_hdw *hdw,u32 *dp)
49868c2ecf20Sopenharmony_ci{
49878c2ecf20Sopenharmony_ci	return pvr2_read_register(hdw,PVR2_GPIO_IN,dp);
49888c2ecf20Sopenharmony_ci}
49898c2ecf20Sopenharmony_ci
49908c2ecf20Sopenharmony_ci
49918c2ecf20Sopenharmony_ciint pvr2_hdw_gpio_chg_dir(struct pvr2_hdw *hdw,u32 msk,u32 val)
49928c2ecf20Sopenharmony_ci{
49938c2ecf20Sopenharmony_ci	u32 cval,nval;
49948c2ecf20Sopenharmony_ci	int ret;
49958c2ecf20Sopenharmony_ci	if (~msk) {
49968c2ecf20Sopenharmony_ci		ret = pvr2_read_register(hdw,PVR2_GPIO_DIR,&cval);
49978c2ecf20Sopenharmony_ci		if (ret) return ret;
49988c2ecf20Sopenharmony_ci		nval = (cval & ~msk) | (val & msk);
49998c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_GPIO,
50008c2ecf20Sopenharmony_ci			   "GPIO direction changing 0x%x:0x%x from 0x%x to 0x%x",
50018c2ecf20Sopenharmony_ci			   msk,val,cval,nval);
50028c2ecf20Sopenharmony_ci	} else {
50038c2ecf20Sopenharmony_ci		nval = val;
50048c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_GPIO,
50058c2ecf20Sopenharmony_ci			   "GPIO direction changing to 0x%x",nval);
50068c2ecf20Sopenharmony_ci	}
50078c2ecf20Sopenharmony_ci	return pvr2_write_register(hdw,PVR2_GPIO_DIR,nval);
50088c2ecf20Sopenharmony_ci}
50098c2ecf20Sopenharmony_ci
50108c2ecf20Sopenharmony_ci
50118c2ecf20Sopenharmony_ciint pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val)
50128c2ecf20Sopenharmony_ci{
50138c2ecf20Sopenharmony_ci	u32 cval,nval;
50148c2ecf20Sopenharmony_ci	int ret;
50158c2ecf20Sopenharmony_ci	if (~msk) {
50168c2ecf20Sopenharmony_ci		ret = pvr2_read_register(hdw,PVR2_GPIO_OUT,&cval);
50178c2ecf20Sopenharmony_ci		if (ret) return ret;
50188c2ecf20Sopenharmony_ci		nval = (cval & ~msk) | (val & msk);
50198c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_GPIO,
50208c2ecf20Sopenharmony_ci			   "GPIO output changing 0x%x:0x%x from 0x%x to 0x%x",
50218c2ecf20Sopenharmony_ci			   msk,val,cval,nval);
50228c2ecf20Sopenharmony_ci	} else {
50238c2ecf20Sopenharmony_ci		nval = val;
50248c2ecf20Sopenharmony_ci		pvr2_trace(PVR2_TRACE_GPIO,
50258c2ecf20Sopenharmony_ci			   "GPIO output changing to 0x%x",nval);
50268c2ecf20Sopenharmony_ci	}
50278c2ecf20Sopenharmony_ci	return pvr2_write_register(hdw,PVR2_GPIO_OUT,nval);
50288c2ecf20Sopenharmony_ci}
50298c2ecf20Sopenharmony_ci
50308c2ecf20Sopenharmony_ci
50318c2ecf20Sopenharmony_civoid pvr2_hdw_status_poll(struct pvr2_hdw *hdw)
50328c2ecf20Sopenharmony_ci{
50338c2ecf20Sopenharmony_ci	struct v4l2_tuner *vtp = &hdw->tuner_signal_info;
50348c2ecf20Sopenharmony_ci	memset(vtp, 0, sizeof(*vtp));
50358c2ecf20Sopenharmony_ci	vtp->type = (hdw->input_val == PVR2_CVAL_INPUT_RADIO) ?
50368c2ecf20Sopenharmony_ci		V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
50378c2ecf20Sopenharmony_ci	hdw->tuner_signal_stale = 0;
50388c2ecf20Sopenharmony_ci	/* Note: There apparently is no replacement for VIDIOC_CROPCAP
50398c2ecf20Sopenharmony_ci	   using v4l2-subdev - therefore we can't support that AT ALL right
50408c2ecf20Sopenharmony_ci	   now.  (Of course, no sub-drivers seem to implement it either.
50418c2ecf20Sopenharmony_ci	   But now it's a a chicken and egg problem...) */
50428c2ecf20Sopenharmony_ci	v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, g_tuner, vtp);
50438c2ecf20Sopenharmony_ci	pvr2_trace(PVR2_TRACE_CHIPS, "subdev status poll type=%u strength=%u audio=0x%x cap=0x%x low=%u hi=%u",
50448c2ecf20Sopenharmony_ci		   vtp->type,
50458c2ecf20Sopenharmony_ci		   vtp->signal, vtp->rxsubchans, vtp->capability,
50468c2ecf20Sopenharmony_ci		   vtp->rangelow, vtp->rangehigh);
50478c2ecf20Sopenharmony_ci
50488c2ecf20Sopenharmony_ci	/* We have to do this to avoid getting into constant polling if
50498c2ecf20Sopenharmony_ci	   there's nobody to answer a poll of cropcap info. */
50508c2ecf20Sopenharmony_ci	hdw->cropcap_stale = 0;
50518c2ecf20Sopenharmony_ci}
50528c2ecf20Sopenharmony_ci
50538c2ecf20Sopenharmony_ci
50548c2ecf20Sopenharmony_ciunsigned int pvr2_hdw_get_input_available(struct pvr2_hdw *hdw)
50558c2ecf20Sopenharmony_ci{
50568c2ecf20Sopenharmony_ci	return hdw->input_avail_mask;
50578c2ecf20Sopenharmony_ci}
50588c2ecf20Sopenharmony_ci
50598c2ecf20Sopenharmony_ci
50608c2ecf20Sopenharmony_ciunsigned int pvr2_hdw_get_input_allowed(struct pvr2_hdw *hdw)
50618c2ecf20Sopenharmony_ci{
50628c2ecf20Sopenharmony_ci	return hdw->input_allowed_mask;
50638c2ecf20Sopenharmony_ci}
50648c2ecf20Sopenharmony_ci
50658c2ecf20Sopenharmony_ci
50668c2ecf20Sopenharmony_cistatic int pvr2_hdw_set_input(struct pvr2_hdw *hdw,int v)
50678c2ecf20Sopenharmony_ci{
50688c2ecf20Sopenharmony_ci	if (hdw->input_val != v) {
50698c2ecf20Sopenharmony_ci		hdw->input_val = v;
50708c2ecf20Sopenharmony_ci		hdw->input_dirty = !0;
50718c2ecf20Sopenharmony_ci	}
50728c2ecf20Sopenharmony_ci
50738c2ecf20Sopenharmony_ci	/* Handle side effects - if we switch to a mode that needs the RF
50748c2ecf20Sopenharmony_ci	   tuner, then select the right frequency choice as well and mark
50758c2ecf20Sopenharmony_ci	   it dirty. */
50768c2ecf20Sopenharmony_ci	if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
50778c2ecf20Sopenharmony_ci		hdw->freqSelector = 0;
50788c2ecf20Sopenharmony_ci		hdw->freqDirty = !0;
50798c2ecf20Sopenharmony_ci	} else if ((hdw->input_val == PVR2_CVAL_INPUT_TV) ||
50808c2ecf20Sopenharmony_ci		   (hdw->input_val == PVR2_CVAL_INPUT_DTV)) {
50818c2ecf20Sopenharmony_ci		hdw->freqSelector = 1;
50828c2ecf20Sopenharmony_ci		hdw->freqDirty = !0;
50838c2ecf20Sopenharmony_ci	}
50848c2ecf20Sopenharmony_ci	return 0;
50858c2ecf20Sopenharmony_ci}
50868c2ecf20Sopenharmony_ci
50878c2ecf20Sopenharmony_ci
50888c2ecf20Sopenharmony_ciint pvr2_hdw_set_input_allowed(struct pvr2_hdw *hdw,
50898c2ecf20Sopenharmony_ci			       unsigned int change_mask,
50908c2ecf20Sopenharmony_ci			       unsigned int change_val)
50918c2ecf20Sopenharmony_ci{
50928c2ecf20Sopenharmony_ci	int ret = 0;
50938c2ecf20Sopenharmony_ci	unsigned int nv,m,idx;
50948c2ecf20Sopenharmony_ci	LOCK_TAKE(hdw->big_lock);
50958c2ecf20Sopenharmony_ci	do {
50968c2ecf20Sopenharmony_ci		nv = hdw->input_allowed_mask & ~change_mask;
50978c2ecf20Sopenharmony_ci		nv |= (change_val & change_mask);
50988c2ecf20Sopenharmony_ci		nv &= hdw->input_avail_mask;
50998c2ecf20Sopenharmony_ci		if (!nv) {
51008c2ecf20Sopenharmony_ci			/* No legal modes left; return error instead. */
51018c2ecf20Sopenharmony_ci			ret = -EPERM;
51028c2ecf20Sopenharmony_ci			break;
51038c2ecf20Sopenharmony_ci		}
51048c2ecf20Sopenharmony_ci		hdw->input_allowed_mask = nv;
51058c2ecf20Sopenharmony_ci		if ((1UL << hdw->input_val) & hdw->input_allowed_mask) {
51068c2ecf20Sopenharmony_ci			/* Current mode is still in the allowed mask, so
51078c2ecf20Sopenharmony_ci			   we're done. */
51088c2ecf20Sopenharmony_ci			break;
51098c2ecf20Sopenharmony_ci		}
51108c2ecf20Sopenharmony_ci		/* Select and switch to a mode that is still in the allowed
51118c2ecf20Sopenharmony_ci		   mask */
51128c2ecf20Sopenharmony_ci		if (!hdw->input_allowed_mask) {
51138c2ecf20Sopenharmony_ci			/* Nothing legal; give up */
51148c2ecf20Sopenharmony_ci			break;
51158c2ecf20Sopenharmony_ci		}
51168c2ecf20Sopenharmony_ci		m = hdw->input_allowed_mask;
51178c2ecf20Sopenharmony_ci		for (idx = 0; idx < (sizeof(m) << 3); idx++) {
51188c2ecf20Sopenharmony_ci			if (!((1UL << idx) & m)) continue;
51198c2ecf20Sopenharmony_ci			pvr2_hdw_set_input(hdw,idx);
51208c2ecf20Sopenharmony_ci			break;
51218c2ecf20Sopenharmony_ci		}
51228c2ecf20Sopenharmony_ci	} while (0);
51238c2ecf20Sopenharmony_ci	LOCK_GIVE(hdw->big_lock);
51248c2ecf20Sopenharmony_ci	return ret;
51258c2ecf20Sopenharmony_ci}
51268c2ecf20Sopenharmony_ci
51278c2ecf20Sopenharmony_ci
51288c2ecf20Sopenharmony_ci/* Find I2C address of eeprom */
51298c2ecf20Sopenharmony_cistatic int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw)
51308c2ecf20Sopenharmony_ci{
51318c2ecf20Sopenharmony_ci	int result;
51328c2ecf20Sopenharmony_ci	LOCK_TAKE(hdw->ctl_lock); do {
51338c2ecf20Sopenharmony_ci		hdw->cmd_buffer[0] = FX2CMD_GET_EEPROM_ADDR;
51348c2ecf20Sopenharmony_ci		result = pvr2_send_request(hdw,
51358c2ecf20Sopenharmony_ci					   hdw->cmd_buffer,1,
51368c2ecf20Sopenharmony_ci					   hdw->cmd_buffer,1);
51378c2ecf20Sopenharmony_ci		if (result < 0) break;
51388c2ecf20Sopenharmony_ci		result = hdw->cmd_buffer[0];
51398c2ecf20Sopenharmony_ci	} while(0); LOCK_GIVE(hdw->ctl_lock);
51408c2ecf20Sopenharmony_ci	return result;
51418c2ecf20Sopenharmony_ci}
5142