xref: /OK3568_Linux_fs/kernel/drivers/media/pci/cx25821/cx25821-video.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *  Driver for the Conexant CX25821 PCIe bridge
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  *  Copyright (C) 2009 Conexant Systems Inc.
6*4882a593Smuzhiyun  *  Authors  <shu.lin@conexant.com>, <hiep.huynh@conexant.com>
7*4882a593Smuzhiyun  *  Based on Steven Toth <stoth@linuxtv.org> cx25821 driver
8*4882a593Smuzhiyun  *  Parts adapted/taken from Eduardo Moscoso Rubino
9*4882a593Smuzhiyun  *  Copyright (C) 2009 Eduardo Moscoso Rubino <moscoso@TopoLogica.com>
10*4882a593Smuzhiyun  */
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun #include "cx25821-video.h"
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards");
17*4882a593Smuzhiyun MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>");
18*4882a593Smuzhiyun MODULE_LICENSE("GPL");
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun static unsigned int video_nr[] = {[0 ... (CX25821_MAXBOARDS - 1)] = UNSET };
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun module_param_array(video_nr, int, NULL, 0444);
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun MODULE_PARM_DESC(video_nr, "video device numbers");
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun static unsigned int video_debug = VIDEO_DEBUG;
27*4882a593Smuzhiyun module_param(video_debug, int, 0644);
28*4882a593Smuzhiyun MODULE_PARM_DESC(video_debug, "enable debug messages [video]");
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun static unsigned int irq_debug;
31*4882a593Smuzhiyun module_param(irq_debug, int, 0644);
32*4882a593Smuzhiyun MODULE_PARM_DESC(irq_debug, "enable debug messages [IRQ handler]");
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun #define FORMAT_FLAGS_PACKED       0x01
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun static const struct cx25821_fmt formats[] = {
37*4882a593Smuzhiyun 	{
38*4882a593Smuzhiyun 		.fourcc = V4L2_PIX_FMT_Y41P,
39*4882a593Smuzhiyun 		.depth = 12,
40*4882a593Smuzhiyun 		.flags = FORMAT_FLAGS_PACKED,
41*4882a593Smuzhiyun 	}, {
42*4882a593Smuzhiyun 		.fourcc = V4L2_PIX_FMT_YUYV,
43*4882a593Smuzhiyun 		.depth = 16,
44*4882a593Smuzhiyun 		.flags = FORMAT_FLAGS_PACKED,
45*4882a593Smuzhiyun 	},
46*4882a593Smuzhiyun };
47*4882a593Smuzhiyun 
cx25821_format_by_fourcc(unsigned int fourcc)48*4882a593Smuzhiyun static const struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc)
49*4882a593Smuzhiyun {
50*4882a593Smuzhiyun 	unsigned int i;
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun 	for (i = 0; i < ARRAY_SIZE(formats); i++)
53*4882a593Smuzhiyun 		if (formats[i].fourcc == fourcc)
54*4882a593Smuzhiyun 			return formats + i;
55*4882a593Smuzhiyun 	return NULL;
56*4882a593Smuzhiyun }
57*4882a593Smuzhiyun 
cx25821_start_video_dma(struct cx25821_dev * dev,struct cx25821_dmaqueue * q,struct cx25821_buffer * buf,const struct sram_channel * channel)58*4882a593Smuzhiyun int cx25821_start_video_dma(struct cx25821_dev *dev,
59*4882a593Smuzhiyun 			    struct cx25821_dmaqueue *q,
60*4882a593Smuzhiyun 			    struct cx25821_buffer *buf,
61*4882a593Smuzhiyun 			    const struct sram_channel *channel)
62*4882a593Smuzhiyun {
63*4882a593Smuzhiyun 	int tmp = 0;
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun 	/* setup fifo + format */
66*4882a593Smuzhiyun 	cx25821_sram_channel_setup(dev, channel, buf->bpl, buf->risc.dma);
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun 	/* reset counter */
69*4882a593Smuzhiyun 	cx_write(channel->gpcnt_ctl, 3);
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun 	/* enable irq */
72*4882a593Smuzhiyun 	cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << channel->i));
73*4882a593Smuzhiyun 	cx_set(channel->int_msk, 0x11);
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun 	/* start dma */
76*4882a593Smuzhiyun 	cx_write(channel->dma_ctl, 0x11);	/* FIFO and RISC enable */
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun 	/* make sure upstream setting if any is reversed */
79*4882a593Smuzhiyun 	tmp = cx_read(VID_CH_MODE_SEL);
80*4882a593Smuzhiyun 	cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00);
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun 	return 0;
83*4882a593Smuzhiyun }
84*4882a593Smuzhiyun 
cx25821_video_irq(struct cx25821_dev * dev,int chan_num,u32 status)85*4882a593Smuzhiyun int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status)
86*4882a593Smuzhiyun {
87*4882a593Smuzhiyun 	int handled = 0;
88*4882a593Smuzhiyun 	u32 mask;
89*4882a593Smuzhiyun 	const struct sram_channel *channel = dev->channels[chan_num].sram_channels;
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun 	mask = cx_read(channel->int_msk);
92*4882a593Smuzhiyun 	if (0 == (status & mask))
93*4882a593Smuzhiyun 		return handled;
94*4882a593Smuzhiyun 
95*4882a593Smuzhiyun 	cx_write(channel->int_stat, status);
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun 	/* risc op code error */
98*4882a593Smuzhiyun 	if (status & (1 << 16)) {
99*4882a593Smuzhiyun 		pr_warn("%s, %s: video risc op code error\n",
100*4882a593Smuzhiyun 			dev->name, channel->name);
101*4882a593Smuzhiyun 		cx_clear(channel->dma_ctl, 0x11);
102*4882a593Smuzhiyun 		cx25821_sram_channel_dump(dev, channel);
103*4882a593Smuzhiyun 	}
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun 	/* risc1 y */
106*4882a593Smuzhiyun 	if (status & FLD_VID_DST_RISC1) {
107*4882a593Smuzhiyun 		struct cx25821_dmaqueue *dmaq =
108*4882a593Smuzhiyun 			&dev->channels[channel->i].dma_vidq;
109*4882a593Smuzhiyun 		struct cx25821_buffer *buf;
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun 		spin_lock(&dev->slock);
112*4882a593Smuzhiyun 		if (!list_empty(&dmaq->active)) {
113*4882a593Smuzhiyun 			buf = list_entry(dmaq->active.next,
114*4882a593Smuzhiyun 					 struct cx25821_buffer, queue);
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun 			buf->vb.vb2_buf.timestamp = ktime_get_ns();
117*4882a593Smuzhiyun 			buf->vb.sequence = dmaq->count++;
118*4882a593Smuzhiyun 			list_del(&buf->queue);
119*4882a593Smuzhiyun 			vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
120*4882a593Smuzhiyun 		}
121*4882a593Smuzhiyun 		spin_unlock(&dev->slock);
122*4882a593Smuzhiyun 		handled++;
123*4882a593Smuzhiyun 	}
124*4882a593Smuzhiyun 	return handled;
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun 
cx25821_queue_setup(struct vb2_queue * q,unsigned int * num_buffers,unsigned int * num_planes,unsigned int sizes[],struct device * alloc_devs[])127*4882a593Smuzhiyun static int cx25821_queue_setup(struct vb2_queue *q,
128*4882a593Smuzhiyun 			   unsigned int *num_buffers, unsigned int *num_planes,
129*4882a593Smuzhiyun 			   unsigned int sizes[], struct device *alloc_devs[])
130*4882a593Smuzhiyun {
131*4882a593Smuzhiyun 	struct cx25821_channel *chan = q->drv_priv;
132*4882a593Smuzhiyun 	unsigned size = (chan->fmt->depth * chan->width * chan->height) >> 3;
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun 	if (*num_planes)
135*4882a593Smuzhiyun 		return sizes[0] < size ? -EINVAL : 0;
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	*num_planes = 1;
138*4882a593Smuzhiyun 	sizes[0] = size;
139*4882a593Smuzhiyun 	return 0;
140*4882a593Smuzhiyun }
141*4882a593Smuzhiyun 
cx25821_buffer_prepare(struct vb2_buffer * vb)142*4882a593Smuzhiyun static int cx25821_buffer_prepare(struct vb2_buffer *vb)
143*4882a593Smuzhiyun {
144*4882a593Smuzhiyun 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
145*4882a593Smuzhiyun 	struct cx25821_channel *chan = vb->vb2_queue->drv_priv;
146*4882a593Smuzhiyun 	struct cx25821_dev *dev = chan->dev;
147*4882a593Smuzhiyun 	struct cx25821_buffer *buf =
148*4882a593Smuzhiyun 		container_of(vbuf, struct cx25821_buffer, vb);
149*4882a593Smuzhiyun 	struct sg_table *sgt = vb2_dma_sg_plane_desc(vb, 0);
150*4882a593Smuzhiyun 	u32 line0_offset;
151*4882a593Smuzhiyun 	int bpl_local = LINE_SIZE_D1;
152*4882a593Smuzhiyun 	int ret;
153*4882a593Smuzhiyun 
154*4882a593Smuzhiyun 	if (chan->pixel_formats == PIXEL_FRMT_411)
155*4882a593Smuzhiyun 		buf->bpl = (chan->fmt->depth * chan->width) >> 3;
156*4882a593Smuzhiyun 	else
157*4882a593Smuzhiyun 		buf->bpl = (chan->fmt->depth >> 3) * chan->width;
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun 	if (vb2_plane_size(vb, 0) < chan->height * buf->bpl)
160*4882a593Smuzhiyun 		return -EINVAL;
161*4882a593Smuzhiyun 	vb2_set_plane_payload(vb, 0, chan->height * buf->bpl);
162*4882a593Smuzhiyun 	buf->vb.field = chan->field;
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 	if (chan->pixel_formats == PIXEL_FRMT_411) {
165*4882a593Smuzhiyun 		bpl_local = buf->bpl;
166*4882a593Smuzhiyun 	} else {
167*4882a593Smuzhiyun 		bpl_local = buf->bpl;   /* Default */
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 		if (chan->use_cif_resolution) {
170*4882a593Smuzhiyun 			if (dev->tvnorm & V4L2_STD_625_50)
171*4882a593Smuzhiyun 				bpl_local = 352 << 1;
172*4882a593Smuzhiyun 			else
173*4882a593Smuzhiyun 				bpl_local = chan->cif_width << 1;
174*4882a593Smuzhiyun 		}
175*4882a593Smuzhiyun 	}
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 	switch (chan->field) {
178*4882a593Smuzhiyun 	case V4L2_FIELD_TOP:
179*4882a593Smuzhiyun 		ret = cx25821_risc_buffer(dev->pci, &buf->risc,
180*4882a593Smuzhiyun 				sgt->sgl, 0, UNSET,
181*4882a593Smuzhiyun 				buf->bpl, 0, chan->height);
182*4882a593Smuzhiyun 		break;
183*4882a593Smuzhiyun 	case V4L2_FIELD_BOTTOM:
184*4882a593Smuzhiyun 		ret = cx25821_risc_buffer(dev->pci, &buf->risc,
185*4882a593Smuzhiyun 				sgt->sgl, UNSET, 0,
186*4882a593Smuzhiyun 				buf->bpl, 0, chan->height);
187*4882a593Smuzhiyun 		break;
188*4882a593Smuzhiyun 	case V4L2_FIELD_INTERLACED:
189*4882a593Smuzhiyun 		/* All other formats are top field first */
190*4882a593Smuzhiyun 		line0_offset = 0;
191*4882a593Smuzhiyun 		dprintk(1, "top field first\n");
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 		ret = cx25821_risc_buffer(dev->pci, &buf->risc,
194*4882a593Smuzhiyun 				sgt->sgl, line0_offset,
195*4882a593Smuzhiyun 				bpl_local, bpl_local, bpl_local,
196*4882a593Smuzhiyun 				chan->height >> 1);
197*4882a593Smuzhiyun 		break;
198*4882a593Smuzhiyun 	case V4L2_FIELD_SEQ_TB:
199*4882a593Smuzhiyun 		ret = cx25821_risc_buffer(dev->pci, &buf->risc,
200*4882a593Smuzhiyun 				sgt->sgl,
201*4882a593Smuzhiyun 				0, buf->bpl * (chan->height >> 1),
202*4882a593Smuzhiyun 				buf->bpl, 0, chan->height >> 1);
203*4882a593Smuzhiyun 		break;
204*4882a593Smuzhiyun 	case V4L2_FIELD_SEQ_BT:
205*4882a593Smuzhiyun 		ret = cx25821_risc_buffer(dev->pci, &buf->risc,
206*4882a593Smuzhiyun 				sgt->sgl,
207*4882a593Smuzhiyun 				buf->bpl * (chan->height >> 1), 0,
208*4882a593Smuzhiyun 				buf->bpl, 0, chan->height >> 1);
209*4882a593Smuzhiyun 		break;
210*4882a593Smuzhiyun 	default:
211*4882a593Smuzhiyun 		WARN_ON(1);
212*4882a593Smuzhiyun 		ret = -EINVAL;
213*4882a593Smuzhiyun 		break;
214*4882a593Smuzhiyun 	}
215*4882a593Smuzhiyun 
216*4882a593Smuzhiyun 	dprintk(2, "[%p/%d] buffer_prep - %dx%d %dbpp 0x%08x - dma=0x%08lx\n",
217*4882a593Smuzhiyun 		buf, buf->vb.vb2_buf.index, chan->width, chan->height,
218*4882a593Smuzhiyun 		chan->fmt->depth, chan->fmt->fourcc,
219*4882a593Smuzhiyun 		(unsigned long)buf->risc.dma);
220*4882a593Smuzhiyun 
221*4882a593Smuzhiyun 	return ret;
222*4882a593Smuzhiyun }
223*4882a593Smuzhiyun 
cx25821_buffer_finish(struct vb2_buffer * vb)224*4882a593Smuzhiyun static void cx25821_buffer_finish(struct vb2_buffer *vb)
225*4882a593Smuzhiyun {
226*4882a593Smuzhiyun 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
227*4882a593Smuzhiyun 	struct cx25821_buffer *buf =
228*4882a593Smuzhiyun 		container_of(vbuf, struct cx25821_buffer, vb);
229*4882a593Smuzhiyun 	struct cx25821_channel *chan = vb->vb2_queue->drv_priv;
230*4882a593Smuzhiyun 	struct cx25821_dev *dev = chan->dev;
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	cx25821_free_buffer(dev, buf);
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun 
cx25821_buffer_queue(struct vb2_buffer * vb)235*4882a593Smuzhiyun static void cx25821_buffer_queue(struct vb2_buffer *vb)
236*4882a593Smuzhiyun {
237*4882a593Smuzhiyun 	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
238*4882a593Smuzhiyun 	struct cx25821_buffer *buf =
239*4882a593Smuzhiyun 		container_of(vbuf, struct cx25821_buffer, vb);
240*4882a593Smuzhiyun 	struct cx25821_channel *chan = vb->vb2_queue->drv_priv;
241*4882a593Smuzhiyun 	struct cx25821_dev *dev = chan->dev;
242*4882a593Smuzhiyun 	struct cx25821_buffer *prev;
243*4882a593Smuzhiyun 	struct cx25821_dmaqueue *q = &dev->channels[chan->id].dma_vidq;
244*4882a593Smuzhiyun 
245*4882a593Smuzhiyun 	buf->risc.cpu[1] = cpu_to_le32(buf->risc.dma + 12);
246*4882a593Smuzhiyun 	buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_CNT_INC);
247*4882a593Smuzhiyun 	buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma + 12);
248*4882a593Smuzhiyun 	buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun 	if (list_empty(&q->active)) {
251*4882a593Smuzhiyun 		list_add_tail(&buf->queue, &q->active);
252*4882a593Smuzhiyun 	} else {
253*4882a593Smuzhiyun 		buf->risc.cpu[0] |= cpu_to_le32(RISC_IRQ1);
254*4882a593Smuzhiyun 		prev = list_entry(q->active.prev, struct cx25821_buffer,
255*4882a593Smuzhiyun 				queue);
256*4882a593Smuzhiyun 		list_add_tail(&buf->queue, &q->active);
257*4882a593Smuzhiyun 		prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
258*4882a593Smuzhiyun 	}
259*4882a593Smuzhiyun }
260*4882a593Smuzhiyun 
cx25821_start_streaming(struct vb2_queue * q,unsigned int count)261*4882a593Smuzhiyun static int cx25821_start_streaming(struct vb2_queue *q, unsigned int count)
262*4882a593Smuzhiyun {
263*4882a593Smuzhiyun 	struct cx25821_channel *chan = q->drv_priv;
264*4882a593Smuzhiyun 	struct cx25821_dev *dev = chan->dev;
265*4882a593Smuzhiyun 	struct cx25821_dmaqueue *dmaq = &dev->channels[chan->id].dma_vidq;
266*4882a593Smuzhiyun 	struct cx25821_buffer *buf = list_entry(dmaq->active.next,
267*4882a593Smuzhiyun 			struct cx25821_buffer, queue);
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 	dmaq->count = 0;
270*4882a593Smuzhiyun 	cx25821_start_video_dma(dev, dmaq, buf, chan->sram_channels);
271*4882a593Smuzhiyun 	return 0;
272*4882a593Smuzhiyun }
273*4882a593Smuzhiyun 
cx25821_stop_streaming(struct vb2_queue * q)274*4882a593Smuzhiyun static void cx25821_stop_streaming(struct vb2_queue *q)
275*4882a593Smuzhiyun {
276*4882a593Smuzhiyun 	struct cx25821_channel *chan = q->drv_priv;
277*4882a593Smuzhiyun 	struct cx25821_dev *dev = chan->dev;
278*4882a593Smuzhiyun 	struct cx25821_dmaqueue *dmaq = &dev->channels[chan->id].dma_vidq;
279*4882a593Smuzhiyun 	unsigned long flags;
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun 	cx_write(chan->sram_channels->dma_ctl, 0); /* FIFO and RISC disable */
282*4882a593Smuzhiyun 	spin_lock_irqsave(&dev->slock, flags);
283*4882a593Smuzhiyun 	while (!list_empty(&dmaq->active)) {
284*4882a593Smuzhiyun 		struct cx25821_buffer *buf = list_entry(dmaq->active.next,
285*4882a593Smuzhiyun 			struct cx25821_buffer, queue);
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun 		list_del(&buf->queue);
288*4882a593Smuzhiyun 		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
289*4882a593Smuzhiyun 	}
290*4882a593Smuzhiyun 	spin_unlock_irqrestore(&dev->slock, flags);
291*4882a593Smuzhiyun }
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun static const struct vb2_ops cx25821_video_qops = {
294*4882a593Smuzhiyun 	.queue_setup    = cx25821_queue_setup,
295*4882a593Smuzhiyun 	.buf_prepare  = cx25821_buffer_prepare,
296*4882a593Smuzhiyun 	.buf_finish = cx25821_buffer_finish,
297*4882a593Smuzhiyun 	.buf_queue    = cx25821_buffer_queue,
298*4882a593Smuzhiyun 	.wait_prepare = vb2_ops_wait_prepare,
299*4882a593Smuzhiyun 	.wait_finish = vb2_ops_wait_finish,
300*4882a593Smuzhiyun 	.start_streaming = cx25821_start_streaming,
301*4882a593Smuzhiyun 	.stop_streaming = cx25821_stop_streaming,
302*4882a593Smuzhiyun };
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun /* VIDEO IOCTLS */
305*4882a593Smuzhiyun 
cx25821_vidioc_enum_fmt_vid_cap(struct file * file,void * priv,struct v4l2_fmtdesc * f)306*4882a593Smuzhiyun static int cx25821_vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
307*4882a593Smuzhiyun 			    struct v4l2_fmtdesc *f)
308*4882a593Smuzhiyun {
309*4882a593Smuzhiyun 	if (unlikely(f->index >= ARRAY_SIZE(formats)))
310*4882a593Smuzhiyun 		return -EINVAL;
311*4882a593Smuzhiyun 
312*4882a593Smuzhiyun 	f->pixelformat = formats[f->index].fourcc;
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 	return 0;
315*4882a593Smuzhiyun }
316*4882a593Smuzhiyun 
cx25821_vidioc_g_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)317*4882a593Smuzhiyun static int cx25821_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
318*4882a593Smuzhiyun 				 struct v4l2_format *f)
319*4882a593Smuzhiyun {
320*4882a593Smuzhiyun 	struct cx25821_channel *chan = video_drvdata(file);
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun 	f->fmt.pix.width = chan->width;
323*4882a593Smuzhiyun 	f->fmt.pix.height = chan->height;
324*4882a593Smuzhiyun 	f->fmt.pix.field = chan->field;
325*4882a593Smuzhiyun 	f->fmt.pix.pixelformat = chan->fmt->fourcc;
326*4882a593Smuzhiyun 	f->fmt.pix.bytesperline = (chan->width * chan->fmt->depth) >> 3;
327*4882a593Smuzhiyun 	f->fmt.pix.sizeimage = chan->height * f->fmt.pix.bytesperline;
328*4882a593Smuzhiyun 	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
329*4882a593Smuzhiyun 
330*4882a593Smuzhiyun 	return 0;
331*4882a593Smuzhiyun }
332*4882a593Smuzhiyun 
cx25821_vidioc_try_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)333*4882a593Smuzhiyun static int cx25821_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
334*4882a593Smuzhiyun 				   struct v4l2_format *f)
335*4882a593Smuzhiyun {
336*4882a593Smuzhiyun 	struct cx25821_channel *chan = video_drvdata(file);
337*4882a593Smuzhiyun 	struct cx25821_dev *dev = chan->dev;
338*4882a593Smuzhiyun 	const struct cx25821_fmt *fmt;
339*4882a593Smuzhiyun 	enum v4l2_field field = f->fmt.pix.field;
340*4882a593Smuzhiyun 	unsigned int maxh;
341*4882a593Smuzhiyun 	unsigned w;
342*4882a593Smuzhiyun 
343*4882a593Smuzhiyun 	fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat);
344*4882a593Smuzhiyun 	if (NULL == fmt)
345*4882a593Smuzhiyun 		return -EINVAL;
346*4882a593Smuzhiyun 	maxh = (dev->tvnorm & V4L2_STD_625_50) ? 576 : 480;
347*4882a593Smuzhiyun 
348*4882a593Smuzhiyun 	w = f->fmt.pix.width;
349*4882a593Smuzhiyun 	if (field != V4L2_FIELD_BOTTOM)
350*4882a593Smuzhiyun 		field = V4L2_FIELD_TOP;
351*4882a593Smuzhiyun 	if (w < 352) {
352*4882a593Smuzhiyun 		w = 176;
353*4882a593Smuzhiyun 		f->fmt.pix.height = maxh / 4;
354*4882a593Smuzhiyun 	} else if (w < 720) {
355*4882a593Smuzhiyun 		w = 352;
356*4882a593Smuzhiyun 		f->fmt.pix.height = maxh / 2;
357*4882a593Smuzhiyun 	} else {
358*4882a593Smuzhiyun 		w = 720;
359*4882a593Smuzhiyun 		f->fmt.pix.height = maxh;
360*4882a593Smuzhiyun 		field = V4L2_FIELD_INTERLACED;
361*4882a593Smuzhiyun 	}
362*4882a593Smuzhiyun 	f->fmt.pix.field = field;
363*4882a593Smuzhiyun 	f->fmt.pix.width = w;
364*4882a593Smuzhiyun 	f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
365*4882a593Smuzhiyun 	f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
366*4882a593Smuzhiyun 	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
367*4882a593Smuzhiyun 
368*4882a593Smuzhiyun 	return 0;
369*4882a593Smuzhiyun }
370*4882a593Smuzhiyun 
vidioc_s_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)371*4882a593Smuzhiyun static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
372*4882a593Smuzhiyun 				struct v4l2_format *f)
373*4882a593Smuzhiyun {
374*4882a593Smuzhiyun 	struct cx25821_channel *chan = video_drvdata(file);
375*4882a593Smuzhiyun 	struct cx25821_dev *dev = chan->dev;
376*4882a593Smuzhiyun 	int pix_format = PIXEL_FRMT_422;
377*4882a593Smuzhiyun 	int err;
378*4882a593Smuzhiyun 
379*4882a593Smuzhiyun 	err = cx25821_vidioc_try_fmt_vid_cap(file, priv, f);
380*4882a593Smuzhiyun 
381*4882a593Smuzhiyun 	if (0 != err)
382*4882a593Smuzhiyun 		return err;
383*4882a593Smuzhiyun 
384*4882a593Smuzhiyun 	chan->fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat);
385*4882a593Smuzhiyun 	chan->field = f->fmt.pix.field;
386*4882a593Smuzhiyun 	chan->width = f->fmt.pix.width;
387*4882a593Smuzhiyun 	chan->height = f->fmt.pix.height;
388*4882a593Smuzhiyun 
389*4882a593Smuzhiyun 	if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P)
390*4882a593Smuzhiyun 		pix_format = PIXEL_FRMT_411;
391*4882a593Smuzhiyun 	else
392*4882a593Smuzhiyun 		pix_format = PIXEL_FRMT_422;
393*4882a593Smuzhiyun 
394*4882a593Smuzhiyun 	cx25821_set_pixel_format(dev, SRAM_CH00, pix_format);
395*4882a593Smuzhiyun 
396*4882a593Smuzhiyun 	/* check if cif resolution */
397*4882a593Smuzhiyun 	if (chan->width == 320 || chan->width == 352)
398*4882a593Smuzhiyun 		chan->use_cif_resolution = 1;
399*4882a593Smuzhiyun 	else
400*4882a593Smuzhiyun 		chan->use_cif_resolution = 0;
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun 	chan->cif_width = chan->width;
403*4882a593Smuzhiyun 	medusa_set_resolution(dev, chan->width, SRAM_CH00);
404*4882a593Smuzhiyun 	return 0;
405*4882a593Smuzhiyun }
406*4882a593Smuzhiyun 
vidioc_log_status(struct file * file,void * priv)407*4882a593Smuzhiyun static int vidioc_log_status(struct file *file, void *priv)
408*4882a593Smuzhiyun {
409*4882a593Smuzhiyun 	struct cx25821_channel *chan = video_drvdata(file);
410*4882a593Smuzhiyun 	struct cx25821_dev *dev = chan->dev;
411*4882a593Smuzhiyun 	const struct sram_channel *sram_ch = chan->sram_channels;
412*4882a593Smuzhiyun 	u32 tmp = 0;
413*4882a593Smuzhiyun 
414*4882a593Smuzhiyun 	tmp = cx_read(sram_ch->dma_ctl);
415*4882a593Smuzhiyun 	pr_info("Video input 0 is %s\n",
416*4882a593Smuzhiyun 		(tmp & 0x11) ? "streaming" : "stopped");
417*4882a593Smuzhiyun 	return 0;
418*4882a593Smuzhiyun }
419*4882a593Smuzhiyun 
420*4882a593Smuzhiyun 
cx25821_vidioc_querycap(struct file * file,void * priv,struct v4l2_capability * cap)421*4882a593Smuzhiyun static int cx25821_vidioc_querycap(struct file *file, void *priv,
422*4882a593Smuzhiyun 			    struct v4l2_capability *cap)
423*4882a593Smuzhiyun {
424*4882a593Smuzhiyun 	struct cx25821_channel *chan = video_drvdata(file);
425*4882a593Smuzhiyun 	struct cx25821_dev *dev = chan->dev;
426*4882a593Smuzhiyun 
427*4882a593Smuzhiyun 	strscpy(cap->driver, "cx25821", sizeof(cap->driver));
428*4882a593Smuzhiyun 	strscpy(cap->card, cx25821_boards[dev->board].name, sizeof(cap->card));
429*4882a593Smuzhiyun 	sprintf(cap->bus_info, "PCIe:%s", pci_name(dev->pci));
430*4882a593Smuzhiyun 	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT |
431*4882a593Smuzhiyun 			    V4L2_CAP_READWRITE | V4L2_CAP_STREAMING |
432*4882a593Smuzhiyun 			    V4L2_CAP_DEVICE_CAPS;
433*4882a593Smuzhiyun 	return 0;
434*4882a593Smuzhiyun }
435*4882a593Smuzhiyun 
cx25821_vidioc_g_std(struct file * file,void * priv,v4l2_std_id * tvnorms)436*4882a593Smuzhiyun static int cx25821_vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorms)
437*4882a593Smuzhiyun {
438*4882a593Smuzhiyun 	struct cx25821_channel *chan = video_drvdata(file);
439*4882a593Smuzhiyun 
440*4882a593Smuzhiyun 	*tvnorms = chan->dev->tvnorm;
441*4882a593Smuzhiyun 	return 0;
442*4882a593Smuzhiyun }
443*4882a593Smuzhiyun 
cx25821_vidioc_s_std(struct file * file,void * priv,v4l2_std_id tvnorms)444*4882a593Smuzhiyun static int cx25821_vidioc_s_std(struct file *file, void *priv,
445*4882a593Smuzhiyun 				v4l2_std_id tvnorms)
446*4882a593Smuzhiyun {
447*4882a593Smuzhiyun 	struct cx25821_channel *chan = video_drvdata(file);
448*4882a593Smuzhiyun 	struct cx25821_dev *dev = chan->dev;
449*4882a593Smuzhiyun 
450*4882a593Smuzhiyun 	if (dev->tvnorm == tvnorms)
451*4882a593Smuzhiyun 		return 0;
452*4882a593Smuzhiyun 
453*4882a593Smuzhiyun 	dev->tvnorm = tvnorms;
454*4882a593Smuzhiyun 	chan->width = 720;
455*4882a593Smuzhiyun 	chan->height = (dev->tvnorm & V4L2_STD_625_50) ? 576 : 480;
456*4882a593Smuzhiyun 
457*4882a593Smuzhiyun 	medusa_set_videostandard(dev);
458*4882a593Smuzhiyun 
459*4882a593Smuzhiyun 	return 0;
460*4882a593Smuzhiyun }
461*4882a593Smuzhiyun 
cx25821_vidioc_enum_input(struct file * file,void * priv,struct v4l2_input * i)462*4882a593Smuzhiyun static int cx25821_vidioc_enum_input(struct file *file, void *priv,
463*4882a593Smuzhiyun 			      struct v4l2_input *i)
464*4882a593Smuzhiyun {
465*4882a593Smuzhiyun 	if (i->index)
466*4882a593Smuzhiyun 		return -EINVAL;
467*4882a593Smuzhiyun 
468*4882a593Smuzhiyun 	i->type = V4L2_INPUT_TYPE_CAMERA;
469*4882a593Smuzhiyun 	i->std = CX25821_NORMS;
470*4882a593Smuzhiyun 	strscpy(i->name, "Composite", sizeof(i->name));
471*4882a593Smuzhiyun 	return 0;
472*4882a593Smuzhiyun }
473*4882a593Smuzhiyun 
cx25821_vidioc_g_input(struct file * file,void * priv,unsigned int * i)474*4882a593Smuzhiyun static int cx25821_vidioc_g_input(struct file *file, void *priv, unsigned int *i)
475*4882a593Smuzhiyun {
476*4882a593Smuzhiyun 	*i = 0;
477*4882a593Smuzhiyun 	return 0;
478*4882a593Smuzhiyun }
479*4882a593Smuzhiyun 
cx25821_vidioc_s_input(struct file * file,void * priv,unsigned int i)480*4882a593Smuzhiyun static int cx25821_vidioc_s_input(struct file *file, void *priv, unsigned int i)
481*4882a593Smuzhiyun {
482*4882a593Smuzhiyun 	return i ? -EINVAL : 0;
483*4882a593Smuzhiyun }
484*4882a593Smuzhiyun 
cx25821_s_ctrl(struct v4l2_ctrl * ctrl)485*4882a593Smuzhiyun static int cx25821_s_ctrl(struct v4l2_ctrl *ctrl)
486*4882a593Smuzhiyun {
487*4882a593Smuzhiyun 	struct cx25821_channel *chan =
488*4882a593Smuzhiyun 		container_of(ctrl->handler, struct cx25821_channel, hdl);
489*4882a593Smuzhiyun 	struct cx25821_dev *dev = chan->dev;
490*4882a593Smuzhiyun 
491*4882a593Smuzhiyun 	switch (ctrl->id) {
492*4882a593Smuzhiyun 	case V4L2_CID_BRIGHTNESS:
493*4882a593Smuzhiyun 		medusa_set_brightness(dev, ctrl->val, chan->id);
494*4882a593Smuzhiyun 		break;
495*4882a593Smuzhiyun 	case V4L2_CID_HUE:
496*4882a593Smuzhiyun 		medusa_set_hue(dev, ctrl->val, chan->id);
497*4882a593Smuzhiyun 		break;
498*4882a593Smuzhiyun 	case V4L2_CID_CONTRAST:
499*4882a593Smuzhiyun 		medusa_set_contrast(dev, ctrl->val, chan->id);
500*4882a593Smuzhiyun 		break;
501*4882a593Smuzhiyun 	case V4L2_CID_SATURATION:
502*4882a593Smuzhiyun 		medusa_set_saturation(dev, ctrl->val, chan->id);
503*4882a593Smuzhiyun 		break;
504*4882a593Smuzhiyun 	default:
505*4882a593Smuzhiyun 		return -EINVAL;
506*4882a593Smuzhiyun 	}
507*4882a593Smuzhiyun 	return 0;
508*4882a593Smuzhiyun }
509*4882a593Smuzhiyun 
cx25821_vidioc_enum_output(struct file * file,void * priv,struct v4l2_output * o)510*4882a593Smuzhiyun static int cx25821_vidioc_enum_output(struct file *file, void *priv,
511*4882a593Smuzhiyun 			      struct v4l2_output *o)
512*4882a593Smuzhiyun {
513*4882a593Smuzhiyun 	if (o->index)
514*4882a593Smuzhiyun 		return -EINVAL;
515*4882a593Smuzhiyun 
516*4882a593Smuzhiyun 	o->type = V4L2_INPUT_TYPE_CAMERA;
517*4882a593Smuzhiyun 	o->std = CX25821_NORMS;
518*4882a593Smuzhiyun 	strscpy(o->name, "Composite", sizeof(o->name));
519*4882a593Smuzhiyun 	return 0;
520*4882a593Smuzhiyun }
521*4882a593Smuzhiyun 
cx25821_vidioc_g_output(struct file * file,void * priv,unsigned int * o)522*4882a593Smuzhiyun static int cx25821_vidioc_g_output(struct file *file, void *priv, unsigned int *o)
523*4882a593Smuzhiyun {
524*4882a593Smuzhiyun 	*o = 0;
525*4882a593Smuzhiyun 	return 0;
526*4882a593Smuzhiyun }
527*4882a593Smuzhiyun 
cx25821_vidioc_s_output(struct file * file,void * priv,unsigned int o)528*4882a593Smuzhiyun static int cx25821_vidioc_s_output(struct file *file, void *priv, unsigned int o)
529*4882a593Smuzhiyun {
530*4882a593Smuzhiyun 	return o ? -EINVAL : 0;
531*4882a593Smuzhiyun }
532*4882a593Smuzhiyun 
cx25821_vidioc_try_fmt_vid_out(struct file * file,void * priv,struct v4l2_format * f)533*4882a593Smuzhiyun static int cx25821_vidioc_try_fmt_vid_out(struct file *file, void *priv,
534*4882a593Smuzhiyun 				   struct v4l2_format *f)
535*4882a593Smuzhiyun {
536*4882a593Smuzhiyun 	struct cx25821_channel *chan = video_drvdata(file);
537*4882a593Smuzhiyun 	struct cx25821_dev *dev = chan->dev;
538*4882a593Smuzhiyun 	const struct cx25821_fmt *fmt;
539*4882a593Smuzhiyun 
540*4882a593Smuzhiyun 	fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat);
541*4882a593Smuzhiyun 	if (NULL == fmt)
542*4882a593Smuzhiyun 		return -EINVAL;
543*4882a593Smuzhiyun 	f->fmt.pix.width = 720;
544*4882a593Smuzhiyun 	f->fmt.pix.height = (dev->tvnorm & V4L2_STD_625_50) ? 576 : 480;
545*4882a593Smuzhiyun 	f->fmt.pix.field = V4L2_FIELD_INTERLACED;
546*4882a593Smuzhiyun 	f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
547*4882a593Smuzhiyun 	f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
548*4882a593Smuzhiyun 	f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
549*4882a593Smuzhiyun 	return 0;
550*4882a593Smuzhiyun }
551*4882a593Smuzhiyun 
vidioc_s_fmt_vid_out(struct file * file,void * priv,struct v4l2_format * f)552*4882a593Smuzhiyun static int vidioc_s_fmt_vid_out(struct file *file, void *priv,
553*4882a593Smuzhiyun 				struct v4l2_format *f)
554*4882a593Smuzhiyun {
555*4882a593Smuzhiyun 	struct cx25821_channel *chan = video_drvdata(file);
556*4882a593Smuzhiyun 	int err;
557*4882a593Smuzhiyun 
558*4882a593Smuzhiyun 	err = cx25821_vidioc_try_fmt_vid_out(file, priv, f);
559*4882a593Smuzhiyun 
560*4882a593Smuzhiyun 	if (0 != err)
561*4882a593Smuzhiyun 		return err;
562*4882a593Smuzhiyun 
563*4882a593Smuzhiyun 	chan->fmt = cx25821_format_by_fourcc(f->fmt.pix.pixelformat);
564*4882a593Smuzhiyun 	chan->field = f->fmt.pix.field;
565*4882a593Smuzhiyun 	chan->width = f->fmt.pix.width;
566*4882a593Smuzhiyun 	chan->height = f->fmt.pix.height;
567*4882a593Smuzhiyun 	if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P)
568*4882a593Smuzhiyun 		chan->pixel_formats = PIXEL_FRMT_411;
569*4882a593Smuzhiyun 	else
570*4882a593Smuzhiyun 		chan->pixel_formats = PIXEL_FRMT_422;
571*4882a593Smuzhiyun 	return 0;
572*4882a593Smuzhiyun }
573*4882a593Smuzhiyun 
574*4882a593Smuzhiyun static const struct v4l2_ctrl_ops cx25821_ctrl_ops = {
575*4882a593Smuzhiyun 	.s_ctrl = cx25821_s_ctrl,
576*4882a593Smuzhiyun };
577*4882a593Smuzhiyun 
578*4882a593Smuzhiyun static const struct v4l2_file_operations video_fops = {
579*4882a593Smuzhiyun 	.owner = THIS_MODULE,
580*4882a593Smuzhiyun 	.open = v4l2_fh_open,
581*4882a593Smuzhiyun 	.release        = vb2_fop_release,
582*4882a593Smuzhiyun 	.read           = vb2_fop_read,
583*4882a593Smuzhiyun 	.poll		= vb2_fop_poll,
584*4882a593Smuzhiyun 	.unlocked_ioctl = video_ioctl2,
585*4882a593Smuzhiyun 	.mmap           = vb2_fop_mmap,
586*4882a593Smuzhiyun };
587*4882a593Smuzhiyun 
588*4882a593Smuzhiyun static const struct v4l2_ioctl_ops video_ioctl_ops = {
589*4882a593Smuzhiyun 	.vidioc_querycap = cx25821_vidioc_querycap,
590*4882a593Smuzhiyun 	.vidioc_enum_fmt_vid_cap = cx25821_vidioc_enum_fmt_vid_cap,
591*4882a593Smuzhiyun 	.vidioc_g_fmt_vid_cap = cx25821_vidioc_g_fmt_vid_cap,
592*4882a593Smuzhiyun 	.vidioc_try_fmt_vid_cap = cx25821_vidioc_try_fmt_vid_cap,
593*4882a593Smuzhiyun 	.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
594*4882a593Smuzhiyun 	.vidioc_reqbufs       = vb2_ioctl_reqbufs,
595*4882a593Smuzhiyun 	.vidioc_prepare_buf   = vb2_ioctl_prepare_buf,
596*4882a593Smuzhiyun 	.vidioc_create_bufs   = vb2_ioctl_create_bufs,
597*4882a593Smuzhiyun 	.vidioc_querybuf      = vb2_ioctl_querybuf,
598*4882a593Smuzhiyun 	.vidioc_qbuf          = vb2_ioctl_qbuf,
599*4882a593Smuzhiyun 	.vidioc_dqbuf         = vb2_ioctl_dqbuf,
600*4882a593Smuzhiyun 	.vidioc_streamon      = vb2_ioctl_streamon,
601*4882a593Smuzhiyun 	.vidioc_streamoff     = vb2_ioctl_streamoff,
602*4882a593Smuzhiyun 	.vidioc_g_std = cx25821_vidioc_g_std,
603*4882a593Smuzhiyun 	.vidioc_s_std = cx25821_vidioc_s_std,
604*4882a593Smuzhiyun 	.vidioc_enum_input = cx25821_vidioc_enum_input,
605*4882a593Smuzhiyun 	.vidioc_g_input = cx25821_vidioc_g_input,
606*4882a593Smuzhiyun 	.vidioc_s_input = cx25821_vidioc_s_input,
607*4882a593Smuzhiyun 	.vidioc_log_status = vidioc_log_status,
608*4882a593Smuzhiyun 	.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
609*4882a593Smuzhiyun 	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
610*4882a593Smuzhiyun };
611*4882a593Smuzhiyun 
612*4882a593Smuzhiyun static const struct video_device cx25821_video_device = {
613*4882a593Smuzhiyun 	.name = "cx25821-video",
614*4882a593Smuzhiyun 	.fops = &video_fops,
615*4882a593Smuzhiyun 	.release = video_device_release_empty,
616*4882a593Smuzhiyun 	.minor = -1,
617*4882a593Smuzhiyun 	.ioctl_ops = &video_ioctl_ops,
618*4882a593Smuzhiyun 	.tvnorms = CX25821_NORMS,
619*4882a593Smuzhiyun 	.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
620*4882a593Smuzhiyun 		       V4L2_CAP_STREAMING,
621*4882a593Smuzhiyun };
622*4882a593Smuzhiyun 
623*4882a593Smuzhiyun static const struct v4l2_file_operations video_out_fops = {
624*4882a593Smuzhiyun 	.owner = THIS_MODULE,
625*4882a593Smuzhiyun 	.open = v4l2_fh_open,
626*4882a593Smuzhiyun 	.release        = vb2_fop_release,
627*4882a593Smuzhiyun 	.write          = vb2_fop_write,
628*4882a593Smuzhiyun 	.poll		= vb2_fop_poll,
629*4882a593Smuzhiyun 	.unlocked_ioctl = video_ioctl2,
630*4882a593Smuzhiyun 	.mmap           = vb2_fop_mmap,
631*4882a593Smuzhiyun };
632*4882a593Smuzhiyun 
633*4882a593Smuzhiyun static const struct v4l2_ioctl_ops video_out_ioctl_ops = {
634*4882a593Smuzhiyun 	.vidioc_querycap = cx25821_vidioc_querycap,
635*4882a593Smuzhiyun 	.vidioc_enum_fmt_vid_out = cx25821_vidioc_enum_fmt_vid_cap,
636*4882a593Smuzhiyun 	.vidioc_g_fmt_vid_out = cx25821_vidioc_g_fmt_vid_cap,
637*4882a593Smuzhiyun 	.vidioc_try_fmt_vid_out = cx25821_vidioc_try_fmt_vid_out,
638*4882a593Smuzhiyun 	.vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out,
639*4882a593Smuzhiyun 	.vidioc_g_std = cx25821_vidioc_g_std,
640*4882a593Smuzhiyun 	.vidioc_s_std = cx25821_vidioc_s_std,
641*4882a593Smuzhiyun 	.vidioc_enum_output = cx25821_vidioc_enum_output,
642*4882a593Smuzhiyun 	.vidioc_g_output = cx25821_vidioc_g_output,
643*4882a593Smuzhiyun 	.vidioc_s_output = cx25821_vidioc_s_output,
644*4882a593Smuzhiyun 	.vidioc_log_status = vidioc_log_status,
645*4882a593Smuzhiyun };
646*4882a593Smuzhiyun 
647*4882a593Smuzhiyun static const struct video_device cx25821_video_out_device = {
648*4882a593Smuzhiyun 	.name = "cx25821-video",
649*4882a593Smuzhiyun 	.fops = &video_out_fops,
650*4882a593Smuzhiyun 	.release = video_device_release_empty,
651*4882a593Smuzhiyun 	.minor = -1,
652*4882a593Smuzhiyun 	.ioctl_ops = &video_out_ioctl_ops,
653*4882a593Smuzhiyun 	.tvnorms = CX25821_NORMS,
654*4882a593Smuzhiyun 	.device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_READWRITE,
655*4882a593Smuzhiyun };
656*4882a593Smuzhiyun 
cx25821_video_unregister(struct cx25821_dev * dev,int chan_num)657*4882a593Smuzhiyun void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num)
658*4882a593Smuzhiyun {
659*4882a593Smuzhiyun 	cx_clear(PCI_INT_MSK, 1);
660*4882a593Smuzhiyun 
661*4882a593Smuzhiyun 	if (video_is_registered(&dev->channels[chan_num].vdev)) {
662*4882a593Smuzhiyun 		video_unregister_device(&dev->channels[chan_num].vdev);
663*4882a593Smuzhiyun 		v4l2_ctrl_handler_free(&dev->channels[chan_num].hdl);
664*4882a593Smuzhiyun 	}
665*4882a593Smuzhiyun }
666*4882a593Smuzhiyun 
cx25821_video_register(struct cx25821_dev * dev)667*4882a593Smuzhiyun int cx25821_video_register(struct cx25821_dev *dev)
668*4882a593Smuzhiyun {
669*4882a593Smuzhiyun 	int err;
670*4882a593Smuzhiyun 	int i;
671*4882a593Smuzhiyun 
672*4882a593Smuzhiyun 	/* initial device configuration */
673*4882a593Smuzhiyun 	dev->tvnorm = V4L2_STD_NTSC_M;
674*4882a593Smuzhiyun 
675*4882a593Smuzhiyun 	spin_lock_init(&dev->slock);
676*4882a593Smuzhiyun 
677*4882a593Smuzhiyun 	for (i = 0; i < MAX_VID_CAP_CHANNEL_NUM - 1; ++i) {
678*4882a593Smuzhiyun 		struct cx25821_channel *chan = &dev->channels[i];
679*4882a593Smuzhiyun 		struct video_device *vdev = &chan->vdev;
680*4882a593Smuzhiyun 		struct v4l2_ctrl_handler *hdl = &chan->hdl;
681*4882a593Smuzhiyun 		struct vb2_queue *q;
682*4882a593Smuzhiyun 		bool is_output = i > SRAM_CH08;
683*4882a593Smuzhiyun 
684*4882a593Smuzhiyun 		if (i == SRAM_CH08) /* audio channel */
685*4882a593Smuzhiyun 			continue;
686*4882a593Smuzhiyun 
687*4882a593Smuzhiyun 		if (!is_output) {
688*4882a593Smuzhiyun 			v4l2_ctrl_handler_init(hdl, 4);
689*4882a593Smuzhiyun 			v4l2_ctrl_new_std(hdl, &cx25821_ctrl_ops,
690*4882a593Smuzhiyun 					V4L2_CID_BRIGHTNESS, 0, 10000, 1, 6200);
691*4882a593Smuzhiyun 			v4l2_ctrl_new_std(hdl, &cx25821_ctrl_ops,
692*4882a593Smuzhiyun 					V4L2_CID_CONTRAST, 0, 10000, 1, 5000);
693*4882a593Smuzhiyun 			v4l2_ctrl_new_std(hdl, &cx25821_ctrl_ops,
694*4882a593Smuzhiyun 					V4L2_CID_SATURATION, 0, 10000, 1, 5000);
695*4882a593Smuzhiyun 			v4l2_ctrl_new_std(hdl, &cx25821_ctrl_ops,
696*4882a593Smuzhiyun 					V4L2_CID_HUE, 0, 10000, 1, 5000);
697*4882a593Smuzhiyun 			if (hdl->error) {
698*4882a593Smuzhiyun 				err = hdl->error;
699*4882a593Smuzhiyun 				goto fail_unreg;
700*4882a593Smuzhiyun 			}
701*4882a593Smuzhiyun 			err = v4l2_ctrl_handler_setup(hdl);
702*4882a593Smuzhiyun 			if (err)
703*4882a593Smuzhiyun 				goto fail_unreg;
704*4882a593Smuzhiyun 		} else {
705*4882a593Smuzhiyun 			chan->out = &dev->vid_out_data[i - SRAM_CH09];
706*4882a593Smuzhiyun 			chan->out->chan = chan;
707*4882a593Smuzhiyun 		}
708*4882a593Smuzhiyun 
709*4882a593Smuzhiyun 		chan->sram_channels = &cx25821_sram_channels[i];
710*4882a593Smuzhiyun 		chan->width = 720;
711*4882a593Smuzhiyun 		chan->field = V4L2_FIELD_INTERLACED;
712*4882a593Smuzhiyun 		if (dev->tvnorm & V4L2_STD_625_50)
713*4882a593Smuzhiyun 			chan->height = 576;
714*4882a593Smuzhiyun 		else
715*4882a593Smuzhiyun 			chan->height = 480;
716*4882a593Smuzhiyun 
717*4882a593Smuzhiyun 		if (chan->pixel_formats == PIXEL_FRMT_411)
718*4882a593Smuzhiyun 			chan->fmt = cx25821_format_by_fourcc(V4L2_PIX_FMT_Y41P);
719*4882a593Smuzhiyun 		else
720*4882a593Smuzhiyun 			chan->fmt = cx25821_format_by_fourcc(V4L2_PIX_FMT_YUYV);
721*4882a593Smuzhiyun 
722*4882a593Smuzhiyun 		cx_write(chan->sram_channels->int_stat, 0xffffffff);
723*4882a593Smuzhiyun 
724*4882a593Smuzhiyun 		INIT_LIST_HEAD(&chan->dma_vidq.active);
725*4882a593Smuzhiyun 
726*4882a593Smuzhiyun 		q = &chan->vidq;
727*4882a593Smuzhiyun 
728*4882a593Smuzhiyun 		q->type = is_output ? V4L2_BUF_TYPE_VIDEO_OUTPUT :
729*4882a593Smuzhiyun 				      V4L2_BUF_TYPE_VIDEO_CAPTURE;
730*4882a593Smuzhiyun 		q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
731*4882a593Smuzhiyun 		q->io_modes |= is_output ? VB2_WRITE : VB2_READ;
732*4882a593Smuzhiyun 		q->gfp_flags = GFP_DMA32;
733*4882a593Smuzhiyun 		q->min_buffers_needed = 2;
734*4882a593Smuzhiyun 		q->drv_priv = chan;
735*4882a593Smuzhiyun 		q->buf_struct_size = sizeof(struct cx25821_buffer);
736*4882a593Smuzhiyun 		q->ops = &cx25821_video_qops;
737*4882a593Smuzhiyun 		q->mem_ops = &vb2_dma_sg_memops;
738*4882a593Smuzhiyun 		q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
739*4882a593Smuzhiyun 		q->lock = &dev->lock;
740*4882a593Smuzhiyun 		q->dev = &dev->pci->dev;
741*4882a593Smuzhiyun 
742*4882a593Smuzhiyun 		if (!is_output) {
743*4882a593Smuzhiyun 			err = vb2_queue_init(q);
744*4882a593Smuzhiyun 			if (err < 0)
745*4882a593Smuzhiyun 				goto fail_unreg;
746*4882a593Smuzhiyun 		}
747*4882a593Smuzhiyun 
748*4882a593Smuzhiyun 		/* register v4l devices */
749*4882a593Smuzhiyun 		*vdev = is_output ? cx25821_video_out_device : cx25821_video_device;
750*4882a593Smuzhiyun 		vdev->v4l2_dev = &dev->v4l2_dev;
751*4882a593Smuzhiyun 		if (!is_output)
752*4882a593Smuzhiyun 			vdev->ctrl_handler = hdl;
753*4882a593Smuzhiyun 		else
754*4882a593Smuzhiyun 			vdev->vfl_dir = VFL_DIR_TX;
755*4882a593Smuzhiyun 		vdev->lock = &dev->lock;
756*4882a593Smuzhiyun 		vdev->queue = q;
757*4882a593Smuzhiyun 		snprintf(vdev->name, sizeof(vdev->name), "%s #%d", dev->name, i);
758*4882a593Smuzhiyun 		video_set_drvdata(vdev, chan);
759*4882a593Smuzhiyun 
760*4882a593Smuzhiyun 		err = video_register_device(vdev, VFL_TYPE_VIDEO,
761*4882a593Smuzhiyun 					    video_nr[dev->nr]);
762*4882a593Smuzhiyun 
763*4882a593Smuzhiyun 		if (err < 0)
764*4882a593Smuzhiyun 			goto fail_unreg;
765*4882a593Smuzhiyun 	}
766*4882a593Smuzhiyun 
767*4882a593Smuzhiyun 	/* set PCI interrupt */
768*4882a593Smuzhiyun 	cx_set(PCI_INT_MSK, 0xff);
769*4882a593Smuzhiyun 
770*4882a593Smuzhiyun 	return 0;
771*4882a593Smuzhiyun 
772*4882a593Smuzhiyun fail_unreg:
773*4882a593Smuzhiyun 	while (i >= 0)
774*4882a593Smuzhiyun 		cx25821_video_unregister(dev, i--);
775*4882a593Smuzhiyun 	return err;
776*4882a593Smuzhiyun }
777