1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * device driver for Conexant 2388x based TV cards
5*4882a593Smuzhiyun * video4linux video interface
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * (c) 2003-04 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
8*4882a593Smuzhiyun *
9*4882a593Smuzhiyun * (c) 2005-2006 Mauro Carvalho Chehab <mchehab@kernel.org>
10*4882a593Smuzhiyun * - Multituner support
11*4882a593Smuzhiyun * - video_ioctl2 conversion
12*4882a593Smuzhiyun * - PAL/M fixes
13*4882a593Smuzhiyun */
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun #include "cx88.h"
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun #include <linux/init.h>
18*4882a593Smuzhiyun #include <linux/list.h>
19*4882a593Smuzhiyun #include <linux/module.h>
20*4882a593Smuzhiyun #include <linux/kmod.h>
21*4882a593Smuzhiyun #include <linux/kernel.h>
22*4882a593Smuzhiyun #include <linux/slab.h>
23*4882a593Smuzhiyun #include <linux/interrupt.h>
24*4882a593Smuzhiyun #include <linux/dma-mapping.h>
25*4882a593Smuzhiyun #include <linux/delay.h>
26*4882a593Smuzhiyun #include <linux/kthread.h>
27*4882a593Smuzhiyun #include <asm/div64.h>
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun #include <media/v4l2-common.h>
30*4882a593Smuzhiyun #include <media/v4l2-ioctl.h>
31*4882a593Smuzhiyun #include <media/v4l2-event.h>
32*4882a593Smuzhiyun #include <media/i2c/wm8775.h>
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards");
35*4882a593Smuzhiyun MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
36*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
37*4882a593Smuzhiyun MODULE_VERSION(CX88_VERSION);
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun /* ------------------------------------------------------------------ */
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun static unsigned int video_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
42*4882a593Smuzhiyun static unsigned int vbi_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
43*4882a593Smuzhiyun static unsigned int radio_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun module_param_array(video_nr, int, NULL, 0444);
46*4882a593Smuzhiyun module_param_array(vbi_nr, int, NULL, 0444);
47*4882a593Smuzhiyun module_param_array(radio_nr, int, NULL, 0444);
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun MODULE_PARM_DESC(video_nr, "video device numbers");
50*4882a593Smuzhiyun MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
51*4882a593Smuzhiyun MODULE_PARM_DESC(radio_nr, "radio device numbers");
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun static unsigned int video_debug;
54*4882a593Smuzhiyun module_param(video_debug, int, 0644);
55*4882a593Smuzhiyun MODULE_PARM_DESC(video_debug, "enable debug messages [video]");
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun static unsigned int irq_debug;
58*4882a593Smuzhiyun module_param(irq_debug, int, 0644);
59*4882a593Smuzhiyun MODULE_PARM_DESC(irq_debug, "enable debug messages [IRQ handler]");
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun #define dprintk(level, fmt, arg...) do { \
62*4882a593Smuzhiyun if (video_debug >= level) \
63*4882a593Smuzhiyun printk(KERN_DEBUG pr_fmt("%s: video:" fmt), \
64*4882a593Smuzhiyun __func__, ##arg); \
65*4882a593Smuzhiyun } while (0)
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun /* ------------------------------------------------------------------- */
68*4882a593Smuzhiyun /* static data */
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun static const struct cx8800_fmt formats[] = {
71*4882a593Smuzhiyun {
72*4882a593Smuzhiyun .fourcc = V4L2_PIX_FMT_GREY,
73*4882a593Smuzhiyun .cxformat = ColorFormatY8,
74*4882a593Smuzhiyun .depth = 8,
75*4882a593Smuzhiyun .flags = FORMAT_FLAGS_PACKED,
76*4882a593Smuzhiyun }, {
77*4882a593Smuzhiyun .fourcc = V4L2_PIX_FMT_RGB555,
78*4882a593Smuzhiyun .cxformat = ColorFormatRGB15,
79*4882a593Smuzhiyun .depth = 16,
80*4882a593Smuzhiyun .flags = FORMAT_FLAGS_PACKED,
81*4882a593Smuzhiyun }, {
82*4882a593Smuzhiyun .fourcc = V4L2_PIX_FMT_RGB555X,
83*4882a593Smuzhiyun .cxformat = ColorFormatRGB15 | ColorFormatBSWAP,
84*4882a593Smuzhiyun .depth = 16,
85*4882a593Smuzhiyun .flags = FORMAT_FLAGS_PACKED,
86*4882a593Smuzhiyun }, {
87*4882a593Smuzhiyun .fourcc = V4L2_PIX_FMT_RGB565,
88*4882a593Smuzhiyun .cxformat = ColorFormatRGB16,
89*4882a593Smuzhiyun .depth = 16,
90*4882a593Smuzhiyun .flags = FORMAT_FLAGS_PACKED,
91*4882a593Smuzhiyun }, {
92*4882a593Smuzhiyun .fourcc = V4L2_PIX_FMT_RGB565X,
93*4882a593Smuzhiyun .cxformat = ColorFormatRGB16 | ColorFormatBSWAP,
94*4882a593Smuzhiyun .depth = 16,
95*4882a593Smuzhiyun .flags = FORMAT_FLAGS_PACKED,
96*4882a593Smuzhiyun }, {
97*4882a593Smuzhiyun .fourcc = V4L2_PIX_FMT_BGR24,
98*4882a593Smuzhiyun .cxformat = ColorFormatRGB24,
99*4882a593Smuzhiyun .depth = 24,
100*4882a593Smuzhiyun .flags = FORMAT_FLAGS_PACKED,
101*4882a593Smuzhiyun }, {
102*4882a593Smuzhiyun .fourcc = V4L2_PIX_FMT_BGR32,
103*4882a593Smuzhiyun .cxformat = ColorFormatRGB32,
104*4882a593Smuzhiyun .depth = 32,
105*4882a593Smuzhiyun .flags = FORMAT_FLAGS_PACKED,
106*4882a593Smuzhiyun }, {
107*4882a593Smuzhiyun .fourcc = V4L2_PIX_FMT_RGB32,
108*4882a593Smuzhiyun .cxformat = ColorFormatRGB32 | ColorFormatBSWAP |
109*4882a593Smuzhiyun ColorFormatWSWAP,
110*4882a593Smuzhiyun .depth = 32,
111*4882a593Smuzhiyun .flags = FORMAT_FLAGS_PACKED,
112*4882a593Smuzhiyun }, {
113*4882a593Smuzhiyun .fourcc = V4L2_PIX_FMT_YUYV,
114*4882a593Smuzhiyun .cxformat = ColorFormatYUY2,
115*4882a593Smuzhiyun .depth = 16,
116*4882a593Smuzhiyun .flags = FORMAT_FLAGS_PACKED,
117*4882a593Smuzhiyun }, {
118*4882a593Smuzhiyun .fourcc = V4L2_PIX_FMT_UYVY,
119*4882a593Smuzhiyun .cxformat = ColorFormatYUY2 | ColorFormatBSWAP,
120*4882a593Smuzhiyun .depth = 16,
121*4882a593Smuzhiyun .flags = FORMAT_FLAGS_PACKED,
122*4882a593Smuzhiyun },
123*4882a593Smuzhiyun };
124*4882a593Smuzhiyun
format_by_fourcc(unsigned int fourcc)125*4882a593Smuzhiyun static const struct cx8800_fmt *format_by_fourcc(unsigned int fourcc)
126*4882a593Smuzhiyun {
127*4882a593Smuzhiyun unsigned int i;
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(formats); i++)
130*4882a593Smuzhiyun if (formats[i].fourcc == fourcc)
131*4882a593Smuzhiyun return formats + i;
132*4882a593Smuzhiyun return NULL;
133*4882a593Smuzhiyun }
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun /* ------------------------------------------------------------------- */
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun struct cx88_ctrl {
138*4882a593Smuzhiyun /* control information */
139*4882a593Smuzhiyun u32 id;
140*4882a593Smuzhiyun s32 minimum;
141*4882a593Smuzhiyun s32 maximum;
142*4882a593Smuzhiyun u32 step;
143*4882a593Smuzhiyun s32 default_value;
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun /* control register information */
146*4882a593Smuzhiyun u32 off;
147*4882a593Smuzhiyun u32 reg;
148*4882a593Smuzhiyun u32 sreg;
149*4882a593Smuzhiyun u32 mask;
150*4882a593Smuzhiyun u32 shift;
151*4882a593Smuzhiyun };
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun static const struct cx88_ctrl cx8800_vid_ctls[] = {
154*4882a593Smuzhiyun /* --- video --- */
155*4882a593Smuzhiyun {
156*4882a593Smuzhiyun .id = V4L2_CID_BRIGHTNESS,
157*4882a593Smuzhiyun .minimum = 0x00,
158*4882a593Smuzhiyun .maximum = 0xff,
159*4882a593Smuzhiyun .step = 1,
160*4882a593Smuzhiyun .default_value = 0x7f,
161*4882a593Smuzhiyun .off = 128,
162*4882a593Smuzhiyun .reg = MO_CONTR_BRIGHT,
163*4882a593Smuzhiyun .mask = 0x00ff,
164*4882a593Smuzhiyun .shift = 0,
165*4882a593Smuzhiyun }, {
166*4882a593Smuzhiyun .id = V4L2_CID_CONTRAST,
167*4882a593Smuzhiyun .minimum = 0,
168*4882a593Smuzhiyun .maximum = 0xff,
169*4882a593Smuzhiyun .step = 1,
170*4882a593Smuzhiyun .default_value = 0x3f,
171*4882a593Smuzhiyun .off = 0,
172*4882a593Smuzhiyun .reg = MO_CONTR_BRIGHT,
173*4882a593Smuzhiyun .mask = 0xff00,
174*4882a593Smuzhiyun .shift = 8,
175*4882a593Smuzhiyun }, {
176*4882a593Smuzhiyun .id = V4L2_CID_HUE,
177*4882a593Smuzhiyun .minimum = 0,
178*4882a593Smuzhiyun .maximum = 0xff,
179*4882a593Smuzhiyun .step = 1,
180*4882a593Smuzhiyun .default_value = 0x7f,
181*4882a593Smuzhiyun .off = 128,
182*4882a593Smuzhiyun .reg = MO_HUE,
183*4882a593Smuzhiyun .mask = 0x00ff,
184*4882a593Smuzhiyun .shift = 0,
185*4882a593Smuzhiyun }, {
186*4882a593Smuzhiyun /* strictly, this only describes only U saturation.
187*4882a593Smuzhiyun * V saturation is handled specially through code.
188*4882a593Smuzhiyun */
189*4882a593Smuzhiyun .id = V4L2_CID_SATURATION,
190*4882a593Smuzhiyun .minimum = 0,
191*4882a593Smuzhiyun .maximum = 0xff,
192*4882a593Smuzhiyun .step = 1,
193*4882a593Smuzhiyun .default_value = 0x7f,
194*4882a593Smuzhiyun .off = 0,
195*4882a593Smuzhiyun .reg = MO_UV_SATURATION,
196*4882a593Smuzhiyun .mask = 0x00ff,
197*4882a593Smuzhiyun .shift = 0,
198*4882a593Smuzhiyun }, {
199*4882a593Smuzhiyun .id = V4L2_CID_SHARPNESS,
200*4882a593Smuzhiyun .minimum = 0,
201*4882a593Smuzhiyun .maximum = 4,
202*4882a593Smuzhiyun .step = 1,
203*4882a593Smuzhiyun .default_value = 0x0,
204*4882a593Smuzhiyun .off = 0,
205*4882a593Smuzhiyun /*
206*4882a593Smuzhiyun * NOTE: the value is converted and written to both even
207*4882a593Smuzhiyun * and odd registers in the code
208*4882a593Smuzhiyun */
209*4882a593Smuzhiyun .reg = MO_FILTER_ODD,
210*4882a593Smuzhiyun .mask = 7 << 7,
211*4882a593Smuzhiyun .shift = 7,
212*4882a593Smuzhiyun }, {
213*4882a593Smuzhiyun .id = V4L2_CID_CHROMA_AGC,
214*4882a593Smuzhiyun .minimum = 0,
215*4882a593Smuzhiyun .maximum = 1,
216*4882a593Smuzhiyun .default_value = 0x1,
217*4882a593Smuzhiyun .reg = MO_INPUT_FORMAT,
218*4882a593Smuzhiyun .mask = 1 << 10,
219*4882a593Smuzhiyun .shift = 10,
220*4882a593Smuzhiyun }, {
221*4882a593Smuzhiyun .id = V4L2_CID_COLOR_KILLER,
222*4882a593Smuzhiyun .minimum = 0,
223*4882a593Smuzhiyun .maximum = 1,
224*4882a593Smuzhiyun .default_value = 0x1,
225*4882a593Smuzhiyun .reg = MO_INPUT_FORMAT,
226*4882a593Smuzhiyun .mask = 1 << 9,
227*4882a593Smuzhiyun .shift = 9,
228*4882a593Smuzhiyun }, {
229*4882a593Smuzhiyun .id = V4L2_CID_BAND_STOP_FILTER,
230*4882a593Smuzhiyun .minimum = 0,
231*4882a593Smuzhiyun .maximum = 1,
232*4882a593Smuzhiyun .step = 1,
233*4882a593Smuzhiyun .default_value = 0x0,
234*4882a593Smuzhiyun .off = 0,
235*4882a593Smuzhiyun .reg = MO_HTOTAL,
236*4882a593Smuzhiyun .mask = 3 << 11,
237*4882a593Smuzhiyun .shift = 11,
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun };
240*4882a593Smuzhiyun
241*4882a593Smuzhiyun static const struct cx88_ctrl cx8800_aud_ctls[] = {
242*4882a593Smuzhiyun {
243*4882a593Smuzhiyun /* --- audio --- */
244*4882a593Smuzhiyun .id = V4L2_CID_AUDIO_MUTE,
245*4882a593Smuzhiyun .minimum = 0,
246*4882a593Smuzhiyun .maximum = 1,
247*4882a593Smuzhiyun .default_value = 1,
248*4882a593Smuzhiyun .reg = AUD_VOL_CTL,
249*4882a593Smuzhiyun .sreg = SHADOW_AUD_VOL_CTL,
250*4882a593Smuzhiyun .mask = (1 << 6),
251*4882a593Smuzhiyun .shift = 6,
252*4882a593Smuzhiyun }, {
253*4882a593Smuzhiyun .id = V4L2_CID_AUDIO_VOLUME,
254*4882a593Smuzhiyun .minimum = 0,
255*4882a593Smuzhiyun .maximum = 0x3f,
256*4882a593Smuzhiyun .step = 1,
257*4882a593Smuzhiyun .default_value = 0x3f,
258*4882a593Smuzhiyun .reg = AUD_VOL_CTL,
259*4882a593Smuzhiyun .sreg = SHADOW_AUD_VOL_CTL,
260*4882a593Smuzhiyun .mask = 0x3f,
261*4882a593Smuzhiyun .shift = 0,
262*4882a593Smuzhiyun }, {
263*4882a593Smuzhiyun .id = V4L2_CID_AUDIO_BALANCE,
264*4882a593Smuzhiyun .minimum = 0,
265*4882a593Smuzhiyun .maximum = 0x7f,
266*4882a593Smuzhiyun .step = 1,
267*4882a593Smuzhiyun .default_value = 0x40,
268*4882a593Smuzhiyun .reg = AUD_BAL_CTL,
269*4882a593Smuzhiyun .sreg = SHADOW_AUD_BAL_CTL,
270*4882a593Smuzhiyun .mask = 0x7f,
271*4882a593Smuzhiyun .shift = 0,
272*4882a593Smuzhiyun }
273*4882a593Smuzhiyun };
274*4882a593Smuzhiyun
275*4882a593Smuzhiyun enum {
276*4882a593Smuzhiyun CX8800_VID_CTLS = ARRAY_SIZE(cx8800_vid_ctls),
277*4882a593Smuzhiyun CX8800_AUD_CTLS = ARRAY_SIZE(cx8800_aud_ctls),
278*4882a593Smuzhiyun };
279*4882a593Smuzhiyun
280*4882a593Smuzhiyun /* ------------------------------------------------------------------ */
281*4882a593Smuzhiyun
cx88_video_mux(struct cx88_core * core,unsigned int input)282*4882a593Smuzhiyun int cx88_video_mux(struct cx88_core *core, unsigned int input)
283*4882a593Smuzhiyun {
284*4882a593Smuzhiyun /* struct cx88_core *core = dev->core; */
285*4882a593Smuzhiyun
286*4882a593Smuzhiyun dprintk(1, "video_mux: %d [vmux=%d,gpio=0x%x,0x%x,0x%x,0x%x]\n",
287*4882a593Smuzhiyun input, INPUT(input).vmux,
288*4882a593Smuzhiyun INPUT(input).gpio0, INPUT(input).gpio1,
289*4882a593Smuzhiyun INPUT(input).gpio2, INPUT(input).gpio3);
290*4882a593Smuzhiyun core->input = input;
291*4882a593Smuzhiyun cx_andor(MO_INPUT_FORMAT, 0x03 << 14, INPUT(input).vmux << 14);
292*4882a593Smuzhiyun cx_write(MO_GP3_IO, INPUT(input).gpio3);
293*4882a593Smuzhiyun cx_write(MO_GP0_IO, INPUT(input).gpio0);
294*4882a593Smuzhiyun cx_write(MO_GP1_IO, INPUT(input).gpio1);
295*4882a593Smuzhiyun cx_write(MO_GP2_IO, INPUT(input).gpio2);
296*4882a593Smuzhiyun
297*4882a593Smuzhiyun switch (INPUT(input).type) {
298*4882a593Smuzhiyun case CX88_VMUX_SVIDEO:
299*4882a593Smuzhiyun cx_set(MO_AFECFG_IO, 0x00000001);
300*4882a593Smuzhiyun cx_set(MO_INPUT_FORMAT, 0x00010010);
301*4882a593Smuzhiyun cx_set(MO_FILTER_EVEN, 0x00002020);
302*4882a593Smuzhiyun cx_set(MO_FILTER_ODD, 0x00002020);
303*4882a593Smuzhiyun break;
304*4882a593Smuzhiyun default:
305*4882a593Smuzhiyun cx_clear(MO_AFECFG_IO, 0x00000001);
306*4882a593Smuzhiyun cx_clear(MO_INPUT_FORMAT, 0x00010010);
307*4882a593Smuzhiyun cx_clear(MO_FILTER_EVEN, 0x00002020);
308*4882a593Smuzhiyun cx_clear(MO_FILTER_ODD, 0x00002020);
309*4882a593Smuzhiyun break;
310*4882a593Smuzhiyun }
311*4882a593Smuzhiyun
312*4882a593Smuzhiyun /*
313*4882a593Smuzhiyun * if there are audioroutes defined, we have an external
314*4882a593Smuzhiyun * ADC to deal with audio
315*4882a593Smuzhiyun */
316*4882a593Smuzhiyun if (INPUT(input).audioroute) {
317*4882a593Smuzhiyun /*
318*4882a593Smuzhiyun * The wm8775 module has the "2" route hardwired into
319*4882a593Smuzhiyun * the initialization. Some boards may use different
320*4882a593Smuzhiyun * routes for different inputs. HVR-1300 surely does
321*4882a593Smuzhiyun */
322*4882a593Smuzhiyun if (core->sd_wm8775) {
323*4882a593Smuzhiyun call_all(core, audio, s_routing,
324*4882a593Smuzhiyun INPUT(input).audioroute, 0, 0);
325*4882a593Smuzhiyun }
326*4882a593Smuzhiyun /*
327*4882a593Smuzhiyun * cx2388's C-ADC is connected to the tuner only.
328*4882a593Smuzhiyun * When used with S-Video, that ADC is busy dealing with
329*4882a593Smuzhiyun * chroma, so an external must be used for baseband audio
330*4882a593Smuzhiyun */
331*4882a593Smuzhiyun if (INPUT(input).type != CX88_VMUX_TELEVISION &&
332*4882a593Smuzhiyun INPUT(input).type != CX88_VMUX_CABLE) {
333*4882a593Smuzhiyun /* "I2S ADC mode" */
334*4882a593Smuzhiyun core->tvaudio = WW_I2SADC;
335*4882a593Smuzhiyun cx88_set_tvaudio(core);
336*4882a593Smuzhiyun } else {
337*4882a593Smuzhiyun /* Normal mode */
338*4882a593Smuzhiyun cx_write(AUD_I2SCNTL, 0x0);
339*4882a593Smuzhiyun cx_clear(AUD_CTL, EN_I2SIN_ENABLE);
340*4882a593Smuzhiyun }
341*4882a593Smuzhiyun }
342*4882a593Smuzhiyun
343*4882a593Smuzhiyun return 0;
344*4882a593Smuzhiyun }
345*4882a593Smuzhiyun EXPORT_SYMBOL(cx88_video_mux);
346*4882a593Smuzhiyun
347*4882a593Smuzhiyun /* ------------------------------------------------------------------ */
348*4882a593Smuzhiyun
start_video_dma(struct cx8800_dev * dev,struct cx88_dmaqueue * q,struct cx88_buffer * buf)349*4882a593Smuzhiyun static int start_video_dma(struct cx8800_dev *dev,
350*4882a593Smuzhiyun struct cx88_dmaqueue *q,
351*4882a593Smuzhiyun struct cx88_buffer *buf)
352*4882a593Smuzhiyun {
353*4882a593Smuzhiyun struct cx88_core *core = dev->core;
354*4882a593Smuzhiyun
355*4882a593Smuzhiyun /* setup fifo + format */
356*4882a593Smuzhiyun cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH21],
357*4882a593Smuzhiyun buf->bpl, buf->risc.dma);
358*4882a593Smuzhiyun cx88_set_scale(core, core->width, core->height, core->field);
359*4882a593Smuzhiyun cx_write(MO_COLOR_CTRL, dev->fmt->cxformat | ColorFormatGamma);
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun /* reset counter */
362*4882a593Smuzhiyun cx_write(MO_VIDY_GPCNTRL, GP_COUNT_CONTROL_RESET);
363*4882a593Smuzhiyun q->count = 0;
364*4882a593Smuzhiyun
365*4882a593Smuzhiyun /* enable irqs */
366*4882a593Smuzhiyun cx_set(MO_PCI_INTMSK, core->pci_irqmask | PCI_INT_VIDINT);
367*4882a593Smuzhiyun
368*4882a593Smuzhiyun /*
369*4882a593Smuzhiyun * Enables corresponding bits at PCI_INT_STAT:
370*4882a593Smuzhiyun * bits 0 to 4: video, audio, transport stream, VIP, Host
371*4882a593Smuzhiyun * bit 7: timer
372*4882a593Smuzhiyun * bits 8 and 9: DMA complete for: SRC, DST
373*4882a593Smuzhiyun * bits 10 and 11: BERR signal asserted for RISC: RD, WR
374*4882a593Smuzhiyun * bits 12 to 15: BERR signal asserted for: BRDG, SRC, DST, IPB
375*4882a593Smuzhiyun */
376*4882a593Smuzhiyun cx_set(MO_VID_INTMSK, 0x0f0011);
377*4882a593Smuzhiyun
378*4882a593Smuzhiyun /* enable capture */
379*4882a593Smuzhiyun cx_set(VID_CAPTURE_CONTROL, 0x06);
380*4882a593Smuzhiyun
381*4882a593Smuzhiyun /* start dma */
382*4882a593Smuzhiyun cx_set(MO_DEV_CNTRL2, (1 << 5));
383*4882a593Smuzhiyun cx_set(MO_VID_DMACNTRL, 0x11); /* Planar Y and packed FIFO and RISC enable */
384*4882a593Smuzhiyun
385*4882a593Smuzhiyun return 0;
386*4882a593Smuzhiyun }
387*4882a593Smuzhiyun
stop_video_dma(struct cx8800_dev * dev)388*4882a593Smuzhiyun static int __maybe_unused stop_video_dma(struct cx8800_dev *dev)
389*4882a593Smuzhiyun {
390*4882a593Smuzhiyun struct cx88_core *core = dev->core;
391*4882a593Smuzhiyun
392*4882a593Smuzhiyun /* stop dma */
393*4882a593Smuzhiyun cx_clear(MO_VID_DMACNTRL, 0x11);
394*4882a593Smuzhiyun
395*4882a593Smuzhiyun /* disable capture */
396*4882a593Smuzhiyun cx_clear(VID_CAPTURE_CONTROL, 0x06);
397*4882a593Smuzhiyun
398*4882a593Smuzhiyun /* disable irqs */
399*4882a593Smuzhiyun cx_clear(MO_PCI_INTMSK, PCI_INT_VIDINT);
400*4882a593Smuzhiyun cx_clear(MO_VID_INTMSK, 0x0f0011);
401*4882a593Smuzhiyun return 0;
402*4882a593Smuzhiyun }
403*4882a593Smuzhiyun
restart_video_queue(struct cx8800_dev * dev,struct cx88_dmaqueue * q)404*4882a593Smuzhiyun static int __maybe_unused restart_video_queue(struct cx8800_dev *dev,
405*4882a593Smuzhiyun struct cx88_dmaqueue *q)
406*4882a593Smuzhiyun {
407*4882a593Smuzhiyun struct cx88_buffer *buf;
408*4882a593Smuzhiyun
409*4882a593Smuzhiyun if (!list_empty(&q->active)) {
410*4882a593Smuzhiyun buf = list_entry(q->active.next, struct cx88_buffer, list);
411*4882a593Smuzhiyun dprintk(2, "restart_queue [%p/%d]: restart dma\n",
412*4882a593Smuzhiyun buf, buf->vb.vb2_buf.index);
413*4882a593Smuzhiyun start_video_dma(dev, q, buf);
414*4882a593Smuzhiyun }
415*4882a593Smuzhiyun return 0;
416*4882a593Smuzhiyun }
417*4882a593Smuzhiyun
418*4882a593Smuzhiyun /* ------------------------------------------------------------------ */
419*4882a593Smuzhiyun
queue_setup(struct vb2_queue * q,unsigned int * num_buffers,unsigned int * num_planes,unsigned int sizes[],struct device * alloc_devs[])420*4882a593Smuzhiyun static int queue_setup(struct vb2_queue *q,
421*4882a593Smuzhiyun unsigned int *num_buffers, unsigned int *num_planes,
422*4882a593Smuzhiyun unsigned int sizes[], struct device *alloc_devs[])
423*4882a593Smuzhiyun {
424*4882a593Smuzhiyun struct cx8800_dev *dev = q->drv_priv;
425*4882a593Smuzhiyun struct cx88_core *core = dev->core;
426*4882a593Smuzhiyun
427*4882a593Smuzhiyun *num_planes = 1;
428*4882a593Smuzhiyun sizes[0] = (dev->fmt->depth * core->width * core->height) >> 3;
429*4882a593Smuzhiyun return 0;
430*4882a593Smuzhiyun }
431*4882a593Smuzhiyun
buffer_prepare(struct vb2_buffer * vb)432*4882a593Smuzhiyun static int buffer_prepare(struct vb2_buffer *vb)
433*4882a593Smuzhiyun {
434*4882a593Smuzhiyun int ret;
435*4882a593Smuzhiyun struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
436*4882a593Smuzhiyun struct cx8800_dev *dev = vb->vb2_queue->drv_priv;
437*4882a593Smuzhiyun struct cx88_core *core = dev->core;
438*4882a593Smuzhiyun struct cx88_buffer *buf = container_of(vbuf, struct cx88_buffer, vb);
439*4882a593Smuzhiyun struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
440*4882a593Smuzhiyun
441*4882a593Smuzhiyun buf->bpl = core->width * dev->fmt->depth >> 3;
442*4882a593Smuzhiyun
443*4882a593Smuzhiyun if (vb2_plane_size(vb, 0) < core->height * buf->bpl)
444*4882a593Smuzhiyun return -EINVAL;
445*4882a593Smuzhiyun vb2_set_plane_payload(vb, 0, core->height * buf->bpl);
446*4882a593Smuzhiyun
447*4882a593Smuzhiyun switch (core->field) {
448*4882a593Smuzhiyun case V4L2_FIELD_TOP:
449*4882a593Smuzhiyun ret = cx88_risc_buffer(dev->pci, &buf->risc,
450*4882a593Smuzhiyun sgt->sgl, 0, UNSET,
451*4882a593Smuzhiyun buf->bpl, 0, core->height);
452*4882a593Smuzhiyun break;
453*4882a593Smuzhiyun case V4L2_FIELD_BOTTOM:
454*4882a593Smuzhiyun ret = cx88_risc_buffer(dev->pci, &buf->risc,
455*4882a593Smuzhiyun sgt->sgl, UNSET, 0,
456*4882a593Smuzhiyun buf->bpl, 0, core->height);
457*4882a593Smuzhiyun break;
458*4882a593Smuzhiyun case V4L2_FIELD_SEQ_TB:
459*4882a593Smuzhiyun ret = cx88_risc_buffer(dev->pci, &buf->risc,
460*4882a593Smuzhiyun sgt->sgl,
461*4882a593Smuzhiyun 0, buf->bpl * (core->height >> 1),
462*4882a593Smuzhiyun buf->bpl, 0,
463*4882a593Smuzhiyun core->height >> 1);
464*4882a593Smuzhiyun break;
465*4882a593Smuzhiyun case V4L2_FIELD_SEQ_BT:
466*4882a593Smuzhiyun ret = cx88_risc_buffer(dev->pci, &buf->risc,
467*4882a593Smuzhiyun sgt->sgl,
468*4882a593Smuzhiyun buf->bpl * (core->height >> 1), 0,
469*4882a593Smuzhiyun buf->bpl, 0,
470*4882a593Smuzhiyun core->height >> 1);
471*4882a593Smuzhiyun break;
472*4882a593Smuzhiyun case V4L2_FIELD_INTERLACED:
473*4882a593Smuzhiyun default:
474*4882a593Smuzhiyun ret = cx88_risc_buffer(dev->pci, &buf->risc,
475*4882a593Smuzhiyun sgt->sgl, 0, buf->bpl,
476*4882a593Smuzhiyun buf->bpl, buf->bpl,
477*4882a593Smuzhiyun core->height >> 1);
478*4882a593Smuzhiyun break;
479*4882a593Smuzhiyun }
480*4882a593Smuzhiyun dprintk(2,
481*4882a593Smuzhiyun "[%p/%d] %s - %dx%d %dbpp 0x%08x - dma=0x%08lx\n",
482*4882a593Smuzhiyun buf, buf->vb.vb2_buf.index, __func__,
483*4882a593Smuzhiyun core->width, core->height, dev->fmt->depth, dev->fmt->fourcc,
484*4882a593Smuzhiyun (unsigned long)buf->risc.dma);
485*4882a593Smuzhiyun return ret;
486*4882a593Smuzhiyun }
487*4882a593Smuzhiyun
buffer_finish(struct vb2_buffer * vb)488*4882a593Smuzhiyun static void buffer_finish(struct vb2_buffer *vb)
489*4882a593Smuzhiyun {
490*4882a593Smuzhiyun struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
491*4882a593Smuzhiyun struct cx8800_dev *dev = vb->vb2_queue->drv_priv;
492*4882a593Smuzhiyun struct cx88_buffer *buf = container_of(vbuf, struct cx88_buffer, vb);
493*4882a593Smuzhiyun struct cx88_riscmem *risc = &buf->risc;
494*4882a593Smuzhiyun
495*4882a593Smuzhiyun if (risc->cpu)
496*4882a593Smuzhiyun pci_free_consistent(dev->pci, risc->size, risc->cpu, risc->dma);
497*4882a593Smuzhiyun memset(risc, 0, sizeof(*risc));
498*4882a593Smuzhiyun }
499*4882a593Smuzhiyun
buffer_queue(struct vb2_buffer * vb)500*4882a593Smuzhiyun static void buffer_queue(struct vb2_buffer *vb)
501*4882a593Smuzhiyun {
502*4882a593Smuzhiyun struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
503*4882a593Smuzhiyun struct cx8800_dev *dev = vb->vb2_queue->drv_priv;
504*4882a593Smuzhiyun struct cx88_buffer *buf = container_of(vbuf, struct cx88_buffer, vb);
505*4882a593Smuzhiyun struct cx88_buffer *prev;
506*4882a593Smuzhiyun struct cx88_dmaqueue *q = &dev->vidq;
507*4882a593Smuzhiyun
508*4882a593Smuzhiyun /* add jump to start */
509*4882a593Smuzhiyun buf->risc.cpu[1] = cpu_to_le32(buf->risc.dma + 8);
510*4882a593Smuzhiyun buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_CNT_INC);
511*4882a593Smuzhiyun buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma + 8);
512*4882a593Smuzhiyun
513*4882a593Smuzhiyun if (list_empty(&q->active)) {
514*4882a593Smuzhiyun list_add_tail(&buf->list, &q->active);
515*4882a593Smuzhiyun dprintk(2, "[%p/%d] buffer_queue - first active\n",
516*4882a593Smuzhiyun buf, buf->vb.vb2_buf.index);
517*4882a593Smuzhiyun
518*4882a593Smuzhiyun } else {
519*4882a593Smuzhiyun buf->risc.cpu[0] |= cpu_to_le32(RISC_IRQ1);
520*4882a593Smuzhiyun prev = list_entry(q->active.prev, struct cx88_buffer, list);
521*4882a593Smuzhiyun list_add_tail(&buf->list, &q->active);
522*4882a593Smuzhiyun prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
523*4882a593Smuzhiyun dprintk(2, "[%p/%d] buffer_queue - append to active\n",
524*4882a593Smuzhiyun buf, buf->vb.vb2_buf.index);
525*4882a593Smuzhiyun }
526*4882a593Smuzhiyun }
527*4882a593Smuzhiyun
start_streaming(struct vb2_queue * q,unsigned int count)528*4882a593Smuzhiyun static int start_streaming(struct vb2_queue *q, unsigned int count)
529*4882a593Smuzhiyun {
530*4882a593Smuzhiyun struct cx8800_dev *dev = q->drv_priv;
531*4882a593Smuzhiyun struct cx88_dmaqueue *dmaq = &dev->vidq;
532*4882a593Smuzhiyun struct cx88_buffer *buf = list_entry(dmaq->active.next,
533*4882a593Smuzhiyun struct cx88_buffer, list);
534*4882a593Smuzhiyun
535*4882a593Smuzhiyun start_video_dma(dev, dmaq, buf);
536*4882a593Smuzhiyun return 0;
537*4882a593Smuzhiyun }
538*4882a593Smuzhiyun
stop_streaming(struct vb2_queue * q)539*4882a593Smuzhiyun static void stop_streaming(struct vb2_queue *q)
540*4882a593Smuzhiyun {
541*4882a593Smuzhiyun struct cx8800_dev *dev = q->drv_priv;
542*4882a593Smuzhiyun struct cx88_core *core = dev->core;
543*4882a593Smuzhiyun struct cx88_dmaqueue *dmaq = &dev->vidq;
544*4882a593Smuzhiyun unsigned long flags;
545*4882a593Smuzhiyun
546*4882a593Smuzhiyun cx_clear(MO_VID_DMACNTRL, 0x11);
547*4882a593Smuzhiyun cx_clear(VID_CAPTURE_CONTROL, 0x06);
548*4882a593Smuzhiyun spin_lock_irqsave(&dev->slock, flags);
549*4882a593Smuzhiyun while (!list_empty(&dmaq->active)) {
550*4882a593Smuzhiyun struct cx88_buffer *buf = list_entry(dmaq->active.next,
551*4882a593Smuzhiyun struct cx88_buffer, list);
552*4882a593Smuzhiyun
553*4882a593Smuzhiyun list_del(&buf->list);
554*4882a593Smuzhiyun vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
555*4882a593Smuzhiyun }
556*4882a593Smuzhiyun spin_unlock_irqrestore(&dev->slock, flags);
557*4882a593Smuzhiyun }
558*4882a593Smuzhiyun
559*4882a593Smuzhiyun static const struct vb2_ops cx8800_video_qops = {
560*4882a593Smuzhiyun .queue_setup = queue_setup,
561*4882a593Smuzhiyun .buf_prepare = buffer_prepare,
562*4882a593Smuzhiyun .buf_finish = buffer_finish,
563*4882a593Smuzhiyun .buf_queue = buffer_queue,
564*4882a593Smuzhiyun .wait_prepare = vb2_ops_wait_prepare,
565*4882a593Smuzhiyun .wait_finish = vb2_ops_wait_finish,
566*4882a593Smuzhiyun .start_streaming = start_streaming,
567*4882a593Smuzhiyun .stop_streaming = stop_streaming,
568*4882a593Smuzhiyun };
569*4882a593Smuzhiyun
570*4882a593Smuzhiyun /* ------------------------------------------------------------------ */
571*4882a593Smuzhiyun
radio_open(struct file * file)572*4882a593Smuzhiyun static int radio_open(struct file *file)
573*4882a593Smuzhiyun {
574*4882a593Smuzhiyun struct cx8800_dev *dev = video_drvdata(file);
575*4882a593Smuzhiyun struct cx88_core *core = dev->core;
576*4882a593Smuzhiyun int ret = v4l2_fh_open(file);
577*4882a593Smuzhiyun
578*4882a593Smuzhiyun if (ret)
579*4882a593Smuzhiyun return ret;
580*4882a593Smuzhiyun
581*4882a593Smuzhiyun cx_write(MO_GP3_IO, core->board.radio.gpio3);
582*4882a593Smuzhiyun cx_write(MO_GP0_IO, core->board.radio.gpio0);
583*4882a593Smuzhiyun cx_write(MO_GP1_IO, core->board.radio.gpio1);
584*4882a593Smuzhiyun cx_write(MO_GP2_IO, core->board.radio.gpio2);
585*4882a593Smuzhiyun if (core->board.radio.audioroute) {
586*4882a593Smuzhiyun if (core->sd_wm8775) {
587*4882a593Smuzhiyun call_all(core, audio, s_routing,
588*4882a593Smuzhiyun core->board.radio.audioroute, 0, 0);
589*4882a593Smuzhiyun }
590*4882a593Smuzhiyun /* "I2S ADC mode" */
591*4882a593Smuzhiyun core->tvaudio = WW_I2SADC;
592*4882a593Smuzhiyun cx88_set_tvaudio(core);
593*4882a593Smuzhiyun } else {
594*4882a593Smuzhiyun /* FM Mode */
595*4882a593Smuzhiyun core->tvaudio = WW_FM;
596*4882a593Smuzhiyun cx88_set_tvaudio(core);
597*4882a593Smuzhiyun cx88_set_stereo(core, V4L2_TUNER_MODE_STEREO, 1);
598*4882a593Smuzhiyun }
599*4882a593Smuzhiyun call_all(core, tuner, s_radio);
600*4882a593Smuzhiyun return 0;
601*4882a593Smuzhiyun }
602*4882a593Smuzhiyun
603*4882a593Smuzhiyun /* ------------------------------------------------------------------ */
604*4882a593Smuzhiyun /* VIDEO CTRL IOCTLS */
605*4882a593Smuzhiyun
cx8800_s_vid_ctrl(struct v4l2_ctrl * ctrl)606*4882a593Smuzhiyun static int cx8800_s_vid_ctrl(struct v4l2_ctrl *ctrl)
607*4882a593Smuzhiyun {
608*4882a593Smuzhiyun struct cx88_core *core =
609*4882a593Smuzhiyun container_of(ctrl->handler, struct cx88_core, video_hdl);
610*4882a593Smuzhiyun const struct cx88_ctrl *cc = ctrl->priv;
611*4882a593Smuzhiyun u32 value, mask;
612*4882a593Smuzhiyun
613*4882a593Smuzhiyun mask = cc->mask;
614*4882a593Smuzhiyun switch (ctrl->id) {
615*4882a593Smuzhiyun case V4L2_CID_SATURATION:
616*4882a593Smuzhiyun /* special v_sat handling */
617*4882a593Smuzhiyun
618*4882a593Smuzhiyun value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
619*4882a593Smuzhiyun
620*4882a593Smuzhiyun if (core->tvnorm & V4L2_STD_SECAM) {
621*4882a593Smuzhiyun /* For SECAM, both U and V sat should be equal */
622*4882a593Smuzhiyun value = value << 8 | value;
623*4882a593Smuzhiyun } else {
624*4882a593Smuzhiyun /* Keeps U Saturation proportional to V Sat */
625*4882a593Smuzhiyun value = (value * 0x5a) / 0x7f << 8 | value;
626*4882a593Smuzhiyun }
627*4882a593Smuzhiyun mask = 0xffff;
628*4882a593Smuzhiyun break;
629*4882a593Smuzhiyun case V4L2_CID_SHARPNESS:
630*4882a593Smuzhiyun /* 0b000, 0b100, 0b101, 0b110, or 0b111 */
631*4882a593Smuzhiyun value = (ctrl->val < 1 ? 0 : ((ctrl->val + 3) << 7));
632*4882a593Smuzhiyun /* needs to be set for both fields */
633*4882a593Smuzhiyun cx_andor(MO_FILTER_EVEN, mask, value);
634*4882a593Smuzhiyun break;
635*4882a593Smuzhiyun case V4L2_CID_CHROMA_AGC:
636*4882a593Smuzhiyun value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
637*4882a593Smuzhiyun break;
638*4882a593Smuzhiyun default:
639*4882a593Smuzhiyun value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
640*4882a593Smuzhiyun break;
641*4882a593Smuzhiyun }
642*4882a593Smuzhiyun dprintk(1,
643*4882a593Smuzhiyun "set_control id=0x%X(%s) ctrl=0x%02x, reg=0x%02x val=0x%02x (mask 0x%02x)%s\n",
644*4882a593Smuzhiyun ctrl->id, ctrl->name, ctrl->val, cc->reg, value,
645*4882a593Smuzhiyun mask, cc->sreg ? " [shadowed]" : "");
646*4882a593Smuzhiyun if (cc->sreg)
647*4882a593Smuzhiyun cx_sandor(cc->sreg, cc->reg, mask, value);
648*4882a593Smuzhiyun else
649*4882a593Smuzhiyun cx_andor(cc->reg, mask, value);
650*4882a593Smuzhiyun return 0;
651*4882a593Smuzhiyun }
652*4882a593Smuzhiyun
cx8800_s_aud_ctrl(struct v4l2_ctrl * ctrl)653*4882a593Smuzhiyun static int cx8800_s_aud_ctrl(struct v4l2_ctrl *ctrl)
654*4882a593Smuzhiyun {
655*4882a593Smuzhiyun struct cx88_core *core =
656*4882a593Smuzhiyun container_of(ctrl->handler, struct cx88_core, audio_hdl);
657*4882a593Smuzhiyun const struct cx88_ctrl *cc = ctrl->priv;
658*4882a593Smuzhiyun u32 value, mask;
659*4882a593Smuzhiyun
660*4882a593Smuzhiyun /* Pass changes onto any WM8775 */
661*4882a593Smuzhiyun if (core->sd_wm8775) {
662*4882a593Smuzhiyun switch (ctrl->id) {
663*4882a593Smuzhiyun case V4L2_CID_AUDIO_MUTE:
664*4882a593Smuzhiyun wm8775_s_ctrl(core, ctrl->id, ctrl->val);
665*4882a593Smuzhiyun break;
666*4882a593Smuzhiyun case V4L2_CID_AUDIO_VOLUME:
667*4882a593Smuzhiyun wm8775_s_ctrl(core, ctrl->id, (ctrl->val) ?
668*4882a593Smuzhiyun (0x90 + ctrl->val) << 8 : 0);
669*4882a593Smuzhiyun break;
670*4882a593Smuzhiyun case V4L2_CID_AUDIO_BALANCE:
671*4882a593Smuzhiyun wm8775_s_ctrl(core, ctrl->id, ctrl->val << 9);
672*4882a593Smuzhiyun break;
673*4882a593Smuzhiyun default:
674*4882a593Smuzhiyun break;
675*4882a593Smuzhiyun }
676*4882a593Smuzhiyun }
677*4882a593Smuzhiyun
678*4882a593Smuzhiyun mask = cc->mask;
679*4882a593Smuzhiyun switch (ctrl->id) {
680*4882a593Smuzhiyun case V4L2_CID_AUDIO_BALANCE:
681*4882a593Smuzhiyun value = (ctrl->val < 0x40) ?
682*4882a593Smuzhiyun (0x7f - ctrl->val) : (ctrl->val - 0x40);
683*4882a593Smuzhiyun break;
684*4882a593Smuzhiyun case V4L2_CID_AUDIO_VOLUME:
685*4882a593Smuzhiyun value = 0x3f - (ctrl->val & 0x3f);
686*4882a593Smuzhiyun break;
687*4882a593Smuzhiyun default:
688*4882a593Smuzhiyun value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
689*4882a593Smuzhiyun break;
690*4882a593Smuzhiyun }
691*4882a593Smuzhiyun dprintk(1,
692*4882a593Smuzhiyun "set_control id=0x%X(%s) ctrl=0x%02x, reg=0x%02x val=0x%02x (mask 0x%02x)%s\n",
693*4882a593Smuzhiyun ctrl->id, ctrl->name, ctrl->val, cc->reg, value,
694*4882a593Smuzhiyun mask, cc->sreg ? " [shadowed]" : "");
695*4882a593Smuzhiyun if (cc->sreg)
696*4882a593Smuzhiyun cx_sandor(cc->sreg, cc->reg, mask, value);
697*4882a593Smuzhiyun else
698*4882a593Smuzhiyun cx_andor(cc->reg, mask, value);
699*4882a593Smuzhiyun return 0;
700*4882a593Smuzhiyun }
701*4882a593Smuzhiyun
702*4882a593Smuzhiyun /* ------------------------------------------------------------------ */
703*4882a593Smuzhiyun /* VIDEO IOCTLS */
704*4882a593Smuzhiyun
vidioc_g_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)705*4882a593Smuzhiyun static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
706*4882a593Smuzhiyun struct v4l2_format *f)
707*4882a593Smuzhiyun {
708*4882a593Smuzhiyun struct cx8800_dev *dev = video_drvdata(file);
709*4882a593Smuzhiyun struct cx88_core *core = dev->core;
710*4882a593Smuzhiyun
711*4882a593Smuzhiyun f->fmt.pix.width = core->width;
712*4882a593Smuzhiyun f->fmt.pix.height = core->height;
713*4882a593Smuzhiyun f->fmt.pix.field = core->field;
714*4882a593Smuzhiyun f->fmt.pix.pixelformat = dev->fmt->fourcc;
715*4882a593Smuzhiyun f->fmt.pix.bytesperline =
716*4882a593Smuzhiyun (f->fmt.pix.width * dev->fmt->depth) >> 3;
717*4882a593Smuzhiyun f->fmt.pix.sizeimage =
718*4882a593Smuzhiyun f->fmt.pix.height * f->fmt.pix.bytesperline;
719*4882a593Smuzhiyun f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
720*4882a593Smuzhiyun return 0;
721*4882a593Smuzhiyun }
722*4882a593Smuzhiyun
vidioc_try_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)723*4882a593Smuzhiyun static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
724*4882a593Smuzhiyun struct v4l2_format *f)
725*4882a593Smuzhiyun {
726*4882a593Smuzhiyun struct cx8800_dev *dev = video_drvdata(file);
727*4882a593Smuzhiyun struct cx88_core *core = dev->core;
728*4882a593Smuzhiyun const struct cx8800_fmt *fmt;
729*4882a593Smuzhiyun enum v4l2_field field;
730*4882a593Smuzhiyun unsigned int maxw, maxh;
731*4882a593Smuzhiyun
732*4882a593Smuzhiyun fmt = format_by_fourcc(f->fmt.pix.pixelformat);
733*4882a593Smuzhiyun if (!fmt)
734*4882a593Smuzhiyun return -EINVAL;
735*4882a593Smuzhiyun
736*4882a593Smuzhiyun maxw = norm_maxw(core->tvnorm);
737*4882a593Smuzhiyun maxh = norm_maxh(core->tvnorm);
738*4882a593Smuzhiyun
739*4882a593Smuzhiyun field = f->fmt.pix.field;
740*4882a593Smuzhiyun
741*4882a593Smuzhiyun switch (field) {
742*4882a593Smuzhiyun case V4L2_FIELD_TOP:
743*4882a593Smuzhiyun case V4L2_FIELD_BOTTOM:
744*4882a593Smuzhiyun case V4L2_FIELD_INTERLACED:
745*4882a593Smuzhiyun case V4L2_FIELD_SEQ_BT:
746*4882a593Smuzhiyun case V4L2_FIELD_SEQ_TB:
747*4882a593Smuzhiyun break;
748*4882a593Smuzhiyun default:
749*4882a593Smuzhiyun field = (f->fmt.pix.height > maxh / 2)
750*4882a593Smuzhiyun ? V4L2_FIELD_INTERLACED
751*4882a593Smuzhiyun : V4L2_FIELD_BOTTOM;
752*4882a593Smuzhiyun break;
753*4882a593Smuzhiyun }
754*4882a593Smuzhiyun if (V4L2_FIELD_HAS_T_OR_B(field))
755*4882a593Smuzhiyun maxh /= 2;
756*4882a593Smuzhiyun
757*4882a593Smuzhiyun v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2,
758*4882a593Smuzhiyun &f->fmt.pix.height, 32, maxh, 0, 0);
759*4882a593Smuzhiyun f->fmt.pix.field = field;
760*4882a593Smuzhiyun f->fmt.pix.bytesperline =
761*4882a593Smuzhiyun (f->fmt.pix.width * fmt->depth) >> 3;
762*4882a593Smuzhiyun f->fmt.pix.sizeimage =
763*4882a593Smuzhiyun f->fmt.pix.height * f->fmt.pix.bytesperline;
764*4882a593Smuzhiyun f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
765*4882a593Smuzhiyun
766*4882a593Smuzhiyun return 0;
767*4882a593Smuzhiyun }
768*4882a593Smuzhiyun
vidioc_s_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)769*4882a593Smuzhiyun static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
770*4882a593Smuzhiyun struct v4l2_format *f)
771*4882a593Smuzhiyun {
772*4882a593Smuzhiyun struct cx8800_dev *dev = video_drvdata(file);
773*4882a593Smuzhiyun struct cx88_core *core = dev->core;
774*4882a593Smuzhiyun int err = vidioc_try_fmt_vid_cap(file, priv, f);
775*4882a593Smuzhiyun
776*4882a593Smuzhiyun if (err != 0)
777*4882a593Smuzhiyun return err;
778*4882a593Smuzhiyun if (vb2_is_busy(&dev->vb2_vidq) || vb2_is_busy(&dev->vb2_vbiq))
779*4882a593Smuzhiyun return -EBUSY;
780*4882a593Smuzhiyun if (core->dvbdev && vb2_is_busy(&core->dvbdev->vb2_mpegq))
781*4882a593Smuzhiyun return -EBUSY;
782*4882a593Smuzhiyun dev->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
783*4882a593Smuzhiyun core->width = f->fmt.pix.width;
784*4882a593Smuzhiyun core->height = f->fmt.pix.height;
785*4882a593Smuzhiyun core->field = f->fmt.pix.field;
786*4882a593Smuzhiyun return 0;
787*4882a593Smuzhiyun }
788*4882a593Smuzhiyun
cx88_querycap(struct file * file,struct cx88_core * core,struct v4l2_capability * cap)789*4882a593Smuzhiyun int cx88_querycap(struct file *file, struct cx88_core *core,
790*4882a593Smuzhiyun struct v4l2_capability *cap)
791*4882a593Smuzhiyun {
792*4882a593Smuzhiyun strscpy(cap->card, core->board.name, sizeof(cap->card));
793*4882a593Smuzhiyun cap->capabilities = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING |
794*4882a593Smuzhiyun V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VBI_CAPTURE |
795*4882a593Smuzhiyun V4L2_CAP_DEVICE_CAPS;
796*4882a593Smuzhiyun if (core->board.tuner_type != UNSET)
797*4882a593Smuzhiyun cap->capabilities |= V4L2_CAP_TUNER;
798*4882a593Smuzhiyun if (core->board.radio.type == CX88_RADIO)
799*4882a593Smuzhiyun cap->capabilities |= V4L2_CAP_RADIO;
800*4882a593Smuzhiyun return 0;
801*4882a593Smuzhiyun }
802*4882a593Smuzhiyun EXPORT_SYMBOL(cx88_querycap);
803*4882a593Smuzhiyun
vidioc_querycap(struct file * file,void * priv,struct v4l2_capability * cap)804*4882a593Smuzhiyun static int vidioc_querycap(struct file *file, void *priv,
805*4882a593Smuzhiyun struct v4l2_capability *cap)
806*4882a593Smuzhiyun {
807*4882a593Smuzhiyun struct cx8800_dev *dev = video_drvdata(file);
808*4882a593Smuzhiyun struct cx88_core *core = dev->core;
809*4882a593Smuzhiyun
810*4882a593Smuzhiyun strscpy(cap->driver, "cx8800", sizeof(cap->driver));
811*4882a593Smuzhiyun sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
812*4882a593Smuzhiyun return cx88_querycap(file, core, cap);
813*4882a593Smuzhiyun }
814*4882a593Smuzhiyun
vidioc_enum_fmt_vid_cap(struct file * file,void * priv,struct v4l2_fmtdesc * f)815*4882a593Smuzhiyun static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
816*4882a593Smuzhiyun struct v4l2_fmtdesc *f)
817*4882a593Smuzhiyun {
818*4882a593Smuzhiyun if (unlikely(f->index >= ARRAY_SIZE(formats)))
819*4882a593Smuzhiyun return -EINVAL;
820*4882a593Smuzhiyun
821*4882a593Smuzhiyun f->pixelformat = formats[f->index].fourcc;
822*4882a593Smuzhiyun
823*4882a593Smuzhiyun return 0;
824*4882a593Smuzhiyun }
825*4882a593Smuzhiyun
vidioc_g_std(struct file * file,void * priv,v4l2_std_id * tvnorm)826*4882a593Smuzhiyun static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorm)
827*4882a593Smuzhiyun {
828*4882a593Smuzhiyun struct cx8800_dev *dev = video_drvdata(file);
829*4882a593Smuzhiyun struct cx88_core *core = dev->core;
830*4882a593Smuzhiyun
831*4882a593Smuzhiyun *tvnorm = core->tvnorm;
832*4882a593Smuzhiyun return 0;
833*4882a593Smuzhiyun }
834*4882a593Smuzhiyun
vidioc_s_std(struct file * file,void * priv,v4l2_std_id tvnorms)835*4882a593Smuzhiyun static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id tvnorms)
836*4882a593Smuzhiyun {
837*4882a593Smuzhiyun struct cx8800_dev *dev = video_drvdata(file);
838*4882a593Smuzhiyun struct cx88_core *core = dev->core;
839*4882a593Smuzhiyun
840*4882a593Smuzhiyun return cx88_set_tvnorm(core, tvnorms);
841*4882a593Smuzhiyun }
842*4882a593Smuzhiyun
843*4882a593Smuzhiyun /* only one input in this sample driver */
cx88_enum_input(struct cx88_core * core,struct v4l2_input * i)844*4882a593Smuzhiyun int cx88_enum_input(struct cx88_core *core, struct v4l2_input *i)
845*4882a593Smuzhiyun {
846*4882a593Smuzhiyun static const char * const iname[] = {
847*4882a593Smuzhiyun [CX88_VMUX_COMPOSITE1] = "Composite1",
848*4882a593Smuzhiyun [CX88_VMUX_COMPOSITE2] = "Composite2",
849*4882a593Smuzhiyun [CX88_VMUX_COMPOSITE3] = "Composite3",
850*4882a593Smuzhiyun [CX88_VMUX_COMPOSITE4] = "Composite4",
851*4882a593Smuzhiyun [CX88_VMUX_SVIDEO] = "S-Video",
852*4882a593Smuzhiyun [CX88_VMUX_TELEVISION] = "Television",
853*4882a593Smuzhiyun [CX88_VMUX_CABLE] = "Cable TV",
854*4882a593Smuzhiyun [CX88_VMUX_DVB] = "DVB",
855*4882a593Smuzhiyun [CX88_VMUX_DEBUG] = "for debug only",
856*4882a593Smuzhiyun };
857*4882a593Smuzhiyun unsigned int n = i->index;
858*4882a593Smuzhiyun
859*4882a593Smuzhiyun if (n >= 4)
860*4882a593Smuzhiyun return -EINVAL;
861*4882a593Smuzhiyun if (!INPUT(n).type)
862*4882a593Smuzhiyun return -EINVAL;
863*4882a593Smuzhiyun i->type = V4L2_INPUT_TYPE_CAMERA;
864*4882a593Smuzhiyun strscpy(i->name, iname[INPUT(n).type], sizeof(i->name));
865*4882a593Smuzhiyun if ((INPUT(n).type == CX88_VMUX_TELEVISION) ||
866*4882a593Smuzhiyun (INPUT(n).type == CX88_VMUX_CABLE))
867*4882a593Smuzhiyun i->type = V4L2_INPUT_TYPE_TUNER;
868*4882a593Smuzhiyun
869*4882a593Smuzhiyun i->std = CX88_NORMS;
870*4882a593Smuzhiyun return 0;
871*4882a593Smuzhiyun }
872*4882a593Smuzhiyun EXPORT_SYMBOL(cx88_enum_input);
873*4882a593Smuzhiyun
vidioc_enum_input(struct file * file,void * priv,struct v4l2_input * i)874*4882a593Smuzhiyun static int vidioc_enum_input(struct file *file, void *priv,
875*4882a593Smuzhiyun struct v4l2_input *i)
876*4882a593Smuzhiyun {
877*4882a593Smuzhiyun struct cx8800_dev *dev = video_drvdata(file);
878*4882a593Smuzhiyun struct cx88_core *core = dev->core;
879*4882a593Smuzhiyun
880*4882a593Smuzhiyun return cx88_enum_input(core, i);
881*4882a593Smuzhiyun }
882*4882a593Smuzhiyun
vidioc_g_input(struct file * file,void * priv,unsigned int * i)883*4882a593Smuzhiyun static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
884*4882a593Smuzhiyun {
885*4882a593Smuzhiyun struct cx8800_dev *dev = video_drvdata(file);
886*4882a593Smuzhiyun struct cx88_core *core = dev->core;
887*4882a593Smuzhiyun
888*4882a593Smuzhiyun *i = core->input;
889*4882a593Smuzhiyun return 0;
890*4882a593Smuzhiyun }
891*4882a593Smuzhiyun
vidioc_s_input(struct file * file,void * priv,unsigned int i)892*4882a593Smuzhiyun static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
893*4882a593Smuzhiyun {
894*4882a593Smuzhiyun struct cx8800_dev *dev = video_drvdata(file);
895*4882a593Smuzhiyun struct cx88_core *core = dev->core;
896*4882a593Smuzhiyun
897*4882a593Smuzhiyun if (i >= 4)
898*4882a593Smuzhiyun return -EINVAL;
899*4882a593Smuzhiyun if (!INPUT(i).type)
900*4882a593Smuzhiyun return -EINVAL;
901*4882a593Smuzhiyun
902*4882a593Smuzhiyun cx88_newstation(core);
903*4882a593Smuzhiyun cx88_video_mux(core, i);
904*4882a593Smuzhiyun return 0;
905*4882a593Smuzhiyun }
906*4882a593Smuzhiyun
vidioc_g_tuner(struct file * file,void * priv,struct v4l2_tuner * t)907*4882a593Smuzhiyun static int vidioc_g_tuner(struct file *file, void *priv,
908*4882a593Smuzhiyun struct v4l2_tuner *t)
909*4882a593Smuzhiyun {
910*4882a593Smuzhiyun struct cx8800_dev *dev = video_drvdata(file);
911*4882a593Smuzhiyun struct cx88_core *core = dev->core;
912*4882a593Smuzhiyun u32 reg;
913*4882a593Smuzhiyun
914*4882a593Smuzhiyun if (unlikely(core->board.tuner_type == UNSET))
915*4882a593Smuzhiyun return -EINVAL;
916*4882a593Smuzhiyun if (t->index != 0)
917*4882a593Smuzhiyun return -EINVAL;
918*4882a593Smuzhiyun
919*4882a593Smuzhiyun strscpy(t->name, "Television", sizeof(t->name));
920*4882a593Smuzhiyun t->capability = V4L2_TUNER_CAP_NORM;
921*4882a593Smuzhiyun t->rangehigh = 0xffffffffUL;
922*4882a593Smuzhiyun call_all(core, tuner, g_tuner, t);
923*4882a593Smuzhiyun
924*4882a593Smuzhiyun cx88_get_stereo(core, t);
925*4882a593Smuzhiyun reg = cx_read(MO_DEVICE_STATUS);
926*4882a593Smuzhiyun t->signal = (reg & (1 << 5)) ? 0xffff : 0x0000;
927*4882a593Smuzhiyun return 0;
928*4882a593Smuzhiyun }
929*4882a593Smuzhiyun
vidioc_s_tuner(struct file * file,void * priv,const struct v4l2_tuner * t)930*4882a593Smuzhiyun static int vidioc_s_tuner(struct file *file, void *priv,
931*4882a593Smuzhiyun const struct v4l2_tuner *t)
932*4882a593Smuzhiyun {
933*4882a593Smuzhiyun struct cx8800_dev *dev = video_drvdata(file);
934*4882a593Smuzhiyun struct cx88_core *core = dev->core;
935*4882a593Smuzhiyun
936*4882a593Smuzhiyun if (core->board.tuner_type == UNSET)
937*4882a593Smuzhiyun return -EINVAL;
938*4882a593Smuzhiyun if (t->index != 0)
939*4882a593Smuzhiyun return -EINVAL;
940*4882a593Smuzhiyun
941*4882a593Smuzhiyun cx88_set_stereo(core, t->audmode, 1);
942*4882a593Smuzhiyun return 0;
943*4882a593Smuzhiyun }
944*4882a593Smuzhiyun
vidioc_g_frequency(struct file * file,void * priv,struct v4l2_frequency * f)945*4882a593Smuzhiyun static int vidioc_g_frequency(struct file *file, void *priv,
946*4882a593Smuzhiyun struct v4l2_frequency *f)
947*4882a593Smuzhiyun {
948*4882a593Smuzhiyun struct cx8800_dev *dev = video_drvdata(file);
949*4882a593Smuzhiyun struct cx88_core *core = dev->core;
950*4882a593Smuzhiyun
951*4882a593Smuzhiyun if (unlikely(core->board.tuner_type == UNSET))
952*4882a593Smuzhiyun return -EINVAL;
953*4882a593Smuzhiyun if (f->tuner)
954*4882a593Smuzhiyun return -EINVAL;
955*4882a593Smuzhiyun
956*4882a593Smuzhiyun f->frequency = core->freq;
957*4882a593Smuzhiyun
958*4882a593Smuzhiyun call_all(core, tuner, g_frequency, f);
959*4882a593Smuzhiyun
960*4882a593Smuzhiyun return 0;
961*4882a593Smuzhiyun }
962*4882a593Smuzhiyun
cx88_set_freq(struct cx88_core * core,const struct v4l2_frequency * f)963*4882a593Smuzhiyun int cx88_set_freq(struct cx88_core *core,
964*4882a593Smuzhiyun const struct v4l2_frequency *f)
965*4882a593Smuzhiyun {
966*4882a593Smuzhiyun struct v4l2_frequency new_freq = *f;
967*4882a593Smuzhiyun
968*4882a593Smuzhiyun if (unlikely(core->board.tuner_type == UNSET))
969*4882a593Smuzhiyun return -EINVAL;
970*4882a593Smuzhiyun if (unlikely(f->tuner != 0))
971*4882a593Smuzhiyun return -EINVAL;
972*4882a593Smuzhiyun
973*4882a593Smuzhiyun cx88_newstation(core);
974*4882a593Smuzhiyun call_all(core, tuner, s_frequency, f);
975*4882a593Smuzhiyun call_all(core, tuner, g_frequency, &new_freq);
976*4882a593Smuzhiyun core->freq = new_freq.frequency;
977*4882a593Smuzhiyun
978*4882a593Smuzhiyun /* When changing channels it is required to reset TVAUDIO */
979*4882a593Smuzhiyun usleep_range(10000, 20000);
980*4882a593Smuzhiyun cx88_set_tvaudio(core);
981*4882a593Smuzhiyun
982*4882a593Smuzhiyun return 0;
983*4882a593Smuzhiyun }
984*4882a593Smuzhiyun EXPORT_SYMBOL(cx88_set_freq);
985*4882a593Smuzhiyun
vidioc_s_frequency(struct file * file,void * priv,const struct v4l2_frequency * f)986*4882a593Smuzhiyun static int vidioc_s_frequency(struct file *file, void *priv,
987*4882a593Smuzhiyun const struct v4l2_frequency *f)
988*4882a593Smuzhiyun {
989*4882a593Smuzhiyun struct cx8800_dev *dev = video_drvdata(file);
990*4882a593Smuzhiyun struct cx88_core *core = dev->core;
991*4882a593Smuzhiyun
992*4882a593Smuzhiyun return cx88_set_freq(core, f);
993*4882a593Smuzhiyun }
994*4882a593Smuzhiyun
995*4882a593Smuzhiyun #ifdef CONFIG_VIDEO_ADV_DEBUG
vidioc_g_register(struct file * file,void * fh,struct v4l2_dbg_register * reg)996*4882a593Smuzhiyun static int vidioc_g_register(struct file *file, void *fh,
997*4882a593Smuzhiyun struct v4l2_dbg_register *reg)
998*4882a593Smuzhiyun {
999*4882a593Smuzhiyun struct cx8800_dev *dev = video_drvdata(file);
1000*4882a593Smuzhiyun struct cx88_core *core = dev->core;
1001*4882a593Smuzhiyun
1002*4882a593Smuzhiyun /* cx2388x has a 24-bit register space */
1003*4882a593Smuzhiyun reg->val = cx_read(reg->reg & 0xfffffc);
1004*4882a593Smuzhiyun reg->size = 4;
1005*4882a593Smuzhiyun return 0;
1006*4882a593Smuzhiyun }
1007*4882a593Smuzhiyun
vidioc_s_register(struct file * file,void * fh,const struct v4l2_dbg_register * reg)1008*4882a593Smuzhiyun static int vidioc_s_register(struct file *file, void *fh,
1009*4882a593Smuzhiyun const struct v4l2_dbg_register *reg)
1010*4882a593Smuzhiyun {
1011*4882a593Smuzhiyun struct cx8800_dev *dev = video_drvdata(file);
1012*4882a593Smuzhiyun struct cx88_core *core = dev->core;
1013*4882a593Smuzhiyun
1014*4882a593Smuzhiyun cx_write(reg->reg & 0xfffffc, reg->val);
1015*4882a593Smuzhiyun return 0;
1016*4882a593Smuzhiyun }
1017*4882a593Smuzhiyun #endif
1018*4882a593Smuzhiyun
1019*4882a593Smuzhiyun /* ----------------------------------------------------------- */
1020*4882a593Smuzhiyun /* RADIO ESPECIFIC IOCTLS */
1021*4882a593Smuzhiyun /* ----------------------------------------------------------- */
1022*4882a593Smuzhiyun
radio_g_tuner(struct file * file,void * priv,struct v4l2_tuner * t)1023*4882a593Smuzhiyun static int radio_g_tuner(struct file *file, void *priv,
1024*4882a593Smuzhiyun struct v4l2_tuner *t)
1025*4882a593Smuzhiyun {
1026*4882a593Smuzhiyun struct cx8800_dev *dev = video_drvdata(file);
1027*4882a593Smuzhiyun struct cx88_core *core = dev->core;
1028*4882a593Smuzhiyun
1029*4882a593Smuzhiyun if (unlikely(t->index > 0))
1030*4882a593Smuzhiyun return -EINVAL;
1031*4882a593Smuzhiyun
1032*4882a593Smuzhiyun strscpy(t->name, "Radio", sizeof(t->name));
1033*4882a593Smuzhiyun
1034*4882a593Smuzhiyun call_all(core, tuner, g_tuner, t);
1035*4882a593Smuzhiyun return 0;
1036*4882a593Smuzhiyun }
1037*4882a593Smuzhiyun
radio_s_tuner(struct file * file,void * priv,const struct v4l2_tuner * t)1038*4882a593Smuzhiyun static int radio_s_tuner(struct file *file, void *priv,
1039*4882a593Smuzhiyun const struct v4l2_tuner *t)
1040*4882a593Smuzhiyun {
1041*4882a593Smuzhiyun struct cx8800_dev *dev = video_drvdata(file);
1042*4882a593Smuzhiyun struct cx88_core *core = dev->core;
1043*4882a593Smuzhiyun
1044*4882a593Smuzhiyun if (t->index != 0)
1045*4882a593Smuzhiyun return -EINVAL;
1046*4882a593Smuzhiyun
1047*4882a593Smuzhiyun call_all(core, tuner, s_tuner, t);
1048*4882a593Smuzhiyun return 0;
1049*4882a593Smuzhiyun }
1050*4882a593Smuzhiyun
1051*4882a593Smuzhiyun /* ----------------------------------------------------------- */
1052*4882a593Smuzhiyun
1053*4882a593Smuzhiyun static const char *cx88_vid_irqs[32] = {
1054*4882a593Smuzhiyun "y_risci1", "u_risci1", "v_risci1", "vbi_risc1",
1055*4882a593Smuzhiyun "y_risci2", "u_risci2", "v_risci2", "vbi_risc2",
1056*4882a593Smuzhiyun "y_oflow", "u_oflow", "v_oflow", "vbi_oflow",
1057*4882a593Smuzhiyun "y_sync", "u_sync", "v_sync", "vbi_sync",
1058*4882a593Smuzhiyun "opc_err", "par_err", "rip_err", "pci_abort",
1059*4882a593Smuzhiyun };
1060*4882a593Smuzhiyun
cx8800_vid_irq(struct cx8800_dev * dev)1061*4882a593Smuzhiyun static void cx8800_vid_irq(struct cx8800_dev *dev)
1062*4882a593Smuzhiyun {
1063*4882a593Smuzhiyun struct cx88_core *core = dev->core;
1064*4882a593Smuzhiyun u32 status, mask, count;
1065*4882a593Smuzhiyun
1066*4882a593Smuzhiyun status = cx_read(MO_VID_INTSTAT);
1067*4882a593Smuzhiyun mask = cx_read(MO_VID_INTMSK);
1068*4882a593Smuzhiyun if (0 == (status & mask))
1069*4882a593Smuzhiyun return;
1070*4882a593Smuzhiyun cx_write(MO_VID_INTSTAT, status);
1071*4882a593Smuzhiyun if (irq_debug || (status & mask & ~0xff))
1072*4882a593Smuzhiyun cx88_print_irqbits("irq vid",
1073*4882a593Smuzhiyun cx88_vid_irqs, ARRAY_SIZE(cx88_vid_irqs),
1074*4882a593Smuzhiyun status, mask);
1075*4882a593Smuzhiyun
1076*4882a593Smuzhiyun /* risc op code error */
1077*4882a593Smuzhiyun if (status & (1 << 16)) {
1078*4882a593Smuzhiyun pr_warn("video risc op code error\n");
1079*4882a593Smuzhiyun cx_clear(MO_VID_DMACNTRL, 0x11);
1080*4882a593Smuzhiyun cx_clear(VID_CAPTURE_CONTROL, 0x06);
1081*4882a593Smuzhiyun cx88_sram_channel_dump(core, &cx88_sram_channels[SRAM_CH21]);
1082*4882a593Smuzhiyun }
1083*4882a593Smuzhiyun
1084*4882a593Smuzhiyun /* risc1 y */
1085*4882a593Smuzhiyun if (status & 0x01) {
1086*4882a593Smuzhiyun spin_lock(&dev->slock);
1087*4882a593Smuzhiyun count = cx_read(MO_VIDY_GPCNT);
1088*4882a593Smuzhiyun cx88_wakeup(core, &dev->vidq, count);
1089*4882a593Smuzhiyun spin_unlock(&dev->slock);
1090*4882a593Smuzhiyun }
1091*4882a593Smuzhiyun
1092*4882a593Smuzhiyun /* risc1 vbi */
1093*4882a593Smuzhiyun if (status & 0x08) {
1094*4882a593Smuzhiyun spin_lock(&dev->slock);
1095*4882a593Smuzhiyun count = cx_read(MO_VBI_GPCNT);
1096*4882a593Smuzhiyun cx88_wakeup(core, &dev->vbiq, count);
1097*4882a593Smuzhiyun spin_unlock(&dev->slock);
1098*4882a593Smuzhiyun }
1099*4882a593Smuzhiyun }
1100*4882a593Smuzhiyun
cx8800_irq(int irq,void * dev_id)1101*4882a593Smuzhiyun static irqreturn_t cx8800_irq(int irq, void *dev_id)
1102*4882a593Smuzhiyun {
1103*4882a593Smuzhiyun struct cx8800_dev *dev = dev_id;
1104*4882a593Smuzhiyun struct cx88_core *core = dev->core;
1105*4882a593Smuzhiyun u32 status;
1106*4882a593Smuzhiyun int loop, handled = 0;
1107*4882a593Smuzhiyun
1108*4882a593Smuzhiyun for (loop = 0; loop < 10; loop++) {
1109*4882a593Smuzhiyun status = cx_read(MO_PCI_INTSTAT) &
1110*4882a593Smuzhiyun (core->pci_irqmask | PCI_INT_VIDINT);
1111*4882a593Smuzhiyun if (status == 0)
1112*4882a593Smuzhiyun goto out;
1113*4882a593Smuzhiyun cx_write(MO_PCI_INTSTAT, status);
1114*4882a593Smuzhiyun handled = 1;
1115*4882a593Smuzhiyun
1116*4882a593Smuzhiyun if (status & core->pci_irqmask)
1117*4882a593Smuzhiyun cx88_core_irq(core, status);
1118*4882a593Smuzhiyun if (status & PCI_INT_VIDINT)
1119*4882a593Smuzhiyun cx8800_vid_irq(dev);
1120*4882a593Smuzhiyun }
1121*4882a593Smuzhiyun if (loop == 10) {
1122*4882a593Smuzhiyun pr_warn("irq loop -- clearing mask\n");
1123*4882a593Smuzhiyun cx_write(MO_PCI_INTMSK, 0);
1124*4882a593Smuzhiyun }
1125*4882a593Smuzhiyun
1126*4882a593Smuzhiyun out:
1127*4882a593Smuzhiyun return IRQ_RETVAL(handled);
1128*4882a593Smuzhiyun }
1129*4882a593Smuzhiyun
1130*4882a593Smuzhiyun /* ----------------------------------------------------------- */
1131*4882a593Smuzhiyun /* exported stuff */
1132*4882a593Smuzhiyun
1133*4882a593Smuzhiyun static const struct v4l2_file_operations video_fops = {
1134*4882a593Smuzhiyun .owner = THIS_MODULE,
1135*4882a593Smuzhiyun .open = v4l2_fh_open,
1136*4882a593Smuzhiyun .release = vb2_fop_release,
1137*4882a593Smuzhiyun .read = vb2_fop_read,
1138*4882a593Smuzhiyun .poll = vb2_fop_poll,
1139*4882a593Smuzhiyun .mmap = vb2_fop_mmap,
1140*4882a593Smuzhiyun .unlocked_ioctl = video_ioctl2,
1141*4882a593Smuzhiyun };
1142*4882a593Smuzhiyun
1143*4882a593Smuzhiyun static const struct v4l2_ioctl_ops video_ioctl_ops = {
1144*4882a593Smuzhiyun .vidioc_querycap = vidioc_querycap,
1145*4882a593Smuzhiyun .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
1146*4882a593Smuzhiyun .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
1147*4882a593Smuzhiyun .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
1148*4882a593Smuzhiyun .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
1149*4882a593Smuzhiyun .vidioc_reqbufs = vb2_ioctl_reqbufs,
1150*4882a593Smuzhiyun .vidioc_querybuf = vb2_ioctl_querybuf,
1151*4882a593Smuzhiyun .vidioc_qbuf = vb2_ioctl_qbuf,
1152*4882a593Smuzhiyun .vidioc_dqbuf = vb2_ioctl_dqbuf,
1153*4882a593Smuzhiyun .vidioc_g_std = vidioc_g_std,
1154*4882a593Smuzhiyun .vidioc_s_std = vidioc_s_std,
1155*4882a593Smuzhiyun .vidioc_enum_input = vidioc_enum_input,
1156*4882a593Smuzhiyun .vidioc_g_input = vidioc_g_input,
1157*4882a593Smuzhiyun .vidioc_s_input = vidioc_s_input,
1158*4882a593Smuzhiyun .vidioc_streamon = vb2_ioctl_streamon,
1159*4882a593Smuzhiyun .vidioc_streamoff = vb2_ioctl_streamoff,
1160*4882a593Smuzhiyun .vidioc_g_tuner = vidioc_g_tuner,
1161*4882a593Smuzhiyun .vidioc_s_tuner = vidioc_s_tuner,
1162*4882a593Smuzhiyun .vidioc_g_frequency = vidioc_g_frequency,
1163*4882a593Smuzhiyun .vidioc_s_frequency = vidioc_s_frequency,
1164*4882a593Smuzhiyun .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
1165*4882a593Smuzhiyun .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
1166*4882a593Smuzhiyun #ifdef CONFIG_VIDEO_ADV_DEBUG
1167*4882a593Smuzhiyun .vidioc_g_register = vidioc_g_register,
1168*4882a593Smuzhiyun .vidioc_s_register = vidioc_s_register,
1169*4882a593Smuzhiyun #endif
1170*4882a593Smuzhiyun };
1171*4882a593Smuzhiyun
1172*4882a593Smuzhiyun static const struct video_device cx8800_video_template = {
1173*4882a593Smuzhiyun .name = "cx8800-video",
1174*4882a593Smuzhiyun .fops = &video_fops,
1175*4882a593Smuzhiyun .ioctl_ops = &video_ioctl_ops,
1176*4882a593Smuzhiyun .tvnorms = CX88_NORMS,
1177*4882a593Smuzhiyun };
1178*4882a593Smuzhiyun
1179*4882a593Smuzhiyun static const struct v4l2_ioctl_ops vbi_ioctl_ops = {
1180*4882a593Smuzhiyun .vidioc_querycap = vidioc_querycap,
1181*4882a593Smuzhiyun .vidioc_g_fmt_vbi_cap = cx8800_vbi_fmt,
1182*4882a593Smuzhiyun .vidioc_try_fmt_vbi_cap = cx8800_vbi_fmt,
1183*4882a593Smuzhiyun .vidioc_s_fmt_vbi_cap = cx8800_vbi_fmt,
1184*4882a593Smuzhiyun .vidioc_reqbufs = vb2_ioctl_reqbufs,
1185*4882a593Smuzhiyun .vidioc_querybuf = vb2_ioctl_querybuf,
1186*4882a593Smuzhiyun .vidioc_qbuf = vb2_ioctl_qbuf,
1187*4882a593Smuzhiyun .vidioc_dqbuf = vb2_ioctl_dqbuf,
1188*4882a593Smuzhiyun .vidioc_g_std = vidioc_g_std,
1189*4882a593Smuzhiyun .vidioc_s_std = vidioc_s_std,
1190*4882a593Smuzhiyun .vidioc_enum_input = vidioc_enum_input,
1191*4882a593Smuzhiyun .vidioc_g_input = vidioc_g_input,
1192*4882a593Smuzhiyun .vidioc_s_input = vidioc_s_input,
1193*4882a593Smuzhiyun .vidioc_streamon = vb2_ioctl_streamon,
1194*4882a593Smuzhiyun .vidioc_streamoff = vb2_ioctl_streamoff,
1195*4882a593Smuzhiyun .vidioc_g_tuner = vidioc_g_tuner,
1196*4882a593Smuzhiyun .vidioc_s_tuner = vidioc_s_tuner,
1197*4882a593Smuzhiyun .vidioc_g_frequency = vidioc_g_frequency,
1198*4882a593Smuzhiyun .vidioc_s_frequency = vidioc_s_frequency,
1199*4882a593Smuzhiyun #ifdef CONFIG_VIDEO_ADV_DEBUG
1200*4882a593Smuzhiyun .vidioc_g_register = vidioc_g_register,
1201*4882a593Smuzhiyun .vidioc_s_register = vidioc_s_register,
1202*4882a593Smuzhiyun #endif
1203*4882a593Smuzhiyun };
1204*4882a593Smuzhiyun
1205*4882a593Smuzhiyun static const struct video_device cx8800_vbi_template = {
1206*4882a593Smuzhiyun .name = "cx8800-vbi",
1207*4882a593Smuzhiyun .fops = &video_fops,
1208*4882a593Smuzhiyun .ioctl_ops = &vbi_ioctl_ops,
1209*4882a593Smuzhiyun .tvnorms = CX88_NORMS,
1210*4882a593Smuzhiyun };
1211*4882a593Smuzhiyun
1212*4882a593Smuzhiyun static const struct v4l2_file_operations radio_fops = {
1213*4882a593Smuzhiyun .owner = THIS_MODULE,
1214*4882a593Smuzhiyun .open = radio_open,
1215*4882a593Smuzhiyun .poll = v4l2_ctrl_poll,
1216*4882a593Smuzhiyun .release = v4l2_fh_release,
1217*4882a593Smuzhiyun .unlocked_ioctl = video_ioctl2,
1218*4882a593Smuzhiyun };
1219*4882a593Smuzhiyun
1220*4882a593Smuzhiyun static const struct v4l2_ioctl_ops radio_ioctl_ops = {
1221*4882a593Smuzhiyun .vidioc_querycap = vidioc_querycap,
1222*4882a593Smuzhiyun .vidioc_g_tuner = radio_g_tuner,
1223*4882a593Smuzhiyun .vidioc_s_tuner = radio_s_tuner,
1224*4882a593Smuzhiyun .vidioc_g_frequency = vidioc_g_frequency,
1225*4882a593Smuzhiyun .vidioc_s_frequency = vidioc_s_frequency,
1226*4882a593Smuzhiyun .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
1227*4882a593Smuzhiyun .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
1228*4882a593Smuzhiyun #ifdef CONFIG_VIDEO_ADV_DEBUG
1229*4882a593Smuzhiyun .vidioc_g_register = vidioc_g_register,
1230*4882a593Smuzhiyun .vidioc_s_register = vidioc_s_register,
1231*4882a593Smuzhiyun #endif
1232*4882a593Smuzhiyun };
1233*4882a593Smuzhiyun
1234*4882a593Smuzhiyun static const struct video_device cx8800_radio_template = {
1235*4882a593Smuzhiyun .name = "cx8800-radio",
1236*4882a593Smuzhiyun .fops = &radio_fops,
1237*4882a593Smuzhiyun .ioctl_ops = &radio_ioctl_ops,
1238*4882a593Smuzhiyun };
1239*4882a593Smuzhiyun
1240*4882a593Smuzhiyun static const struct v4l2_ctrl_ops cx8800_ctrl_vid_ops = {
1241*4882a593Smuzhiyun .s_ctrl = cx8800_s_vid_ctrl,
1242*4882a593Smuzhiyun };
1243*4882a593Smuzhiyun
1244*4882a593Smuzhiyun static const struct v4l2_ctrl_ops cx8800_ctrl_aud_ops = {
1245*4882a593Smuzhiyun .s_ctrl = cx8800_s_aud_ctrl,
1246*4882a593Smuzhiyun };
1247*4882a593Smuzhiyun
1248*4882a593Smuzhiyun /* ----------------------------------------------------------- */
1249*4882a593Smuzhiyun
cx8800_unregister_video(struct cx8800_dev * dev)1250*4882a593Smuzhiyun static void cx8800_unregister_video(struct cx8800_dev *dev)
1251*4882a593Smuzhiyun {
1252*4882a593Smuzhiyun video_unregister_device(&dev->radio_dev);
1253*4882a593Smuzhiyun video_unregister_device(&dev->vbi_dev);
1254*4882a593Smuzhiyun video_unregister_device(&dev->video_dev);
1255*4882a593Smuzhiyun }
1256*4882a593Smuzhiyun
cx8800_initdev(struct pci_dev * pci_dev,const struct pci_device_id * pci_id)1257*4882a593Smuzhiyun static int cx8800_initdev(struct pci_dev *pci_dev,
1258*4882a593Smuzhiyun const struct pci_device_id *pci_id)
1259*4882a593Smuzhiyun {
1260*4882a593Smuzhiyun struct cx8800_dev *dev;
1261*4882a593Smuzhiyun struct cx88_core *core;
1262*4882a593Smuzhiyun struct vb2_queue *q;
1263*4882a593Smuzhiyun int err;
1264*4882a593Smuzhiyun int i;
1265*4882a593Smuzhiyun
1266*4882a593Smuzhiyun dev = kzalloc(sizeof(*dev), GFP_KERNEL);
1267*4882a593Smuzhiyun if (!dev)
1268*4882a593Smuzhiyun return -ENOMEM;
1269*4882a593Smuzhiyun
1270*4882a593Smuzhiyun /* pci init */
1271*4882a593Smuzhiyun dev->pci = pci_dev;
1272*4882a593Smuzhiyun if (pci_enable_device(pci_dev)) {
1273*4882a593Smuzhiyun err = -EIO;
1274*4882a593Smuzhiyun goto fail_free;
1275*4882a593Smuzhiyun }
1276*4882a593Smuzhiyun core = cx88_core_get(dev->pci);
1277*4882a593Smuzhiyun if (!core) {
1278*4882a593Smuzhiyun err = -EINVAL;
1279*4882a593Smuzhiyun goto fail_disable;
1280*4882a593Smuzhiyun }
1281*4882a593Smuzhiyun dev->core = core;
1282*4882a593Smuzhiyun
1283*4882a593Smuzhiyun /* print pci info */
1284*4882a593Smuzhiyun dev->pci_rev = pci_dev->revision;
1285*4882a593Smuzhiyun pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat);
1286*4882a593Smuzhiyun pr_info("found at %s, rev: %d, irq: %d, latency: %d, mmio: 0x%llx\n",
1287*4882a593Smuzhiyun pci_name(pci_dev), dev->pci_rev, pci_dev->irq,
1288*4882a593Smuzhiyun dev->pci_lat,
1289*4882a593Smuzhiyun (unsigned long long)pci_resource_start(pci_dev, 0));
1290*4882a593Smuzhiyun
1291*4882a593Smuzhiyun pci_set_master(pci_dev);
1292*4882a593Smuzhiyun err = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32));
1293*4882a593Smuzhiyun if (err) {
1294*4882a593Smuzhiyun pr_err("Oops: no 32bit PCI DMA ???\n");
1295*4882a593Smuzhiyun goto fail_core;
1296*4882a593Smuzhiyun }
1297*4882a593Smuzhiyun
1298*4882a593Smuzhiyun /* initialize driver struct */
1299*4882a593Smuzhiyun spin_lock_init(&dev->slock);
1300*4882a593Smuzhiyun
1301*4882a593Smuzhiyun /* init video dma queues */
1302*4882a593Smuzhiyun INIT_LIST_HEAD(&dev->vidq.active);
1303*4882a593Smuzhiyun
1304*4882a593Smuzhiyun /* init vbi dma queues */
1305*4882a593Smuzhiyun INIT_LIST_HEAD(&dev->vbiq.active);
1306*4882a593Smuzhiyun
1307*4882a593Smuzhiyun /* get irq */
1308*4882a593Smuzhiyun err = request_irq(pci_dev->irq, cx8800_irq,
1309*4882a593Smuzhiyun IRQF_SHARED, core->name, dev);
1310*4882a593Smuzhiyun if (err < 0) {
1311*4882a593Smuzhiyun pr_err("can't get IRQ %d\n", pci_dev->irq);
1312*4882a593Smuzhiyun goto fail_core;
1313*4882a593Smuzhiyun }
1314*4882a593Smuzhiyun cx_set(MO_PCI_INTMSK, core->pci_irqmask);
1315*4882a593Smuzhiyun
1316*4882a593Smuzhiyun for (i = 0; i < CX8800_AUD_CTLS; i++) {
1317*4882a593Smuzhiyun const struct cx88_ctrl *cc = &cx8800_aud_ctls[i];
1318*4882a593Smuzhiyun struct v4l2_ctrl *vc;
1319*4882a593Smuzhiyun
1320*4882a593Smuzhiyun vc = v4l2_ctrl_new_std(&core->audio_hdl, &cx8800_ctrl_aud_ops,
1321*4882a593Smuzhiyun cc->id, cc->minimum, cc->maximum,
1322*4882a593Smuzhiyun cc->step, cc->default_value);
1323*4882a593Smuzhiyun if (!vc) {
1324*4882a593Smuzhiyun err = core->audio_hdl.error;
1325*4882a593Smuzhiyun goto fail_irq;
1326*4882a593Smuzhiyun }
1327*4882a593Smuzhiyun vc->priv = (void *)cc;
1328*4882a593Smuzhiyun }
1329*4882a593Smuzhiyun
1330*4882a593Smuzhiyun for (i = 0; i < CX8800_VID_CTLS; i++) {
1331*4882a593Smuzhiyun const struct cx88_ctrl *cc = &cx8800_vid_ctls[i];
1332*4882a593Smuzhiyun struct v4l2_ctrl *vc;
1333*4882a593Smuzhiyun
1334*4882a593Smuzhiyun vc = v4l2_ctrl_new_std(&core->video_hdl, &cx8800_ctrl_vid_ops,
1335*4882a593Smuzhiyun cc->id, cc->minimum, cc->maximum,
1336*4882a593Smuzhiyun cc->step, cc->default_value);
1337*4882a593Smuzhiyun if (!vc) {
1338*4882a593Smuzhiyun err = core->video_hdl.error;
1339*4882a593Smuzhiyun goto fail_irq;
1340*4882a593Smuzhiyun }
1341*4882a593Smuzhiyun vc->priv = (void *)cc;
1342*4882a593Smuzhiyun if (vc->id == V4L2_CID_CHROMA_AGC)
1343*4882a593Smuzhiyun core->chroma_agc = vc;
1344*4882a593Smuzhiyun }
1345*4882a593Smuzhiyun v4l2_ctrl_add_handler(&core->video_hdl, &core->audio_hdl, NULL, false);
1346*4882a593Smuzhiyun
1347*4882a593Smuzhiyun /* load and configure helper modules */
1348*4882a593Smuzhiyun
1349*4882a593Smuzhiyun if (core->board.audio_chip == CX88_AUDIO_WM8775) {
1350*4882a593Smuzhiyun struct i2c_board_info wm8775_info = {
1351*4882a593Smuzhiyun .type = "wm8775",
1352*4882a593Smuzhiyun .addr = 0x36 >> 1,
1353*4882a593Smuzhiyun .platform_data = &core->wm8775_data,
1354*4882a593Smuzhiyun };
1355*4882a593Smuzhiyun struct v4l2_subdev *sd;
1356*4882a593Smuzhiyun
1357*4882a593Smuzhiyun if (core->boardnr == CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1)
1358*4882a593Smuzhiyun core->wm8775_data.is_nova_s = true;
1359*4882a593Smuzhiyun else
1360*4882a593Smuzhiyun core->wm8775_data.is_nova_s = false;
1361*4882a593Smuzhiyun
1362*4882a593Smuzhiyun sd = v4l2_i2c_new_subdev_board(&core->v4l2_dev, &core->i2c_adap,
1363*4882a593Smuzhiyun &wm8775_info, NULL);
1364*4882a593Smuzhiyun if (sd) {
1365*4882a593Smuzhiyun core->sd_wm8775 = sd;
1366*4882a593Smuzhiyun sd->grp_id = WM8775_GID;
1367*4882a593Smuzhiyun }
1368*4882a593Smuzhiyun }
1369*4882a593Smuzhiyun
1370*4882a593Smuzhiyun if (core->board.audio_chip == CX88_AUDIO_TVAUDIO) {
1371*4882a593Smuzhiyun /*
1372*4882a593Smuzhiyun * This probes for a tda9874 as is used on some
1373*4882a593Smuzhiyun * Pixelview Ultra boards.
1374*4882a593Smuzhiyun */
1375*4882a593Smuzhiyun v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap,
1376*4882a593Smuzhiyun "tvaudio", 0, I2C_ADDRS(0xb0 >> 1));
1377*4882a593Smuzhiyun }
1378*4882a593Smuzhiyun
1379*4882a593Smuzhiyun switch (core->boardnr) {
1380*4882a593Smuzhiyun case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD:
1381*4882a593Smuzhiyun case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD: {
1382*4882a593Smuzhiyun static const struct i2c_board_info rtc_info = {
1383*4882a593Smuzhiyun I2C_BOARD_INFO("isl1208", 0x6f)
1384*4882a593Smuzhiyun };
1385*4882a593Smuzhiyun
1386*4882a593Smuzhiyun request_module("rtc-isl1208");
1387*4882a593Smuzhiyun core->i2c_rtc = i2c_new_client_device(&core->i2c_adap, &rtc_info);
1388*4882a593Smuzhiyun }
1389*4882a593Smuzhiyun fallthrough;
1390*4882a593Smuzhiyun case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
1391*4882a593Smuzhiyun request_module("ir-kbd-i2c");
1392*4882a593Smuzhiyun }
1393*4882a593Smuzhiyun
1394*4882a593Smuzhiyun /* Sets device info at pci_dev */
1395*4882a593Smuzhiyun pci_set_drvdata(pci_dev, dev);
1396*4882a593Smuzhiyun
1397*4882a593Smuzhiyun dev->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
1398*4882a593Smuzhiyun
1399*4882a593Smuzhiyun /* Maintain a reference so cx88-blackbird can query the 8800 device. */
1400*4882a593Smuzhiyun core->v4ldev = dev;
1401*4882a593Smuzhiyun
1402*4882a593Smuzhiyun /* initial device configuration */
1403*4882a593Smuzhiyun mutex_lock(&core->lock);
1404*4882a593Smuzhiyun cx88_set_tvnorm(core, V4L2_STD_NTSC_M);
1405*4882a593Smuzhiyun v4l2_ctrl_handler_setup(&core->video_hdl);
1406*4882a593Smuzhiyun v4l2_ctrl_handler_setup(&core->audio_hdl);
1407*4882a593Smuzhiyun cx88_video_mux(core, 0);
1408*4882a593Smuzhiyun
1409*4882a593Smuzhiyun q = &dev->vb2_vidq;
1410*4882a593Smuzhiyun q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
1411*4882a593Smuzhiyun q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
1412*4882a593Smuzhiyun q->gfp_flags = GFP_DMA32;
1413*4882a593Smuzhiyun q->min_buffers_needed = 2;
1414*4882a593Smuzhiyun q->drv_priv = dev;
1415*4882a593Smuzhiyun q->buf_struct_size = sizeof(struct cx88_buffer);
1416*4882a593Smuzhiyun q->ops = &cx8800_video_qops;
1417*4882a593Smuzhiyun q->mem_ops = &vb2_dma_sg_memops;
1418*4882a593Smuzhiyun q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
1419*4882a593Smuzhiyun q->lock = &core->lock;
1420*4882a593Smuzhiyun q->dev = &dev->pci->dev;
1421*4882a593Smuzhiyun
1422*4882a593Smuzhiyun err = vb2_queue_init(q);
1423*4882a593Smuzhiyun if (err < 0)
1424*4882a593Smuzhiyun goto fail_unreg;
1425*4882a593Smuzhiyun
1426*4882a593Smuzhiyun q = &dev->vb2_vbiq;
1427*4882a593Smuzhiyun q->type = V4L2_BUF_TYPE_VBI_CAPTURE;
1428*4882a593Smuzhiyun q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ;
1429*4882a593Smuzhiyun q->gfp_flags = GFP_DMA32;
1430*4882a593Smuzhiyun q->min_buffers_needed = 2;
1431*4882a593Smuzhiyun q->drv_priv = dev;
1432*4882a593Smuzhiyun q->buf_struct_size = sizeof(struct cx88_buffer);
1433*4882a593Smuzhiyun q->ops = &cx8800_vbi_qops;
1434*4882a593Smuzhiyun q->mem_ops = &vb2_dma_sg_memops;
1435*4882a593Smuzhiyun q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
1436*4882a593Smuzhiyun q->lock = &core->lock;
1437*4882a593Smuzhiyun q->dev = &dev->pci->dev;
1438*4882a593Smuzhiyun
1439*4882a593Smuzhiyun err = vb2_queue_init(q);
1440*4882a593Smuzhiyun if (err < 0)
1441*4882a593Smuzhiyun goto fail_unreg;
1442*4882a593Smuzhiyun
1443*4882a593Smuzhiyun /* register v4l devices */
1444*4882a593Smuzhiyun cx88_vdev_init(core, dev->pci, &dev->video_dev,
1445*4882a593Smuzhiyun &cx8800_video_template, "video");
1446*4882a593Smuzhiyun video_set_drvdata(&dev->video_dev, dev);
1447*4882a593Smuzhiyun dev->video_dev.ctrl_handler = &core->video_hdl;
1448*4882a593Smuzhiyun dev->video_dev.queue = &dev->vb2_vidq;
1449*4882a593Smuzhiyun dev->video_dev.device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING |
1450*4882a593Smuzhiyun V4L2_CAP_VIDEO_CAPTURE;
1451*4882a593Smuzhiyun if (core->board.tuner_type != UNSET)
1452*4882a593Smuzhiyun dev->video_dev.device_caps |= V4L2_CAP_TUNER;
1453*4882a593Smuzhiyun err = video_register_device(&dev->video_dev, VFL_TYPE_VIDEO,
1454*4882a593Smuzhiyun video_nr[core->nr]);
1455*4882a593Smuzhiyun if (err < 0) {
1456*4882a593Smuzhiyun pr_err("can't register video device\n");
1457*4882a593Smuzhiyun goto fail_unreg;
1458*4882a593Smuzhiyun }
1459*4882a593Smuzhiyun pr_info("registered device %s [v4l2]\n",
1460*4882a593Smuzhiyun video_device_node_name(&dev->video_dev));
1461*4882a593Smuzhiyun
1462*4882a593Smuzhiyun cx88_vdev_init(core, dev->pci, &dev->vbi_dev,
1463*4882a593Smuzhiyun &cx8800_vbi_template, "vbi");
1464*4882a593Smuzhiyun video_set_drvdata(&dev->vbi_dev, dev);
1465*4882a593Smuzhiyun dev->vbi_dev.queue = &dev->vb2_vbiq;
1466*4882a593Smuzhiyun dev->vbi_dev.device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING |
1467*4882a593Smuzhiyun V4L2_CAP_VBI_CAPTURE;
1468*4882a593Smuzhiyun if (core->board.tuner_type != UNSET)
1469*4882a593Smuzhiyun dev->vbi_dev.device_caps |= V4L2_CAP_TUNER;
1470*4882a593Smuzhiyun err = video_register_device(&dev->vbi_dev, VFL_TYPE_VBI,
1471*4882a593Smuzhiyun vbi_nr[core->nr]);
1472*4882a593Smuzhiyun if (err < 0) {
1473*4882a593Smuzhiyun pr_err("can't register vbi device\n");
1474*4882a593Smuzhiyun goto fail_unreg;
1475*4882a593Smuzhiyun }
1476*4882a593Smuzhiyun pr_info("registered device %s\n",
1477*4882a593Smuzhiyun video_device_node_name(&dev->vbi_dev));
1478*4882a593Smuzhiyun
1479*4882a593Smuzhiyun if (core->board.radio.type == CX88_RADIO) {
1480*4882a593Smuzhiyun cx88_vdev_init(core, dev->pci, &dev->radio_dev,
1481*4882a593Smuzhiyun &cx8800_radio_template, "radio");
1482*4882a593Smuzhiyun video_set_drvdata(&dev->radio_dev, dev);
1483*4882a593Smuzhiyun dev->radio_dev.ctrl_handler = &core->audio_hdl;
1484*4882a593Smuzhiyun dev->radio_dev.device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER;
1485*4882a593Smuzhiyun err = video_register_device(&dev->radio_dev, VFL_TYPE_RADIO,
1486*4882a593Smuzhiyun radio_nr[core->nr]);
1487*4882a593Smuzhiyun if (err < 0) {
1488*4882a593Smuzhiyun pr_err("can't register radio device\n");
1489*4882a593Smuzhiyun goto fail_unreg;
1490*4882a593Smuzhiyun }
1491*4882a593Smuzhiyun pr_info("registered device %s\n",
1492*4882a593Smuzhiyun video_device_node_name(&dev->radio_dev));
1493*4882a593Smuzhiyun }
1494*4882a593Smuzhiyun
1495*4882a593Smuzhiyun /* start tvaudio thread */
1496*4882a593Smuzhiyun if (core->board.tuner_type != UNSET) {
1497*4882a593Smuzhiyun core->kthread = kthread_run(cx88_audio_thread,
1498*4882a593Smuzhiyun core, "cx88 tvaudio");
1499*4882a593Smuzhiyun if (IS_ERR(core->kthread)) {
1500*4882a593Smuzhiyun err = PTR_ERR(core->kthread);
1501*4882a593Smuzhiyun pr_err("failed to create cx88 audio thread, err=%d\n",
1502*4882a593Smuzhiyun err);
1503*4882a593Smuzhiyun }
1504*4882a593Smuzhiyun }
1505*4882a593Smuzhiyun mutex_unlock(&core->lock);
1506*4882a593Smuzhiyun
1507*4882a593Smuzhiyun return 0;
1508*4882a593Smuzhiyun
1509*4882a593Smuzhiyun fail_unreg:
1510*4882a593Smuzhiyun cx8800_unregister_video(dev);
1511*4882a593Smuzhiyun mutex_unlock(&core->lock);
1512*4882a593Smuzhiyun fail_irq:
1513*4882a593Smuzhiyun free_irq(pci_dev->irq, dev);
1514*4882a593Smuzhiyun fail_core:
1515*4882a593Smuzhiyun core->v4ldev = NULL;
1516*4882a593Smuzhiyun cx88_core_put(core, dev->pci);
1517*4882a593Smuzhiyun fail_disable:
1518*4882a593Smuzhiyun pci_disable_device(pci_dev);
1519*4882a593Smuzhiyun fail_free:
1520*4882a593Smuzhiyun kfree(dev);
1521*4882a593Smuzhiyun return err;
1522*4882a593Smuzhiyun }
1523*4882a593Smuzhiyun
cx8800_finidev(struct pci_dev * pci_dev)1524*4882a593Smuzhiyun static void cx8800_finidev(struct pci_dev *pci_dev)
1525*4882a593Smuzhiyun {
1526*4882a593Smuzhiyun struct cx8800_dev *dev = pci_get_drvdata(pci_dev);
1527*4882a593Smuzhiyun struct cx88_core *core = dev->core;
1528*4882a593Smuzhiyun
1529*4882a593Smuzhiyun /* stop thread */
1530*4882a593Smuzhiyun if (core->kthread) {
1531*4882a593Smuzhiyun kthread_stop(core->kthread);
1532*4882a593Smuzhiyun core->kthread = NULL;
1533*4882a593Smuzhiyun }
1534*4882a593Smuzhiyun
1535*4882a593Smuzhiyun if (core->ir)
1536*4882a593Smuzhiyun cx88_ir_stop(core);
1537*4882a593Smuzhiyun
1538*4882a593Smuzhiyun cx88_shutdown(core); /* FIXME */
1539*4882a593Smuzhiyun
1540*4882a593Smuzhiyun /* unregister stuff */
1541*4882a593Smuzhiyun
1542*4882a593Smuzhiyun free_irq(pci_dev->irq, dev);
1543*4882a593Smuzhiyun cx8800_unregister_video(dev);
1544*4882a593Smuzhiyun pci_disable_device(pci_dev);
1545*4882a593Smuzhiyun
1546*4882a593Smuzhiyun core->v4ldev = NULL;
1547*4882a593Smuzhiyun
1548*4882a593Smuzhiyun /* free memory */
1549*4882a593Smuzhiyun cx88_core_put(core, dev->pci);
1550*4882a593Smuzhiyun kfree(dev);
1551*4882a593Smuzhiyun }
1552*4882a593Smuzhiyun
cx8800_suspend(struct device * dev_d)1553*4882a593Smuzhiyun static int __maybe_unused cx8800_suspend(struct device *dev_d)
1554*4882a593Smuzhiyun {
1555*4882a593Smuzhiyun struct cx8800_dev *dev = dev_get_drvdata(dev_d);
1556*4882a593Smuzhiyun struct cx88_core *core = dev->core;
1557*4882a593Smuzhiyun unsigned long flags;
1558*4882a593Smuzhiyun
1559*4882a593Smuzhiyun /* stop video+vbi capture */
1560*4882a593Smuzhiyun spin_lock_irqsave(&dev->slock, flags);
1561*4882a593Smuzhiyun if (!list_empty(&dev->vidq.active)) {
1562*4882a593Smuzhiyun pr_info("suspend video\n");
1563*4882a593Smuzhiyun stop_video_dma(dev);
1564*4882a593Smuzhiyun }
1565*4882a593Smuzhiyun if (!list_empty(&dev->vbiq.active)) {
1566*4882a593Smuzhiyun pr_info("suspend vbi\n");
1567*4882a593Smuzhiyun cx8800_stop_vbi_dma(dev);
1568*4882a593Smuzhiyun }
1569*4882a593Smuzhiyun spin_unlock_irqrestore(&dev->slock, flags);
1570*4882a593Smuzhiyun
1571*4882a593Smuzhiyun if (core->ir)
1572*4882a593Smuzhiyun cx88_ir_stop(core);
1573*4882a593Smuzhiyun /* FIXME -- shutdown device */
1574*4882a593Smuzhiyun cx88_shutdown(core);
1575*4882a593Smuzhiyun
1576*4882a593Smuzhiyun dev->state.disabled = 1;
1577*4882a593Smuzhiyun return 0;
1578*4882a593Smuzhiyun }
1579*4882a593Smuzhiyun
cx8800_resume(struct device * dev_d)1580*4882a593Smuzhiyun static int __maybe_unused cx8800_resume(struct device *dev_d)
1581*4882a593Smuzhiyun {
1582*4882a593Smuzhiyun struct cx8800_dev *dev = dev_get_drvdata(dev_d);
1583*4882a593Smuzhiyun struct cx88_core *core = dev->core;
1584*4882a593Smuzhiyun unsigned long flags;
1585*4882a593Smuzhiyun
1586*4882a593Smuzhiyun dev->state.disabled = 0;
1587*4882a593Smuzhiyun
1588*4882a593Smuzhiyun /* FIXME: re-initialize hardware */
1589*4882a593Smuzhiyun cx88_reset(core);
1590*4882a593Smuzhiyun if (core->ir)
1591*4882a593Smuzhiyun cx88_ir_start(core);
1592*4882a593Smuzhiyun
1593*4882a593Smuzhiyun cx_set(MO_PCI_INTMSK, core->pci_irqmask);
1594*4882a593Smuzhiyun
1595*4882a593Smuzhiyun /* restart video+vbi capture */
1596*4882a593Smuzhiyun spin_lock_irqsave(&dev->slock, flags);
1597*4882a593Smuzhiyun if (!list_empty(&dev->vidq.active)) {
1598*4882a593Smuzhiyun pr_info("resume video\n");
1599*4882a593Smuzhiyun restart_video_queue(dev, &dev->vidq);
1600*4882a593Smuzhiyun }
1601*4882a593Smuzhiyun if (!list_empty(&dev->vbiq.active)) {
1602*4882a593Smuzhiyun pr_info("resume vbi\n");
1603*4882a593Smuzhiyun cx8800_restart_vbi_queue(dev, &dev->vbiq);
1604*4882a593Smuzhiyun }
1605*4882a593Smuzhiyun spin_unlock_irqrestore(&dev->slock, flags);
1606*4882a593Smuzhiyun
1607*4882a593Smuzhiyun return 0;
1608*4882a593Smuzhiyun }
1609*4882a593Smuzhiyun
1610*4882a593Smuzhiyun /* ----------------------------------------------------------- */
1611*4882a593Smuzhiyun
1612*4882a593Smuzhiyun static const struct pci_device_id cx8800_pci_tbl[] = {
1613*4882a593Smuzhiyun {
1614*4882a593Smuzhiyun .vendor = 0x14f1,
1615*4882a593Smuzhiyun .device = 0x8800,
1616*4882a593Smuzhiyun .subvendor = PCI_ANY_ID,
1617*4882a593Smuzhiyun .subdevice = PCI_ANY_ID,
1618*4882a593Smuzhiyun }, {
1619*4882a593Smuzhiyun /* --- end of list --- */
1620*4882a593Smuzhiyun }
1621*4882a593Smuzhiyun };
1622*4882a593Smuzhiyun MODULE_DEVICE_TABLE(pci, cx8800_pci_tbl);
1623*4882a593Smuzhiyun
1624*4882a593Smuzhiyun static SIMPLE_DEV_PM_OPS(cx8800_pm_ops, cx8800_suspend, cx8800_resume);
1625*4882a593Smuzhiyun
1626*4882a593Smuzhiyun static struct pci_driver cx8800_pci_driver = {
1627*4882a593Smuzhiyun .name = "cx8800",
1628*4882a593Smuzhiyun .id_table = cx8800_pci_tbl,
1629*4882a593Smuzhiyun .probe = cx8800_initdev,
1630*4882a593Smuzhiyun .remove = cx8800_finidev,
1631*4882a593Smuzhiyun .driver.pm = &cx8800_pm_ops,
1632*4882a593Smuzhiyun };
1633*4882a593Smuzhiyun
1634*4882a593Smuzhiyun module_pci_driver(cx8800_pci_driver);
1635