1b285192aSMauro Carvalho Chehab /*
2b285192aSMauro Carvalho Chehab  *  Driver for the NXP SAA7164 PCIe bridge
3b285192aSMauro Carvalho Chehab  *
463a412ecSSteven Toth  *  Copyright (c) 2010-2015 Steven Toth <stoth@kernellabs.com>
5b285192aSMauro Carvalho Chehab  *
6b285192aSMauro Carvalho Chehab  *  This program is free software; you can redistribute it and/or modify
7b285192aSMauro Carvalho Chehab  *  it under the terms of the GNU General Public License as published by
8b285192aSMauro Carvalho Chehab  *  the Free Software Foundation; either version 2 of the License, or
9b285192aSMauro Carvalho Chehab  *  (at your option) any later version.
10b285192aSMauro Carvalho Chehab  *
11b285192aSMauro Carvalho Chehab  *  This program is distributed in the hope that it will be useful,
12b285192aSMauro Carvalho Chehab  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13b285192aSMauro Carvalho Chehab  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14b285192aSMauro Carvalho Chehab  *
15b285192aSMauro Carvalho Chehab  *  GNU General Public License for more details.
16b285192aSMauro Carvalho Chehab  */
17b285192aSMauro Carvalho Chehab 
18b285192aSMauro Carvalho Chehab #include "saa7164.h"
19b285192aSMauro Carvalho Chehab 
20b285192aSMauro Carvalho Chehab #define ENCODER_MAX_BITRATE 6500000
21b285192aSMauro Carvalho Chehab #define ENCODER_MIN_BITRATE 1000000
22b285192aSMauro Carvalho Chehab #define ENCODER_DEF_BITRATE 5000000
23b285192aSMauro Carvalho Chehab 
24031d2297SHans Verkuil /*
25031d2297SHans Verkuil  * This is a dummy non-zero value for the sizeimage field of v4l2_pix_format.
26031d2297SHans Verkuil  * It is not actually used for anything since this driver does not support
27031d2297SHans Verkuil  * stream I/O, only read(), and because this driver produces an MPEG stream
28031d2297SHans Verkuil  * and not discrete frames. But the V4L2 spec doesn't allow for this value
29031d2297SHans Verkuil  * to be 0, so set it to 0x10000 instead.
30031d2297SHans Verkuil  *
31031d2297SHans Verkuil  * If we ever change this driver to support stream I/O, then this field
32031d2297SHans Verkuil  * will be the size of the streaming buffers.
33031d2297SHans Verkuil  */
34031d2297SHans Verkuil #define SAA7164_SIZEIMAGE (0x10000)
35031d2297SHans Verkuil 
36b285192aSMauro Carvalho Chehab static struct saa7164_tvnorm saa7164_tvnorms[] = {
37b285192aSMauro Carvalho Chehab 	{
38b285192aSMauro Carvalho Chehab 		.name      = "NTSC-M",
39b285192aSMauro Carvalho Chehab 		.id        = V4L2_STD_NTSC_M,
40b285192aSMauro Carvalho Chehab 	}, {
41b285192aSMauro Carvalho Chehab 		.name      = "NTSC-JP",
42b285192aSMauro Carvalho Chehab 		.id        = V4L2_STD_NTSC_M_JP,
43b285192aSMauro Carvalho Chehab 	}
44b285192aSMauro Carvalho Chehab };
45b285192aSMauro Carvalho Chehab 
46b285192aSMauro Carvalho Chehab /* Take the encoder configuration form the port struct and
47b285192aSMauro Carvalho Chehab  * flush it to the hardware.
48b285192aSMauro Carvalho Chehab  */
49b285192aSMauro Carvalho Chehab static void saa7164_encoder_configure(struct saa7164_port *port)
50b285192aSMauro Carvalho Chehab {
51b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
52b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s()\n", __func__);
53b285192aSMauro Carvalho Chehab 
54b285192aSMauro Carvalho Chehab 	port->encoder_params.width = port->width;
55b285192aSMauro Carvalho Chehab 	port->encoder_params.height = port->height;
56b285192aSMauro Carvalho Chehab 	port->encoder_params.is_50hz =
57b285192aSMauro Carvalho Chehab 		(port->encodernorm.id & V4L2_STD_625_50) != 0;
58b285192aSMauro Carvalho Chehab 
59b285192aSMauro Carvalho Chehab 	/* Set up the DIF (enable it) for analog mode by default */
60b285192aSMauro Carvalho Chehab 	saa7164_api_initialize_dif(port);
61b285192aSMauro Carvalho Chehab 
62b285192aSMauro Carvalho Chehab 	/* Configure the correct video standard */
63b285192aSMauro Carvalho Chehab 	saa7164_api_configure_dif(port, port->encodernorm.id);
64b285192aSMauro Carvalho Chehab 
65b285192aSMauro Carvalho Chehab 	/* Ensure the audio decoder is correct configured */
66b285192aSMauro Carvalho Chehab 	saa7164_api_set_audio_std(port);
67b285192aSMauro Carvalho Chehab }
68b285192aSMauro Carvalho Chehab 
69b285192aSMauro Carvalho Chehab static int saa7164_encoder_buffers_dealloc(struct saa7164_port *port)
70b285192aSMauro Carvalho Chehab {
71b285192aSMauro Carvalho Chehab 	struct list_head *c, *n, *p, *q, *l, *v;
72b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
73b285192aSMauro Carvalho Chehab 	struct saa7164_buffer *buf;
74b285192aSMauro Carvalho Chehab 	struct saa7164_user_buffer *ubuf;
75b285192aSMauro Carvalho Chehab 
76b285192aSMauro Carvalho Chehab 	/* Remove any allocated buffers */
77b285192aSMauro Carvalho Chehab 	mutex_lock(&port->dmaqueue_lock);
78b285192aSMauro Carvalho Chehab 
79b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s(port=%d) dmaqueue\n", __func__, port->nr);
80b285192aSMauro Carvalho Chehab 	list_for_each_safe(c, n, &port->dmaqueue.list) {
81b285192aSMauro Carvalho Chehab 		buf = list_entry(c, struct saa7164_buffer, list);
82b285192aSMauro Carvalho Chehab 		list_del(c);
83b285192aSMauro Carvalho Chehab 		saa7164_buffer_dealloc(buf);
84b285192aSMauro Carvalho Chehab 	}
85b285192aSMauro Carvalho Chehab 
86b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s(port=%d) used\n", __func__, port->nr);
87b285192aSMauro Carvalho Chehab 	list_for_each_safe(p, q, &port->list_buf_used.list) {
88b285192aSMauro Carvalho Chehab 		ubuf = list_entry(p, struct saa7164_user_buffer, list);
89b285192aSMauro Carvalho Chehab 		list_del(p);
90b285192aSMauro Carvalho Chehab 		saa7164_buffer_dealloc_user(ubuf);
91b285192aSMauro Carvalho Chehab 	}
92b285192aSMauro Carvalho Chehab 
93b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s(port=%d) free\n", __func__, port->nr);
94b285192aSMauro Carvalho Chehab 	list_for_each_safe(l, v, &port->list_buf_free.list) {
95b285192aSMauro Carvalho Chehab 		ubuf = list_entry(l, struct saa7164_user_buffer, list);
96b285192aSMauro Carvalho Chehab 		list_del(l);
97b285192aSMauro Carvalho Chehab 		saa7164_buffer_dealloc_user(ubuf);
98b285192aSMauro Carvalho Chehab 	}
99b285192aSMauro Carvalho Chehab 
100b285192aSMauro Carvalho Chehab 	mutex_unlock(&port->dmaqueue_lock);
101b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s(port=%d) done\n", __func__, port->nr);
102b285192aSMauro Carvalho Chehab 
103b285192aSMauro Carvalho Chehab 	return 0;
104b285192aSMauro Carvalho Chehab }
105b285192aSMauro Carvalho Chehab 
106b285192aSMauro Carvalho Chehab /* Dynamic buffer switch at encoder start time */
107b285192aSMauro Carvalho Chehab static int saa7164_encoder_buffers_alloc(struct saa7164_port *port)
108b285192aSMauro Carvalho Chehab {
109b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
110b285192aSMauro Carvalho Chehab 	struct saa7164_buffer *buf;
111b285192aSMauro Carvalho Chehab 	struct saa7164_user_buffer *ubuf;
112b285192aSMauro Carvalho Chehab 	struct tmHWStreamParameters *params = &port->hw_streamingparams;
113b285192aSMauro Carvalho Chehab 	int result = -ENODEV, i;
114b285192aSMauro Carvalho Chehab 	int len = 0;
115b285192aSMauro Carvalho Chehab 
116b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s()\n", __func__);
117b285192aSMauro Carvalho Chehab 
118b285192aSMauro Carvalho Chehab 	if (port->encoder_params.stream_type ==
119b285192aSMauro Carvalho Chehab 		V4L2_MPEG_STREAM_TYPE_MPEG2_PS) {
120b285192aSMauro Carvalho Chehab 		dprintk(DBGLVL_ENC,
121b285192aSMauro Carvalho Chehab 			"%s() type=V4L2_MPEG_STREAM_TYPE_MPEG2_PS\n",
122b285192aSMauro Carvalho Chehab 			__func__);
123b285192aSMauro Carvalho Chehab 		params->samplesperline = 128;
124b285192aSMauro Carvalho Chehab 		params->numberoflines = 256;
125b285192aSMauro Carvalho Chehab 		params->pitch = 128;
126b285192aSMauro Carvalho Chehab 		params->numpagetables = 2 +
127b285192aSMauro Carvalho Chehab 			((SAA7164_PS_NUMBER_OF_LINES * 128) / PAGE_SIZE);
128b285192aSMauro Carvalho Chehab 	} else
129b285192aSMauro Carvalho Chehab 	if (port->encoder_params.stream_type ==
130b285192aSMauro Carvalho Chehab 		V4L2_MPEG_STREAM_TYPE_MPEG2_TS) {
131b285192aSMauro Carvalho Chehab 		dprintk(DBGLVL_ENC,
132b285192aSMauro Carvalho Chehab 			"%s() type=V4L2_MPEG_STREAM_TYPE_MPEG2_TS\n",
133b285192aSMauro Carvalho Chehab 			__func__);
134b285192aSMauro Carvalho Chehab 		params->samplesperline = 188;
135b285192aSMauro Carvalho Chehab 		params->numberoflines = 312;
136b285192aSMauro Carvalho Chehab 		params->pitch = 188;
137b285192aSMauro Carvalho Chehab 		params->numpagetables = 2 +
138b285192aSMauro Carvalho Chehab 			((SAA7164_TS_NUMBER_OF_LINES * 188) / PAGE_SIZE);
139b285192aSMauro Carvalho Chehab 	} else
140b285192aSMauro Carvalho Chehab 		BUG();
141b285192aSMauro Carvalho Chehab 
142b285192aSMauro Carvalho Chehab 	/* Init and establish defaults */
143b285192aSMauro Carvalho Chehab 	params->bitspersample = 8;
144b285192aSMauro Carvalho Chehab 	params->linethreshold = 0;
145b285192aSMauro Carvalho Chehab 	params->pagetablelistvirt = NULL;
146b285192aSMauro Carvalho Chehab 	params->pagetablelistphys = NULL;
147b285192aSMauro Carvalho Chehab 	params->numpagetableentries = port->hwcfg.buffercount;
148b285192aSMauro Carvalho Chehab 
149b285192aSMauro Carvalho Chehab 	/* Allocate the PCI resources, buffers (hard) */
150b285192aSMauro Carvalho Chehab 	for (i = 0; i < port->hwcfg.buffercount; i++) {
151b285192aSMauro Carvalho Chehab 		buf = saa7164_buffer_alloc(port,
152b285192aSMauro Carvalho Chehab 			params->numberoflines *
153b285192aSMauro Carvalho Chehab 			params->pitch);
154b285192aSMauro Carvalho Chehab 
155b285192aSMauro Carvalho Chehab 		if (!buf) {
15624f711c1SMauro Carvalho Chehab 			printk(KERN_ERR "%s() failed (errno = %d), unable to allocate buffer\n",
157b285192aSMauro Carvalho Chehab 				__func__, result);
158b285192aSMauro Carvalho Chehab 			result = -ENOMEM;
159b285192aSMauro Carvalho Chehab 			goto failed;
160b285192aSMauro Carvalho Chehab 		} else {
161b285192aSMauro Carvalho Chehab 
162b285192aSMauro Carvalho Chehab 			mutex_lock(&port->dmaqueue_lock);
163b285192aSMauro Carvalho Chehab 			list_add_tail(&buf->list, &port->dmaqueue.list);
164b285192aSMauro Carvalho Chehab 			mutex_unlock(&port->dmaqueue_lock);
165b285192aSMauro Carvalho Chehab 
166b285192aSMauro Carvalho Chehab 		}
167b285192aSMauro Carvalho Chehab 	}
168b285192aSMauro Carvalho Chehab 
169b285192aSMauro Carvalho Chehab 	/* Allocate some kernel buffers for copying
170b285192aSMauro Carvalho Chehab 	 * to userpsace.
171b285192aSMauro Carvalho Chehab 	 */
172b285192aSMauro Carvalho Chehab 	len = params->numberoflines * params->pitch;
173b285192aSMauro Carvalho Chehab 
174b285192aSMauro Carvalho Chehab 	if (encoder_buffers < 16)
175b285192aSMauro Carvalho Chehab 		encoder_buffers = 16;
176b285192aSMauro Carvalho Chehab 	if (encoder_buffers > 512)
177b285192aSMauro Carvalho Chehab 		encoder_buffers = 512;
178b285192aSMauro Carvalho Chehab 
179b285192aSMauro Carvalho Chehab 	for (i = 0; i < encoder_buffers; i++) {
180b285192aSMauro Carvalho Chehab 
181b285192aSMauro Carvalho Chehab 		ubuf = saa7164_buffer_alloc_user(dev, len);
182b285192aSMauro Carvalho Chehab 		if (ubuf) {
183b285192aSMauro Carvalho Chehab 			mutex_lock(&port->dmaqueue_lock);
184b285192aSMauro Carvalho Chehab 			list_add_tail(&ubuf->list, &port->list_buf_free.list);
185b285192aSMauro Carvalho Chehab 			mutex_unlock(&port->dmaqueue_lock);
186b285192aSMauro Carvalho Chehab 		}
187b285192aSMauro Carvalho Chehab 
188b285192aSMauro Carvalho Chehab 	}
189b285192aSMauro Carvalho Chehab 
190b285192aSMauro Carvalho Chehab 	result = 0;
191b285192aSMauro Carvalho Chehab 
192b285192aSMauro Carvalho Chehab failed:
193b285192aSMauro Carvalho Chehab 	return result;
194b285192aSMauro Carvalho Chehab }
195b285192aSMauro Carvalho Chehab 
196b285192aSMauro Carvalho Chehab static int saa7164_encoder_initialize(struct saa7164_port *port)
197b285192aSMauro Carvalho Chehab {
198b285192aSMauro Carvalho Chehab 	saa7164_encoder_configure(port);
199b285192aSMauro Carvalho Chehab 	return 0;
200b285192aSMauro Carvalho Chehab }
201b285192aSMauro Carvalho Chehab 
202b285192aSMauro Carvalho Chehab /* -- V4L2 --------------------------------------------------------- */
203225b783bSHans Verkuil int saa7164_s_std(struct saa7164_port *port, v4l2_std_id id)
204b285192aSMauro Carvalho Chehab {
205b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
206b285192aSMauro Carvalho Chehab 	unsigned int i;
207b285192aSMauro Carvalho Chehab 
208314527acSHans Verkuil 	dprintk(DBGLVL_ENC, "%s(id=0x%x)\n", __func__, (u32)id);
209b285192aSMauro Carvalho Chehab 
210b285192aSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(saa7164_tvnorms); i++) {
211314527acSHans Verkuil 		if (id & saa7164_tvnorms[i].id)
212b285192aSMauro Carvalho Chehab 			break;
213b285192aSMauro Carvalho Chehab 	}
214b285192aSMauro Carvalho Chehab 	if (i == ARRAY_SIZE(saa7164_tvnorms))
215b285192aSMauro Carvalho Chehab 		return -EINVAL;
216b285192aSMauro Carvalho Chehab 
217b285192aSMauro Carvalho Chehab 	port->encodernorm = saa7164_tvnorms[i];
2188d2d41e9SHans Verkuil 	port->std = id;
219b285192aSMauro Carvalho Chehab 
220b285192aSMauro Carvalho Chehab 	/* Update the audio decoder while is not running in
221b285192aSMauro Carvalho Chehab 	 * auto detect mode.
222b285192aSMauro Carvalho Chehab 	 */
223b285192aSMauro Carvalho Chehab 	saa7164_api_set_audio_std(port);
224b285192aSMauro Carvalho Chehab 
225314527acSHans Verkuil 	dprintk(DBGLVL_ENC, "%s(id=0x%x) OK\n", __func__, (u32)id);
226b285192aSMauro Carvalho Chehab 
227b285192aSMauro Carvalho Chehab 	return 0;
228b285192aSMauro Carvalho Chehab }
229b285192aSMauro Carvalho Chehab 
230225b783bSHans Verkuil static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id id)
2318d2d41e9SHans Verkuil {
2328d2d41e9SHans Verkuil 	struct saa7164_encoder_fh *fh = file->private_data;
2338d2d41e9SHans Verkuil 
234225b783bSHans Verkuil 	return saa7164_s_std(fh->port, id);
235225b783bSHans Verkuil }
236225b783bSHans Verkuil 
237225b783bSHans Verkuil int saa7164_g_std(struct saa7164_port *port, v4l2_std_id *id)
238225b783bSHans Verkuil {
2398d2d41e9SHans Verkuil 	*id = port->std;
2408d2d41e9SHans Verkuil 	return 0;
2418d2d41e9SHans Verkuil }
2428d2d41e9SHans Verkuil 
243225b783bSHans Verkuil static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *id)
244225b783bSHans Verkuil {
245225b783bSHans Verkuil 	struct saa7164_encoder_fh *fh = file->private_data;
246225b783bSHans Verkuil 
247225b783bSHans Verkuil 	return saa7164_g_std(fh->port, id);
248225b783bSHans Verkuil }
249225b783bSHans Verkuil 
250225b783bSHans Verkuil int saa7164_enum_input(struct file *file, void *priv, struct v4l2_input *i)
251b285192aSMauro Carvalho Chehab {
2526b996126SHans Verkuil 	static const char * const inputs[] = {
2536b996126SHans Verkuil 		"tuner", "composite", "svideo", "aux",
2546b996126SHans Verkuil 		"composite 2", "svideo 2", "aux 2"
2556b996126SHans Verkuil 	};
256b285192aSMauro Carvalho Chehab 	int n;
257b285192aSMauro Carvalho Chehab 
258b285192aSMauro Carvalho Chehab 	if (i->index >= 7)
259b285192aSMauro Carvalho Chehab 		return -EINVAL;
260b285192aSMauro Carvalho Chehab 
261b285192aSMauro Carvalho Chehab 	strcpy(i->name, inputs[i->index]);
262b285192aSMauro Carvalho Chehab 
263b285192aSMauro Carvalho Chehab 	if (i->index == 0)
264b285192aSMauro Carvalho Chehab 		i->type = V4L2_INPUT_TYPE_TUNER;
265b285192aSMauro Carvalho Chehab 	else
266b285192aSMauro Carvalho Chehab 		i->type  = V4L2_INPUT_TYPE_CAMERA;
267b285192aSMauro Carvalho Chehab 
268b285192aSMauro Carvalho Chehab 	for (n = 0; n < ARRAY_SIZE(saa7164_tvnorms); n++)
269b285192aSMauro Carvalho Chehab 		i->std |= saa7164_tvnorms[n].id;
270b285192aSMauro Carvalho Chehab 
271b285192aSMauro Carvalho Chehab 	return 0;
272b285192aSMauro Carvalho Chehab }
273b285192aSMauro Carvalho Chehab 
274225b783bSHans Verkuil int saa7164_g_input(struct saa7164_port *port, unsigned int *i)
275b285192aSMauro Carvalho Chehab {
276b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
277b285192aSMauro Carvalho Chehab 
278b285192aSMauro Carvalho Chehab 	if (saa7164_api_get_videomux(port) != SAA_OK)
279b285192aSMauro Carvalho Chehab 		return -EIO;
280b285192aSMauro Carvalho Chehab 
281b285192aSMauro Carvalho Chehab 	*i = (port->mux_input - 1);
282b285192aSMauro Carvalho Chehab 
283b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s() input=%d\n", __func__, *i);
284b285192aSMauro Carvalho Chehab 
285b285192aSMauro Carvalho Chehab 	return 0;
286b285192aSMauro Carvalho Chehab }
287b285192aSMauro Carvalho Chehab 
288225b783bSHans Verkuil static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
289b285192aSMauro Carvalho Chehab {
290b285192aSMauro Carvalho Chehab 	struct saa7164_encoder_fh *fh = file->private_data;
291225b783bSHans Verkuil 
292225b783bSHans Verkuil 	return saa7164_g_input(fh->port, i);
293225b783bSHans Verkuil }
294225b783bSHans Verkuil 
295225b783bSHans Verkuil int saa7164_s_input(struct saa7164_port *port, unsigned int i)
296225b783bSHans Verkuil {
297b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
298b285192aSMauro Carvalho Chehab 
299b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s() input=%d\n", __func__, i);
300b285192aSMauro Carvalho Chehab 
301b285192aSMauro Carvalho Chehab 	if (i >= 7)
302b285192aSMauro Carvalho Chehab 		return -EINVAL;
303b285192aSMauro Carvalho Chehab 
304b285192aSMauro Carvalho Chehab 	port->mux_input = i + 1;
305b285192aSMauro Carvalho Chehab 
306b285192aSMauro Carvalho Chehab 	if (saa7164_api_set_videomux(port) != SAA_OK)
307b285192aSMauro Carvalho Chehab 		return -EIO;
308b285192aSMauro Carvalho Chehab 
309b285192aSMauro Carvalho Chehab 	return 0;
310b285192aSMauro Carvalho Chehab }
311b285192aSMauro Carvalho Chehab 
312225b783bSHans Verkuil static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
313225b783bSHans Verkuil {
314225b783bSHans Verkuil 	struct saa7164_encoder_fh *fh = file->private_data;
315225b783bSHans Verkuil 
316225b783bSHans Verkuil 	return saa7164_s_input(fh->port, i);
317225b783bSHans Verkuil }
318225b783bSHans Verkuil 
319225b783bSHans Verkuil int saa7164_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
320b285192aSMauro Carvalho Chehab {
321b285192aSMauro Carvalho Chehab 	struct saa7164_encoder_fh *fh = file->private_data;
322b285192aSMauro Carvalho Chehab 	struct saa7164_port *port = fh->port;
323b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
324b285192aSMauro Carvalho Chehab 
325b285192aSMauro Carvalho Chehab 	if (0 != t->index)
326b285192aSMauro Carvalho Chehab 		return -EINVAL;
327b285192aSMauro Carvalho Chehab 
328b285192aSMauro Carvalho Chehab 	strcpy(t->name, "tuner");
329b285192aSMauro Carvalho Chehab 	t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO;
3306b996126SHans Verkuil 	t->rangelow = SAA7164_TV_MIN_FREQ;
3316b996126SHans Verkuil 	t->rangehigh = SAA7164_TV_MAX_FREQ;
332b285192aSMauro Carvalho Chehab 
333b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "VIDIOC_G_TUNER: tuner type %d\n", t->type);
334b285192aSMauro Carvalho Chehab 
335b285192aSMauro Carvalho Chehab 	return 0;
336b285192aSMauro Carvalho Chehab }
337b285192aSMauro Carvalho Chehab 
338225b783bSHans Verkuil int saa7164_s_tuner(struct file *file, void *priv,
3392f73c7c5SHans Verkuil 			   const struct v4l2_tuner *t)
340b285192aSMauro Carvalho Chehab {
3416b996126SHans Verkuil 	if (0 != t->index)
3426b996126SHans Verkuil 		return -EINVAL;
3436b996126SHans Verkuil 
344b285192aSMauro Carvalho Chehab 	/* Update the A/V core */
345b285192aSMauro Carvalho Chehab 	return 0;
346b285192aSMauro Carvalho Chehab }
347b285192aSMauro Carvalho Chehab 
348225b783bSHans Verkuil int saa7164_g_frequency(struct saa7164_port *port, struct v4l2_frequency *f)
349b285192aSMauro Carvalho Chehab {
3506b996126SHans Verkuil 	if (f->tuner)
3516b996126SHans Verkuil 		return -EINVAL;
3526b996126SHans Verkuil 
353b285192aSMauro Carvalho Chehab 	f->frequency = port->freq;
354b285192aSMauro Carvalho Chehab 	return 0;
355b285192aSMauro Carvalho Chehab }
356b285192aSMauro Carvalho Chehab 
357225b783bSHans Verkuil static int vidioc_g_frequency(struct file *file, void *priv,
358225b783bSHans Verkuil 	struct v4l2_frequency *f)
359b285192aSMauro Carvalho Chehab {
360b285192aSMauro Carvalho Chehab 	struct saa7164_encoder_fh *fh = file->private_data;
361225b783bSHans Verkuil 
362225b783bSHans Verkuil 	return saa7164_g_frequency(fh->port, f);
363225b783bSHans Verkuil }
364225b783bSHans Verkuil 
365225b783bSHans Verkuil int saa7164_s_frequency(struct saa7164_port *port,
366225b783bSHans Verkuil 			const struct v4l2_frequency *f)
367225b783bSHans Verkuil {
368b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
369b285192aSMauro Carvalho Chehab 	struct saa7164_port *tsport;
370b285192aSMauro Carvalho Chehab 	struct dvb_frontend *fe;
371b285192aSMauro Carvalho Chehab 
372b285192aSMauro Carvalho Chehab 	/* TODO: Pull this for the std */
373b285192aSMauro Carvalho Chehab 	struct analog_parameters params = {
374b285192aSMauro Carvalho Chehab 		.mode      = V4L2_TUNER_ANALOG_TV,
375b285192aSMauro Carvalho Chehab 		.audmode   = V4L2_TUNER_MODE_STEREO,
376b285192aSMauro Carvalho Chehab 		.std       = port->encodernorm.id,
377b285192aSMauro Carvalho Chehab 		.frequency = f->frequency
378b285192aSMauro Carvalho Chehab 	};
379b285192aSMauro Carvalho Chehab 
380b285192aSMauro Carvalho Chehab 	/* Stop the encoder */
381b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s() frequency=%d tuner=%d\n", __func__,
382b285192aSMauro Carvalho Chehab 		f->frequency, f->tuner);
383b285192aSMauro Carvalho Chehab 
384b285192aSMauro Carvalho Chehab 	if (f->tuner != 0)
385b285192aSMauro Carvalho Chehab 		return -EINVAL;
386b285192aSMauro Carvalho Chehab 
3876b996126SHans Verkuil 	port->freq = clamp(f->frequency,
3886b996126SHans Verkuil 			   SAA7164_TV_MIN_FREQ, SAA7164_TV_MAX_FREQ);
389b285192aSMauro Carvalho Chehab 
390b285192aSMauro Carvalho Chehab 	/* Update the hardware */
391b285192aSMauro Carvalho Chehab 	if (port->nr == SAA7164_PORT_ENC1)
392b285192aSMauro Carvalho Chehab 		tsport = &dev->ports[SAA7164_PORT_TS1];
393225b783bSHans Verkuil 	else if (port->nr == SAA7164_PORT_ENC2)
394b285192aSMauro Carvalho Chehab 		tsport = &dev->ports[SAA7164_PORT_TS2];
395b285192aSMauro Carvalho Chehab 	else
396b285192aSMauro Carvalho Chehab 		BUG();
397b285192aSMauro Carvalho Chehab 
398b285192aSMauro Carvalho Chehab 	fe = tsport->dvb.frontend;
399b285192aSMauro Carvalho Chehab 
400b285192aSMauro Carvalho Chehab 	if (fe && fe->ops.tuner_ops.set_analog_params)
401b285192aSMauro Carvalho Chehab 		fe->ops.tuner_ops.set_analog_params(fe, &params);
402b285192aSMauro Carvalho Chehab 	else
403b285192aSMauro Carvalho Chehab 		printk(KERN_ERR "%s() No analog tuner, aborting\n", __func__);
404b285192aSMauro Carvalho Chehab 
405b285192aSMauro Carvalho Chehab 	saa7164_encoder_initialize(port);
406b285192aSMauro Carvalho Chehab 
407b285192aSMauro Carvalho Chehab 	return 0;
408b285192aSMauro Carvalho Chehab }
409b285192aSMauro Carvalho Chehab 
410225b783bSHans Verkuil static int vidioc_s_frequency(struct file *file, void *priv,
411225b783bSHans Verkuil 			      const struct v4l2_frequency *f)
412225b783bSHans Verkuil {
413225b783bSHans Verkuil 	struct saa7164_encoder_fh *fh = file->private_data;
414225b783bSHans Verkuil 
415225b783bSHans Verkuil 	return saa7164_s_frequency(fh->port, f);
416225b783bSHans Verkuil }
417225b783bSHans Verkuil 
4181a708ea0SHans Verkuil static int saa7164_s_ctrl(struct v4l2_ctrl *ctrl)
419b285192aSMauro Carvalho Chehab {
4201a708ea0SHans Verkuil 	struct saa7164_port *port =
4211a708ea0SHans Verkuil 		container_of(ctrl->handler, struct saa7164_port, ctrl_handler);
4221a708ea0SHans Verkuil 	struct saa7164_encoder_params *params = &port->encoder_params;
423b285192aSMauro Carvalho Chehab 	int ret = 0;
424b285192aSMauro Carvalho Chehab 
4251a708ea0SHans Verkuil 	switch (ctrl->id) {
426b285192aSMauro Carvalho Chehab 	case V4L2_CID_BRIGHTNESS:
4271a708ea0SHans Verkuil 		port->ctl_brightness = ctrl->val;
4281a708ea0SHans Verkuil 		saa7164_api_set_usercontrol(port, PU_BRIGHTNESS_CONTROL);
429b285192aSMauro Carvalho Chehab 		break;
430b285192aSMauro Carvalho Chehab 	case V4L2_CID_CONTRAST:
4311a708ea0SHans Verkuil 		port->ctl_contrast = ctrl->val;
432b285192aSMauro Carvalho Chehab 		saa7164_api_set_usercontrol(port, PU_CONTRAST_CONTROL);
433b285192aSMauro Carvalho Chehab 		break;
434b285192aSMauro Carvalho Chehab 	case V4L2_CID_SATURATION:
4351a708ea0SHans Verkuil 		port->ctl_saturation = ctrl->val;
4361a708ea0SHans Verkuil 		saa7164_api_set_usercontrol(port, PU_SATURATION_CONTROL);
437b285192aSMauro Carvalho Chehab 		break;
438b285192aSMauro Carvalho Chehab 	case V4L2_CID_HUE:
4391a708ea0SHans Verkuil 		port->ctl_hue = ctrl->val;
440b285192aSMauro Carvalho Chehab 		saa7164_api_set_usercontrol(port, PU_HUE_CONTROL);
441b285192aSMauro Carvalho Chehab 		break;
442b285192aSMauro Carvalho Chehab 	case V4L2_CID_SHARPNESS:
4431a708ea0SHans Verkuil 		port->ctl_sharpness = ctrl->val;
444b285192aSMauro Carvalho Chehab 		saa7164_api_set_usercontrol(port, PU_SHARPNESS_CONTROL);
445b285192aSMauro Carvalho Chehab 		break;
446b285192aSMauro Carvalho Chehab 	case V4L2_CID_AUDIO_VOLUME:
4471a708ea0SHans Verkuil 		port->ctl_volume = ctrl->val;
448b285192aSMauro Carvalho Chehab 		saa7164_api_set_audio_volume(port, port->ctl_volume);
449b285192aSMauro Carvalho Chehab 		break;
450b285192aSMauro Carvalho Chehab 	case V4L2_CID_MPEG_VIDEO_BITRATE:
4511a708ea0SHans Verkuil 		params->bitrate = ctrl->val;
452b285192aSMauro Carvalho Chehab 		break;
453b285192aSMauro Carvalho Chehab 	case V4L2_CID_MPEG_STREAM_TYPE:
4541a708ea0SHans Verkuil 		params->stream_type = ctrl->val;
455b285192aSMauro Carvalho Chehab 		break;
456b285192aSMauro Carvalho Chehab 	case V4L2_CID_MPEG_AUDIO_MUTE:
4571a708ea0SHans Verkuil 		params->ctl_mute = ctrl->val;
458b285192aSMauro Carvalho Chehab 		ret = saa7164_api_audio_mute(port, params->ctl_mute);
459b285192aSMauro Carvalho Chehab 		if (ret != SAA_OK) {
460b285192aSMauro Carvalho Chehab 			printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__,
461b285192aSMauro Carvalho Chehab 				ret);
462b285192aSMauro Carvalho Chehab 			ret = -EIO;
463b285192aSMauro Carvalho Chehab 		}
464b285192aSMauro Carvalho Chehab 		break;
465b285192aSMauro Carvalho Chehab 	case V4L2_CID_MPEG_VIDEO_ASPECT:
4661a708ea0SHans Verkuil 		params->ctl_aspect = ctrl->val;
467b285192aSMauro Carvalho Chehab 		ret = saa7164_api_set_aspect_ratio(port);
468b285192aSMauro Carvalho Chehab 		if (ret != SAA_OK) {
469b285192aSMauro Carvalho Chehab 			printk(KERN_ERR "%s() error, ret = 0x%x\n", __func__,
470b285192aSMauro Carvalho Chehab 				ret);
471b285192aSMauro Carvalho Chehab 			ret = -EIO;
472b285192aSMauro Carvalho Chehab 		}
473b285192aSMauro Carvalho Chehab 		break;
474b285192aSMauro Carvalho Chehab 	case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
4751a708ea0SHans Verkuil 		params->bitrate_mode = ctrl->val;
476b285192aSMauro Carvalho Chehab 		break;
477b285192aSMauro Carvalho Chehab 	case V4L2_CID_MPEG_VIDEO_B_FRAMES:
4781a708ea0SHans Verkuil 		params->refdist = ctrl->val;
479b285192aSMauro Carvalho Chehab 		break;
480b285192aSMauro Carvalho Chehab 	case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
4811a708ea0SHans Verkuil 		params->bitrate_peak = ctrl->val;
482b285192aSMauro Carvalho Chehab 		break;
483b285192aSMauro Carvalho Chehab 	case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
4841a708ea0SHans Verkuil 		params->gop_size = ctrl->val;
485b285192aSMauro Carvalho Chehab 		break;
486b285192aSMauro Carvalho Chehab 	default:
4871a708ea0SHans Verkuil 		ret = -EINVAL;
488b285192aSMauro Carvalho Chehab 	}
489b285192aSMauro Carvalho Chehab 
490b285192aSMauro Carvalho Chehab 	return ret;
491b285192aSMauro Carvalho Chehab }
492b285192aSMauro Carvalho Chehab 
493b285192aSMauro Carvalho Chehab static int vidioc_querycap(struct file *file, void  *priv,
494b285192aSMauro Carvalho Chehab 	struct v4l2_capability *cap)
495b285192aSMauro Carvalho Chehab {
496b285192aSMauro Carvalho Chehab 	struct saa7164_encoder_fh *fh = file->private_data;
497b285192aSMauro Carvalho Chehab 	struct saa7164_port *port = fh->port;
498b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
499b285192aSMauro Carvalho Chehab 
500b285192aSMauro Carvalho Chehab 	strcpy(cap->driver, dev->name);
501b285192aSMauro Carvalho Chehab 	strlcpy(cap->card, saa7164_boards[dev->board].name,
502b285192aSMauro Carvalho Chehab 		sizeof(cap->card));
503b285192aSMauro Carvalho Chehab 	sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
504b285192aSMauro Carvalho Chehab 
505534bc3e2SHans Verkuil 	cap->device_caps =
506b285192aSMauro Carvalho Chehab 		V4L2_CAP_VIDEO_CAPTURE |
507b285192aSMauro Carvalho Chehab 		V4L2_CAP_READWRITE |
508534bc3e2SHans Verkuil 		V4L2_CAP_TUNER;
509b285192aSMauro Carvalho Chehab 
510534bc3e2SHans Verkuil 	cap->capabilities = cap->device_caps |
511534bc3e2SHans Verkuil 		V4L2_CAP_VBI_CAPTURE |
512534bc3e2SHans Verkuil 		V4L2_CAP_DEVICE_CAPS;
513b285192aSMauro Carvalho Chehab 
514b285192aSMauro Carvalho Chehab 	return 0;
515b285192aSMauro Carvalho Chehab }
516b285192aSMauro Carvalho Chehab 
517b285192aSMauro Carvalho Chehab static int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
518b285192aSMauro Carvalho Chehab 	struct v4l2_fmtdesc *f)
519b285192aSMauro Carvalho Chehab {
520b285192aSMauro Carvalho Chehab 	if (f->index != 0)
521b285192aSMauro Carvalho Chehab 		return -EINVAL;
522b285192aSMauro Carvalho Chehab 
523b285192aSMauro Carvalho Chehab 	strlcpy(f->description, "MPEG", sizeof(f->description));
524b285192aSMauro Carvalho Chehab 	f->pixelformat = V4L2_PIX_FMT_MPEG;
525b285192aSMauro Carvalho Chehab 
526b285192aSMauro Carvalho Chehab 	return 0;
527b285192aSMauro Carvalho Chehab }
528b285192aSMauro Carvalho Chehab 
529031d2297SHans Verkuil static int vidioc_fmt_vid_cap(struct file *file, void *priv,
530b285192aSMauro Carvalho Chehab 				struct v4l2_format *f)
531b285192aSMauro Carvalho Chehab {
532b285192aSMauro Carvalho Chehab 	struct saa7164_encoder_fh *fh = file->private_data;
533b285192aSMauro Carvalho Chehab 	struct saa7164_port *port = fh->port;
534b285192aSMauro Carvalho Chehab 
535b285192aSMauro Carvalho Chehab 	f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
536b285192aSMauro Carvalho Chehab 	f->fmt.pix.bytesperline = 0;
537031d2297SHans Verkuil 	f->fmt.pix.sizeimage    = SAA7164_SIZEIMAGE;
538031d2297SHans Verkuil 	f->fmt.pix.field        = V4L2_FIELD_INTERLACED;
539031d2297SHans Verkuil 	f->fmt.pix.colorspace   = V4L2_COLORSPACE_SMPTE170M;
540b285192aSMauro Carvalho Chehab 	f->fmt.pix.width        = port->width;
541b285192aSMauro Carvalho Chehab 	f->fmt.pix.height       = port->height;
542b285192aSMauro Carvalho Chehab 	return 0;
543b285192aSMauro Carvalho Chehab }
544b285192aSMauro Carvalho Chehab 
545b285192aSMauro Carvalho Chehab static int saa7164_encoder_stop_port(struct saa7164_port *port)
546b285192aSMauro Carvalho Chehab {
547b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
548b285192aSMauro Carvalho Chehab 	int ret;
549b285192aSMauro Carvalho Chehab 
550b285192aSMauro Carvalho Chehab 	ret = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
551b285192aSMauro Carvalho Chehab 	if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
552b285192aSMauro Carvalho Chehab 		printk(KERN_ERR "%s() stop transition failed, ret = 0x%x\n",
553b285192aSMauro Carvalho Chehab 			__func__, ret);
554b285192aSMauro Carvalho Chehab 		ret = -EIO;
555b285192aSMauro Carvalho Chehab 	} else {
556b285192aSMauro Carvalho Chehab 		dprintk(DBGLVL_ENC, "%s()    Stopped\n", __func__);
557b285192aSMauro Carvalho Chehab 		ret = 0;
558b285192aSMauro Carvalho Chehab 	}
559b285192aSMauro Carvalho Chehab 
560b285192aSMauro Carvalho Chehab 	return ret;
561b285192aSMauro Carvalho Chehab }
562b285192aSMauro Carvalho Chehab 
563b285192aSMauro Carvalho Chehab static int saa7164_encoder_acquire_port(struct saa7164_port *port)
564b285192aSMauro Carvalho Chehab {
565b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
566b285192aSMauro Carvalho Chehab 	int ret;
567b285192aSMauro Carvalho Chehab 
568b285192aSMauro Carvalho Chehab 	ret = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE);
569b285192aSMauro Carvalho Chehab 	if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
570b285192aSMauro Carvalho Chehab 		printk(KERN_ERR "%s() acquire transition failed, ret = 0x%x\n",
571b285192aSMauro Carvalho Chehab 			__func__, ret);
572b285192aSMauro Carvalho Chehab 		ret = -EIO;
573b285192aSMauro Carvalho Chehab 	} else {
574b285192aSMauro Carvalho Chehab 		dprintk(DBGLVL_ENC, "%s() Acquired\n", __func__);
575b285192aSMauro Carvalho Chehab 		ret = 0;
576b285192aSMauro Carvalho Chehab 	}
577b285192aSMauro Carvalho Chehab 
578b285192aSMauro Carvalho Chehab 	return ret;
579b285192aSMauro Carvalho Chehab }
580b285192aSMauro Carvalho Chehab 
581b285192aSMauro Carvalho Chehab static int saa7164_encoder_pause_port(struct saa7164_port *port)
582b285192aSMauro Carvalho Chehab {
583b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
584b285192aSMauro Carvalho Chehab 	int ret;
585b285192aSMauro Carvalho Chehab 
586b285192aSMauro Carvalho Chehab 	ret = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE);
587b285192aSMauro Carvalho Chehab 	if ((ret != SAA_OK) && (ret != SAA_ERR_ALREADY_STOPPED)) {
588b285192aSMauro Carvalho Chehab 		printk(KERN_ERR "%s() pause transition failed, ret = 0x%x\n",
589b285192aSMauro Carvalho Chehab 			__func__, ret);
590b285192aSMauro Carvalho Chehab 		ret = -EIO;
591b285192aSMauro Carvalho Chehab 	} else {
592b285192aSMauro Carvalho Chehab 		dprintk(DBGLVL_ENC, "%s()   Paused\n", __func__);
593b285192aSMauro Carvalho Chehab 		ret = 0;
594b285192aSMauro Carvalho Chehab 	}
595b285192aSMauro Carvalho Chehab 
596b285192aSMauro Carvalho Chehab 	return ret;
597b285192aSMauro Carvalho Chehab }
598b285192aSMauro Carvalho Chehab 
599b285192aSMauro Carvalho Chehab /* Firmware is very windows centric, meaning you have to transition
600b285192aSMauro Carvalho Chehab  * the part through AVStream / KS Windows stages, forwards or backwards.
601b285192aSMauro Carvalho Chehab  * States are: stopped, acquired (h/w), paused, started.
602b285192aSMauro Carvalho Chehab  * We have to leave here will all of the soft buffers on the free list,
603b285192aSMauro Carvalho Chehab  * else the cfg_post() func won't have soft buffers to correctly configure.
604b285192aSMauro Carvalho Chehab  */
605b285192aSMauro Carvalho Chehab static int saa7164_encoder_stop_streaming(struct saa7164_port *port)
606b285192aSMauro Carvalho Chehab {
607b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
608b285192aSMauro Carvalho Chehab 	struct saa7164_buffer *buf;
609b285192aSMauro Carvalho Chehab 	struct saa7164_user_buffer *ubuf;
610b285192aSMauro Carvalho Chehab 	struct list_head *c, *n;
611b285192aSMauro Carvalho Chehab 	int ret;
612b285192aSMauro Carvalho Chehab 
613b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s(port=%d)\n", __func__, port->nr);
614b285192aSMauro Carvalho Chehab 
615b285192aSMauro Carvalho Chehab 	ret = saa7164_encoder_pause_port(port);
616b285192aSMauro Carvalho Chehab 	ret = saa7164_encoder_acquire_port(port);
617b285192aSMauro Carvalho Chehab 	ret = saa7164_encoder_stop_port(port);
618b285192aSMauro Carvalho Chehab 
619b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s(port=%d) Hardware stopped\n", __func__,
620b285192aSMauro Carvalho Chehab 		port->nr);
621b285192aSMauro Carvalho Chehab 
622b285192aSMauro Carvalho Chehab 	/* Reset the state of any allocated buffer resources */
623b285192aSMauro Carvalho Chehab 	mutex_lock(&port->dmaqueue_lock);
624b285192aSMauro Carvalho Chehab 
625b285192aSMauro Carvalho Chehab 	/* Reset the hard and soft buffer state */
626b285192aSMauro Carvalho Chehab 	list_for_each_safe(c, n, &port->dmaqueue.list) {
627b285192aSMauro Carvalho Chehab 		buf = list_entry(c, struct saa7164_buffer, list);
628b285192aSMauro Carvalho Chehab 		buf->flags = SAA7164_BUFFER_FREE;
629b285192aSMauro Carvalho Chehab 		buf->pos = 0;
630b285192aSMauro Carvalho Chehab 	}
631b285192aSMauro Carvalho Chehab 
632b285192aSMauro Carvalho Chehab 	list_for_each_safe(c, n, &port->list_buf_used.list) {
633b285192aSMauro Carvalho Chehab 		ubuf = list_entry(c, struct saa7164_user_buffer, list);
634b285192aSMauro Carvalho Chehab 		ubuf->pos = 0;
635b285192aSMauro Carvalho Chehab 		list_move_tail(&ubuf->list, &port->list_buf_free.list);
636b285192aSMauro Carvalho Chehab 	}
637b285192aSMauro Carvalho Chehab 
638b285192aSMauro Carvalho Chehab 	mutex_unlock(&port->dmaqueue_lock);
639b285192aSMauro Carvalho Chehab 
640b285192aSMauro Carvalho Chehab 	/* Free any allocated resources */
641b285192aSMauro Carvalho Chehab 	saa7164_encoder_buffers_dealloc(port);
642b285192aSMauro Carvalho Chehab 
643b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s(port=%d) Released\n", __func__, port->nr);
644b285192aSMauro Carvalho Chehab 
645b285192aSMauro Carvalho Chehab 	return ret;
646b285192aSMauro Carvalho Chehab }
647b285192aSMauro Carvalho Chehab 
648b285192aSMauro Carvalho Chehab static int saa7164_encoder_start_streaming(struct saa7164_port *port)
649b285192aSMauro Carvalho Chehab {
650b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
651b285192aSMauro Carvalho Chehab 	int result, ret = 0;
652b285192aSMauro Carvalho Chehab 
653b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s(port=%d)\n", __func__, port->nr);
654b285192aSMauro Carvalho Chehab 
655b285192aSMauro Carvalho Chehab 	port->done_first_interrupt = 0;
656b285192aSMauro Carvalho Chehab 
657b285192aSMauro Carvalho Chehab 	/* allocate all of the PCIe DMA buffer resources on the fly,
658b285192aSMauro Carvalho Chehab 	 * allowing switching between TS and PS payloads without
659b285192aSMauro Carvalho Chehab 	 * requiring a complete driver reload.
660b285192aSMauro Carvalho Chehab 	 */
661b285192aSMauro Carvalho Chehab 	saa7164_encoder_buffers_alloc(port);
662b285192aSMauro Carvalho Chehab 
663b285192aSMauro Carvalho Chehab 	/* Configure the encoder with any cache values */
664b285192aSMauro Carvalho Chehab 	saa7164_api_set_encoder(port);
665b285192aSMauro Carvalho Chehab 	saa7164_api_get_encoder(port);
666b285192aSMauro Carvalho Chehab 
667b285192aSMauro Carvalho Chehab 	/* Place the empty buffers on the hardware */
668b285192aSMauro Carvalho Chehab 	saa7164_buffer_cfg_port(port);
669b285192aSMauro Carvalho Chehab 
670b285192aSMauro Carvalho Chehab 	/* Acquire the hardware */
671b285192aSMauro Carvalho Chehab 	result = saa7164_api_transition_port(port, SAA_DMASTATE_ACQUIRE);
672b285192aSMauro Carvalho Chehab 	if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
673b285192aSMauro Carvalho Chehab 		printk(KERN_ERR "%s() acquire transition failed, res = 0x%x\n",
674b285192aSMauro Carvalho Chehab 			__func__, result);
675b285192aSMauro Carvalho Chehab 
676b285192aSMauro Carvalho Chehab 		/* Stop the hardware, regardless */
677b285192aSMauro Carvalho Chehab 		result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
678b285192aSMauro Carvalho Chehab 		if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
67924f711c1SMauro Carvalho Chehab 			printk(KERN_ERR "%s() acquire/forced stop transition failed, res = 0x%x\n",
68024f711c1SMauro Carvalho Chehab 			       __func__, result);
681b285192aSMauro Carvalho Chehab 		}
682b285192aSMauro Carvalho Chehab 		ret = -EIO;
683b285192aSMauro Carvalho Chehab 		goto out;
684b285192aSMauro Carvalho Chehab 	} else
685b285192aSMauro Carvalho Chehab 		dprintk(DBGLVL_ENC, "%s()   Acquired\n", __func__);
686b285192aSMauro Carvalho Chehab 
687b285192aSMauro Carvalho Chehab 	/* Pause the hardware */
688b285192aSMauro Carvalho Chehab 	result = saa7164_api_transition_port(port, SAA_DMASTATE_PAUSE);
689b285192aSMauro Carvalho Chehab 	if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
690b285192aSMauro Carvalho Chehab 		printk(KERN_ERR "%s() pause transition failed, res = 0x%x\n",
691b285192aSMauro Carvalho Chehab 				__func__, result);
692b285192aSMauro Carvalho Chehab 
693b285192aSMauro Carvalho Chehab 		/* Stop the hardware, regardless */
694b285192aSMauro Carvalho Chehab 		result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
695b285192aSMauro Carvalho Chehab 		if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
69624f711c1SMauro Carvalho Chehab 			printk(KERN_ERR "%s() pause/forced stop transition failed, res = 0x%x\n",
69724f711c1SMauro Carvalho Chehab 			       __func__, result);
698b285192aSMauro Carvalho Chehab 		}
699b285192aSMauro Carvalho Chehab 
700b285192aSMauro Carvalho Chehab 		ret = -EIO;
701b285192aSMauro Carvalho Chehab 		goto out;
702b285192aSMauro Carvalho Chehab 	} else
703b285192aSMauro Carvalho Chehab 		dprintk(DBGLVL_ENC, "%s()   Paused\n", __func__);
704b285192aSMauro Carvalho Chehab 
705b285192aSMauro Carvalho Chehab 	/* Start the hardware */
706b285192aSMauro Carvalho Chehab 	result = saa7164_api_transition_port(port, SAA_DMASTATE_RUN);
707b285192aSMauro Carvalho Chehab 	if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
708b285192aSMauro Carvalho Chehab 		printk(KERN_ERR "%s() run transition failed, result = 0x%x\n",
709b285192aSMauro Carvalho Chehab 				__func__, result);
710b285192aSMauro Carvalho Chehab 
711b285192aSMauro Carvalho Chehab 		/* Stop the hardware, regardless */
712b285192aSMauro Carvalho Chehab 		result = saa7164_api_transition_port(port, SAA_DMASTATE_STOP);
713b285192aSMauro Carvalho Chehab 		if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) {
71424f711c1SMauro Carvalho Chehab 			printk(KERN_ERR "%s() run/forced stop transition failed, res = 0x%x\n",
71524f711c1SMauro Carvalho Chehab 			       __func__, result);
716b285192aSMauro Carvalho Chehab 		}
717b285192aSMauro Carvalho Chehab 
718b285192aSMauro Carvalho Chehab 		ret = -EIO;
719b285192aSMauro Carvalho Chehab 	} else
720b285192aSMauro Carvalho Chehab 		dprintk(DBGLVL_ENC, "%s()   Running\n", __func__);
721b285192aSMauro Carvalho Chehab 
722b285192aSMauro Carvalho Chehab out:
723b285192aSMauro Carvalho Chehab 	return ret;
724b285192aSMauro Carvalho Chehab }
725b285192aSMauro Carvalho Chehab 
726b285192aSMauro Carvalho Chehab static int fops_open(struct file *file)
727b285192aSMauro Carvalho Chehab {
728b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev;
729b285192aSMauro Carvalho Chehab 	struct saa7164_port *port;
730b285192aSMauro Carvalho Chehab 	struct saa7164_encoder_fh *fh;
731b285192aSMauro Carvalho Chehab 
732b285192aSMauro Carvalho Chehab 	port = (struct saa7164_port *)video_get_drvdata(video_devdata(file));
733b285192aSMauro Carvalho Chehab 	if (!port)
734b285192aSMauro Carvalho Chehab 		return -ENODEV;
735b285192aSMauro Carvalho Chehab 
736b285192aSMauro Carvalho Chehab 	dev = port->dev;
737b285192aSMauro Carvalho Chehab 
738b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s()\n", __func__);
739b285192aSMauro Carvalho Chehab 
740b285192aSMauro Carvalho Chehab 	/* allocate + initialize per filehandle data */
741b285192aSMauro Carvalho Chehab 	fh = kzalloc(sizeof(*fh), GFP_KERNEL);
742b285192aSMauro Carvalho Chehab 	if (NULL == fh)
743b285192aSMauro Carvalho Chehab 		return -ENOMEM;
744b285192aSMauro Carvalho Chehab 
745b285192aSMauro Carvalho Chehab 	fh->port = port;
746d6d3fe2fSHans Verkuil 	v4l2_fh_init(&fh->fh, video_devdata(file));
747d6d3fe2fSHans Verkuil 	v4l2_fh_add(&fh->fh);
748d6d3fe2fSHans Verkuil 	file->private_data = fh;
749b285192aSMauro Carvalho Chehab 
750b285192aSMauro Carvalho Chehab 	return 0;
751b285192aSMauro Carvalho Chehab }
752b285192aSMauro Carvalho Chehab 
753b285192aSMauro Carvalho Chehab static int fops_release(struct file *file)
754b285192aSMauro Carvalho Chehab {
755b285192aSMauro Carvalho Chehab 	struct saa7164_encoder_fh *fh = file->private_data;
756b285192aSMauro Carvalho Chehab 	struct saa7164_port *port = fh->port;
757b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
758b285192aSMauro Carvalho Chehab 
759b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s()\n", __func__);
760b285192aSMauro Carvalho Chehab 
761b285192aSMauro Carvalho Chehab 	/* Shut device down on last close */
762b285192aSMauro Carvalho Chehab 	if (atomic_cmpxchg(&fh->v4l_reading, 1, 0) == 1) {
763b285192aSMauro Carvalho Chehab 		if (atomic_dec_return(&port->v4l_reader_count) == 0) {
764b285192aSMauro Carvalho Chehab 			/* stop mpeg capture then cancel buffers */
765b285192aSMauro Carvalho Chehab 			saa7164_encoder_stop_streaming(port);
766b285192aSMauro Carvalho Chehab 		}
767b285192aSMauro Carvalho Chehab 	}
768b285192aSMauro Carvalho Chehab 
769d6d3fe2fSHans Verkuil 	v4l2_fh_del(&fh->fh);
770d6d3fe2fSHans Verkuil 	v4l2_fh_exit(&fh->fh);
771b285192aSMauro Carvalho Chehab 	kfree(fh);
772b285192aSMauro Carvalho Chehab 
773b285192aSMauro Carvalho Chehab 	return 0;
774b285192aSMauro Carvalho Chehab }
775b285192aSMauro Carvalho Chehab 
7765faf7db8SMauro Carvalho Chehab static struct
7775faf7db8SMauro Carvalho Chehab saa7164_user_buffer *saa7164_enc_next_buf(struct saa7164_port *port)
778b285192aSMauro Carvalho Chehab {
779b285192aSMauro Carvalho Chehab 	struct saa7164_user_buffer *ubuf = NULL;
780b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
781b285192aSMauro Carvalho Chehab 	u32 crc;
782b285192aSMauro Carvalho Chehab 
783b285192aSMauro Carvalho Chehab 	mutex_lock(&port->dmaqueue_lock);
784b285192aSMauro Carvalho Chehab 	if (!list_empty(&port->list_buf_used.list)) {
785b285192aSMauro Carvalho Chehab 		ubuf = list_first_entry(&port->list_buf_used.list,
786b285192aSMauro Carvalho Chehab 			struct saa7164_user_buffer, list);
787b285192aSMauro Carvalho Chehab 
788b285192aSMauro Carvalho Chehab 		if (crc_checking) {
789b285192aSMauro Carvalho Chehab 			crc = crc32(0, ubuf->data, ubuf->actual_size);
790b285192aSMauro Carvalho Chehab 			if (crc != ubuf->crc) {
791b285192aSMauro Carvalho Chehab 				printk(KERN_ERR
792b285192aSMauro Carvalho Chehab 		"%s() ubuf %p crc became invalid, was 0x%x became 0x%x\n",
793b285192aSMauro Carvalho Chehab 					__func__,
794b285192aSMauro Carvalho Chehab 					ubuf, ubuf->crc, crc);
795b285192aSMauro Carvalho Chehab 			}
796b285192aSMauro Carvalho Chehab 		}
797b285192aSMauro Carvalho Chehab 
798b285192aSMauro Carvalho Chehab 	}
799b285192aSMauro Carvalho Chehab 	mutex_unlock(&port->dmaqueue_lock);
800b285192aSMauro Carvalho Chehab 
801b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s() returns %p\n", __func__, ubuf);
802b285192aSMauro Carvalho Chehab 
803b285192aSMauro Carvalho Chehab 	return ubuf;
804b285192aSMauro Carvalho Chehab }
805b285192aSMauro Carvalho Chehab 
806b285192aSMauro Carvalho Chehab static ssize_t fops_read(struct file *file, char __user *buffer,
807b285192aSMauro Carvalho Chehab 	size_t count, loff_t *pos)
808b285192aSMauro Carvalho Chehab {
809b285192aSMauro Carvalho Chehab 	struct saa7164_encoder_fh *fh = file->private_data;
810b285192aSMauro Carvalho Chehab 	struct saa7164_port *port = fh->port;
811b285192aSMauro Carvalho Chehab 	struct saa7164_user_buffer *ubuf = NULL;
812b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
813b285192aSMauro Carvalho Chehab 	int ret = 0;
814b285192aSMauro Carvalho Chehab 	int rem, cnt;
815b285192aSMauro Carvalho Chehab 	u8 *p;
816b285192aSMauro Carvalho Chehab 
817b285192aSMauro Carvalho Chehab 	port->last_read_msecs_diff = port->last_read_msecs;
818b285192aSMauro Carvalho Chehab 	port->last_read_msecs = jiffies_to_msecs(jiffies);
819b285192aSMauro Carvalho Chehab 	port->last_read_msecs_diff = port->last_read_msecs -
820b285192aSMauro Carvalho Chehab 		port->last_read_msecs_diff;
821b285192aSMauro Carvalho Chehab 
822b285192aSMauro Carvalho Chehab 	saa7164_histogram_update(&port->read_interval,
823b285192aSMauro Carvalho Chehab 		port->last_read_msecs_diff);
824b285192aSMauro Carvalho Chehab 
825b285192aSMauro Carvalho Chehab 	if (*pos) {
826b285192aSMauro Carvalho Chehab 		printk(KERN_ERR "%s() ESPIPE\n", __func__);
827b285192aSMauro Carvalho Chehab 		return -ESPIPE;
828b285192aSMauro Carvalho Chehab 	}
829b285192aSMauro Carvalho Chehab 
830b285192aSMauro Carvalho Chehab 	if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {
831b285192aSMauro Carvalho Chehab 		if (atomic_inc_return(&port->v4l_reader_count) == 1) {
832b285192aSMauro Carvalho Chehab 
833b285192aSMauro Carvalho Chehab 			if (saa7164_encoder_initialize(port) < 0) {
834b285192aSMauro Carvalho Chehab 				printk(KERN_ERR "%s() EINVAL\n", __func__);
835b285192aSMauro Carvalho Chehab 				return -EINVAL;
836b285192aSMauro Carvalho Chehab 			}
837b285192aSMauro Carvalho Chehab 
838b285192aSMauro Carvalho Chehab 			saa7164_encoder_start_streaming(port);
839b285192aSMauro Carvalho Chehab 			msleep(200);
840b285192aSMauro Carvalho Chehab 		}
841b285192aSMauro Carvalho Chehab 	}
842b285192aSMauro Carvalho Chehab 
843b285192aSMauro Carvalho Chehab 	/* blocking wait for buffer */
844b285192aSMauro Carvalho Chehab 	if ((file->f_flags & O_NONBLOCK) == 0) {
845b285192aSMauro Carvalho Chehab 		if (wait_event_interruptible(port->wait_read,
846b285192aSMauro Carvalho Chehab 			saa7164_enc_next_buf(port))) {
847b285192aSMauro Carvalho Chehab 				printk(KERN_ERR "%s() ERESTARTSYS\n", __func__);
848b285192aSMauro Carvalho Chehab 				return -ERESTARTSYS;
849b285192aSMauro Carvalho Chehab 		}
850b285192aSMauro Carvalho Chehab 	}
851b285192aSMauro Carvalho Chehab 
852b285192aSMauro Carvalho Chehab 	/* Pull the first buffer from the used list */
853b285192aSMauro Carvalho Chehab 	ubuf = saa7164_enc_next_buf(port);
854b285192aSMauro Carvalho Chehab 
855b285192aSMauro Carvalho Chehab 	while ((count > 0) && ubuf) {
856b285192aSMauro Carvalho Chehab 
857b285192aSMauro Carvalho Chehab 		/* set remaining bytes to copy */
858b285192aSMauro Carvalho Chehab 		rem = ubuf->actual_size - ubuf->pos;
859b285192aSMauro Carvalho Chehab 		cnt = rem > count ? count : rem;
860b285192aSMauro Carvalho Chehab 
861b285192aSMauro Carvalho Chehab 		p = ubuf->data + ubuf->pos;
862b285192aSMauro Carvalho Chehab 
863b285192aSMauro Carvalho Chehab 		dprintk(DBGLVL_ENC,
864b285192aSMauro Carvalho Chehab 			"%s() count=%d cnt=%d rem=%d buf=%p buf->pos=%d\n",
865b285192aSMauro Carvalho Chehab 			__func__, (int)count, cnt, rem, ubuf, ubuf->pos);
866b285192aSMauro Carvalho Chehab 
867b285192aSMauro Carvalho Chehab 		if (copy_to_user(buffer, p, cnt)) {
868b285192aSMauro Carvalho Chehab 			printk(KERN_ERR "%s() copy_to_user failed\n", __func__);
869b285192aSMauro Carvalho Chehab 			if (!ret) {
870b285192aSMauro Carvalho Chehab 				printk(KERN_ERR "%s() EFAULT\n", __func__);
871b285192aSMauro Carvalho Chehab 				ret = -EFAULT;
872b285192aSMauro Carvalho Chehab 			}
873b285192aSMauro Carvalho Chehab 			goto err;
874b285192aSMauro Carvalho Chehab 		}
875b285192aSMauro Carvalho Chehab 
876b285192aSMauro Carvalho Chehab 		ubuf->pos += cnt;
877b285192aSMauro Carvalho Chehab 		count -= cnt;
878b285192aSMauro Carvalho Chehab 		buffer += cnt;
879b285192aSMauro Carvalho Chehab 		ret += cnt;
880b285192aSMauro Carvalho Chehab 
881b285192aSMauro Carvalho Chehab 		if (ubuf->pos > ubuf->actual_size)
882b285192aSMauro Carvalho Chehab 			printk(KERN_ERR "read() pos > actual, huh?\n");
883b285192aSMauro Carvalho Chehab 
884b285192aSMauro Carvalho Chehab 		if (ubuf->pos == ubuf->actual_size) {
885b285192aSMauro Carvalho Chehab 
886b285192aSMauro Carvalho Chehab 			/* finished with current buffer, take next buffer */
887b285192aSMauro Carvalho Chehab 
888b285192aSMauro Carvalho Chehab 			/* Requeue the buffer on the free list */
889b285192aSMauro Carvalho Chehab 			ubuf->pos = 0;
890b285192aSMauro Carvalho Chehab 
891b285192aSMauro Carvalho Chehab 			mutex_lock(&port->dmaqueue_lock);
892b285192aSMauro Carvalho Chehab 			list_move_tail(&ubuf->list, &port->list_buf_free.list);
893b285192aSMauro Carvalho Chehab 			mutex_unlock(&port->dmaqueue_lock);
894b285192aSMauro Carvalho Chehab 
895b285192aSMauro Carvalho Chehab 			/* Dequeue next */
896b285192aSMauro Carvalho Chehab 			if ((file->f_flags & O_NONBLOCK) == 0) {
897b285192aSMauro Carvalho Chehab 				if (wait_event_interruptible(port->wait_read,
898b285192aSMauro Carvalho Chehab 					saa7164_enc_next_buf(port))) {
899b285192aSMauro Carvalho Chehab 						break;
900b285192aSMauro Carvalho Chehab 				}
901b285192aSMauro Carvalho Chehab 			}
902b285192aSMauro Carvalho Chehab 			ubuf = saa7164_enc_next_buf(port);
903b285192aSMauro Carvalho Chehab 		}
904b285192aSMauro Carvalho Chehab 	}
905b285192aSMauro Carvalho Chehab err:
906b285192aSMauro Carvalho Chehab 	if (!ret && !ubuf)
907b285192aSMauro Carvalho Chehab 		ret = -EAGAIN;
908b285192aSMauro Carvalho Chehab 
909b285192aSMauro Carvalho Chehab 	return ret;
910b285192aSMauro Carvalho Chehab }
911b285192aSMauro Carvalho Chehab 
912b285192aSMauro Carvalho Chehab static unsigned int fops_poll(struct file *file, poll_table *wait)
913b285192aSMauro Carvalho Chehab {
91401699437SAl Viro 	__poll_t req_events = poll_requested_events(wait);
915b285192aSMauro Carvalho Chehab 	struct saa7164_encoder_fh *fh =
916b285192aSMauro Carvalho Chehab 		(struct saa7164_encoder_fh *)file->private_data;
917b285192aSMauro Carvalho Chehab 	struct saa7164_port *port = fh->port;
918245b5ae9SHans Verkuil 	unsigned int mask = v4l2_ctrl_poll(file, wait);
919b285192aSMauro Carvalho Chehab 
920b285192aSMauro Carvalho Chehab 	port->last_poll_msecs_diff = port->last_poll_msecs;
921b285192aSMauro Carvalho Chehab 	port->last_poll_msecs = jiffies_to_msecs(jiffies);
922b285192aSMauro Carvalho Chehab 	port->last_poll_msecs_diff = port->last_poll_msecs -
923b285192aSMauro Carvalho Chehab 		port->last_poll_msecs_diff;
924b285192aSMauro Carvalho Chehab 
925b285192aSMauro Carvalho Chehab 	saa7164_histogram_update(&port->poll_interval,
926b285192aSMauro Carvalho Chehab 		port->last_poll_msecs_diff);
927b285192aSMauro Carvalho Chehab 
92845053edcSHans Verkuil 	if (!(req_events & (POLLIN | POLLRDNORM)))
92945053edcSHans Verkuil 		return mask;
930b285192aSMauro Carvalho Chehab 
931b285192aSMauro Carvalho Chehab 	if (atomic_cmpxchg(&fh->v4l_reading, 0, 1) == 0) {
932b285192aSMauro Carvalho Chehab 		if (atomic_inc_return(&port->v4l_reader_count) == 1) {
933b285192aSMauro Carvalho Chehab 			if (saa7164_encoder_initialize(port) < 0)
934245b5ae9SHans Verkuil 				return mask | POLLERR;
935b285192aSMauro Carvalho Chehab 			saa7164_encoder_start_streaming(port);
936b285192aSMauro Carvalho Chehab 			msleep(200);
937b285192aSMauro Carvalho Chehab 		}
938b285192aSMauro Carvalho Chehab 	}
939b285192aSMauro Carvalho Chehab 
940b285192aSMauro Carvalho Chehab 	/* Pull the first buffer from the used list */
941b285192aSMauro Carvalho Chehab 	if (!list_empty(&port->list_buf_used.list))
942b285192aSMauro Carvalho Chehab 		mask |= POLLIN | POLLRDNORM;
943b285192aSMauro Carvalho Chehab 
944b285192aSMauro Carvalho Chehab 	return mask;
945b285192aSMauro Carvalho Chehab }
946b285192aSMauro Carvalho Chehab 
9471a708ea0SHans Verkuil static const struct v4l2_ctrl_ops saa7164_ctrl_ops = {
9481a708ea0SHans Verkuil 	.s_ctrl = saa7164_s_ctrl,
9491a708ea0SHans Verkuil };
9501a708ea0SHans Verkuil 
951b285192aSMauro Carvalho Chehab static const struct v4l2_file_operations mpeg_fops = {
952b285192aSMauro Carvalho Chehab 	.owner		= THIS_MODULE,
953b285192aSMauro Carvalho Chehab 	.open		= fops_open,
954b285192aSMauro Carvalho Chehab 	.release	= fops_release,
955b285192aSMauro Carvalho Chehab 	.read		= fops_read,
956b285192aSMauro Carvalho Chehab 	.poll		= fops_poll,
957b285192aSMauro Carvalho Chehab 	.unlocked_ioctl	= video_ioctl2,
958b285192aSMauro Carvalho Chehab };
959b285192aSMauro Carvalho Chehab 
960b285192aSMauro Carvalho Chehab static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
961b285192aSMauro Carvalho Chehab 	.vidioc_s_std		 = vidioc_s_std,
9628d2d41e9SHans Verkuil 	.vidioc_g_std		 = vidioc_g_std,
963225b783bSHans Verkuil 	.vidioc_enum_input	 = saa7164_enum_input,
964b285192aSMauro Carvalho Chehab 	.vidioc_g_input		 = vidioc_g_input,
965b285192aSMauro Carvalho Chehab 	.vidioc_s_input		 = vidioc_s_input,
966225b783bSHans Verkuil 	.vidioc_g_tuner		 = saa7164_g_tuner,
967225b783bSHans Verkuil 	.vidioc_s_tuner		 = saa7164_s_tuner,
968b285192aSMauro Carvalho Chehab 	.vidioc_g_frequency	 = vidioc_g_frequency,
969b285192aSMauro Carvalho Chehab 	.vidioc_s_frequency	 = vidioc_s_frequency,
970b285192aSMauro Carvalho Chehab 	.vidioc_querycap	 = vidioc_querycap,
971b285192aSMauro Carvalho Chehab 	.vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
972031d2297SHans Verkuil 	.vidioc_g_fmt_vid_cap	 = vidioc_fmt_vid_cap,
973031d2297SHans Verkuil 	.vidioc_try_fmt_vid_cap	 = vidioc_fmt_vid_cap,
974031d2297SHans Verkuil 	.vidioc_s_fmt_vid_cap	 = vidioc_fmt_vid_cap,
975245b5ae9SHans Verkuil 	.vidioc_log_status	 = v4l2_ctrl_log_status,
976245b5ae9SHans Verkuil 	.vidioc_subscribe_event  = v4l2_ctrl_subscribe_event,
977245b5ae9SHans Verkuil 	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
978b285192aSMauro Carvalho Chehab };
979b285192aSMauro Carvalho Chehab 
980b285192aSMauro Carvalho Chehab static struct video_device saa7164_mpeg_template = {
981b285192aSMauro Carvalho Chehab 	.name          = "saa7164",
982b285192aSMauro Carvalho Chehab 	.fops          = &mpeg_fops,
983b285192aSMauro Carvalho Chehab 	.ioctl_ops     = &mpeg_ioctl_ops,
984b285192aSMauro Carvalho Chehab 	.minor         = -1,
985b285192aSMauro Carvalho Chehab 	.tvnorms       = SAA7164_NORMS,
986b285192aSMauro Carvalho Chehab };
987b285192aSMauro Carvalho Chehab 
988b285192aSMauro Carvalho Chehab static struct video_device *saa7164_encoder_alloc(
989b285192aSMauro Carvalho Chehab 	struct saa7164_port *port,
990b285192aSMauro Carvalho Chehab 	struct pci_dev *pci,
991b285192aSMauro Carvalho Chehab 	struct video_device *template,
992b285192aSMauro Carvalho Chehab 	char *type)
993b285192aSMauro Carvalho Chehab {
994b285192aSMauro Carvalho Chehab 	struct video_device *vfd;
995b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
996b285192aSMauro Carvalho Chehab 
997b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s()\n", __func__);
998b285192aSMauro Carvalho Chehab 
999b285192aSMauro Carvalho Chehab 	vfd = video_device_alloc();
1000b285192aSMauro Carvalho Chehab 	if (NULL == vfd)
1001b285192aSMauro Carvalho Chehab 		return NULL;
1002b285192aSMauro Carvalho Chehab 
1003b285192aSMauro Carvalho Chehab 	*vfd = *template;
1004b285192aSMauro Carvalho Chehab 	snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name,
1005b285192aSMauro Carvalho Chehab 		type, saa7164_boards[dev->board].name);
1006b285192aSMauro Carvalho Chehab 
1007d66de790SHans Verkuil 	vfd->v4l2_dev  = &dev->v4l2_dev;
1008b285192aSMauro Carvalho Chehab 	vfd->release = video_device_release;
1009b285192aSMauro Carvalho Chehab 	return vfd;
1010b285192aSMauro Carvalho Chehab }
1011b285192aSMauro Carvalho Chehab 
1012b285192aSMauro Carvalho Chehab int saa7164_encoder_register(struct saa7164_port *port)
1013b285192aSMauro Carvalho Chehab {
1014b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
10151a708ea0SHans Verkuil 	struct v4l2_ctrl_handler *hdl = &port->ctrl_handler;
1016b285192aSMauro Carvalho Chehab 	int result = -ENODEV;
1017b285192aSMauro Carvalho Chehab 
1018b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s()\n", __func__);
1019b285192aSMauro Carvalho Chehab 
10202aefee05SAmitoj Kaur Chawla 	BUG_ON(port->type != SAA7164_MPEG_ENCODER);
1021b285192aSMauro Carvalho Chehab 
1022b285192aSMauro Carvalho Chehab 	/* Sanity check that the PCI configuration space is active */
1023b285192aSMauro Carvalho Chehab 	if (port->hwcfg.BARLocation == 0) {
102424f711c1SMauro Carvalho Chehab 		printk(KERN_ERR "%s() failed (errno = %d), NO PCI configuration\n",
1025b285192aSMauro Carvalho Chehab 			__func__, result);
1026b285192aSMauro Carvalho Chehab 		result = -ENOMEM;
1027b285192aSMauro Carvalho Chehab 		goto failed;
1028b285192aSMauro Carvalho Chehab 	}
1029b285192aSMauro Carvalho Chehab 
1030b285192aSMauro Carvalho Chehab 	/* Establish encoder defaults here */
1031b285192aSMauro Carvalho Chehab 	/* Set default TV standard */
1032b285192aSMauro Carvalho Chehab 	port->encodernorm = saa7164_tvnorms[0];
1033b285192aSMauro Carvalho Chehab 	port->width = 720;
1034b285192aSMauro Carvalho Chehab 	port->mux_input = 1; /* Composite */
1035b285192aSMauro Carvalho Chehab 	port->video_format = EU_VIDEO_FORMAT_MPEG_2;
1036b285192aSMauro Carvalho Chehab 	port->audio_format = 0;
1037b285192aSMauro Carvalho Chehab 	port->video_resolution = 0;
10386b996126SHans Verkuil 	port->freq = SAA7164_TV_MIN_FREQ;
10391a708ea0SHans Verkuil 
10401a708ea0SHans Verkuil 	v4l2_ctrl_handler_init(hdl, 14);
10411a708ea0SHans Verkuil 	v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
10421a708ea0SHans Verkuil 			  V4L2_CID_BRIGHTNESS, 0, 255, 1, 127);
10431a708ea0SHans Verkuil 	v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
10441a708ea0SHans Verkuil 			  V4L2_CID_CONTRAST, 0, 255, 1, 66);
10451a708ea0SHans Verkuil 	v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
10461a708ea0SHans Verkuil 			  V4L2_CID_SATURATION, 0, 255, 1, 62);
10471a708ea0SHans Verkuil 	v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
10481a708ea0SHans Verkuil 			  V4L2_CID_HUE, 0, 255, 1, 128);
10491a708ea0SHans Verkuil 	v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
10501a708ea0SHans Verkuil 			  V4L2_CID_SHARPNESS, 0x0, 0x0f, 1, 8);
10511a708ea0SHans Verkuil 	v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
10521a708ea0SHans Verkuil 			  V4L2_CID_MPEG_AUDIO_MUTE, 0x0, 0x01, 1, 0);
10531a708ea0SHans Verkuil 	v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
10541a708ea0SHans Verkuil 			  V4L2_CID_AUDIO_VOLUME, -83, 24, 1, 20);
10551a708ea0SHans Verkuil 	v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
10561a708ea0SHans Verkuil 			  V4L2_CID_MPEG_VIDEO_BITRATE,
10571a708ea0SHans Verkuil 			  ENCODER_MIN_BITRATE, ENCODER_MAX_BITRATE,
10581a708ea0SHans Verkuil 			  100000, ENCODER_DEF_BITRATE);
10591a708ea0SHans Verkuil 	v4l2_ctrl_new_std_menu(hdl, &saa7164_ctrl_ops,
10601a708ea0SHans Verkuil 			       V4L2_CID_MPEG_STREAM_TYPE,
10611a708ea0SHans Verkuil 			       V4L2_MPEG_STREAM_TYPE_MPEG2_TS, 0,
10621a708ea0SHans Verkuil 			       V4L2_MPEG_STREAM_TYPE_MPEG2_PS);
10631a708ea0SHans Verkuil 	v4l2_ctrl_new_std_menu(hdl, &saa7164_ctrl_ops,
10641a708ea0SHans Verkuil 			       V4L2_CID_MPEG_VIDEO_ASPECT,
10651a708ea0SHans Verkuil 			       V4L2_MPEG_VIDEO_ASPECT_221x100, 0,
10661a708ea0SHans Verkuil 			       V4L2_MPEG_VIDEO_ASPECT_4x3);
10671a708ea0SHans Verkuil 	v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
10681a708ea0SHans Verkuil 			  V4L2_CID_MPEG_VIDEO_GOP_SIZE, 1, 255, 1, 15);
10691a708ea0SHans Verkuil 	v4l2_ctrl_new_std_menu(hdl, &saa7164_ctrl_ops,
10701a708ea0SHans Verkuil 			       V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
10711a708ea0SHans Verkuil 			       V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0,
10721a708ea0SHans Verkuil 			       V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
10731a708ea0SHans Verkuil 	v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
10741a708ea0SHans Verkuil 			  V4L2_CID_MPEG_VIDEO_B_FRAMES, 1, 3, 1, 1);
10751a708ea0SHans Verkuil 	v4l2_ctrl_new_std(hdl, &saa7164_ctrl_ops,
10761a708ea0SHans Verkuil 			  V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
10771a708ea0SHans Verkuil 			  ENCODER_MIN_BITRATE, ENCODER_MAX_BITRATE,
10781a708ea0SHans Verkuil 			  100000, ENCODER_DEF_BITRATE);
10791a708ea0SHans Verkuil 	if (hdl->error) {
10801a708ea0SHans Verkuil 		result = hdl->error;
10811a708ea0SHans Verkuil 		goto failed;
10821a708ea0SHans Verkuil 	}
10831a708ea0SHans Verkuil 
10848d2d41e9SHans Verkuil 	port->std = V4L2_STD_NTSC_M;
1085b285192aSMauro Carvalho Chehab 
1086b285192aSMauro Carvalho Chehab 	if (port->encodernorm.id & V4L2_STD_525_60)
1087b285192aSMauro Carvalho Chehab 		port->height = 480;
1088b285192aSMauro Carvalho Chehab 	else
1089b285192aSMauro Carvalho Chehab 		port->height = 576;
1090b285192aSMauro Carvalho Chehab 
1091b285192aSMauro Carvalho Chehab 	/* Allocate and register the video device node */
1092b285192aSMauro Carvalho Chehab 	port->v4l_device = saa7164_encoder_alloc(port,
1093b285192aSMauro Carvalho Chehab 		dev->pci, &saa7164_mpeg_template, "mpeg");
1094b285192aSMauro Carvalho Chehab 
1095b285192aSMauro Carvalho Chehab 	if (!port->v4l_device) {
1096b285192aSMauro Carvalho Chehab 		printk(KERN_INFO "%s: can't allocate mpeg device\n",
1097b285192aSMauro Carvalho Chehab 			dev->name);
1098b285192aSMauro Carvalho Chehab 		result = -ENOMEM;
1099b285192aSMauro Carvalho Chehab 		goto failed;
1100b285192aSMauro Carvalho Chehab 	}
1101b285192aSMauro Carvalho Chehab 
11021a708ea0SHans Verkuil 	port->v4l_device->ctrl_handler = hdl;
11031a708ea0SHans Verkuil 	v4l2_ctrl_handler_setup(hdl);
1104b285192aSMauro Carvalho Chehab 	video_set_drvdata(port->v4l_device, port);
1105b285192aSMauro Carvalho Chehab 	result = video_register_device(port->v4l_device,
1106b285192aSMauro Carvalho Chehab 		VFL_TYPE_GRABBER, -1);
1107b285192aSMauro Carvalho Chehab 	if (result < 0) {
1108b285192aSMauro Carvalho Chehab 		printk(KERN_INFO "%s: can't register mpeg device\n",
1109b285192aSMauro Carvalho Chehab 			dev->name);
1110b285192aSMauro Carvalho Chehab 		/* TODO: We're going to leak here if we don't dealloc
1111b285192aSMauro Carvalho Chehab 		 The buffers above. The unreg function can't deal wit it.
1112b285192aSMauro Carvalho Chehab 		*/
1113b285192aSMauro Carvalho Chehab 		goto failed;
1114b285192aSMauro Carvalho Chehab 	}
1115b285192aSMauro Carvalho Chehab 
1116b285192aSMauro Carvalho Chehab 	printk(KERN_INFO "%s: registered device video%d [mpeg]\n",
1117b285192aSMauro Carvalho Chehab 		dev->name, port->v4l_device->num);
1118b285192aSMauro Carvalho Chehab 
1119b285192aSMauro Carvalho Chehab 	/* Configure the hardware defaults */
1120b285192aSMauro Carvalho Chehab 	saa7164_api_set_videomux(port);
1121b285192aSMauro Carvalho Chehab 	saa7164_api_set_usercontrol(port, PU_BRIGHTNESS_CONTROL);
1122b285192aSMauro Carvalho Chehab 	saa7164_api_set_usercontrol(port, PU_CONTRAST_CONTROL);
1123b285192aSMauro Carvalho Chehab 	saa7164_api_set_usercontrol(port, PU_HUE_CONTROL);
1124b285192aSMauro Carvalho Chehab 	saa7164_api_set_usercontrol(port, PU_SATURATION_CONTROL);
1125b285192aSMauro Carvalho Chehab 	saa7164_api_set_usercontrol(port, PU_SHARPNESS_CONTROL);
1126b285192aSMauro Carvalho Chehab 	saa7164_api_audio_mute(port, 0);
1127b285192aSMauro Carvalho Chehab 	saa7164_api_set_audio_volume(port, 20);
1128b285192aSMauro Carvalho Chehab 	saa7164_api_set_aspect_ratio(port);
1129b285192aSMauro Carvalho Chehab 
1130b285192aSMauro Carvalho Chehab 	/* Disable audio standard detection, it's buggy */
1131b285192aSMauro Carvalho Chehab 	saa7164_api_set_audio_detection(port, 0);
1132b285192aSMauro Carvalho Chehab 
1133b285192aSMauro Carvalho Chehab 	saa7164_api_set_encoder(port);
1134b285192aSMauro Carvalho Chehab 	saa7164_api_get_encoder(port);
1135b285192aSMauro Carvalho Chehab 
1136b285192aSMauro Carvalho Chehab 	result = 0;
1137b285192aSMauro Carvalho Chehab failed:
1138b285192aSMauro Carvalho Chehab 	return result;
1139b285192aSMauro Carvalho Chehab }
1140b285192aSMauro Carvalho Chehab 
1141b285192aSMauro Carvalho Chehab void saa7164_encoder_unregister(struct saa7164_port *port)
1142b285192aSMauro Carvalho Chehab {
1143b285192aSMauro Carvalho Chehab 	struct saa7164_dev *dev = port->dev;
1144b285192aSMauro Carvalho Chehab 
1145b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s(port=%d)\n", __func__, port->nr);
1146b285192aSMauro Carvalho Chehab 
11472aefee05SAmitoj Kaur Chawla 	BUG_ON(port->type != SAA7164_MPEG_ENCODER);
1148b285192aSMauro Carvalho Chehab 
1149b285192aSMauro Carvalho Chehab 	if (port->v4l_device) {
1150b285192aSMauro Carvalho Chehab 		if (port->v4l_device->minor != -1)
1151b285192aSMauro Carvalho Chehab 			video_unregister_device(port->v4l_device);
1152b285192aSMauro Carvalho Chehab 		else
1153b285192aSMauro Carvalho Chehab 			video_device_release(port->v4l_device);
1154b285192aSMauro Carvalho Chehab 
1155b285192aSMauro Carvalho Chehab 		port->v4l_device = NULL;
1156b285192aSMauro Carvalho Chehab 	}
11571a708ea0SHans Verkuil 	v4l2_ctrl_handler_free(&port->ctrl_handler);
1158b285192aSMauro Carvalho Chehab 
1159b285192aSMauro Carvalho Chehab 	dprintk(DBGLVL_ENC, "%s(port=%d) done\n", __func__, port->nr);
1160b285192aSMauro Carvalho Chehab }
1161b285192aSMauro Carvalho Chehab 
1162