162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2005-2006 Micronas USA Inc.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/wait.h>
1162306a36Sopenharmony_ci#include <linux/list.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <linux/time.h>
1462306a36Sopenharmony_ci#include <linux/mm.h>
1562306a36Sopenharmony_ci#include <linux/usb.h>
1662306a36Sopenharmony_ci#include <linux/i2c.h>
1762306a36Sopenharmony_ci#include <asm/byteorder.h>
1862306a36Sopenharmony_ci#include <media/i2c/saa7115.h>
1962306a36Sopenharmony_ci#include <media/tuner.h>
2062306a36Sopenharmony_ci#include <media/i2c/uda1342.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "go7007-priv.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic unsigned int assume_endura;
2562306a36Sopenharmony_cimodule_param(assume_endura, int, 0644);
2662306a36Sopenharmony_ciMODULE_PARM_DESC(assume_endura,
2762306a36Sopenharmony_ci			"when probing fails, hardware is a Pelco Endura");
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/* #define GO7007_I2C_DEBUG */ /* for debugging the EZ-USB I2C adapter */
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define	HPI_STATUS_ADDR	0xFFF4
3262306a36Sopenharmony_ci#define	INT_PARAM_ADDR	0xFFF6
3362306a36Sopenharmony_ci#define	INT_INDEX_ADDR	0xFFF8
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/*
3662306a36Sopenharmony_ci * Pipes on EZ-USB interface:
3762306a36Sopenharmony_ci *	0 snd - Control
3862306a36Sopenharmony_ci *	0 rcv - Control
3962306a36Sopenharmony_ci *	2 snd - Download firmware (control)
4062306a36Sopenharmony_ci *	4 rcv - Read Interrupt (interrupt)
4162306a36Sopenharmony_ci *	6 rcv - Read Video (bulk)
4262306a36Sopenharmony_ci *	8 rcv - Read Audio (bulk)
4362306a36Sopenharmony_ci */
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#define GO7007_USB_EZUSB		(1<<0)
4662306a36Sopenharmony_ci#define GO7007_USB_EZUSB_I2C		(1<<1)
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistruct go7007_usb_board {
4962306a36Sopenharmony_ci	unsigned int flags;
5062306a36Sopenharmony_ci	struct go7007_board_info main_info;
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistruct go7007_usb {
5462306a36Sopenharmony_ci	const struct go7007_usb_board *board;
5562306a36Sopenharmony_ci	struct mutex i2c_lock;
5662306a36Sopenharmony_ci	struct usb_device *usbdev;
5762306a36Sopenharmony_ci	struct urb *video_urbs[8];
5862306a36Sopenharmony_ci	struct urb *audio_urbs[8];
5962306a36Sopenharmony_ci	struct urb *intr_urb;
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/*********************** Product specification data ***********************/
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic const struct go7007_usb_board board_matrix_ii = {
6562306a36Sopenharmony_ci	.flags		= GO7007_USB_EZUSB,
6662306a36Sopenharmony_ci	.main_info	= {
6762306a36Sopenharmony_ci		.flags		 = GO7007_BOARD_HAS_AUDIO |
6862306a36Sopenharmony_ci					GO7007_BOARD_USE_ONBOARD_I2C,
6962306a36Sopenharmony_ci		.audio_flags	 = GO7007_AUDIO_I2S_MODE_1 |
7062306a36Sopenharmony_ci					GO7007_AUDIO_WORD_16,
7162306a36Sopenharmony_ci		.audio_rate	 = 48000,
7262306a36Sopenharmony_ci		.audio_bclk_div	 = 8,
7362306a36Sopenharmony_ci		.audio_main_div	 = 2,
7462306a36Sopenharmony_ci		.hpi_buffer_cap  = 7,
7562306a36Sopenharmony_ci		.sensor_flags	 = GO7007_SENSOR_656 |
7662306a36Sopenharmony_ci					GO7007_SENSOR_VALID_ENABLE |
7762306a36Sopenharmony_ci					GO7007_SENSOR_TV |
7862306a36Sopenharmony_ci					GO7007_SENSOR_SAA7115 |
7962306a36Sopenharmony_ci					GO7007_SENSOR_VBI |
8062306a36Sopenharmony_ci					GO7007_SENSOR_SCALING,
8162306a36Sopenharmony_ci		.num_i2c_devs	 = 1,
8262306a36Sopenharmony_ci		.i2c_devs	 = {
8362306a36Sopenharmony_ci			{
8462306a36Sopenharmony_ci				.type	= "saa7115",
8562306a36Sopenharmony_ci				.addr	= 0x20,
8662306a36Sopenharmony_ci				.is_video = 1,
8762306a36Sopenharmony_ci			},
8862306a36Sopenharmony_ci		},
8962306a36Sopenharmony_ci		.num_inputs	 = 2,
9062306a36Sopenharmony_ci		.inputs		 = {
9162306a36Sopenharmony_ci			{
9262306a36Sopenharmony_ci				.video_input	= 0,
9362306a36Sopenharmony_ci				.name		= "Composite",
9462306a36Sopenharmony_ci			},
9562306a36Sopenharmony_ci			{
9662306a36Sopenharmony_ci				.video_input	= 9,
9762306a36Sopenharmony_ci				.name		= "S-Video",
9862306a36Sopenharmony_ci			},
9962306a36Sopenharmony_ci		},
10062306a36Sopenharmony_ci		.video_config	= SAA7115_IDQ_IS_DEFAULT,
10162306a36Sopenharmony_ci	},
10262306a36Sopenharmony_ci};
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic const struct go7007_usb_board board_matrix_reload = {
10562306a36Sopenharmony_ci	.flags		= GO7007_USB_EZUSB,
10662306a36Sopenharmony_ci	.main_info	= {
10762306a36Sopenharmony_ci		.flags		 = GO7007_BOARD_HAS_AUDIO |
10862306a36Sopenharmony_ci					GO7007_BOARD_USE_ONBOARD_I2C,
10962306a36Sopenharmony_ci		.audio_flags	 = GO7007_AUDIO_I2S_MODE_1 |
11062306a36Sopenharmony_ci					GO7007_AUDIO_I2S_MASTER |
11162306a36Sopenharmony_ci					GO7007_AUDIO_WORD_16,
11262306a36Sopenharmony_ci		.audio_rate	 = 48000,
11362306a36Sopenharmony_ci		.audio_bclk_div	 = 8,
11462306a36Sopenharmony_ci		.audio_main_div	 = 2,
11562306a36Sopenharmony_ci		.hpi_buffer_cap  = 7,
11662306a36Sopenharmony_ci		.sensor_flags	 = GO7007_SENSOR_656 |
11762306a36Sopenharmony_ci					GO7007_SENSOR_TV,
11862306a36Sopenharmony_ci		.num_i2c_devs	 = 1,
11962306a36Sopenharmony_ci		.i2c_devs	 = {
12062306a36Sopenharmony_ci			{
12162306a36Sopenharmony_ci				.type	= "saa7113",
12262306a36Sopenharmony_ci				.addr	= 0x25,
12362306a36Sopenharmony_ci				.is_video = 1,
12462306a36Sopenharmony_ci			},
12562306a36Sopenharmony_ci		},
12662306a36Sopenharmony_ci		.num_inputs	 = 2,
12762306a36Sopenharmony_ci		.inputs		 = {
12862306a36Sopenharmony_ci			{
12962306a36Sopenharmony_ci				.video_input	= 0,
13062306a36Sopenharmony_ci				.name		= "Composite",
13162306a36Sopenharmony_ci			},
13262306a36Sopenharmony_ci			{
13362306a36Sopenharmony_ci				.video_input	= 9,
13462306a36Sopenharmony_ci				.name		= "S-Video",
13562306a36Sopenharmony_ci			},
13662306a36Sopenharmony_ci		},
13762306a36Sopenharmony_ci		.video_config	= SAA7115_IDQ_IS_DEFAULT,
13862306a36Sopenharmony_ci	},
13962306a36Sopenharmony_ci};
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic const struct go7007_usb_board board_star_trek = {
14262306a36Sopenharmony_ci	.flags		= GO7007_USB_EZUSB | GO7007_USB_EZUSB_I2C,
14362306a36Sopenharmony_ci	.main_info	= {
14462306a36Sopenharmony_ci		.flags		 = GO7007_BOARD_HAS_AUDIO, /* |
14562306a36Sopenharmony_ci					GO7007_BOARD_HAS_TUNER, */
14662306a36Sopenharmony_ci		.sensor_flags	 = GO7007_SENSOR_656 |
14762306a36Sopenharmony_ci					GO7007_SENSOR_VALID_ENABLE |
14862306a36Sopenharmony_ci					GO7007_SENSOR_TV |
14962306a36Sopenharmony_ci					GO7007_SENSOR_SAA7115 |
15062306a36Sopenharmony_ci					GO7007_SENSOR_VBI |
15162306a36Sopenharmony_ci					GO7007_SENSOR_SCALING,
15262306a36Sopenharmony_ci		.audio_flags	 = GO7007_AUDIO_I2S_MODE_1 |
15362306a36Sopenharmony_ci					GO7007_AUDIO_WORD_16,
15462306a36Sopenharmony_ci		.audio_bclk_div	 = 8,
15562306a36Sopenharmony_ci		.audio_main_div	 = 2,
15662306a36Sopenharmony_ci		.hpi_buffer_cap  = 7,
15762306a36Sopenharmony_ci		.num_i2c_devs	 = 1,
15862306a36Sopenharmony_ci		.i2c_devs	 = {
15962306a36Sopenharmony_ci			{
16062306a36Sopenharmony_ci				.type	= "saa7115",
16162306a36Sopenharmony_ci				.addr	= 0x20,
16262306a36Sopenharmony_ci				.is_video = 1,
16362306a36Sopenharmony_ci			},
16462306a36Sopenharmony_ci		},
16562306a36Sopenharmony_ci		.num_inputs	 = 2,
16662306a36Sopenharmony_ci		.inputs		 = {
16762306a36Sopenharmony_ci		/*	{
16862306a36Sopenharmony_ci		 *		.video_input	= 3,
16962306a36Sopenharmony_ci		 *		.audio_index	= AUDIO_TUNER,
17062306a36Sopenharmony_ci		 *		.name		= "Tuner",
17162306a36Sopenharmony_ci		 *	},
17262306a36Sopenharmony_ci		 */
17362306a36Sopenharmony_ci			{
17462306a36Sopenharmony_ci				.video_input	= 1,
17562306a36Sopenharmony_ci			/*	.audio_index	= AUDIO_EXTERN, */
17662306a36Sopenharmony_ci				.name		= "Composite",
17762306a36Sopenharmony_ci			},
17862306a36Sopenharmony_ci			{
17962306a36Sopenharmony_ci				.video_input	= 8,
18062306a36Sopenharmony_ci			/*	.audio_index	= AUDIO_EXTERN, */
18162306a36Sopenharmony_ci				.name		= "S-Video",
18262306a36Sopenharmony_ci			},
18362306a36Sopenharmony_ci		},
18462306a36Sopenharmony_ci		.video_config	= SAA7115_IDQ_IS_DEFAULT,
18562306a36Sopenharmony_ci	},
18662306a36Sopenharmony_ci};
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic const struct go7007_usb_board board_px_tv402u = {
18962306a36Sopenharmony_ci	.flags		= GO7007_USB_EZUSB | GO7007_USB_EZUSB_I2C,
19062306a36Sopenharmony_ci	.main_info	= {
19162306a36Sopenharmony_ci		.flags		 = GO7007_BOARD_HAS_AUDIO |
19262306a36Sopenharmony_ci					GO7007_BOARD_HAS_TUNER,
19362306a36Sopenharmony_ci		.sensor_flags	 = GO7007_SENSOR_656 |
19462306a36Sopenharmony_ci					GO7007_SENSOR_VALID_ENABLE |
19562306a36Sopenharmony_ci					GO7007_SENSOR_TV |
19662306a36Sopenharmony_ci					GO7007_SENSOR_SAA7115 |
19762306a36Sopenharmony_ci					GO7007_SENSOR_VBI |
19862306a36Sopenharmony_ci					GO7007_SENSOR_SCALING,
19962306a36Sopenharmony_ci		.audio_flags	 = GO7007_AUDIO_I2S_MODE_1 |
20062306a36Sopenharmony_ci					GO7007_AUDIO_WORD_16,
20162306a36Sopenharmony_ci		.audio_bclk_div	 = 8,
20262306a36Sopenharmony_ci		.audio_main_div	 = 2,
20362306a36Sopenharmony_ci		.hpi_buffer_cap  = 7,
20462306a36Sopenharmony_ci		.num_i2c_devs	 = 5,
20562306a36Sopenharmony_ci		.i2c_devs	 = {
20662306a36Sopenharmony_ci			{
20762306a36Sopenharmony_ci				.type	= "saa7115",
20862306a36Sopenharmony_ci				.addr	= 0x20,
20962306a36Sopenharmony_ci				.is_video = 1,
21062306a36Sopenharmony_ci			},
21162306a36Sopenharmony_ci			{
21262306a36Sopenharmony_ci				.type	= "uda1342",
21362306a36Sopenharmony_ci				.addr	= 0x1a,
21462306a36Sopenharmony_ci				.is_audio = 1,
21562306a36Sopenharmony_ci			},
21662306a36Sopenharmony_ci			{
21762306a36Sopenharmony_ci				.type	= "tuner",
21862306a36Sopenharmony_ci				.addr	= 0x60,
21962306a36Sopenharmony_ci			},
22062306a36Sopenharmony_ci			{
22162306a36Sopenharmony_ci				.type	= "tuner",
22262306a36Sopenharmony_ci				.addr	= 0x43,
22362306a36Sopenharmony_ci			},
22462306a36Sopenharmony_ci			{
22562306a36Sopenharmony_ci				.type	= "sony-btf-mpx",
22662306a36Sopenharmony_ci				.addr	= 0x44,
22762306a36Sopenharmony_ci			},
22862306a36Sopenharmony_ci		},
22962306a36Sopenharmony_ci		.num_inputs	 = 3,
23062306a36Sopenharmony_ci		.inputs		 = {
23162306a36Sopenharmony_ci			{
23262306a36Sopenharmony_ci				.video_input	= 3,
23362306a36Sopenharmony_ci				.audio_index	= 0,
23462306a36Sopenharmony_ci				.name		= "Tuner",
23562306a36Sopenharmony_ci			},
23662306a36Sopenharmony_ci			{
23762306a36Sopenharmony_ci				.video_input	= 1,
23862306a36Sopenharmony_ci				.audio_index	= 1,
23962306a36Sopenharmony_ci				.name		= "Composite",
24062306a36Sopenharmony_ci			},
24162306a36Sopenharmony_ci			{
24262306a36Sopenharmony_ci				.video_input	= 8,
24362306a36Sopenharmony_ci				.audio_index	= 1,
24462306a36Sopenharmony_ci				.name		= "S-Video",
24562306a36Sopenharmony_ci			},
24662306a36Sopenharmony_ci		},
24762306a36Sopenharmony_ci		.video_config	= SAA7115_IDQ_IS_DEFAULT,
24862306a36Sopenharmony_ci		.num_aud_inputs	 = 2,
24962306a36Sopenharmony_ci		.aud_inputs	 = {
25062306a36Sopenharmony_ci			{
25162306a36Sopenharmony_ci				.audio_input	= UDA1342_IN2,
25262306a36Sopenharmony_ci				.name		= "Tuner",
25362306a36Sopenharmony_ci			},
25462306a36Sopenharmony_ci			{
25562306a36Sopenharmony_ci				.audio_input	= UDA1342_IN1,
25662306a36Sopenharmony_ci				.name		= "Line In",
25762306a36Sopenharmony_ci			},
25862306a36Sopenharmony_ci		},
25962306a36Sopenharmony_ci	},
26062306a36Sopenharmony_ci};
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic const struct go7007_usb_board board_xmen = {
26362306a36Sopenharmony_ci	.flags		= 0,
26462306a36Sopenharmony_ci	.main_info	= {
26562306a36Sopenharmony_ci		.flags		  = GO7007_BOARD_USE_ONBOARD_I2C,
26662306a36Sopenharmony_ci		.hpi_buffer_cap   = 0,
26762306a36Sopenharmony_ci		.sensor_flags	  = GO7007_SENSOR_VREF_POLAR,
26862306a36Sopenharmony_ci		.sensor_width	  = 320,
26962306a36Sopenharmony_ci		.sensor_height	  = 240,
27062306a36Sopenharmony_ci		.sensor_framerate = 30030,
27162306a36Sopenharmony_ci		.audio_flags	  = GO7007_AUDIO_ONE_CHANNEL |
27262306a36Sopenharmony_ci					GO7007_AUDIO_I2S_MODE_3 |
27362306a36Sopenharmony_ci					GO7007_AUDIO_WORD_14 |
27462306a36Sopenharmony_ci					GO7007_AUDIO_I2S_MASTER |
27562306a36Sopenharmony_ci					GO7007_AUDIO_BCLK_POLAR |
27662306a36Sopenharmony_ci					GO7007_AUDIO_OKI_MODE,
27762306a36Sopenharmony_ci		.audio_rate	  = 8000,
27862306a36Sopenharmony_ci		.audio_bclk_div	  = 48,
27962306a36Sopenharmony_ci		.audio_main_div	  = 1,
28062306a36Sopenharmony_ci		.num_i2c_devs	  = 1,
28162306a36Sopenharmony_ci		.i2c_devs	  = {
28262306a36Sopenharmony_ci			{
28362306a36Sopenharmony_ci				.type	= "ov7640",
28462306a36Sopenharmony_ci				.addr	= 0x21,
28562306a36Sopenharmony_ci			},
28662306a36Sopenharmony_ci		},
28762306a36Sopenharmony_ci		.num_inputs	  = 1,
28862306a36Sopenharmony_ci		.inputs		  = {
28962306a36Sopenharmony_ci			{
29062306a36Sopenharmony_ci				.name		= "Camera",
29162306a36Sopenharmony_ci			},
29262306a36Sopenharmony_ci		},
29362306a36Sopenharmony_ci	},
29462306a36Sopenharmony_ci};
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic const struct go7007_usb_board board_matrix_revolution = {
29762306a36Sopenharmony_ci	.flags		= GO7007_USB_EZUSB,
29862306a36Sopenharmony_ci	.main_info	= {
29962306a36Sopenharmony_ci		.flags		 = GO7007_BOARD_HAS_AUDIO |
30062306a36Sopenharmony_ci					GO7007_BOARD_USE_ONBOARD_I2C,
30162306a36Sopenharmony_ci		.audio_flags	 = GO7007_AUDIO_I2S_MODE_1 |
30262306a36Sopenharmony_ci					GO7007_AUDIO_I2S_MASTER |
30362306a36Sopenharmony_ci					GO7007_AUDIO_WORD_16,
30462306a36Sopenharmony_ci		.audio_rate	 = 48000,
30562306a36Sopenharmony_ci		.audio_bclk_div	 = 8,
30662306a36Sopenharmony_ci		.audio_main_div	 = 2,
30762306a36Sopenharmony_ci		.hpi_buffer_cap  = 7,
30862306a36Sopenharmony_ci		.sensor_flags	 = GO7007_SENSOR_656 |
30962306a36Sopenharmony_ci					GO7007_SENSOR_TV |
31062306a36Sopenharmony_ci					GO7007_SENSOR_VBI,
31162306a36Sopenharmony_ci		.num_i2c_devs	 = 1,
31262306a36Sopenharmony_ci		.i2c_devs	 = {
31362306a36Sopenharmony_ci			{
31462306a36Sopenharmony_ci				.type	= "tw9903",
31562306a36Sopenharmony_ci				.is_video = 1,
31662306a36Sopenharmony_ci				.addr	= 0x44,
31762306a36Sopenharmony_ci			},
31862306a36Sopenharmony_ci		},
31962306a36Sopenharmony_ci		.num_inputs	 = 2,
32062306a36Sopenharmony_ci		.inputs		 = {
32162306a36Sopenharmony_ci			{
32262306a36Sopenharmony_ci				.video_input	= 2,
32362306a36Sopenharmony_ci				.name		= "Composite",
32462306a36Sopenharmony_ci			},
32562306a36Sopenharmony_ci			{
32662306a36Sopenharmony_ci				.video_input	= 8,
32762306a36Sopenharmony_ci				.name		= "S-Video",
32862306a36Sopenharmony_ci			},
32962306a36Sopenharmony_ci		},
33062306a36Sopenharmony_ci	},
33162306a36Sopenharmony_ci};
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci#if 0
33462306a36Sopenharmony_cistatic const struct go7007_usb_board board_lifeview_lr192 = {
33562306a36Sopenharmony_ci	.flags		= GO7007_USB_EZUSB,
33662306a36Sopenharmony_ci	.main_info	= {
33762306a36Sopenharmony_ci		.flags		 = GO7007_BOARD_HAS_AUDIO |
33862306a36Sopenharmony_ci					GO7007_BOARD_USE_ONBOARD_I2C,
33962306a36Sopenharmony_ci		.audio_flags	 = GO7007_AUDIO_I2S_MODE_1 |
34062306a36Sopenharmony_ci					GO7007_AUDIO_WORD_16,
34162306a36Sopenharmony_ci		.audio_rate	 = 48000,
34262306a36Sopenharmony_ci		.audio_bclk_div	 = 8,
34362306a36Sopenharmony_ci		.audio_main_div	 = 2,
34462306a36Sopenharmony_ci		.hpi_buffer_cap  = 7,
34562306a36Sopenharmony_ci		.sensor_flags	 = GO7007_SENSOR_656 |
34662306a36Sopenharmony_ci					GO7007_SENSOR_VALID_ENABLE |
34762306a36Sopenharmony_ci					GO7007_SENSOR_TV |
34862306a36Sopenharmony_ci					GO7007_SENSOR_VBI |
34962306a36Sopenharmony_ci					GO7007_SENSOR_SCALING,
35062306a36Sopenharmony_ci		.num_i2c_devs	 = 0,
35162306a36Sopenharmony_ci		.num_inputs	 = 1,
35262306a36Sopenharmony_ci		.inputs		 = {
35362306a36Sopenharmony_ci			{
35462306a36Sopenharmony_ci				.video_input	= 0,
35562306a36Sopenharmony_ci				.name		= "Composite",
35662306a36Sopenharmony_ci			},
35762306a36Sopenharmony_ci		},
35862306a36Sopenharmony_ci	},
35962306a36Sopenharmony_ci};
36062306a36Sopenharmony_ci#endif
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic const struct go7007_usb_board board_endura = {
36362306a36Sopenharmony_ci	.flags		= 0,
36462306a36Sopenharmony_ci	.main_info	= {
36562306a36Sopenharmony_ci		.flags		 = 0,
36662306a36Sopenharmony_ci		.audio_flags	 = GO7007_AUDIO_I2S_MODE_1 |
36762306a36Sopenharmony_ci					GO7007_AUDIO_I2S_MASTER |
36862306a36Sopenharmony_ci					GO7007_AUDIO_WORD_16,
36962306a36Sopenharmony_ci		.audio_rate	 = 8000,
37062306a36Sopenharmony_ci		.audio_bclk_div	 = 48,
37162306a36Sopenharmony_ci		.audio_main_div	 = 8,
37262306a36Sopenharmony_ci		.hpi_buffer_cap  = 0,
37362306a36Sopenharmony_ci		.sensor_flags	 = GO7007_SENSOR_656 |
37462306a36Sopenharmony_ci					GO7007_SENSOR_TV,
37562306a36Sopenharmony_ci		.sensor_h_offset = 8,
37662306a36Sopenharmony_ci		.num_i2c_devs	 = 0,
37762306a36Sopenharmony_ci		.num_inputs	 = 1,
37862306a36Sopenharmony_ci		.inputs		 = {
37962306a36Sopenharmony_ci			{
38062306a36Sopenharmony_ci				.name		= "Camera",
38162306a36Sopenharmony_ci			},
38262306a36Sopenharmony_ci		},
38362306a36Sopenharmony_ci	},
38462306a36Sopenharmony_ci};
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_cistatic const struct go7007_usb_board board_adlink_mpg24 = {
38762306a36Sopenharmony_ci	.flags		= 0,
38862306a36Sopenharmony_ci	.main_info	= {
38962306a36Sopenharmony_ci		.flags		 = GO7007_BOARD_USE_ONBOARD_I2C,
39062306a36Sopenharmony_ci		.audio_flags	 = GO7007_AUDIO_I2S_MODE_1 |
39162306a36Sopenharmony_ci					GO7007_AUDIO_I2S_MASTER |
39262306a36Sopenharmony_ci					GO7007_AUDIO_WORD_16,
39362306a36Sopenharmony_ci		.audio_rate	 = 48000,
39462306a36Sopenharmony_ci		.audio_bclk_div	 = 8,
39562306a36Sopenharmony_ci		.audio_main_div	 = 2,
39662306a36Sopenharmony_ci		.hpi_buffer_cap  = 0,
39762306a36Sopenharmony_ci		.sensor_flags	 = GO7007_SENSOR_656 |
39862306a36Sopenharmony_ci					GO7007_SENSOR_TV |
39962306a36Sopenharmony_ci					GO7007_SENSOR_VBI,
40062306a36Sopenharmony_ci		.num_i2c_devs	 = 1,
40162306a36Sopenharmony_ci		.i2c_devs	 = {
40262306a36Sopenharmony_ci			{
40362306a36Sopenharmony_ci				.type	= "tw2804",
40462306a36Sopenharmony_ci				.addr	= 0x00, /* yes, really */
40562306a36Sopenharmony_ci				.flags  = I2C_CLIENT_TEN,
40662306a36Sopenharmony_ci				.is_video = 1,
40762306a36Sopenharmony_ci			},
40862306a36Sopenharmony_ci		},
40962306a36Sopenharmony_ci		.num_inputs	 = 1,
41062306a36Sopenharmony_ci		.inputs		 = {
41162306a36Sopenharmony_ci			{
41262306a36Sopenharmony_ci				.name		= "Composite",
41362306a36Sopenharmony_ci			},
41462306a36Sopenharmony_ci		},
41562306a36Sopenharmony_ci	},
41662306a36Sopenharmony_ci};
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cistatic const struct go7007_usb_board board_sensoray_2250 = {
41962306a36Sopenharmony_ci	.flags		= GO7007_USB_EZUSB | GO7007_USB_EZUSB_I2C,
42062306a36Sopenharmony_ci	.main_info	= {
42162306a36Sopenharmony_ci		.audio_flags	 = GO7007_AUDIO_I2S_MODE_1 |
42262306a36Sopenharmony_ci					GO7007_AUDIO_I2S_MASTER |
42362306a36Sopenharmony_ci					GO7007_AUDIO_WORD_16,
42462306a36Sopenharmony_ci		.flags		 = GO7007_BOARD_HAS_AUDIO,
42562306a36Sopenharmony_ci		.audio_rate	 = 48000,
42662306a36Sopenharmony_ci		.audio_bclk_div	 = 8,
42762306a36Sopenharmony_ci		.audio_main_div	 = 2,
42862306a36Sopenharmony_ci		.hpi_buffer_cap  = 7,
42962306a36Sopenharmony_ci		.sensor_flags	 = GO7007_SENSOR_656 |
43062306a36Sopenharmony_ci					GO7007_SENSOR_TV,
43162306a36Sopenharmony_ci		.num_i2c_devs	 = 1,
43262306a36Sopenharmony_ci		.i2c_devs	 = {
43362306a36Sopenharmony_ci			{
43462306a36Sopenharmony_ci				.type	= "s2250",
43562306a36Sopenharmony_ci				.addr	= 0x43,
43662306a36Sopenharmony_ci				.is_video = 1,
43762306a36Sopenharmony_ci				.is_audio = 1,
43862306a36Sopenharmony_ci			},
43962306a36Sopenharmony_ci		},
44062306a36Sopenharmony_ci		.num_inputs	 = 2,
44162306a36Sopenharmony_ci		.inputs		 = {
44262306a36Sopenharmony_ci			{
44362306a36Sopenharmony_ci				.video_input	= 0,
44462306a36Sopenharmony_ci				.name		= "Composite",
44562306a36Sopenharmony_ci			},
44662306a36Sopenharmony_ci			{
44762306a36Sopenharmony_ci				.video_input	= 1,
44862306a36Sopenharmony_ci				.name		= "S-Video",
44962306a36Sopenharmony_ci			},
45062306a36Sopenharmony_ci		},
45162306a36Sopenharmony_ci		.num_aud_inputs	 = 3,
45262306a36Sopenharmony_ci		.aud_inputs	 = {
45362306a36Sopenharmony_ci			{
45462306a36Sopenharmony_ci				.audio_input	= 0,
45562306a36Sopenharmony_ci				.name		= "Line In",
45662306a36Sopenharmony_ci			},
45762306a36Sopenharmony_ci			{
45862306a36Sopenharmony_ci				.audio_input	= 1,
45962306a36Sopenharmony_ci				.name		= "Mic",
46062306a36Sopenharmony_ci			},
46162306a36Sopenharmony_ci			{
46262306a36Sopenharmony_ci				.audio_input	= 2,
46362306a36Sopenharmony_ci				.name		= "Mic Boost",
46462306a36Sopenharmony_ci			},
46562306a36Sopenharmony_ci		},
46662306a36Sopenharmony_ci	},
46762306a36Sopenharmony_ci};
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_cistatic const struct go7007_usb_board board_ads_usbav_709 = {
47062306a36Sopenharmony_ci	.flags		= GO7007_USB_EZUSB,
47162306a36Sopenharmony_ci	.main_info	= {
47262306a36Sopenharmony_ci		.flags		 = GO7007_BOARD_HAS_AUDIO |
47362306a36Sopenharmony_ci					GO7007_BOARD_USE_ONBOARD_I2C,
47462306a36Sopenharmony_ci		.audio_flags	 = GO7007_AUDIO_I2S_MODE_1 |
47562306a36Sopenharmony_ci					GO7007_AUDIO_I2S_MASTER |
47662306a36Sopenharmony_ci					GO7007_AUDIO_WORD_16,
47762306a36Sopenharmony_ci		.audio_rate	 = 48000,
47862306a36Sopenharmony_ci		.audio_bclk_div	 = 8,
47962306a36Sopenharmony_ci		.audio_main_div	 = 2,
48062306a36Sopenharmony_ci		.hpi_buffer_cap  = 7,
48162306a36Sopenharmony_ci		.sensor_flags	 = GO7007_SENSOR_656 |
48262306a36Sopenharmony_ci					GO7007_SENSOR_TV |
48362306a36Sopenharmony_ci					GO7007_SENSOR_VBI,
48462306a36Sopenharmony_ci		.num_i2c_devs	 = 1,
48562306a36Sopenharmony_ci		.i2c_devs	 = {
48662306a36Sopenharmony_ci			{
48762306a36Sopenharmony_ci				.type	= "tw9906",
48862306a36Sopenharmony_ci				.is_video = 1,
48962306a36Sopenharmony_ci				.addr	= 0x44,
49062306a36Sopenharmony_ci			},
49162306a36Sopenharmony_ci		},
49262306a36Sopenharmony_ci		.num_inputs	 = 2,
49362306a36Sopenharmony_ci		.inputs		 = {
49462306a36Sopenharmony_ci			{
49562306a36Sopenharmony_ci				.video_input	= 0,
49662306a36Sopenharmony_ci				.name		= "Composite",
49762306a36Sopenharmony_ci			},
49862306a36Sopenharmony_ci			{
49962306a36Sopenharmony_ci				.video_input	= 10,
50062306a36Sopenharmony_ci				.name		= "S-Video",
50162306a36Sopenharmony_ci			},
50262306a36Sopenharmony_ci		},
50362306a36Sopenharmony_ci	},
50462306a36Sopenharmony_ci};
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_cistatic const struct usb_device_id go7007_usb_id_table[] = {
50762306a36Sopenharmony_ci	{
50862306a36Sopenharmony_ci		.match_flags	= USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
50962306a36Sopenharmony_ci					USB_DEVICE_ID_MATCH_INT_INFO,
51062306a36Sopenharmony_ci		.idVendor	= 0x0eb1,  /* Vendor ID of WIS Technologies */
51162306a36Sopenharmony_ci		.idProduct	= 0x7007,  /* Product ID of GO7007SB chip */
51262306a36Sopenharmony_ci		.bcdDevice_lo	= 0x200,   /* Revision number of XMen */
51362306a36Sopenharmony_ci		.bcdDevice_hi	= 0x200,
51462306a36Sopenharmony_ci		.bInterfaceClass	= 255,
51562306a36Sopenharmony_ci		.bInterfaceSubClass	= 0,
51662306a36Sopenharmony_ci		.bInterfaceProtocol	= 255,
51762306a36Sopenharmony_ci		.driver_info	= (kernel_ulong_t)GO7007_BOARDID_XMEN,
51862306a36Sopenharmony_ci	},
51962306a36Sopenharmony_ci	{
52062306a36Sopenharmony_ci		.match_flags	= USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
52162306a36Sopenharmony_ci		.idVendor	= 0x0eb1,  /* Vendor ID of WIS Technologies */
52262306a36Sopenharmony_ci		.idProduct	= 0x7007,  /* Product ID of GO7007SB chip */
52362306a36Sopenharmony_ci		.bcdDevice_lo	= 0x202,   /* Revision number of Matrix II */
52462306a36Sopenharmony_ci		.bcdDevice_hi	= 0x202,
52562306a36Sopenharmony_ci		.driver_info	= (kernel_ulong_t)GO7007_BOARDID_MATRIX_II,
52662306a36Sopenharmony_ci	},
52762306a36Sopenharmony_ci	{
52862306a36Sopenharmony_ci		.match_flags	= USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
52962306a36Sopenharmony_ci		.idVendor	= 0x0eb1,  /* Vendor ID of WIS Technologies */
53062306a36Sopenharmony_ci		.idProduct	= 0x7007,  /* Product ID of GO7007SB chip */
53162306a36Sopenharmony_ci		.bcdDevice_lo	= 0x204,   /* Revision number of Matrix */
53262306a36Sopenharmony_ci		.bcdDevice_hi	= 0x204,   /*     Reloaded */
53362306a36Sopenharmony_ci		.driver_info	= (kernel_ulong_t)GO7007_BOARDID_MATRIX_RELOAD,
53462306a36Sopenharmony_ci	},
53562306a36Sopenharmony_ci	{
53662306a36Sopenharmony_ci		.match_flags	= USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
53762306a36Sopenharmony_ci					USB_DEVICE_ID_MATCH_INT_INFO,
53862306a36Sopenharmony_ci		.idVendor	= 0x0eb1,  /* Vendor ID of WIS Technologies */
53962306a36Sopenharmony_ci		.idProduct	= 0x7007,  /* Product ID of GO7007SB chip */
54062306a36Sopenharmony_ci		.bcdDevice_lo	= 0x205,   /* Revision number of XMen-II */
54162306a36Sopenharmony_ci		.bcdDevice_hi	= 0x205,
54262306a36Sopenharmony_ci		.bInterfaceClass	= 255,
54362306a36Sopenharmony_ci		.bInterfaceSubClass	= 0,
54462306a36Sopenharmony_ci		.bInterfaceProtocol	= 255,
54562306a36Sopenharmony_ci		.driver_info	= (kernel_ulong_t)GO7007_BOARDID_XMEN_II,
54662306a36Sopenharmony_ci	},
54762306a36Sopenharmony_ci	{
54862306a36Sopenharmony_ci		.match_flags	= USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
54962306a36Sopenharmony_ci		.idVendor	= 0x0eb1,  /* Vendor ID of WIS Technologies */
55062306a36Sopenharmony_ci		.idProduct	= 0x7007,  /* Product ID of GO7007SB chip */
55162306a36Sopenharmony_ci		.bcdDevice_lo	= 0x208,   /* Revision number of Star Trek */
55262306a36Sopenharmony_ci		.bcdDevice_hi	= 0x208,
55362306a36Sopenharmony_ci		.driver_info	= (kernel_ulong_t)GO7007_BOARDID_STAR_TREK,
55462306a36Sopenharmony_ci	},
55562306a36Sopenharmony_ci	{
55662306a36Sopenharmony_ci		.match_flags	= USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
55762306a36Sopenharmony_ci					USB_DEVICE_ID_MATCH_INT_INFO,
55862306a36Sopenharmony_ci		.idVendor	= 0x0eb1,  /* Vendor ID of WIS Technologies */
55962306a36Sopenharmony_ci		.idProduct	= 0x7007,  /* Product ID of GO7007SB chip */
56062306a36Sopenharmony_ci		.bcdDevice_lo	= 0x209,   /* Revision number of XMen-III */
56162306a36Sopenharmony_ci		.bcdDevice_hi	= 0x209,
56262306a36Sopenharmony_ci		.bInterfaceClass	= 255,
56362306a36Sopenharmony_ci		.bInterfaceSubClass	= 0,
56462306a36Sopenharmony_ci		.bInterfaceProtocol	= 255,
56562306a36Sopenharmony_ci		.driver_info	= (kernel_ulong_t)GO7007_BOARDID_XMEN_III,
56662306a36Sopenharmony_ci	},
56762306a36Sopenharmony_ci	{
56862306a36Sopenharmony_ci		.match_flags	= USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
56962306a36Sopenharmony_ci		.idVendor	= 0x0eb1,  /* Vendor ID of WIS Technologies */
57062306a36Sopenharmony_ci		.idProduct	= 0x7007,  /* Product ID of GO7007SB chip */
57162306a36Sopenharmony_ci		.bcdDevice_lo	= 0x210,   /* Revision number of Matrix */
57262306a36Sopenharmony_ci		.bcdDevice_hi	= 0x210,   /*     Revolution */
57362306a36Sopenharmony_ci		.driver_info	= (kernel_ulong_t)GO7007_BOARDID_MATRIX_REV,
57462306a36Sopenharmony_ci	},
57562306a36Sopenharmony_ci	{
57662306a36Sopenharmony_ci		.match_flags	= USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
57762306a36Sopenharmony_ci		.idVendor	= 0x093b,  /* Vendor ID of Plextor */
57862306a36Sopenharmony_ci		.idProduct	= 0xa102,  /* Product ID of M402U */
57962306a36Sopenharmony_ci		.bcdDevice_lo	= 0x1,	   /* revision number of Blueberry */
58062306a36Sopenharmony_ci		.bcdDevice_hi	= 0x1,
58162306a36Sopenharmony_ci		.driver_info	= (kernel_ulong_t)GO7007_BOARDID_PX_M402U,
58262306a36Sopenharmony_ci	},
58362306a36Sopenharmony_ci	{
58462306a36Sopenharmony_ci		.match_flags	= USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
58562306a36Sopenharmony_ci		.idVendor	= 0x093b,  /* Vendor ID of Plextor */
58662306a36Sopenharmony_ci		.idProduct	= 0xa104,  /* Product ID of TV402U */
58762306a36Sopenharmony_ci		.bcdDevice_lo	= 0x1,
58862306a36Sopenharmony_ci		.bcdDevice_hi	= 0x1,
58962306a36Sopenharmony_ci		.driver_info	= (kernel_ulong_t)GO7007_BOARDID_PX_TV402U,
59062306a36Sopenharmony_ci	},
59162306a36Sopenharmony_ci	{
59262306a36Sopenharmony_ci		.match_flags	= USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
59362306a36Sopenharmony_ci		.idVendor	= 0x10fd,  /* Vendor ID of Anubis Electronics */
59462306a36Sopenharmony_ci		.idProduct	= 0xde00,  /* Product ID of Lifeview LR192 */
59562306a36Sopenharmony_ci		.bcdDevice_lo	= 0x1,
59662306a36Sopenharmony_ci		.bcdDevice_hi	= 0x1,
59762306a36Sopenharmony_ci		.driver_info	= (kernel_ulong_t)GO7007_BOARDID_LIFEVIEW_LR192,
59862306a36Sopenharmony_ci	},
59962306a36Sopenharmony_ci	{
60062306a36Sopenharmony_ci		.match_flags	= USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
60162306a36Sopenharmony_ci		.idVendor	= 0x1943,  /* Vendor ID Sensoray */
60262306a36Sopenharmony_ci		.idProduct	= 0x2250,  /* Product ID of 2250/2251 */
60362306a36Sopenharmony_ci		.bcdDevice_lo	= 0x1,
60462306a36Sopenharmony_ci		.bcdDevice_hi	= 0x1,
60562306a36Sopenharmony_ci		.driver_info	= (kernel_ulong_t)GO7007_BOARDID_SENSORAY_2250,
60662306a36Sopenharmony_ci	},
60762306a36Sopenharmony_ci	{
60862306a36Sopenharmony_ci		.match_flags	= USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
60962306a36Sopenharmony_ci		.idVendor	= 0x06e1,  /* Vendor ID of ADS Technologies */
61062306a36Sopenharmony_ci		.idProduct	= 0x0709,  /* Product ID of DVD Xpress DX2 */
61162306a36Sopenharmony_ci		.bcdDevice_lo	= 0x204,
61262306a36Sopenharmony_ci		.bcdDevice_hi	= 0x204,
61362306a36Sopenharmony_ci		.driver_info	= (kernel_ulong_t)GO7007_BOARDID_ADS_USBAV_709,
61462306a36Sopenharmony_ci	},
61562306a36Sopenharmony_ci	{ }					/* Terminating entry */
61662306a36Sopenharmony_ci};
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, go7007_usb_id_table);
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci/********************* Driver for EZ-USB HPI interface *********************/
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_cistatic int go7007_usb_vendor_request(struct go7007 *go, int request,
62362306a36Sopenharmony_ci		int value, int index, void *transfer_buffer, int length, int in)
62462306a36Sopenharmony_ci{
62562306a36Sopenharmony_ci	struct go7007_usb *usb = go->hpi_context;
62662306a36Sopenharmony_ci	int timeout = 5000;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	if (in) {
62962306a36Sopenharmony_ci		return usb_control_msg(usb->usbdev,
63062306a36Sopenharmony_ci				usb_rcvctrlpipe(usb->usbdev, 0), request,
63162306a36Sopenharmony_ci				USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
63262306a36Sopenharmony_ci				value, index, transfer_buffer, length, timeout);
63362306a36Sopenharmony_ci	} else {
63462306a36Sopenharmony_ci		return usb_control_msg(usb->usbdev,
63562306a36Sopenharmony_ci				usb_sndctrlpipe(usb->usbdev, 0), request,
63662306a36Sopenharmony_ci				USB_TYPE_VENDOR | USB_RECIP_DEVICE,
63762306a36Sopenharmony_ci				value, index, transfer_buffer, length, timeout);
63862306a36Sopenharmony_ci	}
63962306a36Sopenharmony_ci}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_cistatic int go7007_usb_interface_reset(struct go7007 *go)
64262306a36Sopenharmony_ci{
64362306a36Sopenharmony_ci	struct go7007_usb *usb = go->hpi_context;
64462306a36Sopenharmony_ci	u16 intr_val, intr_data;
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	if (go->status == STATUS_SHUTDOWN)
64762306a36Sopenharmony_ci		return -1;
64862306a36Sopenharmony_ci	/* Reset encoder */
64962306a36Sopenharmony_ci	if (go7007_write_interrupt(go, 0x0001, 0x0001) < 0)
65062306a36Sopenharmony_ci		return -1;
65162306a36Sopenharmony_ci	msleep(100);
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	if (usb->board->flags & GO7007_USB_EZUSB) {
65462306a36Sopenharmony_ci		/* Reset buffer in EZ-USB */
65562306a36Sopenharmony_ci		pr_debug("resetting EZ-USB buffers\n");
65662306a36Sopenharmony_ci		if (go7007_usb_vendor_request(go, 0x10, 0, 0, NULL, 0, 0) < 0 ||
65762306a36Sopenharmony_ci		    go7007_usb_vendor_request(go, 0x10, 0, 0, NULL, 0, 0) < 0)
65862306a36Sopenharmony_ci			return -1;
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci		/* Reset encoder again */
66162306a36Sopenharmony_ci		if (go7007_write_interrupt(go, 0x0001, 0x0001) < 0)
66262306a36Sopenharmony_ci			return -1;
66362306a36Sopenharmony_ci		msleep(100);
66462306a36Sopenharmony_ci	}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	/* Wait for an interrupt to indicate successful hardware reset */
66762306a36Sopenharmony_ci	if (go7007_read_interrupt(go, &intr_val, &intr_data) < 0 ||
66862306a36Sopenharmony_ci			(intr_val & ~0x1) != 0x55aa) {
66962306a36Sopenharmony_ci		dev_err(go->dev, "unable to reset the USB interface\n");
67062306a36Sopenharmony_ci		return -1;
67162306a36Sopenharmony_ci	}
67262306a36Sopenharmony_ci	return 0;
67362306a36Sopenharmony_ci}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_cistatic int go7007_usb_ezusb_write_interrupt(struct go7007 *go,
67662306a36Sopenharmony_ci						int addr, int data)
67762306a36Sopenharmony_ci{
67862306a36Sopenharmony_ci	struct go7007_usb *usb = go->hpi_context;
67962306a36Sopenharmony_ci	int i, r;
68062306a36Sopenharmony_ci	u16 status_reg = 0;
68162306a36Sopenharmony_ci	int timeout = 500;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	pr_debug("WriteInterrupt: %04x %04x\n", addr, data);
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	for (i = 0; i < 100; ++i) {
68662306a36Sopenharmony_ci		r = usb_control_msg(usb->usbdev,
68762306a36Sopenharmony_ci				usb_rcvctrlpipe(usb->usbdev, 0), 0x14,
68862306a36Sopenharmony_ci				USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
68962306a36Sopenharmony_ci				0, HPI_STATUS_ADDR, go->usb_buf,
69062306a36Sopenharmony_ci				sizeof(status_reg), timeout);
69162306a36Sopenharmony_ci		if (r < 0)
69262306a36Sopenharmony_ci			break;
69362306a36Sopenharmony_ci		status_reg = le16_to_cpu(*((__le16 *)go->usb_buf));
69462306a36Sopenharmony_ci		if (!(status_reg & 0x0010))
69562306a36Sopenharmony_ci			break;
69662306a36Sopenharmony_ci		msleep(10);
69762306a36Sopenharmony_ci	}
69862306a36Sopenharmony_ci	if (r < 0)
69962306a36Sopenharmony_ci		goto write_int_error;
70062306a36Sopenharmony_ci	if (i == 100) {
70162306a36Sopenharmony_ci		dev_err(go->dev, "device is hung, status reg = 0x%04x\n", status_reg);
70262306a36Sopenharmony_ci		return -1;
70362306a36Sopenharmony_ci	}
70462306a36Sopenharmony_ci	r = usb_control_msg(usb->usbdev, usb_sndctrlpipe(usb->usbdev, 0), 0x12,
70562306a36Sopenharmony_ci			USB_TYPE_VENDOR | USB_RECIP_DEVICE, data,
70662306a36Sopenharmony_ci			INT_PARAM_ADDR, NULL, 0, timeout);
70762306a36Sopenharmony_ci	if (r < 0)
70862306a36Sopenharmony_ci		goto write_int_error;
70962306a36Sopenharmony_ci	r = usb_control_msg(usb->usbdev, usb_sndctrlpipe(usb->usbdev, 0),
71062306a36Sopenharmony_ci			0x12, USB_TYPE_VENDOR | USB_RECIP_DEVICE, addr,
71162306a36Sopenharmony_ci			INT_INDEX_ADDR, NULL, 0, timeout);
71262306a36Sopenharmony_ci	if (r < 0)
71362306a36Sopenharmony_ci		goto write_int_error;
71462306a36Sopenharmony_ci	return 0;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ciwrite_int_error:
71762306a36Sopenharmony_ci	dev_err(go->dev, "error in WriteInterrupt: %d\n", r);
71862306a36Sopenharmony_ci	return r;
71962306a36Sopenharmony_ci}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_cistatic int go7007_usb_onboard_write_interrupt(struct go7007 *go,
72262306a36Sopenharmony_ci						int addr, int data)
72362306a36Sopenharmony_ci{
72462306a36Sopenharmony_ci	struct go7007_usb *usb = go->hpi_context;
72562306a36Sopenharmony_ci	int r;
72662306a36Sopenharmony_ci	int timeout = 500;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	pr_debug("WriteInterrupt: %04x %04x\n", addr, data);
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	go->usb_buf[0] = data & 0xff;
73162306a36Sopenharmony_ci	go->usb_buf[1] = data >> 8;
73262306a36Sopenharmony_ci	go->usb_buf[2] = addr & 0xff;
73362306a36Sopenharmony_ci	go->usb_buf[3] = addr >> 8;
73462306a36Sopenharmony_ci	go->usb_buf[4] = go->usb_buf[5] = go->usb_buf[6] = go->usb_buf[7] = 0;
73562306a36Sopenharmony_ci	r = usb_control_msg(usb->usbdev, usb_sndctrlpipe(usb->usbdev, 2), 0x00,
73662306a36Sopenharmony_ci			USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, 0x55aa,
73762306a36Sopenharmony_ci			0xf0f0, go->usb_buf, 8, timeout);
73862306a36Sopenharmony_ci	if (r < 0) {
73962306a36Sopenharmony_ci		dev_err(go->dev, "error in WriteInterrupt: %d\n", r);
74062306a36Sopenharmony_ci		return r;
74162306a36Sopenharmony_ci	}
74262306a36Sopenharmony_ci	return 0;
74362306a36Sopenharmony_ci}
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_cistatic void go7007_usb_readinterrupt_complete(struct urb *urb)
74662306a36Sopenharmony_ci{
74762306a36Sopenharmony_ci	struct go7007 *go = (struct go7007 *)urb->context;
74862306a36Sopenharmony_ci	__le16 *regs = (__le16 *)urb->transfer_buffer;
74962306a36Sopenharmony_ci	int status = urb->status;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	if (status) {
75262306a36Sopenharmony_ci		if (status != -ESHUTDOWN &&
75362306a36Sopenharmony_ci				go->status != STATUS_SHUTDOWN) {
75462306a36Sopenharmony_ci			dev_err(go->dev, "error in read interrupt: %d\n", urb->status);
75562306a36Sopenharmony_ci		} else {
75662306a36Sopenharmony_ci			wake_up(&go->interrupt_waitq);
75762306a36Sopenharmony_ci			return;
75862306a36Sopenharmony_ci		}
75962306a36Sopenharmony_ci	} else if (urb->actual_length != urb->transfer_buffer_length) {
76062306a36Sopenharmony_ci		dev_err(go->dev, "short read in interrupt pipe!\n");
76162306a36Sopenharmony_ci	} else {
76262306a36Sopenharmony_ci		go->interrupt_available = 1;
76362306a36Sopenharmony_ci		go->interrupt_data = __le16_to_cpu(regs[0]);
76462306a36Sopenharmony_ci		go->interrupt_value = __le16_to_cpu(regs[1]);
76562306a36Sopenharmony_ci		pr_debug("ReadInterrupt: %04x %04x\n",
76662306a36Sopenharmony_ci				go->interrupt_value, go->interrupt_data);
76762306a36Sopenharmony_ci	}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	wake_up(&go->interrupt_waitq);
77062306a36Sopenharmony_ci}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_cistatic int go7007_usb_read_interrupt(struct go7007 *go)
77362306a36Sopenharmony_ci{
77462306a36Sopenharmony_ci	struct go7007_usb *usb = go->hpi_context;
77562306a36Sopenharmony_ci	int r;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	r = usb_submit_urb(usb->intr_urb, GFP_KERNEL);
77862306a36Sopenharmony_ci	if (r < 0) {
77962306a36Sopenharmony_ci		dev_err(go->dev, "unable to submit interrupt urb: %d\n", r);
78062306a36Sopenharmony_ci		return r;
78162306a36Sopenharmony_ci	}
78262306a36Sopenharmony_ci	return 0;
78362306a36Sopenharmony_ci}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_cistatic void go7007_usb_read_video_pipe_complete(struct urb *urb)
78662306a36Sopenharmony_ci{
78762306a36Sopenharmony_ci	struct go7007 *go = (struct go7007 *)urb->context;
78862306a36Sopenharmony_ci	int r, status = urb->status;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	if (!vb2_is_streaming(&go->vidq)) {
79162306a36Sopenharmony_ci		wake_up_interruptible(&go->frame_waitq);
79262306a36Sopenharmony_ci		return;
79362306a36Sopenharmony_ci	}
79462306a36Sopenharmony_ci	if (status) {
79562306a36Sopenharmony_ci		dev_err(go->dev, "error in video pipe: %d\n", status);
79662306a36Sopenharmony_ci		return;
79762306a36Sopenharmony_ci	}
79862306a36Sopenharmony_ci	if (urb->actual_length != urb->transfer_buffer_length) {
79962306a36Sopenharmony_ci		dev_err(go->dev, "short read in video pipe!\n");
80062306a36Sopenharmony_ci		return;
80162306a36Sopenharmony_ci	}
80262306a36Sopenharmony_ci	go7007_parse_video_stream(go, urb->transfer_buffer, urb->actual_length);
80362306a36Sopenharmony_ci	r = usb_submit_urb(urb, GFP_ATOMIC);
80462306a36Sopenharmony_ci	if (r < 0)
80562306a36Sopenharmony_ci		dev_err(go->dev, "error in video pipe: %d\n", r);
80662306a36Sopenharmony_ci}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_cistatic void go7007_usb_read_audio_pipe_complete(struct urb *urb)
80962306a36Sopenharmony_ci{
81062306a36Sopenharmony_ci	struct go7007 *go = (struct go7007 *)urb->context;
81162306a36Sopenharmony_ci	int r, status = urb->status;
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	if (!vb2_is_streaming(&go->vidq))
81462306a36Sopenharmony_ci		return;
81562306a36Sopenharmony_ci	if (status) {
81662306a36Sopenharmony_ci		dev_err(go->dev, "error in audio pipe: %d\n",
81762306a36Sopenharmony_ci			status);
81862306a36Sopenharmony_ci		return;
81962306a36Sopenharmony_ci	}
82062306a36Sopenharmony_ci	if (urb->actual_length != urb->transfer_buffer_length) {
82162306a36Sopenharmony_ci		dev_err(go->dev, "short read in audio pipe!\n");
82262306a36Sopenharmony_ci		return;
82362306a36Sopenharmony_ci	}
82462306a36Sopenharmony_ci	if (go->audio_deliver != NULL)
82562306a36Sopenharmony_ci		go->audio_deliver(go, urb->transfer_buffer, urb->actual_length);
82662306a36Sopenharmony_ci	r = usb_submit_urb(urb, GFP_ATOMIC);
82762306a36Sopenharmony_ci	if (r < 0)
82862306a36Sopenharmony_ci		dev_err(go->dev, "error in audio pipe: %d\n", r);
82962306a36Sopenharmony_ci}
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_cistatic int go7007_usb_stream_start(struct go7007 *go)
83262306a36Sopenharmony_ci{
83362306a36Sopenharmony_ci	struct go7007_usb *usb = go->hpi_context;
83462306a36Sopenharmony_ci	int i, r;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	for (i = 0; i < 8; ++i) {
83762306a36Sopenharmony_ci		r = usb_submit_urb(usb->video_urbs[i], GFP_KERNEL);
83862306a36Sopenharmony_ci		if (r < 0) {
83962306a36Sopenharmony_ci			dev_err(go->dev, "error submitting video urb %d: %d\n", i, r);
84062306a36Sopenharmony_ci			goto video_submit_failed;
84162306a36Sopenharmony_ci		}
84262306a36Sopenharmony_ci	}
84362306a36Sopenharmony_ci	if (!go->audio_enabled)
84462306a36Sopenharmony_ci		return 0;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	for (i = 0; i < 8; ++i) {
84762306a36Sopenharmony_ci		r = usb_submit_urb(usb->audio_urbs[i], GFP_KERNEL);
84862306a36Sopenharmony_ci		if (r < 0) {
84962306a36Sopenharmony_ci			dev_err(go->dev, "error submitting audio urb %d: %d\n", i, r);
85062306a36Sopenharmony_ci			goto audio_submit_failed;
85162306a36Sopenharmony_ci		}
85262306a36Sopenharmony_ci	}
85362306a36Sopenharmony_ci	return 0;
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ciaudio_submit_failed:
85662306a36Sopenharmony_ci	for (i = 0; i < 7; ++i)
85762306a36Sopenharmony_ci		usb_kill_urb(usb->audio_urbs[i]);
85862306a36Sopenharmony_civideo_submit_failed:
85962306a36Sopenharmony_ci	for (i = 0; i < 8; ++i)
86062306a36Sopenharmony_ci		usb_kill_urb(usb->video_urbs[i]);
86162306a36Sopenharmony_ci	return -1;
86262306a36Sopenharmony_ci}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_cistatic int go7007_usb_stream_stop(struct go7007 *go)
86562306a36Sopenharmony_ci{
86662306a36Sopenharmony_ci	struct go7007_usb *usb = go->hpi_context;
86762306a36Sopenharmony_ci	int i;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	if (go->status == STATUS_SHUTDOWN)
87062306a36Sopenharmony_ci		return 0;
87162306a36Sopenharmony_ci	for (i = 0; i < 8; ++i)
87262306a36Sopenharmony_ci		usb_kill_urb(usb->video_urbs[i]);
87362306a36Sopenharmony_ci	if (go->audio_enabled)
87462306a36Sopenharmony_ci		for (i = 0; i < 8; ++i)
87562306a36Sopenharmony_ci			usb_kill_urb(usb->audio_urbs[i]);
87662306a36Sopenharmony_ci	return 0;
87762306a36Sopenharmony_ci}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_cistatic int go7007_usb_send_firmware(struct go7007 *go, u8 *data, int len)
88062306a36Sopenharmony_ci{
88162306a36Sopenharmony_ci	struct go7007_usb *usb = go->hpi_context;
88262306a36Sopenharmony_ci	int transferred, pipe;
88362306a36Sopenharmony_ci	int timeout = 500;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	pr_debug("DownloadBuffer sending %d bytes\n", len);
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	if (usb->board->flags & GO7007_USB_EZUSB)
88862306a36Sopenharmony_ci		pipe = usb_sndbulkpipe(usb->usbdev, 2);
88962306a36Sopenharmony_ci	else
89062306a36Sopenharmony_ci		pipe = usb_sndbulkpipe(usb->usbdev, 3);
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	return usb_bulk_msg(usb->usbdev, pipe, data, len,
89362306a36Sopenharmony_ci					&transferred, timeout);
89462306a36Sopenharmony_ci}
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_cistatic void go7007_usb_release(struct go7007 *go)
89762306a36Sopenharmony_ci{
89862306a36Sopenharmony_ci	struct go7007_usb *usb = go->hpi_context;
89962306a36Sopenharmony_ci	struct urb *vurb, *aurb;
90062306a36Sopenharmony_ci	int i;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	if (usb->intr_urb) {
90362306a36Sopenharmony_ci		usb_kill_urb(usb->intr_urb);
90462306a36Sopenharmony_ci		kfree(usb->intr_urb->transfer_buffer);
90562306a36Sopenharmony_ci		usb_free_urb(usb->intr_urb);
90662306a36Sopenharmony_ci	}
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	/* Free USB-related structs */
90962306a36Sopenharmony_ci	for (i = 0; i < 8; ++i) {
91062306a36Sopenharmony_ci		vurb = usb->video_urbs[i];
91162306a36Sopenharmony_ci		if (vurb) {
91262306a36Sopenharmony_ci			usb_kill_urb(vurb);
91362306a36Sopenharmony_ci			kfree(vurb->transfer_buffer);
91462306a36Sopenharmony_ci			usb_free_urb(vurb);
91562306a36Sopenharmony_ci		}
91662306a36Sopenharmony_ci		aurb = usb->audio_urbs[i];
91762306a36Sopenharmony_ci		if (aurb) {
91862306a36Sopenharmony_ci			usb_kill_urb(aurb);
91962306a36Sopenharmony_ci			kfree(aurb->transfer_buffer);
92062306a36Sopenharmony_ci			usb_free_urb(aurb);
92162306a36Sopenharmony_ci		}
92262306a36Sopenharmony_ci	}
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	kfree(go->hpi_context);
92562306a36Sopenharmony_ci}
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_cistatic const struct go7007_hpi_ops go7007_usb_ezusb_hpi_ops = {
92862306a36Sopenharmony_ci	.interface_reset	= go7007_usb_interface_reset,
92962306a36Sopenharmony_ci	.write_interrupt	= go7007_usb_ezusb_write_interrupt,
93062306a36Sopenharmony_ci	.read_interrupt		= go7007_usb_read_interrupt,
93162306a36Sopenharmony_ci	.stream_start		= go7007_usb_stream_start,
93262306a36Sopenharmony_ci	.stream_stop		= go7007_usb_stream_stop,
93362306a36Sopenharmony_ci	.send_firmware		= go7007_usb_send_firmware,
93462306a36Sopenharmony_ci	.release		= go7007_usb_release,
93562306a36Sopenharmony_ci};
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_cistatic const struct go7007_hpi_ops go7007_usb_onboard_hpi_ops = {
93862306a36Sopenharmony_ci	.interface_reset	= go7007_usb_interface_reset,
93962306a36Sopenharmony_ci	.write_interrupt	= go7007_usb_onboard_write_interrupt,
94062306a36Sopenharmony_ci	.read_interrupt		= go7007_usb_read_interrupt,
94162306a36Sopenharmony_ci	.stream_start		= go7007_usb_stream_start,
94262306a36Sopenharmony_ci	.stream_stop		= go7007_usb_stream_stop,
94362306a36Sopenharmony_ci	.send_firmware		= go7007_usb_send_firmware,
94462306a36Sopenharmony_ci	.release		= go7007_usb_release,
94562306a36Sopenharmony_ci};
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci/********************* Driver for EZ-USB I2C adapter *********************/
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_cistatic int go7007_usb_i2c_master_xfer(struct i2c_adapter *adapter,
95062306a36Sopenharmony_ci					struct i2c_msg msgs[], int num)
95162306a36Sopenharmony_ci{
95262306a36Sopenharmony_ci	struct go7007 *go = i2c_get_adapdata(adapter);
95362306a36Sopenharmony_ci	struct go7007_usb *usb = go->hpi_context;
95462306a36Sopenharmony_ci	u8 *buf = go->usb_buf;
95562306a36Sopenharmony_ci	int buf_len, i;
95662306a36Sopenharmony_ci	int ret = -EIO;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	if (go->status == STATUS_SHUTDOWN)
95962306a36Sopenharmony_ci		return -ENODEV;
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	mutex_lock(&usb->i2c_lock);
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	for (i = 0; i < num; ++i) {
96462306a36Sopenharmony_ci		/* The hardware command is "write some bytes then read some
96562306a36Sopenharmony_ci		 * bytes", so we try to coalesce a write followed by a read
96662306a36Sopenharmony_ci		 * into a single USB transaction */
96762306a36Sopenharmony_ci		if (i + 1 < num && msgs[i].addr == msgs[i + 1].addr &&
96862306a36Sopenharmony_ci				!(msgs[i].flags & I2C_M_RD) &&
96962306a36Sopenharmony_ci				(msgs[i + 1].flags & I2C_M_RD)) {
97062306a36Sopenharmony_ci#ifdef GO7007_I2C_DEBUG
97162306a36Sopenharmony_ci			pr_debug("i2c write/read %d/%d bytes on %02x\n",
97262306a36Sopenharmony_ci				msgs[i].len, msgs[i + 1].len, msgs[i].addr);
97362306a36Sopenharmony_ci#endif
97462306a36Sopenharmony_ci			buf[0] = 0x01;
97562306a36Sopenharmony_ci			buf[1] = msgs[i].len + 1;
97662306a36Sopenharmony_ci			buf[2] = msgs[i].addr << 1;
97762306a36Sopenharmony_ci			memcpy(&buf[3], msgs[i].buf, msgs[i].len);
97862306a36Sopenharmony_ci			buf_len = msgs[i].len + 3;
97962306a36Sopenharmony_ci			buf[buf_len++] = msgs[++i].len;
98062306a36Sopenharmony_ci		} else if (msgs[i].flags & I2C_M_RD) {
98162306a36Sopenharmony_ci#ifdef GO7007_I2C_DEBUG
98262306a36Sopenharmony_ci			pr_debug("i2c read %d bytes on %02x\n",
98362306a36Sopenharmony_ci					msgs[i].len, msgs[i].addr);
98462306a36Sopenharmony_ci#endif
98562306a36Sopenharmony_ci			buf[0] = 0x01;
98662306a36Sopenharmony_ci			buf[1] = 1;
98762306a36Sopenharmony_ci			buf[2] = msgs[i].addr << 1;
98862306a36Sopenharmony_ci			buf[3] = msgs[i].len;
98962306a36Sopenharmony_ci			buf_len = 4;
99062306a36Sopenharmony_ci		} else {
99162306a36Sopenharmony_ci#ifdef GO7007_I2C_DEBUG
99262306a36Sopenharmony_ci			pr_debug("i2c write %d bytes on %02x\n",
99362306a36Sopenharmony_ci					msgs[i].len, msgs[i].addr);
99462306a36Sopenharmony_ci#endif
99562306a36Sopenharmony_ci			buf[0] = 0x00;
99662306a36Sopenharmony_ci			buf[1] = msgs[i].len + 1;
99762306a36Sopenharmony_ci			buf[2] = msgs[i].addr << 1;
99862306a36Sopenharmony_ci			memcpy(&buf[3], msgs[i].buf, msgs[i].len);
99962306a36Sopenharmony_ci			buf_len = msgs[i].len + 3;
100062306a36Sopenharmony_ci			buf[buf_len++] = 0;
100162306a36Sopenharmony_ci		}
100262306a36Sopenharmony_ci		if (go7007_usb_vendor_request(go, 0x24, 0, 0,
100362306a36Sopenharmony_ci						buf, buf_len, 0) < 0)
100462306a36Sopenharmony_ci			goto i2c_done;
100562306a36Sopenharmony_ci		if (msgs[i].flags & I2C_M_RD) {
100662306a36Sopenharmony_ci			memset(buf, 0, msgs[i].len + 1);
100762306a36Sopenharmony_ci			if (go7007_usb_vendor_request(go, 0x25, 0, 0, buf,
100862306a36Sopenharmony_ci						msgs[i].len + 1, 1) < 0)
100962306a36Sopenharmony_ci				goto i2c_done;
101062306a36Sopenharmony_ci			memcpy(msgs[i].buf, buf + 1, msgs[i].len);
101162306a36Sopenharmony_ci		}
101262306a36Sopenharmony_ci	}
101362306a36Sopenharmony_ci	ret = num;
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_cii2c_done:
101662306a36Sopenharmony_ci	mutex_unlock(&usb->i2c_lock);
101762306a36Sopenharmony_ci	return ret;
101862306a36Sopenharmony_ci}
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_cistatic u32 go7007_usb_functionality(struct i2c_adapter *adapter)
102162306a36Sopenharmony_ci{
102262306a36Sopenharmony_ci	/* No errors are reported by the hardware, so we don't bother
102362306a36Sopenharmony_ci	 * supporting quick writes to avoid confusing probing */
102462306a36Sopenharmony_ci	return (I2C_FUNC_SMBUS_EMUL) & ~I2C_FUNC_SMBUS_QUICK;
102562306a36Sopenharmony_ci}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_cistatic const struct i2c_algorithm go7007_usb_algo = {
102862306a36Sopenharmony_ci	.master_xfer	= go7007_usb_i2c_master_xfer,
102962306a36Sopenharmony_ci	.functionality	= go7007_usb_functionality,
103062306a36Sopenharmony_ci};
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_cistatic struct i2c_adapter go7007_usb_adap_templ = {
103362306a36Sopenharmony_ci	.owner			= THIS_MODULE,
103462306a36Sopenharmony_ci	.name			= "WIS GO7007SB EZ-USB",
103562306a36Sopenharmony_ci	.algo			= &go7007_usb_algo,
103662306a36Sopenharmony_ci};
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci/********************* USB add/remove functions *********************/
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_cistatic int go7007_usb_probe(struct usb_interface *intf,
104162306a36Sopenharmony_ci		const struct usb_device_id *id)
104262306a36Sopenharmony_ci{
104362306a36Sopenharmony_ci	struct go7007 *go;
104462306a36Sopenharmony_ci	struct go7007_usb *usb;
104562306a36Sopenharmony_ci	const struct go7007_usb_board *board;
104662306a36Sopenharmony_ci	struct usb_device *usbdev = interface_to_usbdev(intf);
104762306a36Sopenharmony_ci	struct usb_host_endpoint *ep;
104862306a36Sopenharmony_ci	unsigned num_i2c_devs;
104962306a36Sopenharmony_ci	char *name;
105062306a36Sopenharmony_ci	int video_pipe, i, v_urb_len;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	pr_debug("probing new GO7007 USB board\n");
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	switch (id->driver_info) {
105562306a36Sopenharmony_ci	case GO7007_BOARDID_MATRIX_II:
105662306a36Sopenharmony_ci		name = "WIS Matrix II or compatible";
105762306a36Sopenharmony_ci		board = &board_matrix_ii;
105862306a36Sopenharmony_ci		break;
105962306a36Sopenharmony_ci	case GO7007_BOARDID_MATRIX_RELOAD:
106062306a36Sopenharmony_ci		name = "WIS Matrix Reloaded or compatible";
106162306a36Sopenharmony_ci		board = &board_matrix_reload;
106262306a36Sopenharmony_ci		break;
106362306a36Sopenharmony_ci	case GO7007_BOARDID_MATRIX_REV:
106462306a36Sopenharmony_ci		name = "WIS Matrix Revolution or compatible";
106562306a36Sopenharmony_ci		board = &board_matrix_revolution;
106662306a36Sopenharmony_ci		break;
106762306a36Sopenharmony_ci	case GO7007_BOARDID_STAR_TREK:
106862306a36Sopenharmony_ci		name = "WIS Star Trek or compatible";
106962306a36Sopenharmony_ci		board = &board_star_trek;
107062306a36Sopenharmony_ci		break;
107162306a36Sopenharmony_ci	case GO7007_BOARDID_XMEN:
107262306a36Sopenharmony_ci		name = "WIS XMen or compatible";
107362306a36Sopenharmony_ci		board = &board_xmen;
107462306a36Sopenharmony_ci		break;
107562306a36Sopenharmony_ci	case GO7007_BOARDID_XMEN_II:
107662306a36Sopenharmony_ci		name = "WIS XMen II or compatible";
107762306a36Sopenharmony_ci		board = &board_xmen;
107862306a36Sopenharmony_ci		break;
107962306a36Sopenharmony_ci	case GO7007_BOARDID_XMEN_III:
108062306a36Sopenharmony_ci		name = "WIS XMen III or compatible";
108162306a36Sopenharmony_ci		board = &board_xmen;
108262306a36Sopenharmony_ci		break;
108362306a36Sopenharmony_ci	case GO7007_BOARDID_PX_M402U:
108462306a36Sopenharmony_ci		name = "Plextor PX-M402U";
108562306a36Sopenharmony_ci		board = &board_matrix_ii;
108662306a36Sopenharmony_ci		break;
108762306a36Sopenharmony_ci	case GO7007_BOARDID_PX_TV402U:
108862306a36Sopenharmony_ci		name = "Plextor PX-TV402U (unknown tuner)";
108962306a36Sopenharmony_ci		board = &board_px_tv402u;
109062306a36Sopenharmony_ci		break;
109162306a36Sopenharmony_ci	case GO7007_BOARDID_LIFEVIEW_LR192:
109262306a36Sopenharmony_ci		dev_err(&intf->dev, "The Lifeview TV Walker Ultra is not supported. Sorry!\n");
109362306a36Sopenharmony_ci		return -ENODEV;
109462306a36Sopenharmony_ci#if 0
109562306a36Sopenharmony_ci		name = "Lifeview TV Walker Ultra";
109662306a36Sopenharmony_ci		board = &board_lifeview_lr192;
109762306a36Sopenharmony_ci#endif
109862306a36Sopenharmony_ci		break;
109962306a36Sopenharmony_ci	case GO7007_BOARDID_SENSORAY_2250:
110062306a36Sopenharmony_ci		dev_info(&intf->dev, "Sensoray 2250 found\n");
110162306a36Sopenharmony_ci		name = "Sensoray 2250/2251";
110262306a36Sopenharmony_ci		board = &board_sensoray_2250;
110362306a36Sopenharmony_ci		break;
110462306a36Sopenharmony_ci	case GO7007_BOARDID_ADS_USBAV_709:
110562306a36Sopenharmony_ci		name = "ADS Tech DVD Xpress DX2";
110662306a36Sopenharmony_ci		board = &board_ads_usbav_709;
110762306a36Sopenharmony_ci		break;
110862306a36Sopenharmony_ci	default:
110962306a36Sopenharmony_ci		dev_err(&intf->dev, "unknown board ID %d!\n",
111062306a36Sopenharmony_ci				(unsigned int)id->driver_info);
111162306a36Sopenharmony_ci		return -ENODEV;
111262306a36Sopenharmony_ci	}
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	go = go7007_alloc(&board->main_info, &intf->dev);
111562306a36Sopenharmony_ci	if (go == NULL)
111662306a36Sopenharmony_ci		return -ENOMEM;
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	usb = kzalloc(sizeof(struct go7007_usb), GFP_KERNEL);
111962306a36Sopenharmony_ci	if (usb == NULL) {
112062306a36Sopenharmony_ci		kfree(go);
112162306a36Sopenharmony_ci		return -ENOMEM;
112262306a36Sopenharmony_ci	}
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci	usb->board = board;
112562306a36Sopenharmony_ci	usb->usbdev = usbdev;
112662306a36Sopenharmony_ci	usb_make_path(usbdev, go->bus_info, sizeof(go->bus_info));
112762306a36Sopenharmony_ci	go->board_id = id->driver_info;
112862306a36Sopenharmony_ci	strscpy(go->name, name, sizeof(go->name));
112962306a36Sopenharmony_ci	if (board->flags & GO7007_USB_EZUSB)
113062306a36Sopenharmony_ci		go->hpi_ops = &go7007_usb_ezusb_hpi_ops;
113162306a36Sopenharmony_ci	else
113262306a36Sopenharmony_ci		go->hpi_ops = &go7007_usb_onboard_hpi_ops;
113362306a36Sopenharmony_ci	go->hpi_context = usb;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	ep = usb->usbdev->ep_in[4];
113662306a36Sopenharmony_ci	if (!ep)
113762306a36Sopenharmony_ci		goto allocfail;
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	/* Allocate the URB and buffer for receiving incoming interrupts */
114062306a36Sopenharmony_ci	usb->intr_urb = usb_alloc_urb(0, GFP_KERNEL);
114162306a36Sopenharmony_ci	if (usb->intr_urb == NULL)
114262306a36Sopenharmony_ci		goto allocfail;
114362306a36Sopenharmony_ci	usb->intr_urb->transfer_buffer = kmalloc_array(2, sizeof(u16),
114462306a36Sopenharmony_ci						       GFP_KERNEL);
114562306a36Sopenharmony_ci	if (usb->intr_urb->transfer_buffer == NULL)
114662306a36Sopenharmony_ci		goto allocfail;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	if (usb_endpoint_type(&ep->desc) == USB_ENDPOINT_XFER_BULK)
114962306a36Sopenharmony_ci		usb_fill_bulk_urb(usb->intr_urb, usb->usbdev,
115062306a36Sopenharmony_ci			usb_rcvbulkpipe(usb->usbdev, 4),
115162306a36Sopenharmony_ci			usb->intr_urb->transfer_buffer, 2*sizeof(u16),
115262306a36Sopenharmony_ci			go7007_usb_readinterrupt_complete, go);
115362306a36Sopenharmony_ci	else
115462306a36Sopenharmony_ci		usb_fill_int_urb(usb->intr_urb, usb->usbdev,
115562306a36Sopenharmony_ci			usb_rcvintpipe(usb->usbdev, 4),
115662306a36Sopenharmony_ci			usb->intr_urb->transfer_buffer, 2*sizeof(u16),
115762306a36Sopenharmony_ci			go7007_usb_readinterrupt_complete, go, 8);
115862306a36Sopenharmony_ci	usb_set_intfdata(intf, &go->v4l2_dev);
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	/* Boot the GO7007 */
116162306a36Sopenharmony_ci	if (go7007_boot_encoder(go, go->board_info->flags &
116262306a36Sopenharmony_ci					GO7007_BOARD_USE_ONBOARD_I2C) < 0)
116362306a36Sopenharmony_ci		goto allocfail;
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	/* Register the EZ-USB I2C adapter, if we're using it */
116662306a36Sopenharmony_ci	if (board->flags & GO7007_USB_EZUSB_I2C) {
116762306a36Sopenharmony_ci		memcpy(&go->i2c_adapter, &go7007_usb_adap_templ,
116862306a36Sopenharmony_ci				sizeof(go7007_usb_adap_templ));
116962306a36Sopenharmony_ci		mutex_init(&usb->i2c_lock);
117062306a36Sopenharmony_ci		go->i2c_adapter.dev.parent = go->dev;
117162306a36Sopenharmony_ci		i2c_set_adapdata(&go->i2c_adapter, go);
117262306a36Sopenharmony_ci		if (i2c_add_adapter(&go->i2c_adapter) < 0) {
117362306a36Sopenharmony_ci			dev_err(go->dev, "error: i2c_add_adapter failed\n");
117462306a36Sopenharmony_ci			goto allocfail;
117562306a36Sopenharmony_ci		}
117662306a36Sopenharmony_ci		go->i2c_adapter_online = 1;
117762306a36Sopenharmony_ci	}
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	/* Pelco and Adlink reused the XMen and XMen-III vendor and product
118062306a36Sopenharmony_ci	 * IDs for their own incompatible designs.  We can detect XMen boards
118162306a36Sopenharmony_ci	 * by probing the sensor, but there is no way to probe the sensors on
118262306a36Sopenharmony_ci	 * the Pelco and Adlink designs so we default to the Adlink.  If it
118362306a36Sopenharmony_ci	 * is actually a Pelco, the user must set the assume_endura module
118462306a36Sopenharmony_ci	 * parameter. */
118562306a36Sopenharmony_ci	if ((go->board_id == GO7007_BOARDID_XMEN ||
118662306a36Sopenharmony_ci				go->board_id == GO7007_BOARDID_XMEN_III) &&
118762306a36Sopenharmony_ci			go->i2c_adapter_online) {
118862306a36Sopenharmony_ci		union i2c_smbus_data data;
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci		/* Check to see if register 0x0A is 0x76 */
119162306a36Sopenharmony_ci		i2c_smbus_xfer(&go->i2c_adapter, 0x21, I2C_CLIENT_SCCB,
119262306a36Sopenharmony_ci			I2C_SMBUS_READ, 0x0A, I2C_SMBUS_BYTE_DATA, &data);
119362306a36Sopenharmony_ci		if (data.byte != 0x76) {
119462306a36Sopenharmony_ci			if (assume_endura) {
119562306a36Sopenharmony_ci				go->board_id = GO7007_BOARDID_ENDURA;
119662306a36Sopenharmony_ci				usb->board = board = &board_endura;
119762306a36Sopenharmony_ci				go->board_info = &board->main_info;
119862306a36Sopenharmony_ci				strscpy(go->name, "Pelco Endura",
119962306a36Sopenharmony_ci					sizeof(go->name));
120062306a36Sopenharmony_ci			} else {
120162306a36Sopenharmony_ci				u16 channel;
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci				/* read channel number from GPIO[1:0] */
120462306a36Sopenharmony_ci				if (go7007_read_addr(go, 0x3c81, &channel))
120562306a36Sopenharmony_ci					goto allocfail;
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci				channel &= 0x3;
120862306a36Sopenharmony_ci				go->board_id = GO7007_BOARDID_ADLINK_MPG24;
120962306a36Sopenharmony_ci				usb->board = board = &board_adlink_mpg24;
121062306a36Sopenharmony_ci				go->board_info = &board->main_info;
121162306a36Sopenharmony_ci				go->channel_number = channel;
121262306a36Sopenharmony_ci				snprintf(go->name, sizeof(go->name),
121362306a36Sopenharmony_ci					"Adlink PCI-MPG24, channel #%d",
121462306a36Sopenharmony_ci					channel);
121562306a36Sopenharmony_ci			}
121662306a36Sopenharmony_ci			go7007_update_board(go);
121762306a36Sopenharmony_ci		}
121862306a36Sopenharmony_ci	}
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	num_i2c_devs = go->board_info->num_i2c_devs;
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci	/* Probe the tuner model on the TV402U */
122362306a36Sopenharmony_ci	if (go->board_id == GO7007_BOARDID_PX_TV402U) {
122462306a36Sopenharmony_ci		/* Board strapping indicates tuner model */
122562306a36Sopenharmony_ci		if (go7007_usb_vendor_request(go, 0x41, 0, 0, go->usb_buf, 3,
122662306a36Sopenharmony_ci					1) < 0) {
122762306a36Sopenharmony_ci			dev_err(go->dev, "GPIO read failed!\n");
122862306a36Sopenharmony_ci			goto allocfail;
122962306a36Sopenharmony_ci		}
123062306a36Sopenharmony_ci		switch (go->usb_buf[0] >> 6) {
123162306a36Sopenharmony_ci		case 1:
123262306a36Sopenharmony_ci			go->tuner_type = TUNER_SONY_BTF_PG472Z;
123362306a36Sopenharmony_ci			go->std = V4L2_STD_PAL;
123462306a36Sopenharmony_ci			strscpy(go->name, "Plextor PX-TV402U-EU",
123562306a36Sopenharmony_ci				sizeof(go->name));
123662306a36Sopenharmony_ci			break;
123762306a36Sopenharmony_ci		case 2:
123862306a36Sopenharmony_ci			go->tuner_type = TUNER_SONY_BTF_PK467Z;
123962306a36Sopenharmony_ci			go->std = V4L2_STD_NTSC_M_JP;
124062306a36Sopenharmony_ci			num_i2c_devs -= 2;
124162306a36Sopenharmony_ci			strscpy(go->name, "Plextor PX-TV402U-JP",
124262306a36Sopenharmony_ci				sizeof(go->name));
124362306a36Sopenharmony_ci			break;
124462306a36Sopenharmony_ci		case 3:
124562306a36Sopenharmony_ci			go->tuner_type = TUNER_SONY_BTF_PB463Z;
124662306a36Sopenharmony_ci			num_i2c_devs -= 2;
124762306a36Sopenharmony_ci			strscpy(go->name, "Plextor PX-TV402U-NA",
124862306a36Sopenharmony_ci				sizeof(go->name));
124962306a36Sopenharmony_ci			break;
125062306a36Sopenharmony_ci		default:
125162306a36Sopenharmony_ci			pr_debug("unable to detect tuner type!\n");
125262306a36Sopenharmony_ci			break;
125362306a36Sopenharmony_ci		}
125462306a36Sopenharmony_ci		/* Configure tuner mode selection inputs connected
125562306a36Sopenharmony_ci		 * to the EZ-USB GPIO output pins */
125662306a36Sopenharmony_ci		if (go7007_usb_vendor_request(go, 0x40, 0x7f02, 0,
125762306a36Sopenharmony_ci					NULL, 0, 0) < 0) {
125862306a36Sopenharmony_ci			dev_err(go->dev, "GPIO write failed!\n");
125962306a36Sopenharmony_ci			goto allocfail;
126062306a36Sopenharmony_ci		}
126162306a36Sopenharmony_ci	}
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	/* Print a nasty message if the user attempts to use a USB2.0 device in
126462306a36Sopenharmony_ci	 * a USB1.1 port.  There will be silent corruption of the stream. */
126562306a36Sopenharmony_ci	if ((board->flags & GO7007_USB_EZUSB) &&
126662306a36Sopenharmony_ci			usbdev->speed != USB_SPEED_HIGH)
126762306a36Sopenharmony_ci		dev_err(go->dev, "*** WARNING ***  This device must be connected to a USB 2.0 port! Attempting to capture video through a USB 1.1 port will result in stream corruption, even at low bitrates!\n");
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	/* Allocate the URBs and buffers for receiving the video stream */
127062306a36Sopenharmony_ci	if (board->flags & GO7007_USB_EZUSB) {
127162306a36Sopenharmony_ci		if (!usb->usbdev->ep_in[6])
127262306a36Sopenharmony_ci			goto allocfail;
127362306a36Sopenharmony_ci		v_urb_len = 1024;
127462306a36Sopenharmony_ci		video_pipe = usb_rcvbulkpipe(usb->usbdev, 6);
127562306a36Sopenharmony_ci	} else {
127662306a36Sopenharmony_ci		if (!usb->usbdev->ep_in[1])
127762306a36Sopenharmony_ci			goto allocfail;
127862306a36Sopenharmony_ci		v_urb_len = 512;
127962306a36Sopenharmony_ci		video_pipe = usb_rcvbulkpipe(usb->usbdev, 1);
128062306a36Sopenharmony_ci	}
128162306a36Sopenharmony_ci	for (i = 0; i < 8; ++i) {
128262306a36Sopenharmony_ci		usb->video_urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
128362306a36Sopenharmony_ci		if (usb->video_urbs[i] == NULL)
128462306a36Sopenharmony_ci			goto allocfail;
128562306a36Sopenharmony_ci		usb->video_urbs[i]->transfer_buffer =
128662306a36Sopenharmony_ci						kmalloc(v_urb_len, GFP_KERNEL);
128762306a36Sopenharmony_ci		if (usb->video_urbs[i]->transfer_buffer == NULL)
128862306a36Sopenharmony_ci			goto allocfail;
128962306a36Sopenharmony_ci		usb_fill_bulk_urb(usb->video_urbs[i], usb->usbdev, video_pipe,
129062306a36Sopenharmony_ci				usb->video_urbs[i]->transfer_buffer, v_urb_len,
129162306a36Sopenharmony_ci				go7007_usb_read_video_pipe_complete, go);
129262306a36Sopenharmony_ci	}
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	/* Allocate the URBs and buffers for receiving the audio stream */
129562306a36Sopenharmony_ci	if ((board->flags & GO7007_USB_EZUSB) &&
129662306a36Sopenharmony_ci	    (board->main_info.flags & GO7007_BOARD_HAS_AUDIO)) {
129762306a36Sopenharmony_ci		if (!usb->usbdev->ep_in[8])
129862306a36Sopenharmony_ci			goto allocfail;
129962306a36Sopenharmony_ci		for (i = 0; i < 8; ++i) {
130062306a36Sopenharmony_ci			usb->audio_urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
130162306a36Sopenharmony_ci			if (usb->audio_urbs[i] == NULL)
130262306a36Sopenharmony_ci				goto allocfail;
130362306a36Sopenharmony_ci			usb->audio_urbs[i]->transfer_buffer = kmalloc(4096,
130462306a36Sopenharmony_ci								GFP_KERNEL);
130562306a36Sopenharmony_ci			if (usb->audio_urbs[i]->transfer_buffer == NULL)
130662306a36Sopenharmony_ci				goto allocfail;
130762306a36Sopenharmony_ci			usb_fill_bulk_urb(usb->audio_urbs[i], usb->usbdev,
130862306a36Sopenharmony_ci				usb_rcvbulkpipe(usb->usbdev, 8),
130962306a36Sopenharmony_ci				usb->audio_urbs[i]->transfer_buffer, 4096,
131062306a36Sopenharmony_ci				go7007_usb_read_audio_pipe_complete, go);
131162306a36Sopenharmony_ci		}
131262306a36Sopenharmony_ci	}
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	/* Do any final GO7007 initialization, then register the
131562306a36Sopenharmony_ci	 * V4L2 and ALSA interfaces */
131662306a36Sopenharmony_ci	if (go7007_register_encoder(go, num_i2c_devs) < 0)
131762306a36Sopenharmony_ci		goto allocfail;
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	go->status = STATUS_ONLINE;
132062306a36Sopenharmony_ci	return 0;
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ciallocfail:
132362306a36Sopenharmony_ci	go7007_usb_release(go);
132462306a36Sopenharmony_ci	kfree(go);
132562306a36Sopenharmony_ci	return -ENOMEM;
132662306a36Sopenharmony_ci}
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_cistatic void go7007_usb_disconnect(struct usb_interface *intf)
132962306a36Sopenharmony_ci{
133062306a36Sopenharmony_ci	struct go7007 *go = to_go7007(usb_get_intfdata(intf));
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_ci	mutex_lock(&go->queue_lock);
133362306a36Sopenharmony_ci	mutex_lock(&go->serialize_lock);
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci	if (go->audio_enabled)
133662306a36Sopenharmony_ci		go7007_snd_remove(go);
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	go->status = STATUS_SHUTDOWN;
133962306a36Sopenharmony_ci	v4l2_device_disconnect(&go->v4l2_dev);
134062306a36Sopenharmony_ci	video_unregister_device(&go->vdev);
134162306a36Sopenharmony_ci	mutex_unlock(&go->serialize_lock);
134262306a36Sopenharmony_ci	mutex_unlock(&go->queue_lock);
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_ci	v4l2_device_put(&go->v4l2_dev);
134562306a36Sopenharmony_ci}
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_cistatic struct usb_driver go7007_usb_driver = {
134862306a36Sopenharmony_ci	.name		= "go7007",
134962306a36Sopenharmony_ci	.probe		= go7007_usb_probe,
135062306a36Sopenharmony_ci	.disconnect	= go7007_usb_disconnect,
135162306a36Sopenharmony_ci	.id_table	= go7007_usb_id_table,
135262306a36Sopenharmony_ci};
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_cimodule_usb_driver(go7007_usb_driver);
135562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
1356