11da177e4SLinus Torvalds /* radio-aztech.c - Aztech radio card driver for Linux 2.2
21da177e4SLinus Torvalds  *
3a4366af4SMauro Carvalho Chehab  * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
41da177e4SLinus Torvalds  * Adapted to support the Video for Linux API by
51da177e4SLinus Torvalds  * Russell Kroll <rkroll@exploits.org>.  Based on original tuner code by:
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * Quay Ly
81da177e4SLinus Torvalds  * Donald Song
91da177e4SLinus Torvalds  * Jason Lewis      (jlewis@twilight.vtc.vsc.edu)
101da177e4SLinus Torvalds  * Scott McGrath    (smcgrath@twilight.vtc.vsc.edu)
111da177e4SLinus Torvalds  * William McGrath  (wmcgrath@twilight.vtc.vsc.edu)
121da177e4SLinus Torvalds  *
131da177e4SLinus Torvalds  * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/
141da177e4SLinus Torvalds  * along with more information on the card itself.
151da177e4SLinus Torvalds  *
161da177e4SLinus Torvalds  * History:
171da177e4SLinus Torvalds  * 1999-02-24	Russell Kroll <rkroll@exploits.org>
181da177e4SLinus Torvalds  *		Fine tuning/VIDEO_TUNER_LOW
191da177e4SLinus Torvalds  * 		Range expanded to 87-108 MHz (from 87.9-107.8)
201da177e4SLinus Torvalds  *
211da177e4SLinus Torvalds  * Notable changes from the original source:
221da177e4SLinus Torvalds  * - includes stripped down to the essentials
231da177e4SLinus Torvalds  * - for loops used as delays replaced with udelay()
241da177e4SLinus Torvalds  * - #defines removed, changed to static values
251da177e4SLinus Torvalds  * - tuning structure changed - no more character arrays, other changes
261da177e4SLinus Torvalds */
271da177e4SLinus Torvalds 
281da177e4SLinus Torvalds #include <linux/module.h>	/* Modules 			*/
291da177e4SLinus Torvalds #include <linux/init.h>		/* Initdata			*/
30fb911ee8SPeter Osterlund #include <linux/ioport.h>	/* request_region		*/
311da177e4SLinus Torvalds #include <linux/delay.h>	/* udelay			*/
32a4366af4SMauro Carvalho Chehab #include <linux/videodev2.h>	/* kernel radio structs		*/
33e697e12eSHans Verkuil #include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
34e697e12eSHans Verkuil #include <linux/io.h>		/* outb, outb_p			*/
35e697e12eSHans Verkuil #include <linux/uaccess.h>	/* copy to/from user		*/
36e697e12eSHans Verkuil #include <media/v4l2-device.h>
3735ea11ffSHans Verkuil #include <media/v4l2-ioctl.h>
381da177e4SLinus Torvalds 
39e697e12eSHans Verkuil MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
40e697e12eSHans Verkuil MODULE_DESCRIPTION("A driver for the Aztech radio card.");
41e697e12eSHans Verkuil MODULE_LICENSE("GPL");
42a4366af4SMauro Carvalho Chehab 
431da177e4SLinus Torvalds /* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds #ifndef CONFIG_RADIO_AZTECH_PORT
461da177e4SLinus Torvalds #define CONFIG_RADIO_AZTECH_PORT -1
471da177e4SLinus Torvalds #endif
481da177e4SLinus Torvalds 
491da177e4SLinus Torvalds static int io = CONFIG_RADIO_AZTECH_PORT;
501da177e4SLinus Torvalds static int radio_nr = -1;
511da177e4SLinus Torvalds static int radio_wait_time = 1000;
521da177e4SLinus Torvalds 
53e697e12eSHans Verkuil module_param(io, int, 0);
54e697e12eSHans Verkuil module_param(radio_nr, int, 0);
55e697e12eSHans Verkuil MODULE_PARM_DESC(io, "I/O address of the Aztech card (0x350 or 0x358)");
56e697e12eSHans Verkuil 
57e697e12eSHans Verkuil #define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
58e697e12eSHans Verkuil 
59e697e12eSHans Verkuil struct aztech
601da177e4SLinus Torvalds {
61e697e12eSHans Verkuil 	struct v4l2_device v4l2_dev;
62e697e12eSHans Verkuil 	struct video_device vdev;
63e697e12eSHans Verkuil 	int io;
641da177e4SLinus Torvalds 	int curvol;
651da177e4SLinus Torvalds 	unsigned long curfreq;
661da177e4SLinus Torvalds 	int stereo;
67e697e12eSHans Verkuil 	struct mutex lock;
681da177e4SLinus Torvalds };
691da177e4SLinus Torvalds 
70e697e12eSHans Verkuil static struct aztech aztech_card;
71e697e12eSHans Verkuil 
721da177e4SLinus Torvalds static int volconvert(int level)
731da177e4SLinus Torvalds {
741da177e4SLinus Torvalds 	level >>= 14;		/* Map 16bits down to 2 bit */
751da177e4SLinus Torvalds 	level &= 3;
761da177e4SLinus Torvalds 
771da177e4SLinus Torvalds 	/* convert to card-friendly values */
78e697e12eSHans Verkuil 	switch (level) {
791da177e4SLinus Torvalds 	case 0:
801da177e4SLinus Torvalds 		return 0;
811da177e4SLinus Torvalds 	case 1:
821da177e4SLinus Torvalds 		return 1;
831da177e4SLinus Torvalds 	case 2:
841da177e4SLinus Torvalds 		return 4;
851da177e4SLinus Torvalds 	case 3:
861da177e4SLinus Torvalds 		return 5;
871da177e4SLinus Torvalds 	}
881da177e4SLinus Torvalds 	return 0;	/* Quieten gcc */
891da177e4SLinus Torvalds }
901da177e4SLinus Torvalds 
91e697e12eSHans Verkuil static void send_0_byte(struct aztech *az)
921da177e4SLinus Torvalds {
931da177e4SLinus Torvalds 	udelay(radio_wait_time);
94e697e12eSHans Verkuil 	outb_p(2 + volconvert(az->curvol), az->io);
95e697e12eSHans Verkuil 	outb_p(64 + 2 + volconvert(az->curvol), az->io);
961da177e4SLinus Torvalds }
971da177e4SLinus Torvalds 
98e697e12eSHans Verkuil static void send_1_byte(struct aztech *az)
991da177e4SLinus Torvalds {
1001da177e4SLinus Torvalds 	udelay (radio_wait_time);
101e697e12eSHans Verkuil 	outb_p(128 + 2 + volconvert(az->curvol), az->io);
102e697e12eSHans Verkuil 	outb_p(128 + 64 + 2 + volconvert(az->curvol), az->io);
1031da177e4SLinus Torvalds }
1041da177e4SLinus Torvalds 
105e697e12eSHans Verkuil static int az_setvol(struct aztech *az, int vol)
1061da177e4SLinus Torvalds {
107e697e12eSHans Verkuil 	mutex_lock(&az->lock);
108e697e12eSHans Verkuil 	outb(volconvert(vol), az->io);
109e697e12eSHans Verkuil 	mutex_unlock(&az->lock);
1101da177e4SLinus Torvalds 	return 0;
1111da177e4SLinus Torvalds }
1121da177e4SLinus Torvalds 
1131da177e4SLinus Torvalds /* thanks to Michael Dwyer for giving me a dose of clues in
1141da177e4SLinus Torvalds  * the signal strength department..
1151da177e4SLinus Torvalds  *
1161da177e4SLinus Torvalds  * This card has a stereo bit - bit 0 set = mono, not set = stereo
1171da177e4SLinus Torvalds  * It also has a "signal" bit - bit 1 set = bad signal, not set = good
1181da177e4SLinus Torvalds  *
1191da177e4SLinus Torvalds  */
1201da177e4SLinus Torvalds 
121e697e12eSHans Verkuil static int az_getsigstr(struct aztech *az)
1221da177e4SLinus Torvalds {
123e697e12eSHans Verkuil 	int sig = 1;
124e697e12eSHans Verkuil 
125e697e12eSHans Verkuil 	mutex_lock(&az->lock);
126e697e12eSHans Verkuil 	if (inb(az->io) & 2)	/* bit set = no signal present */
127e697e12eSHans Verkuil 		sig = 0;
128e697e12eSHans Verkuil 	mutex_unlock(&az->lock);
129e697e12eSHans Verkuil 	return sig;
1301da177e4SLinus Torvalds }
1311da177e4SLinus Torvalds 
132e697e12eSHans Verkuil static int az_getstereo(struct aztech *az)
1331da177e4SLinus Torvalds {
134e697e12eSHans Verkuil 	int stereo = 1;
135e697e12eSHans Verkuil 
136e697e12eSHans Verkuil 	mutex_lock(&az->lock);
137e697e12eSHans Verkuil 	if (inb(az->io) & 1) 	/* bit set = mono */
138e697e12eSHans Verkuil 		stereo = 0;
139e697e12eSHans Verkuil 	mutex_unlock(&az->lock);
140e697e12eSHans Verkuil 	return stereo;
1411da177e4SLinus Torvalds }
1421da177e4SLinus Torvalds 
143e697e12eSHans Verkuil static int az_setfreq(struct aztech *az, unsigned long frequency)
1441da177e4SLinus Torvalds {
1451da177e4SLinus Torvalds 	int  i;
1461da177e4SLinus Torvalds 
147e697e12eSHans Verkuil 	mutex_lock(&az->lock);
148e697e12eSHans Verkuil 
149e697e12eSHans Verkuil 	az->curfreq = frequency;
1501da177e4SLinus Torvalds 	frequency += 171200;		/* Add 10.7 MHz IF		*/
1511da177e4SLinus Torvalds 	frequency /= 800;		/* Convert to 50 kHz units	*/
1521da177e4SLinus Torvalds 
153e697e12eSHans Verkuil 	send_0_byte(az);		/*  0: LSB of frequency       */
1541da177e4SLinus Torvalds 
1551da177e4SLinus Torvalds 	for (i = 0; i < 13; i++)	/*   : frequency bits (1-13)  */
1561da177e4SLinus Torvalds 		if (frequency & (1 << i))
157e697e12eSHans Verkuil 			send_1_byte(az);
1581da177e4SLinus Torvalds 		else
159e697e12eSHans Verkuil 			send_0_byte(az);
1601da177e4SLinus Torvalds 
161e697e12eSHans Verkuil 	send_0_byte(az);		/* 14: test bit - always 0    */
162e697e12eSHans Verkuil 	send_0_byte(az);		/* 15: test bit - always 0    */
163e697e12eSHans Verkuil 	send_0_byte(az);		/* 16: band data 0 - always 0 */
164e697e12eSHans Verkuil 	if (az->stereo)		/* 17: stereo (1 to enable)   */
165e697e12eSHans Verkuil 		send_1_byte(az);
1661da177e4SLinus Torvalds 	else
167e697e12eSHans Verkuil 		send_0_byte(az);
1681da177e4SLinus Torvalds 
169e697e12eSHans Verkuil 	send_1_byte(az);		/* 18: band data 1 - unknown  */
170e697e12eSHans Verkuil 	send_0_byte(az);		/* 19: time base - always 0   */
171e697e12eSHans Verkuil 	send_0_byte(az);		/* 20: spacing (0 = 25 kHz)   */
172e697e12eSHans Verkuil 	send_1_byte(az);		/* 21: spacing (1 = 25 kHz)   */
173e697e12eSHans Verkuil 	send_0_byte(az);		/* 22: spacing (0 = 25 kHz)   */
174e697e12eSHans Verkuil 	send_1_byte(az);		/* 23: AM/FM (FM = 1, always) */
1751da177e4SLinus Torvalds 
1761da177e4SLinus Torvalds 	/* latch frequency */
1771da177e4SLinus Torvalds 
1781da177e4SLinus Torvalds 	udelay(radio_wait_time);
179e697e12eSHans Verkuil 	outb_p(128 + 64 + volconvert(az->curvol), az->io);
1801da177e4SLinus Torvalds 
181e697e12eSHans Verkuil 	mutex_unlock(&az->lock);
1821da177e4SLinus Torvalds 
1831da177e4SLinus Torvalds 	return 0;
1841da177e4SLinus Torvalds }
1851da177e4SLinus Torvalds 
18699218fe4SMauro Carvalho Chehab static int vidioc_querycap(struct file *file, void  *priv,
18799218fe4SMauro Carvalho Chehab 					struct v4l2_capability *v)
1881da177e4SLinus Torvalds {
189a4366af4SMauro Carvalho Chehab 	strlcpy(v->driver, "radio-aztech", sizeof(v->driver));
190a4366af4SMauro Carvalho Chehab 	strlcpy(v->card, "Aztech Radio", sizeof(v->card));
191e697e12eSHans Verkuil 	strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
192a4366af4SMauro Carvalho Chehab 	v->version = RADIO_VERSION;
193e697e12eSHans Verkuil 	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
1941da177e4SLinus Torvalds 	return 0;
1951da177e4SLinus Torvalds }
19699218fe4SMauro Carvalho Chehab 
19799218fe4SMauro Carvalho Chehab static int vidioc_g_tuner(struct file *file, void *priv,
19899218fe4SMauro Carvalho Chehab 				struct v4l2_tuner *v)
1991da177e4SLinus Torvalds {
200e697e12eSHans Verkuil 	struct aztech *az = video_drvdata(file);
201a4366af4SMauro Carvalho Chehab 
202a4366af4SMauro Carvalho Chehab 	if (v->index > 0)
2031da177e4SLinus Torvalds 		return -EINVAL;
204a4366af4SMauro Carvalho Chehab 
205e697e12eSHans Verkuil 	strlcpy(v->name, "FM", sizeof(v->name));
206a4366af4SMauro Carvalho Chehab 	v->type = V4L2_TUNER_RADIO;
207a4366af4SMauro Carvalho Chehab 
208e697e12eSHans Verkuil 	v->rangelow = 87 * 16000;
209e697e12eSHans Verkuil 	v->rangehigh = 108 * 16000;
210a4366af4SMauro Carvalho Chehab 	v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
211a4366af4SMauro Carvalho Chehab 	v->capability = V4L2_TUNER_CAP_LOW;
2121da177e4SLinus Torvalds 	if (az_getstereo(az))
213a4366af4SMauro Carvalho Chehab 		v->audmode = V4L2_TUNER_MODE_STEREO;
214a4366af4SMauro Carvalho Chehab 	else
215a4366af4SMauro Carvalho Chehab 		v->audmode = V4L2_TUNER_MODE_MONO;
216a4366af4SMauro Carvalho Chehab 	v->signal = 0xFFFF * az_getsigstr(az);
217a4366af4SMauro Carvalho Chehab 
2181da177e4SLinus Torvalds 	return 0;
2191da177e4SLinus Torvalds }
220a4366af4SMauro Carvalho Chehab 
22199218fe4SMauro Carvalho Chehab static int vidioc_s_tuner(struct file *file, void *priv,
22299218fe4SMauro Carvalho Chehab 				struct v4l2_tuner *v)
22399218fe4SMauro Carvalho Chehab {
224e697e12eSHans Verkuil 	return v->index ? -EINVAL : 0;
225676b0ac7SMauro Carvalho Chehab }
226676b0ac7SMauro Carvalho Chehab 
227a0c05ab9SMauro Carvalho Chehab static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
228a0c05ab9SMauro Carvalho Chehab {
229a0c05ab9SMauro Carvalho Chehab 	*i = 0;
230a0c05ab9SMauro Carvalho Chehab 	return 0;
231a0c05ab9SMauro Carvalho Chehab }
232a0c05ab9SMauro Carvalho Chehab 
233a0c05ab9SMauro Carvalho Chehab static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
234a0c05ab9SMauro Carvalho Chehab {
235e697e12eSHans Verkuil 	return i ? -EINVAL : 0;
236a0c05ab9SMauro Carvalho Chehab }
237a0c05ab9SMauro Carvalho Chehab 
238e697e12eSHans Verkuil static int vidioc_g_audio(struct file *file, void *priv,
239e697e12eSHans Verkuil 			   struct v4l2_audio *a)
240e697e12eSHans Verkuil {
241e697e12eSHans Verkuil 	a->index = 0;
242e697e12eSHans Verkuil 	strlcpy(a->name, "Radio", sizeof(a->name));
243e697e12eSHans Verkuil 	a->capability = V4L2_AUDCAP_STEREO;
244e697e12eSHans Verkuil 	return 0;
245e697e12eSHans Verkuil }
246a0c05ab9SMauro Carvalho Chehab 
247676b0ac7SMauro Carvalho Chehab static int vidioc_s_audio(struct file *file, void *priv,
248676b0ac7SMauro Carvalho Chehab 			   struct v4l2_audio *a)
249676b0ac7SMauro Carvalho Chehab {
250e697e12eSHans Verkuil 	return a->index ? -EINVAL : 0;
251676b0ac7SMauro Carvalho Chehab }
252676b0ac7SMauro Carvalho Chehab 
25399218fe4SMauro Carvalho Chehab static int vidioc_s_frequency(struct file *file, void *priv,
25499218fe4SMauro Carvalho Chehab 				struct v4l2_frequency *f)
2551da177e4SLinus Torvalds {
256e697e12eSHans Verkuil 	struct aztech *az = video_drvdata(file);
257a4366af4SMauro Carvalho Chehab 
258e697e12eSHans Verkuil 	az_setfreq(az, f->frequency);
2591da177e4SLinus Torvalds 	return 0;
2601da177e4SLinus Torvalds }
26199218fe4SMauro Carvalho Chehab 
26299218fe4SMauro Carvalho Chehab static int vidioc_g_frequency(struct file *file, void *priv,
26399218fe4SMauro Carvalho Chehab 				struct v4l2_frequency *f)
2641da177e4SLinus Torvalds {
265e697e12eSHans Verkuil 	struct aztech *az = video_drvdata(file);
2661da177e4SLinus Torvalds 
267a4366af4SMauro Carvalho Chehab 	f->type = V4L2_TUNER_RADIO;
268a4366af4SMauro Carvalho Chehab 	f->frequency = az->curfreq;
2691da177e4SLinus Torvalds 	return 0;
2701da177e4SLinus Torvalds }
271a4366af4SMauro Carvalho Chehab 
27299218fe4SMauro Carvalho Chehab static int vidioc_queryctrl(struct file *file, void *priv,
27399218fe4SMauro Carvalho Chehab 			    struct v4l2_queryctrl *qc)
274a4366af4SMauro Carvalho Chehab {
275e697e12eSHans Verkuil 	switch (qc->id) {
276e697e12eSHans Verkuil 	case V4L2_CID_AUDIO_MUTE:
277e697e12eSHans Verkuil 		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
278e697e12eSHans Verkuil 	case V4L2_CID_AUDIO_VOLUME:
279e697e12eSHans Verkuil 		return v4l2_ctrl_query_fill(qc, 0, 0xff, 1, 0xff);
280a4366af4SMauro Carvalho Chehab 	}
281a4366af4SMauro Carvalho Chehab 	return -EINVAL;
282a4366af4SMauro Carvalho Chehab }
28399218fe4SMauro Carvalho Chehab 
28499218fe4SMauro Carvalho Chehab static int vidioc_g_ctrl(struct file *file, void *priv,
28599218fe4SMauro Carvalho Chehab 			    struct v4l2_control *ctrl)
286a4366af4SMauro Carvalho Chehab {
287e697e12eSHans Verkuil 	struct aztech *az = video_drvdata(file);
288a4366af4SMauro Carvalho Chehab 
289a4366af4SMauro Carvalho Chehab 	switch (ctrl->id) {
290a4366af4SMauro Carvalho Chehab 	case V4L2_CID_AUDIO_MUTE:
291a4366af4SMauro Carvalho Chehab 		if (az->curvol == 0)
292a4366af4SMauro Carvalho Chehab 			ctrl->value = 1;
293a4366af4SMauro Carvalho Chehab 		else
294a4366af4SMauro Carvalho Chehab 			ctrl->value = 0;
295e697e12eSHans Verkuil 		return 0;
296a4366af4SMauro Carvalho Chehab 	case V4L2_CID_AUDIO_VOLUME:
297a4366af4SMauro Carvalho Chehab 		ctrl->value = az->curvol * 6554;
298e697e12eSHans Verkuil 		return 0;
299a4366af4SMauro Carvalho Chehab 	}
300a4366af4SMauro Carvalho Chehab 	return -EINVAL;
301a4366af4SMauro Carvalho Chehab }
30299218fe4SMauro Carvalho Chehab 
30399218fe4SMauro Carvalho Chehab static int vidioc_s_ctrl(struct file *file, void *priv,
30499218fe4SMauro Carvalho Chehab 			    struct v4l2_control *ctrl)
305a4366af4SMauro Carvalho Chehab {
306e697e12eSHans Verkuil 	struct aztech *az = video_drvdata(file);
307a4366af4SMauro Carvalho Chehab 
308a4366af4SMauro Carvalho Chehab 	switch (ctrl->id) {
309a4366af4SMauro Carvalho Chehab 	case V4L2_CID_AUDIO_MUTE:
310e697e12eSHans Verkuil 		if (ctrl->value)
311a4366af4SMauro Carvalho Chehab 			az_setvol(az, 0);
312e697e12eSHans Verkuil 		else
313a4366af4SMauro Carvalho Chehab 			az_setvol(az, az->curvol);
314e697e12eSHans Verkuil 		return 0;
315a4366af4SMauro Carvalho Chehab 	case V4L2_CID_AUDIO_VOLUME:
316a4366af4SMauro Carvalho Chehab 		az_setvol(az, ctrl->value);
317e697e12eSHans Verkuil 		return 0;
318a4366af4SMauro Carvalho Chehab 	}
319a4366af4SMauro Carvalho Chehab 	return -EINVAL;
320a4366af4SMauro Carvalho Chehab }
321a4366af4SMauro Carvalho Chehab 
322e697e12eSHans Verkuil static int aztech_open(struct file *file)
3233ca685aaSHans Verkuil {
324e697e12eSHans Verkuil 	return 0;
3253ca685aaSHans Verkuil }
3263ca685aaSHans Verkuil 
327e697e12eSHans Verkuil static int aztech_release(struct file *file)
3283ca685aaSHans Verkuil {
3293ca685aaSHans Verkuil 	return 0;
3303ca685aaSHans Verkuil }
3313ca685aaSHans Verkuil 
332bec43661SHans Verkuil static const struct v4l2_file_operations aztech_fops = {
3331da177e4SLinus Torvalds 	.owner		= THIS_MODULE,
334e697e12eSHans Verkuil 	.open           = aztech_open,
335e697e12eSHans Verkuil 	.release        = aztech_release,
33699218fe4SMauro Carvalho Chehab 	.ioctl		= video_ioctl2,
3371da177e4SLinus Torvalds };
3381da177e4SLinus Torvalds 
339a399810cSHans Verkuil static const struct v4l2_ioctl_ops aztech_ioctl_ops = {
34099218fe4SMauro Carvalho Chehab 	.vidioc_querycap    = vidioc_querycap,
34199218fe4SMauro Carvalho Chehab 	.vidioc_g_tuner     = vidioc_g_tuner,
34299218fe4SMauro Carvalho Chehab 	.vidioc_s_tuner     = vidioc_s_tuner,
343676b0ac7SMauro Carvalho Chehab 	.vidioc_g_audio     = vidioc_g_audio,
344676b0ac7SMauro Carvalho Chehab 	.vidioc_s_audio     = vidioc_s_audio,
345a0c05ab9SMauro Carvalho Chehab 	.vidioc_g_input     = vidioc_g_input,
346a0c05ab9SMauro Carvalho Chehab 	.vidioc_s_input     = vidioc_s_input,
34799218fe4SMauro Carvalho Chehab 	.vidioc_g_frequency = vidioc_g_frequency,
34899218fe4SMauro Carvalho Chehab 	.vidioc_s_frequency = vidioc_s_frequency,
34999218fe4SMauro Carvalho Chehab 	.vidioc_queryctrl   = vidioc_queryctrl,
35099218fe4SMauro Carvalho Chehab 	.vidioc_g_ctrl      = vidioc_g_ctrl,
35199218fe4SMauro Carvalho Chehab 	.vidioc_s_ctrl      = vidioc_s_ctrl,
3521da177e4SLinus Torvalds };
3531da177e4SLinus Torvalds 
3541da177e4SLinus Torvalds static int __init aztech_init(void)
3551da177e4SLinus Torvalds {
356e697e12eSHans Verkuil 	struct aztech *az = &aztech_card;
357e697e12eSHans Verkuil 	struct v4l2_device *v4l2_dev = &az->v4l2_dev;
358e697e12eSHans Verkuil 	int res;
359e697e12eSHans Verkuil 
360e697e12eSHans Verkuil 	strlcpy(v4l2_dev->name, "aztech", sizeof(v4l2_dev->name));
361e697e12eSHans Verkuil 	az->io = io;
362e697e12eSHans Verkuil 
363e697e12eSHans Verkuil 	if (az->io == -1) {
364e697e12eSHans Verkuil 		v4l2_err(v4l2_dev, "you must set an I/O address with io=0x???\n");
3651da177e4SLinus Torvalds 		return -EINVAL;
3661da177e4SLinus Torvalds 	}
3671da177e4SLinus Torvalds 
368e697e12eSHans Verkuil 	if (!request_region(az->io, 2, "aztech")) {
369e697e12eSHans Verkuil 		v4l2_err(v4l2_dev, "port 0x%x already in use\n", az->io);
3701da177e4SLinus Torvalds 		return -EBUSY;
3711da177e4SLinus Torvalds 	}
3721da177e4SLinus Torvalds 
373e697e12eSHans Verkuil 	res = v4l2_device_register(NULL, v4l2_dev);
374e697e12eSHans Verkuil 	if (res < 0) {
375e697e12eSHans Verkuil 		release_region(az->io, 2);
376e697e12eSHans Verkuil 		v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
377e697e12eSHans Verkuil 		return res;
378e697e12eSHans Verkuil 	}
3791da177e4SLinus Torvalds 
380e697e12eSHans Verkuil 	mutex_init(&az->lock);
381e697e12eSHans Verkuil 	strlcpy(az->vdev.name, v4l2_dev->name, sizeof(az->vdev.name));
382e697e12eSHans Verkuil 	az->vdev.v4l2_dev = v4l2_dev;
383e697e12eSHans Verkuil 	az->vdev.fops = &aztech_fops;
384e697e12eSHans Verkuil 	az->vdev.ioctl_ops = &aztech_ioctl_ops;
385e697e12eSHans Verkuil 	az->vdev.release = video_device_release_empty;
386e697e12eSHans Verkuil 	video_set_drvdata(&az->vdev, az);
387e697e12eSHans Verkuil 
388e697e12eSHans Verkuil 	if (video_register_device(&az->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
389e697e12eSHans Verkuil 		v4l2_device_unregister(v4l2_dev);
390e697e12eSHans Verkuil 		release_region(az->io, 2);
3911da177e4SLinus Torvalds 		return -EINVAL;
3921da177e4SLinus Torvalds 	}
3931da177e4SLinus Torvalds 
394e697e12eSHans Verkuil 	v4l2_info(v4l2_dev, "Aztech radio card driver v1.00/19990224 rkroll@exploits.org\n");
3951da177e4SLinus Torvalds 	/* mute card - prevents noisy bootups */
396e697e12eSHans Verkuil 	outb(0, az->io);
3971da177e4SLinus Torvalds 	return 0;
3981da177e4SLinus Torvalds }
3991da177e4SLinus Torvalds 
400e697e12eSHans Verkuil static void __exit aztech_exit(void)
4011da177e4SLinus Torvalds {
402e697e12eSHans Verkuil 	struct aztech *az = &aztech_card;
403e697e12eSHans Verkuil 
404e697e12eSHans Verkuil 	video_unregister_device(&az->vdev);
405e697e12eSHans Verkuil 	v4l2_device_unregister(&az->v4l2_dev);
406e697e12eSHans Verkuil 	release_region(az->io, 2);
4071da177e4SLinus Torvalds }
4081da177e4SLinus Torvalds 
4091da177e4SLinus Torvalds module_init(aztech_init);
410e697e12eSHans Verkuil module_exit(aztech_exit);
411