| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* GemTek radio card driver for Linux (C) 1998 Jonas Munsin <jmunsin@iki.fi> | 
 | 2 |  * | 
 | 3 |  * GemTek hasn't released any specs on the card, so the protocol had to | 
 | 4 |  * be reverse engineered with dosemu. | 
 | 5 |  * | 
 | 6 |  * Besides the protocol changes, this is mostly a copy of: | 
 | 7 |  * | 
 | 8 |  *    RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff | 
| Mauro Carvalho Chehab | 4286c6f | 2006-04-08 16:06:16 -0300 | [diff] [blame] | 9 |  * | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 10 |  *    Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood | 
| Alan Cox | d9b0144 | 2008-10-27 15:13:47 -0300 | [diff] [blame] | 11 |  *    Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 12 |  *    Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org> | 
 | 13 |  * | 
 | 14 |  * TODO: Allow for more than one of these foolish entities :-) | 
 | 15 |  * | 
| Mauro Carvalho Chehab | d1c4ecd | 2006-08-08 09:10:01 -0300 | [diff] [blame] | 16 |  * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 17 |  */ | 
 | 18 |  | 
 | 19 | #include <linux/module.h>	/* Modules 			*/ | 
 | 20 | #include <linux/init.h>		/* Initdata			*/ | 
| Peter Osterlund | fb911ee | 2005-09-13 01:25:15 -0700 | [diff] [blame] | 21 | #include <linux/ioport.h>	/* request_region		*/ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 22 | #include <linux/delay.h>	/* udelay			*/ | 
| Mauro Carvalho Chehab | d1c4ecd | 2006-08-08 09:10:01 -0300 | [diff] [blame] | 23 | #include <linux/videodev2.h>	/* kernel radio structs		*/ | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 24 | #include <linux/version.h>	/* for KERNEL_VERSION MACRO	*/ | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 25 | #include <linux/mutex.h> | 
 | 26 | #include <linux/io.h>		/* outb, outb_p			*/ | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 27 | #include <media/v4l2-ioctl.h> | 
 | 28 | #include <media/v4l2-device.h> | 
 | 29 |  | 
 | 30 | #define RADIO_VERSION KERNEL_VERSION(0, 0, 3) | 
| Mauro Carvalho Chehab | d1c4ecd | 2006-08-08 09:10:01 -0300 | [diff] [blame] | 31 |  | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 32 | /* | 
 | 33 |  * Module info. | 
 | 34 |  */ | 
 | 35 |  | 
 | 36 | MODULE_AUTHOR("Jonas Munsin, Pekka Seppänen <pexu@kapsi.fi>"); | 
 | 37 | MODULE_DESCRIPTION("A driver for the GemTek Radio card."); | 
 | 38 | MODULE_LICENSE("GPL"); | 
 | 39 |  | 
 | 40 | /* | 
 | 41 |  * Module params. | 
 | 42 |  */ | 
| Mauro Carvalho Chehab | d1c4ecd | 2006-08-08 09:10:01 -0300 | [diff] [blame] | 43 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 44 | #ifndef CONFIG_RADIO_GEMTEK_PORT | 
 | 45 | #define CONFIG_RADIO_GEMTEK_PORT -1 | 
 | 46 | #endif | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 47 | #ifndef CONFIG_RADIO_GEMTEK_PROBE | 
 | 48 | #define CONFIG_RADIO_GEMTEK_PROBE 1 | 
 | 49 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 50 |  | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 51 | static int io		= CONFIG_RADIO_GEMTEK_PORT; | 
 | 52 | static int probe	= CONFIG_RADIO_GEMTEK_PROBE; | 
 | 53 | static int hardmute; | 
 | 54 | static int shutdown	= 1; | 
 | 55 | static int keepmuted	= 1; | 
 | 56 | static int initmute	= 1; | 
 | 57 | static int radio_nr	= -1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 58 |  | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 59 | module_param(io, int, 0444); | 
| Joe Perches | 13d9701 | 2007-11-19 22:48:16 -0300 | [diff] [blame] | 60 | MODULE_PARM_DESC(io, "Force I/O port for the GemTek Radio card if automatic " | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 61 | 	 "probing is disabled or fails. The most common I/O ports are: 0x20c " | 
 | 62 | 	 "0x30c, 0x24c or 0x34c (0x20c, 0x248 and 0x28c have been reported to " | 
| Joe Perches | 13d9701 | 2007-11-19 22:48:16 -0300 | [diff] [blame] | 63 | 	 "work for the combined sound/radiocard)."); | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 64 |  | 
 | 65 | module_param(probe, bool, 0444); | 
 | 66 | MODULE_PARM_DESC(probe, "Enable automatic device probing. Note: only the most " | 
 | 67 | 	"common I/O ports used by the card are probed."); | 
 | 68 |  | 
 | 69 | module_param(hardmute, bool, 0644); | 
 | 70 | MODULE_PARM_DESC(hardmute, "Enable `hard muting' by shutting down PLL, may " | 
 | 71 | 	 "reduce static noise."); | 
 | 72 |  | 
 | 73 | module_param(shutdown, bool, 0644); | 
 | 74 | MODULE_PARM_DESC(shutdown, "Enable shutting down PLL and muting line when " | 
 | 75 | 	 "module is unloaded."); | 
 | 76 |  | 
 | 77 | module_param(keepmuted, bool, 0644); | 
 | 78 | MODULE_PARM_DESC(keepmuted, "Keep card muted even when frequency is changed."); | 
 | 79 |  | 
 | 80 | module_param(initmute, bool, 0444); | 
 | 81 | MODULE_PARM_DESC(initmute, "Mute card when module is loaded."); | 
 | 82 |  | 
 | 83 | module_param(radio_nr, int, 0444); | 
 | 84 |  | 
 | 85 | /* | 
 | 86 |  * Functions for controlling the card. | 
 | 87 |  */ | 
 | 88 | #define GEMTEK_LOWFREQ	(87*16000) | 
 | 89 | #define GEMTEK_HIGHFREQ	(108*16000) | 
 | 90 |  | 
| Trent Piepho | 857e594 | 2007-10-01 00:32:25 -0300 | [diff] [blame] | 91 | /* | 
 | 92 |  * Frequency calculation constants.  Intermediate frequency 10.52 MHz (nominal | 
 | 93 |  * value 10.7 MHz), reference divisor 6.39 kHz (nominal 6.25 kHz). | 
 | 94 |  */ | 
 | 95 | #define FSCALE		8 | 
 | 96 | #define IF_OFFSET	((unsigned int)(10.52 * 16000 * (1<<FSCALE))) | 
 | 97 | #define REF_FREQ	((unsigned int)(6.39 * 16 * (1<<FSCALE))) | 
 | 98 |  | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 99 | #define GEMTEK_CK		0x01	/* Clock signal			*/ | 
 | 100 | #define GEMTEK_DA		0x02	/* Serial data			*/ | 
 | 101 | #define GEMTEK_CE		0x04	/* Chip enable			*/ | 
 | 102 | #define GEMTEK_NS		0x08	/* No signal			*/ | 
 | 103 | #define GEMTEK_MT		0x10	/* Line mute			*/ | 
 | 104 | #define GEMTEK_STDF_3_125_KHZ	0x01	/* Standard frequency 3.125 kHz	*/ | 
 | 105 | #define GEMTEK_PLL_OFF		0x07	/* PLL off			*/ | 
 | 106 |  | 
 | 107 | #define BU2614_BUS_SIZE	32	/* BU2614 / BU2614FS bus size		*/ | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 108 |  | 
 | 109 | #define SHORT_DELAY 5		/* usec */ | 
 | 110 | #define LONG_DELAY 75		/* usec */ | 
 | 111 |  | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 112 | struct gemtek { | 
 | 113 | 	struct v4l2_device v4l2_dev; | 
 | 114 | 	struct video_device vdev; | 
 | 115 | 	struct mutex lock; | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 116 | 	unsigned long lastfreq; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 117 | 	int muted; | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 118 | 	int verified; | 
 | 119 | 	int io; | 
| Trent Piepho | b25be97 | 2007-10-01 00:38:30 -0300 | [diff] [blame] | 120 | 	u32 bu2614data; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 121 | }; | 
 | 122 |  | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 123 | static struct gemtek gemtek_card; | 
 | 124 |  | 
| Trent Piepho | b25be97 | 2007-10-01 00:38:30 -0300 | [diff] [blame] | 125 | #define BU2614_FREQ_BITS 	16 /* D0..D15, Frequency data		*/ | 
 | 126 | #define BU2614_PORT_BITS	3 /* P0..P2, Output port control data	*/ | 
 | 127 | #define BU2614_VOID_BITS	4 /* unused 				*/ | 
 | 128 | #define BU2614_FMES_BITS	1 /* CT, Frequency measurement beginning data */ | 
 | 129 | #define BU2614_STDF_BITS	3 /* R0..R2, Standard frequency data	*/ | 
 | 130 | #define BU2614_SWIN_BITS	1 /* S, Switch between FMIN / AMIN	*/ | 
 | 131 | #define BU2614_SWAL_BITS        1 /* PS, Swallow counter division (AMIN only)*/ | 
 | 132 | #define BU2614_VOID2_BITS	1 /* unused				*/ | 
 | 133 | #define BU2614_FMUN_BITS	1 /* GT, Frequency measurement time & unlock */ | 
 | 134 | #define BU2614_TEST_BITS	1 /* TS, Test data is input		*/ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 135 |  | 
| Trent Piepho | b25be97 | 2007-10-01 00:38:30 -0300 | [diff] [blame] | 136 | #define BU2614_FREQ_SHIFT 	0 | 
 | 137 | #define BU2614_PORT_SHIFT	(BU2614_FREQ_BITS + BU2614_FREQ_SHIFT) | 
 | 138 | #define BU2614_VOID_SHIFT	(BU2614_PORT_BITS + BU2614_PORT_SHIFT) | 
 | 139 | #define BU2614_FMES_SHIFT	(BU2614_VOID_BITS + BU2614_VOID_SHIFT) | 
 | 140 | #define BU2614_STDF_SHIFT	(BU2614_FMES_BITS + BU2614_FMES_SHIFT) | 
 | 141 | #define BU2614_SWIN_SHIFT	(BU2614_STDF_BITS + BU2614_STDF_SHIFT) | 
 | 142 | #define BU2614_SWAL_SHIFT	(BU2614_SWIN_BITS + BU2614_SWIN_SHIFT) | 
 | 143 | #define BU2614_VOID2_SHIFT	(BU2614_SWAL_BITS + BU2614_SWAL_SHIFT) | 
 | 144 | #define BU2614_FMUN_SHIFT	(BU2614_VOID2_BITS + BU2614_VOID2_SHIFT) | 
 | 145 | #define BU2614_TEST_SHIFT	(BU2614_FMUN_BITS + BU2614_FMUN_SHIFT) | 
 | 146 |  | 
 | 147 | #define MKMASK(field)	(((1<<BU2614_##field##_BITS) - 1) << \ | 
 | 148 | 			BU2614_##field##_SHIFT) | 
 | 149 | #define BU2614_PORT_MASK	MKMASK(PORT) | 
 | 150 | #define BU2614_FREQ_MASK	MKMASK(FREQ) | 
 | 151 | #define BU2614_VOID_MASK	MKMASK(VOID) | 
 | 152 | #define BU2614_FMES_MASK	MKMASK(FMES) | 
 | 153 | #define BU2614_STDF_MASK	MKMASK(STDF) | 
 | 154 | #define BU2614_SWIN_MASK	MKMASK(SWIN) | 
 | 155 | #define BU2614_SWAL_MASK	MKMASK(SWAL) | 
 | 156 | #define BU2614_VOID2_MASK	MKMASK(VOID2) | 
 | 157 | #define BU2614_FMUN_MASK	MKMASK(FMUN) | 
 | 158 | #define BU2614_TEST_MASK	MKMASK(TEST) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 159 |  | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 160 | /* | 
 | 161 |  * Set data which will be sent to BU2614FS. | 
 | 162 |  */ | 
| Trent Piepho | b25be97 | 2007-10-01 00:38:30 -0300 | [diff] [blame] | 163 | #define gemtek_bu2614_set(dev, field, data) ((dev)->bu2614data = \ | 
 | 164 | 	((dev)->bu2614data & ~field##_MASK) | ((data) << field##_SHIFT)) | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 165 |  | 
 | 166 | /* | 
 | 167 |  * Transmit settings to BU2614FS over GemTek IC. | 
 | 168 |  */ | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 169 | static void gemtek_bu2614_transmit(struct gemtek *gt) | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 170 | { | 
 | 171 | 	int i, bit, q, mute; | 
 | 172 |  | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 173 | 	mutex_lock(>->lock); | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 174 |  | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 175 | 	mute = gt->muted ? GEMTEK_MT : 0x00; | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 176 |  | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 177 | 	outb_p(mute | GEMTEK_DA | GEMTEK_CK, gt->io); | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 178 | 	udelay(SHORT_DELAY); | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 179 | 	outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, gt->io); | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 180 | 	udelay(LONG_DELAY); | 
 | 181 |  | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 182 | 	for (i = 0, q = gt->bu2614data; i < 32; i++, q >>= 1) { | 
 | 183 | 		bit = (q & 1) ? GEMTEK_DA : 0; | 
 | 184 | 		outb_p(mute | GEMTEK_CE | bit, gt->io); | 
 | 185 | 		udelay(SHORT_DELAY); | 
 | 186 | 		outb_p(mute | GEMTEK_CE | bit | GEMTEK_CK, gt->io); | 
 | 187 | 		udelay(SHORT_DELAY); | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 188 | 	} | 
 | 189 |  | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 190 | 	outb_p(mute | GEMTEK_DA | GEMTEK_CK, gt->io); | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 191 | 	udelay(SHORT_DELAY); | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 192 | 	outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, gt->io); | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 193 | 	udelay(LONG_DELAY); | 
 | 194 |  | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 195 | 	mutex_unlock(>->lock); | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 196 | } | 
 | 197 |  | 
 | 198 | /* | 
| Trent Piepho | 857e594 | 2007-10-01 00:32:25 -0300 | [diff] [blame] | 199 |  * Calculate divisor from FM-frequency for BU2614FS (3.125 KHz STDF expected). | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 200 |  */ | 
| Trent Piepho | 857e594 | 2007-10-01 00:32:25 -0300 | [diff] [blame] | 201 | static unsigned long gemtek_convfreq(unsigned long freq) | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 202 | { | 
| Trent Piepho | 857e594 | 2007-10-01 00:32:25 -0300 | [diff] [blame] | 203 | 	return ((freq<<FSCALE) + IF_OFFSET + REF_FREQ/2) / REF_FREQ; | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 204 | } | 
 | 205 |  | 
 | 206 | /* | 
 | 207 |  * Set FM-frequency. | 
 | 208 |  */ | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 209 | static void gemtek_setfreq(struct gemtek *gt, unsigned long freq) | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 210 | { | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 211 | 	if (keepmuted && hardmute && gt->muted) | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 212 | 		return; | 
 | 213 |  | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 214 | 	freq = clamp_val(freq, GEMTEK_LOWFREQ, GEMTEK_HIGHFREQ); | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 215 |  | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 216 | 	gt->lastfreq = freq; | 
 | 217 | 	gt->muted = 0; | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 218 |  | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 219 | 	gemtek_bu2614_set(gt, BU2614_PORT, 0); | 
 | 220 | 	gemtek_bu2614_set(gt, BU2614_FMES, 0); | 
 | 221 | 	gemtek_bu2614_set(gt, BU2614_SWIN, 0);	/* FM-mode	*/ | 
 | 222 | 	gemtek_bu2614_set(gt, BU2614_SWAL, 0); | 
 | 223 | 	gemtek_bu2614_set(gt, BU2614_FMUN, 1);	/* GT bit set	*/ | 
 | 224 | 	gemtek_bu2614_set(gt, BU2614_TEST, 0); | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 225 |  | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 226 | 	gemtek_bu2614_set(gt, BU2614_STDF, GEMTEK_STDF_3_125_KHZ); | 
 | 227 | 	gemtek_bu2614_set(gt, BU2614_FREQ, gemtek_convfreq(freq)); | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 228 |  | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 229 | 	gemtek_bu2614_transmit(gt); | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 230 | } | 
 | 231 |  | 
 | 232 | /* | 
 | 233 |  * Set mute flag. | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 234 |  */ | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 235 | static void gemtek_mute(struct gemtek *gt) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 236 | { | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 237 | 	int i; | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 238 |  | 
 | 239 | 	gt->muted = 1; | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 240 |  | 
 | 241 | 	if (hardmute) { | 
 | 242 | 		/* Turn off PLL, disable data output */ | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 243 | 		gemtek_bu2614_set(gt, BU2614_PORT, 0); | 
 | 244 | 		gemtek_bu2614_set(gt, BU2614_FMES, 0);	/* CT bit off	*/ | 
 | 245 | 		gemtek_bu2614_set(gt, BU2614_SWIN, 0);	/* FM-mode	*/ | 
 | 246 | 		gemtek_bu2614_set(gt, BU2614_SWAL, 0); | 
 | 247 | 		gemtek_bu2614_set(gt, BU2614_FMUN, 0);	/* GT bit off	*/ | 
 | 248 | 		gemtek_bu2614_set(gt, BU2614_TEST, 0); | 
 | 249 | 		gemtek_bu2614_set(gt, BU2614_STDF, GEMTEK_PLL_OFF); | 
 | 250 | 		gemtek_bu2614_set(gt, BU2614_FREQ, 0); | 
 | 251 | 		gemtek_bu2614_transmit(gt); | 
 | 252 | 		return; | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 253 | 	} | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 254 |  | 
 | 255 | 	mutex_lock(>->lock); | 
 | 256 |  | 
 | 257 | 	/* Read bus contents (CE, CK and DA). */ | 
 | 258 | 	i = inb_p(gt->io); | 
 | 259 | 	/* Write it back with mute flag set. */ | 
 | 260 | 	outb_p((i >> 5) | GEMTEK_MT, gt->io); | 
 | 261 | 	udelay(SHORT_DELAY); | 
 | 262 |  | 
 | 263 | 	mutex_unlock(>->lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 264 | } | 
 | 265 |  | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 266 | /* | 
 | 267 |  * Unset mute flag. | 
 | 268 |  */ | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 269 | static void gemtek_unmute(struct gemtek *gt) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 270 | { | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 271 | 	int i; | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 272 |  | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 273 | 	gt->muted = 0; | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 274 | 	if (hardmute) { | 
 | 275 | 		/* Turn PLL back on. */ | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 276 | 		gemtek_setfreq(gt, gt->lastfreq); | 
 | 277 | 		return; | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 278 | 	} | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 279 | 	mutex_lock(>->lock); | 
 | 280 |  | 
 | 281 | 	i = inb_p(gt->io); | 
 | 282 | 	outb_p(i >> 5, gt->io); | 
 | 283 | 	udelay(SHORT_DELAY); | 
 | 284 |  | 
 | 285 | 	mutex_unlock(>->lock); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 286 | } | 
 | 287 |  | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 288 | /* | 
 | 289 |  * Get signal strength (= stereo status). | 
 | 290 |  */ | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 291 | static inline int gemtek_getsigstr(struct gemtek *gt) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 292 | { | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 293 | 	int sig; | 
 | 294 |  | 
 | 295 | 	mutex_lock(>->lock); | 
 | 296 | 	sig = inb_p(gt->io) & GEMTEK_NS ? 0 : 1; | 
 | 297 | 	mutex_unlock(>->lock); | 
 | 298 | 	return sig; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 299 | } | 
 | 300 |  | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 301 | /* | 
 | 302 |  * Check if requested card acts like GemTek Radio card. | 
 | 303 |  */ | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 304 | static int gemtek_verify(struct gemtek *gt, int port) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 305 | { | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 306 | 	int i, q; | 
 | 307 |  | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 308 | 	if (gt->verified == port) | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 309 | 		return 1; | 
 | 310 |  | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 311 | 	mutex_lock(>->lock); | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 312 |  | 
 | 313 | 	q = inb_p(port);	/* Read bus contents before probing. */ | 
 | 314 | 	/* Try to turn on CE, CK and DA respectively and check if card responds | 
 | 315 | 	   properly. */ | 
 | 316 | 	for (i = 0; i < 3; ++i) { | 
 | 317 | 		outb_p(1 << i, port); | 
 | 318 | 		udelay(SHORT_DELAY); | 
 | 319 |  | 
 | 320 | 		if ((inb_p(port) & (~GEMTEK_NS)) != (0x17 | (1 << (i + 5)))) { | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 321 | 			mutex_unlock(>->lock); | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 322 | 			return 0; | 
 | 323 | 		} | 
 | 324 | 	} | 
 | 325 | 	outb_p(q >> 5, port);	/* Write bus contents back. */ | 
 | 326 | 	udelay(SHORT_DELAY); | 
 | 327 |  | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 328 | 	mutex_unlock(>->lock); | 
 | 329 | 	gt->verified = port; | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 330 |  | 
 | 331 | 	return 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 332 | } | 
 | 333 |  | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 334 | /* | 
 | 335 |  * Automatic probing for card. | 
 | 336 |  */ | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 337 | static int gemtek_probe(struct gemtek *gt) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 338 | { | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 339 | 	struct v4l2_device *v4l2_dev = >->v4l2_dev; | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 340 | 	int ioports[] = { 0x20c, 0x30c, 0x24c, 0x34c, 0x248, 0x28c }; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 341 | 	int i; | 
 | 342 |  | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 343 | 	if (!probe) { | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 344 | 		v4l2_info(v4l2_dev, "Automatic device probing disabled.\n"); | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 345 | 		return -1; | 
 | 346 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 347 |  | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 348 | 	v4l2_info(v4l2_dev, "Automatic device probing enabled.\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 349 |  | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 350 | 	for (i = 0; i < ARRAY_SIZE(ioports); ++i) { | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 351 | 		v4l2_info(v4l2_dev, "Trying I/O port 0x%x...\n", ioports[i]); | 
| Mauro Carvalho Chehab | 4286c6f | 2006-04-08 16:06:16 -0300 | [diff] [blame] | 352 |  | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 353 | 		if (!request_region(ioports[i], 1, "gemtek-probe")) { | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 354 | 			v4l2_warn(v4l2_dev, "I/O port 0x%x busy!\n", | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 355 | 			       ioports[i]); | 
 | 356 | 			continue; | 
 | 357 | 		} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 358 |  | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 359 | 		if (gemtek_verify(gt, ioports[i])) { | 
 | 360 | 			v4l2_info(v4l2_dev, "Card found from I/O port " | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 361 | 			       "0x%x!\n", ioports[i]); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 362 |  | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 363 | 			release_region(ioports[i], 1); | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 364 | 			gt->io = ioports[i]; | 
 | 365 | 			return gt->io; | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 366 | 		} | 
| Mauro Carvalho Chehab | 4286c6f | 2006-04-08 16:06:16 -0300 | [diff] [blame] | 367 |  | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 368 | 		release_region(ioports[i], 1); | 
 | 369 | 	} | 
 | 370 |  | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 371 | 	v4l2_err(v4l2_dev, "Automatic probing failed!\n"); | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 372 | 	return -1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 373 | } | 
 | 374 |  | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 375 | /* | 
 | 376 |  * Video 4 Linux stuff. | 
 | 377 |  */ | 
| Hans Verkuil | 3ca685a | 2008-08-23 04:49:13 -0300 | [diff] [blame] | 378 |  | 
| Hans Verkuil | bec4366 | 2008-12-30 06:58:20 -0300 | [diff] [blame] | 379 | static const struct v4l2_file_operations gemtek_fops = { | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 380 | 	.owner		= THIS_MODULE, | 
| Hans Verkuil | 32958fd | 2010-11-14 09:36:23 -0300 | [diff] [blame] | 381 | 	.unlocked_ioctl	= video_ioctl2, | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 382 | }; | 
 | 383 |  | 
 | 384 | static int vidioc_querycap(struct file *file, void *priv, | 
 | 385 | 			   struct v4l2_capability *v) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 386 | { | 
| Douglas Landgraf | e9bb9c6 | 2007-04-25 00:15:46 -0300 | [diff] [blame] | 387 | 	strlcpy(v->driver, "radio-gemtek", sizeof(v->driver)); | 
 | 388 | 	strlcpy(v->card, "GemTek", sizeof(v->card)); | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 389 | 	strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); | 
| Douglas Landgraf | e9bb9c6 | 2007-04-25 00:15:46 -0300 | [diff] [blame] | 390 | 	v->version = RADIO_VERSION; | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 391 | 	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; | 
| Douglas Landgraf | e9bb9c6 | 2007-04-25 00:15:46 -0300 | [diff] [blame] | 392 | 	return 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 393 | } | 
 | 394 |  | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 395 | static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 396 | { | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 397 | 	struct gemtek *gt = video_drvdata(file); | 
 | 398 |  | 
| Douglas Landgraf | e9bb9c6 | 2007-04-25 00:15:46 -0300 | [diff] [blame] | 399 | 	if (v->index > 0) | 
 | 400 | 		return -EINVAL; | 
 | 401 |  | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 402 | 	strlcpy(v->name, "FM", sizeof(v->name)); | 
| Douglas Landgraf | e9bb9c6 | 2007-04-25 00:15:46 -0300 | [diff] [blame] | 403 | 	v->type = V4L2_TUNER_RADIO; | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 404 | 	v->rangelow = GEMTEK_LOWFREQ; | 
 | 405 | 	v->rangehigh = GEMTEK_HIGHFREQ; | 
 | 406 | 	v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 407 | 	v->signal = 0xffff * gemtek_getsigstr(gt); | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 408 | 	if (v->signal) { | 
 | 409 | 		v->audmode = V4L2_TUNER_MODE_STEREO; | 
 | 410 | 		v->rxsubchans = V4L2_TUNER_SUB_STEREO; | 
 | 411 | 	} else { | 
 | 412 | 		v->audmode = V4L2_TUNER_MODE_MONO; | 
 | 413 | 		v->rxsubchans = V4L2_TUNER_SUB_MONO; | 
 | 414 | 	} | 
| Douglas Landgraf | e9bb9c6 | 2007-04-25 00:15:46 -0300 | [diff] [blame] | 415 | 	return 0; | 
 | 416 | } | 
 | 417 |  | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 418 | static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *v) | 
| Douglas Landgraf | e9bb9c6 | 2007-04-25 00:15:46 -0300 | [diff] [blame] | 419 | { | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 420 | 	return (v->index != 0) ? -EINVAL : 0; | 
 | 421 | } | 
 | 422 |  | 
 | 423 | static int vidioc_g_frequency(struct file *file, void *priv, | 
 | 424 | 			      struct v4l2_frequency *f) | 
 | 425 | { | 
 | 426 | 	struct gemtek *gt = video_drvdata(file); | 
 | 427 |  | 
 | 428 | 	if (f->tuner != 0) | 
| Douglas Landgraf | e9bb9c6 | 2007-04-25 00:15:46 -0300 | [diff] [blame] | 429 | 		return -EINVAL; | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 430 | 	f->type = V4L2_TUNER_RADIO; | 
 | 431 | 	f->frequency = gt->lastfreq; | 
| Douglas Landgraf | e9bb9c6 | 2007-04-25 00:15:46 -0300 | [diff] [blame] | 432 | 	return 0; | 
 | 433 | } | 
 | 434 |  | 
 | 435 | static int vidioc_s_frequency(struct file *file, void *priv, | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 436 | 			      struct v4l2_frequency *f) | 
| Douglas Landgraf | e9bb9c6 | 2007-04-25 00:15:46 -0300 | [diff] [blame] | 437 | { | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 438 | 	struct gemtek *gt = video_drvdata(file); | 
| Douglas Landgraf | e9bb9c6 | 2007-04-25 00:15:46 -0300 | [diff] [blame] | 439 |  | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 440 | 	if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) | 
 | 441 | 		return -EINVAL; | 
 | 442 | 	gemtek_setfreq(gt, f->frequency); | 
| Douglas Landgraf | e9bb9c6 | 2007-04-25 00:15:46 -0300 | [diff] [blame] | 443 | 	return 0; | 
 | 444 | } | 
 | 445 |  | 
 | 446 | static int vidioc_queryctrl(struct file *file, void *priv, | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 447 | 			    struct v4l2_queryctrl *qc) | 
| Douglas Landgraf | e9bb9c6 | 2007-04-25 00:15:46 -0300 | [diff] [blame] | 448 | { | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 449 | 	switch (qc->id) { | 
 | 450 | 	case V4L2_CID_AUDIO_MUTE: | 
 | 451 | 		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); | 
 | 452 | 	default: | 
 | 453 | 		return -EINVAL; | 
| Douglas Landgraf | e9bb9c6 | 2007-04-25 00:15:46 -0300 | [diff] [blame] | 454 | 	} | 
| Douglas Landgraf | e9bb9c6 | 2007-04-25 00:15:46 -0300 | [diff] [blame] | 455 | } | 
 | 456 |  | 
 | 457 | static int vidioc_g_ctrl(struct file *file, void *priv, | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 458 | 			 struct v4l2_control *ctrl) | 
| Douglas Landgraf | e9bb9c6 | 2007-04-25 00:15:46 -0300 | [diff] [blame] | 459 | { | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 460 | 	struct gemtek *gt = video_drvdata(file); | 
| Douglas Landgraf | e9bb9c6 | 2007-04-25 00:15:46 -0300 | [diff] [blame] | 461 |  | 
 | 462 | 	switch (ctrl->id) { | 
 | 463 | 	case V4L2_CID_AUDIO_MUTE: | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 464 | 		ctrl->value = gt->muted; | 
| Douglas Landgraf | e9bb9c6 | 2007-04-25 00:15:46 -0300 | [diff] [blame] | 465 | 		return 0; | 
 | 466 | 	} | 
 | 467 | 	return -EINVAL; | 
 | 468 | } | 
 | 469 |  | 
 | 470 | static int vidioc_s_ctrl(struct file *file, void *priv, | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 471 | 			 struct v4l2_control *ctrl) | 
| Douglas Landgraf | e9bb9c6 | 2007-04-25 00:15:46 -0300 | [diff] [blame] | 472 | { | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 473 | 	struct gemtek *gt = video_drvdata(file); | 
| Douglas Landgraf | e9bb9c6 | 2007-04-25 00:15:46 -0300 | [diff] [blame] | 474 |  | 
 | 475 | 	switch (ctrl->id) { | 
 | 476 | 	case V4L2_CID_AUDIO_MUTE: | 
 | 477 | 		if (ctrl->value) | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 478 | 			gemtek_mute(gt); | 
| Douglas Landgraf | e9bb9c6 | 2007-04-25 00:15:46 -0300 | [diff] [blame] | 479 | 		else | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 480 | 			gemtek_unmute(gt); | 
| Douglas Landgraf | e9bb9c6 | 2007-04-25 00:15:46 -0300 | [diff] [blame] | 481 | 		return 0; | 
 | 482 | 	} | 
 | 483 | 	return -EINVAL; | 
 | 484 | } | 
 | 485 |  | 
| Douglas Landgraf | e9bb9c6 | 2007-04-25 00:15:46 -0300 | [diff] [blame] | 486 | static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) | 
 | 487 | { | 
 | 488 | 	*i = 0; | 
 | 489 | 	return 0; | 
 | 490 | } | 
 | 491 |  | 
 | 492 | static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) | 
 | 493 | { | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 494 | 	return (i != 0) ? -EINVAL : 0; | 
 | 495 | } | 
 | 496 |  | 
 | 497 | static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) | 
 | 498 | { | 
 | 499 | 	a->index = 0; | 
 | 500 | 	strlcpy(a->name, "Radio", sizeof(a->name)); | 
 | 501 | 	a->capability = V4L2_AUDCAP_STEREO; | 
| Douglas Landgraf | e9bb9c6 | 2007-04-25 00:15:46 -0300 | [diff] [blame] | 502 | 	return 0; | 
 | 503 | } | 
 | 504 |  | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 505 | static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) | 
| Douglas Landgraf | e9bb9c6 | 2007-04-25 00:15:46 -0300 | [diff] [blame] | 506 | { | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 507 | 	return (a->index != 0) ? -EINVAL : 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 508 | } | 
 | 509 |  | 
| Hans Verkuil | a399810 | 2008-07-21 02:57:38 -0300 | [diff] [blame] | 510 | static const struct v4l2_ioctl_ops gemtek_ioctl_ops = { | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 511 | 	.vidioc_querycap	= vidioc_querycap, | 
 | 512 | 	.vidioc_g_tuner		= vidioc_g_tuner, | 
 | 513 | 	.vidioc_s_tuner		= vidioc_s_tuner, | 
 | 514 | 	.vidioc_g_audio		= vidioc_g_audio, | 
 | 515 | 	.vidioc_s_audio		= vidioc_s_audio, | 
 | 516 | 	.vidioc_g_input		= vidioc_g_input, | 
 | 517 | 	.vidioc_s_input		= vidioc_s_input, | 
 | 518 | 	.vidioc_g_frequency	= vidioc_g_frequency, | 
 | 519 | 	.vidioc_s_frequency	= vidioc_s_frequency, | 
 | 520 | 	.vidioc_queryctrl	= vidioc_queryctrl, | 
 | 521 | 	.vidioc_g_ctrl		= vidioc_g_ctrl, | 
 | 522 | 	.vidioc_s_ctrl		= vidioc_s_ctrl | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 523 | }; | 
 | 524 |  | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 525 | /* | 
 | 526 |  * Initialization / cleanup related stuff. | 
 | 527 |  */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 528 |  | 
 | 529 | static int __init gemtek_init(void) | 
 | 530 | { | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 531 | 	struct gemtek *gt = &gemtek_card; | 
 | 532 | 	struct v4l2_device *v4l2_dev = >->v4l2_dev; | 
 | 533 | 	int res; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 534 |  | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 535 | 	strlcpy(v4l2_dev->name, "gemtek", sizeof(v4l2_dev->name)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 536 |  | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 537 | 	v4l2_info(v4l2_dev, "GemTek Radio card driver: v0.0.3\n"); | 
 | 538 |  | 
 | 539 | 	mutex_init(>->lock); | 
 | 540 |  | 
 | 541 | 	gt->verified = -1; | 
 | 542 | 	gt->io = io; | 
 | 543 | 	gemtek_probe(gt); | 
 | 544 | 	if (gt->io) { | 
 | 545 | 		if (!request_region(gt->io, 1, "gemtek")) { | 
 | 546 | 			v4l2_err(v4l2_dev, "I/O port 0x%x already in use.\n", gt->io); | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 547 | 			return -EBUSY; | 
 | 548 | 		} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 549 |  | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 550 | 		if (!gemtek_verify(gt, gt->io)) | 
 | 551 | 			v4l2_warn(v4l2_dev, "Card at I/O port 0x%x does not " | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 552 | 			       "respond properly, check your " | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 553 | 			       "configuration.\n", gt->io); | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 554 | 		else | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 555 | 			v4l2_info(v4l2_dev, "Using I/O port 0x%x.\n", gt->io); | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 556 | 	} else if (probe) { | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 557 | 		v4l2_err(v4l2_dev, "Automatic probing failed and no " | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 558 | 		       "fixed I/O port defined.\n"); | 
 | 559 | 		return -ENODEV; | 
 | 560 | 	} else { | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 561 | 		v4l2_err(v4l2_dev, "Automatic probing disabled but no fixed " | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 562 | 		       "I/O port defined."); | 
 | 563 | 		return -EINVAL; | 
 | 564 | 	} | 
 | 565 |  | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 566 | 	res = v4l2_device_register(NULL, v4l2_dev); | 
 | 567 | 	if (res < 0) { | 
 | 568 | 		v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); | 
 | 569 | 		release_region(gt->io, 1); | 
 | 570 | 		return res; | 
 | 571 | 	} | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 572 |  | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 573 | 	strlcpy(gt->vdev.name, v4l2_dev->name, sizeof(gt->vdev.name)); | 
 | 574 | 	gt->vdev.v4l2_dev = v4l2_dev; | 
 | 575 | 	gt->vdev.fops = &gemtek_fops; | 
 | 576 | 	gt->vdev.ioctl_ops = &gemtek_ioctl_ops; | 
 | 577 | 	gt->vdev.release = video_device_release_empty; | 
 | 578 | 	video_set_drvdata(>->vdev, gt); | 
 | 579 |  | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 580 | 	/* Set defaults */ | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 581 | 	gt->lastfreq = GEMTEK_LOWFREQ; | 
 | 582 | 	gt->bu2614data = 0; | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 583 |  | 
 | 584 | 	if (initmute) | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 585 | 		gemtek_mute(gt); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 586 |  | 
| Hans Verkuil | 32958fd | 2010-11-14 09:36:23 -0300 | [diff] [blame] | 587 | 	if (video_register_device(>->vdev, VFL_TYPE_RADIO, radio_nr) < 0) { | 
 | 588 | 		v4l2_device_unregister(v4l2_dev); | 
 | 589 | 		release_region(gt->io, 1); | 
 | 590 | 		return -EBUSY; | 
 | 591 | 	} | 
 | 592 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 593 | 	return 0; | 
 | 594 | } | 
 | 595 |  | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 596 | /* | 
 | 597 |  * Module cleanup | 
 | 598 |  */ | 
 | 599 | static void __exit gemtek_exit(void) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 600 | { | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 601 | 	struct gemtek *gt = &gemtek_card; | 
 | 602 | 	struct v4l2_device *v4l2_dev = >->v4l2_dev; | 
 | 603 |  | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 604 | 	if (shutdown) { | 
 | 605 | 		hardmute = 1;	/* Turn off PLL */ | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 606 | 		gemtek_mute(gt); | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 607 | 	} else { | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 608 | 		v4l2_info(v4l2_dev, "Module unloaded but card not muted!\n"); | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 609 | 	} | 
 | 610 |  | 
| Hans Verkuil | 502b71b | 2009-03-06 13:50:42 -0300 | [diff] [blame] | 611 | 	video_unregister_device(>->vdev); | 
 | 612 | 	v4l2_device_unregister(>->v4l2_dev); | 
 | 613 | 	release_region(gt->io, 1); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 614 | } | 
 | 615 |  | 
 | 616 | module_init(gemtek_init); | 
| Pekka Seppanen | 4753647 | 2007-10-01 00:27:55 -0300 | [diff] [blame] | 617 | module_exit(gemtek_exit); |