1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * tw68 functions to handle video data
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Much of this code is derived from the cx88 and sa7134 drivers, which
6*4882a593Smuzhiyun * were in turn derived from the bt87x driver. The original work was by
7*4882a593Smuzhiyun * Gerd Knorr; more recently the code was enhanced by Mauro Carvalho Chehab,
8*4882a593Smuzhiyun * Hans Verkuil, Andy Walls and many others. Their work is gratefully
9*4882a593Smuzhiyun * acknowledged. Full credit goes to them - any problems within this code
10*4882a593Smuzhiyun * are mine.
11*4882a593Smuzhiyun *
12*4882a593Smuzhiyun * Copyright (C) 2009 William M. Brack
13*4882a593Smuzhiyun *
14*4882a593Smuzhiyun * Refactored and updated to the latest v4l core frameworks:
15*4882a593Smuzhiyun *
16*4882a593Smuzhiyun * Copyright (C) 2014 Hans Verkuil <hverkuil@xs4all.nl>
17*4882a593Smuzhiyun */
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun #include <linux/module.h>
20*4882a593Smuzhiyun #include <media/v4l2-common.h>
21*4882a593Smuzhiyun #include <media/v4l2-event.h>
22*4882a593Smuzhiyun #include <media/videobuf2-dma-sg.h>
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun #include "tw68.h"
25*4882a593Smuzhiyun #include "tw68-reg.h"
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun /* ------------------------------------------------------------------ */
28*4882a593Smuzhiyun /* data structs for video */
29*4882a593Smuzhiyun /*
30*4882a593Smuzhiyun * FIXME -
31*4882a593Smuzhiyun * Note that the saa7134 has formats, e.g. YUV420, which are classified
32*4882a593Smuzhiyun * as "planar". These affect overlay mode, and are flagged with a field
33*4882a593Smuzhiyun * ".planar" in the format. Do we need to implement this in this driver?
34*4882a593Smuzhiyun */
35*4882a593Smuzhiyun static const struct tw68_format formats[] = {
36*4882a593Smuzhiyun {
37*4882a593Smuzhiyun .fourcc = V4L2_PIX_FMT_RGB555,
38*4882a593Smuzhiyun .depth = 16,
39*4882a593Smuzhiyun .twformat = ColorFormatRGB15,
40*4882a593Smuzhiyun }, {
41*4882a593Smuzhiyun .fourcc = V4L2_PIX_FMT_RGB555X,
42*4882a593Smuzhiyun .depth = 16,
43*4882a593Smuzhiyun .twformat = ColorFormatRGB15 | ColorFormatBSWAP,
44*4882a593Smuzhiyun }, {
45*4882a593Smuzhiyun .fourcc = V4L2_PIX_FMT_RGB565,
46*4882a593Smuzhiyun .depth = 16,
47*4882a593Smuzhiyun .twformat = ColorFormatRGB16,
48*4882a593Smuzhiyun }, {
49*4882a593Smuzhiyun .fourcc = V4L2_PIX_FMT_RGB565X,
50*4882a593Smuzhiyun .depth = 16,
51*4882a593Smuzhiyun .twformat = ColorFormatRGB16 | ColorFormatBSWAP,
52*4882a593Smuzhiyun }, {
53*4882a593Smuzhiyun .fourcc = V4L2_PIX_FMT_BGR24,
54*4882a593Smuzhiyun .depth = 24,
55*4882a593Smuzhiyun .twformat = ColorFormatRGB24,
56*4882a593Smuzhiyun }, {
57*4882a593Smuzhiyun .fourcc = V4L2_PIX_FMT_RGB24,
58*4882a593Smuzhiyun .depth = 24,
59*4882a593Smuzhiyun .twformat = ColorFormatRGB24 | ColorFormatBSWAP,
60*4882a593Smuzhiyun }, {
61*4882a593Smuzhiyun .fourcc = V4L2_PIX_FMT_BGR32,
62*4882a593Smuzhiyun .depth = 32,
63*4882a593Smuzhiyun .twformat = ColorFormatRGB32,
64*4882a593Smuzhiyun }, {
65*4882a593Smuzhiyun .fourcc = V4L2_PIX_FMT_RGB32,
66*4882a593Smuzhiyun .depth = 32,
67*4882a593Smuzhiyun .twformat = ColorFormatRGB32 | ColorFormatBSWAP |
68*4882a593Smuzhiyun ColorFormatWSWAP,
69*4882a593Smuzhiyun }, {
70*4882a593Smuzhiyun .fourcc = V4L2_PIX_FMT_YUYV,
71*4882a593Smuzhiyun .depth = 16,
72*4882a593Smuzhiyun .twformat = ColorFormatYUY2,
73*4882a593Smuzhiyun }, {
74*4882a593Smuzhiyun .fourcc = V4L2_PIX_FMT_UYVY,
75*4882a593Smuzhiyun .depth = 16,
76*4882a593Smuzhiyun .twformat = ColorFormatYUY2 | ColorFormatBSWAP,
77*4882a593Smuzhiyun }
78*4882a593Smuzhiyun };
79*4882a593Smuzhiyun #define FORMATS ARRAY_SIZE(formats)
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun #define NORM_625_50 \
82*4882a593Smuzhiyun .h_delay = 3, \
83*4882a593Smuzhiyun .h_delay0 = 133, \
84*4882a593Smuzhiyun .h_start = 0, \
85*4882a593Smuzhiyun .h_stop = 719, \
86*4882a593Smuzhiyun .v_delay = 24, \
87*4882a593Smuzhiyun .vbi_v_start_0 = 7, \
88*4882a593Smuzhiyun .vbi_v_stop_0 = 22, \
89*4882a593Smuzhiyun .video_v_start = 24, \
90*4882a593Smuzhiyun .video_v_stop = 311, \
91*4882a593Smuzhiyun .vbi_v_start_1 = 319
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun #define NORM_525_60 \
94*4882a593Smuzhiyun .h_delay = 8, \
95*4882a593Smuzhiyun .h_delay0 = 138, \
96*4882a593Smuzhiyun .h_start = 0, \
97*4882a593Smuzhiyun .h_stop = 719, \
98*4882a593Smuzhiyun .v_delay = 22, \
99*4882a593Smuzhiyun .vbi_v_start_0 = 10, \
100*4882a593Smuzhiyun .vbi_v_stop_0 = 21, \
101*4882a593Smuzhiyun .video_v_start = 22, \
102*4882a593Smuzhiyun .video_v_stop = 262, \
103*4882a593Smuzhiyun .vbi_v_start_1 = 273
104*4882a593Smuzhiyun
105*4882a593Smuzhiyun /*
106*4882a593Smuzhiyun * The following table is searched by tw68_s_std, first for a specific
107*4882a593Smuzhiyun * match, then for an entry which contains the desired id. The table
108*4882a593Smuzhiyun * entries should therefore be ordered in ascending order of specificity.
109*4882a593Smuzhiyun */
110*4882a593Smuzhiyun static const struct tw68_tvnorm tvnorms[] = {
111*4882a593Smuzhiyun {
112*4882a593Smuzhiyun .name = "PAL", /* autodetect */
113*4882a593Smuzhiyun .id = V4L2_STD_PAL,
114*4882a593Smuzhiyun NORM_625_50,
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun .sync_control = 0x18,
117*4882a593Smuzhiyun .luma_control = 0x40,
118*4882a593Smuzhiyun .chroma_ctrl1 = 0x81,
119*4882a593Smuzhiyun .chroma_gain = 0x2a,
120*4882a593Smuzhiyun .chroma_ctrl2 = 0x06,
121*4882a593Smuzhiyun .vgate_misc = 0x1c,
122*4882a593Smuzhiyun .format = VideoFormatPALBDGHI,
123*4882a593Smuzhiyun }, {
124*4882a593Smuzhiyun .name = "NTSC",
125*4882a593Smuzhiyun .id = V4L2_STD_NTSC,
126*4882a593Smuzhiyun NORM_525_60,
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun .sync_control = 0x59,
129*4882a593Smuzhiyun .luma_control = 0x40,
130*4882a593Smuzhiyun .chroma_ctrl1 = 0x89,
131*4882a593Smuzhiyun .chroma_gain = 0x2a,
132*4882a593Smuzhiyun .chroma_ctrl2 = 0x0e,
133*4882a593Smuzhiyun .vgate_misc = 0x18,
134*4882a593Smuzhiyun .format = VideoFormatNTSC,
135*4882a593Smuzhiyun }, {
136*4882a593Smuzhiyun .name = "SECAM",
137*4882a593Smuzhiyun .id = V4L2_STD_SECAM,
138*4882a593Smuzhiyun NORM_625_50,
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun .sync_control = 0x18,
141*4882a593Smuzhiyun .luma_control = 0x1b,
142*4882a593Smuzhiyun .chroma_ctrl1 = 0xd1,
143*4882a593Smuzhiyun .chroma_gain = 0x80,
144*4882a593Smuzhiyun .chroma_ctrl2 = 0x00,
145*4882a593Smuzhiyun .vgate_misc = 0x1c,
146*4882a593Smuzhiyun .format = VideoFormatSECAM,
147*4882a593Smuzhiyun }, {
148*4882a593Smuzhiyun .name = "PAL-M",
149*4882a593Smuzhiyun .id = V4L2_STD_PAL_M,
150*4882a593Smuzhiyun NORM_525_60,
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun .sync_control = 0x59,
153*4882a593Smuzhiyun .luma_control = 0x40,
154*4882a593Smuzhiyun .chroma_ctrl1 = 0xb9,
155*4882a593Smuzhiyun .chroma_gain = 0x2a,
156*4882a593Smuzhiyun .chroma_ctrl2 = 0x0e,
157*4882a593Smuzhiyun .vgate_misc = 0x18,
158*4882a593Smuzhiyun .format = VideoFormatPALM,
159*4882a593Smuzhiyun }, {
160*4882a593Smuzhiyun .name = "PAL-Nc",
161*4882a593Smuzhiyun .id = V4L2_STD_PAL_Nc,
162*4882a593Smuzhiyun NORM_625_50,
163*4882a593Smuzhiyun
164*4882a593Smuzhiyun .sync_control = 0x18,
165*4882a593Smuzhiyun .luma_control = 0x40,
166*4882a593Smuzhiyun .chroma_ctrl1 = 0xa1,
167*4882a593Smuzhiyun .chroma_gain = 0x2a,
168*4882a593Smuzhiyun .chroma_ctrl2 = 0x06,
169*4882a593Smuzhiyun .vgate_misc = 0x1c,
170*4882a593Smuzhiyun .format = VideoFormatPALNC,
171*4882a593Smuzhiyun }, {
172*4882a593Smuzhiyun .name = "PAL-60",
173*4882a593Smuzhiyun .id = V4L2_STD_PAL_60,
174*4882a593Smuzhiyun .h_delay = 186,
175*4882a593Smuzhiyun .h_start = 0,
176*4882a593Smuzhiyun .h_stop = 719,
177*4882a593Smuzhiyun .v_delay = 26,
178*4882a593Smuzhiyun .video_v_start = 23,
179*4882a593Smuzhiyun .video_v_stop = 262,
180*4882a593Smuzhiyun .vbi_v_start_0 = 10,
181*4882a593Smuzhiyun .vbi_v_stop_0 = 21,
182*4882a593Smuzhiyun .vbi_v_start_1 = 273,
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun .sync_control = 0x18,
185*4882a593Smuzhiyun .luma_control = 0x40,
186*4882a593Smuzhiyun .chroma_ctrl1 = 0x81,
187*4882a593Smuzhiyun .chroma_gain = 0x2a,
188*4882a593Smuzhiyun .chroma_ctrl2 = 0x06,
189*4882a593Smuzhiyun .vgate_misc = 0x1c,
190*4882a593Smuzhiyun .format = VideoFormatPAL60,
191*4882a593Smuzhiyun }
192*4882a593Smuzhiyun };
193*4882a593Smuzhiyun #define TVNORMS ARRAY_SIZE(tvnorms)
194*4882a593Smuzhiyun
format_by_fourcc(unsigned int fourcc)195*4882a593Smuzhiyun static const struct tw68_format *format_by_fourcc(unsigned int fourcc)
196*4882a593Smuzhiyun {
197*4882a593Smuzhiyun unsigned int i;
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun for (i = 0; i < FORMATS; i++)
200*4882a593Smuzhiyun if (formats[i].fourcc == fourcc)
201*4882a593Smuzhiyun return formats+i;
202*4882a593Smuzhiyun return NULL;
203*4882a593Smuzhiyun }
204*4882a593Smuzhiyun
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun /* ------------------------------------------------------------------ */
207*4882a593Smuzhiyun /*
208*4882a593Smuzhiyun * Note that the cropping rectangles are described in terms of a single
209*4882a593Smuzhiyun * frame, i.e. line positions are only 1/2 the interlaced equivalent
210*4882a593Smuzhiyun */
set_tvnorm(struct tw68_dev * dev,const struct tw68_tvnorm * norm)211*4882a593Smuzhiyun static void set_tvnorm(struct tw68_dev *dev, const struct tw68_tvnorm *norm)
212*4882a593Smuzhiyun {
213*4882a593Smuzhiyun if (norm != dev->tvnorm) {
214*4882a593Smuzhiyun dev->width = 720;
215*4882a593Smuzhiyun dev->height = (norm->id & V4L2_STD_525_60) ? 480 : 576;
216*4882a593Smuzhiyun dev->tvnorm = norm;
217*4882a593Smuzhiyun tw68_set_tvnorm_hw(dev);
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun }
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun /*
222*4882a593Smuzhiyun * tw68_set_scale
223*4882a593Smuzhiyun *
224*4882a593Smuzhiyun * Scaling and Cropping for video decoding
225*4882a593Smuzhiyun *
226*4882a593Smuzhiyun * We are working with 3 values for horizontal and vertical - scale,
227*4882a593Smuzhiyun * delay and active.
228*4882a593Smuzhiyun *
229*4882a593Smuzhiyun * HACTIVE represent the actual number of pixels in the "usable" image,
230*4882a593Smuzhiyun * before scaling. HDELAY represents the number of pixels skipped
231*4882a593Smuzhiyun * between the start of the horizontal sync and the start of the image.
232*4882a593Smuzhiyun * HSCALE is calculated using the formula
233*4882a593Smuzhiyun * HSCALE = (HACTIVE / (#pixels desired)) * 256
234*4882a593Smuzhiyun *
235*4882a593Smuzhiyun * The vertical registers are similar, except based upon the total number
236*4882a593Smuzhiyun * of lines in the image, and the first line of the image (i.e. ignoring
237*4882a593Smuzhiyun * vertical sync and VBI).
238*4882a593Smuzhiyun *
239*4882a593Smuzhiyun * Note that the number of bytes reaching the FIFO (and hence needing
240*4882a593Smuzhiyun * to be processed by the DMAP program) is completely dependent upon
241*4882a593Smuzhiyun * these values, especially HSCALE.
242*4882a593Smuzhiyun *
243*4882a593Smuzhiyun * Parameters:
244*4882a593Smuzhiyun * @dev pointer to the device structure, needed for
245*4882a593Smuzhiyun * getting current norm (as well as debug print)
246*4882a593Smuzhiyun * @width actual image width (from user buffer)
247*4882a593Smuzhiyun * @height actual image height
248*4882a593Smuzhiyun * @field indicates Top, Bottom or Interlaced
249*4882a593Smuzhiyun */
tw68_set_scale(struct tw68_dev * dev,unsigned int width,unsigned int height,enum v4l2_field field)250*4882a593Smuzhiyun static int tw68_set_scale(struct tw68_dev *dev, unsigned int width,
251*4882a593Smuzhiyun unsigned int height, enum v4l2_field field)
252*4882a593Smuzhiyun {
253*4882a593Smuzhiyun const struct tw68_tvnorm *norm = dev->tvnorm;
254*4882a593Smuzhiyun /* set individually for debugging clarity */
255*4882a593Smuzhiyun int hactive, hdelay, hscale;
256*4882a593Smuzhiyun int vactive, vdelay, vscale;
257*4882a593Smuzhiyun int comb;
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun if (V4L2_FIELD_HAS_BOTH(field)) /* if field is interlaced */
260*4882a593Smuzhiyun height /= 2; /* we must set for 1-frame */
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun pr_debug("%s: width=%d, height=%d, both=%d\n"
263*4882a593Smuzhiyun " tvnorm h_delay=%d, h_start=%d, h_stop=%d, v_delay=%d, v_start=%d, v_stop=%d\n",
264*4882a593Smuzhiyun __func__, width, height, V4L2_FIELD_HAS_BOTH(field),
265*4882a593Smuzhiyun norm->h_delay, norm->h_start, norm->h_stop,
266*4882a593Smuzhiyun norm->v_delay, norm->video_v_start,
267*4882a593Smuzhiyun norm->video_v_stop);
268*4882a593Smuzhiyun
269*4882a593Smuzhiyun switch (dev->vdecoder) {
270*4882a593Smuzhiyun case TW6800:
271*4882a593Smuzhiyun hdelay = norm->h_delay0;
272*4882a593Smuzhiyun break;
273*4882a593Smuzhiyun default:
274*4882a593Smuzhiyun hdelay = norm->h_delay;
275*4882a593Smuzhiyun break;
276*4882a593Smuzhiyun }
277*4882a593Smuzhiyun
278*4882a593Smuzhiyun hdelay += norm->h_start;
279*4882a593Smuzhiyun hactive = norm->h_stop - norm->h_start + 1;
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun hscale = (hactive * 256) / (width);
282*4882a593Smuzhiyun
283*4882a593Smuzhiyun vdelay = norm->v_delay;
284*4882a593Smuzhiyun vactive = ((norm->id & V4L2_STD_525_60) ? 524 : 624) / 2 - norm->video_v_start;
285*4882a593Smuzhiyun vscale = (vactive * 256) / height;
286*4882a593Smuzhiyun
287*4882a593Smuzhiyun pr_debug("%s: %dx%d [%s%s,%s]\n", __func__,
288*4882a593Smuzhiyun width, height,
289*4882a593Smuzhiyun V4L2_FIELD_HAS_TOP(field) ? "T" : "",
290*4882a593Smuzhiyun V4L2_FIELD_HAS_BOTTOM(field) ? "B" : "",
291*4882a593Smuzhiyun v4l2_norm_to_name(dev->tvnorm->id));
292*4882a593Smuzhiyun pr_debug("%s: hactive=%d, hdelay=%d, hscale=%d; vactive=%d, vdelay=%d, vscale=%d\n",
293*4882a593Smuzhiyun __func__,
294*4882a593Smuzhiyun hactive, hdelay, hscale, vactive, vdelay, vscale);
295*4882a593Smuzhiyun
296*4882a593Smuzhiyun comb = ((vdelay & 0x300) >> 2) |
297*4882a593Smuzhiyun ((vactive & 0x300) >> 4) |
298*4882a593Smuzhiyun ((hdelay & 0x300) >> 6) |
299*4882a593Smuzhiyun ((hactive & 0x300) >> 8);
300*4882a593Smuzhiyun pr_debug("%s: setting CROP_HI=%02x, VDELAY_LO=%02x, VACTIVE_LO=%02x, HDELAY_LO=%02x, HACTIVE_LO=%02x\n",
301*4882a593Smuzhiyun __func__, comb, vdelay, vactive, hdelay, hactive);
302*4882a593Smuzhiyun tw_writeb(TW68_CROP_HI, comb);
303*4882a593Smuzhiyun tw_writeb(TW68_VDELAY_LO, vdelay & 0xff);
304*4882a593Smuzhiyun tw_writeb(TW68_VACTIVE_LO, vactive & 0xff);
305*4882a593Smuzhiyun tw_writeb(TW68_HDELAY_LO, hdelay & 0xff);
306*4882a593Smuzhiyun tw_writeb(TW68_HACTIVE_LO, hactive & 0xff);
307*4882a593Smuzhiyun
308*4882a593Smuzhiyun comb = ((vscale & 0xf00) >> 4) | ((hscale & 0xf00) >> 8);
309*4882a593Smuzhiyun pr_debug("%s: setting SCALE_HI=%02x, VSCALE_LO=%02x, HSCALE_LO=%02x\n",
310*4882a593Smuzhiyun __func__, comb, vscale, hscale);
311*4882a593Smuzhiyun tw_writeb(TW68_SCALE_HI, comb);
312*4882a593Smuzhiyun tw_writeb(TW68_VSCALE_LO, vscale);
313*4882a593Smuzhiyun tw_writeb(TW68_HSCALE_LO, hscale);
314*4882a593Smuzhiyun
315*4882a593Smuzhiyun return 0;
316*4882a593Smuzhiyun }
317*4882a593Smuzhiyun
318*4882a593Smuzhiyun /* ------------------------------------------------------------------ */
319*4882a593Smuzhiyun
tw68_video_start_dma(struct tw68_dev * dev,struct tw68_buf * buf)320*4882a593Smuzhiyun int tw68_video_start_dma(struct tw68_dev *dev, struct tw68_buf *buf)
321*4882a593Smuzhiyun {
322*4882a593Smuzhiyun /* Set cropping and scaling */
323*4882a593Smuzhiyun tw68_set_scale(dev, dev->width, dev->height, dev->field);
324*4882a593Smuzhiyun /*
325*4882a593Smuzhiyun * Set start address for RISC program. Note that if the DMAP
326*4882a593Smuzhiyun * processor is currently running, it must be stopped before
327*4882a593Smuzhiyun * a new address can be set.
328*4882a593Smuzhiyun */
329*4882a593Smuzhiyun tw_clearl(TW68_DMAC, TW68_DMAP_EN);
330*4882a593Smuzhiyun tw_writel(TW68_DMAP_SA, buf->dma);
331*4882a593Smuzhiyun /* Clear any pending interrupts */
332*4882a593Smuzhiyun tw_writel(TW68_INTSTAT, dev->board_virqmask);
333*4882a593Smuzhiyun /* Enable the risc engine and the fifo */
334*4882a593Smuzhiyun tw_andorl(TW68_DMAC, 0xff, dev->fmt->twformat |
335*4882a593Smuzhiyun ColorFormatGamma | TW68_DMAP_EN | TW68_FIFO_EN);
336*4882a593Smuzhiyun dev->pci_irqmask |= dev->board_virqmask;
337*4882a593Smuzhiyun tw_setl(TW68_INTMASK, dev->pci_irqmask);
338*4882a593Smuzhiyun return 0;
339*4882a593Smuzhiyun }
340*4882a593Smuzhiyun
341*4882a593Smuzhiyun /* ------------------------------------------------------------------ */
342*4882a593Smuzhiyun
343*4882a593Smuzhiyun /* calc max # of buffers from size (must not exceed the 4MB virtual
344*4882a593Smuzhiyun * address space per DMA channel) */
tw68_buffer_count(unsigned int size,unsigned int count)345*4882a593Smuzhiyun static int tw68_buffer_count(unsigned int size, unsigned int count)
346*4882a593Smuzhiyun {
347*4882a593Smuzhiyun unsigned int maxcount;
348*4882a593Smuzhiyun
349*4882a593Smuzhiyun maxcount = (4 * 1024 * 1024) / roundup(size, PAGE_SIZE);
350*4882a593Smuzhiyun if (count > maxcount)
351*4882a593Smuzhiyun count = maxcount;
352*4882a593Smuzhiyun return count;
353*4882a593Smuzhiyun }
354*4882a593Smuzhiyun
355*4882a593Smuzhiyun /* ------------------------------------------------------------- */
356*4882a593Smuzhiyun /* vb2 queue operations */
357*4882a593Smuzhiyun
tw68_queue_setup(struct vb2_queue * q,unsigned int * num_buffers,unsigned int * num_planes,unsigned int sizes[],struct device * alloc_devs[])358*4882a593Smuzhiyun static int tw68_queue_setup(struct vb2_queue *q,
359*4882a593Smuzhiyun unsigned int *num_buffers, unsigned int *num_planes,
360*4882a593Smuzhiyun unsigned int sizes[], struct device *alloc_devs[])
361*4882a593Smuzhiyun {
362*4882a593Smuzhiyun struct tw68_dev *dev = vb2_get_drv_priv(q);
363*4882a593Smuzhiyun unsigned tot_bufs = q->num_buffers + *num_buffers;
364*4882a593Smuzhiyun unsigned size = (dev->fmt->depth * dev->width * dev->height) >> 3;
365*4882a593Smuzhiyun
366*4882a593Smuzhiyun if (tot_bufs < 2)
367*4882a593Smuzhiyun tot_bufs = 2;
368*4882a593Smuzhiyun tot_bufs = tw68_buffer_count(size, tot_bufs);
369*4882a593Smuzhiyun *num_buffers = tot_bufs - q->num_buffers;
370*4882a593Smuzhiyun /*
371*4882a593Smuzhiyun * We allow create_bufs, but only if the sizeimage is >= as the
372*4882a593Smuzhiyun * current sizeimage. The tw68_buffer_count calculation becomes quite
373*4882a593Smuzhiyun * difficult otherwise.
374*4882a593Smuzhiyun */
375*4882a593Smuzhiyun if (*num_planes)
376*4882a593Smuzhiyun return sizes[0] < size ? -EINVAL : 0;
377*4882a593Smuzhiyun *num_planes = 1;
378*4882a593Smuzhiyun sizes[0] = size;
379*4882a593Smuzhiyun
380*4882a593Smuzhiyun return 0;
381*4882a593Smuzhiyun }
382*4882a593Smuzhiyun
383*4882a593Smuzhiyun /*
384*4882a593Smuzhiyun * The risc program for each buffers works as follows: it starts with a simple
385*4882a593Smuzhiyun * 'JUMP to addr + 8', which is effectively a NOP. Then the program to DMA the
386*4882a593Smuzhiyun * buffer follows and at the end we have a JUMP back to the start + 8 (skipping
387*4882a593Smuzhiyun * the initial JUMP).
388*4882a593Smuzhiyun *
389*4882a593Smuzhiyun * This is the program of the first buffer to be queued if the active list is
390*4882a593Smuzhiyun * empty and it just keeps DMAing this buffer without generating any interrupts.
391*4882a593Smuzhiyun *
392*4882a593Smuzhiyun * If a new buffer is added then the initial JUMP in the program generates an
393*4882a593Smuzhiyun * interrupt as well which signals that the previous buffer has been DMAed
394*4882a593Smuzhiyun * successfully and that it can be returned to userspace.
395*4882a593Smuzhiyun *
396*4882a593Smuzhiyun * It also sets the final jump of the previous buffer to the start of the new
397*4882a593Smuzhiyun * buffer, thus chaining the new buffer into the DMA chain. This is a single
398*4882a593Smuzhiyun * atomic u32 write, so there is no race condition.
399*4882a593Smuzhiyun *
400*4882a593Smuzhiyun * The end-result of all this that you only get an interrupt when a buffer
401*4882a593Smuzhiyun * is ready, so the control flow is very easy.
402*4882a593Smuzhiyun */
tw68_buf_queue(struct vb2_buffer * vb)403*4882a593Smuzhiyun static void tw68_buf_queue(struct vb2_buffer *vb)
404*4882a593Smuzhiyun {
405*4882a593Smuzhiyun struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
406*4882a593Smuzhiyun struct vb2_queue *vq = vb->vb2_queue;
407*4882a593Smuzhiyun struct tw68_dev *dev = vb2_get_drv_priv(vq);
408*4882a593Smuzhiyun struct tw68_buf *buf = container_of(vbuf, struct tw68_buf, vb);
409*4882a593Smuzhiyun struct tw68_buf *prev;
410*4882a593Smuzhiyun unsigned long flags;
411*4882a593Smuzhiyun
412*4882a593Smuzhiyun spin_lock_irqsave(&dev->slock, flags);
413*4882a593Smuzhiyun
414*4882a593Smuzhiyun /* append a 'JUMP to start of buffer' to the buffer risc program */
415*4882a593Smuzhiyun buf->jmp[0] = cpu_to_le32(RISC_JUMP);
416*4882a593Smuzhiyun buf->jmp[1] = cpu_to_le32(buf->dma + 8);
417*4882a593Smuzhiyun
418*4882a593Smuzhiyun if (!list_empty(&dev->active)) {
419*4882a593Smuzhiyun prev = list_entry(dev->active.prev, struct tw68_buf, list);
420*4882a593Smuzhiyun buf->cpu[0] |= cpu_to_le32(RISC_INT_BIT);
421*4882a593Smuzhiyun prev->jmp[1] = cpu_to_le32(buf->dma);
422*4882a593Smuzhiyun }
423*4882a593Smuzhiyun list_add_tail(&buf->list, &dev->active);
424*4882a593Smuzhiyun spin_unlock_irqrestore(&dev->slock, flags);
425*4882a593Smuzhiyun }
426*4882a593Smuzhiyun
427*4882a593Smuzhiyun /*
428*4882a593Smuzhiyun * buffer_prepare
429*4882a593Smuzhiyun *
430*4882a593Smuzhiyun * Set the ancillary information into the buffer structure. This
431*4882a593Smuzhiyun * includes generating the necessary risc program if it hasn't already
432*4882a593Smuzhiyun * been done for the current buffer format.
433*4882a593Smuzhiyun * The structure fh contains the details of the format requested by the
434*4882a593Smuzhiyun * user - type, width, height and #fields. This is compared with the
435*4882a593Smuzhiyun * last format set for the current buffer. If they differ, the risc
436*4882a593Smuzhiyun * code (which controls the filling of the buffer) is (re-)generated.
437*4882a593Smuzhiyun */
tw68_buf_prepare(struct vb2_buffer * vb)438*4882a593Smuzhiyun static int tw68_buf_prepare(struct vb2_buffer *vb)
439*4882a593Smuzhiyun {
440*4882a593Smuzhiyun struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
441*4882a593Smuzhiyun struct vb2_queue *vq = vb->vb2_queue;
442*4882a593Smuzhiyun struct tw68_dev *dev = vb2_get_drv_priv(vq);
443*4882a593Smuzhiyun struct tw68_buf *buf = container_of(vbuf, struct tw68_buf, vb);
444*4882a593Smuzhiyun struct sg_table *dma = vb2_dma_sg_plane_desc(vb, 0);
445*4882a593Smuzhiyun unsigned size, bpl;
446*4882a593Smuzhiyun
447*4882a593Smuzhiyun size = (dev->width * dev->height * dev->fmt->depth) >> 3;
448*4882a593Smuzhiyun if (vb2_plane_size(vb, 0) < size)
449*4882a593Smuzhiyun return -EINVAL;
450*4882a593Smuzhiyun vb2_set_plane_payload(vb, 0, size);
451*4882a593Smuzhiyun
452*4882a593Smuzhiyun bpl = (dev->width * dev->fmt->depth) >> 3;
453*4882a593Smuzhiyun switch (dev->field) {
454*4882a593Smuzhiyun case V4L2_FIELD_TOP:
455*4882a593Smuzhiyun tw68_risc_buffer(dev->pci, buf, dma->sgl,
456*4882a593Smuzhiyun 0, UNSET, bpl, 0, dev->height);
457*4882a593Smuzhiyun break;
458*4882a593Smuzhiyun case V4L2_FIELD_BOTTOM:
459*4882a593Smuzhiyun tw68_risc_buffer(dev->pci, buf, dma->sgl,
460*4882a593Smuzhiyun UNSET, 0, bpl, 0, dev->height);
461*4882a593Smuzhiyun break;
462*4882a593Smuzhiyun case V4L2_FIELD_SEQ_TB:
463*4882a593Smuzhiyun tw68_risc_buffer(dev->pci, buf, dma->sgl,
464*4882a593Smuzhiyun 0, bpl * (dev->height >> 1),
465*4882a593Smuzhiyun bpl, 0, dev->height >> 1);
466*4882a593Smuzhiyun break;
467*4882a593Smuzhiyun case V4L2_FIELD_SEQ_BT:
468*4882a593Smuzhiyun tw68_risc_buffer(dev->pci, buf, dma->sgl,
469*4882a593Smuzhiyun bpl * (dev->height >> 1), 0,
470*4882a593Smuzhiyun bpl, 0, dev->height >> 1);
471*4882a593Smuzhiyun break;
472*4882a593Smuzhiyun case V4L2_FIELD_INTERLACED:
473*4882a593Smuzhiyun default:
474*4882a593Smuzhiyun tw68_risc_buffer(dev->pci, buf, dma->sgl,
475*4882a593Smuzhiyun 0, bpl, bpl, bpl, dev->height >> 1);
476*4882a593Smuzhiyun break;
477*4882a593Smuzhiyun }
478*4882a593Smuzhiyun return 0;
479*4882a593Smuzhiyun }
480*4882a593Smuzhiyun
tw68_buf_finish(struct vb2_buffer * vb)481*4882a593Smuzhiyun static void tw68_buf_finish(struct vb2_buffer *vb)
482*4882a593Smuzhiyun {
483*4882a593Smuzhiyun struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
484*4882a593Smuzhiyun struct vb2_queue *vq = vb->vb2_queue;
485*4882a593Smuzhiyun struct tw68_dev *dev = vb2_get_drv_priv(vq);
486*4882a593Smuzhiyun struct tw68_buf *buf = container_of(vbuf, struct tw68_buf, vb);
487*4882a593Smuzhiyun
488*4882a593Smuzhiyun pci_free_consistent(dev->pci, buf->size, buf->cpu, buf->dma);
489*4882a593Smuzhiyun }
490*4882a593Smuzhiyun
tw68_start_streaming(struct vb2_queue * q,unsigned int count)491*4882a593Smuzhiyun static int tw68_start_streaming(struct vb2_queue *q, unsigned int count)
492*4882a593Smuzhiyun {
493*4882a593Smuzhiyun struct tw68_dev *dev = vb2_get_drv_priv(q);
494*4882a593Smuzhiyun struct tw68_buf *buf =
495*4882a593Smuzhiyun container_of(dev->active.next, struct tw68_buf, list);
496*4882a593Smuzhiyun
497*4882a593Smuzhiyun dev->seqnr = 0;
498*4882a593Smuzhiyun tw68_video_start_dma(dev, buf);
499*4882a593Smuzhiyun return 0;
500*4882a593Smuzhiyun }
501*4882a593Smuzhiyun
tw68_stop_streaming(struct vb2_queue * q)502*4882a593Smuzhiyun static void tw68_stop_streaming(struct vb2_queue *q)
503*4882a593Smuzhiyun {
504*4882a593Smuzhiyun struct tw68_dev *dev = vb2_get_drv_priv(q);
505*4882a593Smuzhiyun
506*4882a593Smuzhiyun /* Stop risc & fifo */
507*4882a593Smuzhiyun tw_clearl(TW68_DMAC, TW68_DMAP_EN | TW68_FIFO_EN);
508*4882a593Smuzhiyun while (!list_empty(&dev->active)) {
509*4882a593Smuzhiyun struct tw68_buf *buf =
510*4882a593Smuzhiyun container_of(dev->active.next, struct tw68_buf, list);
511*4882a593Smuzhiyun
512*4882a593Smuzhiyun list_del(&buf->list);
513*4882a593Smuzhiyun vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
514*4882a593Smuzhiyun }
515*4882a593Smuzhiyun }
516*4882a593Smuzhiyun
517*4882a593Smuzhiyun static const struct vb2_ops tw68_video_qops = {
518*4882a593Smuzhiyun .queue_setup = tw68_queue_setup,
519*4882a593Smuzhiyun .buf_queue = tw68_buf_queue,
520*4882a593Smuzhiyun .buf_prepare = tw68_buf_prepare,
521*4882a593Smuzhiyun .buf_finish = tw68_buf_finish,
522*4882a593Smuzhiyun .start_streaming = tw68_start_streaming,
523*4882a593Smuzhiyun .stop_streaming = tw68_stop_streaming,
524*4882a593Smuzhiyun .wait_prepare = vb2_ops_wait_prepare,
525*4882a593Smuzhiyun .wait_finish = vb2_ops_wait_finish,
526*4882a593Smuzhiyun };
527*4882a593Smuzhiyun
528*4882a593Smuzhiyun /* ------------------------------------------------------------------ */
529*4882a593Smuzhiyun
tw68_s_ctrl(struct v4l2_ctrl * ctrl)530*4882a593Smuzhiyun static int tw68_s_ctrl(struct v4l2_ctrl *ctrl)
531*4882a593Smuzhiyun {
532*4882a593Smuzhiyun struct tw68_dev *dev =
533*4882a593Smuzhiyun container_of(ctrl->handler, struct tw68_dev, hdl);
534*4882a593Smuzhiyun
535*4882a593Smuzhiyun switch (ctrl->id) {
536*4882a593Smuzhiyun case V4L2_CID_BRIGHTNESS:
537*4882a593Smuzhiyun tw_writeb(TW68_BRIGHT, ctrl->val);
538*4882a593Smuzhiyun break;
539*4882a593Smuzhiyun case V4L2_CID_HUE:
540*4882a593Smuzhiyun tw_writeb(TW68_HUE, ctrl->val);
541*4882a593Smuzhiyun break;
542*4882a593Smuzhiyun case V4L2_CID_CONTRAST:
543*4882a593Smuzhiyun tw_writeb(TW68_CONTRAST, ctrl->val);
544*4882a593Smuzhiyun break;
545*4882a593Smuzhiyun case V4L2_CID_SATURATION:
546*4882a593Smuzhiyun tw_writeb(TW68_SAT_U, ctrl->val);
547*4882a593Smuzhiyun tw_writeb(TW68_SAT_V, ctrl->val);
548*4882a593Smuzhiyun break;
549*4882a593Smuzhiyun case V4L2_CID_COLOR_KILLER:
550*4882a593Smuzhiyun if (ctrl->val)
551*4882a593Smuzhiyun tw_andorb(TW68_MISC2, 0xe0, 0xe0);
552*4882a593Smuzhiyun else
553*4882a593Smuzhiyun tw_andorb(TW68_MISC2, 0xe0, 0x00);
554*4882a593Smuzhiyun break;
555*4882a593Smuzhiyun case V4L2_CID_CHROMA_AGC:
556*4882a593Smuzhiyun if (ctrl->val)
557*4882a593Smuzhiyun tw_andorb(TW68_LOOP, 0x30, 0x20);
558*4882a593Smuzhiyun else
559*4882a593Smuzhiyun tw_andorb(TW68_LOOP, 0x30, 0x00);
560*4882a593Smuzhiyun break;
561*4882a593Smuzhiyun }
562*4882a593Smuzhiyun return 0;
563*4882a593Smuzhiyun }
564*4882a593Smuzhiyun
565*4882a593Smuzhiyun /* ------------------------------------------------------------------ */
566*4882a593Smuzhiyun
567*4882a593Smuzhiyun /*
568*4882a593Smuzhiyun * Note that this routine returns what is stored in the fh structure, and
569*4882a593Smuzhiyun * does not interrogate any of the device registers.
570*4882a593Smuzhiyun */
tw68_g_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)571*4882a593Smuzhiyun static int tw68_g_fmt_vid_cap(struct file *file, void *priv,
572*4882a593Smuzhiyun struct v4l2_format *f)
573*4882a593Smuzhiyun {
574*4882a593Smuzhiyun struct tw68_dev *dev = video_drvdata(file);
575*4882a593Smuzhiyun
576*4882a593Smuzhiyun f->fmt.pix.width = dev->width;
577*4882a593Smuzhiyun f->fmt.pix.height = dev->height;
578*4882a593Smuzhiyun f->fmt.pix.field = dev->field;
579*4882a593Smuzhiyun f->fmt.pix.pixelformat = dev->fmt->fourcc;
580*4882a593Smuzhiyun f->fmt.pix.bytesperline =
581*4882a593Smuzhiyun (f->fmt.pix.width * (dev->fmt->depth)) >> 3;
582*4882a593Smuzhiyun f->fmt.pix.sizeimage =
583*4882a593Smuzhiyun f->fmt.pix.height * f->fmt.pix.bytesperline;
584*4882a593Smuzhiyun f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
585*4882a593Smuzhiyun return 0;
586*4882a593Smuzhiyun }
587*4882a593Smuzhiyun
tw68_try_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)588*4882a593Smuzhiyun static int tw68_try_fmt_vid_cap(struct file *file, void *priv,
589*4882a593Smuzhiyun struct v4l2_format *f)
590*4882a593Smuzhiyun {
591*4882a593Smuzhiyun struct tw68_dev *dev = video_drvdata(file);
592*4882a593Smuzhiyun const struct tw68_format *fmt;
593*4882a593Smuzhiyun enum v4l2_field field;
594*4882a593Smuzhiyun unsigned int maxh;
595*4882a593Smuzhiyun
596*4882a593Smuzhiyun fmt = format_by_fourcc(f->fmt.pix.pixelformat);
597*4882a593Smuzhiyun if (NULL == fmt)
598*4882a593Smuzhiyun return -EINVAL;
599*4882a593Smuzhiyun
600*4882a593Smuzhiyun field = f->fmt.pix.field;
601*4882a593Smuzhiyun maxh = (dev->tvnorm->id & V4L2_STD_525_60) ? 480 : 576;
602*4882a593Smuzhiyun
603*4882a593Smuzhiyun switch (field) {
604*4882a593Smuzhiyun case V4L2_FIELD_TOP:
605*4882a593Smuzhiyun case V4L2_FIELD_BOTTOM:
606*4882a593Smuzhiyun break;
607*4882a593Smuzhiyun case V4L2_FIELD_INTERLACED:
608*4882a593Smuzhiyun case V4L2_FIELD_SEQ_BT:
609*4882a593Smuzhiyun case V4L2_FIELD_SEQ_TB:
610*4882a593Smuzhiyun maxh = maxh * 2;
611*4882a593Smuzhiyun break;
612*4882a593Smuzhiyun default:
613*4882a593Smuzhiyun field = (f->fmt.pix.height > maxh / 2)
614*4882a593Smuzhiyun ? V4L2_FIELD_INTERLACED
615*4882a593Smuzhiyun : V4L2_FIELD_BOTTOM;
616*4882a593Smuzhiyun break;
617*4882a593Smuzhiyun }
618*4882a593Smuzhiyun
619*4882a593Smuzhiyun f->fmt.pix.field = field;
620*4882a593Smuzhiyun if (f->fmt.pix.width < 48)
621*4882a593Smuzhiyun f->fmt.pix.width = 48;
622*4882a593Smuzhiyun if (f->fmt.pix.height < 32)
623*4882a593Smuzhiyun f->fmt.pix.height = 32;
624*4882a593Smuzhiyun if (f->fmt.pix.width > 720)
625*4882a593Smuzhiyun f->fmt.pix.width = 720;
626*4882a593Smuzhiyun if (f->fmt.pix.height > maxh)
627*4882a593Smuzhiyun f->fmt.pix.height = maxh;
628*4882a593Smuzhiyun f->fmt.pix.width &= ~0x03;
629*4882a593Smuzhiyun f->fmt.pix.bytesperline =
630*4882a593Smuzhiyun (f->fmt.pix.width * (fmt->depth)) >> 3;
631*4882a593Smuzhiyun f->fmt.pix.sizeimage =
632*4882a593Smuzhiyun f->fmt.pix.height * f->fmt.pix.bytesperline;
633*4882a593Smuzhiyun f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
634*4882a593Smuzhiyun return 0;
635*4882a593Smuzhiyun }
636*4882a593Smuzhiyun
637*4882a593Smuzhiyun /*
638*4882a593Smuzhiyun * Note that tw68_s_fmt_vid_cap sets the information into the fh structure,
639*4882a593Smuzhiyun * and it will be used for all future new buffers. However, there could be
640*4882a593Smuzhiyun * some number of buffers on the "active" chain which will be filled before
641*4882a593Smuzhiyun * the change takes place.
642*4882a593Smuzhiyun */
tw68_s_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)643*4882a593Smuzhiyun static int tw68_s_fmt_vid_cap(struct file *file, void *priv,
644*4882a593Smuzhiyun struct v4l2_format *f)
645*4882a593Smuzhiyun {
646*4882a593Smuzhiyun struct tw68_dev *dev = video_drvdata(file);
647*4882a593Smuzhiyun int err;
648*4882a593Smuzhiyun
649*4882a593Smuzhiyun err = tw68_try_fmt_vid_cap(file, priv, f);
650*4882a593Smuzhiyun if (0 != err)
651*4882a593Smuzhiyun return err;
652*4882a593Smuzhiyun
653*4882a593Smuzhiyun dev->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
654*4882a593Smuzhiyun dev->width = f->fmt.pix.width;
655*4882a593Smuzhiyun dev->height = f->fmt.pix.height;
656*4882a593Smuzhiyun dev->field = f->fmt.pix.field;
657*4882a593Smuzhiyun return 0;
658*4882a593Smuzhiyun }
659*4882a593Smuzhiyun
tw68_enum_input(struct file * file,void * priv,struct v4l2_input * i)660*4882a593Smuzhiyun static int tw68_enum_input(struct file *file, void *priv,
661*4882a593Smuzhiyun struct v4l2_input *i)
662*4882a593Smuzhiyun {
663*4882a593Smuzhiyun struct tw68_dev *dev = video_drvdata(file);
664*4882a593Smuzhiyun unsigned int n;
665*4882a593Smuzhiyun
666*4882a593Smuzhiyun n = i->index;
667*4882a593Smuzhiyun if (n >= TW68_INPUT_MAX)
668*4882a593Smuzhiyun return -EINVAL;
669*4882a593Smuzhiyun i->index = n;
670*4882a593Smuzhiyun i->type = V4L2_INPUT_TYPE_CAMERA;
671*4882a593Smuzhiyun snprintf(i->name, sizeof(i->name), "Composite %d", n);
672*4882a593Smuzhiyun
673*4882a593Smuzhiyun /* If the query is for the current input, get live data */
674*4882a593Smuzhiyun if (n == dev->input) {
675*4882a593Smuzhiyun int v1 = tw_readb(TW68_STATUS1);
676*4882a593Smuzhiyun int v2 = tw_readb(TW68_MVSN);
677*4882a593Smuzhiyun
678*4882a593Smuzhiyun if (0 != (v1 & (1 << 7)))
679*4882a593Smuzhiyun i->status |= V4L2_IN_ST_NO_SYNC;
680*4882a593Smuzhiyun if (0 != (v1 & (1 << 6)))
681*4882a593Smuzhiyun i->status |= V4L2_IN_ST_NO_H_LOCK;
682*4882a593Smuzhiyun if (0 != (v1 & (1 << 2)))
683*4882a593Smuzhiyun i->status |= V4L2_IN_ST_NO_SIGNAL;
684*4882a593Smuzhiyun if (0 != (v1 & 1 << 1))
685*4882a593Smuzhiyun i->status |= V4L2_IN_ST_NO_COLOR;
686*4882a593Smuzhiyun if (0 != (v2 & (1 << 2)))
687*4882a593Smuzhiyun i->status |= V4L2_IN_ST_MACROVISION;
688*4882a593Smuzhiyun }
689*4882a593Smuzhiyun i->std = video_devdata(file)->tvnorms;
690*4882a593Smuzhiyun return 0;
691*4882a593Smuzhiyun }
692*4882a593Smuzhiyun
tw68_g_input(struct file * file,void * priv,unsigned int * i)693*4882a593Smuzhiyun static int tw68_g_input(struct file *file, void *priv, unsigned int *i)
694*4882a593Smuzhiyun {
695*4882a593Smuzhiyun struct tw68_dev *dev = video_drvdata(file);
696*4882a593Smuzhiyun
697*4882a593Smuzhiyun *i = dev->input;
698*4882a593Smuzhiyun return 0;
699*4882a593Smuzhiyun }
700*4882a593Smuzhiyun
tw68_s_input(struct file * file,void * priv,unsigned int i)701*4882a593Smuzhiyun static int tw68_s_input(struct file *file, void *priv, unsigned int i)
702*4882a593Smuzhiyun {
703*4882a593Smuzhiyun struct tw68_dev *dev = video_drvdata(file);
704*4882a593Smuzhiyun
705*4882a593Smuzhiyun if (i >= TW68_INPUT_MAX)
706*4882a593Smuzhiyun return -EINVAL;
707*4882a593Smuzhiyun dev->input = i;
708*4882a593Smuzhiyun tw_andorb(TW68_INFORM, 0x03 << 2, dev->input << 2);
709*4882a593Smuzhiyun return 0;
710*4882a593Smuzhiyun }
711*4882a593Smuzhiyun
tw68_querycap(struct file * file,void * priv,struct v4l2_capability * cap)712*4882a593Smuzhiyun static int tw68_querycap(struct file *file, void *priv,
713*4882a593Smuzhiyun struct v4l2_capability *cap)
714*4882a593Smuzhiyun {
715*4882a593Smuzhiyun struct tw68_dev *dev = video_drvdata(file);
716*4882a593Smuzhiyun
717*4882a593Smuzhiyun strscpy(cap->driver, "tw68", sizeof(cap->driver));
718*4882a593Smuzhiyun strscpy(cap->card, "Techwell Capture Card",
719*4882a593Smuzhiyun sizeof(cap->card));
720*4882a593Smuzhiyun sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
721*4882a593Smuzhiyun return 0;
722*4882a593Smuzhiyun }
723*4882a593Smuzhiyun
tw68_s_std(struct file * file,void * priv,v4l2_std_id id)724*4882a593Smuzhiyun static int tw68_s_std(struct file *file, void *priv, v4l2_std_id id)
725*4882a593Smuzhiyun {
726*4882a593Smuzhiyun struct tw68_dev *dev = video_drvdata(file);
727*4882a593Smuzhiyun unsigned int i;
728*4882a593Smuzhiyun
729*4882a593Smuzhiyun if (vb2_is_busy(&dev->vidq))
730*4882a593Smuzhiyun return -EBUSY;
731*4882a593Smuzhiyun
732*4882a593Smuzhiyun /* Look for match on complete norm id (may have mult bits) */
733*4882a593Smuzhiyun for (i = 0; i < TVNORMS; i++) {
734*4882a593Smuzhiyun if (id == tvnorms[i].id)
735*4882a593Smuzhiyun break;
736*4882a593Smuzhiyun }
737*4882a593Smuzhiyun
738*4882a593Smuzhiyun /* If no exact match, look for norm which contains this one */
739*4882a593Smuzhiyun if (i == TVNORMS) {
740*4882a593Smuzhiyun for (i = 0; i < TVNORMS; i++)
741*4882a593Smuzhiyun if (id & tvnorms[i].id)
742*4882a593Smuzhiyun break;
743*4882a593Smuzhiyun }
744*4882a593Smuzhiyun /* If still not matched, give up */
745*4882a593Smuzhiyun if (i == TVNORMS)
746*4882a593Smuzhiyun return -EINVAL;
747*4882a593Smuzhiyun
748*4882a593Smuzhiyun set_tvnorm(dev, &tvnorms[i]); /* do the actual setting */
749*4882a593Smuzhiyun return 0;
750*4882a593Smuzhiyun }
751*4882a593Smuzhiyun
tw68_g_std(struct file * file,void * priv,v4l2_std_id * id)752*4882a593Smuzhiyun static int tw68_g_std(struct file *file, void *priv, v4l2_std_id *id)
753*4882a593Smuzhiyun {
754*4882a593Smuzhiyun struct tw68_dev *dev = video_drvdata(file);
755*4882a593Smuzhiyun
756*4882a593Smuzhiyun *id = dev->tvnorm->id;
757*4882a593Smuzhiyun return 0;
758*4882a593Smuzhiyun }
759*4882a593Smuzhiyun
tw68_enum_fmt_vid_cap(struct file * file,void * priv,struct v4l2_fmtdesc * f)760*4882a593Smuzhiyun static int tw68_enum_fmt_vid_cap(struct file *file, void *priv,
761*4882a593Smuzhiyun struct v4l2_fmtdesc *f)
762*4882a593Smuzhiyun {
763*4882a593Smuzhiyun if (f->index >= FORMATS)
764*4882a593Smuzhiyun return -EINVAL;
765*4882a593Smuzhiyun
766*4882a593Smuzhiyun f->pixelformat = formats[f->index].fourcc;
767*4882a593Smuzhiyun
768*4882a593Smuzhiyun return 0;
769*4882a593Smuzhiyun }
770*4882a593Smuzhiyun
771*4882a593Smuzhiyun /*
772*4882a593Smuzhiyun * Used strictly for internal development and debugging, this routine
773*4882a593Smuzhiyun * prints out the current register contents for the tw68xx device.
774*4882a593Smuzhiyun */
tw68_dump_regs(struct tw68_dev * dev)775*4882a593Smuzhiyun static void tw68_dump_regs(struct tw68_dev *dev)
776*4882a593Smuzhiyun {
777*4882a593Smuzhiyun unsigned char line[80];
778*4882a593Smuzhiyun int i, j, k;
779*4882a593Smuzhiyun unsigned char *cptr;
780*4882a593Smuzhiyun
781*4882a593Smuzhiyun pr_info("Full dump of TW68 registers:\n");
782*4882a593Smuzhiyun /* First we do the PCI regs, 8 4-byte regs per line */
783*4882a593Smuzhiyun for (i = 0; i < 0x100; i += 32) {
784*4882a593Smuzhiyun cptr = line;
785*4882a593Smuzhiyun cptr += sprintf(cptr, "%03x ", i);
786*4882a593Smuzhiyun /* j steps through the next 4 words */
787*4882a593Smuzhiyun for (j = i; j < i + 16; j += 4)
788*4882a593Smuzhiyun cptr += sprintf(cptr, "%08x ", tw_readl(j));
789*4882a593Smuzhiyun *cptr++ = ' ';
790*4882a593Smuzhiyun for (; j < i + 32; j += 4)
791*4882a593Smuzhiyun cptr += sprintf(cptr, "%08x ", tw_readl(j));
792*4882a593Smuzhiyun *cptr++ = '\n';
793*4882a593Smuzhiyun *cptr = 0;
794*4882a593Smuzhiyun pr_info("%s", line);
795*4882a593Smuzhiyun }
796*4882a593Smuzhiyun /* Next the control regs, which are single-byte, address mod 4 */
797*4882a593Smuzhiyun while (i < 0x400) {
798*4882a593Smuzhiyun cptr = line;
799*4882a593Smuzhiyun cptr += sprintf(cptr, "%03x ", i);
800*4882a593Smuzhiyun /* Print out 4 groups of 4 bytes */
801*4882a593Smuzhiyun for (j = 0; j < 4; j++) {
802*4882a593Smuzhiyun for (k = 0; k < 4; k++) {
803*4882a593Smuzhiyun cptr += sprintf(cptr, "%02x ",
804*4882a593Smuzhiyun tw_readb(i));
805*4882a593Smuzhiyun i += 4;
806*4882a593Smuzhiyun }
807*4882a593Smuzhiyun *cptr++ = ' ';
808*4882a593Smuzhiyun }
809*4882a593Smuzhiyun *cptr++ = '\n';
810*4882a593Smuzhiyun *cptr = 0;
811*4882a593Smuzhiyun pr_info("%s", line);
812*4882a593Smuzhiyun }
813*4882a593Smuzhiyun }
814*4882a593Smuzhiyun
vidioc_log_status(struct file * file,void * priv)815*4882a593Smuzhiyun static int vidioc_log_status(struct file *file, void *priv)
816*4882a593Smuzhiyun {
817*4882a593Smuzhiyun struct tw68_dev *dev = video_drvdata(file);
818*4882a593Smuzhiyun
819*4882a593Smuzhiyun tw68_dump_regs(dev);
820*4882a593Smuzhiyun return v4l2_ctrl_log_status(file, priv);
821*4882a593Smuzhiyun }
822*4882a593Smuzhiyun
823*4882a593Smuzhiyun #ifdef CONFIG_VIDEO_ADV_DEBUG
vidioc_g_register(struct file * file,void * priv,struct v4l2_dbg_register * reg)824*4882a593Smuzhiyun static int vidioc_g_register(struct file *file, void *priv,
825*4882a593Smuzhiyun struct v4l2_dbg_register *reg)
826*4882a593Smuzhiyun {
827*4882a593Smuzhiyun struct tw68_dev *dev = video_drvdata(file);
828*4882a593Smuzhiyun
829*4882a593Smuzhiyun if (reg->size == 1)
830*4882a593Smuzhiyun reg->val = tw_readb(reg->reg);
831*4882a593Smuzhiyun else
832*4882a593Smuzhiyun reg->val = tw_readl(reg->reg);
833*4882a593Smuzhiyun return 0;
834*4882a593Smuzhiyun }
835*4882a593Smuzhiyun
vidioc_s_register(struct file * file,void * priv,const struct v4l2_dbg_register * reg)836*4882a593Smuzhiyun static int vidioc_s_register(struct file *file, void *priv,
837*4882a593Smuzhiyun const struct v4l2_dbg_register *reg)
838*4882a593Smuzhiyun {
839*4882a593Smuzhiyun struct tw68_dev *dev = video_drvdata(file);
840*4882a593Smuzhiyun
841*4882a593Smuzhiyun if (reg->size == 1)
842*4882a593Smuzhiyun tw_writeb(reg->reg, reg->val);
843*4882a593Smuzhiyun else
844*4882a593Smuzhiyun tw_writel(reg->reg & 0xffff, reg->val);
845*4882a593Smuzhiyun return 0;
846*4882a593Smuzhiyun }
847*4882a593Smuzhiyun #endif
848*4882a593Smuzhiyun
849*4882a593Smuzhiyun static const struct v4l2_ctrl_ops tw68_ctrl_ops = {
850*4882a593Smuzhiyun .s_ctrl = tw68_s_ctrl,
851*4882a593Smuzhiyun };
852*4882a593Smuzhiyun
853*4882a593Smuzhiyun static const struct v4l2_file_operations video_fops = {
854*4882a593Smuzhiyun .owner = THIS_MODULE,
855*4882a593Smuzhiyun .open = v4l2_fh_open,
856*4882a593Smuzhiyun .release = vb2_fop_release,
857*4882a593Smuzhiyun .read = vb2_fop_read,
858*4882a593Smuzhiyun .poll = vb2_fop_poll,
859*4882a593Smuzhiyun .mmap = vb2_fop_mmap,
860*4882a593Smuzhiyun .unlocked_ioctl = video_ioctl2,
861*4882a593Smuzhiyun };
862*4882a593Smuzhiyun
863*4882a593Smuzhiyun static const struct v4l2_ioctl_ops video_ioctl_ops = {
864*4882a593Smuzhiyun .vidioc_querycap = tw68_querycap,
865*4882a593Smuzhiyun .vidioc_enum_fmt_vid_cap = tw68_enum_fmt_vid_cap,
866*4882a593Smuzhiyun .vidioc_reqbufs = vb2_ioctl_reqbufs,
867*4882a593Smuzhiyun .vidioc_create_bufs = vb2_ioctl_create_bufs,
868*4882a593Smuzhiyun .vidioc_querybuf = vb2_ioctl_querybuf,
869*4882a593Smuzhiyun .vidioc_qbuf = vb2_ioctl_qbuf,
870*4882a593Smuzhiyun .vidioc_dqbuf = vb2_ioctl_dqbuf,
871*4882a593Smuzhiyun .vidioc_s_std = tw68_s_std,
872*4882a593Smuzhiyun .vidioc_g_std = tw68_g_std,
873*4882a593Smuzhiyun .vidioc_enum_input = tw68_enum_input,
874*4882a593Smuzhiyun .vidioc_g_input = tw68_g_input,
875*4882a593Smuzhiyun .vidioc_s_input = tw68_s_input,
876*4882a593Smuzhiyun .vidioc_streamon = vb2_ioctl_streamon,
877*4882a593Smuzhiyun .vidioc_streamoff = vb2_ioctl_streamoff,
878*4882a593Smuzhiyun .vidioc_g_fmt_vid_cap = tw68_g_fmt_vid_cap,
879*4882a593Smuzhiyun .vidioc_try_fmt_vid_cap = tw68_try_fmt_vid_cap,
880*4882a593Smuzhiyun .vidioc_s_fmt_vid_cap = tw68_s_fmt_vid_cap,
881*4882a593Smuzhiyun .vidioc_log_status = vidioc_log_status,
882*4882a593Smuzhiyun .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
883*4882a593Smuzhiyun .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
884*4882a593Smuzhiyun #ifdef CONFIG_VIDEO_ADV_DEBUG
885*4882a593Smuzhiyun .vidioc_g_register = vidioc_g_register,
886*4882a593Smuzhiyun .vidioc_s_register = vidioc_s_register,
887*4882a593Smuzhiyun #endif
888*4882a593Smuzhiyun };
889*4882a593Smuzhiyun
890*4882a593Smuzhiyun static const struct video_device tw68_video_template = {
891*4882a593Smuzhiyun .name = "tw68_video",
892*4882a593Smuzhiyun .fops = &video_fops,
893*4882a593Smuzhiyun .ioctl_ops = &video_ioctl_ops,
894*4882a593Smuzhiyun .release = video_device_release_empty,
895*4882a593Smuzhiyun .tvnorms = TW68_NORMS,
896*4882a593Smuzhiyun .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
897*4882a593Smuzhiyun V4L2_CAP_STREAMING,
898*4882a593Smuzhiyun };
899*4882a593Smuzhiyun
900*4882a593Smuzhiyun /* ------------------------------------------------------------------ */
901*4882a593Smuzhiyun /* exported stuff */
tw68_set_tvnorm_hw(struct tw68_dev * dev)902*4882a593Smuzhiyun void tw68_set_tvnorm_hw(struct tw68_dev *dev)
903*4882a593Smuzhiyun {
904*4882a593Smuzhiyun tw_andorb(TW68_SDT, 0x07, dev->tvnorm->format);
905*4882a593Smuzhiyun }
906*4882a593Smuzhiyun
tw68_video_init1(struct tw68_dev * dev)907*4882a593Smuzhiyun int tw68_video_init1(struct tw68_dev *dev)
908*4882a593Smuzhiyun {
909*4882a593Smuzhiyun struct v4l2_ctrl_handler *hdl = &dev->hdl;
910*4882a593Smuzhiyun
911*4882a593Smuzhiyun v4l2_ctrl_handler_init(hdl, 6);
912*4882a593Smuzhiyun v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops,
913*4882a593Smuzhiyun V4L2_CID_BRIGHTNESS, -128, 127, 1, 20);
914*4882a593Smuzhiyun v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops,
915*4882a593Smuzhiyun V4L2_CID_CONTRAST, 0, 255, 1, 100);
916*4882a593Smuzhiyun v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops,
917*4882a593Smuzhiyun V4L2_CID_SATURATION, 0, 255, 1, 128);
918*4882a593Smuzhiyun /* NTSC only */
919*4882a593Smuzhiyun v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops,
920*4882a593Smuzhiyun V4L2_CID_HUE, -128, 127, 1, 0);
921*4882a593Smuzhiyun v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops,
922*4882a593Smuzhiyun V4L2_CID_COLOR_KILLER, 0, 1, 1, 0);
923*4882a593Smuzhiyun v4l2_ctrl_new_std(hdl, &tw68_ctrl_ops,
924*4882a593Smuzhiyun V4L2_CID_CHROMA_AGC, 0, 1, 1, 1);
925*4882a593Smuzhiyun if (hdl->error) {
926*4882a593Smuzhiyun v4l2_ctrl_handler_free(hdl);
927*4882a593Smuzhiyun return hdl->error;
928*4882a593Smuzhiyun }
929*4882a593Smuzhiyun dev->v4l2_dev.ctrl_handler = hdl;
930*4882a593Smuzhiyun v4l2_ctrl_handler_setup(hdl);
931*4882a593Smuzhiyun return 0;
932*4882a593Smuzhiyun }
933*4882a593Smuzhiyun
tw68_video_init2(struct tw68_dev * dev,int video_nr)934*4882a593Smuzhiyun int tw68_video_init2(struct tw68_dev *dev, int video_nr)
935*4882a593Smuzhiyun {
936*4882a593Smuzhiyun int ret;
937*4882a593Smuzhiyun
938*4882a593Smuzhiyun set_tvnorm(dev, &tvnorms[0]);
939*4882a593Smuzhiyun
940*4882a593Smuzhiyun dev->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
941*4882a593Smuzhiyun dev->width = 720;
942*4882a593Smuzhiyun dev->height = 576;
943*4882a593Smuzhiyun dev->field = V4L2_FIELD_INTERLACED;
944*4882a593Smuzhiyun
945*4882a593Smuzhiyun INIT_LIST_HEAD(&dev->active);
946*4882a593Smuzhiyun dev->vidq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
947*4882a593Smuzhiyun dev->vidq.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
948*4882a593Smuzhiyun dev->vidq.io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ | VB2_DMABUF;
949*4882a593Smuzhiyun dev->vidq.ops = &tw68_video_qops;
950*4882a593Smuzhiyun dev->vidq.mem_ops = &vb2_dma_sg_memops;
951*4882a593Smuzhiyun dev->vidq.drv_priv = dev;
952*4882a593Smuzhiyun dev->vidq.gfp_flags = __GFP_DMA32 | __GFP_KSWAPD_RECLAIM;
953*4882a593Smuzhiyun dev->vidq.buf_struct_size = sizeof(struct tw68_buf);
954*4882a593Smuzhiyun dev->vidq.lock = &dev->lock;
955*4882a593Smuzhiyun dev->vidq.min_buffers_needed = 2;
956*4882a593Smuzhiyun dev->vidq.dev = &dev->pci->dev;
957*4882a593Smuzhiyun ret = vb2_queue_init(&dev->vidq);
958*4882a593Smuzhiyun if (ret)
959*4882a593Smuzhiyun return ret;
960*4882a593Smuzhiyun dev->vdev = tw68_video_template;
961*4882a593Smuzhiyun dev->vdev.v4l2_dev = &dev->v4l2_dev;
962*4882a593Smuzhiyun dev->vdev.lock = &dev->lock;
963*4882a593Smuzhiyun dev->vdev.queue = &dev->vidq;
964*4882a593Smuzhiyun video_set_drvdata(&dev->vdev, dev);
965*4882a593Smuzhiyun return video_register_device(&dev->vdev, VFL_TYPE_VIDEO, video_nr);
966*4882a593Smuzhiyun }
967*4882a593Smuzhiyun
968*4882a593Smuzhiyun /*
969*4882a593Smuzhiyun * tw68_irq_video_done
970*4882a593Smuzhiyun */
tw68_irq_video_done(struct tw68_dev * dev,unsigned long status)971*4882a593Smuzhiyun void tw68_irq_video_done(struct tw68_dev *dev, unsigned long status)
972*4882a593Smuzhiyun {
973*4882a593Smuzhiyun __u32 reg;
974*4882a593Smuzhiyun
975*4882a593Smuzhiyun /* reset interrupts handled by this routine */
976*4882a593Smuzhiyun tw_writel(TW68_INTSTAT, status);
977*4882a593Smuzhiyun /*
978*4882a593Smuzhiyun * Check most likely first
979*4882a593Smuzhiyun *
980*4882a593Smuzhiyun * DMAPI shows we have reached the end of the risc code
981*4882a593Smuzhiyun * for the current buffer.
982*4882a593Smuzhiyun */
983*4882a593Smuzhiyun if (status & TW68_DMAPI) {
984*4882a593Smuzhiyun struct tw68_buf *buf;
985*4882a593Smuzhiyun
986*4882a593Smuzhiyun spin_lock(&dev->slock);
987*4882a593Smuzhiyun buf = list_entry(dev->active.next, struct tw68_buf, list);
988*4882a593Smuzhiyun list_del(&buf->list);
989*4882a593Smuzhiyun spin_unlock(&dev->slock);
990*4882a593Smuzhiyun buf->vb.vb2_buf.timestamp = ktime_get_ns();
991*4882a593Smuzhiyun buf->vb.field = dev->field;
992*4882a593Smuzhiyun buf->vb.sequence = dev->seqnr++;
993*4882a593Smuzhiyun vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
994*4882a593Smuzhiyun status &= ~(TW68_DMAPI);
995*4882a593Smuzhiyun if (0 == status)
996*4882a593Smuzhiyun return;
997*4882a593Smuzhiyun }
998*4882a593Smuzhiyun if (status & (TW68_VLOCK | TW68_HLOCK))
999*4882a593Smuzhiyun dev_dbg(&dev->pci->dev, "Lost sync\n");
1000*4882a593Smuzhiyun if (status & TW68_PABORT)
1001*4882a593Smuzhiyun dev_err(&dev->pci->dev, "PABORT interrupt\n");
1002*4882a593Smuzhiyun if (status & TW68_DMAPERR)
1003*4882a593Smuzhiyun dev_err(&dev->pci->dev, "DMAPERR interrupt\n");
1004*4882a593Smuzhiyun /*
1005*4882a593Smuzhiyun * On TW6800, FDMIS is apparently generated if video input is switched
1006*4882a593Smuzhiyun * during operation. Therefore, it is not enabled for that chip.
1007*4882a593Smuzhiyun */
1008*4882a593Smuzhiyun if (status & TW68_FDMIS)
1009*4882a593Smuzhiyun dev_dbg(&dev->pci->dev, "FDMIS interrupt\n");
1010*4882a593Smuzhiyun if (status & TW68_FFOF) {
1011*4882a593Smuzhiyun /* probably a logic error */
1012*4882a593Smuzhiyun reg = tw_readl(TW68_DMAC) & TW68_FIFO_EN;
1013*4882a593Smuzhiyun tw_clearl(TW68_DMAC, TW68_FIFO_EN);
1014*4882a593Smuzhiyun dev_dbg(&dev->pci->dev, "FFOF interrupt\n");
1015*4882a593Smuzhiyun tw_setl(TW68_DMAC, reg);
1016*4882a593Smuzhiyun }
1017*4882a593Smuzhiyun if (status & TW68_FFERR)
1018*4882a593Smuzhiyun dev_dbg(&dev->pci->dev, "FFERR interrupt\n");
1019*4882a593Smuzhiyun }
1020