162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  cx18 driver initialization and card probing
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Derived from ivtv-driver.c
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
862306a36Sopenharmony_ci *  Copyright (C) 2008  Andy Walls <awalls@md.metrocast.net>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include "cx18-driver.h"
1262306a36Sopenharmony_ci#include "cx18-io.h"
1362306a36Sopenharmony_ci#include "cx18-version.h"
1462306a36Sopenharmony_ci#include "cx18-cards.h"
1562306a36Sopenharmony_ci#include "cx18-i2c.h"
1662306a36Sopenharmony_ci#include "cx18-irq.h"
1762306a36Sopenharmony_ci#include "cx18-gpio.h"
1862306a36Sopenharmony_ci#include "cx18-firmware.h"
1962306a36Sopenharmony_ci#include "cx18-queue.h"
2062306a36Sopenharmony_ci#include "cx18-streams.h"
2162306a36Sopenharmony_ci#include "cx18-av-core.h"
2262306a36Sopenharmony_ci#include "cx18-scb.h"
2362306a36Sopenharmony_ci#include "cx18-mailbox.h"
2462306a36Sopenharmony_ci#include "cx18-ioctl.h"
2562306a36Sopenharmony_ci#include "cx18-controls.h"
2662306a36Sopenharmony_ci#include "xc2028.h"
2762306a36Sopenharmony_ci#include <linux/dma-mapping.h>
2862306a36Sopenharmony_ci#include <media/tveeprom.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/* If you have already X v4l cards, then set this to X. This way
3162306a36Sopenharmony_ci   the device numbers stay matched. Example: you have a WinTV card
3262306a36Sopenharmony_ci   without radio and a Compro H900 with. Normally this would give a
3362306a36Sopenharmony_ci   video1 device together with a radio0 device for the Compro. By
3462306a36Sopenharmony_ci   setting this to 1 you ensure that radio0 is now also radio1. */
3562306a36Sopenharmony_ciint cx18_first_minor;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/* Callback for registering extensions */
3862306a36Sopenharmony_ciint (*cx18_ext_init)(struct cx18 *);
3962306a36Sopenharmony_ciEXPORT_SYMBOL(cx18_ext_init);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/* add your revision and whatnot here */
4262306a36Sopenharmony_cistatic const struct pci_device_id cx18_pci_tbl[] = {
4362306a36Sopenharmony_ci	{PCI_VENDOR_ID_CX, PCI_DEVICE_ID_CX23418,
4462306a36Sopenharmony_ci	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
4562306a36Sopenharmony_ci	{0,}
4662306a36Sopenharmony_ci};
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, cx18_pci_tbl);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic atomic_t cx18_instance = ATOMIC_INIT(0);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci/* Parameter declarations */
5362306a36Sopenharmony_cistatic int cardtype[CX18_MAX_CARDS];
5462306a36Sopenharmony_cistatic int tuner[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
5562306a36Sopenharmony_ci				     -1, -1, -1, -1, -1, -1, -1, -1,
5662306a36Sopenharmony_ci				     -1, -1, -1, -1, -1, -1, -1, -1,
5762306a36Sopenharmony_ci				     -1, -1, -1, -1, -1, -1, -1, -1 };
5862306a36Sopenharmony_cistatic int radio[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
5962306a36Sopenharmony_ci				     -1, -1, -1, -1, -1, -1, -1, -1,
6062306a36Sopenharmony_ci				     -1, -1, -1, -1, -1, -1, -1, -1,
6162306a36Sopenharmony_ci				     -1, -1, -1, -1, -1, -1, -1, -1 };
6262306a36Sopenharmony_cistatic unsigned cardtype_c = 1;
6362306a36Sopenharmony_cistatic unsigned tuner_c = 1;
6462306a36Sopenharmony_cistatic unsigned radio_c = 1;
6562306a36Sopenharmony_cistatic char pal[] = "--";
6662306a36Sopenharmony_cistatic char secam[] = "--";
6762306a36Sopenharmony_cistatic char ntsc[] = "-";
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/* Buffers */
7062306a36Sopenharmony_cistatic int enc_ts_buffers = CX18_DEFAULT_ENC_TS_BUFFERS;
7162306a36Sopenharmony_cistatic int enc_mpg_buffers = CX18_DEFAULT_ENC_MPG_BUFFERS;
7262306a36Sopenharmony_cistatic int enc_idx_buffers = CX18_DEFAULT_ENC_IDX_BUFFERS;
7362306a36Sopenharmony_cistatic int enc_yuv_buffers = CX18_DEFAULT_ENC_YUV_BUFFERS;
7462306a36Sopenharmony_cistatic int enc_vbi_buffers = CX18_DEFAULT_ENC_VBI_BUFFERS;
7562306a36Sopenharmony_cistatic int enc_pcm_buffers = CX18_DEFAULT_ENC_PCM_BUFFERS;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic int enc_ts_bufsize = CX18_DEFAULT_ENC_TS_BUFSIZE;
7862306a36Sopenharmony_cistatic int enc_mpg_bufsize = CX18_DEFAULT_ENC_MPG_BUFSIZE;
7962306a36Sopenharmony_cistatic int enc_idx_bufsize = CX18_DEFAULT_ENC_IDX_BUFSIZE;
8062306a36Sopenharmony_cistatic int enc_yuv_bufsize = CX18_DEFAULT_ENC_YUV_BUFSIZE;
8162306a36Sopenharmony_cistatic int enc_pcm_bufsize = CX18_DEFAULT_ENC_PCM_BUFSIZE;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic int enc_ts_bufs = -1;
8462306a36Sopenharmony_cistatic int enc_mpg_bufs = -1;
8562306a36Sopenharmony_cistatic int enc_idx_bufs = CX18_MAX_FW_MDLS_PER_STREAM;
8662306a36Sopenharmony_cistatic int enc_yuv_bufs = -1;
8762306a36Sopenharmony_cistatic int enc_vbi_bufs = -1;
8862306a36Sopenharmony_cistatic int enc_pcm_bufs = -1;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic int cx18_pci_latency = 1;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic int mmio_ndelay;
9462306a36Sopenharmony_cistatic int retry_mmio = 1;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ciint cx18_debug;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cimodule_param_array(tuner, int, &tuner_c, 0644);
9962306a36Sopenharmony_cimodule_param_array(radio, int, &radio_c, 0644);
10062306a36Sopenharmony_cimodule_param_array(cardtype, int, &cardtype_c, 0644);
10162306a36Sopenharmony_cimodule_param_string(pal, pal, sizeof(pal), 0644);
10262306a36Sopenharmony_cimodule_param_string(secam, secam, sizeof(secam), 0644);
10362306a36Sopenharmony_cimodule_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
10462306a36Sopenharmony_cimodule_param_named(debug, cx18_debug, int, 0644);
10562306a36Sopenharmony_cimodule_param(mmio_ndelay, int, 0644);
10662306a36Sopenharmony_cimodule_param(retry_mmio, int, 0644);
10762306a36Sopenharmony_cimodule_param(cx18_pci_latency, int, 0644);
10862306a36Sopenharmony_cimodule_param(cx18_first_minor, int, 0644);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cimodule_param(enc_ts_buffers, int, 0644);
11162306a36Sopenharmony_cimodule_param(enc_mpg_buffers, int, 0644);
11262306a36Sopenharmony_cimodule_param(enc_idx_buffers, int, 0644);
11362306a36Sopenharmony_cimodule_param(enc_yuv_buffers, int, 0644);
11462306a36Sopenharmony_cimodule_param(enc_vbi_buffers, int, 0644);
11562306a36Sopenharmony_cimodule_param(enc_pcm_buffers, int, 0644);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cimodule_param(enc_ts_bufsize, int, 0644);
11862306a36Sopenharmony_cimodule_param(enc_mpg_bufsize, int, 0644);
11962306a36Sopenharmony_cimodule_param(enc_idx_bufsize, int, 0644);
12062306a36Sopenharmony_cimodule_param(enc_yuv_bufsize, int, 0644);
12162306a36Sopenharmony_cimodule_param(enc_pcm_bufsize, int, 0644);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cimodule_param(enc_ts_bufs, int, 0644);
12462306a36Sopenharmony_cimodule_param(enc_mpg_bufs, int, 0644);
12562306a36Sopenharmony_cimodule_param(enc_idx_bufs, int, 0644);
12662306a36Sopenharmony_cimodule_param(enc_yuv_bufs, int, 0644);
12762306a36Sopenharmony_cimodule_param(enc_vbi_bufs, int, 0644);
12862306a36Sopenharmony_cimodule_param(enc_pcm_bufs, int, 0644);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ciMODULE_PARM_DESC(tuner, "Tuner type selection,\n"
13162306a36Sopenharmony_ci			"\t\t\tsee tuner.h for values");
13262306a36Sopenharmony_ciMODULE_PARM_DESC(radio,
13362306a36Sopenharmony_ci		 "Enable or disable the radio. Use only if autodetection\n"
13462306a36Sopenharmony_ci		 "\t\t\tfails. 0 = disable, 1 = enable");
13562306a36Sopenharmony_ciMODULE_PARM_DESC(cardtype,
13662306a36Sopenharmony_ci		 "Only use this option if your card is not detected properly.\n"
13762306a36Sopenharmony_ci		 "\t\tSpecify card type:\n"
13862306a36Sopenharmony_ci		 "\t\t\t 1 = Hauppauge HVR 1600 (ESMT memory)\n"
13962306a36Sopenharmony_ci		 "\t\t\t 2 = Hauppauge HVR 1600 (Samsung memory)\n"
14062306a36Sopenharmony_ci		 "\t\t\t 3 = Compro VideoMate H900\n"
14162306a36Sopenharmony_ci		 "\t\t\t 4 = Yuan MPC718\n"
14262306a36Sopenharmony_ci		 "\t\t\t 5 = Conexant Raptor PAL/SECAM\n"
14362306a36Sopenharmony_ci		 "\t\t\t 6 = Toshiba Qosmio DVB-T/Analog\n"
14462306a36Sopenharmony_ci		 "\t\t\t 7 = Leadtek WinFast PVR2100\n"
14562306a36Sopenharmony_ci		 "\t\t\t 8 = Leadtek WinFast DVR3100 H\n"
14662306a36Sopenharmony_ci		 "\t\t\t 9 = GoTView PCI DVD3 Hybrid\n"
14762306a36Sopenharmony_ci		 "\t\t\t 10 = Hauppauge HVR 1600 (S5H1411)\n"
14862306a36Sopenharmony_ci		 "\t\t\t 0 = Autodetect (default)\n"
14962306a36Sopenharmony_ci		 "\t\t\t-1 = Ignore this card\n\t\t");
15062306a36Sopenharmony_ciMODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60");
15162306a36Sopenharmony_ciMODULE_PARM_DESC(secam, "Set SECAM standard: B, G, H, D, K, L, LC");
15262306a36Sopenharmony_ciMODULE_PARM_DESC(ntsc, "Set NTSC standard: M, J, K");
15362306a36Sopenharmony_ciMODULE_PARM_DESC(debug,
15462306a36Sopenharmony_ci		 "Debug level (bitmask). Default: 0\n"
15562306a36Sopenharmony_ci		 "\t\t\t  1/0x0001: warning\n"
15662306a36Sopenharmony_ci		 "\t\t\t  2/0x0002: info\n"
15762306a36Sopenharmony_ci		 "\t\t\t  4/0x0004: mailbox\n"
15862306a36Sopenharmony_ci		 "\t\t\t  8/0x0008: dma\n"
15962306a36Sopenharmony_ci		 "\t\t\t 16/0x0010: ioctl\n"
16062306a36Sopenharmony_ci		 "\t\t\t 32/0x0020: file\n"
16162306a36Sopenharmony_ci		 "\t\t\t 64/0x0040: i2c\n"
16262306a36Sopenharmony_ci		 "\t\t\t128/0x0080: irq\n"
16362306a36Sopenharmony_ci		 "\t\t\t256/0x0100: high volume\n");
16462306a36Sopenharmony_ciMODULE_PARM_DESC(cx18_pci_latency,
16562306a36Sopenharmony_ci		 "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n"
16662306a36Sopenharmony_ci		 "\t\t\tDefault: Yes");
16762306a36Sopenharmony_ciMODULE_PARM_DESC(retry_mmio,
16862306a36Sopenharmony_ci		 "(Deprecated) MMIO writes are now always checked and retried\n"
16962306a36Sopenharmony_ci		 "\t\t\tEffectively: 1 [Yes]");
17062306a36Sopenharmony_ciMODULE_PARM_DESC(mmio_ndelay,
17162306a36Sopenharmony_ci		 "(Deprecated) MMIO accesses are now never purposely delayed\n"
17262306a36Sopenharmony_ci		 "\t\t\tEffectively: 0 ns");
17362306a36Sopenharmony_ciMODULE_PARM_DESC(enc_ts_buffers,
17462306a36Sopenharmony_ci		 "Encoder TS buffer memory (MB). (enc_ts_bufs can override)\n"
17562306a36Sopenharmony_ci		 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFFERS));
17662306a36Sopenharmony_ciMODULE_PARM_DESC(enc_ts_bufsize,
17762306a36Sopenharmony_ci		 "Size of an encoder TS buffer (kB)\n"
17862306a36Sopenharmony_ci		 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFSIZE));
17962306a36Sopenharmony_ciMODULE_PARM_DESC(enc_ts_bufs,
18062306a36Sopenharmony_ci		 "Number of encoder TS buffers\n"
18162306a36Sopenharmony_ci		 "\t\t\tDefault is computed from other enc_ts_* parameters");
18262306a36Sopenharmony_ciMODULE_PARM_DESC(enc_mpg_buffers,
18362306a36Sopenharmony_ci		 "Encoder MPG buffer memory (MB). (enc_mpg_bufs can override)\n"
18462306a36Sopenharmony_ci		 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS));
18562306a36Sopenharmony_ciMODULE_PARM_DESC(enc_mpg_bufsize,
18662306a36Sopenharmony_ci		 "Size of an encoder MPG buffer (kB)\n"
18762306a36Sopenharmony_ci		 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFSIZE));
18862306a36Sopenharmony_ciMODULE_PARM_DESC(enc_mpg_bufs,
18962306a36Sopenharmony_ci		 "Number of encoder MPG buffers\n"
19062306a36Sopenharmony_ci		 "\t\t\tDefault is computed from other enc_mpg_* parameters");
19162306a36Sopenharmony_ciMODULE_PARM_DESC(enc_idx_buffers,
19262306a36Sopenharmony_ci		 "(Deprecated) Encoder IDX buffer memory (MB)\n"
19362306a36Sopenharmony_ci		 "\t\t\tIgnored, except 0 disables IDX buffer allocations\n"
19462306a36Sopenharmony_ci		 "\t\t\tDefault: 1 [Enabled]");
19562306a36Sopenharmony_ciMODULE_PARM_DESC(enc_idx_bufsize,
19662306a36Sopenharmony_ci		 "Size of an encoder IDX buffer (kB)\n"
19762306a36Sopenharmony_ci		 "\t\t\tAllowed values are multiples of 1.5 kB rounded up\n"
19862306a36Sopenharmony_ci		 "\t\t\t(multiples of size required for 64 index entries)\n"
19962306a36Sopenharmony_ci		 "\t\t\tDefault: 2");
20062306a36Sopenharmony_ciMODULE_PARM_DESC(enc_idx_bufs,
20162306a36Sopenharmony_ci		 "Number of encoder IDX buffers\n"
20262306a36Sopenharmony_ci		 "\t\t\tDefault: " __stringify(CX18_MAX_FW_MDLS_PER_STREAM));
20362306a36Sopenharmony_ciMODULE_PARM_DESC(enc_yuv_buffers,
20462306a36Sopenharmony_ci		 "Encoder YUV buffer memory (MB). (enc_yuv_bufs can override)\n"
20562306a36Sopenharmony_ci		 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFFERS));
20662306a36Sopenharmony_ciMODULE_PARM_DESC(enc_yuv_bufsize,
20762306a36Sopenharmony_ci		 "Size of an encoder YUV buffer (kB)\n"
20862306a36Sopenharmony_ci		 "\t\t\tAllowed values are multiples of 33.75 kB rounded up\n"
20962306a36Sopenharmony_ci		 "\t\t\t(multiples of size required for 32 screen lines)\n"
21062306a36Sopenharmony_ci		 "\t\t\tDefault: 102");
21162306a36Sopenharmony_ciMODULE_PARM_DESC(enc_yuv_bufs,
21262306a36Sopenharmony_ci		 "Number of encoder YUV buffers\n"
21362306a36Sopenharmony_ci		 "\t\t\tDefault is computed from other enc_yuv_* parameters");
21462306a36Sopenharmony_ciMODULE_PARM_DESC(enc_vbi_buffers,
21562306a36Sopenharmony_ci		 "Encoder VBI buffer memory (MB). (enc_vbi_bufs can override)\n"
21662306a36Sopenharmony_ci		 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_VBI_BUFFERS));
21762306a36Sopenharmony_ciMODULE_PARM_DESC(enc_vbi_bufs,
21862306a36Sopenharmony_ci		 "Number of encoder VBI buffers\n"
21962306a36Sopenharmony_ci		 "\t\t\tDefault is computed from enc_vbi_buffers");
22062306a36Sopenharmony_ciMODULE_PARM_DESC(enc_pcm_buffers,
22162306a36Sopenharmony_ci		 "Encoder PCM buffer memory (MB). (enc_pcm_bufs can override)\n"
22262306a36Sopenharmony_ci		 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFFERS));
22362306a36Sopenharmony_ciMODULE_PARM_DESC(enc_pcm_bufsize,
22462306a36Sopenharmony_ci		 "Size of an encoder PCM buffer (kB)\n"
22562306a36Sopenharmony_ci		 "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFSIZE));
22662306a36Sopenharmony_ciMODULE_PARM_DESC(enc_pcm_bufs,
22762306a36Sopenharmony_ci		 "Number of encoder PCM buffers\n"
22862306a36Sopenharmony_ci		 "\t\t\tDefault is computed from other enc_pcm_* parameters");
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ciMODULE_PARM_DESC(cx18_first_minor,
23162306a36Sopenharmony_ci		 "Set device node number assigned to first card");
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ciMODULE_AUTHOR("Hans Verkuil");
23462306a36Sopenharmony_ciMODULE_DESCRIPTION("CX23418 driver");
23562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ciMODULE_VERSION(CX18_VERSION);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci#if defined(CONFIG_MODULES) && defined(MODULE)
24062306a36Sopenharmony_cistatic void request_module_async(struct work_struct *work)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	struct cx18 *dev = container_of(work, struct cx18, request_module_wk);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	/* Make sure cx18-alsa module is loaded */
24562306a36Sopenharmony_ci	request_module("cx18-alsa");
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	/* Initialize cx18-alsa for this instance of the cx18 device */
24862306a36Sopenharmony_ci	if (cx18_ext_init)
24962306a36Sopenharmony_ci		cx18_ext_init(dev);
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic void request_modules(struct cx18 *dev)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	INIT_WORK(&dev->request_module_wk, request_module_async);
25562306a36Sopenharmony_ci	schedule_work(&dev->request_module_wk);
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_cistatic void flush_request_modules(struct cx18 *dev)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	flush_work(&dev->request_module_wk);
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci#else
26362306a36Sopenharmony_ci#define request_modules(dev)
26462306a36Sopenharmony_ci#define flush_request_modules(dev)
26562306a36Sopenharmony_ci#endif /* CONFIG_MODULES */
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci/* Generic utility functions */
26862306a36Sopenharmony_ciint cx18_msleep_timeout(unsigned int msecs, int intr)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci	long int timeout = msecs_to_jiffies(msecs);
27162306a36Sopenharmony_ci	int sig;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	do {
27462306a36Sopenharmony_ci		set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
27562306a36Sopenharmony_ci		timeout = schedule_timeout(timeout);
27662306a36Sopenharmony_ci		sig = intr ? signal_pending(current) : 0;
27762306a36Sopenharmony_ci	} while (!sig && timeout);
27862306a36Sopenharmony_ci	return sig;
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci/* Release ioremapped memory */
28262306a36Sopenharmony_cistatic void cx18_iounmap(struct cx18 *cx)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	if (!cx)
28562306a36Sopenharmony_ci		return;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	/* Release io memory */
28862306a36Sopenharmony_ci	if (cx->enc_mem) {
28962306a36Sopenharmony_ci		CX18_DEBUG_INFO("releasing enc_mem\n");
29062306a36Sopenharmony_ci		iounmap(cx->enc_mem);
29162306a36Sopenharmony_ci		cx->enc_mem = NULL;
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_cistatic void cx18_eeprom_dump(struct cx18 *cx, unsigned char *eedata, int len)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	int i;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	CX18_INFO("eeprom dump:\n");
30062306a36Sopenharmony_ci	for (i = 0; i < len; i++) {
30162306a36Sopenharmony_ci		if (0 == (i % 16))
30262306a36Sopenharmony_ci			CX18_INFO("eeprom %02x:", i);
30362306a36Sopenharmony_ci		printk(KERN_CONT " %02x", eedata[i]);
30462306a36Sopenharmony_ci		if (15 == (i % 16))
30562306a36Sopenharmony_ci			printk(KERN_CONT "\n");
30662306a36Sopenharmony_ci	}
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci/* Hauppauge card? get values from tveeprom */
31062306a36Sopenharmony_civoid cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	struct i2c_client *c;
31362306a36Sopenharmony_ci	u8 eedata[256];
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	memset(tv, 0, sizeof(*tv));
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	c = kzalloc(sizeof(*c), GFP_KERNEL);
31862306a36Sopenharmony_ci	if (!c)
31962306a36Sopenharmony_ci		return;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	strscpy(c->name, "cx18 tveeprom tmp", sizeof(c->name));
32262306a36Sopenharmony_ci	c->adapter = &cx->i2c_adap[0];
32362306a36Sopenharmony_ci	c->addr = 0xa0 >> 1;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	if (tveeprom_read(c, eedata, sizeof(eedata)))
32662306a36Sopenharmony_ci		goto ret;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	switch (cx->card->type) {
32962306a36Sopenharmony_ci	case CX18_CARD_HVR_1600_ESMT:
33062306a36Sopenharmony_ci	case CX18_CARD_HVR_1600_SAMSUNG:
33162306a36Sopenharmony_ci	case CX18_CARD_HVR_1600_S5H1411:
33262306a36Sopenharmony_ci		tveeprom_hauppauge_analog(tv, eedata);
33362306a36Sopenharmony_ci		break;
33462306a36Sopenharmony_ci	case CX18_CARD_YUAN_MPC718:
33562306a36Sopenharmony_ci	case CX18_CARD_GOTVIEW_PCI_DVD3:
33662306a36Sopenharmony_ci		tv->model = 0x718;
33762306a36Sopenharmony_ci		cx18_eeprom_dump(cx, eedata, sizeof(eedata));
33862306a36Sopenharmony_ci		CX18_INFO("eeprom PCI ID: %02x%02x:%02x%02x\n",
33962306a36Sopenharmony_ci			  eedata[2], eedata[1], eedata[4], eedata[3]);
34062306a36Sopenharmony_ci		break;
34162306a36Sopenharmony_ci	default:
34262306a36Sopenharmony_ci		tv->model = 0xffffffff;
34362306a36Sopenharmony_ci		cx18_eeprom_dump(cx, eedata, sizeof(eedata));
34462306a36Sopenharmony_ci		break;
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ciret:
34862306a36Sopenharmony_ci	kfree(c);
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic void cx18_process_eeprom(struct cx18 *cx)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	struct tveeprom tv;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	cx18_read_eeprom(cx, &tv);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	/* Many thanks to Steven Toth from Hauppauge for providing the
35862306a36Sopenharmony_ci	   model numbers */
35962306a36Sopenharmony_ci	/* Note: the Samsung memory models cannot be reliably determined
36062306a36Sopenharmony_ci	   from the model number. Use the cardtype module option if you
36162306a36Sopenharmony_ci	   have one of these preproduction models. */
36262306a36Sopenharmony_ci	switch (tv.model) {
36362306a36Sopenharmony_ci	case 74301: /* Retail models */
36462306a36Sopenharmony_ci	case 74321:
36562306a36Sopenharmony_ci	case 74351: /* OEM models */
36662306a36Sopenharmony_ci	case 74361:
36762306a36Sopenharmony_ci		/* Digital side is s5h1411/tda18271 */
36862306a36Sopenharmony_ci		cx->card = cx18_get_card(CX18_CARD_HVR_1600_S5H1411);
36962306a36Sopenharmony_ci		break;
37062306a36Sopenharmony_ci	case 74021: /* Retail models */
37162306a36Sopenharmony_ci	case 74031:
37262306a36Sopenharmony_ci	case 74041:
37362306a36Sopenharmony_ci	case 74141:
37462306a36Sopenharmony_ci	case 74541: /* OEM models */
37562306a36Sopenharmony_ci	case 74551:
37662306a36Sopenharmony_ci	case 74591:
37762306a36Sopenharmony_ci	case 74651:
37862306a36Sopenharmony_ci	case 74691:
37962306a36Sopenharmony_ci	case 74751:
38062306a36Sopenharmony_ci	case 74891:
38162306a36Sopenharmony_ci		/* Digital side is s5h1409/mxl5005s */
38262306a36Sopenharmony_ci		cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
38362306a36Sopenharmony_ci		break;
38462306a36Sopenharmony_ci	case 0x718:
38562306a36Sopenharmony_ci		return;
38662306a36Sopenharmony_ci	case 0xffffffff:
38762306a36Sopenharmony_ci		CX18_INFO("Unknown EEPROM encoding\n");
38862306a36Sopenharmony_ci		return;
38962306a36Sopenharmony_ci	case 0:
39062306a36Sopenharmony_ci		CX18_ERR("Invalid EEPROM\n");
39162306a36Sopenharmony_ci		return;
39262306a36Sopenharmony_ci	default:
39362306a36Sopenharmony_ci		CX18_ERR("Unknown model %d, defaulting to original HVR-1600 (cardtype=1)\n",
39462306a36Sopenharmony_ci			 tv.model);
39562306a36Sopenharmony_ci		cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
39662306a36Sopenharmony_ci		break;
39762306a36Sopenharmony_ci	}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	cx->v4l2_cap = cx->card->v4l2_capabilities;
40062306a36Sopenharmony_ci	cx->card_name = cx->card->name;
40162306a36Sopenharmony_ci	cx->card_i2c = cx->card->i2c;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	CX18_INFO("Autodetected %s\n", cx->card_name);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (tv.tuner_type == TUNER_ABSENT)
40662306a36Sopenharmony_ci		CX18_ERR("tveeprom cannot autodetect tuner!\n");
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	if (cx->options.tuner == -1)
40962306a36Sopenharmony_ci		cx->options.tuner = tv.tuner_type;
41062306a36Sopenharmony_ci	if (cx->options.radio == -1)
41162306a36Sopenharmony_ci		cx->options.radio = (tv.has_radio != 0);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	if (cx->std != 0)
41462306a36Sopenharmony_ci		/* user specified tuner standard */
41562306a36Sopenharmony_ci		return;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	/* autodetect tuner standard */
41862306a36Sopenharmony_ci#define TVEEPROM_TUNER_FORMAT_ALL (V4L2_STD_B  | V4L2_STD_GH | \
41962306a36Sopenharmony_ci				   V4L2_STD_MN | \
42062306a36Sopenharmony_ci				   V4L2_STD_PAL_I | \
42162306a36Sopenharmony_ci				   V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC | \
42262306a36Sopenharmony_ci				   V4L2_STD_DK)
42362306a36Sopenharmony_ci	if ((tv.tuner_formats & TVEEPROM_TUNER_FORMAT_ALL)
42462306a36Sopenharmony_ci					== TVEEPROM_TUNER_FORMAT_ALL) {
42562306a36Sopenharmony_ci		CX18_DEBUG_INFO("Worldwide tuner detected\n");
42662306a36Sopenharmony_ci		cx->std = V4L2_STD_ALL;
42762306a36Sopenharmony_ci	} else if (tv.tuner_formats & V4L2_STD_PAL) {
42862306a36Sopenharmony_ci		CX18_DEBUG_INFO("PAL tuner detected\n");
42962306a36Sopenharmony_ci		cx->std |= V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
43062306a36Sopenharmony_ci	} else if (tv.tuner_formats & V4L2_STD_NTSC) {
43162306a36Sopenharmony_ci		CX18_DEBUG_INFO("NTSC tuner detected\n");
43262306a36Sopenharmony_ci		cx->std |= V4L2_STD_NTSC_M;
43362306a36Sopenharmony_ci	} else if (tv.tuner_formats & V4L2_STD_SECAM) {
43462306a36Sopenharmony_ci		CX18_DEBUG_INFO("SECAM tuner detected\n");
43562306a36Sopenharmony_ci		cx->std |= V4L2_STD_SECAM_L;
43662306a36Sopenharmony_ci	} else {
43762306a36Sopenharmony_ci		CX18_INFO("No tuner detected, default to NTSC-M\n");
43862306a36Sopenharmony_ci		cx->std |= V4L2_STD_NTSC_M;
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic v4l2_std_id cx18_parse_std(struct cx18 *cx)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	switch (pal[0]) {
44562306a36Sopenharmony_ci	case '6':
44662306a36Sopenharmony_ci		return V4L2_STD_PAL_60;
44762306a36Sopenharmony_ci	case 'b':
44862306a36Sopenharmony_ci	case 'B':
44962306a36Sopenharmony_ci	case 'g':
45062306a36Sopenharmony_ci	case 'G':
45162306a36Sopenharmony_ci		return V4L2_STD_PAL_BG;
45262306a36Sopenharmony_ci	case 'h':
45362306a36Sopenharmony_ci	case 'H':
45462306a36Sopenharmony_ci		return V4L2_STD_PAL_H;
45562306a36Sopenharmony_ci	case 'n':
45662306a36Sopenharmony_ci	case 'N':
45762306a36Sopenharmony_ci		if (pal[1] == 'c' || pal[1] == 'C')
45862306a36Sopenharmony_ci			return V4L2_STD_PAL_Nc;
45962306a36Sopenharmony_ci		return V4L2_STD_PAL_N;
46062306a36Sopenharmony_ci	case 'i':
46162306a36Sopenharmony_ci	case 'I':
46262306a36Sopenharmony_ci		return V4L2_STD_PAL_I;
46362306a36Sopenharmony_ci	case 'd':
46462306a36Sopenharmony_ci	case 'D':
46562306a36Sopenharmony_ci	case 'k':
46662306a36Sopenharmony_ci	case 'K':
46762306a36Sopenharmony_ci		return V4L2_STD_PAL_DK;
46862306a36Sopenharmony_ci	case 'M':
46962306a36Sopenharmony_ci	case 'm':
47062306a36Sopenharmony_ci		return V4L2_STD_PAL_M;
47162306a36Sopenharmony_ci	case '-':
47262306a36Sopenharmony_ci		break;
47362306a36Sopenharmony_ci	default:
47462306a36Sopenharmony_ci		CX18_WARN("pal= argument not recognised\n");
47562306a36Sopenharmony_ci		return 0;
47662306a36Sopenharmony_ci	}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	switch (secam[0]) {
47962306a36Sopenharmony_ci	case 'b':
48062306a36Sopenharmony_ci	case 'B':
48162306a36Sopenharmony_ci	case 'g':
48262306a36Sopenharmony_ci	case 'G':
48362306a36Sopenharmony_ci	case 'h':
48462306a36Sopenharmony_ci	case 'H':
48562306a36Sopenharmony_ci		return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
48662306a36Sopenharmony_ci	case 'd':
48762306a36Sopenharmony_ci	case 'D':
48862306a36Sopenharmony_ci	case 'k':
48962306a36Sopenharmony_ci	case 'K':
49062306a36Sopenharmony_ci		return V4L2_STD_SECAM_DK;
49162306a36Sopenharmony_ci	case 'l':
49262306a36Sopenharmony_ci	case 'L':
49362306a36Sopenharmony_ci		if (secam[1] == 'C' || secam[1] == 'c')
49462306a36Sopenharmony_ci			return V4L2_STD_SECAM_LC;
49562306a36Sopenharmony_ci		return V4L2_STD_SECAM_L;
49662306a36Sopenharmony_ci	case '-':
49762306a36Sopenharmony_ci		break;
49862306a36Sopenharmony_ci	default:
49962306a36Sopenharmony_ci		CX18_WARN("secam= argument not recognised\n");
50062306a36Sopenharmony_ci		return 0;
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	switch (ntsc[0]) {
50462306a36Sopenharmony_ci	case 'm':
50562306a36Sopenharmony_ci	case 'M':
50662306a36Sopenharmony_ci		return V4L2_STD_NTSC_M;
50762306a36Sopenharmony_ci	case 'j':
50862306a36Sopenharmony_ci	case 'J':
50962306a36Sopenharmony_ci		return V4L2_STD_NTSC_M_JP;
51062306a36Sopenharmony_ci	case 'k':
51162306a36Sopenharmony_ci	case 'K':
51262306a36Sopenharmony_ci		return V4L2_STD_NTSC_M_KR;
51362306a36Sopenharmony_ci	case '-':
51462306a36Sopenharmony_ci		break;
51562306a36Sopenharmony_ci	default:
51662306a36Sopenharmony_ci		CX18_WARN("ntsc= argument not recognised\n");
51762306a36Sopenharmony_ci		return 0;
51862306a36Sopenharmony_ci	}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	/* no match found */
52162306a36Sopenharmony_ci	return 0;
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cistatic void cx18_process_options(struct cx18 *cx)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	int i, j;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	cx->options.megabytes[CX18_ENC_STREAM_TYPE_TS] = enc_ts_buffers;
52962306a36Sopenharmony_ci	cx->options.megabytes[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers;
53062306a36Sopenharmony_ci	cx->options.megabytes[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_buffers;
53162306a36Sopenharmony_ci	cx->options.megabytes[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers;
53262306a36Sopenharmony_ci	cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers;
53362306a36Sopenharmony_ci	cx->options.megabytes[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers;
53462306a36Sopenharmony_ci	cx->options.megabytes[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control only */
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	cx->stream_buffers[CX18_ENC_STREAM_TYPE_TS] = enc_ts_bufs;
53762306a36Sopenharmony_ci	cx->stream_buffers[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufs;
53862306a36Sopenharmony_ci	cx->stream_buffers[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufs;
53962306a36Sopenharmony_ci	cx->stream_buffers[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufs;
54062306a36Sopenharmony_ci	cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_bufs;
54162306a36Sopenharmony_ci	cx->stream_buffers[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufs;
54262306a36Sopenharmony_ci	cx->stream_buffers[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control, no data */
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	cx->stream_buf_size[CX18_ENC_STREAM_TYPE_TS] = enc_ts_bufsize;
54562306a36Sopenharmony_ci	cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufsize;
54662306a36Sopenharmony_ci	cx->stream_buf_size[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufsize;
54762306a36Sopenharmony_ci	cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufsize;
54862306a36Sopenharmony_ci	cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = VBI_ACTIVE_SAMPLES * 36;
54962306a36Sopenharmony_ci	cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufsize;
55062306a36Sopenharmony_ci	cx->stream_buf_size[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control no data */
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	/* Ensure stream_buffers & stream_buf_size are valid */
55362306a36Sopenharmony_ci	for (i = 0; i < CX18_MAX_STREAMS; i++) {
55462306a36Sopenharmony_ci		if (cx->stream_buffers[i] == 0 ||     /* User said 0 buffers */
55562306a36Sopenharmony_ci		    cx->options.megabytes[i] <= 0 ||  /* User said 0 MB total */
55662306a36Sopenharmony_ci		    cx->stream_buf_size[i] <= 0) {    /* User said buf size 0 */
55762306a36Sopenharmony_ci			cx->options.megabytes[i] = 0;
55862306a36Sopenharmony_ci			cx->stream_buffers[i] = 0;
55962306a36Sopenharmony_ci			cx->stream_buf_size[i] = 0;
56062306a36Sopenharmony_ci			continue;
56162306a36Sopenharmony_ci		}
56262306a36Sopenharmony_ci		/*
56362306a36Sopenharmony_ci		 * YUV is a special case where the stream_buf_size needs to be
56462306a36Sopenharmony_ci		 * an integral multiple of 33.75 kB (storage for 32 screens
56562306a36Sopenharmony_ci		 * lines to maintain alignment in case of lost buffers).
56662306a36Sopenharmony_ci		 *
56762306a36Sopenharmony_ci		 * IDX is a special case where the stream_buf_size should be
56862306a36Sopenharmony_ci		 * an integral multiple of 1.5 kB (storage for 64 index entries
56962306a36Sopenharmony_ci		 * to maintain alignment in case of lost buffers).
57062306a36Sopenharmony_ci		 *
57162306a36Sopenharmony_ci		 */
57262306a36Sopenharmony_ci		if (i == CX18_ENC_STREAM_TYPE_YUV) {
57362306a36Sopenharmony_ci			cx->stream_buf_size[i] *= 1024;
57462306a36Sopenharmony_ci			cx->stream_buf_size[i] -=
57562306a36Sopenharmony_ci			   (cx->stream_buf_size[i] % CX18_UNIT_ENC_YUV_BUFSIZE);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci			if (cx->stream_buf_size[i] < CX18_UNIT_ENC_YUV_BUFSIZE)
57862306a36Sopenharmony_ci				cx->stream_buf_size[i] =
57962306a36Sopenharmony_ci						CX18_UNIT_ENC_YUV_BUFSIZE;
58062306a36Sopenharmony_ci		} else if (i == CX18_ENC_STREAM_TYPE_IDX) {
58162306a36Sopenharmony_ci			cx->stream_buf_size[i] *= 1024;
58262306a36Sopenharmony_ci			cx->stream_buf_size[i] -=
58362306a36Sopenharmony_ci			   (cx->stream_buf_size[i] % CX18_UNIT_ENC_IDX_BUFSIZE);
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci			if (cx->stream_buf_size[i] < CX18_UNIT_ENC_IDX_BUFSIZE)
58662306a36Sopenharmony_ci				cx->stream_buf_size[i] =
58762306a36Sopenharmony_ci						CX18_UNIT_ENC_IDX_BUFSIZE;
58862306a36Sopenharmony_ci		}
58962306a36Sopenharmony_ci		/*
59062306a36Sopenharmony_ci		 * YUV and IDX are special cases where the stream_buf_size is
59162306a36Sopenharmony_ci		 * now in bytes.
59262306a36Sopenharmony_ci		 * VBI is a special case where the stream_buf_size is fixed
59362306a36Sopenharmony_ci		 * and already in bytes
59462306a36Sopenharmony_ci		 */
59562306a36Sopenharmony_ci		if (i == CX18_ENC_STREAM_TYPE_VBI ||
59662306a36Sopenharmony_ci		    i == CX18_ENC_STREAM_TYPE_YUV ||
59762306a36Sopenharmony_ci		    i == CX18_ENC_STREAM_TYPE_IDX) {
59862306a36Sopenharmony_ci			if (cx->stream_buffers[i] < 0) {
59962306a36Sopenharmony_ci				cx->stream_buffers[i] =
60062306a36Sopenharmony_ci					cx->options.megabytes[i] * 1024 * 1024
60162306a36Sopenharmony_ci					/ cx->stream_buf_size[i];
60262306a36Sopenharmony_ci			} else {
60362306a36Sopenharmony_ci				/* N.B. This might round down to 0 */
60462306a36Sopenharmony_ci				cx->options.megabytes[i] =
60562306a36Sopenharmony_ci					cx->stream_buffers[i]
60662306a36Sopenharmony_ci					* cx->stream_buf_size[i]/(1024 * 1024);
60762306a36Sopenharmony_ci			}
60862306a36Sopenharmony_ci		} else {
60962306a36Sopenharmony_ci			/* All other streams have stream_buf_size in kB here */
61062306a36Sopenharmony_ci			if (cx->stream_buffers[i] < 0) {
61162306a36Sopenharmony_ci				cx->stream_buffers[i] =
61262306a36Sopenharmony_ci						cx->options.megabytes[i] * 1024
61362306a36Sopenharmony_ci						/ cx->stream_buf_size[i];
61462306a36Sopenharmony_ci			} else {
61562306a36Sopenharmony_ci				/* N.B. This might round down to 0 */
61662306a36Sopenharmony_ci				cx->options.megabytes[i] =
61762306a36Sopenharmony_ci						cx->stream_buffers[i]
61862306a36Sopenharmony_ci						* cx->stream_buf_size[i] / 1024;
61962306a36Sopenharmony_ci			}
62062306a36Sopenharmony_ci			/* convert from kB to bytes */
62162306a36Sopenharmony_ci			cx->stream_buf_size[i] *= 1024;
62262306a36Sopenharmony_ci		}
62362306a36Sopenharmony_ci		CX18_DEBUG_INFO("Stream type %d options: %d MB, %d buffers, %d bytes\n",
62462306a36Sopenharmony_ci				i, cx->options.megabytes[i],
62562306a36Sopenharmony_ci				cx->stream_buffers[i], cx->stream_buf_size[i]);
62662306a36Sopenharmony_ci	}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	cx->options.cardtype = cardtype[cx->instance];
62962306a36Sopenharmony_ci	cx->options.tuner = tuner[cx->instance];
63062306a36Sopenharmony_ci	cx->options.radio = radio[cx->instance];
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	cx->std = cx18_parse_std(cx);
63362306a36Sopenharmony_ci	if (cx->options.cardtype == -1) {
63462306a36Sopenharmony_ci		CX18_INFO("Ignore card\n");
63562306a36Sopenharmony_ci		return;
63662306a36Sopenharmony_ci	}
63762306a36Sopenharmony_ci	cx->card = cx18_get_card(cx->options.cardtype - 1);
63862306a36Sopenharmony_ci	if (cx->card)
63962306a36Sopenharmony_ci		CX18_INFO("User specified %s card\n", cx->card->name);
64062306a36Sopenharmony_ci	else if (cx->options.cardtype != 0)
64162306a36Sopenharmony_ci		CX18_ERR("Unknown user specified type, trying to autodetect card\n");
64262306a36Sopenharmony_ci	if (!cx->card) {
64362306a36Sopenharmony_ci		if (cx->pci_dev->subsystem_vendor == CX18_PCI_ID_HAUPPAUGE) {
64462306a36Sopenharmony_ci			cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
64562306a36Sopenharmony_ci			CX18_INFO("Autodetected Hauppauge card\n");
64662306a36Sopenharmony_ci		}
64762306a36Sopenharmony_ci	}
64862306a36Sopenharmony_ci	if (!cx->card) {
64962306a36Sopenharmony_ci		for (i = 0; (cx->card = cx18_get_card(i)); i++) {
65062306a36Sopenharmony_ci			if (!cx->card->pci_list)
65162306a36Sopenharmony_ci				continue;
65262306a36Sopenharmony_ci			for (j = 0; cx->card->pci_list[j].device; j++) {
65362306a36Sopenharmony_ci				if (cx->pci_dev->device !=
65462306a36Sopenharmony_ci				    cx->card->pci_list[j].device)
65562306a36Sopenharmony_ci					continue;
65662306a36Sopenharmony_ci				if (cx->pci_dev->subsystem_vendor !=
65762306a36Sopenharmony_ci				    cx->card->pci_list[j].subsystem_vendor)
65862306a36Sopenharmony_ci					continue;
65962306a36Sopenharmony_ci				if (cx->pci_dev->subsystem_device !=
66062306a36Sopenharmony_ci				    cx->card->pci_list[j].subsystem_device)
66162306a36Sopenharmony_ci					continue;
66262306a36Sopenharmony_ci				CX18_INFO("Autodetected %s card\n", cx->card->name);
66362306a36Sopenharmony_ci				goto done;
66462306a36Sopenharmony_ci			}
66562306a36Sopenharmony_ci		}
66662306a36Sopenharmony_ci	}
66762306a36Sopenharmony_cidone:
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	if (!cx->card) {
67062306a36Sopenharmony_ci		cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
67162306a36Sopenharmony_ci		CX18_ERR("Unknown card: vendor/device: [%04x:%04x]\n",
67262306a36Sopenharmony_ci			 cx->pci_dev->vendor, cx->pci_dev->device);
67362306a36Sopenharmony_ci		CX18_ERR("              subsystem vendor/device: [%04x:%04x]\n",
67462306a36Sopenharmony_ci			 cx->pci_dev->subsystem_vendor,
67562306a36Sopenharmony_ci			 cx->pci_dev->subsystem_device);
67662306a36Sopenharmony_ci		CX18_ERR("Defaulting to %s card\n", cx->card->name);
67762306a36Sopenharmony_ci		CX18_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n");
67862306a36Sopenharmony_ci		CX18_ERR("card you have to the linux-media mailinglist (www.linuxtv.org)\n");
67962306a36Sopenharmony_ci		CX18_ERR("Prefix your subject line with [UNKNOWN CX18 CARD].\n");
68062306a36Sopenharmony_ci	}
68162306a36Sopenharmony_ci	cx->v4l2_cap = cx->card->v4l2_capabilities;
68262306a36Sopenharmony_ci	cx->card_name = cx->card->name;
68362306a36Sopenharmony_ci	cx->card_i2c = cx->card->i2c;
68462306a36Sopenharmony_ci}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_cistatic int cx18_create_in_workq(struct cx18 *cx)
68762306a36Sopenharmony_ci{
68862306a36Sopenharmony_ci	snprintf(cx->in_workq_name, sizeof(cx->in_workq_name), "%s-in",
68962306a36Sopenharmony_ci		 cx->v4l2_dev.name);
69062306a36Sopenharmony_ci	cx->in_work_queue = alloc_ordered_workqueue("%s", 0, cx->in_workq_name);
69162306a36Sopenharmony_ci	if (!cx->in_work_queue) {
69262306a36Sopenharmony_ci		CX18_ERR("Unable to create incoming mailbox handler thread\n");
69362306a36Sopenharmony_ci		return -ENOMEM;
69462306a36Sopenharmony_ci	}
69562306a36Sopenharmony_ci	return 0;
69662306a36Sopenharmony_ci}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_cistatic void cx18_init_in_work_orders(struct cx18 *cx)
69962306a36Sopenharmony_ci{
70062306a36Sopenharmony_ci	int i;
70162306a36Sopenharmony_ci	for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++) {
70262306a36Sopenharmony_ci		cx->in_work_order[i].cx = cx;
70362306a36Sopenharmony_ci		cx->in_work_order[i].str = cx->epu_debug_str;
70462306a36Sopenharmony_ci		INIT_WORK(&cx->in_work_order[i].work, cx18_in_work_handler);
70562306a36Sopenharmony_ci	}
70662306a36Sopenharmony_ci}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci/* Precondition: the cx18 structure has been memset to 0. Only
70962306a36Sopenharmony_ci   the dev and instance fields have been filled in.
71062306a36Sopenharmony_ci   No assumptions on the card type may be made here (see cx18_init_struct2
71162306a36Sopenharmony_ci   for that).
71262306a36Sopenharmony_ci */
71362306a36Sopenharmony_cistatic int cx18_init_struct1(struct cx18 *cx)
71462306a36Sopenharmony_ci{
71562306a36Sopenharmony_ci	int ret;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	cx->base_addr = pci_resource_start(cx->pci_dev, 0);
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	mutex_init(&cx->serialize_lock);
72062306a36Sopenharmony_ci	mutex_init(&cx->gpio_lock);
72162306a36Sopenharmony_ci	mutex_init(&cx->epu2apu_mb_lock);
72262306a36Sopenharmony_ci	mutex_init(&cx->epu2cpu_mb_lock);
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	ret = cx18_create_in_workq(cx);
72562306a36Sopenharmony_ci	if (ret)
72662306a36Sopenharmony_ci		return ret;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	cx18_init_in_work_orders(cx);
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	/* start counting open_id at 1 */
73162306a36Sopenharmony_ci	cx->open_id = 1;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	/* Initial settings */
73462306a36Sopenharmony_ci	cx->cxhdl.port = CX2341X_PORT_MEMORY;
73562306a36Sopenharmony_ci	cx->cxhdl.capabilities = CX2341X_CAP_HAS_TS | CX2341X_CAP_HAS_SLICED_VBI;
73662306a36Sopenharmony_ci	cx->cxhdl.ops = &cx18_cxhdl_ops;
73762306a36Sopenharmony_ci	cx->cxhdl.func = cx18_api_func;
73862306a36Sopenharmony_ci	cx->cxhdl.priv = &cx->streams[CX18_ENC_STREAM_TYPE_MPG];
73962306a36Sopenharmony_ci	ret = cx2341x_handler_init(&cx->cxhdl, 50);
74062306a36Sopenharmony_ci	if (ret)
74162306a36Sopenharmony_ci		return ret;
74262306a36Sopenharmony_ci	cx->v4l2_dev.ctrl_handler = &cx->cxhdl.hdl;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	cx->temporal_strength = cx->cxhdl.video_temporal_filter->cur.val;
74562306a36Sopenharmony_ci	cx->spatial_strength = cx->cxhdl.video_spatial_filter->cur.val;
74662306a36Sopenharmony_ci	cx->filter_mode = cx->cxhdl.video_spatial_filter_mode->cur.val |
74762306a36Sopenharmony_ci		(cx->cxhdl.video_temporal_filter_mode->cur.val << 1) |
74862306a36Sopenharmony_ci		(cx->cxhdl.video_median_filter_type->cur.val << 2);
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	init_waitqueue_head(&cx->cap_w);
75162306a36Sopenharmony_ci	init_waitqueue_head(&cx->mb_apu_waitq);
75262306a36Sopenharmony_ci	init_waitqueue_head(&cx->mb_cpu_waitq);
75362306a36Sopenharmony_ci	init_waitqueue_head(&cx->dma_waitq);
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	/* VBI */
75662306a36Sopenharmony_ci	cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
75762306a36Sopenharmony_ci	cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced;
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	/* IVTV style VBI insertion into MPEG streams */
76062306a36Sopenharmony_ci	INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_buf.list);
76162306a36Sopenharmony_ci	INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_mdl.list);
76262306a36Sopenharmony_ci	INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_mdl.buf_list);
76362306a36Sopenharmony_ci	list_add(&cx->vbi.sliced_mpeg_buf.list,
76462306a36Sopenharmony_ci		 &cx->vbi.sliced_mpeg_mdl.buf_list);
76562306a36Sopenharmony_ci	return 0;
76662306a36Sopenharmony_ci}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci/* Second initialization part. Here the card type has been
76962306a36Sopenharmony_ci   autodetected. */
77062306a36Sopenharmony_cistatic void cx18_init_struct2(struct cx18 *cx)
77162306a36Sopenharmony_ci{
77262306a36Sopenharmony_ci	int i;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	for (i = 0; i < CX18_CARD_MAX_VIDEO_INPUTS; i++)
77562306a36Sopenharmony_ci		if (cx->card->video_inputs[i].video_type == 0)
77662306a36Sopenharmony_ci			break;
77762306a36Sopenharmony_ci	cx->nof_inputs = i;
77862306a36Sopenharmony_ci	for (i = 0; i < CX18_CARD_MAX_AUDIO_INPUTS; i++)
77962306a36Sopenharmony_ci		if (cx->card->audio_inputs[i].audio_type == 0)
78062306a36Sopenharmony_ci			break;
78162306a36Sopenharmony_ci	cx->nof_audio_inputs = i;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	/* Find tuner input */
78462306a36Sopenharmony_ci	for (i = 0; i < cx->nof_inputs; i++) {
78562306a36Sopenharmony_ci		if (cx->card->video_inputs[i].video_type ==
78662306a36Sopenharmony_ci				CX18_CARD_INPUT_VID_TUNER)
78762306a36Sopenharmony_ci			break;
78862306a36Sopenharmony_ci	}
78962306a36Sopenharmony_ci	if (i == cx->nof_inputs)
79062306a36Sopenharmony_ci		i = 0;
79162306a36Sopenharmony_ci	cx->active_input = i;
79262306a36Sopenharmony_ci	cx->audio_input = cx->card->video_inputs[i].audio_index;
79362306a36Sopenharmony_ci}
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_cistatic int cx18_setup_pci(struct cx18 *cx, struct pci_dev *pci_dev,
79662306a36Sopenharmony_ci			  const struct pci_device_id *pci_id)
79762306a36Sopenharmony_ci{
79862306a36Sopenharmony_ci	u16 cmd;
79962306a36Sopenharmony_ci	unsigned char pci_latency;
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	CX18_DEBUG_INFO("Enabling pci device\n");
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	if (pci_enable_device(pci_dev)) {
80462306a36Sopenharmony_ci		CX18_ERR("Can't enable device %d!\n", cx->instance);
80562306a36Sopenharmony_ci		return -EIO;
80662306a36Sopenharmony_ci	}
80762306a36Sopenharmony_ci	if (dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32))) {
80862306a36Sopenharmony_ci		CX18_ERR("No suitable DMA available, card %d\n", cx->instance);
80962306a36Sopenharmony_ci		return -EIO;
81062306a36Sopenharmony_ci	}
81162306a36Sopenharmony_ci	if (!request_mem_region(cx->base_addr, CX18_MEM_SIZE, "cx18 encoder")) {
81262306a36Sopenharmony_ci		CX18_ERR("Cannot request encoder memory region, card %d\n",
81362306a36Sopenharmony_ci			 cx->instance);
81462306a36Sopenharmony_ci		return -EIO;
81562306a36Sopenharmony_ci	}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	/* Enable bus mastering and memory mapped IO for the CX23418 */
81862306a36Sopenharmony_ci	pci_read_config_word(pci_dev, PCI_COMMAND, &cmd);
81962306a36Sopenharmony_ci	cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
82062306a36Sopenharmony_ci	pci_write_config_word(pci_dev, PCI_COMMAND, cmd);
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	cx->card_rev = pci_dev->revision;
82362306a36Sopenharmony_ci	pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &pci_latency);
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	if (pci_latency < 64 && cx18_pci_latency) {
82662306a36Sopenharmony_ci		CX18_INFO("Unreasonably low latency timer, setting to 64 (was %d)\n",
82762306a36Sopenharmony_ci			  pci_latency);
82862306a36Sopenharmony_ci		pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, 64);
82962306a36Sopenharmony_ci		pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &pci_latency);
83062306a36Sopenharmony_ci	}
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	CX18_DEBUG_INFO("cx%d (rev %d) at %02x:%02x.%x, irq: %d, latency: %d, memory: 0x%llx\n",
83362306a36Sopenharmony_ci		   cx->pci_dev->device, cx->card_rev, pci_dev->bus->number,
83462306a36Sopenharmony_ci		   PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn),
83562306a36Sopenharmony_ci		   cx->pci_dev->irq, pci_latency, (u64)cx->base_addr);
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	return 0;
83862306a36Sopenharmony_ci}
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_cistatic void cx18_init_subdevs(struct cx18 *cx)
84162306a36Sopenharmony_ci{
84262306a36Sopenharmony_ci	u32 hw = cx->card->hw_all;
84362306a36Sopenharmony_ci	u32 device;
84462306a36Sopenharmony_ci	int i;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	for (i = 0, device = 1; i < 32; i++, device <<= 1) {
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci		if (!(device & hw))
84962306a36Sopenharmony_ci			continue;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci		switch (device) {
85262306a36Sopenharmony_ci		case CX18_HW_DVB:
85362306a36Sopenharmony_ci		case CX18_HW_TVEEPROM:
85462306a36Sopenharmony_ci			/* These subordinate devices do not use probing */
85562306a36Sopenharmony_ci			cx->hw_flags |= device;
85662306a36Sopenharmony_ci			break;
85762306a36Sopenharmony_ci		case CX18_HW_418_AV:
85862306a36Sopenharmony_ci			/* The A/V decoder gets probed earlier to set PLLs */
85962306a36Sopenharmony_ci			/* Just note that the card uses it (i.e. has analog) */
86062306a36Sopenharmony_ci			cx->hw_flags |= device;
86162306a36Sopenharmony_ci			break;
86262306a36Sopenharmony_ci		case CX18_HW_GPIO_RESET_CTRL:
86362306a36Sopenharmony_ci			/*
86462306a36Sopenharmony_ci			 * The Reset Controller gets probed and added to
86562306a36Sopenharmony_ci			 * hw_flags earlier for i2c adapter/bus initialization
86662306a36Sopenharmony_ci			 */
86762306a36Sopenharmony_ci			break;
86862306a36Sopenharmony_ci		case CX18_HW_GPIO_MUX:
86962306a36Sopenharmony_ci			if (cx18_gpio_register(cx, device) == 0)
87062306a36Sopenharmony_ci				cx->hw_flags |= device;
87162306a36Sopenharmony_ci			break;
87262306a36Sopenharmony_ci		default:
87362306a36Sopenharmony_ci			if (cx18_i2c_register(cx, i) == 0)
87462306a36Sopenharmony_ci				cx->hw_flags |= device;
87562306a36Sopenharmony_ci			break;
87662306a36Sopenharmony_ci		}
87762306a36Sopenharmony_ci	}
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	if (cx->hw_flags & CX18_HW_418_AV)
88062306a36Sopenharmony_ci		cx->sd_av = cx18_find_hw(cx, CX18_HW_418_AV);
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	if (cx->card->hw_muxer != 0)
88362306a36Sopenharmony_ci		cx->sd_extmux = cx18_find_hw(cx, cx->card->hw_muxer);
88462306a36Sopenharmony_ci}
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_cistatic int cx18_probe(struct pci_dev *pci_dev,
88762306a36Sopenharmony_ci		      const struct pci_device_id *pci_id)
88862306a36Sopenharmony_ci{
88962306a36Sopenharmony_ci	int retval = 0;
89062306a36Sopenharmony_ci	int i;
89162306a36Sopenharmony_ci	u32 devtype;
89262306a36Sopenharmony_ci	struct cx18 *cx;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	/* FIXME - module parameter arrays constrain max instances */
89562306a36Sopenharmony_ci	i = atomic_inc_return(&cx18_instance) - 1;
89662306a36Sopenharmony_ci	if (i >= CX18_MAX_CARDS) {
89762306a36Sopenharmony_ci		printk(KERN_ERR "cx18: cannot manage card %d, driver has a limit of 0 - %d\n",
89862306a36Sopenharmony_ci		       i, CX18_MAX_CARDS - 1);
89962306a36Sopenharmony_ci		return -ENOMEM;
90062306a36Sopenharmony_ci	}
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	cx = kzalloc(sizeof(*cx), GFP_KERNEL);
90362306a36Sopenharmony_ci	if (!cx)
90462306a36Sopenharmony_ci		return -ENOMEM;
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	cx->pci_dev = pci_dev;
90762306a36Sopenharmony_ci	cx->instance = i;
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	retval = v4l2_device_register(&pci_dev->dev, &cx->v4l2_dev);
91062306a36Sopenharmony_ci	if (retval) {
91162306a36Sopenharmony_ci		printk(KERN_ERR "cx18: v4l2_device_register of card %d failed\n",
91262306a36Sopenharmony_ci		       cx->instance);
91362306a36Sopenharmony_ci		kfree(cx);
91462306a36Sopenharmony_ci		return retval;
91562306a36Sopenharmony_ci	}
91662306a36Sopenharmony_ci	snprintf(cx->v4l2_dev.name, sizeof(cx->v4l2_dev.name), "cx18-%d",
91762306a36Sopenharmony_ci		 cx->instance);
91862306a36Sopenharmony_ci	CX18_INFO("Initializing card %d\n", cx->instance);
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	cx18_process_options(cx);
92162306a36Sopenharmony_ci	if (cx->options.cardtype == -1) {
92262306a36Sopenharmony_ci		retval = -ENODEV;
92362306a36Sopenharmony_ci		goto err;
92462306a36Sopenharmony_ci	}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	retval = cx18_init_struct1(cx);
92762306a36Sopenharmony_ci	if (retval)
92862306a36Sopenharmony_ci		goto err;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	CX18_DEBUG_INFO("base addr: 0x%llx\n", (u64)cx->base_addr);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	/* PCI Device Setup */
93362306a36Sopenharmony_ci	retval = cx18_setup_pci(cx, pci_dev, pci_id);
93462306a36Sopenharmony_ci	if (retval != 0)
93562306a36Sopenharmony_ci		goto free_workqueues;
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	/* map io memory */
93862306a36Sopenharmony_ci	CX18_DEBUG_INFO("attempting ioremap at 0x%llx len 0x%08x\n",
93962306a36Sopenharmony_ci		   (u64)cx->base_addr + CX18_MEM_OFFSET, CX18_MEM_SIZE);
94062306a36Sopenharmony_ci	cx->enc_mem = ioremap(cx->base_addr + CX18_MEM_OFFSET,
94162306a36Sopenharmony_ci				       CX18_MEM_SIZE);
94262306a36Sopenharmony_ci	if (!cx->enc_mem) {
94362306a36Sopenharmony_ci		CX18_ERR("ioremap failed. Can't get a window into CX23418 memory and register space\n");
94462306a36Sopenharmony_ci		CX18_ERR("Each capture card with a CX23418 needs 64 MB of vmalloc address space for the window\n");
94562306a36Sopenharmony_ci		CX18_ERR("Check the output of 'grep Vmalloc /proc/meminfo'\n");
94662306a36Sopenharmony_ci		CX18_ERR("Use the vmalloc= kernel command line option to set VmallocTotal to a larger value\n");
94762306a36Sopenharmony_ci		retval = -ENOMEM;
94862306a36Sopenharmony_ci		goto free_mem;
94962306a36Sopenharmony_ci	}
95062306a36Sopenharmony_ci	cx->reg_mem = cx->enc_mem + CX18_REG_OFFSET;
95162306a36Sopenharmony_ci	devtype = cx18_read_reg(cx, 0xC72028);
95262306a36Sopenharmony_ci	switch (devtype & 0xff000000) {
95362306a36Sopenharmony_ci	case 0xff000000:
95462306a36Sopenharmony_ci		CX18_INFO("cx23418 revision %08x (A)\n", devtype);
95562306a36Sopenharmony_ci		break;
95662306a36Sopenharmony_ci	case 0x01000000:
95762306a36Sopenharmony_ci		CX18_INFO("cx23418 revision %08x (B)\n", devtype);
95862306a36Sopenharmony_ci		break;
95962306a36Sopenharmony_ci	default:
96062306a36Sopenharmony_ci		CX18_INFO("cx23418 revision %08x (Unknown)\n", devtype);
96162306a36Sopenharmony_ci		break;
96262306a36Sopenharmony_ci	}
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	cx18_init_power(cx, 1);
96562306a36Sopenharmony_ci	cx18_init_memory(cx);
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci	cx->scb = (struct cx18_scb __iomem *)(cx->enc_mem + SCB_OFFSET);
96862306a36Sopenharmony_ci	cx18_init_scb(cx);
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	cx18_gpio_init(cx);
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	/* Initialize integrated A/V decoder early to set PLLs, just in case */
97362306a36Sopenharmony_ci	retval = cx18_av_probe(cx);
97462306a36Sopenharmony_ci	if (retval) {
97562306a36Sopenharmony_ci		CX18_ERR("Could not register A/V decoder subdevice\n");
97662306a36Sopenharmony_ci		goto free_map;
97762306a36Sopenharmony_ci	}
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	/* Initialize GPIO Reset Controller to do chip resets during i2c init */
98062306a36Sopenharmony_ci	if (cx->card->hw_all & CX18_HW_GPIO_RESET_CTRL) {
98162306a36Sopenharmony_ci		if (cx18_gpio_register(cx, CX18_HW_GPIO_RESET_CTRL) != 0)
98262306a36Sopenharmony_ci			CX18_WARN("Could not register GPIO reset controllersubdevice; proceeding anyway.\n");
98362306a36Sopenharmony_ci		else
98462306a36Sopenharmony_ci			cx->hw_flags |= CX18_HW_GPIO_RESET_CTRL;
98562306a36Sopenharmony_ci	}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	/* active i2c  */
98862306a36Sopenharmony_ci	CX18_DEBUG_INFO("activating i2c...\n");
98962306a36Sopenharmony_ci	retval = init_cx18_i2c(cx);
99062306a36Sopenharmony_ci	if (retval) {
99162306a36Sopenharmony_ci		CX18_ERR("Could not initialize i2c\n");
99262306a36Sopenharmony_ci		goto free_map;
99362306a36Sopenharmony_ci	}
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	if (cx->card->hw_all & CX18_HW_TVEEPROM) {
99662306a36Sopenharmony_ci		/* Based on the model number the cardtype may be changed.
99762306a36Sopenharmony_ci		   The PCI IDs are not always reliable. */
99862306a36Sopenharmony_ci		const struct cx18_card *orig_card = cx->card;
99962306a36Sopenharmony_ci		cx18_process_eeprom(cx);
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci		if (cx->card != orig_card) {
100262306a36Sopenharmony_ci			/* Changed the cardtype; re-reset the I2C chips */
100362306a36Sopenharmony_ci			cx18_gpio_init(cx);
100462306a36Sopenharmony_ci			cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL,
100562306a36Sopenharmony_ci					core, reset, (u32) CX18_GPIO_RESET_I2C);
100662306a36Sopenharmony_ci		}
100762306a36Sopenharmony_ci	}
100862306a36Sopenharmony_ci	if (cx->card->comment)
100962306a36Sopenharmony_ci		CX18_INFO("%s", cx->card->comment);
101062306a36Sopenharmony_ci	if (cx->card->v4l2_capabilities == 0) {
101162306a36Sopenharmony_ci		retval = -ENODEV;
101262306a36Sopenharmony_ci		goto free_i2c;
101362306a36Sopenharmony_ci	}
101462306a36Sopenharmony_ci	cx18_init_memory(cx);
101562306a36Sopenharmony_ci	cx18_init_scb(cx);
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	/* Register IRQ */
101862306a36Sopenharmony_ci	retval = request_irq(cx->pci_dev->irq, cx18_irq_handler,
101962306a36Sopenharmony_ci			     IRQF_SHARED, cx->v4l2_dev.name, (void *)cx);
102062306a36Sopenharmony_ci	if (retval) {
102162306a36Sopenharmony_ci		CX18_ERR("Failed to register irq %d\n", retval);
102262306a36Sopenharmony_ci		goto free_i2c;
102362306a36Sopenharmony_ci	}
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	if (cx->std == 0)
102662306a36Sopenharmony_ci		cx->std = V4L2_STD_NTSC_M;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	if (cx->options.tuner == -1) {
102962306a36Sopenharmony_ci		for (i = 0; i < CX18_CARD_MAX_TUNERS; i++) {
103062306a36Sopenharmony_ci			if ((cx->std & cx->card->tuners[i].std) == 0)
103162306a36Sopenharmony_ci				continue;
103262306a36Sopenharmony_ci			cx->options.tuner = cx->card->tuners[i].tuner;
103362306a36Sopenharmony_ci			break;
103462306a36Sopenharmony_ci		}
103562306a36Sopenharmony_ci	}
103662306a36Sopenharmony_ci	/* if no tuner was found, then pick the first tuner in the card list */
103762306a36Sopenharmony_ci	if (cx->options.tuner == -1 && cx->card->tuners[0].std) {
103862306a36Sopenharmony_ci		cx->std = cx->card->tuners[0].std;
103962306a36Sopenharmony_ci		if (cx->std & V4L2_STD_PAL)
104062306a36Sopenharmony_ci			cx->std = V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
104162306a36Sopenharmony_ci		else if (cx->std & V4L2_STD_NTSC)
104262306a36Sopenharmony_ci			cx->std = V4L2_STD_NTSC_M;
104362306a36Sopenharmony_ci		else if (cx->std & V4L2_STD_SECAM)
104462306a36Sopenharmony_ci			cx->std = V4L2_STD_SECAM_L;
104562306a36Sopenharmony_ci		cx->options.tuner = cx->card->tuners[0].tuner;
104662306a36Sopenharmony_ci	}
104762306a36Sopenharmony_ci	if (cx->options.radio == -1)
104862306a36Sopenharmony_ci		cx->options.radio = (cx->card->radio_input.audio_type != 0);
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	/* The card is now fully identified, continue with card-specific
105162306a36Sopenharmony_ci	   initialization. */
105262306a36Sopenharmony_ci	cx18_init_struct2(cx);
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	cx18_init_subdevs(cx);
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	if (cx->std & V4L2_STD_525_60)
105762306a36Sopenharmony_ci		cx->is_60hz = 1;
105862306a36Sopenharmony_ci	else
105962306a36Sopenharmony_ci		cx->is_50hz = 1;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	cx2341x_handler_set_50hz(&cx->cxhdl, !cx->is_60hz);
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	if (cx->options.radio > 0)
106462306a36Sopenharmony_ci		cx->v4l2_cap |= V4L2_CAP_RADIO;
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	if (cx->options.tuner > -1) {
106762306a36Sopenharmony_ci		struct tuner_setup setup;
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci		setup.addr = ADDR_UNSET;
107062306a36Sopenharmony_ci		setup.type = cx->options.tuner;
107162306a36Sopenharmony_ci		setup.mode_mask = T_ANALOG_TV;  /* matches TV tuners */
107262306a36Sopenharmony_ci		setup.config = NULL;
107362306a36Sopenharmony_ci		if (cx->options.radio > 0)
107462306a36Sopenharmony_ci			setup.mode_mask |= T_RADIO;
107562306a36Sopenharmony_ci		setup.tuner_callback = (setup.type == TUNER_XC2028) ?
107662306a36Sopenharmony_ci			cx18_reset_tuner_gpio : NULL;
107762306a36Sopenharmony_ci		cx18_call_all(cx, tuner, s_type_addr, &setup);
107862306a36Sopenharmony_ci		if (setup.type == TUNER_XC2028) {
107962306a36Sopenharmony_ci			static struct xc2028_ctrl ctrl = {
108062306a36Sopenharmony_ci				.fname = XC2028_DEFAULT_FIRMWARE,
108162306a36Sopenharmony_ci				.max_len = 64,
108262306a36Sopenharmony_ci			};
108362306a36Sopenharmony_ci			struct v4l2_priv_tun_config cfg = {
108462306a36Sopenharmony_ci				.tuner = cx->options.tuner,
108562306a36Sopenharmony_ci				.priv = &ctrl,
108662306a36Sopenharmony_ci			};
108762306a36Sopenharmony_ci			cx18_call_all(cx, tuner, s_config, &cfg);
108862306a36Sopenharmony_ci		}
108962306a36Sopenharmony_ci	}
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	/* The tuner is fixed to the standard. The other inputs (e.g. S-Video)
109262306a36Sopenharmony_ci	   are not. */
109362306a36Sopenharmony_ci	cx->tuner_std = cx->std;
109462306a36Sopenharmony_ci	if (cx->std == V4L2_STD_ALL)
109562306a36Sopenharmony_ci		cx->std = V4L2_STD_NTSC_M;
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	retval = cx18_streams_setup(cx);
109862306a36Sopenharmony_ci	if (retval) {
109962306a36Sopenharmony_ci		CX18_ERR("Error %d setting up streams\n", retval);
110062306a36Sopenharmony_ci		goto free_irq;
110162306a36Sopenharmony_ci	}
110262306a36Sopenharmony_ci	retval = cx18_streams_register(cx);
110362306a36Sopenharmony_ci	if (retval) {
110462306a36Sopenharmony_ci		CX18_ERR("Error %d registering devices\n", retval);
110562306a36Sopenharmony_ci		goto free_streams;
110662306a36Sopenharmony_ci	}
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	CX18_INFO("Initialized card: %s\n", cx->card_name);
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	/* Load cx18 submodules (cx18-alsa) */
111162306a36Sopenharmony_ci	request_modules(cx);
111262306a36Sopenharmony_ci	return 0;
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_cifree_streams:
111562306a36Sopenharmony_ci	cx18_streams_cleanup(cx, 1);
111662306a36Sopenharmony_cifree_irq:
111762306a36Sopenharmony_ci	free_irq(cx->pci_dev->irq, (void *)cx);
111862306a36Sopenharmony_cifree_i2c:
111962306a36Sopenharmony_ci	exit_cx18_i2c(cx);
112062306a36Sopenharmony_cifree_map:
112162306a36Sopenharmony_ci	cx18_iounmap(cx);
112262306a36Sopenharmony_cifree_mem:
112362306a36Sopenharmony_ci	release_mem_region(cx->base_addr, CX18_MEM_SIZE);
112462306a36Sopenharmony_cifree_workqueues:
112562306a36Sopenharmony_ci	destroy_workqueue(cx->in_work_queue);
112662306a36Sopenharmony_cierr:
112762306a36Sopenharmony_ci	CX18_ERR("Error %d on initialization\n", retval);
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	v4l2_device_unregister(&cx->v4l2_dev);
113062306a36Sopenharmony_ci	kfree(cx);
113162306a36Sopenharmony_ci	return retval;
113262306a36Sopenharmony_ci}
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ciint cx18_init_on_first_open(struct cx18 *cx)
113562306a36Sopenharmony_ci{
113662306a36Sopenharmony_ci	int video_input;
113762306a36Sopenharmony_ci	int fw_retry_count = 3;
113862306a36Sopenharmony_ci	struct v4l2_frequency vf;
113962306a36Sopenharmony_ci	struct cx18_open_id fh;
114062306a36Sopenharmony_ci	v4l2_std_id std;
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_ci	fh.cx = cx;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	if (test_bit(CX18_F_I_FAILED, &cx->i_flags))
114562306a36Sopenharmony_ci		return -ENXIO;
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_ci	if (test_and_set_bit(CX18_F_I_INITED, &cx->i_flags))
114862306a36Sopenharmony_ci		return 0;
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci	while (--fw_retry_count > 0) {
115162306a36Sopenharmony_ci		/* load firmware */
115262306a36Sopenharmony_ci		if (cx18_firmware_init(cx) == 0)
115362306a36Sopenharmony_ci			break;
115462306a36Sopenharmony_ci		if (fw_retry_count > 1)
115562306a36Sopenharmony_ci			CX18_WARN("Retry loading firmware\n");
115662306a36Sopenharmony_ci	}
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	if (fw_retry_count == 0) {
115962306a36Sopenharmony_ci		set_bit(CX18_F_I_FAILED, &cx->i_flags);
116062306a36Sopenharmony_ci		return -ENXIO;
116162306a36Sopenharmony_ci	}
116262306a36Sopenharmony_ci	set_bit(CX18_F_I_LOADED_FW, &cx->i_flags);
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	/*
116562306a36Sopenharmony_ci	 * Init the firmware twice to work around a silicon bug
116662306a36Sopenharmony_ci	 * with the digital TS.
116762306a36Sopenharmony_ci	 *
116862306a36Sopenharmony_ci	 * The second firmware load requires us to normalize the APU state,
116962306a36Sopenharmony_ci	 * or the audio for the first analog capture will be badly incorrect.
117062306a36Sopenharmony_ci	 *
117162306a36Sopenharmony_ci	 * I can't seem to call APU_RESETAI and have it succeed without the
117262306a36Sopenharmony_ci	 * APU capturing audio, so we start and stop it here to do the reset
117362306a36Sopenharmony_ci	 */
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	/* MPEG Encoding, 224 kbps, MPEG Layer II, 48 ksps */
117662306a36Sopenharmony_ci	cx18_vapi(cx, CX18_APU_START, 2, CX18_APU_ENCODING_METHOD_MPEG|0xb9, 0);
117762306a36Sopenharmony_ci	cx18_vapi(cx, CX18_APU_RESETAI, 0);
117862306a36Sopenharmony_ci	cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG);
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	fw_retry_count = 3;
118162306a36Sopenharmony_ci	while (--fw_retry_count > 0) {
118262306a36Sopenharmony_ci		/* load firmware */
118362306a36Sopenharmony_ci		if (cx18_firmware_init(cx) == 0)
118462306a36Sopenharmony_ci			break;
118562306a36Sopenharmony_ci		if (fw_retry_count > 1)
118662306a36Sopenharmony_ci			CX18_WARN("Retry loading firmware\n");
118762306a36Sopenharmony_ci	}
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	if (fw_retry_count == 0) {
119062306a36Sopenharmony_ci		set_bit(CX18_F_I_FAILED, &cx->i_flags);
119162306a36Sopenharmony_ci		return -ENXIO;
119262306a36Sopenharmony_ci	}
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	/*
119562306a36Sopenharmony_ci	 * The second firmware load requires us to normalize the APU state,
119662306a36Sopenharmony_ci	 * or the audio for the first analog capture will be badly incorrect.
119762306a36Sopenharmony_ci	 *
119862306a36Sopenharmony_ci	 * I can't seem to call APU_RESETAI and have it succeed without the
119962306a36Sopenharmony_ci	 * APU capturing audio, so we start and stop it here to do the reset
120062306a36Sopenharmony_ci	 */
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	/* MPEG Encoding, 224 kbps, MPEG Layer II, 48 ksps */
120362306a36Sopenharmony_ci	cx18_vapi(cx, CX18_APU_START, 2, CX18_APU_ENCODING_METHOD_MPEG|0xb9, 0);
120462306a36Sopenharmony_ci	cx18_vapi(cx, CX18_APU_RESETAI, 0);
120562306a36Sopenharmony_ci	cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG);
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	/* Init the A/V decoder, if it hasn't been already */
120862306a36Sopenharmony_ci	v4l2_subdev_call(cx->sd_av, core, load_fw);
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	vf.tuner = 0;
121162306a36Sopenharmony_ci	vf.type = V4L2_TUNER_ANALOG_TV;
121262306a36Sopenharmony_ci	vf.frequency = 6400; /* the tuner 'baseline' frequency */
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	/* Set initial frequency. For PAL/SECAM broadcasts no
121562306a36Sopenharmony_ci	   'default' channel exists AFAIK. */
121662306a36Sopenharmony_ci	if (cx->std == V4L2_STD_NTSC_M_JP)
121762306a36Sopenharmony_ci		vf.frequency = 1460;	/* ch. 1 91250*16/1000 */
121862306a36Sopenharmony_ci	else if (cx->std & V4L2_STD_NTSC_M)
121962306a36Sopenharmony_ci		vf.frequency = 1076;	/* ch. 4 67250*16/1000 */
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	video_input = cx->active_input;
122262306a36Sopenharmony_ci	cx->active_input++;	/* Force update of input */
122362306a36Sopenharmony_ci	cx18_s_input(NULL, &fh, video_input);
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	/* Let the VIDIOC_S_STD ioctl do all the work, keeps the code
122662306a36Sopenharmony_ci	   in one place. */
122762306a36Sopenharmony_ci	cx->std++;		/* Force full standard initialization */
122862306a36Sopenharmony_ci	std = (cx->tuner_std == V4L2_STD_ALL) ? V4L2_STD_NTSC_M : cx->tuner_std;
122962306a36Sopenharmony_ci	cx18_s_std(NULL, &fh, std);
123062306a36Sopenharmony_ci	cx18_s_frequency(NULL, &fh, &vf);
123162306a36Sopenharmony_ci	return 0;
123262306a36Sopenharmony_ci}
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_cistatic void cx18_cancel_in_work_orders(struct cx18 *cx)
123562306a36Sopenharmony_ci{
123662306a36Sopenharmony_ci	int i;
123762306a36Sopenharmony_ci	for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++)
123862306a36Sopenharmony_ci		cancel_work_sync(&cx->in_work_order[i].work);
123962306a36Sopenharmony_ci}
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_cistatic void cx18_cancel_out_work_orders(struct cx18 *cx)
124262306a36Sopenharmony_ci{
124362306a36Sopenharmony_ci	int i;
124462306a36Sopenharmony_ci	for (i = 0; i < CX18_MAX_STREAMS; i++)
124562306a36Sopenharmony_ci		if (cx->streams[i].video_dev.v4l2_dev)
124662306a36Sopenharmony_ci			cancel_work_sync(&cx->streams[i].out_work_order);
124762306a36Sopenharmony_ci}
124862306a36Sopenharmony_ci
124962306a36Sopenharmony_cistatic void cx18_remove(struct pci_dev *pci_dev)
125062306a36Sopenharmony_ci{
125162306a36Sopenharmony_ci	struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
125262306a36Sopenharmony_ci	struct cx18 *cx = to_cx18(v4l2_dev);
125362306a36Sopenharmony_ci	int i;
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	CX18_DEBUG_INFO("Removing Card\n");
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	flush_request_modules(cx);
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	/* Stop all captures */
126062306a36Sopenharmony_ci	CX18_DEBUG_INFO("Stopping all streams\n");
126162306a36Sopenharmony_ci	if (atomic_read(&cx->tot_capturing) > 0)
126262306a36Sopenharmony_ci		cx18_stop_all_captures(cx);
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci	/* Stop interrupts that cause incoming work to be queued */
126562306a36Sopenharmony_ci	cx18_sw1_irq_disable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci	/* Incoming work can cause outgoing work, so clean up incoming first */
126862306a36Sopenharmony_ci	cx18_cancel_in_work_orders(cx);
126962306a36Sopenharmony_ci	cx18_cancel_out_work_orders(cx);
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	/* Stop ack interrupts that may have been needed for work to finish */
127262306a36Sopenharmony_ci	cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	cx18_halt_firmware(cx);
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	destroy_workqueue(cx->in_work_queue);
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	cx18_streams_cleanup(cx, 1);
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	exit_cx18_i2c(cx);
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	free_irq(cx->pci_dev->irq, (void *)cx);
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	cx18_iounmap(cx);
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	release_mem_region(cx->base_addr, CX18_MEM_SIZE);
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	pci_disable_device(cx->pci_dev);
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	if (cx->vbi.sliced_mpeg_data[0])
129162306a36Sopenharmony_ci		for (i = 0; i < CX18_VBI_FRAMES; i++)
129262306a36Sopenharmony_ci			kfree(cx->vbi.sliced_mpeg_data[i]);
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	v4l2_ctrl_handler_free(&cx->av_state.hdl);
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	CX18_INFO("Removed %s\n", cx->card_name);
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	v4l2_device_unregister(v4l2_dev);
129962306a36Sopenharmony_ci	kfree(cx);
130062306a36Sopenharmony_ci}
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci/* define a pci_driver for card detection */
130462306a36Sopenharmony_cistatic struct pci_driver cx18_pci_driver = {
130562306a36Sopenharmony_ci      .name =     "cx18",
130662306a36Sopenharmony_ci      .id_table = cx18_pci_tbl,
130762306a36Sopenharmony_ci      .probe =    cx18_probe,
130862306a36Sopenharmony_ci      .remove =   cx18_remove,
130962306a36Sopenharmony_ci};
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_cistatic int __init module_start(void)
131262306a36Sopenharmony_ci{
131362306a36Sopenharmony_ci	printk(KERN_INFO "cx18:  Start initialization, version %s\n",
131462306a36Sopenharmony_ci	       CX18_VERSION);
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	/* Validate parameters */
131762306a36Sopenharmony_ci	if (cx18_first_minor < 0 || cx18_first_minor >= CX18_MAX_CARDS) {
131862306a36Sopenharmony_ci		printk(KERN_ERR "cx18:  Exiting, cx18_first_minor must be between 0 and %d\n",
131962306a36Sopenharmony_ci		     CX18_MAX_CARDS - 1);
132062306a36Sopenharmony_ci		return -1;
132162306a36Sopenharmony_ci	}
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	if (cx18_debug < 0 || cx18_debug > 511) {
132462306a36Sopenharmony_ci		cx18_debug = 0;
132562306a36Sopenharmony_ci		printk(KERN_INFO "cx18:   Debug value must be >= 0 and <= 511!\n");
132662306a36Sopenharmony_ci	}
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	if (pci_register_driver(&cx18_pci_driver)) {
132962306a36Sopenharmony_ci		printk(KERN_ERR "cx18:   Error detecting PCI card\n");
133062306a36Sopenharmony_ci		return -ENODEV;
133162306a36Sopenharmony_ci	}
133262306a36Sopenharmony_ci	printk(KERN_INFO "cx18:  End initialization\n");
133362306a36Sopenharmony_ci	return 0;
133462306a36Sopenharmony_ci}
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_cistatic void __exit module_cleanup(void)
133762306a36Sopenharmony_ci{
133862306a36Sopenharmony_ci	pci_unregister_driver(&cx18_pci_driver);
133962306a36Sopenharmony_ci}
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_cimodule_init(module_start);
134262306a36Sopenharmony_cimodule_exit(module_cleanup);
134362306a36Sopenharmony_ciMODULE_FIRMWARE(XC2028_DEFAULT_FIRMWARE);
1344