1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2b285192aSMauro Carvalho Chehab /*
3b285192aSMauro Carvalho Chehab  *  Driver for the NXP SAA7164 PCIe bridge
4b285192aSMauro Carvalho Chehab  *
563a412ecSSteven Toth  *  Copyright (c) 2010-2015 Steven Toth <stoth@kernellabs.com>
6b285192aSMauro Carvalho Chehab  */
7b285192aSMauro Carvalho Chehab 
8b285192aSMauro Carvalho Chehab #include "saa7164.h"
9b285192aSMauro Carvalho Chehab 
10b285192aSMauro Carvalho Chehab #define ENCODER_MAX_BITRATE 6500000
11b285192aSMauro Carvalho Chehab #define ENCODER_MIN_BITRATE 1000000
12b285192aSMauro Carvalho Chehab #define ENCODER_DEF_BITRATE 5000000
13b285192aSMauro Carvalho Chehab 
14031d2297SHans Verkuil /*
15031d2297SHans Verkuil  * This is a dummy non-zero value for the sizeimage field of v4l2_pix_format.
16031d2297SHans Verkuil  * It is not actually used for anything since this driver does not support
17031d2297SHans Verkuil  * stream I/O, only read(), and because this driver produces an MPEG stream
18031d2297SHans Verkuil  * and not discrete frames. But the V4L2 spec doesn't allow for this value
19031d2297SHans Verkuil  * to be 0, so set it to 0x10000 instead.
20031d2297SHans Verkuil  *
21031d2297SHans Verkuil  * If we ever change this driver to support stream I/O, then this field
22031d2297SHans Verkuil  * will be the size of the streaming buffers.
23031d2297SHans Verkuil  */
24031d2297SHans Verkuil #define SAA7164_SIZEIMAGE (0x10000)
25031d2297SHans Verkuil 
26b285192aSMauro Carvalho Chehab static struct saa7164_tvnorm saa7164_tvnorms[] = {
27b285192aSMauro Carvalho Chehab 	{
28b285192aSMauro Carvalho Chehab 		.name      = "NTSC-M",
29b285192aSMauro Carvalho Chehab 		.id        = V4L2_STD_NTSC_M,
30b285192aSMauro Carvalho Chehab 	}, {
31b285192aSMauro Carvalho Chehab 		.name      = "NTSC-JP",
32b285192aSMauro Carvalho Chehab 		.id        = V4L2_STD_NTSC_M_JP,
33b285192aSMauro Carvalho Chehab 	}
34b285192aSMauro Carvalho Chehab };
35b285192aSMauro Carvalho Chehab 
36b285192aSMauro Carvalho Chehab /* Take the encoder configuration form the port struct and
37b285192aSMauro Carvalho Chehab  * flush it to the hardware.
38b285192aSMauro Carvalho Chehab  */
saa7164_encoder_configure(struct saa7164_port * port)39b285192aSMauro Carvalho Chehab static void saa7164_encoder_configure(struct saa7164_port *port)
40b285192aSMauro Carvalho Chehab {
41b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
42b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s()\n", __func__);
43b285192aSMauro Carvalho Chehab 
44b285192aSMauro Carvalho Chehab 	port->encoder_params.width = port->width;
45b285192aSMauro Carvalho Chehab 	port->encoder_params.height = port->height;
46b285192aSMauro Carvalho Chehab 	port->encoder_params.is_50hz =
47b285192aSMauro Carvalho Chehab 		(port->encodernorm.id & V4L2_STD_625_50) != 0;
48b285192aSMauro Carvalho Chehab 
49b285192aSMauro Carvalho Chehab 	/* Set up the DIF (enable it) for analog mode by default */
50b285192aSMauro Carvalho Chehab 	saa7164_api_initialize_dif(port);
51b285192aSMauro Carvalho Chehab 
52b285192aSMauro Carvalho Chehab 	/* Configure the correct video standard */
53b285192aSMauro Carvalho Chehab 	saa7164_api_configure_dif(port, port->encodernorm.id);
54b285192aSMauro Carvalho Chehab 
55b285192aSMauro Carvalho Chehab 	/* Ensure the audio decoder is correct configured */
56b285192aSMauro Carvalho Chehab 	saa7164_api_set_audio_std(port);
57b285192aSMauro Carvalho Chehab }
58b285192aSMauro Carvalho Chehab 
saa7164_encoder_buffers_dealloc(struct saa7164_port * port)59b285192aSMauro Carvalho Chehab static int saa7164_encoder_buffers_dealloc(struct saa7164_port *port)
60b285192aSMauro Carvalho Chehab {
61b285192aSMauro Carvalho Chehab 	struct list_head *c, *n, *p, *q, *l, *v;
62b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
63b285192aSMauro Carvalho Chehab 	struct saa7164_buffer *buf;
64b285192aSMauro Carvalho Chehab 	struct saa7164_user_buffer *ubuf;
65b285192aSMauro Carvalho Chehab 
66b285192aSMauro Carvalho Chehab 	/* Remove any allocated buffers */
67b285192aSMauro Carvalho Chehab 	mutex_lock(&port->dmaqueue_lock);
68b285192aSMauro Carvalho Chehab 
69b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s(port=%d) dmaqueue\n", __func__, port->nr);
70b285192aSMauro Carvalho Chehab 	list_for_each_safe(c, n, &port->dmaqueue.list) {
71b285192aSMauro Carvalho Chehab 		buf = list_entry(c, struct saa7164_buffer, list);
72b285192aSMauro Carvalho Chehab 		list_del(c);
73b285192aSMauro Carvalho Chehab 		saa7164_buffer_dealloc(buf);
74b285192aSMauro Carvalho Chehab 	}
75b285192aSMauro Carvalho Chehab 
76b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s(port=%d) used\n", __func__, port->nr);
77b285192aSMauro Carvalho Chehab 	list_for_each_safe(p, q, &port->list_buf_used.list) {
78b285192aSMauro Carvalho Chehab 		ubuf = list_entry(p, struct saa7164_user_buffer, list);
79b285192aSMauro Carvalho Chehab 		list_del(p);
80b285192aSMauro Carvalho Chehab 		saa7164_buffer_dealloc_user(ubuf);
81b285192aSMauro Carvalho Chehab 	}
82b285192aSMauro Carvalho Chehab 
83b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s(port=%d) free\n", __func__, port->nr);
84b285192aSMauro Carvalho Chehab 	list_for_each_safe(l, v, &port->list_buf_free.list) {
85b285192aSMauro Carvalho Chehab 		ubuf = list_entry(l, struct saa7164_user_buffer, list);
86b285192aSMauro Carvalho Chehab 		list_del(l);
87b285192aSMauro Carvalho Chehab 		saa7164_buffer_dealloc_user(ubuf);
88b285192aSMauro Carvalho Chehab 	}
89b285192aSMauro Carvalho Chehab 
90b285192aSMauro Carvalho Chehab 	mutex_unlock(&port->dmaqueue_lock);
91b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s(port=%d) done\n", __func__, port->nr);
92b285192aSMauro Carvalho Chehab 
93b285192aSMauro Carvalho Chehab 	return 0;
94b285192aSMauro Carvalho Chehab }
95b285192aSMauro Carvalho Chehab 
96b285192aSMauro Carvalho Chehab /* Dynamic buffer switch at encoder start time */
saa7164_encoder_buffers_alloc(struct saa7164_port * port)97b285192aSMauro Carvalho Chehab static int saa7164_encoder_buffers_alloc(struct saa7164_port *port)
98b285192aSMauro Carvalho Chehab {
99b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
100b285192aSMauro Carvalho Chehab 	struct saa7164_buffer *buf;
101b285192aSMauro Carvalho Chehab 	struct saa7164_user_buffer *ubuf;
102b285192aSMauro Carvalho Chehab 	struct tmHWStreamParameters *params = &port->hw_streamingparams;
103b285192aSMauro Carvalho Chehab 	int result = -ENODEV, i;
104b285192aSMauro Carvalho Chehab 	int len = 0;
105b285192aSMauro Carvalho Chehab 
106b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s()\n", __func__);
107b285192aSMauro Carvalho Chehab 
108b285192aSMauro Carvalho Chehab 	if (port->encoder_params.stream_type ==
109b285192aSMauro Carvalho Chehab 		V4L2_MPEG_STREAM_TYPE_MPEG2_PS) {
110b285192aSMauro Carvalho Chehab 		dprintk(DBGLVL_ENC,
111b285192aSMauro Carvalho Chehab 			"%s() type=V4L2_MPEG_STREAM_TYPE_MPEG2_PS\n",
112b285192aSMauro Carvalho Chehab 			__func__);
113b285192aSMauro Carvalho Chehab 		params->samplesperline = 128;
114b285192aSMauro Carvalho Chehab 		params->numberoflines = 256;
115b285192aSMauro Carvalho Chehab 		params->pitch = 128;
116b285192aSMauro Carvalho Chehab 		params->numpagetables = 2 +
117b285192aSMauro Carvalho Chehab 			((SAA7164_PS_NUMBER_OF_LINES * 128) / PAGE_SIZE);
118b285192aSMauro Carvalho Chehab 	} else
119b285192aSMauro Carvalho Chehab 	if (port->encoder_params.stream_type ==
120b285192aSMauro Carvalho Chehab 		V4L2_MPEG_STREAM_TYPE_MPEG2_TS) {
121b285192aSMauro Carvalho Chehab 		dprintk(DBGLVL_ENC,
122b285192aSMauro Carvalho Chehab 			"%s() type=V4L2_MPEG_STREAM_TYPE_MPEG2_TS\n",
123b285192aSMauro Carvalho Chehab 			__func__);
124b285192aSMauro Carvalho Chehab 		params->samplesperline = 188;
125b285192aSMauro Carvalho Chehab 		params->numberoflines = 312;
126b285192aSMauro Carvalho Chehab 		params->pitch = 188;
127b285192aSMauro Carvalho Chehab 		params->numpagetables = 2 +
128b285192aSMauro Carvalho Chehab 			((SAA7164_TS_NUMBER_OF_LINES * 188) / PAGE_SIZE);
129b285192aSMauro Carvalho Chehab 	} else
130b285192aSMauro Carvalho Chehab 		BUG();
131b285192aSMauro Carvalho Chehab 
132b285192aSMauro Carvalho Chehab 	/* Init and establish defaults */
133b285192aSMauro Carvalho Chehab 	params->bitspersample = 8;
134b285192aSMauro Carvalho Chehab 	params->linethreshold = 0;
135b285192aSMauro Carvalho Chehab 	params->pagetablelistvirt = NULL;
136b285192aSMauro Carvalho Chehab 	params->pagetablelistphys = NULL;
137b285192aSMauro Carvalho Chehab 	params->numpagetableentries = port->hwcfg.buffercount;
138b285192aSMauro Carvalho Chehab 
139b285192aSMauro Carvalho Chehab 	/* Allocate the PCI resources, buffers (hard) */
140b285192aSMauro Carvalho Chehab 	for (i = 0; i < port->hwcfg.buffercount; i++) {
141b285192aSMauro Carvalho Chehab 		buf = saa7164_buffer_alloc(port,
142b285192aSMauro Carvalho Chehab 			params->numberoflines *
143b285192aSMauro Carvalho Chehab 			params->pitch);
144b285192aSMauro Carvalho Chehab 
145b285192aSMauro Carvalho Chehab 		if (!buf) {
14624f711c1SMauro Carvalho Chehab 			printk(KERN_ERR "%s() failed (errno = %d), unable to allocate buffer\n",
147b285192aSMauro Carvalho Chehab 				__func__, result);
148b285192aSMauro Carvalho Chehab 			result = -ENOMEM;
149b285192aSMauro Carvalho Chehab 			goto failed;
150b285192aSMauro Carvalho Chehab 		} else {
151b285192aSMauro Carvalho Chehab 
152b285192aSMauro Carvalho Chehab 			mutex_lock(&port->dmaqueue_lock);
153b285192aSMauro Carvalho Chehab 			list_add_tail(&buf->list, &port->dmaqueue.list);
154b285192aSMauro Carvalho Chehab 			mutex_unlock(&port->dmaqueue_lock);
155b285192aSMauro Carvalho Chehab 
156b285192aSMauro Carvalho Chehab 		}
157b285192aSMauro Carvalho Chehab 	}
158b285192aSMauro Carvalho Chehab 
159b285192aSMauro Carvalho Chehab 	/* Allocate some kernel buffers for copying
160b285192aSMauro Carvalho Chehab 	 * to userpsace.
161b285192aSMauro Carvalho Chehab 	 */
162b285192aSMauro Carvalho Chehab 	len = params->numberoflines * params->pitch;
163b285192aSMauro Carvalho Chehab 
164b285192aSMauro Carvalho Chehab 	if (encoder_buffers < 16)
165b285192aSMauro Carvalho Chehab 		encoder_buffers = 16;
166b285192aSMauro Carvalho Chehab 	if (encoder_buffers > 512)
167b285192aSMauro Carvalho Chehab 		encoder_buffers = 512;
168b285192aSMauro Carvalho Chehab 
169b285192aSMauro Carvalho Chehab 	for (i = 0; i < encoder_buffers; i++) {
170b285192aSMauro Carvalho Chehab 
171b285192aSMauro Carvalho Chehab 		ubuf = saa7164_buffer_alloc_user(dev, len);
172b285192aSMauro Carvalho Chehab 		if (ubuf) {
173b285192aSMauro Carvalho Chehab 			mutex_lock(&port->dmaqueue_lock);
174b285192aSMauro Carvalho Chehab 			list_add_tail(&ubuf->list, &port->list_buf_free.list);
175b285192aSMauro Carvalho Chehab 			mutex_unlock(&port->dmaqueue_lock);
176b285192aSMauro Carvalho Chehab 		}
177b285192aSMauro Carvalho Chehab 
178b285192aSMauro Carvalho Chehab 	}
179b285192aSMauro Carvalho Chehab 
180b285192aSMauro Carvalho Chehab 	result = 0;
181b285192aSMauro Carvalho Chehab 
182b285192aSMauro Carvalho Chehab failed:
183b285192aSMauro Carvalho Chehab 	return result;
184b285192aSMauro Carvalho Chehab }
185b285192aSMauro Carvalho Chehab 
saa7164_encoder_initialize(struct saa7164_port * port)186b285192aSMauro Carvalho Chehab static int saa7164_encoder_initialize(struct saa7164_port *port)
187b285192aSMauro Carvalho Chehab {
188b285192aSMauro Carvalho Chehab 	saa7164_encoder_configure(port);
189b285192aSMauro Carvalho Chehab 	return 0;
190b285192aSMauro Carvalho Chehab }
191b285192aSMauro Carvalho Chehab 
192b285192aSMauro Carvalho Chehab /* -- V4L2 --------------------------------------------------------- */
saa7164_s_std(struct saa7164_port * port,v4l2_std_id id)193225b783bSHans Verkuil int saa7164_s_std(struct saa7164_port *port, v4l2_std_id id)
194b285192aSMauro Carvalho Chehab {
195b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
196b285192aSMauro Carvalho Chehab 	unsigned int i;
197b285192aSMauro Carvalho Chehab 
198314527acSHans Verkuil 	dprintk(DBGLVL_ENC, "%s(id=0x%x)\n", __func__, (u32)id);
199b285192aSMauro Carvalho Chehab 
200b285192aSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(saa7164_tvnorms); i++) {
201314527acSHans Verkuil 		if (id & saa7164_tvnorms[i].id)
202b285192aSMauro Carvalho Chehab 			break;
203b285192aSMauro Carvalho Chehab 	}
204b285192aSMauro Carvalho Chehab 	if (i == ARRAY_SIZE(saa7164_tvnorms))
205b285192aSMauro Carvalho Chehab 		return -EINVAL;
206b285192aSMauro Carvalho Chehab 
207b285192aSMauro Carvalho Chehab 	port->encodernorm = saa7164_tvnorms[i];
2088d2d41e9SHans Verkuil 	port->std = id;
209b285192aSMauro Carvalho Chehab 
210b285192aSMauro Carvalho Chehab 	/* Update the audio decoder while is not running in
211b285192aSMauro Carvalho Chehab 	 * auto detect mode.
212b285192aSMauro Carvalho Chehab 	 */
213b285192aSMauro Carvalho Chehab 	saa7164_api_set_audio_std(port);
214b285192aSMauro Carvalho Chehab 
215314527acSHans Verkuil 	dprintk(DBGLVL_ENC, "%s(id=0x%x) OK\n", __func__, (u32)id);
216b285192aSMauro Carvalho Chehab 
217b285192aSMauro Carvalho Chehab 	return 0;
218b285192aSMauro Carvalho Chehab }
219b285192aSMauro Carvalho Chehab 
vidioc_s_std(struct file * file,void * priv,v4l2_std_id id)220225b783bSHans Verkuil static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id)
2218d2d41e9SHans Verkuil {
2228d2d41e9SHans Verkuil 	struct saa7164_encoder_fh *fh = file->private_data;
2238d2d41e9SHans Verkuil 
224225b783bSHans Verkuil 	return saa7164_s_std(fh->port, id);
225225b783bSHans Verkuil }
226225b783bSHans Verkuil 
saa7164_g_std(struct saa7164_port * port,v4l2_std_id * id)227225b783bSHans Verkuil int saa7164_g_std(struct saa7164_port *port, v4l2_std_id *id)
228225b783bSHans Verkuil {
2298d2d41e9SHans Verkuil 	*id = port->std;
2308d2d41e9SHans Verkuil 	return 0;
2318d2d41e9SHans Verkuil }
2328d2d41e9SHans Verkuil 
vidioc_g_std(struct file * file,void * priv,v4l2_std_id * id)233225b783bSHans Verkuil static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
234225b783bSHans Verkuil {
235225b783bSHans Verkuil 	struct saa7164_encoder_fh *fh = file->private_data;
236225b783bSHans Verkuil 
237225b783bSHans Verkuil 	return saa7164_g_std(fh->port, id);
238225b783bSHans Verkuil }
239225b783bSHans Verkuil 
saa7164_enum_input(struct file * file,void * priv,struct v4l2_input * i)240225b783bSHans Verkuil int saa7164_enum_input(struct file *file, void *priv, struct v4l2_input *i)
241b285192aSMauro Carvalho Chehab {
2426b996126SHans Verkuil 	static const char * const inputs[] = {
2436b996126SHans Verkuil 		"tuner", "composite", "svideo", "aux",
2446b996126SHans Verkuil 		"composite 2", "svideo 2", "aux 2"
2456b996126SHans Verkuil 	};
246b285192aSMauro Carvalho Chehab 	int n;
247b285192aSMauro Carvalho Chehab 
248b285192aSMauro Carvalho Chehab 	if (i->index >= 7)
249b285192aSMauro Carvalho Chehab 		return -EINVAL;
250b285192aSMauro Carvalho Chehab 
251cc1e6315SMauro Carvalho Chehab 	strscpy(i->name, inputs[i->index], sizeof(i->name));
252b285192aSMauro Carvalho Chehab 
253b285192aSMauro Carvalho Chehab 	if (i->index == 0)
254b285192aSMauro Carvalho Chehab 		i->type = V4L2_INPUT_TYPE_TUNER;
255b285192aSMauro Carvalho Chehab 	else
256b285192aSMauro Carvalho Chehab 		i->type  = V4L2_INPUT_TYPE_CAMERA;
257b285192aSMauro Carvalho Chehab 
258b285192aSMauro Carvalho Chehab 	for (n = 0; n < ARRAY_SIZE(saa7164_tvnorms); n++)
259b285192aSMauro Carvalho Chehab 		i->std |= saa7164_tvnorms[n].id;
260b285192aSMauro Carvalho Chehab 
261b285192aSMauro Carvalho Chehab 	return 0;
262b285192aSMauro Carvalho Chehab }
263b285192aSMauro Carvalho Chehab 
saa7164_g_input(struct saa7164_port * port,unsigned int * i)264225b783bSHans Verkuil int saa7164_g_input(struct saa7164_port *port, unsigned int *i)
265b285192aSMauro Carvalho Chehab {
266b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
267b285192aSMauro Carvalho Chehab 
268b285192aSMauro Carvalho Chehab 	if (saa7164_api_get_videomux(port) != SAA_OK)
269b285192aSMauro Carvalho Chehab 		return -EIO;
270b285192aSMauro Carvalho Chehab 
271b285192aSMauro Carvalho Chehab 	*i = (port->mux_input - 1);
272b285192aSMauro Carvalho Chehab 
273b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s() input=%d\n", __func__, *i);
274b285192aSMauro Carvalho Chehab 
275b285192aSMauro Carvalho Chehab 	return 0;
276b285192aSMauro Carvalho Chehab }
277b285192aSMauro Carvalho Chehab 
vidioc_g_input(struct file * file,void * priv,unsigned int * i)278225b783bSHans Verkuil static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
279b285192aSMauro Carvalho Chehab {
280b285192aSMauro Carvalho Chehab 	struct saa7164_encoder_fh *fh = file->private_data;
281225b783bSHans Verkuil 
282225b783bSHans Verkuil 	return saa7164_g_input(fh->port, i);
283225b783bSHans Verkuil }
284225b783bSHans Verkuil 
saa7164_s_input(struct saa7164_port * port,unsigned int i)285225b783bSHans Verkuil int saa7164_s_input(struct saa7164_port *port, unsigned int i)
286225b783bSHans Verkuil {
287b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
288b285192aSMauro Carvalho Chehab 
289b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s() input=%d\n", __func__, i);
290b285192aSMauro Carvalho Chehab 
291b285192aSMauro Carvalho Chehab 	if (i >= 7)
292b285192aSMauro Carvalho Chehab 		return -EINVAL;
293b285192aSMauro Carvalho Chehab 
294b285192aSMauro Carvalho Chehab 	port->mux_input = i + 1;
295b285192aSMauro Carvalho Chehab 
296b285192aSMauro Carvalho Chehab 	if (saa7164_api_set_videomux(port) != SAA_OK)
297b285192aSMauro Carvalho Chehab 		return -EIO;
298b285192aSMauro Carvalho Chehab 
299b285192aSMauro Carvalho Chehab 	return 0;
300b285192aSMauro Carvalho Chehab }
301b285192aSMauro Carvalho Chehab 
vidioc_s_input(struct file * file,void * priv,unsigned int i)302225b783bSHans Verkuil static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
303225b783bSHans Verkuil {
304225b783bSHans Verkuil 	struct saa7164_encoder_fh *fh = file->private_data;
305225b783bSHans Verkuil 
306225b783bSHans Verkuil 	return saa7164_s_input(fh->port, i);
307225b783bSHans Verkuil }
308225b783bSHans Verkuil 
saa7164_g_tuner(struct file * file,void * priv,struct v4l2_tuner * t)309225b783bSHans Verkuil int saa7164_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
310b285192aSMauro Carvalho Chehab {
311b285192aSMauro Carvalho Chehab 	struct saa7164_encoder_fh *fh = file->private_data;
312b285192aSMauro Carvalho Chehab 	struct saa7164_port *port = fh->port;
313b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
314b285192aSMauro Carvalho Chehab 
315b285192aSMauro Carvalho Chehab 	if (0 != t->index)
316b285192aSMauro Carvalho Chehab 		return -EINVAL;
317b285192aSMauro Carvalho Chehab 
318cc1e6315SMauro Carvalho Chehab 	strscpy(t->name, "tuner", sizeof(t->name));
319b285192aSMauro Carvalho Chehab 	t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO;
3206b996126SHans Verkuil 	t->rangelow = SAA7164_TV_MIN_FREQ;
3216b996126SHans Verkuil 	t->rangehigh = SAA7164_TV_MAX_FREQ;
322b285192aSMauro Carvalho Chehab 
323b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "VIDIOC_G_TUNER: tuner type %d\n", t->type);
324b285192aSMauro Carvalho Chehab 
325b285192aSMauro Carvalho Chehab 	return 0;
326b285192aSMauro Carvalho Chehab }
327b285192aSMauro Carvalho Chehab 
saa7164_s_tuner(struct file * file,void * priv,const struct v4l2_tuner * t)328225b783bSHans Verkuil int saa7164_s_tuner(struct file *file, void *priv,
3292f73c7c5SHans Verkuil 			   const struct v4l2_tuner *t)
330b285192aSMauro Carvalho Chehab {
3316b996126SHans Verkuil 	if (0 != t->index)
3326b996126SHans Verkuil 		return -EINVAL;
3336b996126SHans Verkuil 
334b285192aSMauro Carvalho Chehab 	/* Update the A/V core */
335b285192aSMauro Carvalho Chehab 	return 0;
336b285192aSMauro Carvalho Chehab }
337b285192aSMauro Carvalho Chehab 
saa7164_g_frequency(struct saa7164_port * port,struct v4l2_frequency * f)338225b783bSHans Verkuil int saa7164_g_frequency(struct saa7164_port *port, struct v4l2_frequency *f)
339b285192aSMauro Carvalho Chehab {
3406b996126SHans Verkuil 	if (f->tuner)
3416b996126SHans Verkuil 		return -EINVAL;
3426b996126SHans Verkuil 
343b285192aSMauro Carvalho Chehab 	f->frequency = port->freq;
344b285192aSMauro Carvalho Chehab 	return 0;
345b285192aSMauro Carvalho Chehab }
346b285192aSMauro Carvalho Chehab 
vidioc_g_frequency(struct file * file,void * priv,struct v4l2_frequency * f)347225b783bSHans Verkuil static int vidioc_g_frequency(struct file *file, void *priv,
348225b783bSHans Verkuil 	struct v4l2_frequency *f)
349b285192aSMauro Carvalho Chehab {
350b285192aSMauro Carvalho Chehab 	struct saa7164_encoder_fh *fh = file->private_data;
351225b783bSHans Verkuil 
352225b783bSHans Verkuil 	return saa7164_g_frequency(fh->port, f);
353225b783bSHans Verkuil }
354225b783bSHans Verkuil 
saa7164_s_frequency(struct saa7164_port * port,const struct v4l2_frequency * f)355225b783bSHans Verkuil int saa7164_s_frequency(struct saa7164_port *port,
356225b783bSHans Verkuil 			const struct v4l2_frequency *f)
357225b783bSHans Verkuil {
358b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
359b285192aSMauro Carvalho Chehab 	struct saa7164_port *tsport;
360b285192aSMauro Carvalho Chehab 	struct dvb_frontend *fe;
361b285192aSMauro Carvalho Chehab 
362b285192aSMauro Carvalho Chehab 	/* TODO: Pull this for the std */
363b285192aSMauro Carvalho Chehab 	struct analog_parameters params = {
364b285192aSMauro Carvalho Chehab 		.mode      = V4L2_TUNER_ANALOG_TV,
365b285192aSMauro Carvalho Chehab 		.audmode   = V4L2_TUNER_MODE_STEREO,
366b285192aSMauro Carvalho Chehab 		.std       = port->encodernorm.id,
367b285192aSMauro Carvalho Chehab 		.frequency = f->frequency
368b285192aSMauro Carvalho Chehab 	};
369b285192aSMauro Carvalho Chehab 
370b285192aSMauro Carvalho Chehab 	/* Stop the encoder */
371b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s() frequency=%d tuner=%d\n", __func__,
372b285192aSMauro Carvalho Chehab 		f->frequency, f->tuner);
373b285192aSMauro Carvalho Chehab 
374b285192aSMauro Carvalho Chehab 	if (f->tuner != 0)
375b285192aSMauro Carvalho Chehab 		return -EINVAL;
376b285192aSMauro Carvalho Chehab 
3776b996126SHans Verkuil 	port->freq = clamp(f->frequency,
3786b996126SHans Verkuil 			   SAA7164_TV_MIN_FREQ, SAA7164_TV_MAX_FREQ);
379b285192aSMauro Carvalho Chehab 
380b285192aSMauro Carvalho Chehab 	/* Update the hardware */
381b285192aSMauro Carvalho Chehab 	if (port->nr == SAA7164_PORT_ENC1)
382b285192aSMauro Carvalho Chehab 		tsport = &dev->ports[SAA7164_PORT_TS1];
383225b783bSHans Verkuil 	else if (port->nr == SAA7164_PORT_ENC2)
384b285192aSMauro Carvalho Chehab 		tsport = &dev->ports[SAA7164_PORT_TS2];
385b285192aSMauro Carvalho Chehab 	else
386*c62b0891SHans Verkuil 		return -EINVAL; /* should not happen */
387b285192aSMauro Carvalho Chehab 
388b285192aSMauro Carvalho Chehab 	fe = tsport->dvb.frontend;
389b285192aSMauro Carvalho Chehab 
390b285192aSMauro Carvalho Chehab 	if (fe && fe->ops.tuner_ops.set_analog_params)
391b285192aSMauro Carvalho Chehab 		fe->ops.tuner_ops.set_analog_params(fe, &params);
392b285192aSMauro Carvalho Chehab 	else
393b285192aSMauro Carvalho Chehab 		printk(KERN_ERR "%s() No analog tuner, aborting\n", __func__);
394b285192aSMauro Carvalho Chehab 
395b285192aSMauro Carvalho Chehab 	saa7164_encoder_initialize(port);
396b285192aSMauro Carvalho Chehab 
397b285192aSMauro Carvalho Chehab 	return 0;
398b285192aSMauro Carvalho Chehab }
399b285192aSMauro Carvalho Chehab 
vidioc_s_frequency(struct file * file,void * priv,const struct v4l2_frequency * f)400225b783bSHans Verkuil static int vidioc_s_frequency(struct file *file, void *priv,
401225b783bSHans Verkuil 			      const struct v4l2_frequency *f)
402225b783bSHans Verkuil {
403225b783bSHans Verkuil 	struct saa7164_encoder_fh *fh = file->private_data;
404225b783bSHans Verkuil 
405225b783bSHans Verkuil 	return saa7164_s_frequency(fh->port, f);
406225b783bSHans Verkuil }
407225b783bSHans Verkuil 
saa7164_s_ctrl(struct v4l2_ctrl * ctrl)4081a708ea0SHans Verkuil static int saa7164_s_ctrl(struct v4l2_ctrl *ctrl)
409b285192aSMauro Carvalho Chehab {
4101a708ea0SHans Verkuil 	struct saa7164_port *port =
4111a708ea0SHans Verkuil 		container_of(ctrl->handler, struct saa7164_port, ctrl_handler);
4121a708ea0SHans Verkuil 	struct saa7164_encoder_params *params = &port->encoder_params;
413b285192aSMauro Carvalho Chehab 	int ret = 0;
414b285192aSMauro Carvalho Chehab 
4151a708ea0SHans Verkuil 	switch (ctrl->id) {
416b285192aSMauro Carvalho Chehab 	case V4L2_CID_BRIGHTNESS:
4171a708ea0SHans Verkuil 		port->ctl_brightness = ctrl->val;
4181a708ea0SHans Verkuil 		saa7164_api_set_usercontrol(port, PU_BRIGHTNESS_CONTROL);
419b285192aSMauro Carvalho Chehab 		break;
420b285192aSMauro Carvalho Chehab 	case V4L2_CID_CONTRAST:
4211a708ea0SHans Verkuil 		port->ctl_contrast = ctrl->val;
422b285192aSMauro Carvalho Chehab 		saa7164_api_set_usercontrol(port, PU_CONTRAST_CONTROL);
423b285192aSMauro Carvalho Chehab 		break;
424b285192aSMauro Carvalho Chehab 	case V4L2_CID_SATURATION:
4251a708ea0SHans Verkuil 		port->ctl_saturation = ctrl->val;
4261a708ea0SHans Verkuil 		saa7164_api_set_usercontrol(port, PU_SATURATION_CONTROL);
427b285192aSMauro Carvalho Chehab 		break;
428b285192aSMauro Carvalho Chehab 	case V4L2_CID_HUE:
4291a708ea0SHans Verkuil 		port->ctl_hue = ctrl->val;
430b285192aSMauro Carvalho Chehab 		saa7164_api_set_usercontrol(port, PU_HUE_CONTROL);
431b285192aSMauro Carvalho Chehab 		break;
432b285192aSMauro Carvalho Chehab 	case V4L2_CID_SHARPNESS:
4331a708ea0SHans Verkuil 		port->ctl_sharpness = ctrl->val;
434b285192aSMauro Carvalho Chehab 		saa7164_api_set_usercontrol(port, PU_SHARPNESS_CONTROL);
435b285192aSMauro Carvalho Chehab 		break;
436b285192aSMauro Carvalho Chehab 	case V4L2_CID_AUDIO_VOLUME:
4371a708ea0SHans Verkuil 		port->ctl_volume = ctrl->val;
438b285192aSMauro Carvalho Chehab 		saa7164_api_set_audio_volume(port, port->ctl_volume);
439b285192aSMauro Carvalho Chehab 		break;
440b285192aSMauro Carvalho Chehab 	case V4L2_CID_MPEG_VIDEO_BITRATE:
4411a708ea0SHans Verkuil 		params->bitrate = ctrl->val;
442b285192aSMauro Carvalho Chehab 		break;
443b285192aSMauro Carvalho Chehab 	case V4L2_CID_MPEG_STREAM_TYPE:
4441a708ea0SHans Verkuil 		params->stream_type = ctrl->val;
445b285192aSMauro Carvalho Chehab 		break;
446b285192aSMauro Carvalho Chehab 	case V4L2_CID_MPEG_AUDIO_MUTE:
4471a708ea0SHans Verkuil 		params->ctl_mute = ctrl->val;
448b285192aSMauro Carvalho Chehab 		ret = saa7164_api_audio_mute(port, params->ctl_mute);
449b285192aSMauro Carvalho Chehab 		if (ret != SAA_OK) {
450b285192aSMauro Carvalho Chehab 			printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__,
451b285192aSMauro Carvalho Chehab 				ret);
452b285192aSMauro Carvalho Chehab 			ret = -EIO;
453b285192aSMauro Carvalho Chehab 		}
454b285192aSMauro Carvalho Chehab 		break;
455b285192aSMauro Carvalho Chehab 	case V4L2_CID_MPEG_VIDEO_ASPECT:
4561a708ea0SHans Verkuil 		params->ctl_aspect = ctrl->val;
457b285192aSMauro Carvalho Chehab 		ret = saa7164_api_set_aspect_ratio(port);
458b285192aSMauro Carvalho Chehab 		if (ret != SAA_OK) {
459b285192aSMauro Carvalho Chehab 			printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__,
460b285192aSMauro Carvalho Chehab 				ret);
461b285192aSMauro Carvalho Chehab 			ret = -EIO;
462b285192aSMauro Carvalho Chehab 		}
463b285192aSMauro Carvalho Chehab 		break;
464b285192aSMauro Carvalho Chehab 	case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
4651a708ea0SHans Verkuil 		params->bitrate_mode = ctrl->val;
466b285192aSMauro Carvalho Chehab 		break;
467b285192aSMauro Carvalho Chehab 	case V4L2_CID_MPEG_VIDEO_B_FRAMES:
4681a708ea0SHans Verkuil 		params->refdist = ctrl->val;
469b285192aSMauro Carvalho Chehab 		break;
470b285192aSMauro Carvalho Chehab 	case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
4711a708ea0SHans Verkuil 		params->bitrate_peak = ctrl->val;
472b285192aSMauro Carvalho Chehab 		break;
473b285192aSMauro Carvalho Chehab 	case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
4741a708ea0SHans Verkuil 		params->gop_size = ctrl->val;
475b285192aSMauro Carvalho Chehab 		break;
476b285192aSMauro Carvalho Chehab 	default:
4771a708ea0SHans Verkuil 		ret = -EINVAL;
478b285192aSMauro Carvalho Chehab 	}
479b285192aSMauro Carvalho Chehab 
480b285192aSMauro Carvalho Chehab 	return ret;
481b285192aSMauro Carvalho Chehab }
482b285192aSMauro Carvalho Chehab 
vidioc_querycap(struct file * file,void * priv,struct v4l2_capability * cap)483b285192aSMauro Carvalho Chehab static int vidioc_querycap(struct file *file, void  *priv,
484b285192aSMauro Carvalho Chehab 	struct v4l2_capability *cap)
485b285192aSMauro Carvalho Chehab {
486b285192aSMauro Carvalho Chehab 	struct saa7164_encoder_fh *fh = file->private_data;
487b285192aSMauro Carvalho Chehab 	struct saa7164_port *port = fh->port;
488b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
489b285192aSMauro Carvalho Chehab 
490cc1e6315SMauro Carvalho Chehab 	strscpy(cap->driver, dev->name, sizeof(cap->driver));
491c0decac1SMauro Carvalho Chehab 	strscpy(cap->card, saa7164_boards[dev->board].name,
492b285192aSMauro Carvalho Chehab 		sizeof(cap->card));
49321615365SHans Verkuil 	cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
49421615365SHans Verkuil 			    V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE |
495534bc3e2SHans Verkuil 			    V4L2_CAP_DEVICE_CAPS;
496b285192aSMauro Carvalho Chehab 	return 0;
497b285192aSMauro Carvalho Chehab }
498b285192aSMauro Carvalho Chehab 
vidioc_enum_fmt_vid_cap(struct file * file,void * priv,struct v4l2_fmtdesc * f)499b285192aSMauro Carvalho Chehab static int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
500b285192aSMauro Carvalho Chehab 	struct v4l2_fmtdesc *f)
501b285192aSMauro Carvalho Chehab {
502b285192aSMauro Carvalho Chehab 	if (f->index != 0)
503b285192aSMauro Carvalho Chehab 		return -EINVAL;
504b285192aSMauro Carvalho Chehab 
505b285192aSMauro Carvalho Chehab 	f->pixelformat = V4L2_PIX_FMT_MPEG;
506b285192aSMauro Carvalho Chehab 
507b285192aSMauro Carvalho Chehab 	return 0;
508b285192aSMauro Carvalho Chehab }
509b285192aSMauro Carvalho Chehab 
vidioc_fmt_vid_cap(struct file * file,void * priv,struct v4l2_format * f)510031d2297SHans Verkuil static int vidioc_fmt_vid_cap(struct file *file, void *priv,
511b285192aSMauro Carvalho Chehab 				struct v4l2_format *f)
512b285192aSMauro Carvalho Chehab {
513b285192aSMauro Carvalho Chehab 	struct saa7164_encoder_fh *fh = file->private_data;
514b285192aSMauro Carvalho Chehab 	struct saa7164_port *port = fh->port;
515b285192aSMauro Carvalho Chehab 
516b285192aSMauro Carvalho Chehab 	f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
517b285192aSMauro Carvalho Chehab 	f->fmt.pix.bytesperline = 0;
518031d2297SHans Verkuil 	f->fmt.pix.sizeimage    = SAA7164_SIZEIMAGE;
519031d2297SHans Verkuil 	f->fmt.pix.field        = V4L2_FIELD_INTERLACED;
520031d2297SHans Verkuil 	f->fmt.pix.colorspace   = V4L2_COLORSPACE_SMPTE170M;
521b285192aSMauro Carvalho Chehab 	f->fmt.pix.width        = port->width;
522b285192aSMauro Carvalho Chehab 	f->fmt.pix.height       = port->height;
523b285192aSMauro Carvalho Chehab 	return 0;
524b285192aSMauro Carvalho Chehab }
525b285192aSMauro Carvalho Chehab 
saa7164_encoder_stop_port(struct saa7164_port * port)526b285192aSMauro Carvalho Chehab static int saa7164_encoder_stop_port(struct saa7164_port *port)
527b285192aSMauro Carvalho Chehab {
528b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
529b285192aSMauro Carvalho Chehab 	int ret;
530b285192aSMauro Carvalho Chehab 
531b285192aSMauro Carvalho Chehab 	ret = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
532b285192aSMauro Carvalho Chehab 	if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
533b285192aSMauro Carvalho Chehab 		printk(KERN_ERR "%s() stop transition failed, ret = 0x%x\n",
534b285192aSMauro Carvalho Chehab 			__func__, ret);
535b285192aSMauro Carvalho Chehab 		ret = -EIO;
536b285192aSMauro Carvalho Chehab 	} else {
537b285192aSMauro Carvalho Chehab 		dprintk(DBGLVL_ENC, "%s()    Stopped\n", __func__);
538b285192aSMauro Carvalho Chehab 		ret = 0;
539b285192aSMauro Carvalho Chehab 	}
540b285192aSMauro Carvalho Chehab 
541b285192aSMauro Carvalho Chehab 	return ret;
542b285192aSMauro Carvalho Chehab }
543b285192aSMauro Carvalho Chehab 
saa7164_encoder_acquire_port(struct saa7164_port * port)544b285192aSMauro Carvalho Chehab static int saa7164_encoder_acquire_port(struct saa7164_port *port)
545b285192aSMauro Carvalho Chehab {
546b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
547b285192aSMauro Carvalho Chehab 	int ret;
548b285192aSMauro Carvalho Chehab 
549b285192aSMauro Carvalho Chehab 	ret = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE);
550b285192aSMauro Carvalho Chehab 	if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
551b285192aSMauro Carvalho Chehab 		printk(KERN_ERR "%s() acquire transition failed, ret = 0x%x\n",
552b285192aSMauro Carvalho Chehab 			__func__, ret);
553b285192aSMauro Carvalho Chehab 		ret = -EIO;
554b285192aSMauro Carvalho Chehab 	} else {
555b285192aSMauro Carvalho Chehab 		dprintk(DBGLVL_ENC, "%s() Acquired\n", __func__);
556b285192aSMauro Carvalho Chehab 		ret = 0;
557b285192aSMauro Carvalho Chehab 	}
558b285192aSMauro Carvalho Chehab 
559b285192aSMauro Carvalho Chehab 	return ret;
560b285192aSMauro Carvalho Chehab }
561b285192aSMauro Carvalho Chehab 
saa7164_encoder_pause_port(struct saa7164_port * port)562b285192aSMauro Carvalho Chehab static int saa7164_encoder_pause_port(struct saa7164_port *port)
563b285192aSMauro Carvalho Chehab {
564b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
565b285192aSMauro Carvalho Chehab 	int ret;
566b285192aSMauro Carvalho Chehab 
567b285192aSMauro Carvalho Chehab 	ret = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE);
568b285192aSMauro Carvalho Chehab 	if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
569b285192aSMauro Carvalho Chehab 		printk(KERN_ERR "%s() pause transition failed, ret = 0x%x\n",
570b285192aSMauro Carvalho Chehab 			__func__, ret);
571b285192aSMauro Carvalho Chehab 		ret = -EIO;
572b285192aSMauro Carvalho Chehab 	} else {
573b285192aSMauro Carvalho Chehab 		dprintk(DBGLVL_ENC, "%s()   Paused\n", __func__);
574b285192aSMauro Carvalho Chehab 		ret = 0;
575b285192aSMauro Carvalho Chehab 	}
576b285192aSMauro Carvalho Chehab 
577b285192aSMauro Carvalho Chehab 	return ret;
578b285192aSMauro Carvalho Chehab }
579b285192aSMauro Carvalho Chehab 
580b285192aSMauro Carvalho Chehab /* Firmware is very windows centric, meaning you have to transition
581b285192aSMauro Carvalho Chehab  * the part through AVStream / KS Windows stages, forwards or backwards.
582b285192aSMauro Carvalho Chehab  * States are: stopped, acquired (h/w), paused, started.
583b285192aSMauro Carvalho Chehab  * We have to leave here will all of the soft buffers on the free list,
584b285192aSMauro Carvalho Chehab  * else the cfg_post() func won't have soft buffers to correctly configure.
585b285192aSMauro Carvalho Chehab  */
saa7164_encoder_stop_streaming(struct saa7164_port * port)586b285192aSMauro Carvalho Chehab static int saa7164_encoder_stop_streaming(struct saa7164_port *port)
587b285192aSMauro Carvalho Chehab {
588b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
589b285192aSMauro Carvalho Chehab 	struct saa7164_buffer *buf;
590b285192aSMauro Carvalho Chehab 	struct saa7164_user_buffer *ubuf;
591b285192aSMauro Carvalho Chehab 	struct list_head *c, *n;
592b285192aSMauro Carvalho Chehab 	int ret;
593b285192aSMauro Carvalho Chehab 
594b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s(port=%d)\n", __func__, port->nr);
595b285192aSMauro Carvalho Chehab 
596b285192aSMauro Carvalho Chehab 	ret = saa7164_encoder_pause_port(port);
597b285192aSMauro Carvalho Chehab 	ret = saa7164_encoder_acquire_port(port);
598b285192aSMauro Carvalho Chehab 	ret = saa7164_encoder_stop_port(port);
599b285192aSMauro Carvalho Chehab 
600b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s(port=%d) Hardware stopped\n", __func__,
601b285192aSMauro Carvalho Chehab 		port->nr);
602b285192aSMauro Carvalho Chehab 
603b285192aSMauro Carvalho Chehab 	/* Reset the state of any allocated buffer resources */
604b285192aSMauro Carvalho Chehab 	mutex_lock(&port->dmaqueue_lock);
605b285192aSMauro Carvalho Chehab 
606b285192aSMauro Carvalho Chehab 	/* Reset the hard and soft buffer state */
607b285192aSMauro Carvalho Chehab 	list_for_each_safe(c, n, &port->dmaqueue.list) {
608b285192aSMauro Carvalho Chehab 		buf = list_entry(c, struct saa7164_buffer, list);
609b285192aSMauro Carvalho Chehab 		buf->flags = SAA7164_BUFFER_FREE;
610b285192aSMauro Carvalho Chehab 		buf->pos = 0;
611b285192aSMauro Carvalho Chehab 	}
612b285192aSMauro Carvalho Chehab 
613b285192aSMauro Carvalho Chehab 	list_for_each_safe(c, n, &port->list_buf_used.list) {
614b285192aSMauro Carvalho Chehab 		ubuf = list_entry(c, struct saa7164_user_buffer, list);
615b285192aSMauro Carvalho Chehab 		ubuf->pos = 0;
616b285192aSMauro Carvalho Chehab 		list_move_tail(&ubuf->list, &port->list_buf_free.list);
617b285192aSMauro Carvalho Chehab 	}
618b285192aSMauro Carvalho Chehab 
619b285192aSMauro Carvalho Chehab 	mutex_unlock(&port->dmaqueue_lock);
620b285192aSMauro Carvalho Chehab 
621b285192aSMauro Carvalho Chehab 	/* Free any allocated resources */
622b285192aSMauro Carvalho Chehab 	saa7164_encoder_buffers_dealloc(port);
623b285192aSMauro Carvalho Chehab 
624b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s(port=%d) Released\n", __func__, port->nr);
625b285192aSMauro Carvalho Chehab 
626b285192aSMauro Carvalho Chehab 	return ret;
627b285192aSMauro Carvalho Chehab }
628b285192aSMauro Carvalho Chehab 
saa7164_encoder_start_streaming(struct saa7164_port * port)629b285192aSMauro Carvalho Chehab static int saa7164_encoder_start_streaming(struct saa7164_port *port)
630b285192aSMauro Carvalho Chehab {
631b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
632b285192aSMauro Carvalho Chehab 	int result, ret = 0;
633b285192aSMauro Carvalho Chehab 
634b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s(port=%d)\n", __func__, port->nr);
635b285192aSMauro Carvalho Chehab 
636b285192aSMauro Carvalho Chehab 	port->done_first_interrupt = 0;
637b285192aSMauro Carvalho Chehab 
638b285192aSMauro Carvalho Chehab 	/* allocate all of the PCIe DMA buffer resources on the fly,
639b285192aSMauro Carvalho Chehab 	 * allowing switching between TS and PS payloads without
640b285192aSMauro Carvalho Chehab 	 * requiring a complete driver reload.
641b285192aSMauro Carvalho Chehab 	 */
642b285192aSMauro Carvalho Chehab 	saa7164_encoder_buffers_alloc(port);
643b285192aSMauro Carvalho Chehab 
644b285192aSMauro Carvalho Chehab 	/* Configure the encoder with any cache values */
645b285192aSMauro Carvalho Chehab 	saa7164_api_set_encoder(port);
646b285192aSMauro Carvalho Chehab 	saa7164_api_get_encoder(port);
647b285192aSMauro Carvalho Chehab 
648b285192aSMauro Carvalho Chehab 	/* Place the empty buffers on the hardware */
649b285192aSMauro Carvalho Chehab 	saa7164_buffer_cfg_port(port);
650b285192aSMauro Carvalho Chehab 
651b285192aSMauro Carvalho Chehab 	/* Acquire the hardware */
652b285192aSMauro Carvalho Chehab 	result = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE);
653b285192aSMauro Carvalho Chehab 	if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
654b285192aSMauro Carvalho Chehab 		printk(KERN_ERR "%s() acquire transition failed, res = 0x%x\n",
655b285192aSMauro Carvalho Chehab 			__func__, result);
656b285192aSMauro Carvalho Chehab 
657b285192aSMauro Carvalho Chehab 		/* Stop the hardware, regardless */
658b285192aSMauro Carvalho Chehab 		result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
659b285192aSMauro Carvalho Chehab 		if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
66024f711c1SMauro Carvalho Chehab 			printk(KERN_ERR "%s() acquire/forced stop transition failed, res = 0x%x\n",
66124f711c1SMauro Carvalho Chehab 			       __func__, result);
662b285192aSMauro Carvalho Chehab 		}
663b285192aSMauro Carvalho Chehab 		ret = -EIO;
664b285192aSMauro Carvalho Chehab 		goto out;
665b285192aSMauro Carvalho Chehab 	} else
666b285192aSMauro Carvalho Chehab 		dprintk(DBGLVL_ENC, "%s()   Acquired\n", __func__);
667b285192aSMauro Carvalho Chehab 
668b285192aSMauro Carvalho Chehab 	/* Pause the hardware */
669b285192aSMauro Carvalho Chehab 	result = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE);
670b285192aSMauro Carvalho Chehab 	if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
671b285192aSMauro Carvalho Chehab 		printk(KERN_ERR "%s() pause transition failed, res = 0x%x\n",
672b285192aSMauro Carvalho Chehab 				__func__, result);
673b285192aSMauro Carvalho Chehab 
674b285192aSMauro Carvalho Chehab 		/* Stop the hardware, regardless */
675b285192aSMauro Carvalho Chehab 		result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
676b285192aSMauro Carvalho Chehab 		if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
67724f711c1SMauro Carvalho Chehab 			printk(KERN_ERR "%s() pause/forced stop transition failed, res = 0x%x\n",
67824f711c1SMauro Carvalho Chehab 			       __func__, result);
679b285192aSMauro Carvalho Chehab 		}
680b285192aSMauro Carvalho Chehab 
681b285192aSMauro Carvalho Chehab 		ret = -EIO;
682b285192aSMauro Carvalho Chehab 		goto out;
683b285192aSMauro Carvalho Chehab 	} else
684b285192aSMauro Carvalho Chehab 		dprintk(DBGLVL_ENC, "%s()   Paused\n", __func__);
685b285192aSMauro Carvalho Chehab 
686b285192aSMauro Carvalho Chehab 	/* Start the hardware */
687b285192aSMauro Carvalho Chehab 	result = saa7164_api_transition_port(port, SAA_DMASTATE_RUN);
688b285192aSMauro Carvalho Chehab 	if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
689b285192aSMauro Carvalho Chehab 		printk(KERN_ERR "%s() run transition failed, result = 0x%x\n",
690b285192aSMauro Carvalho Chehab 				__func__, result);
691b285192aSMauro Carvalho Chehab 
692b285192aSMauro Carvalho Chehab 		/* Stop the hardware, regardless */
693b285192aSMauro Carvalho Chehab 		result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
694b285192aSMauro Carvalho Chehab 		if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
69524f711c1SMauro Carvalho Chehab 			printk(KERN_ERR "%s() run/forced stop transition failed, res = 0x%x\n",
69624f711c1SMauro Carvalho Chehab 			       __func__, result);
697b285192aSMauro Carvalho Chehab 		}
698b285192aSMauro Carvalho Chehab 
699b285192aSMauro Carvalho Chehab 		ret = -EIO;
700b285192aSMauro Carvalho Chehab 	} else
701b285192aSMauro Carvalho Chehab 		dprintk(DBGLVL_ENC, "%s()   Running\n", __func__);
702b285192aSMauro Carvalho Chehab 
703b285192aSMauro Carvalho Chehab out:
704b285192aSMauro Carvalho Chehab 	return ret;
705b285192aSMauro Carvalho Chehab }
706b285192aSMauro Carvalho Chehab 
fops_open(struct file * file)707b285192aSMauro Carvalho Chehab static int fops_open(struct file *file)
708b285192aSMauro Carvalho Chehab {
709b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev;
710b285192aSMauro Carvalho Chehab 	struct saa7164_port *port;
711b285192aSMauro Carvalho Chehab 	struct saa7164_encoder_fh *fh;
712b285192aSMauro Carvalho Chehab 
713b285192aSMauro Carvalho Chehab 	port = (struct saa7164_port *)video_get_drvdata(video_devdata(file));
714b285192aSMauro Carvalho Chehab 	if (!port)
715b285192aSMauro Carvalho Chehab 		return -ENODEV;
716b285192aSMauro Carvalho Chehab 
717b285192aSMauro Carvalho Chehab 	dev = port->dev;
718b285192aSMauro Carvalho Chehab 
719b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s()\n", __func__);
720b285192aSMauro Carvalho Chehab 
721b285192aSMauro Carvalho Chehab 	/* allocate + initialize per filehandle data */
722b285192aSMauro Carvalho Chehab 	fh = kzalloc(sizeof(*fh), GFP_KERNEL);
723b285192aSMauro Carvalho Chehab 	if (NULL == fh)
724b285192aSMauro Carvalho Chehab 		return -ENOMEM;
725b285192aSMauro Carvalho Chehab 
726b285192aSMauro Carvalho Chehab 	fh->port = port;
727d6d3fe2fSHans Verkuil 	v4l2_fh_init(&fh->fh, video_devdata(file));
728d6d3fe2fSHans Verkuil 	v4l2_fh_add(&fh->fh);
729d6d3fe2fSHans Verkuil 	file->private_data = fh;
730b285192aSMauro Carvalho Chehab 
731b285192aSMauro Carvalho Chehab 	return 0;
732b285192aSMauro Carvalho Chehab }
733b285192aSMauro Carvalho Chehab 
fops_release(struct file * file)734b285192aSMauro Carvalho Chehab static int fops_release(struct file *file)
735b285192aSMauro Carvalho Chehab {
736b285192aSMauro Carvalho Chehab 	struct saa7164_encoder_fh *fh = file->private_data;
737b285192aSMauro Carvalho Chehab 	struct saa7164_port *port = fh->port;
738b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
739b285192aSMauro Carvalho Chehab 
740b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s()\n", __func__);
741b285192aSMauro Carvalho Chehab 
742b285192aSMauro Carvalho Chehab 	/* Shut device down on last close */
743b285192aSMauro Carvalho Chehab 	if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) {
744b285192aSMauro Carvalho Chehab 		if (atomic_dec_return(&port->v4l_reader_count) == 0) {
745b285192aSMauro Carvalho Chehab 			/* stop mpeg capture then cancel buffers */
746b285192aSMauro Carvalho Chehab 			saa7164_encoder_stop_streaming(port);
747b285192aSMauro Carvalho Chehab 		}
748b285192aSMauro Carvalho Chehab 	}
749b285192aSMauro Carvalho Chehab 
750d6d3fe2fSHans Verkuil 	v4l2_fh_del(&fh->fh);
751d6d3fe2fSHans Verkuil 	v4l2_fh_exit(&fh->fh);
752b285192aSMauro Carvalho Chehab 	kfree(fh);
753b285192aSMauro Carvalho Chehab 
754b285192aSMauro Carvalho Chehab 	return 0;
755b285192aSMauro Carvalho Chehab }
756b285192aSMauro Carvalho Chehab 
7575faf7db8SMauro Carvalho Chehab static struct
saa7164_enc_next_buf(struct saa7164_port * port)7585faf7db8SMauro Carvalho Chehab saa7164_user_buffer *saa7164_enc_next_buf(struct saa7164_port *port)
759b285192aSMauro Carvalho Chehab {
760b285192aSMauro Carvalho Chehab 	struct saa7164_user_buffer *ubuf = NULL;
761b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
762b285192aSMauro Carvalho Chehab 	u32 crc;
763b285192aSMauro Carvalho Chehab 
764b285192aSMauro Carvalho Chehab 	mutex_lock(&port->dmaqueue_lock);
765b285192aSMauro Carvalho Chehab 	if (!list_empty(&port->list_buf_used.list)) {
766b285192aSMauro Carvalho Chehab 		ubuf = list_first_entry(&port->list_buf_used.list,
767b285192aSMauro Carvalho Chehab 			struct saa7164_user_buffer, list);
768b285192aSMauro Carvalho Chehab 
769b285192aSMauro Carvalho Chehab 		if (crc_checking) {
770b285192aSMauro Carvalho Chehab 			crc = crc32(0, ubuf->data, ubuf->actual_size);
771b285192aSMauro Carvalho Chehab 			if (crc != ubuf->crc) {
772b285192aSMauro Carvalho Chehab 				printk(KERN_ERR
773b285192aSMauro Carvalho Chehab 		"%s() ubuf %p crc became invalid, was 0x%x became 0x%x\n",
774b285192aSMauro Carvalho Chehab 					__func__,
775b285192aSMauro Carvalho Chehab 					ubuf, ubuf->crc, crc);
776b285192aSMauro Carvalho Chehab 			}
777b285192aSMauro Carvalho Chehab 		}
778b285192aSMauro Carvalho Chehab 
779b285192aSMauro Carvalho Chehab 	}
780b285192aSMauro Carvalho Chehab 	mutex_unlock(&port->dmaqueue_lock);
781b285192aSMauro Carvalho Chehab 
782b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s() returns %p\n", __func__, ubuf);
783b285192aSMauro Carvalho Chehab 
784b285192aSMauro Carvalho Chehab 	return ubuf;
785b285192aSMauro Carvalho Chehab }
786b285192aSMauro Carvalho Chehab 
fops_read(struct file * file,char __user * buffer,size_t count,loff_t * pos)787b285192aSMauro Carvalho Chehab static ssize_t fops_read(struct file *file, char __user *buffer,
788b285192aSMauro Carvalho Chehab 	size_t count, loff_t *pos)
789b285192aSMauro Carvalho Chehab {
790b285192aSMauro Carvalho Chehab 	struct saa7164_encoder_fh *fh = file->private_data;
791b285192aSMauro Carvalho Chehab 	struct saa7164_port *port = fh->port;
792b285192aSMauro Carvalho Chehab 	struct saa7164_user_buffer *ubuf = NULL;
793b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
794b285192aSMauro Carvalho Chehab 	int ret = 0;
795b285192aSMauro Carvalho Chehab 	int rem, cnt;
796b285192aSMauro Carvalho Chehab 	u8 *p;
797b285192aSMauro Carvalho Chehab 
798b285192aSMauro Carvalho Chehab 	port->last_read_msecs_diff = port->last_read_msecs;
799b285192aSMauro Carvalho Chehab 	port->last_read_msecs = jiffies_to_msecs(jiffies);
800b285192aSMauro Carvalho Chehab 	port->last_read_msecs_diff = port->last_read_msecs -
801b285192aSMauro Carvalho Chehab 		port->last_read_msecs_diff;
802b285192aSMauro Carvalho Chehab 
803b285192aSMauro Carvalho Chehab 	saa7164_histogram_update(&port->read_interval,
804b285192aSMauro Carvalho Chehab 		port->last_read_msecs_diff);
805b285192aSMauro Carvalho Chehab 
806b285192aSMauro Carvalho Chehab 	if (*pos) {
807b285192aSMauro Carvalho Chehab 		printk(KERN_ERR "%s() ESPIPE\n", __func__);
808b285192aSMauro Carvalho Chehab 		return -ESPIPE;
809b285192aSMauro Carvalho Chehab 	}
810b285192aSMauro Carvalho Chehab 
811b285192aSMauro Carvalho Chehab 	if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {
812b285192aSMauro Carvalho Chehab 		if (atomic_inc_return(&port->v4l_reader_count) == 1) {
813b285192aSMauro Carvalho Chehab 
814b285192aSMauro Carvalho Chehab 			if (saa7164_encoder_initialize(port) < 0) {
815b285192aSMauro Carvalho Chehab 				printk(KERN_ERR "%s() EINVAL\n", __func__);
816b285192aSMauro Carvalho Chehab 				return -EINVAL;
817b285192aSMauro Carvalho Chehab 			}
818b285192aSMauro Carvalho Chehab 
819b285192aSMauro Carvalho Chehab 			saa7164_encoder_start_streaming(port);
820b285192aSMauro Carvalho Chehab 			msleep(200);
821b285192aSMauro Carvalho Chehab 		}
822b285192aSMauro Carvalho Chehab 	}
823b285192aSMauro Carvalho Chehab 
824b285192aSMauro Carvalho Chehab 	/* blocking wait for buffer */
825b285192aSMauro Carvalho Chehab 	if ((file->f_flags & O_NONBLOCK) == 0) {
826b285192aSMauro Carvalho Chehab 		if (wait_event_interruptible(port->wait_read,
827b285192aSMauro Carvalho Chehab 			saa7164_enc_next_buf(port))) {
828b285192aSMauro Carvalho Chehab 				printk(KERN_ERR "%s() ERESTARTSYS\n", __func__);
829b285192aSMauro Carvalho Chehab 				return -ERESTARTSYS;
830b285192aSMauro Carvalho Chehab 		}
831b285192aSMauro Carvalho Chehab 	}
832b285192aSMauro Carvalho Chehab 
833b285192aSMauro Carvalho Chehab 	/* Pull the first buffer from the used list */
834b285192aSMauro Carvalho Chehab 	ubuf = saa7164_enc_next_buf(port);
835b285192aSMauro Carvalho Chehab 
836b285192aSMauro Carvalho Chehab 	while ((count > 0) && ubuf) {
837b285192aSMauro Carvalho Chehab 
838b285192aSMauro Carvalho Chehab 		/* set remaining bytes to copy */
839b285192aSMauro Carvalho Chehab 		rem = ubuf->actual_size - ubuf->pos;
840b285192aSMauro Carvalho Chehab 		cnt = rem > count ? count : rem;
841b285192aSMauro Carvalho Chehab 
842b285192aSMauro Carvalho Chehab 		p = ubuf->data + ubuf->pos;
843b285192aSMauro Carvalho Chehab 
844b285192aSMauro Carvalho Chehab 		dprintk(DBGLVL_ENC,
845b285192aSMauro Carvalho Chehab 			"%s() count=%d cnt=%d rem=%d buf=%p buf->pos=%d\n",
846b285192aSMauro Carvalho Chehab 			__func__, (int)count, cnt, rem, ubuf, ubuf->pos);
847b285192aSMauro Carvalho Chehab 
848b285192aSMauro Carvalho Chehab 		if (copy_to_user(buffer, p, cnt)) {
849b285192aSMauro Carvalho Chehab 			printk(KERN_ERR "%s() copy_to_user failed\n", __func__);
850b285192aSMauro Carvalho Chehab 			if (!ret) {
851b285192aSMauro Carvalho Chehab 				printk(KERN_ERR "%s() EFAULT\n", __func__);
852b285192aSMauro Carvalho Chehab 				ret = -EFAULT;
853b285192aSMauro Carvalho Chehab 			}
854b285192aSMauro Carvalho Chehab 			goto err;
855b285192aSMauro Carvalho Chehab 		}
856b285192aSMauro Carvalho Chehab 
857b285192aSMauro Carvalho Chehab 		ubuf->pos += cnt;
858b285192aSMauro Carvalho Chehab 		count -= cnt;
859b285192aSMauro Carvalho Chehab 		buffer += cnt;
860b285192aSMauro Carvalho Chehab 		ret += cnt;
861b285192aSMauro Carvalho Chehab 
862b285192aSMauro Carvalho Chehab 		if (ubuf->pos > ubuf->actual_size)
863b285192aSMauro Carvalho Chehab 			printk(KERN_ERR "read() pos > actual, huh?\n");
864b285192aSMauro Carvalho Chehab 
865b285192aSMauro Carvalho Chehab 		if (ubuf->pos == ubuf->actual_size) {
866b285192aSMauro Carvalho Chehab 
867b285192aSMauro Carvalho Chehab 			/* finished with current buffer, take next buffer */
868b285192aSMauro Carvalho Chehab 
869b285192aSMauro Carvalho Chehab 			/* Requeue the buffer on the free list */
870b285192aSMauro Carvalho Chehab 			ubuf->pos = 0;
871b285192aSMauro Carvalho Chehab 
872b285192aSMauro Carvalho Chehab 			mutex_lock(&port->dmaqueue_lock);
873b285192aSMauro Carvalho Chehab 			list_move_tail(&ubuf->list, &port->list_buf_free.list);
874b285192aSMauro Carvalho Chehab 			mutex_unlock(&port->dmaqueue_lock);
875b285192aSMauro Carvalho Chehab 
876b285192aSMauro Carvalho Chehab 			/* Dequeue next */
877b285192aSMauro Carvalho Chehab 			if ((file->f_flags & O_NONBLOCK) == 0) {
878b285192aSMauro Carvalho Chehab 				if (wait_event_interruptible(port->wait_read,
879b285192aSMauro Carvalho Chehab 					saa7164_enc_next_buf(port))) {
880b285192aSMauro Carvalho Chehab 						break;
881b285192aSMauro Carvalho Chehab 				}
882b285192aSMauro Carvalho Chehab 			}
883b285192aSMauro Carvalho Chehab 			ubuf = saa7164_enc_next_buf(port);
884b285192aSMauro Carvalho Chehab 		}
885b285192aSMauro Carvalho Chehab 	}
886b285192aSMauro Carvalho Chehab err:
887b285192aSMauro Carvalho Chehab 	if (!ret && !ubuf)
888b285192aSMauro Carvalho Chehab 		ret = -EAGAIN;
889b285192aSMauro Carvalho Chehab 
890b285192aSMauro Carvalho Chehab 	return ret;
891b285192aSMauro Carvalho Chehab }
892b285192aSMauro Carvalho Chehab 
fops_poll(struct file * file,poll_table * wait)893c23e0cb8SAl Viro static __poll_t fops_poll(struct file *file, poll_table *wait)
894b285192aSMauro Carvalho Chehab {
89501699437SAl Viro 	__poll_t req_events = poll_requested_events(wait);
896b285192aSMauro Carvalho Chehab 	struct saa7164_encoder_fh *fh =
897b285192aSMauro Carvalho Chehab 		(struct saa7164_encoder_fh *)file->private_data;
898b285192aSMauro Carvalho Chehab 	struct saa7164_port *port = fh->port;
899c23e0cb8SAl Viro 	__poll_t mask = v4l2_ctrl_poll(file, wait);
900b285192aSMauro Carvalho Chehab 
901b285192aSMauro Carvalho Chehab 	port->last_poll_msecs_diff = port->last_poll_msecs;
902b285192aSMauro Carvalho Chehab 	port->last_poll_msecs = jiffies_to_msecs(jiffies);
903b285192aSMauro Carvalho Chehab 	port->last_poll_msecs_diff = port->last_poll_msecs -
904b285192aSMauro Carvalho Chehab 		port->last_poll_msecs_diff;
905b285192aSMauro Carvalho Chehab 
906b285192aSMauro Carvalho Chehab 	saa7164_histogram_update(&port->poll_interval,
907b285192aSMauro Carvalho Chehab 		port->last_poll_msecs_diff);
908b285192aSMauro Carvalho Chehab 
909a9a08845SLinus Torvalds 	if (!(req_events & (EPOLLIN | EPOLLRDNORM)))
91045053edcSHans Verkuil 		return mask;
911b285192aSMauro Carvalho Chehab 
912b285192aSMauro Carvalho Chehab 	if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {
913b285192aSMauro Carvalho Chehab 		if (atomic_inc_return(&port->v4l_reader_count) == 1) {
914b285192aSMauro Carvalho Chehab 			if (saa7164_encoder_initialize(port) < 0)
915a9a08845SLinus Torvalds 				return mask | EPOLLERR;
916b285192aSMauro Carvalho Chehab 			saa7164_encoder_start_streaming(port);
917b285192aSMauro Carvalho Chehab 			msleep(200);
918b285192aSMauro Carvalho Chehab 		}
919b285192aSMauro Carvalho Chehab 	}
920b285192aSMauro Carvalho Chehab 
921b285192aSMauro Carvalho Chehab 	/* Pull the first buffer from the used list */
922b285192aSMauro Carvalho Chehab 	if (!list_empty(&port->list_buf_used.list))
923a9a08845SLinus Torvalds 		mask |= EPOLLIN | EPOLLRDNORM;
924b285192aSMauro Carvalho Chehab 
925b285192aSMauro Carvalho Chehab 	return mask;
926b285192aSMauro Carvalho Chehab }
927b285192aSMauro Carvalho Chehab 
9281a708ea0SHans Verkuil static const struct v4l2_ctrl_ops saa7164_ctrl_ops = {
9291a708ea0SHans Verkuil 	.s_ctrl = saa7164_s_ctrl,
9301a708ea0SHans Verkuil };
9311a708ea0SHans Verkuil 
932b285192aSMauro Carvalho Chehab static const struct v4l2_file_operations mpeg_fops = {
933b285192aSMauro Carvalho Chehab 	.owner		= THIS_MODULE,
934b285192aSMauro Carvalho Chehab 	.open		= fops_open,
935b285192aSMauro Carvalho Chehab 	.release	= fops_release,
936b285192aSMauro Carvalho Chehab 	.read		= fops_read,
937b285192aSMauro Carvalho Chehab 	.poll		= fops_poll,
938b285192aSMauro Carvalho Chehab 	.unlocked_ioctl	= video_ioctl2,
939b285192aSMauro Carvalho Chehab };
940b285192aSMauro Carvalho Chehab 
941b285192aSMauro Carvalho Chehab static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
942b285192aSMauro Carvalho Chehab 	.vidioc_s_std		 = vidioc_s_std,
9438d2d41e9SHans Verkuil 	.vidioc_g_std		 = vidioc_g_std,
944225b783bSHans Verkuil 	.vidioc_enum_input	 = saa7164_enum_input,
945b285192aSMauro Carvalho Chehab 	.vidioc_g_input		 = vidioc_g_input,
946b285192aSMauro Carvalho Chehab 	.vidioc_s_input		 = vidioc_s_input,
947225b783bSHans Verkuil 	.vidioc_g_tuner		 = saa7164_g_tuner,
948225b783bSHans Verkuil 	.vidioc_s_tuner		 = saa7164_s_tuner,
949b285192aSMauro Carvalho Chehab 	.vidioc_g_frequency	 = vidioc_g_frequency,
950b285192aSMauro Carvalho Chehab 	.vidioc_s_frequency	 = vidioc_s_frequency,
951b285192aSMauro Carvalho Chehab 	.vidioc_querycap	 = vidioc_querycap,
952b285192aSMauro Carvalho Chehab 	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
953031d2297SHans Verkuil 	.vidioc_g_fmt_vid_cap	 = vidioc_fmt_vid_cap,
954031d2297SHans Verkuil 	.vidioc_try_fmt_vid_cap	 = vidioc_fmt_vid_cap,
955031d2297SHans Verkuil 	.vidioc_s_fmt_vid_cap	 = vidioc_fmt_vid_cap,
956245b5ae9SHans Verkuil 	.vidioc_log_status	 = v4l2_ctrl_log_status,
957245b5ae9SHans Verkuil 	.vidioc_subscribe_event  = v4l2_ctrl_subscribe_event,
958245b5ae9SHans Verkuil 	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
959b285192aSMauro Carvalho Chehab };
960b285192aSMauro Carvalho Chehab 
961b285192aSMauro Carvalho Chehab static struct video_device saa7164_mpeg_template = {
962b285192aSMauro Carvalho Chehab 	.name          = "saa7164",
963b285192aSMauro Carvalho Chehab 	.fops          = &mpeg_fops,
964b285192aSMauro Carvalho Chehab 	.ioctl_ops     = &mpeg_ioctl_ops,
965b285192aSMauro Carvalho Chehab 	.minor         = -1,
966b285192aSMauro Carvalho Chehab 	.tvnorms       = SAA7164_NORMS,
96721615365SHans Verkuil 	.device_caps   = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE |
96821615365SHans Verkuil 			 V4L2_CAP_TUNER,
969b285192aSMauro Carvalho Chehab };
970b285192aSMauro Carvalho Chehab 
saa7164_encoder_alloc(struct saa7164_port * port,struct pci_dev * pci,struct video_device * template,char * type)971b285192aSMauro Carvalho Chehab static struct video_device *saa7164_encoder_alloc(
972b285192aSMauro Carvalho Chehab 	struct saa7164_port *port,
973b285192aSMauro Carvalho Chehab 	struct pci_dev *pci,
974b285192aSMauro Carvalho Chehab 	struct video_device *template,
975b285192aSMauro Carvalho Chehab 	char *type)
976b285192aSMauro Carvalho Chehab {
977b285192aSMauro Carvalho Chehab 	struct video_device *vfd;
978b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
979b285192aSMauro Carvalho Chehab 
980b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s()\n", __func__);
981b285192aSMauro Carvalho Chehab 
982b285192aSMauro Carvalho Chehab 	vfd = video_device_alloc();
983b285192aSMauro Carvalho Chehab 	if (NULL == vfd)
984b285192aSMauro Carvalho Chehab 		return NULL;
985b285192aSMauro Carvalho Chehab 
986b285192aSMauro Carvalho Chehab 	*vfd = *template;
987b285192aSMauro Carvalho Chehab 	snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name,
988b285192aSMauro Carvalho Chehab 		type, saa7164_boards[dev->board].name);
989b285192aSMauro Carvalho Chehab 
990d66de790SHans Verkuil 	vfd->v4l2_dev  = &dev->v4l2_dev;
991b285192aSMauro Carvalho Chehab 	vfd->release = video_device_release;
992b285192aSMauro Carvalho Chehab 	return vfd;
993b285192aSMauro Carvalho Chehab }
994b285192aSMauro Carvalho Chehab 
saa7164_encoder_register(struct saa7164_port * port)995b285192aSMauro Carvalho Chehab int saa7164_encoder_register(struct saa7164_port *port)
996b285192aSMauro Carvalho Chehab {
997b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
9981a708ea0SHans Verkuil 	struct v4l2_ctrl_handler *hdl = &port->ctrl_handler;
999b285192aSMauro Carvalho Chehab 	int result = -ENODEV;
1000b285192aSMauro Carvalho Chehab 
1001b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s()\n", __func__);
1002b285192aSMauro Carvalho Chehab 
10032aefee05SAmitoj Kaur Chawla 	BUG_ON(port->type != SAA7164_MPEG_ENCODER);
1004b285192aSMauro Carvalho Chehab 
1005b285192aSMauro Carvalho Chehab 	/* Sanity check that the PCI configuration space is active */
1006b285192aSMauro Carvalho Chehab 	if (port->hwcfg.BARLocation == 0) {
100724f711c1SMauro Carvalho Chehab 		printk(KERN_ERR "%s() failed (errno = %d), NO PCI configuration\n",
1008b285192aSMauro Carvalho Chehab 			__func__, result);
1009b285192aSMauro Carvalho Chehab 		result = -ENOMEM;
1010c759b297SDaniel Niv 		goto fail_pci;
1011b285192aSMauro Carvalho Chehab 	}
1012b285192aSMauro Carvalho Chehab 
1013b285192aSMauro Carvalho Chehab 	/* Establish encoder defaults here */
1014b285192aSMauro Carvalho Chehab 	/* Set default TV standard */
1015b285192aSMauro Carvalho Chehab 	port->encodernorm = saa7164_tvnorms[0];
1016b285192aSMauro Carvalho Chehab 	port->width = 720;
1017b285192aSMauro Carvalho Chehab 	port->mux_input = 1; /* Composite */
1018b285192aSMauro Carvalho Chehab 	port->video_format = EU_VIDEO_FORMAT_MPEG_2;
1019b285192aSMauro Carvalho Chehab 	port->audio_format = 0;
1020b285192aSMauro Carvalho Chehab 	port->video_resolution = 0;
10216b996126SHans Verkuil 	port->freq = SAA7164_TV_MIN_FREQ;
10221a708ea0SHans Verkuil 
10231a708ea0SHans Verkuil 	v4l2_ctrl_handler_init(hdl, 14);
10241a708ea0SHans Verkuil 	v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
10251a708ea0SHans Verkuil 			  V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
10261a708ea0SHans Verkuil 	v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
10271a708ea0SHans Verkuil 			  V4L2_CID_CONTRAST, 0, 255, 1, 66);
10281a708ea0SHans Verkuil 	v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
10291a708ea0SHans Verkuil 			  V4L2_CID_SATURATION, 0, 255, 1, 62);
10301a708ea0SHans Verkuil 	v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
10311a708ea0SHans Verkuil 			  V4L2_CID_HUE, 0, 255, 1, 128);
10321a708ea0SHans Verkuil 	v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
10331a708ea0SHans Verkuil 			  V4L2_CID_SHARPNESS, 0x0, 0x0f, 1, 8);
10341a708ea0SHans Verkuil 	v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
10351a708ea0SHans Verkuil 			  V4L2_CID_MPEG_AUDIO_MUTE, 0x0, 0x01, 1, 0);
10361a708ea0SHans Verkuil 	v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
10371a708ea0SHans Verkuil 			  V4L2_CID_AUDIO_VOLUME, -83, 24, 1, 20);
10381a708ea0SHans Verkuil 	v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
10391a708ea0SHans Verkuil 			  V4L2_CID_MPEG_VIDEO_BITRATE,
10401a708ea0SHans Verkuil 			  ENCODER_MIN_BITRATE, ENCODER_MAX_BITRATE,
10411a708ea0SHans Verkuil 			  100000, ENCODER_DEF_BITRATE);
10421a708ea0SHans Verkuil 	v4l2_ctrl_new_std_menu(hdl, &saa7164_ctrl_ops,
10431a708ea0SHans Verkuil 			       V4L2_CID_MPEG_STREAM_TYPE,
10441a708ea0SHans Verkuil 			       V4L2_MPEG_STREAM_TYPE_MPEG2_TS, 0,
10451a708ea0SHans Verkuil 			       V4L2_MPEG_STREAM_TYPE_MPEG2_PS);
10461a708ea0SHans Verkuil 	v4l2_ctrl_new_std_menu(hdl, &saa7164_ctrl_ops,
10471a708ea0SHans Verkuil 			       V4L2_CID_MPEG_VIDEO_ASPECT,
10481a708ea0SHans Verkuil 			       V4L2_MPEG_VIDEO_ASPECT_221x100, 0,
10491a708ea0SHans Verkuil 			       V4L2_MPEG_VIDEO_ASPECT_4x3);
10501a708ea0SHans Verkuil 	v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
10511a708ea0SHans Verkuil 			  V4L2_CID_MPEG_VIDEO_GOP_SIZE, 1, 255, 1, 15);
10521a708ea0SHans Verkuil 	v4l2_ctrl_new_std_menu(hdl, &saa7164_ctrl_ops,
10531a708ea0SHans Verkuil 			       V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
10541a708ea0SHans Verkuil 			       V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0,
10551a708ea0SHans Verkuil 			       V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
10561a708ea0SHans Verkuil 	v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
10571a708ea0SHans Verkuil 			  V4L2_CID_MPEG_VIDEO_B_FRAMES, 1, 3, 1, 1);
10581a708ea0SHans Verkuil 	v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
10591a708ea0SHans Verkuil 			  V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
10601a708ea0SHans Verkuil 			  ENCODER_MIN_BITRATE, ENCODER_MAX_BITRATE,
10611a708ea0SHans Verkuil 			  100000, ENCODER_DEF_BITRATE);
10621a708ea0SHans Verkuil 	if (hdl->error) {
10631a708ea0SHans Verkuil 		result = hdl->error;
1064c759b297SDaniel Niv 		goto fail_hdl;
10651a708ea0SHans Verkuil 	}
10661a708ea0SHans Verkuil 
10678d2d41e9SHans Verkuil 	port->std = V4L2_STD_NTSC_M;
1068b285192aSMauro Carvalho Chehab 
1069b285192aSMauro Carvalho Chehab 	if (port->encodernorm.id & V4L2_STD_525_60)
1070b285192aSMauro Carvalho Chehab 		port->height = 480;
1071b285192aSMauro Carvalho Chehab 	else
1072b285192aSMauro Carvalho Chehab 		port->height = 576;
1073b285192aSMauro Carvalho Chehab 
1074b285192aSMauro Carvalho Chehab 	/* Allocate and register the video device node */
1075b285192aSMauro Carvalho Chehab 	port->v4l_device = saa7164_encoder_alloc(port,
1076b285192aSMauro Carvalho Chehab 		dev->pci, &saa7164_mpeg_template, "mpeg");
1077b285192aSMauro Carvalho Chehab 
1078b285192aSMauro Carvalho Chehab 	if (!port->v4l_device) {
1079b285192aSMauro Carvalho Chehab 		printk(KERN_INFO "%s: can't allocate mpeg device\n",
1080b285192aSMauro Carvalho Chehab 			dev->name);
1081b285192aSMauro Carvalho Chehab 		result = -ENOMEM;
1082c759b297SDaniel Niv 		goto fail_hdl;
1083b285192aSMauro Carvalho Chehab 	}
1084b285192aSMauro Carvalho Chehab 
10851a708ea0SHans Verkuil 	port->v4l_device->ctrl_handler = hdl;
10861a708ea0SHans Verkuil 	v4l2_ctrl_handler_setup(hdl);
1087b285192aSMauro Carvalho Chehab 	video_set_drvdata(port->v4l_device, port);
1088b285192aSMauro Carvalho Chehab 	result = video_register_device(port->v4l_device,
10893e30a927SHans Verkuil 		VFL_TYPE_VIDEO, -1);
1090b285192aSMauro Carvalho Chehab 	if (result < 0) {
1091b285192aSMauro Carvalho Chehab 		printk(KERN_INFO "%s: can't register mpeg device\n",
1092b285192aSMauro Carvalho Chehab 			dev->name);
1093c759b297SDaniel Niv 		goto fail_reg;
1094b285192aSMauro Carvalho Chehab 	}
1095b285192aSMauro Carvalho Chehab 
1096b285192aSMauro Carvalho Chehab 	printk(KERN_INFO "%s: registered device video%d [mpeg]\n",
1097b285192aSMauro Carvalho Chehab 		dev->name, port->v4l_device->num);
1098b285192aSMauro Carvalho Chehab 
1099b285192aSMauro Carvalho Chehab 	/* Configure the hardware defaults */
1100b285192aSMauro Carvalho Chehab 	saa7164_api_set_videomux(port);
1101b285192aSMauro Carvalho Chehab 	saa7164_api_set_usercontrol(port, PU_BRIGHTNESS_CONTROL);
1102b285192aSMauro Carvalho Chehab 	saa7164_api_set_usercontrol(port, PU_CONTRAST_CONTROL);
1103b285192aSMauro Carvalho Chehab 	saa7164_api_set_usercontrol(port, PU_HUE_CONTROL);
1104b285192aSMauro Carvalho Chehab 	saa7164_api_set_usercontrol(port, PU_SATURATION_CONTROL);
1105b285192aSMauro Carvalho Chehab 	saa7164_api_set_usercontrol(port, PU_SHARPNESS_CONTROL);
1106b285192aSMauro Carvalho Chehab 	saa7164_api_audio_mute(port, 0);
1107b285192aSMauro Carvalho Chehab 	saa7164_api_set_audio_volume(port, 20);
1108b285192aSMauro Carvalho Chehab 	saa7164_api_set_aspect_ratio(port);
1109b285192aSMauro Carvalho Chehab 
1110b285192aSMauro Carvalho Chehab 	/* Disable audio standard detection, it's buggy */
1111b285192aSMauro Carvalho Chehab 	saa7164_api_set_audio_detection(port, 0);
1112b285192aSMauro Carvalho Chehab 
1113b285192aSMauro Carvalho Chehab 	saa7164_api_set_encoder(port);
1114b285192aSMauro Carvalho Chehab 	saa7164_api_get_encoder(port);
1115c759b297SDaniel Niv 	return 0;
1116b285192aSMauro Carvalho Chehab 
1117c759b297SDaniel Niv fail_reg:
1118c759b297SDaniel Niv 	video_device_release(port->v4l_device);
1119c759b297SDaniel Niv 	port->v4l_device = NULL;
1120c759b297SDaniel Niv fail_hdl:
1121c759b297SDaniel Niv 	v4l2_ctrl_handler_free(hdl);
1122c759b297SDaniel Niv fail_pci:
1123b285192aSMauro Carvalho Chehab 	return result;
1124b285192aSMauro Carvalho Chehab }
1125b285192aSMauro Carvalho Chehab 
saa7164_encoder_unregister(struct saa7164_port * port)1126b285192aSMauro Carvalho Chehab void saa7164_encoder_unregister(struct saa7164_port *port)
1127b285192aSMauro Carvalho Chehab {
1128b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
1129b285192aSMauro Carvalho Chehab 
1130b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s(port=%d)\n", __func__, port->nr);
1131b285192aSMauro Carvalho Chehab 
11322aefee05SAmitoj Kaur Chawla 	BUG_ON(port->type != SAA7164_MPEG_ENCODER);
1133b285192aSMauro Carvalho Chehab 
1134b285192aSMauro Carvalho Chehab 	if (port->v4l_device) {
1135b285192aSMauro Carvalho Chehab 		if (port->v4l_device->minor != -1)
1136b285192aSMauro Carvalho Chehab 			video_unregister_device(port->v4l_device);
1137b285192aSMauro Carvalho Chehab 		else
1138b285192aSMauro Carvalho Chehab 			video_device_release(port->v4l_device);
1139b285192aSMauro Carvalho Chehab 
1140b285192aSMauro Carvalho Chehab 		port->v4l_device = NULL;
1141b285192aSMauro Carvalho Chehab 	}
11421a708ea0SHans Verkuil 	v4l2_ctrl_handler_free(&port->ctrl_handler);
1143b285192aSMauro Carvalho Chehab 
1144b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s(port=%d) done\n", __func__, port->nr);
1145b285192aSMauro Carvalho Chehab }
1146b285192aSMauro Carvalho Chehab 
1147