162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * device driver for Conexant 2388x based TV cards
562306a36Sopenharmony_ci * video4linux video interface
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * (c) 2003-04 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * (c) 2005-2006 Mauro Carvalho Chehab <mchehab@kernel.org>
1062306a36Sopenharmony_ci *	- Multituner support
1162306a36Sopenharmony_ci *	- video_ioctl2 conversion
1262306a36Sopenharmony_ci *	- PAL/M fixes
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "cx88.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <linux/init.h>
1862306a36Sopenharmony_ci#include <linux/list.h>
1962306a36Sopenharmony_ci#include <linux/module.h>
2062306a36Sopenharmony_ci#include <linux/kmod.h>
2162306a36Sopenharmony_ci#include <linux/kernel.h>
2262306a36Sopenharmony_ci#include <linux/slab.h>
2362306a36Sopenharmony_ci#include <linux/interrupt.h>
2462306a36Sopenharmony_ci#include <linux/dma-mapping.h>
2562306a36Sopenharmony_ci#include <linux/delay.h>
2662306a36Sopenharmony_ci#include <linux/kthread.h>
2762306a36Sopenharmony_ci#include <asm/div64.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include <media/v4l2-common.h>
3062306a36Sopenharmony_ci#include <media/v4l2-ioctl.h>
3162306a36Sopenharmony_ci#include <media/v4l2-event.h>
3262306a36Sopenharmony_ci#include <media/i2c/wm8775.h>
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ciMODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards");
3562306a36Sopenharmony_ciMODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
3662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
3762306a36Sopenharmony_ciMODULE_VERSION(CX88_VERSION);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/* ------------------------------------------------------------------ */
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic unsigned int video_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
4262306a36Sopenharmony_cistatic unsigned int vbi_nr[]   = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
4362306a36Sopenharmony_cistatic unsigned int radio_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cimodule_param_array(video_nr, int, NULL, 0444);
4662306a36Sopenharmony_cimodule_param_array(vbi_nr,   int, NULL, 0444);
4762306a36Sopenharmony_cimodule_param_array(radio_nr, int, NULL, 0444);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ciMODULE_PARM_DESC(video_nr, "video device numbers");
5062306a36Sopenharmony_ciMODULE_PARM_DESC(vbi_nr, "vbi device numbers");
5162306a36Sopenharmony_ciMODULE_PARM_DESC(radio_nr, "radio device numbers");
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic unsigned int video_debug;
5462306a36Sopenharmony_cimodule_param(video_debug, int, 0644);
5562306a36Sopenharmony_ciMODULE_PARM_DESC(video_debug, "enable debug messages [video]");
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic unsigned int irq_debug;
5862306a36Sopenharmony_cimodule_param(irq_debug, int, 0644);
5962306a36Sopenharmony_ciMODULE_PARM_DESC(irq_debug, "enable debug messages [IRQ handler]");
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#define dprintk(level, fmt, arg...) do {			\
6262306a36Sopenharmony_ci	if (video_debug >= level)				\
6362306a36Sopenharmony_ci		printk(KERN_DEBUG pr_fmt("%s: video:" fmt),	\
6462306a36Sopenharmony_ci			__func__, ##arg);			\
6562306a36Sopenharmony_ci} while (0)
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci/* ------------------------------------------------------------------- */
6862306a36Sopenharmony_ci/* static data                                                         */
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic const struct cx8800_fmt formats[] = {
7162306a36Sopenharmony_ci	{
7262306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_GREY,
7362306a36Sopenharmony_ci		.cxformat = ColorFormatY8,
7462306a36Sopenharmony_ci		.depth    = 8,
7562306a36Sopenharmony_ci		.flags    = FORMAT_FLAGS_PACKED,
7662306a36Sopenharmony_ci	}, {
7762306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_RGB555,
7862306a36Sopenharmony_ci		.cxformat = ColorFormatRGB15,
7962306a36Sopenharmony_ci		.depth    = 16,
8062306a36Sopenharmony_ci		.flags    = FORMAT_FLAGS_PACKED,
8162306a36Sopenharmony_ci	}, {
8262306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_RGB555X,
8362306a36Sopenharmony_ci		.cxformat = ColorFormatRGB15 | ColorFormatBSWAP,
8462306a36Sopenharmony_ci		.depth    = 16,
8562306a36Sopenharmony_ci		.flags    = FORMAT_FLAGS_PACKED,
8662306a36Sopenharmony_ci	}, {
8762306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_RGB565,
8862306a36Sopenharmony_ci		.cxformat = ColorFormatRGB16,
8962306a36Sopenharmony_ci		.depth    = 16,
9062306a36Sopenharmony_ci		.flags    = FORMAT_FLAGS_PACKED,
9162306a36Sopenharmony_ci	}, {
9262306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_RGB565X,
9362306a36Sopenharmony_ci		.cxformat = ColorFormatRGB16 | ColorFormatBSWAP,
9462306a36Sopenharmony_ci		.depth    = 16,
9562306a36Sopenharmony_ci		.flags    = FORMAT_FLAGS_PACKED,
9662306a36Sopenharmony_ci	}, {
9762306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_BGR24,
9862306a36Sopenharmony_ci		.cxformat = ColorFormatRGB24,
9962306a36Sopenharmony_ci		.depth    = 24,
10062306a36Sopenharmony_ci		.flags    = FORMAT_FLAGS_PACKED,
10162306a36Sopenharmony_ci	}, {
10262306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_BGR32,
10362306a36Sopenharmony_ci		.cxformat = ColorFormatRGB32,
10462306a36Sopenharmony_ci		.depth    = 32,
10562306a36Sopenharmony_ci		.flags    = FORMAT_FLAGS_PACKED,
10662306a36Sopenharmony_ci	}, {
10762306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_RGB32,
10862306a36Sopenharmony_ci		.cxformat = ColorFormatRGB32 | ColorFormatBSWAP |
10962306a36Sopenharmony_ci			    ColorFormatWSWAP,
11062306a36Sopenharmony_ci		.depth    = 32,
11162306a36Sopenharmony_ci		.flags    = FORMAT_FLAGS_PACKED,
11262306a36Sopenharmony_ci	}, {
11362306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_YUYV,
11462306a36Sopenharmony_ci		.cxformat = ColorFormatYUY2,
11562306a36Sopenharmony_ci		.depth    = 16,
11662306a36Sopenharmony_ci		.flags    = FORMAT_FLAGS_PACKED,
11762306a36Sopenharmony_ci	}, {
11862306a36Sopenharmony_ci		.fourcc   = V4L2_PIX_FMT_UYVY,
11962306a36Sopenharmony_ci		.cxformat = ColorFormatYUY2 | ColorFormatBSWAP,
12062306a36Sopenharmony_ci		.depth    = 16,
12162306a36Sopenharmony_ci		.flags    = FORMAT_FLAGS_PACKED,
12262306a36Sopenharmony_ci	},
12362306a36Sopenharmony_ci};
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic const struct cx8800_fmt *format_by_fourcc(unsigned int fourcc)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	unsigned int i;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(formats); i++)
13062306a36Sopenharmony_ci		if (formats[i].fourcc == fourcc)
13162306a36Sopenharmony_ci			return formats + i;
13262306a36Sopenharmony_ci	return NULL;
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci/* ------------------------------------------------------------------- */
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistruct cx88_ctrl {
13862306a36Sopenharmony_ci	/* control information */
13962306a36Sopenharmony_ci	u32 id;
14062306a36Sopenharmony_ci	s32 minimum;
14162306a36Sopenharmony_ci	s32 maximum;
14262306a36Sopenharmony_ci	u32 step;
14362306a36Sopenharmony_ci	s32 default_value;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	/* control register information */
14662306a36Sopenharmony_ci	u32 off;
14762306a36Sopenharmony_ci	u32 reg;
14862306a36Sopenharmony_ci	u32 sreg;
14962306a36Sopenharmony_ci	u32 mask;
15062306a36Sopenharmony_ci	u32 shift;
15162306a36Sopenharmony_ci};
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic const struct cx88_ctrl cx8800_vid_ctls[] = {
15462306a36Sopenharmony_ci	/* --- video --- */
15562306a36Sopenharmony_ci	{
15662306a36Sopenharmony_ci		.id            = V4L2_CID_BRIGHTNESS,
15762306a36Sopenharmony_ci		.minimum       = 0x00,
15862306a36Sopenharmony_ci		.maximum       = 0xff,
15962306a36Sopenharmony_ci		.step          = 1,
16062306a36Sopenharmony_ci		.default_value = 0x7f,
16162306a36Sopenharmony_ci		.off           = 128,
16262306a36Sopenharmony_ci		.reg           = MO_CONTR_BRIGHT,
16362306a36Sopenharmony_ci		.mask          = 0x00ff,
16462306a36Sopenharmony_ci		.shift         = 0,
16562306a36Sopenharmony_ci	}, {
16662306a36Sopenharmony_ci		.id            = V4L2_CID_CONTRAST,
16762306a36Sopenharmony_ci		.minimum       = 0,
16862306a36Sopenharmony_ci		.maximum       = 0xff,
16962306a36Sopenharmony_ci		.step          = 1,
17062306a36Sopenharmony_ci		.default_value = 0x3f,
17162306a36Sopenharmony_ci		.off           = 0,
17262306a36Sopenharmony_ci		.reg           = MO_CONTR_BRIGHT,
17362306a36Sopenharmony_ci		.mask          = 0xff00,
17462306a36Sopenharmony_ci		.shift         = 8,
17562306a36Sopenharmony_ci	}, {
17662306a36Sopenharmony_ci		.id            = V4L2_CID_HUE,
17762306a36Sopenharmony_ci		.minimum       = 0,
17862306a36Sopenharmony_ci		.maximum       = 0xff,
17962306a36Sopenharmony_ci		.step          = 1,
18062306a36Sopenharmony_ci		.default_value = 0x7f,
18162306a36Sopenharmony_ci		.off           = 128,
18262306a36Sopenharmony_ci		.reg           = MO_HUE,
18362306a36Sopenharmony_ci		.mask          = 0x00ff,
18462306a36Sopenharmony_ci		.shift         = 0,
18562306a36Sopenharmony_ci	}, {
18662306a36Sopenharmony_ci		/* strictly, this only describes only U saturation.
18762306a36Sopenharmony_ci		 * V saturation is handled specially through code.
18862306a36Sopenharmony_ci		 */
18962306a36Sopenharmony_ci		.id            = V4L2_CID_SATURATION,
19062306a36Sopenharmony_ci		.minimum       = 0,
19162306a36Sopenharmony_ci		.maximum       = 0xff,
19262306a36Sopenharmony_ci		.step          = 1,
19362306a36Sopenharmony_ci		.default_value = 0x7f,
19462306a36Sopenharmony_ci		.off           = 0,
19562306a36Sopenharmony_ci		.reg           = MO_UV_SATURATION,
19662306a36Sopenharmony_ci		.mask          = 0x00ff,
19762306a36Sopenharmony_ci		.shift         = 0,
19862306a36Sopenharmony_ci	}, {
19962306a36Sopenharmony_ci		.id            = V4L2_CID_SHARPNESS,
20062306a36Sopenharmony_ci		.minimum       = 0,
20162306a36Sopenharmony_ci		.maximum       = 4,
20262306a36Sopenharmony_ci		.step          = 1,
20362306a36Sopenharmony_ci		.default_value = 0x0,
20462306a36Sopenharmony_ci		.off           = 0,
20562306a36Sopenharmony_ci		/*
20662306a36Sopenharmony_ci		 * NOTE: the value is converted and written to both even
20762306a36Sopenharmony_ci		 * and odd registers in the code
20862306a36Sopenharmony_ci		 */
20962306a36Sopenharmony_ci		.reg           = MO_FILTER_ODD,
21062306a36Sopenharmony_ci		.mask          = 7 << 7,
21162306a36Sopenharmony_ci		.shift         = 7,
21262306a36Sopenharmony_ci	}, {
21362306a36Sopenharmony_ci		.id            = V4L2_CID_CHROMA_AGC,
21462306a36Sopenharmony_ci		.minimum       = 0,
21562306a36Sopenharmony_ci		.maximum       = 1,
21662306a36Sopenharmony_ci		.default_value = 0x1,
21762306a36Sopenharmony_ci		.reg           = MO_INPUT_FORMAT,
21862306a36Sopenharmony_ci		.mask          = 1 << 10,
21962306a36Sopenharmony_ci		.shift         = 10,
22062306a36Sopenharmony_ci	}, {
22162306a36Sopenharmony_ci		.id            = V4L2_CID_COLOR_KILLER,
22262306a36Sopenharmony_ci		.minimum       = 0,
22362306a36Sopenharmony_ci		.maximum       = 1,
22462306a36Sopenharmony_ci		.default_value = 0x1,
22562306a36Sopenharmony_ci		.reg           = MO_INPUT_FORMAT,
22662306a36Sopenharmony_ci		.mask          = 1 << 9,
22762306a36Sopenharmony_ci		.shift         = 9,
22862306a36Sopenharmony_ci	}, {
22962306a36Sopenharmony_ci		.id            = V4L2_CID_BAND_STOP_FILTER,
23062306a36Sopenharmony_ci		.minimum       = 0,
23162306a36Sopenharmony_ci		.maximum       = 1,
23262306a36Sopenharmony_ci		.step          = 1,
23362306a36Sopenharmony_ci		.default_value = 0x0,
23462306a36Sopenharmony_ci		.off           = 0,
23562306a36Sopenharmony_ci		.reg           = MO_HTOTAL,
23662306a36Sopenharmony_ci		.mask          = 3 << 11,
23762306a36Sopenharmony_ci		.shift         = 11,
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci};
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic const struct cx88_ctrl cx8800_aud_ctls[] = {
24262306a36Sopenharmony_ci	{
24362306a36Sopenharmony_ci		/* --- audio --- */
24462306a36Sopenharmony_ci		.id            = V4L2_CID_AUDIO_MUTE,
24562306a36Sopenharmony_ci		.minimum       = 0,
24662306a36Sopenharmony_ci		.maximum       = 1,
24762306a36Sopenharmony_ci		.default_value = 1,
24862306a36Sopenharmony_ci		.reg           = AUD_VOL_CTL,
24962306a36Sopenharmony_ci		.sreg          = SHADOW_AUD_VOL_CTL,
25062306a36Sopenharmony_ci		.mask          = (1 << 6),
25162306a36Sopenharmony_ci		.shift         = 6,
25262306a36Sopenharmony_ci	}, {
25362306a36Sopenharmony_ci		.id            = V4L2_CID_AUDIO_VOLUME,
25462306a36Sopenharmony_ci		.minimum       = 0,
25562306a36Sopenharmony_ci		.maximum       = 0x3f,
25662306a36Sopenharmony_ci		.step          = 1,
25762306a36Sopenharmony_ci		.default_value = 0x3f,
25862306a36Sopenharmony_ci		.reg           = AUD_VOL_CTL,
25962306a36Sopenharmony_ci		.sreg          = SHADOW_AUD_VOL_CTL,
26062306a36Sopenharmony_ci		.mask          = 0x3f,
26162306a36Sopenharmony_ci		.shift         = 0,
26262306a36Sopenharmony_ci	}, {
26362306a36Sopenharmony_ci		.id            = V4L2_CID_AUDIO_BALANCE,
26462306a36Sopenharmony_ci		.minimum       = 0,
26562306a36Sopenharmony_ci		.maximum       = 0x7f,
26662306a36Sopenharmony_ci		.step          = 1,
26762306a36Sopenharmony_ci		.default_value = 0x40,
26862306a36Sopenharmony_ci		.reg           = AUD_BAL_CTL,
26962306a36Sopenharmony_ci		.sreg          = SHADOW_AUD_BAL_CTL,
27062306a36Sopenharmony_ci		.mask          = 0x7f,
27162306a36Sopenharmony_ci		.shift         = 0,
27262306a36Sopenharmony_ci	}
27362306a36Sopenharmony_ci};
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cienum {
27662306a36Sopenharmony_ci	CX8800_VID_CTLS = ARRAY_SIZE(cx8800_vid_ctls),
27762306a36Sopenharmony_ci	CX8800_AUD_CTLS = ARRAY_SIZE(cx8800_aud_ctls),
27862306a36Sopenharmony_ci};
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci/* ------------------------------------------------------------------ */
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ciint cx88_video_mux(struct cx88_core *core, unsigned int input)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	/* struct cx88_core *core = dev->core; */
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	dprintk(1, "video_mux: %d [vmux=%d,gpio=0x%x,0x%x,0x%x,0x%x]\n",
28762306a36Sopenharmony_ci		input, INPUT(input).vmux,
28862306a36Sopenharmony_ci		INPUT(input).gpio0, INPUT(input).gpio1,
28962306a36Sopenharmony_ci		INPUT(input).gpio2, INPUT(input).gpio3);
29062306a36Sopenharmony_ci	core->input = input;
29162306a36Sopenharmony_ci	cx_andor(MO_INPUT_FORMAT, 0x03 << 14, INPUT(input).vmux << 14);
29262306a36Sopenharmony_ci	cx_write(MO_GP3_IO, INPUT(input).gpio3);
29362306a36Sopenharmony_ci	cx_write(MO_GP0_IO, INPUT(input).gpio0);
29462306a36Sopenharmony_ci	cx_write(MO_GP1_IO, INPUT(input).gpio1);
29562306a36Sopenharmony_ci	cx_write(MO_GP2_IO, INPUT(input).gpio2);
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	switch (INPUT(input).type) {
29862306a36Sopenharmony_ci	case CX88_VMUX_SVIDEO:
29962306a36Sopenharmony_ci		cx_set(MO_AFECFG_IO,    0x00000001);
30062306a36Sopenharmony_ci		cx_set(MO_INPUT_FORMAT, 0x00010010);
30162306a36Sopenharmony_ci		cx_set(MO_FILTER_EVEN,  0x00002020);
30262306a36Sopenharmony_ci		cx_set(MO_FILTER_ODD,   0x00002020);
30362306a36Sopenharmony_ci		break;
30462306a36Sopenharmony_ci	default:
30562306a36Sopenharmony_ci		cx_clear(MO_AFECFG_IO,    0x00000001);
30662306a36Sopenharmony_ci		cx_clear(MO_INPUT_FORMAT, 0x00010010);
30762306a36Sopenharmony_ci		cx_clear(MO_FILTER_EVEN,  0x00002020);
30862306a36Sopenharmony_ci		cx_clear(MO_FILTER_ODD,   0x00002020);
30962306a36Sopenharmony_ci		break;
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	/*
31362306a36Sopenharmony_ci	 * if there are audioroutes defined, we have an external
31462306a36Sopenharmony_ci	 * ADC to deal with audio
31562306a36Sopenharmony_ci	 */
31662306a36Sopenharmony_ci	if (INPUT(input).audioroute) {
31762306a36Sopenharmony_ci		/*
31862306a36Sopenharmony_ci		 * The wm8775 module has the "2" route hardwired into
31962306a36Sopenharmony_ci		 * the initialization. Some boards may use different
32062306a36Sopenharmony_ci		 * routes for different inputs. HVR-1300 surely does
32162306a36Sopenharmony_ci		 */
32262306a36Sopenharmony_ci		if (core->sd_wm8775) {
32362306a36Sopenharmony_ci			call_all(core, audio, s_routing,
32462306a36Sopenharmony_ci				 INPUT(input).audioroute, 0, 0);
32562306a36Sopenharmony_ci		}
32662306a36Sopenharmony_ci		/*
32762306a36Sopenharmony_ci		 * cx2388's C-ADC is connected to the tuner only.
32862306a36Sopenharmony_ci		 * When used with S-Video, that ADC is busy dealing with
32962306a36Sopenharmony_ci		 * chroma, so an external must be used for baseband audio
33062306a36Sopenharmony_ci		 */
33162306a36Sopenharmony_ci		if (INPUT(input).type != CX88_VMUX_TELEVISION &&
33262306a36Sopenharmony_ci		    INPUT(input).type != CX88_VMUX_CABLE) {
33362306a36Sopenharmony_ci			/* "I2S ADC mode" */
33462306a36Sopenharmony_ci			core->tvaudio = WW_I2SADC;
33562306a36Sopenharmony_ci			cx88_set_tvaudio(core);
33662306a36Sopenharmony_ci		} else {
33762306a36Sopenharmony_ci			/* Normal mode */
33862306a36Sopenharmony_ci			cx_write(AUD_I2SCNTL, 0x0);
33962306a36Sopenharmony_ci			cx_clear(AUD_CTL, EN_I2SIN_ENABLE);
34062306a36Sopenharmony_ci		}
34162306a36Sopenharmony_ci	}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	return 0;
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ciEXPORT_SYMBOL(cx88_video_mux);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci/* ------------------------------------------------------------------ */
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cistatic int start_video_dma(struct cx8800_dev    *dev,
35062306a36Sopenharmony_ci			   struct cx88_dmaqueue *q,
35162306a36Sopenharmony_ci			   struct cx88_buffer   *buf)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	/* setup fifo + format */
35662306a36Sopenharmony_ci	cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH21],
35762306a36Sopenharmony_ci				buf->bpl, buf->risc.dma);
35862306a36Sopenharmony_ci	cx88_set_scale(core, core->width, core->height, core->field);
35962306a36Sopenharmony_ci	cx_write(MO_COLOR_CTRL, dev->fmt->cxformat | ColorFormatGamma);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	/* reset counter */
36262306a36Sopenharmony_ci	cx_write(MO_VIDY_GPCNTRL, GP_COUNT_CONTROL_RESET);
36362306a36Sopenharmony_ci	q->count = 0;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	/* enable irqs */
36662306a36Sopenharmony_ci	cx_set(MO_PCI_INTMSK, core->pci_irqmask | PCI_INT_VIDINT);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	/*
36962306a36Sopenharmony_ci	 * Enables corresponding bits at PCI_INT_STAT:
37062306a36Sopenharmony_ci	 *	bits 0 to 4: video, audio, transport stream, VIP, Host
37162306a36Sopenharmony_ci	 *	bit 7: timer
37262306a36Sopenharmony_ci	 *	bits 8 and 9: DMA complete for: SRC, DST
37362306a36Sopenharmony_ci	 *	bits 10 and 11: BERR signal asserted for RISC: RD, WR
37462306a36Sopenharmony_ci	 *	bits 12 to 15: BERR signal asserted for: BRDG, SRC, DST, IPB
37562306a36Sopenharmony_ci	 */
37662306a36Sopenharmony_ci	cx_set(MO_VID_INTMSK, 0x0f0011);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	/* enable capture */
37962306a36Sopenharmony_ci	cx_set(VID_CAPTURE_CONTROL, 0x06);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	/* start dma */
38262306a36Sopenharmony_ci	cx_set(MO_DEV_CNTRL2, (1 << 5));
38362306a36Sopenharmony_ci	cx_set(MO_VID_DMACNTRL, 0x11); /* Planar Y and packed FIFO and RISC enable */
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	return 0;
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_cistatic int __maybe_unused stop_video_dma(struct cx8800_dev    *dev)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	/* stop dma */
39362306a36Sopenharmony_ci	cx_clear(MO_VID_DMACNTRL, 0x11);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	/* disable capture */
39662306a36Sopenharmony_ci	cx_clear(VID_CAPTURE_CONTROL, 0x06);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	/* disable irqs */
39962306a36Sopenharmony_ci	cx_clear(MO_PCI_INTMSK, PCI_INT_VIDINT);
40062306a36Sopenharmony_ci	cx_clear(MO_VID_INTMSK, 0x0f0011);
40162306a36Sopenharmony_ci	return 0;
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic int __maybe_unused restart_video_queue(struct cx8800_dev *dev,
40562306a36Sopenharmony_ci					      struct cx88_dmaqueue *q)
40662306a36Sopenharmony_ci{
40762306a36Sopenharmony_ci	struct cx88_buffer *buf;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	if (!list_empty(&q->active)) {
41062306a36Sopenharmony_ci		buf = list_entry(q->active.next, struct cx88_buffer, list);
41162306a36Sopenharmony_ci		dprintk(2, "restart_queue [%p/%d]: restart dma\n",
41262306a36Sopenharmony_ci			buf, buf->vb.vb2_buf.index);
41362306a36Sopenharmony_ci		start_video_dma(dev, q, buf);
41462306a36Sopenharmony_ci	}
41562306a36Sopenharmony_ci	return 0;
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci/* ------------------------------------------------------------------ */
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_cistatic int queue_setup(struct vb2_queue *q,
42162306a36Sopenharmony_ci		       unsigned int *num_buffers, unsigned int *num_planes,
42262306a36Sopenharmony_ci		       unsigned int sizes[], struct device *alloc_devs[])
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	struct cx8800_dev *dev = q->drv_priv;
42562306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	*num_planes = 1;
42862306a36Sopenharmony_ci	sizes[0] = (dev->fmt->depth * core->width * core->height) >> 3;
42962306a36Sopenharmony_ci	return 0;
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_cistatic int buffer_prepare(struct vb2_buffer *vb)
43362306a36Sopenharmony_ci{
43462306a36Sopenharmony_ci	int ret;
43562306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
43662306a36Sopenharmony_ci	struct cx8800_dev *dev = vb->vb2_queue->drv_priv;
43762306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
43862306a36Sopenharmony_ci	struct cx88_buffer *buf = container_of(vbuf, struct cx88_buffer, vb);
43962306a36Sopenharmony_ci	struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	buf->bpl = core->width * dev->fmt->depth >> 3;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	if (vb2_plane_size(vb, 0) < core->height * buf->bpl)
44462306a36Sopenharmony_ci		return -EINVAL;
44562306a36Sopenharmony_ci	vb2_set_plane_payload(vb, 0, core->height * buf->bpl);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	switch (core->field) {
44862306a36Sopenharmony_ci	case V4L2_FIELD_TOP:
44962306a36Sopenharmony_ci		ret = cx88_risc_buffer(dev->pci, &buf->risc,
45062306a36Sopenharmony_ci				       sgt->sgl, 0, UNSET,
45162306a36Sopenharmony_ci				       buf->bpl, 0, core->height);
45262306a36Sopenharmony_ci		break;
45362306a36Sopenharmony_ci	case V4L2_FIELD_BOTTOM:
45462306a36Sopenharmony_ci		ret = cx88_risc_buffer(dev->pci, &buf->risc,
45562306a36Sopenharmony_ci				       sgt->sgl, UNSET, 0,
45662306a36Sopenharmony_ci				       buf->bpl, 0, core->height);
45762306a36Sopenharmony_ci		break;
45862306a36Sopenharmony_ci	case V4L2_FIELD_SEQ_TB:
45962306a36Sopenharmony_ci		ret = cx88_risc_buffer(dev->pci, &buf->risc,
46062306a36Sopenharmony_ci				       sgt->sgl,
46162306a36Sopenharmony_ci				       0, buf->bpl * (core->height >> 1),
46262306a36Sopenharmony_ci				       buf->bpl, 0,
46362306a36Sopenharmony_ci				       core->height >> 1);
46462306a36Sopenharmony_ci		break;
46562306a36Sopenharmony_ci	case V4L2_FIELD_SEQ_BT:
46662306a36Sopenharmony_ci		ret = cx88_risc_buffer(dev->pci, &buf->risc,
46762306a36Sopenharmony_ci				       sgt->sgl,
46862306a36Sopenharmony_ci				       buf->bpl * (core->height >> 1), 0,
46962306a36Sopenharmony_ci				       buf->bpl, 0,
47062306a36Sopenharmony_ci				       core->height >> 1);
47162306a36Sopenharmony_ci		break;
47262306a36Sopenharmony_ci	case V4L2_FIELD_INTERLACED:
47362306a36Sopenharmony_ci	default:
47462306a36Sopenharmony_ci		ret = cx88_risc_buffer(dev->pci, &buf->risc,
47562306a36Sopenharmony_ci				       sgt->sgl, 0, buf->bpl,
47662306a36Sopenharmony_ci				       buf->bpl, buf->bpl,
47762306a36Sopenharmony_ci				       core->height >> 1);
47862306a36Sopenharmony_ci		break;
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ci	dprintk(2,
48162306a36Sopenharmony_ci		"[%p/%d] %s - %dx%d %dbpp 0x%08x - dma=0x%08lx\n",
48262306a36Sopenharmony_ci		buf, buf->vb.vb2_buf.index, __func__,
48362306a36Sopenharmony_ci		core->width, core->height, dev->fmt->depth, dev->fmt->fourcc,
48462306a36Sopenharmony_ci		(unsigned long)buf->risc.dma);
48562306a36Sopenharmony_ci	return ret;
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_cistatic void buffer_finish(struct vb2_buffer *vb)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
49162306a36Sopenharmony_ci	struct cx8800_dev *dev = vb->vb2_queue->drv_priv;
49262306a36Sopenharmony_ci	struct cx88_buffer *buf = container_of(vbuf, struct cx88_buffer, vb);
49362306a36Sopenharmony_ci	struct cx88_riscmem *risc = &buf->risc;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	if (risc->cpu)
49662306a36Sopenharmony_ci		dma_free_coherent(&dev->pci->dev, risc->size, risc->cpu,
49762306a36Sopenharmony_ci				  risc->dma);
49862306a36Sopenharmony_ci	memset(risc, 0, sizeof(*risc));
49962306a36Sopenharmony_ci}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_cistatic void buffer_queue(struct vb2_buffer *vb)
50262306a36Sopenharmony_ci{
50362306a36Sopenharmony_ci	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
50462306a36Sopenharmony_ci	struct cx8800_dev *dev = vb->vb2_queue->drv_priv;
50562306a36Sopenharmony_ci	struct cx88_buffer    *buf = container_of(vbuf, struct cx88_buffer, vb);
50662306a36Sopenharmony_ci	struct cx88_buffer    *prev;
50762306a36Sopenharmony_ci	struct cx88_dmaqueue  *q    = &dev->vidq;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	/* add jump to start */
51062306a36Sopenharmony_ci	buf->risc.cpu[1] = cpu_to_le32(buf->risc.dma + 8);
51162306a36Sopenharmony_ci	buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_CNT_INC);
51262306a36Sopenharmony_ci	buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma + 8);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	if (list_empty(&q->active)) {
51562306a36Sopenharmony_ci		list_add_tail(&buf->list, &q->active);
51662306a36Sopenharmony_ci		dprintk(2, "[%p/%d] buffer_queue - first active\n",
51762306a36Sopenharmony_ci			buf, buf->vb.vb2_buf.index);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	} else {
52062306a36Sopenharmony_ci		buf->risc.cpu[0] |= cpu_to_le32(RISC_IRQ1);
52162306a36Sopenharmony_ci		prev = list_entry(q->active.prev, struct cx88_buffer, list);
52262306a36Sopenharmony_ci		list_add_tail(&buf->list, &q->active);
52362306a36Sopenharmony_ci		prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
52462306a36Sopenharmony_ci		dprintk(2, "[%p/%d] buffer_queue - append to active\n",
52562306a36Sopenharmony_ci			buf, buf->vb.vb2_buf.index);
52662306a36Sopenharmony_ci	}
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_cistatic int start_streaming(struct vb2_queue *q, unsigned int count)
53062306a36Sopenharmony_ci{
53162306a36Sopenharmony_ci	struct cx8800_dev *dev = q->drv_priv;
53262306a36Sopenharmony_ci	struct cx88_dmaqueue *dmaq = &dev->vidq;
53362306a36Sopenharmony_ci	struct cx88_buffer *buf = list_entry(dmaq->active.next,
53462306a36Sopenharmony_ci			struct cx88_buffer, list);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	start_video_dma(dev, dmaq, buf);
53762306a36Sopenharmony_ci	return 0;
53862306a36Sopenharmony_ci}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_cistatic void stop_streaming(struct vb2_queue *q)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	struct cx8800_dev *dev = q->drv_priv;
54362306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
54462306a36Sopenharmony_ci	struct cx88_dmaqueue *dmaq = &dev->vidq;
54562306a36Sopenharmony_ci	unsigned long flags;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	cx_clear(MO_VID_DMACNTRL, 0x11);
54862306a36Sopenharmony_ci	cx_clear(VID_CAPTURE_CONTROL, 0x06);
54962306a36Sopenharmony_ci	spin_lock_irqsave(&dev->slock, flags);
55062306a36Sopenharmony_ci	while (!list_empty(&dmaq->active)) {
55162306a36Sopenharmony_ci		struct cx88_buffer *buf = list_entry(dmaq->active.next,
55262306a36Sopenharmony_ci			struct cx88_buffer, list);
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci		list_del(&buf->list);
55562306a36Sopenharmony_ci		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
55662306a36Sopenharmony_ci	}
55762306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->slock, flags);
55862306a36Sopenharmony_ci}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_cistatic const struct vb2_ops cx8800_video_qops = {
56162306a36Sopenharmony_ci	.queue_setup    = queue_setup,
56262306a36Sopenharmony_ci	.buf_prepare  = buffer_prepare,
56362306a36Sopenharmony_ci	.buf_finish = buffer_finish,
56462306a36Sopenharmony_ci	.buf_queue    = buffer_queue,
56562306a36Sopenharmony_ci	.wait_prepare = vb2_ops_wait_prepare,
56662306a36Sopenharmony_ci	.wait_finish = vb2_ops_wait_finish,
56762306a36Sopenharmony_ci	.start_streaming = start_streaming,
56862306a36Sopenharmony_ci	.stop_streaming = stop_streaming,
56962306a36Sopenharmony_ci};
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci/* ------------------------------------------------------------------ */
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_cistatic int radio_open(struct file *file)
57462306a36Sopenharmony_ci{
57562306a36Sopenharmony_ci	struct cx8800_dev *dev = video_drvdata(file);
57662306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
57762306a36Sopenharmony_ci	int ret = v4l2_fh_open(file);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	if (ret)
58062306a36Sopenharmony_ci		return ret;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	cx_write(MO_GP3_IO, core->board.radio.gpio3);
58362306a36Sopenharmony_ci	cx_write(MO_GP0_IO, core->board.radio.gpio0);
58462306a36Sopenharmony_ci	cx_write(MO_GP1_IO, core->board.radio.gpio1);
58562306a36Sopenharmony_ci	cx_write(MO_GP2_IO, core->board.radio.gpio2);
58662306a36Sopenharmony_ci	if (core->board.radio.audioroute) {
58762306a36Sopenharmony_ci		if (core->sd_wm8775) {
58862306a36Sopenharmony_ci			call_all(core, audio, s_routing,
58962306a36Sopenharmony_ci				 core->board.radio.audioroute, 0, 0);
59062306a36Sopenharmony_ci		}
59162306a36Sopenharmony_ci		/* "I2S ADC mode" */
59262306a36Sopenharmony_ci		core->tvaudio = WW_I2SADC;
59362306a36Sopenharmony_ci		cx88_set_tvaudio(core);
59462306a36Sopenharmony_ci	} else {
59562306a36Sopenharmony_ci		/* FM Mode */
59662306a36Sopenharmony_ci		core->tvaudio = WW_FM;
59762306a36Sopenharmony_ci		cx88_set_tvaudio(core);
59862306a36Sopenharmony_ci		cx88_set_stereo(core, V4L2_TUNER_MODE_STEREO, 1);
59962306a36Sopenharmony_ci	}
60062306a36Sopenharmony_ci	call_all(core, tuner, s_radio);
60162306a36Sopenharmony_ci	return 0;
60262306a36Sopenharmony_ci}
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci/* ------------------------------------------------------------------ */
60562306a36Sopenharmony_ci/* VIDEO CTRL IOCTLS                                                  */
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic int cx8800_s_vid_ctrl(struct v4l2_ctrl *ctrl)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	struct cx88_core *core =
61062306a36Sopenharmony_ci		container_of(ctrl->handler, struct cx88_core, video_hdl);
61162306a36Sopenharmony_ci	const struct cx88_ctrl *cc = ctrl->priv;
61262306a36Sopenharmony_ci	u32 value, mask;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	mask = cc->mask;
61562306a36Sopenharmony_ci	switch (ctrl->id) {
61662306a36Sopenharmony_ci	case V4L2_CID_SATURATION:
61762306a36Sopenharmony_ci		/* special v_sat handling */
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci		value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci		if (core->tvnorm & V4L2_STD_SECAM) {
62262306a36Sopenharmony_ci			/* For SECAM, both U and V sat should be equal */
62362306a36Sopenharmony_ci			value = value << 8 | value;
62462306a36Sopenharmony_ci		} else {
62562306a36Sopenharmony_ci			/* Keeps U Saturation proportional to V Sat */
62662306a36Sopenharmony_ci			value = (value * 0x5a) / 0x7f << 8 | value;
62762306a36Sopenharmony_ci		}
62862306a36Sopenharmony_ci		mask = 0xffff;
62962306a36Sopenharmony_ci		break;
63062306a36Sopenharmony_ci	case V4L2_CID_SHARPNESS:
63162306a36Sopenharmony_ci		/* 0b000, 0b100, 0b101, 0b110, or 0b111 */
63262306a36Sopenharmony_ci		value = (ctrl->val < 1 ? 0 : ((ctrl->val + 3) << 7));
63362306a36Sopenharmony_ci		/* needs to be set for both fields */
63462306a36Sopenharmony_ci		cx_andor(MO_FILTER_EVEN, mask, value);
63562306a36Sopenharmony_ci		break;
63662306a36Sopenharmony_ci	case V4L2_CID_CHROMA_AGC:
63762306a36Sopenharmony_ci		value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
63862306a36Sopenharmony_ci		break;
63962306a36Sopenharmony_ci	default:
64062306a36Sopenharmony_ci		value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
64162306a36Sopenharmony_ci		break;
64262306a36Sopenharmony_ci	}
64362306a36Sopenharmony_ci	dprintk(1,
64462306a36Sopenharmony_ci		"set_control id=0x%X(%s) ctrl=0x%02x, reg=0x%02x val=0x%02x (mask 0x%02x)%s\n",
64562306a36Sopenharmony_ci		ctrl->id, ctrl->name, ctrl->val, cc->reg, value,
64662306a36Sopenharmony_ci		mask, cc->sreg ? " [shadowed]" : "");
64762306a36Sopenharmony_ci	if (cc->sreg)
64862306a36Sopenharmony_ci		cx_sandor(cc->sreg, cc->reg, mask, value);
64962306a36Sopenharmony_ci	else
65062306a36Sopenharmony_ci		cx_andor(cc->reg, mask, value);
65162306a36Sopenharmony_ci	return 0;
65262306a36Sopenharmony_ci}
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_cistatic int cx8800_s_aud_ctrl(struct v4l2_ctrl *ctrl)
65562306a36Sopenharmony_ci{
65662306a36Sopenharmony_ci	struct cx88_core *core =
65762306a36Sopenharmony_ci		container_of(ctrl->handler, struct cx88_core, audio_hdl);
65862306a36Sopenharmony_ci	const struct cx88_ctrl *cc = ctrl->priv;
65962306a36Sopenharmony_ci	u32 value, mask;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci	/* Pass changes onto any WM8775 */
66262306a36Sopenharmony_ci	if (core->sd_wm8775) {
66362306a36Sopenharmony_ci		switch (ctrl->id) {
66462306a36Sopenharmony_ci		case V4L2_CID_AUDIO_MUTE:
66562306a36Sopenharmony_ci			wm8775_s_ctrl(core, ctrl->id, ctrl->val);
66662306a36Sopenharmony_ci			break;
66762306a36Sopenharmony_ci		case V4L2_CID_AUDIO_VOLUME:
66862306a36Sopenharmony_ci			wm8775_s_ctrl(core, ctrl->id, (ctrl->val) ?
66962306a36Sopenharmony_ci						(0x90 + ctrl->val) << 8 : 0);
67062306a36Sopenharmony_ci			break;
67162306a36Sopenharmony_ci		case V4L2_CID_AUDIO_BALANCE:
67262306a36Sopenharmony_ci			wm8775_s_ctrl(core, ctrl->id, ctrl->val << 9);
67362306a36Sopenharmony_ci			break;
67462306a36Sopenharmony_ci		default:
67562306a36Sopenharmony_ci			break;
67662306a36Sopenharmony_ci		}
67762306a36Sopenharmony_ci	}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	mask = cc->mask;
68062306a36Sopenharmony_ci	switch (ctrl->id) {
68162306a36Sopenharmony_ci	case V4L2_CID_AUDIO_BALANCE:
68262306a36Sopenharmony_ci		value = (ctrl->val < 0x40) ?
68362306a36Sopenharmony_ci			(0x7f - ctrl->val) : (ctrl->val - 0x40);
68462306a36Sopenharmony_ci		break;
68562306a36Sopenharmony_ci	case V4L2_CID_AUDIO_VOLUME:
68662306a36Sopenharmony_ci		value = 0x3f - (ctrl->val & 0x3f);
68762306a36Sopenharmony_ci		break;
68862306a36Sopenharmony_ci	default:
68962306a36Sopenharmony_ci		value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
69062306a36Sopenharmony_ci		break;
69162306a36Sopenharmony_ci	}
69262306a36Sopenharmony_ci	dprintk(1,
69362306a36Sopenharmony_ci		"set_control id=0x%X(%s) ctrl=0x%02x, reg=0x%02x val=0x%02x (mask 0x%02x)%s\n",
69462306a36Sopenharmony_ci		ctrl->id, ctrl->name, ctrl->val, cc->reg, value,
69562306a36Sopenharmony_ci		mask, cc->sreg ? " [shadowed]" : "");
69662306a36Sopenharmony_ci	if (cc->sreg)
69762306a36Sopenharmony_ci		cx_sandor(cc->sreg, cc->reg, mask, value);
69862306a36Sopenharmony_ci	else
69962306a36Sopenharmony_ci		cx_andor(cc->reg, mask, value);
70062306a36Sopenharmony_ci	return 0;
70162306a36Sopenharmony_ci}
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci/* ------------------------------------------------------------------ */
70462306a36Sopenharmony_ci/* VIDEO IOCTLS                                                       */
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_cistatic int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
70762306a36Sopenharmony_ci				struct v4l2_format *f)
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	struct cx8800_dev *dev = video_drvdata(file);
71062306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	f->fmt.pix.width        = core->width;
71362306a36Sopenharmony_ci	f->fmt.pix.height       = core->height;
71462306a36Sopenharmony_ci	f->fmt.pix.field        = core->field;
71562306a36Sopenharmony_ci	f->fmt.pix.pixelformat  = dev->fmt->fourcc;
71662306a36Sopenharmony_ci	f->fmt.pix.bytesperline =
71762306a36Sopenharmony_ci		(f->fmt.pix.width * dev->fmt->depth) >> 3;
71862306a36Sopenharmony_ci	f->fmt.pix.sizeimage =
71962306a36Sopenharmony_ci		f->fmt.pix.height * f->fmt.pix.bytesperline;
72062306a36Sopenharmony_ci	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
72162306a36Sopenharmony_ci	return 0;
72262306a36Sopenharmony_ci}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_cistatic int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
72562306a36Sopenharmony_ci				  struct v4l2_format *f)
72662306a36Sopenharmony_ci{
72762306a36Sopenharmony_ci	struct cx8800_dev *dev = video_drvdata(file);
72862306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
72962306a36Sopenharmony_ci	const struct cx8800_fmt *fmt;
73062306a36Sopenharmony_ci	enum v4l2_field   field;
73162306a36Sopenharmony_ci	unsigned int      maxw, maxh;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	fmt = format_by_fourcc(f->fmt.pix.pixelformat);
73462306a36Sopenharmony_ci	if (!fmt)
73562306a36Sopenharmony_ci		return -EINVAL;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	maxw = norm_maxw(core->tvnorm);
73862306a36Sopenharmony_ci	maxh = norm_maxh(core->tvnorm);
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	field = f->fmt.pix.field;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	switch (field) {
74362306a36Sopenharmony_ci	case V4L2_FIELD_TOP:
74462306a36Sopenharmony_ci	case V4L2_FIELD_BOTTOM:
74562306a36Sopenharmony_ci	case V4L2_FIELD_INTERLACED:
74662306a36Sopenharmony_ci	case V4L2_FIELD_SEQ_BT:
74762306a36Sopenharmony_ci	case V4L2_FIELD_SEQ_TB:
74862306a36Sopenharmony_ci		break;
74962306a36Sopenharmony_ci	default:
75062306a36Sopenharmony_ci		field = (f->fmt.pix.height > maxh / 2)
75162306a36Sopenharmony_ci			? V4L2_FIELD_INTERLACED
75262306a36Sopenharmony_ci			: V4L2_FIELD_BOTTOM;
75362306a36Sopenharmony_ci		break;
75462306a36Sopenharmony_ci	}
75562306a36Sopenharmony_ci	if (V4L2_FIELD_HAS_T_OR_B(field))
75662306a36Sopenharmony_ci		maxh /= 2;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2,
75962306a36Sopenharmony_ci			      &f->fmt.pix.height, 32, maxh, 0, 0);
76062306a36Sopenharmony_ci	f->fmt.pix.field = field;
76162306a36Sopenharmony_ci	f->fmt.pix.bytesperline =
76262306a36Sopenharmony_ci		(f->fmt.pix.width * fmt->depth) >> 3;
76362306a36Sopenharmony_ci	f->fmt.pix.sizeimage =
76462306a36Sopenharmony_ci		f->fmt.pix.height * f->fmt.pix.bytesperline;
76562306a36Sopenharmony_ci	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	return 0;
76862306a36Sopenharmony_ci}
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_cistatic int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
77162306a36Sopenharmony_ci				struct v4l2_format *f)
77262306a36Sopenharmony_ci{
77362306a36Sopenharmony_ci	struct cx8800_dev *dev = video_drvdata(file);
77462306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
77562306a36Sopenharmony_ci	int err = vidioc_try_fmt_vid_cap(file, priv, f);
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	if (err != 0)
77862306a36Sopenharmony_ci		return err;
77962306a36Sopenharmony_ci	if (vb2_is_busy(&dev->vb2_vidq) || vb2_is_busy(&dev->vb2_vbiq))
78062306a36Sopenharmony_ci		return -EBUSY;
78162306a36Sopenharmony_ci	if (core->dvbdev && vb2_is_busy(&core->dvbdev->vb2_mpegq))
78262306a36Sopenharmony_ci		return -EBUSY;
78362306a36Sopenharmony_ci	dev->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
78462306a36Sopenharmony_ci	core->width = f->fmt.pix.width;
78562306a36Sopenharmony_ci	core->height = f->fmt.pix.height;
78662306a36Sopenharmony_ci	core->field = f->fmt.pix.field;
78762306a36Sopenharmony_ci	return 0;
78862306a36Sopenharmony_ci}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ciint cx88_querycap(struct file *file, struct cx88_core *core,
79162306a36Sopenharmony_ci		  struct v4l2_capability *cap)
79262306a36Sopenharmony_ci{
79362306a36Sopenharmony_ci	strscpy(cap->card, core->board.name, sizeof(cap->card));
79462306a36Sopenharmony_ci	cap->capabilities = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING |
79562306a36Sopenharmony_ci			    V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VBI_CAPTURE |
79662306a36Sopenharmony_ci			    V4L2_CAP_DEVICE_CAPS;
79762306a36Sopenharmony_ci	if (core->board.tuner_type != UNSET)
79862306a36Sopenharmony_ci		cap->capabilities |= V4L2_CAP_TUNER;
79962306a36Sopenharmony_ci	if (core->board.radio.type == CX88_RADIO)
80062306a36Sopenharmony_ci		cap->capabilities |= V4L2_CAP_RADIO;
80162306a36Sopenharmony_ci	return 0;
80262306a36Sopenharmony_ci}
80362306a36Sopenharmony_ciEXPORT_SYMBOL(cx88_querycap);
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_cistatic int vidioc_querycap(struct file *file, void  *priv,
80662306a36Sopenharmony_ci			   struct v4l2_capability *cap)
80762306a36Sopenharmony_ci{
80862306a36Sopenharmony_ci	struct cx8800_dev *dev = video_drvdata(file);
80962306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	strscpy(cap->driver, "cx8800", sizeof(cap->driver));
81262306a36Sopenharmony_ci	return cx88_querycap(file, core, cap);
81362306a36Sopenharmony_ci}
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_cistatic int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
81662306a36Sopenharmony_ci				   struct v4l2_fmtdesc *f)
81762306a36Sopenharmony_ci{
81862306a36Sopenharmony_ci	if (unlikely(f->index >= ARRAY_SIZE(formats)))
81962306a36Sopenharmony_ci		return -EINVAL;
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	f->pixelformat = formats[f->index].fourcc;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	return 0;
82462306a36Sopenharmony_ci}
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_cistatic int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorm)
82762306a36Sopenharmony_ci{
82862306a36Sopenharmony_ci	struct cx8800_dev *dev = video_drvdata(file);
82962306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	*tvnorm = core->tvnorm;
83262306a36Sopenharmony_ci	return 0;
83362306a36Sopenharmony_ci}
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_cistatic int vidioc_s_std(struct file *file, void *priv, v4l2_std_id tvnorms)
83662306a36Sopenharmony_ci{
83762306a36Sopenharmony_ci	struct cx8800_dev *dev = video_drvdata(file);
83862306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	return cx88_set_tvnorm(core, tvnorms);
84162306a36Sopenharmony_ci}
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci/* only one input in this sample driver */
84462306a36Sopenharmony_ciint cx88_enum_input(struct cx88_core  *core, struct v4l2_input *i)
84562306a36Sopenharmony_ci{
84662306a36Sopenharmony_ci	static const char * const iname[] = {
84762306a36Sopenharmony_ci		[CX88_VMUX_COMPOSITE1] = "Composite1",
84862306a36Sopenharmony_ci		[CX88_VMUX_COMPOSITE2] = "Composite2",
84962306a36Sopenharmony_ci		[CX88_VMUX_COMPOSITE3] = "Composite3",
85062306a36Sopenharmony_ci		[CX88_VMUX_COMPOSITE4] = "Composite4",
85162306a36Sopenharmony_ci		[CX88_VMUX_SVIDEO] = "S-Video",
85262306a36Sopenharmony_ci		[CX88_VMUX_TELEVISION] = "Television",
85362306a36Sopenharmony_ci		[CX88_VMUX_CABLE] = "Cable TV",
85462306a36Sopenharmony_ci		[CX88_VMUX_DVB] = "DVB",
85562306a36Sopenharmony_ci		[CX88_VMUX_DEBUG] = "for debug only",
85662306a36Sopenharmony_ci	};
85762306a36Sopenharmony_ci	unsigned int n = i->index;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	if (n >= 4)
86062306a36Sopenharmony_ci		return -EINVAL;
86162306a36Sopenharmony_ci	if (!INPUT(n).type)
86262306a36Sopenharmony_ci		return -EINVAL;
86362306a36Sopenharmony_ci	i->type  = V4L2_INPUT_TYPE_CAMERA;
86462306a36Sopenharmony_ci	strscpy(i->name, iname[INPUT(n).type], sizeof(i->name));
86562306a36Sopenharmony_ci	if ((INPUT(n).type == CX88_VMUX_TELEVISION) ||
86662306a36Sopenharmony_ci	    (INPUT(n).type == CX88_VMUX_CABLE))
86762306a36Sopenharmony_ci		i->type = V4L2_INPUT_TYPE_TUNER;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	i->std = CX88_NORMS;
87062306a36Sopenharmony_ci	return 0;
87162306a36Sopenharmony_ci}
87262306a36Sopenharmony_ciEXPORT_SYMBOL(cx88_enum_input);
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_cistatic int vidioc_enum_input(struct file *file, void *priv,
87562306a36Sopenharmony_ci			     struct v4l2_input *i)
87662306a36Sopenharmony_ci{
87762306a36Sopenharmony_ci	struct cx8800_dev *dev = video_drvdata(file);
87862306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	return cx88_enum_input(core, i);
88162306a36Sopenharmony_ci}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_cistatic int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
88462306a36Sopenharmony_ci{
88562306a36Sopenharmony_ci	struct cx8800_dev *dev = video_drvdata(file);
88662306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	*i = core->input;
88962306a36Sopenharmony_ci	return 0;
89062306a36Sopenharmony_ci}
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_cistatic int vidioc_s_input(struct file *file, void *priv, unsigned int i)
89362306a36Sopenharmony_ci{
89462306a36Sopenharmony_ci	struct cx8800_dev *dev = video_drvdata(file);
89562306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	if (i >= 4)
89862306a36Sopenharmony_ci		return -EINVAL;
89962306a36Sopenharmony_ci	if (!INPUT(i).type)
90062306a36Sopenharmony_ci		return -EINVAL;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	cx88_newstation(core);
90362306a36Sopenharmony_ci	cx88_video_mux(core, i);
90462306a36Sopenharmony_ci	return 0;
90562306a36Sopenharmony_ci}
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_cistatic int vidioc_g_tuner(struct file *file, void *priv,
90862306a36Sopenharmony_ci			  struct v4l2_tuner *t)
90962306a36Sopenharmony_ci{
91062306a36Sopenharmony_ci	struct cx8800_dev *dev = video_drvdata(file);
91162306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
91262306a36Sopenharmony_ci	u32 reg;
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	if (unlikely(core->board.tuner_type == UNSET))
91562306a36Sopenharmony_ci		return -EINVAL;
91662306a36Sopenharmony_ci	if (t->index != 0)
91762306a36Sopenharmony_ci		return -EINVAL;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	strscpy(t->name, "Television", sizeof(t->name));
92062306a36Sopenharmony_ci	t->capability = V4L2_TUNER_CAP_NORM;
92162306a36Sopenharmony_ci	t->rangehigh  = 0xffffffffUL;
92262306a36Sopenharmony_ci	call_all(core, tuner, g_tuner, t);
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	cx88_get_stereo(core, t);
92562306a36Sopenharmony_ci	reg = cx_read(MO_DEVICE_STATUS);
92662306a36Sopenharmony_ci	t->signal = (reg & (1 << 5)) ? 0xffff : 0x0000;
92762306a36Sopenharmony_ci	return 0;
92862306a36Sopenharmony_ci}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_cistatic int vidioc_s_tuner(struct file *file, void *priv,
93162306a36Sopenharmony_ci			  const struct v4l2_tuner *t)
93262306a36Sopenharmony_ci{
93362306a36Sopenharmony_ci	struct cx8800_dev *dev = video_drvdata(file);
93462306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci	if (core->board.tuner_type == UNSET)
93762306a36Sopenharmony_ci		return -EINVAL;
93862306a36Sopenharmony_ci	if (t->index != 0)
93962306a36Sopenharmony_ci		return -EINVAL;
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	cx88_set_stereo(core, t->audmode, 1);
94262306a36Sopenharmony_ci	return 0;
94362306a36Sopenharmony_ci}
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_cistatic int vidioc_g_frequency(struct file *file, void *priv,
94662306a36Sopenharmony_ci			      struct v4l2_frequency *f)
94762306a36Sopenharmony_ci{
94862306a36Sopenharmony_ci	struct cx8800_dev *dev = video_drvdata(file);
94962306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	if (unlikely(core->board.tuner_type == UNSET))
95262306a36Sopenharmony_ci		return -EINVAL;
95362306a36Sopenharmony_ci	if (f->tuner)
95462306a36Sopenharmony_ci		return -EINVAL;
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	f->frequency = core->freq;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	call_all(core, tuner, g_frequency, f);
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	return 0;
96162306a36Sopenharmony_ci}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ciint cx88_set_freq(struct cx88_core  *core,
96462306a36Sopenharmony_ci		  const struct v4l2_frequency *f)
96562306a36Sopenharmony_ci{
96662306a36Sopenharmony_ci	struct v4l2_frequency new_freq = *f;
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	if (unlikely(core->board.tuner_type == UNSET))
96962306a36Sopenharmony_ci		return -EINVAL;
97062306a36Sopenharmony_ci	if (unlikely(f->tuner != 0))
97162306a36Sopenharmony_ci		return -EINVAL;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	cx88_newstation(core);
97462306a36Sopenharmony_ci	call_all(core, tuner, s_frequency, f);
97562306a36Sopenharmony_ci	call_all(core, tuner, g_frequency, &new_freq);
97662306a36Sopenharmony_ci	core->freq = new_freq.frequency;
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_ci	/* When changing channels it is required to reset TVAUDIO */
97962306a36Sopenharmony_ci	usleep_range(10000, 20000);
98062306a36Sopenharmony_ci	cx88_set_tvaudio(core);
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	return 0;
98362306a36Sopenharmony_ci}
98462306a36Sopenharmony_ciEXPORT_SYMBOL(cx88_set_freq);
98562306a36Sopenharmony_ci
98662306a36Sopenharmony_cistatic int vidioc_s_frequency(struct file *file, void *priv,
98762306a36Sopenharmony_ci			      const struct v4l2_frequency *f)
98862306a36Sopenharmony_ci{
98962306a36Sopenharmony_ci	struct cx8800_dev *dev = video_drvdata(file);
99062306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	return cx88_set_freq(core, f);
99362306a36Sopenharmony_ci}
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG
99662306a36Sopenharmony_cistatic int vidioc_g_register(struct file *file, void *fh,
99762306a36Sopenharmony_ci			     struct v4l2_dbg_register *reg)
99862306a36Sopenharmony_ci{
99962306a36Sopenharmony_ci	struct cx8800_dev *dev = video_drvdata(file);
100062306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	/* cx2388x has a 24-bit register space */
100362306a36Sopenharmony_ci	reg->val = cx_read(reg->reg & 0xfffffc);
100462306a36Sopenharmony_ci	reg->size = 4;
100562306a36Sopenharmony_ci	return 0;
100662306a36Sopenharmony_ci}
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_cistatic int vidioc_s_register(struct file *file, void *fh,
100962306a36Sopenharmony_ci			     const struct v4l2_dbg_register *reg)
101062306a36Sopenharmony_ci{
101162306a36Sopenharmony_ci	struct cx8800_dev *dev = video_drvdata(file);
101262306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	cx_write(reg->reg & 0xfffffc, reg->val);
101562306a36Sopenharmony_ci	return 0;
101662306a36Sopenharmony_ci}
101762306a36Sopenharmony_ci#endif
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci/* ----------------------------------------------------------- */
102062306a36Sopenharmony_ci/* RADIO ESPECIFIC IOCTLS                                      */
102162306a36Sopenharmony_ci/* ----------------------------------------------------------- */
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_cistatic int radio_g_tuner(struct file *file, void *priv,
102462306a36Sopenharmony_ci			 struct v4l2_tuner *t)
102562306a36Sopenharmony_ci{
102662306a36Sopenharmony_ci	struct cx8800_dev *dev = video_drvdata(file);
102762306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	if (unlikely(t->index > 0))
103062306a36Sopenharmony_ci		return -EINVAL;
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	strscpy(t->name, "Radio", sizeof(t->name));
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	call_all(core, tuner, g_tuner, t);
103562306a36Sopenharmony_ci	return 0;
103662306a36Sopenharmony_ci}
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_cistatic int radio_s_tuner(struct file *file, void *priv,
103962306a36Sopenharmony_ci			 const struct v4l2_tuner *t)
104062306a36Sopenharmony_ci{
104162306a36Sopenharmony_ci	struct cx8800_dev *dev = video_drvdata(file);
104262306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	if (t->index != 0)
104562306a36Sopenharmony_ci		return -EINVAL;
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	call_all(core, tuner, s_tuner, t);
104862306a36Sopenharmony_ci	return 0;
104962306a36Sopenharmony_ci}
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci/* ----------------------------------------------------------- */
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_cistatic const char *cx88_vid_irqs[32] = {
105462306a36Sopenharmony_ci	"y_risci1", "u_risci1", "v_risci1", "vbi_risc1",
105562306a36Sopenharmony_ci	"y_risci2", "u_risci2", "v_risci2", "vbi_risc2",
105662306a36Sopenharmony_ci	"y_oflow",  "u_oflow",  "v_oflow",  "vbi_oflow",
105762306a36Sopenharmony_ci	"y_sync",   "u_sync",   "v_sync",   "vbi_sync",
105862306a36Sopenharmony_ci	"opc_err",  "par_err",  "rip_err",  "pci_abort",
105962306a36Sopenharmony_ci};
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_cistatic void cx8800_vid_irq(struct cx8800_dev *dev)
106262306a36Sopenharmony_ci{
106362306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
106462306a36Sopenharmony_ci	u32 status, mask, count;
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	status = cx_read(MO_VID_INTSTAT);
106762306a36Sopenharmony_ci	mask   = cx_read(MO_VID_INTMSK);
106862306a36Sopenharmony_ci	if (0 == (status & mask))
106962306a36Sopenharmony_ci		return;
107062306a36Sopenharmony_ci	cx_write(MO_VID_INTSTAT, status);
107162306a36Sopenharmony_ci	if (irq_debug  ||  (status & mask & ~0xff))
107262306a36Sopenharmony_ci		cx88_print_irqbits("irq vid",
107362306a36Sopenharmony_ci				   cx88_vid_irqs, ARRAY_SIZE(cx88_vid_irqs),
107462306a36Sopenharmony_ci				   status, mask);
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	/* risc op code error */
107762306a36Sopenharmony_ci	if (status & (1 << 16)) {
107862306a36Sopenharmony_ci		pr_warn("video risc op code error\n");
107962306a36Sopenharmony_ci		cx_clear(MO_VID_DMACNTRL, 0x11);
108062306a36Sopenharmony_ci		cx_clear(VID_CAPTURE_CONTROL, 0x06);
108162306a36Sopenharmony_ci		cx88_sram_channel_dump(core, &cx88_sram_channels[SRAM_CH21]);
108262306a36Sopenharmony_ci	}
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci	/* risc1 y */
108562306a36Sopenharmony_ci	if (status & 0x01) {
108662306a36Sopenharmony_ci		spin_lock(&dev->slock);
108762306a36Sopenharmony_ci		count = cx_read(MO_VIDY_GPCNT);
108862306a36Sopenharmony_ci		cx88_wakeup(core, &dev->vidq, count);
108962306a36Sopenharmony_ci		spin_unlock(&dev->slock);
109062306a36Sopenharmony_ci	}
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	/* risc1 vbi */
109362306a36Sopenharmony_ci	if (status & 0x08) {
109462306a36Sopenharmony_ci		spin_lock(&dev->slock);
109562306a36Sopenharmony_ci		count = cx_read(MO_VBI_GPCNT);
109662306a36Sopenharmony_ci		cx88_wakeup(core, &dev->vbiq, count);
109762306a36Sopenharmony_ci		spin_unlock(&dev->slock);
109862306a36Sopenharmony_ci	}
109962306a36Sopenharmony_ci}
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_cistatic irqreturn_t cx8800_irq(int irq, void *dev_id)
110262306a36Sopenharmony_ci{
110362306a36Sopenharmony_ci	struct cx8800_dev *dev = dev_id;
110462306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
110562306a36Sopenharmony_ci	u32 status;
110662306a36Sopenharmony_ci	int loop, handled = 0;
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	for (loop = 0; loop < 10; loop++) {
110962306a36Sopenharmony_ci		status = cx_read(MO_PCI_INTSTAT) &
111062306a36Sopenharmony_ci			(core->pci_irqmask | PCI_INT_VIDINT);
111162306a36Sopenharmony_ci		if (status == 0)
111262306a36Sopenharmony_ci			goto out;
111362306a36Sopenharmony_ci		cx_write(MO_PCI_INTSTAT, status);
111462306a36Sopenharmony_ci		handled = 1;
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci		if (status & core->pci_irqmask)
111762306a36Sopenharmony_ci			cx88_core_irq(core, status);
111862306a36Sopenharmony_ci		if (status & PCI_INT_VIDINT)
111962306a36Sopenharmony_ci			cx8800_vid_irq(dev);
112062306a36Sopenharmony_ci	}
112162306a36Sopenharmony_ci	if (loop == 10) {
112262306a36Sopenharmony_ci		pr_warn("irq loop -- clearing mask\n");
112362306a36Sopenharmony_ci		cx_write(MO_PCI_INTMSK, 0);
112462306a36Sopenharmony_ci	}
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci out:
112762306a36Sopenharmony_ci	return IRQ_RETVAL(handled);
112862306a36Sopenharmony_ci}
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci/* ----------------------------------------------------------- */
113162306a36Sopenharmony_ci/* exported stuff                                              */
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_cistatic const struct v4l2_file_operations video_fops = {
113462306a36Sopenharmony_ci	.owner	       = THIS_MODULE,
113562306a36Sopenharmony_ci	.open	       = v4l2_fh_open,
113662306a36Sopenharmony_ci	.release       = vb2_fop_release,
113762306a36Sopenharmony_ci	.read	       = vb2_fop_read,
113862306a36Sopenharmony_ci	.poll          = vb2_fop_poll,
113962306a36Sopenharmony_ci	.mmap	       = vb2_fop_mmap,
114062306a36Sopenharmony_ci	.unlocked_ioctl = video_ioctl2,
114162306a36Sopenharmony_ci};
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops video_ioctl_ops = {
114462306a36Sopenharmony_ci	.vidioc_querycap      = vidioc_querycap,
114562306a36Sopenharmony_ci	.vidioc_enum_fmt_vid_cap  = vidioc_enum_fmt_vid_cap,
114662306a36Sopenharmony_ci	.vidioc_g_fmt_vid_cap     = vidioc_g_fmt_vid_cap,
114762306a36Sopenharmony_ci	.vidioc_try_fmt_vid_cap   = vidioc_try_fmt_vid_cap,
114862306a36Sopenharmony_ci	.vidioc_s_fmt_vid_cap     = vidioc_s_fmt_vid_cap,
114962306a36Sopenharmony_ci	.vidioc_reqbufs       = vb2_ioctl_reqbufs,
115062306a36Sopenharmony_ci	.vidioc_querybuf      = vb2_ioctl_querybuf,
115162306a36Sopenharmony_ci	.vidioc_qbuf          = vb2_ioctl_qbuf,
115262306a36Sopenharmony_ci	.vidioc_dqbuf         = vb2_ioctl_dqbuf,
115362306a36Sopenharmony_ci	.vidioc_g_std         = vidioc_g_std,
115462306a36Sopenharmony_ci	.vidioc_s_std         = vidioc_s_std,
115562306a36Sopenharmony_ci	.vidioc_enum_input    = vidioc_enum_input,
115662306a36Sopenharmony_ci	.vidioc_g_input       = vidioc_g_input,
115762306a36Sopenharmony_ci	.vidioc_s_input       = vidioc_s_input,
115862306a36Sopenharmony_ci	.vidioc_streamon      = vb2_ioctl_streamon,
115962306a36Sopenharmony_ci	.vidioc_streamoff     = vb2_ioctl_streamoff,
116062306a36Sopenharmony_ci	.vidioc_g_tuner       = vidioc_g_tuner,
116162306a36Sopenharmony_ci	.vidioc_s_tuner       = vidioc_s_tuner,
116262306a36Sopenharmony_ci	.vidioc_g_frequency   = vidioc_g_frequency,
116362306a36Sopenharmony_ci	.vidioc_s_frequency   = vidioc_s_frequency,
116462306a36Sopenharmony_ci	.vidioc_subscribe_event      = v4l2_ctrl_subscribe_event,
116562306a36Sopenharmony_ci	.vidioc_unsubscribe_event    = v4l2_event_unsubscribe,
116662306a36Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG
116762306a36Sopenharmony_ci	.vidioc_g_register    = vidioc_g_register,
116862306a36Sopenharmony_ci	.vidioc_s_register    = vidioc_s_register,
116962306a36Sopenharmony_ci#endif
117062306a36Sopenharmony_ci};
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_cistatic const struct video_device cx8800_video_template = {
117362306a36Sopenharmony_ci	.name                 = "cx8800-video",
117462306a36Sopenharmony_ci	.fops                 = &video_fops,
117562306a36Sopenharmony_ci	.ioctl_ops	      = &video_ioctl_ops,
117662306a36Sopenharmony_ci	.tvnorms              = CX88_NORMS,
117762306a36Sopenharmony_ci};
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops vbi_ioctl_ops = {
118062306a36Sopenharmony_ci	.vidioc_querycap      = vidioc_querycap,
118162306a36Sopenharmony_ci	.vidioc_g_fmt_vbi_cap     = cx8800_vbi_fmt,
118262306a36Sopenharmony_ci	.vidioc_try_fmt_vbi_cap   = cx8800_vbi_fmt,
118362306a36Sopenharmony_ci	.vidioc_s_fmt_vbi_cap     = cx8800_vbi_fmt,
118462306a36Sopenharmony_ci	.vidioc_reqbufs       = vb2_ioctl_reqbufs,
118562306a36Sopenharmony_ci	.vidioc_querybuf      = vb2_ioctl_querybuf,
118662306a36Sopenharmony_ci	.vidioc_qbuf          = vb2_ioctl_qbuf,
118762306a36Sopenharmony_ci	.vidioc_dqbuf         = vb2_ioctl_dqbuf,
118862306a36Sopenharmony_ci	.vidioc_g_std         = vidioc_g_std,
118962306a36Sopenharmony_ci	.vidioc_s_std         = vidioc_s_std,
119062306a36Sopenharmony_ci	.vidioc_enum_input    = vidioc_enum_input,
119162306a36Sopenharmony_ci	.vidioc_g_input       = vidioc_g_input,
119262306a36Sopenharmony_ci	.vidioc_s_input       = vidioc_s_input,
119362306a36Sopenharmony_ci	.vidioc_streamon      = vb2_ioctl_streamon,
119462306a36Sopenharmony_ci	.vidioc_streamoff     = vb2_ioctl_streamoff,
119562306a36Sopenharmony_ci	.vidioc_g_tuner       = vidioc_g_tuner,
119662306a36Sopenharmony_ci	.vidioc_s_tuner       = vidioc_s_tuner,
119762306a36Sopenharmony_ci	.vidioc_g_frequency   = vidioc_g_frequency,
119862306a36Sopenharmony_ci	.vidioc_s_frequency   = vidioc_s_frequency,
119962306a36Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG
120062306a36Sopenharmony_ci	.vidioc_g_register    = vidioc_g_register,
120162306a36Sopenharmony_ci	.vidioc_s_register    = vidioc_s_register,
120262306a36Sopenharmony_ci#endif
120362306a36Sopenharmony_ci};
120462306a36Sopenharmony_ci
120562306a36Sopenharmony_cistatic const struct video_device cx8800_vbi_template = {
120662306a36Sopenharmony_ci	.name                 = "cx8800-vbi",
120762306a36Sopenharmony_ci	.fops                 = &video_fops,
120862306a36Sopenharmony_ci	.ioctl_ops	      = &vbi_ioctl_ops,
120962306a36Sopenharmony_ci	.tvnorms              = CX88_NORMS,
121062306a36Sopenharmony_ci};
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_cistatic const struct v4l2_file_operations radio_fops = {
121362306a36Sopenharmony_ci	.owner         = THIS_MODULE,
121462306a36Sopenharmony_ci	.open          = radio_open,
121562306a36Sopenharmony_ci	.poll          = v4l2_ctrl_poll,
121662306a36Sopenharmony_ci	.release       = v4l2_fh_release,
121762306a36Sopenharmony_ci	.unlocked_ioctl = video_ioctl2,
121862306a36Sopenharmony_ci};
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops radio_ioctl_ops = {
122162306a36Sopenharmony_ci	.vidioc_querycap      = vidioc_querycap,
122262306a36Sopenharmony_ci	.vidioc_g_tuner       = radio_g_tuner,
122362306a36Sopenharmony_ci	.vidioc_s_tuner       = radio_s_tuner,
122462306a36Sopenharmony_ci	.vidioc_g_frequency   = vidioc_g_frequency,
122562306a36Sopenharmony_ci	.vidioc_s_frequency   = vidioc_s_frequency,
122662306a36Sopenharmony_ci	.vidioc_subscribe_event      = v4l2_ctrl_subscribe_event,
122762306a36Sopenharmony_ci	.vidioc_unsubscribe_event    = v4l2_event_unsubscribe,
122862306a36Sopenharmony_ci#ifdef CONFIG_VIDEO_ADV_DEBUG
122962306a36Sopenharmony_ci	.vidioc_g_register    = vidioc_g_register,
123062306a36Sopenharmony_ci	.vidioc_s_register    = vidioc_s_register,
123162306a36Sopenharmony_ci#endif
123262306a36Sopenharmony_ci};
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_cistatic const struct video_device cx8800_radio_template = {
123562306a36Sopenharmony_ci	.name                 = "cx8800-radio",
123662306a36Sopenharmony_ci	.fops                 = &radio_fops,
123762306a36Sopenharmony_ci	.ioctl_ops	      = &radio_ioctl_ops,
123862306a36Sopenharmony_ci};
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops cx8800_ctrl_vid_ops = {
124162306a36Sopenharmony_ci	.s_ctrl = cx8800_s_vid_ctrl,
124262306a36Sopenharmony_ci};
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops cx8800_ctrl_aud_ops = {
124562306a36Sopenharmony_ci	.s_ctrl = cx8800_s_aud_ctrl,
124662306a36Sopenharmony_ci};
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci/* ----------------------------------------------------------- */
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_cistatic void cx8800_unregister_video(struct cx8800_dev *dev)
125162306a36Sopenharmony_ci{
125262306a36Sopenharmony_ci	video_unregister_device(&dev->radio_dev);
125362306a36Sopenharmony_ci	video_unregister_device(&dev->vbi_dev);
125462306a36Sopenharmony_ci	video_unregister_device(&dev->video_dev);
125562306a36Sopenharmony_ci}
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_cistatic int cx8800_initdev(struct pci_dev *pci_dev,
125862306a36Sopenharmony_ci			  const struct pci_device_id *pci_id)
125962306a36Sopenharmony_ci{
126062306a36Sopenharmony_ci	struct cx8800_dev *dev;
126162306a36Sopenharmony_ci	struct cx88_core *core;
126262306a36Sopenharmony_ci	struct vb2_queue *q;
126362306a36Sopenharmony_ci	int err;
126462306a36Sopenharmony_ci	int i;
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
126762306a36Sopenharmony_ci	if (!dev)
126862306a36Sopenharmony_ci		return -ENOMEM;
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	/* pci init */
127162306a36Sopenharmony_ci	dev->pci = pci_dev;
127262306a36Sopenharmony_ci	if (pci_enable_device(pci_dev)) {
127362306a36Sopenharmony_ci		err = -EIO;
127462306a36Sopenharmony_ci		goto fail_free;
127562306a36Sopenharmony_ci	}
127662306a36Sopenharmony_ci	core = cx88_core_get(dev->pci);
127762306a36Sopenharmony_ci	if (!core) {
127862306a36Sopenharmony_ci		err = -EINVAL;
127962306a36Sopenharmony_ci		goto fail_disable;
128062306a36Sopenharmony_ci	}
128162306a36Sopenharmony_ci	dev->core = core;
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	/* print pci info */
128462306a36Sopenharmony_ci	dev->pci_rev = pci_dev->revision;
128562306a36Sopenharmony_ci	pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER,  &dev->pci_lat);
128662306a36Sopenharmony_ci	pr_info("found at %s, rev: %d, irq: %d, latency: %d, mmio: 0x%llx\n",
128762306a36Sopenharmony_ci		pci_name(pci_dev), dev->pci_rev, pci_dev->irq,
128862306a36Sopenharmony_ci		dev->pci_lat,
128962306a36Sopenharmony_ci		(unsigned long long)pci_resource_start(pci_dev, 0));
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	pci_set_master(pci_dev);
129262306a36Sopenharmony_ci	err = dma_set_mask(&pci_dev->dev, DMA_BIT_MASK(32));
129362306a36Sopenharmony_ci	if (err) {
129462306a36Sopenharmony_ci		pr_err("Oops: no 32bit PCI DMA ???\n");
129562306a36Sopenharmony_ci		goto fail_core;
129662306a36Sopenharmony_ci	}
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	/* initialize driver struct */
129962306a36Sopenharmony_ci	spin_lock_init(&dev->slock);
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	/* init video dma queues */
130262306a36Sopenharmony_ci	INIT_LIST_HEAD(&dev->vidq.active);
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci	/* init vbi dma queues */
130562306a36Sopenharmony_ci	INIT_LIST_HEAD(&dev->vbiq.active);
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	/* get irq */
130862306a36Sopenharmony_ci	err = request_irq(pci_dev->irq, cx8800_irq,
130962306a36Sopenharmony_ci			  IRQF_SHARED, core->name, dev);
131062306a36Sopenharmony_ci	if (err < 0) {
131162306a36Sopenharmony_ci		pr_err("can't get IRQ %d\n", pci_dev->irq);
131262306a36Sopenharmony_ci		goto fail_core;
131362306a36Sopenharmony_ci	}
131462306a36Sopenharmony_ci	cx_set(MO_PCI_INTMSK, core->pci_irqmask);
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	for (i = 0; i < CX8800_AUD_CTLS; i++) {
131762306a36Sopenharmony_ci		const struct cx88_ctrl *cc = &cx8800_aud_ctls[i];
131862306a36Sopenharmony_ci		struct v4l2_ctrl *vc;
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci		vc = v4l2_ctrl_new_std(&core->audio_hdl, &cx8800_ctrl_aud_ops,
132162306a36Sopenharmony_ci				       cc->id, cc->minimum, cc->maximum,
132262306a36Sopenharmony_ci				       cc->step, cc->default_value);
132362306a36Sopenharmony_ci		if (!vc) {
132462306a36Sopenharmony_ci			err = core->audio_hdl.error;
132562306a36Sopenharmony_ci			goto fail_irq;
132662306a36Sopenharmony_ci		}
132762306a36Sopenharmony_ci		vc->priv = (void *)cc;
132862306a36Sopenharmony_ci	}
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	for (i = 0; i < CX8800_VID_CTLS; i++) {
133162306a36Sopenharmony_ci		const struct cx88_ctrl *cc = &cx8800_vid_ctls[i];
133262306a36Sopenharmony_ci		struct v4l2_ctrl *vc;
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci		vc = v4l2_ctrl_new_std(&core->video_hdl, &cx8800_ctrl_vid_ops,
133562306a36Sopenharmony_ci				       cc->id, cc->minimum, cc->maximum,
133662306a36Sopenharmony_ci				       cc->step, cc->default_value);
133762306a36Sopenharmony_ci		if (!vc) {
133862306a36Sopenharmony_ci			err = core->video_hdl.error;
133962306a36Sopenharmony_ci			goto fail_irq;
134062306a36Sopenharmony_ci		}
134162306a36Sopenharmony_ci		vc->priv = (void *)cc;
134262306a36Sopenharmony_ci		if (vc->id == V4L2_CID_CHROMA_AGC)
134362306a36Sopenharmony_ci			core->chroma_agc = vc;
134462306a36Sopenharmony_ci	}
134562306a36Sopenharmony_ci	v4l2_ctrl_add_handler(&core->video_hdl, &core->audio_hdl, NULL, false);
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	/* load and configure helper modules */
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	if (core->board.audio_chip == CX88_AUDIO_WM8775) {
135062306a36Sopenharmony_ci		struct i2c_board_info wm8775_info = {
135162306a36Sopenharmony_ci			.type = "wm8775",
135262306a36Sopenharmony_ci			.addr = 0x36 >> 1,
135362306a36Sopenharmony_ci			.platform_data = &core->wm8775_data,
135462306a36Sopenharmony_ci		};
135562306a36Sopenharmony_ci		struct v4l2_subdev *sd;
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci		if (core->boardnr == CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1)
135862306a36Sopenharmony_ci			core->wm8775_data.is_nova_s = true;
135962306a36Sopenharmony_ci		else
136062306a36Sopenharmony_ci			core->wm8775_data.is_nova_s = false;
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci		sd = v4l2_i2c_new_subdev_board(&core->v4l2_dev, &core->i2c_adap,
136362306a36Sopenharmony_ci					       &wm8775_info, NULL);
136462306a36Sopenharmony_ci		if (sd) {
136562306a36Sopenharmony_ci			core->sd_wm8775 = sd;
136662306a36Sopenharmony_ci			sd->grp_id = WM8775_GID;
136762306a36Sopenharmony_ci		}
136862306a36Sopenharmony_ci	}
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci	if (core->board.audio_chip == CX88_AUDIO_TVAUDIO) {
137162306a36Sopenharmony_ci		/*
137262306a36Sopenharmony_ci		 * This probes for a tda9874 as is used on some
137362306a36Sopenharmony_ci		 * Pixelview Ultra boards.
137462306a36Sopenharmony_ci		 */
137562306a36Sopenharmony_ci		v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap,
137662306a36Sopenharmony_ci				    "tvaudio", 0, I2C_ADDRS(0xb0 >> 1));
137762306a36Sopenharmony_ci	}
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	switch (core->boardnr) {
138062306a36Sopenharmony_ci	case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD:
138162306a36Sopenharmony_ci	case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD: {
138262306a36Sopenharmony_ci		static const struct i2c_board_info rtc_info = {
138362306a36Sopenharmony_ci			I2C_BOARD_INFO("isl1208", 0x6f)
138462306a36Sopenharmony_ci		};
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci		request_module("rtc-isl1208");
138762306a36Sopenharmony_ci		core->i2c_rtc = i2c_new_client_device(&core->i2c_adap, &rtc_info);
138862306a36Sopenharmony_ci	}
138962306a36Sopenharmony_ci		fallthrough;
139062306a36Sopenharmony_ci	case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
139162306a36Sopenharmony_ci	case CX88_BOARD_NOTONLYTV_LV3H:
139262306a36Sopenharmony_ci		request_module("ir-kbd-i2c");
139362306a36Sopenharmony_ci	}
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	/* Sets device info at pci_dev */
139662306a36Sopenharmony_ci	pci_set_drvdata(pci_dev, dev);
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci	dev->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	/* Maintain a reference so cx88-blackbird can query the 8800 device. */
140162306a36Sopenharmony_ci	core->v4ldev = dev;
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci	/* initial device configuration */
140462306a36Sopenharmony_ci	mutex_lock(&core->lock);
140562306a36Sopenharmony_ci	cx88_set_tvnorm(core, V4L2_STD_NTSC_M);
140662306a36Sopenharmony_ci	v4l2_ctrl_handler_setup(&core->video_hdl);
140762306a36Sopenharmony_ci	v4l2_ctrl_handler_setup(&core->audio_hdl);
140862306a36Sopenharmony_ci	cx88_video_mux(core, 0);
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	q = &dev->vb2_vidq;
141162306a36Sopenharmony_ci	q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
141262306a36Sopenharmony_ci	q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
141362306a36Sopenharmony_ci	q->gfp_flags = GFP_DMA32;
141462306a36Sopenharmony_ci	q->min_buffers_needed = 2;
141562306a36Sopenharmony_ci	q->drv_priv = dev;
141662306a36Sopenharmony_ci	q->buf_struct_size = sizeof(struct cx88_buffer);
141762306a36Sopenharmony_ci	q->ops = &cx8800_video_qops;
141862306a36Sopenharmony_ci	q->mem_ops = &vb2_dma_sg_memops;
141962306a36Sopenharmony_ci	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
142062306a36Sopenharmony_ci	q->lock = &core->lock;
142162306a36Sopenharmony_ci	q->dev = &dev->pci->dev;
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	err = vb2_queue_init(q);
142462306a36Sopenharmony_ci	if (err < 0)
142562306a36Sopenharmony_ci		goto fail_unreg;
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	q = &dev->vb2_vbiq;
142862306a36Sopenharmony_ci	q->type = V4L2_BUF_TYPE_VBI_CAPTURE;
142962306a36Sopenharmony_ci	q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
143062306a36Sopenharmony_ci	q->gfp_flags = GFP_DMA32;
143162306a36Sopenharmony_ci	q->min_buffers_needed = 2;
143262306a36Sopenharmony_ci	q->drv_priv = dev;
143362306a36Sopenharmony_ci	q->buf_struct_size = sizeof(struct cx88_buffer);
143462306a36Sopenharmony_ci	q->ops = &cx8800_vbi_qops;
143562306a36Sopenharmony_ci	q->mem_ops = &vb2_dma_sg_memops;
143662306a36Sopenharmony_ci	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
143762306a36Sopenharmony_ci	q->lock = &core->lock;
143862306a36Sopenharmony_ci	q->dev = &dev->pci->dev;
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci	err = vb2_queue_init(q);
144162306a36Sopenharmony_ci	if (err < 0)
144262306a36Sopenharmony_ci		goto fail_unreg;
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	/* register v4l devices */
144562306a36Sopenharmony_ci	cx88_vdev_init(core, dev->pci, &dev->video_dev,
144662306a36Sopenharmony_ci		       &cx8800_video_template, "video");
144762306a36Sopenharmony_ci	video_set_drvdata(&dev->video_dev, dev);
144862306a36Sopenharmony_ci	dev->video_dev.ctrl_handler = &core->video_hdl;
144962306a36Sopenharmony_ci	dev->video_dev.queue = &dev->vb2_vidq;
145062306a36Sopenharmony_ci	dev->video_dev.device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING |
145162306a36Sopenharmony_ci				     V4L2_CAP_VIDEO_CAPTURE;
145262306a36Sopenharmony_ci	if (core->board.tuner_type != UNSET)
145362306a36Sopenharmony_ci		dev->video_dev.device_caps |= V4L2_CAP_TUNER;
145462306a36Sopenharmony_ci	err = video_register_device(&dev->video_dev, VFL_TYPE_VIDEO,
145562306a36Sopenharmony_ci				    video_nr[core->nr]);
145662306a36Sopenharmony_ci	if (err < 0) {
145762306a36Sopenharmony_ci		pr_err("can't register video device\n");
145862306a36Sopenharmony_ci		goto fail_unreg;
145962306a36Sopenharmony_ci	}
146062306a36Sopenharmony_ci	pr_info("registered device %s [v4l2]\n",
146162306a36Sopenharmony_ci		video_device_node_name(&dev->video_dev));
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	cx88_vdev_init(core, dev->pci, &dev->vbi_dev,
146462306a36Sopenharmony_ci		       &cx8800_vbi_template, "vbi");
146562306a36Sopenharmony_ci	video_set_drvdata(&dev->vbi_dev, dev);
146662306a36Sopenharmony_ci	dev->vbi_dev.queue = &dev->vb2_vbiq;
146762306a36Sopenharmony_ci	dev->vbi_dev.device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING |
146862306a36Sopenharmony_ci				   V4L2_CAP_VBI_CAPTURE;
146962306a36Sopenharmony_ci	if (core->board.tuner_type != UNSET)
147062306a36Sopenharmony_ci		dev->vbi_dev.device_caps |= V4L2_CAP_TUNER;
147162306a36Sopenharmony_ci	err = video_register_device(&dev->vbi_dev, VFL_TYPE_VBI,
147262306a36Sopenharmony_ci				    vbi_nr[core->nr]);
147362306a36Sopenharmony_ci	if (err < 0) {
147462306a36Sopenharmony_ci		pr_err("can't register vbi device\n");
147562306a36Sopenharmony_ci		goto fail_unreg;
147662306a36Sopenharmony_ci	}
147762306a36Sopenharmony_ci	pr_info("registered device %s\n",
147862306a36Sopenharmony_ci		video_device_node_name(&dev->vbi_dev));
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci	if (core->board.radio.type == CX88_RADIO) {
148162306a36Sopenharmony_ci		cx88_vdev_init(core, dev->pci, &dev->radio_dev,
148262306a36Sopenharmony_ci			       &cx8800_radio_template, "radio");
148362306a36Sopenharmony_ci		video_set_drvdata(&dev->radio_dev, dev);
148462306a36Sopenharmony_ci		dev->radio_dev.ctrl_handler = &core->audio_hdl;
148562306a36Sopenharmony_ci		dev->radio_dev.device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER;
148662306a36Sopenharmony_ci		err = video_register_device(&dev->radio_dev, VFL_TYPE_RADIO,
148762306a36Sopenharmony_ci					    radio_nr[core->nr]);
148862306a36Sopenharmony_ci		if (err < 0) {
148962306a36Sopenharmony_ci			pr_err("can't register radio device\n");
149062306a36Sopenharmony_ci			goto fail_unreg;
149162306a36Sopenharmony_ci		}
149262306a36Sopenharmony_ci		pr_info("registered device %s\n",
149362306a36Sopenharmony_ci			video_device_node_name(&dev->radio_dev));
149462306a36Sopenharmony_ci	}
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	/* start tvaudio thread */
149762306a36Sopenharmony_ci	if (core->board.tuner_type != UNSET) {
149862306a36Sopenharmony_ci		core->kthread = kthread_run(cx88_audio_thread,
149962306a36Sopenharmony_ci					    core, "cx88 tvaudio");
150062306a36Sopenharmony_ci		if (IS_ERR(core->kthread)) {
150162306a36Sopenharmony_ci			err = PTR_ERR(core->kthread);
150262306a36Sopenharmony_ci			pr_err("failed to create cx88 audio thread, err=%d\n",
150362306a36Sopenharmony_ci			       err);
150462306a36Sopenharmony_ci		}
150562306a36Sopenharmony_ci	}
150662306a36Sopenharmony_ci	mutex_unlock(&core->lock);
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	return 0;
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_cifail_unreg:
151162306a36Sopenharmony_ci	cx8800_unregister_video(dev);
151262306a36Sopenharmony_ci	mutex_unlock(&core->lock);
151362306a36Sopenharmony_cifail_irq:
151462306a36Sopenharmony_ci	free_irq(pci_dev->irq, dev);
151562306a36Sopenharmony_cifail_core:
151662306a36Sopenharmony_ci	core->v4ldev = NULL;
151762306a36Sopenharmony_ci	cx88_core_put(core, dev->pci);
151862306a36Sopenharmony_cifail_disable:
151962306a36Sopenharmony_ci	pci_disable_device(pci_dev);
152062306a36Sopenharmony_cifail_free:
152162306a36Sopenharmony_ci	kfree(dev);
152262306a36Sopenharmony_ci	return err;
152362306a36Sopenharmony_ci}
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_cistatic void cx8800_finidev(struct pci_dev *pci_dev)
152662306a36Sopenharmony_ci{
152762306a36Sopenharmony_ci	struct cx8800_dev *dev = pci_get_drvdata(pci_dev);
152862306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
152962306a36Sopenharmony_ci
153062306a36Sopenharmony_ci	/* stop thread */
153162306a36Sopenharmony_ci	if (core->kthread) {
153262306a36Sopenharmony_ci		kthread_stop(core->kthread);
153362306a36Sopenharmony_ci		core->kthread = NULL;
153462306a36Sopenharmony_ci	}
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci	if (core->ir)
153762306a36Sopenharmony_ci		cx88_ir_stop(core);
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci	cx88_shutdown(core); /* FIXME */
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	/* unregister stuff */
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci	free_irq(pci_dev->irq, dev);
154462306a36Sopenharmony_ci	cx8800_unregister_video(dev);
154562306a36Sopenharmony_ci	pci_disable_device(pci_dev);
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci	core->v4ldev = NULL;
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	/* free memory */
155062306a36Sopenharmony_ci	cx88_core_put(core, dev->pci);
155162306a36Sopenharmony_ci	kfree(dev);
155262306a36Sopenharmony_ci}
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_cistatic int __maybe_unused cx8800_suspend(struct device *dev_d)
155562306a36Sopenharmony_ci{
155662306a36Sopenharmony_ci	struct cx8800_dev *dev = dev_get_drvdata(dev_d);
155762306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
155862306a36Sopenharmony_ci	unsigned long flags;
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci	/* stop video+vbi capture */
156162306a36Sopenharmony_ci	spin_lock_irqsave(&dev->slock, flags);
156262306a36Sopenharmony_ci	if (!list_empty(&dev->vidq.active)) {
156362306a36Sopenharmony_ci		pr_info("suspend video\n");
156462306a36Sopenharmony_ci		stop_video_dma(dev);
156562306a36Sopenharmony_ci	}
156662306a36Sopenharmony_ci	if (!list_empty(&dev->vbiq.active)) {
156762306a36Sopenharmony_ci		pr_info("suspend vbi\n");
156862306a36Sopenharmony_ci		cx8800_stop_vbi_dma(dev);
156962306a36Sopenharmony_ci	}
157062306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->slock, flags);
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci	if (core->ir)
157362306a36Sopenharmony_ci		cx88_ir_stop(core);
157462306a36Sopenharmony_ci	/* FIXME -- shutdown device */
157562306a36Sopenharmony_ci	cx88_shutdown(core);
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci	dev->state.disabled = 1;
157862306a36Sopenharmony_ci	return 0;
157962306a36Sopenharmony_ci}
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_cistatic int __maybe_unused cx8800_resume(struct device *dev_d)
158262306a36Sopenharmony_ci{
158362306a36Sopenharmony_ci	struct cx8800_dev *dev = dev_get_drvdata(dev_d);
158462306a36Sopenharmony_ci	struct cx88_core *core = dev->core;
158562306a36Sopenharmony_ci	unsigned long flags;
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	dev->state.disabled = 0;
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	/* FIXME: re-initialize hardware */
159062306a36Sopenharmony_ci	cx88_reset(core);
159162306a36Sopenharmony_ci	if (core->ir)
159262306a36Sopenharmony_ci		cx88_ir_start(core);
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci	cx_set(MO_PCI_INTMSK, core->pci_irqmask);
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	/* restart video+vbi capture */
159762306a36Sopenharmony_ci	spin_lock_irqsave(&dev->slock, flags);
159862306a36Sopenharmony_ci	if (!list_empty(&dev->vidq.active)) {
159962306a36Sopenharmony_ci		pr_info("resume video\n");
160062306a36Sopenharmony_ci		restart_video_queue(dev, &dev->vidq);
160162306a36Sopenharmony_ci	}
160262306a36Sopenharmony_ci	if (!list_empty(&dev->vbiq.active)) {
160362306a36Sopenharmony_ci		pr_info("resume vbi\n");
160462306a36Sopenharmony_ci		cx8800_restart_vbi_queue(dev, &dev->vbiq);
160562306a36Sopenharmony_ci	}
160662306a36Sopenharmony_ci	spin_unlock_irqrestore(&dev->slock, flags);
160762306a36Sopenharmony_ci
160862306a36Sopenharmony_ci	return 0;
160962306a36Sopenharmony_ci}
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci/* ----------------------------------------------------------- */
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_cistatic const struct pci_device_id cx8800_pci_tbl[] = {
161462306a36Sopenharmony_ci	{
161562306a36Sopenharmony_ci		.vendor       = 0x14f1,
161662306a36Sopenharmony_ci		.device       = 0x8800,
161762306a36Sopenharmony_ci		.subvendor    = PCI_ANY_ID,
161862306a36Sopenharmony_ci		.subdevice    = PCI_ANY_ID,
161962306a36Sopenharmony_ci	}, {
162062306a36Sopenharmony_ci		/* --- end of list --- */
162162306a36Sopenharmony_ci	}
162262306a36Sopenharmony_ci};
162362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, cx8800_pci_tbl);
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(cx8800_pm_ops, cx8800_suspend, cx8800_resume);
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_cistatic struct pci_driver cx8800_pci_driver = {
162862306a36Sopenharmony_ci	.name      = "cx8800",
162962306a36Sopenharmony_ci	.id_table  = cx8800_pci_tbl,
163062306a36Sopenharmony_ci	.probe     = cx8800_initdev,
163162306a36Sopenharmony_ci	.remove    = cx8800_finidev,
163262306a36Sopenharmony_ci	.driver.pm = &cx8800_pm_ops,
163362306a36Sopenharmony_ci};
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_cimodule_pci_driver(cx8800_pci_driver);
1636