Blame view

drivers/media/radio/radio-gemtek.c 15.5 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
  /* GemTek radio card driver for Linux (C) 1998 Jonas Munsin <jmunsin@iki.fi>
   *
   * GemTek hasn't released any specs on the card, so the protocol had to
   * be reverse engineered with dosemu.
   *
   * Besides the protocol changes, this is mostly a copy of:
   *
   *    RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
9
   *
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
   *    Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood
d9b01449e   Alan Cox   V4L/DVB (9491): r...
11
   *    Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
13
14
15
   *    Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
   *
   * TODO: Allow for more than one of these foolish entities :-)
   *
d1c4ecdee   Mauro Carvalho Chehab   V4L/DVB (4349): V...
16
   * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
19
20
   */
  
  #include <linux/module.h>	/* Modules 			*/
  #include <linux/init.h>		/* Initdata			*/
fb911ee84   Peter Osterlund   [PATCH] Remove un...
21
  #include <linux/ioport.h>	/* request_region		*/
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
  #include <linux/delay.h>	/* udelay			*/
d1c4ecdee   Mauro Carvalho Chehab   V4L/DVB (4349): V...
23
  #include <linux/videodev2.h>	/* kernel radio structs		*/
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
24
25
  #include <linux/mutex.h>
  #include <linux/io.h>		/* outb, outb_p			*/
35ea11ff8   Hans Verkuil   V4L/DVB (8430): v...
26
  #include <media/v4l2-ioctl.h>
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
27
  #include <media/v4l2-device.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28

4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
29
30
31
  /*
   * Module info.
   */
29834c1ac   Mauro Carvalho Chehab   [media] radio: Us...
32
  MODULE_AUTHOR("Jonas Munsin, Pekka Seppänen <pexu@kapsi.fi>");
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
33
34
  MODULE_DESCRIPTION("A driver for the GemTek Radio card.");
  MODULE_LICENSE("GPL");
29834c1ac   Mauro Carvalho Chehab   [media] radio: Us...
35
  MODULE_VERSION("0.0.4");
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
36
37
38
39
  
  /*
   * Module params.
   */
d1c4ecdee   Mauro Carvalho Chehab   V4L/DVB (4349): V...
40

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
41
42
43
  #ifndef CONFIG_RADIO_GEMTEK_PORT
  #define CONFIG_RADIO_GEMTEK_PORT -1
  #endif
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
44
45
46
  #ifndef CONFIG_RADIO_GEMTEK_PROBE
  #define CONFIG_RADIO_GEMTEK_PROBE 1
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
47

4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
48
  static int io		= CONFIG_RADIO_GEMTEK_PORT;
90ab5ee94   Rusty Russell   module_param: mak...
49
50
51
52
53
  static bool probe	= CONFIG_RADIO_GEMTEK_PROBE;
  static bool hardmute;
  static bool shutdown	= 1;
  static bool keepmuted	= 1;
  static bool initmute	= 1;
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
54
  static int radio_nr	= -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
55

4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
56
  module_param(io, int, 0444);
13d97010e   Joe Perches   V4L/DVB (6616): d...
57
  MODULE_PARM_DESC(io, "Force I/O port for the GemTek Radio card if automatic "
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
58
59
  	 "probing is disabled or fails. The most common I/O ports are: 0x20c "
  	 "0x30c, 0x24c or 0x34c (0x20c, 0x248 and 0x28c have been reported to "
13d97010e   Joe Perches   V4L/DVB (6616): d...
60
  	 "work for the combined sound/radiocard).");
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
  
  module_param(probe, bool, 0444);
  MODULE_PARM_DESC(probe, "Enable automatic device probing. Note: only the most "
  	"common I/O ports used by the card are probed.");
  
  module_param(hardmute, bool, 0644);
  MODULE_PARM_DESC(hardmute, "Enable `hard muting' by shutting down PLL, may "
  	 "reduce static noise.");
  
  module_param(shutdown, bool, 0644);
  MODULE_PARM_DESC(shutdown, "Enable shutting down PLL and muting line when "
  	 "module is unloaded.");
  
  module_param(keepmuted, bool, 0644);
  MODULE_PARM_DESC(keepmuted, "Keep card muted even when frequency is changed.");
  
  module_param(initmute, bool, 0444);
  MODULE_PARM_DESC(initmute, "Mute card when module is loaded.");
  
  module_param(radio_nr, int, 0444);
  
  /*
   * Functions for controlling the card.
   */
  #define GEMTEK_LOWFREQ	(87*16000)
  #define GEMTEK_HIGHFREQ	(108*16000)
857e594ad   Trent Piepho   V4L/DVB (6245): ...
87
88
89
90
91
92
93
  /*
   * Frequency calculation constants.  Intermediate frequency 10.52 MHz (nominal
   * value 10.7 MHz), reference divisor 6.39 kHz (nominal 6.25 kHz).
   */
  #define FSCALE		8
  #define IF_OFFSET	((unsigned int)(10.52 * 16000 * (1<<FSCALE)))
  #define REF_FREQ	((unsigned int)(6.39 * 16 * (1<<FSCALE)))
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
94
95
96
97
98
99
100
101
102
  #define GEMTEK_CK		0x01	/* Clock signal			*/
  #define GEMTEK_DA		0x02	/* Serial data			*/
  #define GEMTEK_CE		0x04	/* Chip enable			*/
  #define GEMTEK_NS		0x08	/* No signal			*/
  #define GEMTEK_MT		0x10	/* Line mute			*/
  #define GEMTEK_STDF_3_125_KHZ	0x01	/* Standard frequency 3.125 kHz	*/
  #define GEMTEK_PLL_OFF		0x07	/* PLL off			*/
  
  #define BU2614_BUS_SIZE	32	/* BU2614 / BU2614FS bus size		*/
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
103
104
105
  
  #define SHORT_DELAY 5		/* usec */
  #define LONG_DELAY 75		/* usec */
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
106
107
108
109
  struct gemtek {
  	struct v4l2_device v4l2_dev;
  	struct video_device vdev;
  	struct mutex lock;
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
110
  	unsigned long lastfreq;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
  	int muted;
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
112
113
  	int verified;
  	int io;
b25be9792   Trent Piepho   V4L/DVB (6246): ...
114
  	u32 bu2614data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
  };
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
116
  static struct gemtek gemtek_card;
b25be9792   Trent Piepho   V4L/DVB (6246): ...
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
  #define BU2614_FREQ_BITS 	16 /* D0..D15, Frequency data		*/
  #define BU2614_PORT_BITS	3 /* P0..P2, Output port control data	*/
  #define BU2614_VOID_BITS	4 /* unused 				*/
  #define BU2614_FMES_BITS	1 /* CT, Frequency measurement beginning data */
  #define BU2614_STDF_BITS	3 /* R0..R2, Standard frequency data	*/
  #define BU2614_SWIN_BITS	1 /* S, Switch between FMIN / AMIN	*/
  #define BU2614_SWAL_BITS        1 /* PS, Swallow counter division (AMIN only)*/
  #define BU2614_VOID2_BITS	1 /* unused				*/
  #define BU2614_FMUN_BITS	1 /* GT, Frequency measurement time & unlock */
  #define BU2614_TEST_BITS	1 /* TS, Test data is input		*/
  
  #define BU2614_FREQ_SHIFT 	0
  #define BU2614_PORT_SHIFT	(BU2614_FREQ_BITS + BU2614_FREQ_SHIFT)
  #define BU2614_VOID_SHIFT	(BU2614_PORT_BITS + BU2614_PORT_SHIFT)
  #define BU2614_FMES_SHIFT	(BU2614_VOID_BITS + BU2614_VOID_SHIFT)
  #define BU2614_STDF_SHIFT	(BU2614_FMES_BITS + BU2614_FMES_SHIFT)
  #define BU2614_SWIN_SHIFT	(BU2614_STDF_BITS + BU2614_STDF_SHIFT)
  #define BU2614_SWAL_SHIFT	(BU2614_SWIN_BITS + BU2614_SWIN_SHIFT)
  #define BU2614_VOID2_SHIFT	(BU2614_SWAL_BITS + BU2614_SWAL_SHIFT)
  #define BU2614_FMUN_SHIFT	(BU2614_VOID2_BITS + BU2614_VOID2_SHIFT)
  #define BU2614_TEST_SHIFT	(BU2614_FMUN_BITS + BU2614_FMUN_SHIFT)
  
  #define MKMASK(field)	(((1<<BU2614_##field##_BITS) - 1) << \
  			BU2614_##field##_SHIFT)
  #define BU2614_PORT_MASK	MKMASK(PORT)
  #define BU2614_FREQ_MASK	MKMASK(FREQ)
  #define BU2614_VOID_MASK	MKMASK(VOID)
  #define BU2614_FMES_MASK	MKMASK(FMES)
  #define BU2614_STDF_MASK	MKMASK(STDF)
  #define BU2614_SWIN_MASK	MKMASK(SWIN)
  #define BU2614_SWAL_MASK	MKMASK(SWAL)
  #define BU2614_VOID2_MASK	MKMASK(VOID2)
  #define BU2614_FMUN_MASK	MKMASK(FMUN)
  #define BU2614_TEST_MASK	MKMASK(TEST)
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
151

4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
152
153
  /*
   * Set data which will be sent to BU2614FS.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
154
   */
b25be9792   Trent Piepho   V4L/DVB (6246): ...
155
156
  #define gemtek_bu2614_set(dev, field, data) ((dev)->bu2614data = \
  	((dev)->bu2614data & ~field##_MASK) | ((data) << field##_SHIFT))
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
157
158
159
160
  
  /*
   * Transmit settings to BU2614FS over GemTek IC.
   */
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
161
  static void gemtek_bu2614_transmit(struct gemtek *gt)
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
162
163
  {
  	int i, bit, q, mute;
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
164
  	mutex_lock(&gt->lock);
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
165

502b71b57   Hans Verkuil   V4L/DVB (10884): ...
166
  	mute = gt->muted ? GEMTEK_MT : 0x00;
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
167

502b71b57   Hans Verkuil   V4L/DVB (10884): ...
168
  	outb_p(mute | GEMTEK_DA | GEMTEK_CK, gt->io);
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
169
  	udelay(SHORT_DELAY);
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
170
  	outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, gt->io);
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
171
  	udelay(LONG_DELAY);
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
172
173
174
175
176
177
  	for (i = 0, q = gt->bu2614data; i < 32; i++, q >>= 1) {
  		bit = (q & 1) ? GEMTEK_DA : 0;
  		outb_p(mute | GEMTEK_CE | bit, gt->io);
  		udelay(SHORT_DELAY);
  		outb_p(mute | GEMTEK_CE | bit | GEMTEK_CK, gt->io);
  		udelay(SHORT_DELAY);
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
178
  	}
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
179
  	outb_p(mute | GEMTEK_DA | GEMTEK_CK, gt->io);
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
180
  	udelay(SHORT_DELAY);
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
181
  	outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, gt->io);
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
182
  	udelay(LONG_DELAY);
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
183
  	mutex_unlock(&gt->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184
  }
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
185
  /*
857e594ad   Trent Piepho   V4L/DVB (6245): ...
186
   * Calculate divisor from FM-frequency for BU2614FS (3.125 KHz STDF expected).
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
187
   */
857e594ad   Trent Piepho   V4L/DVB (6245): ...
188
  static unsigned long gemtek_convfreq(unsigned long freq)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
  {
857e594ad   Trent Piepho   V4L/DVB (6245): ...
190
  	return ((freq<<FSCALE) + IF_OFFSET + REF_FREQ/2) / REF_FREQ;
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
191
192
193
194
195
  }
  
  /*
   * Set FM-frequency.
   */
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
196
  static void gemtek_setfreq(struct gemtek *gt, unsigned long freq)
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
197
  {
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
198
  	if (keepmuted && hardmute && gt->muted)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199
  		return;
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
200

502b71b57   Hans Verkuil   V4L/DVB (10884): ...
201
  	freq = clamp_val(freq, GEMTEK_LOWFREQ, GEMTEK_HIGHFREQ);
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
202

502b71b57   Hans Verkuil   V4L/DVB (10884): ...
203
204
  	gt->lastfreq = freq;
  	gt->muted = 0;
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
205

502b71b57   Hans Verkuil   V4L/DVB (10884): ...
206
207
208
209
210
211
  	gemtek_bu2614_set(gt, BU2614_PORT, 0);
  	gemtek_bu2614_set(gt, BU2614_FMES, 0);
  	gemtek_bu2614_set(gt, BU2614_SWIN, 0);	/* FM-mode	*/
  	gemtek_bu2614_set(gt, BU2614_SWAL, 0);
  	gemtek_bu2614_set(gt, BU2614_FMUN, 1);	/* GT bit set	*/
  	gemtek_bu2614_set(gt, BU2614_TEST, 0);
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
212

502b71b57   Hans Verkuil   V4L/DVB (10884): ...
213
214
  	gemtek_bu2614_set(gt, BU2614_STDF, GEMTEK_STDF_3_125_KHZ);
  	gemtek_bu2614_set(gt, BU2614_FREQ, gemtek_convfreq(freq));
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
215

502b71b57   Hans Verkuil   V4L/DVB (10884): ...
216
  	gemtek_bu2614_transmit(gt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
217
  }
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
218
219
220
  /*
   * Set mute flag.
   */
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
221
  static void gemtek_mute(struct gemtek *gt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222
  {
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
223
  	int i;
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
224
225
  
  	gt->muted = 1;
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
226
227
228
  
  	if (hardmute) {
  		/* Turn off PLL, disable data output */
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
229
230
231
232
233
234
235
236
237
238
239
  		gemtek_bu2614_set(gt, BU2614_PORT, 0);
  		gemtek_bu2614_set(gt, BU2614_FMES, 0);	/* CT bit off	*/
  		gemtek_bu2614_set(gt, BU2614_SWIN, 0);	/* FM-mode	*/
  		gemtek_bu2614_set(gt, BU2614_SWAL, 0);
  		gemtek_bu2614_set(gt, BU2614_FMUN, 0);	/* GT bit off	*/
  		gemtek_bu2614_set(gt, BU2614_TEST, 0);
  		gemtek_bu2614_set(gt, BU2614_STDF, GEMTEK_PLL_OFF);
  		gemtek_bu2614_set(gt, BU2614_FREQ, 0);
  		gemtek_bu2614_transmit(gt);
  		return;
  	}
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
240

502b71b57   Hans Verkuil   V4L/DVB (10884): ...
241
  	mutex_lock(&gt->lock);
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
242

502b71b57   Hans Verkuil   V4L/DVB (10884): ...
243
244
245
246
247
248
249
  	/* Read bus contents (CE, CK and DA). */
  	i = inb_p(gt->io);
  	/* Write it back with mute flag set. */
  	outb_p((i >> 5) | GEMTEK_MT, gt->io);
  	udelay(SHORT_DELAY);
  
  	mutex_unlock(&gt->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
  }
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
251
252
253
  /*
   * Unset mute flag.
   */
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
254
  static void gemtek_unmute(struct gemtek *gt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
  {
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
256
  	int i;
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
257

502b71b57   Hans Verkuil   V4L/DVB (10884): ...
258
  	gt->muted = 0;
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
259
260
  	if (hardmute) {
  		/* Turn PLL back on. */
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
261
262
263
264
  		gemtek_setfreq(gt, gt->lastfreq);
  		return;
  	}
  	mutex_lock(&gt->lock);
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
265

502b71b57   Hans Verkuil   V4L/DVB (10884): ...
266
267
268
  	i = inb_p(gt->io);
  	outb_p(i >> 5, gt->io);
  	udelay(SHORT_DELAY);
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
269

502b71b57   Hans Verkuil   V4L/DVB (10884): ...
270
  	mutex_unlock(&gt->lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
271
  }
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
272
273
274
  /*
   * Get signal strength (= stereo status).
   */
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
275
  static inline int gemtek_getsigstr(struct gemtek *gt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
276
  {
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
277
278
279
280
281
282
  	int sig;
  
  	mutex_lock(&gt->lock);
  	sig = inb_p(gt->io) & GEMTEK_NS ? 0 : 1;
  	mutex_unlock(&gt->lock);
  	return sig;
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
283
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
284

4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
285
286
287
  /*
   * Check if requested card acts like GemTek Radio card.
   */
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
288
  static int gemtek_verify(struct gemtek *gt, int port)
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
289
  {
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
290
  	int i, q;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
291

502b71b57   Hans Verkuil   V4L/DVB (10884): ...
292
  	if (gt->verified == port)
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
293
  		return 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
294

502b71b57   Hans Verkuil   V4L/DVB (10884): ...
295
  	mutex_lock(&gt->lock);
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
296

4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
297
298
299
300
301
302
  	q = inb_p(port);	/* Read bus contents before probing. */
  	/* Try to turn on CE, CK and DA respectively and check if card responds
  	   properly. */
  	for (i = 0; i < 3; ++i) {
  		outb_p(1 << i, port);
  		udelay(SHORT_DELAY);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
303

4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
304
  		if ((inb_p(port) & (~GEMTEK_NS)) != (0x17 | (1 << (i + 5)))) {
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
305
  			mutex_unlock(&gt->lock);
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
306
307
308
309
310
  			return 0;
  		}
  	}
  	outb_p(q >> 5, port);	/* Write bus contents back. */
  	udelay(SHORT_DELAY);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
311

502b71b57   Hans Verkuil   V4L/DVB (10884): ...
312
313
  	mutex_unlock(&gt->lock);
  	gt->verified = port;
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
314

4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
315
  	return 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
316
  }
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
317
318
319
  /*
   * Automatic probing for card.
   */
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
320
  static int gemtek_probe(struct gemtek *gt)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
321
  {
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
322
  	struct v4l2_device *v4l2_dev = &gt->v4l2_dev;
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
323
324
325
326
  	int ioports[] = { 0x20c, 0x30c, 0x24c, 0x34c, 0x248, 0x28c };
  	int i;
  
  	if (!probe) {
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
327
328
  		v4l2_info(v4l2_dev, "Automatic device probing disabled.
  ");
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
329
330
  		return -1;
  	}
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
331
332
  	v4l2_info(v4l2_dev, "Automatic device probing enabled.
  ");
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
333
334
  
  	for (i = 0; i < ARRAY_SIZE(ioports); ++i) {
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
335
336
  		v4l2_info(v4l2_dev, "Trying I/O port 0x%x...
  ", ioports[i]);
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
337
338
  
  		if (!request_region(ioports[i], 1, "gemtek-probe")) {
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
339
340
  			v4l2_warn(v4l2_dev, "I/O port 0x%x busy!
  ",
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
341
342
343
  			       ioports[i]);
  			continue;
  		}
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
344
345
  		if (gemtek_verify(gt, ioports[i])) {
  			v4l2_info(v4l2_dev, "Card found from I/O port "
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
346
347
348
349
  			       "0x%x!
  ", ioports[i]);
  
  			release_region(ioports[i], 1);
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
350
351
  			gt->io = ioports[i];
  			return gt->io;
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
352
353
354
355
  		}
  
  		release_region(ioports[i], 1);
  	}
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
356
357
  	v4l2_err(v4l2_dev, "Automatic probing failed!
  ");
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
358
  	return -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
359
  }
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
360
361
362
  /*
   * Video 4 Linux stuff.
   */
3ca685aae   Hans Verkuil   V4L/DVB (8776): r...
363

bec43661b   Hans Verkuil   V4L/DVB (10135): ...
364
  static const struct v4l2_file_operations gemtek_fops = {
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
365
  	.owner		= THIS_MODULE,
32958fdd1   Hans Verkuil   [media] BKL: triv...
366
  	.unlocked_ioctl	= video_ioctl2,
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
367
368
369
370
  };
  
  static int vidioc_querycap(struct file *file, void *priv,
  			   struct v4l2_capability *v)
e9bb9c643   Douglas Landgraf   V4L/DVB (5556): R...
371
372
373
  {
  	strlcpy(v->driver, "radio-gemtek", sizeof(v->driver));
  	strlcpy(v->card, "GemTek", sizeof(v->card));
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
374
  	strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
375
  	v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
e9bb9c643   Douglas Landgraf   V4L/DVB (5556): R...
376
377
  	return 0;
  }
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
378
  static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
  {
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
380
  	struct gemtek *gt = video_drvdata(file);
e9bb9c643   Douglas Landgraf   V4L/DVB (5556): R...
381
382
  	if (v->index > 0)
  		return -EINVAL;
d1c4ecdee   Mauro Carvalho Chehab   V4L/DVB (4349): V...
383

502b71b57   Hans Verkuil   V4L/DVB (10884): ...
384
  	strlcpy(v->name, "FM", sizeof(v->name));
e9bb9c643   Douglas Landgraf   V4L/DVB (5556): R...
385
  	v->type = V4L2_TUNER_RADIO;
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
386
387
388
  	v->rangelow = GEMTEK_LOWFREQ;
  	v->rangehigh = GEMTEK_HIGHFREQ;
  	v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
389
  	v->signal = 0xffff * gemtek_getsigstr(gt);
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
390
391
392
393
394
395
396
  	if (v->signal) {
  		v->audmode = V4L2_TUNER_MODE_STEREO;
  		v->rxsubchans = V4L2_TUNER_SUB_STEREO;
  	} else {
  		v->audmode = V4L2_TUNER_MODE_MONO;
  		v->rxsubchans = V4L2_TUNER_SUB_MONO;
  	}
e9bb9c643   Douglas Landgraf   V4L/DVB (5556): R...
397
398
  	return 0;
  }
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
399
  static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *v)
e9bb9c643   Douglas Landgraf   V4L/DVB (5556): R...
400
  {
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
401
  	return (v->index != 0) ? -EINVAL : 0;
e9bb9c643   Douglas Landgraf   V4L/DVB (5556): R...
402
  }
d1c4ecdee   Mauro Carvalho Chehab   V4L/DVB (4349): V...
403

502b71b57   Hans Verkuil   V4L/DVB (10884): ...
404
  static int vidioc_g_frequency(struct file *file, void *priv,
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
405
  			      struct v4l2_frequency *f)
e9bb9c643   Douglas Landgraf   V4L/DVB (5556): R...
406
  {
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
407
  	struct gemtek *gt = video_drvdata(file);
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
408

502b71b57   Hans Verkuil   V4L/DVB (10884): ...
409
410
411
412
  	if (f->tuner != 0)
  		return -EINVAL;
  	f->type = V4L2_TUNER_RADIO;
  	f->frequency = gt->lastfreq;
e9bb9c643   Douglas Landgraf   V4L/DVB (5556): R...
413
414
  	return 0;
  }
d1c4ecdee   Mauro Carvalho Chehab   V4L/DVB (4349): V...
415

502b71b57   Hans Verkuil   V4L/DVB (10884): ...
416
  static int vidioc_s_frequency(struct file *file, void *priv,
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
417
  			      struct v4l2_frequency *f)
e9bb9c643   Douglas Landgraf   V4L/DVB (5556): R...
418
  {
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
419
  	struct gemtek *gt = video_drvdata(file);
d1c4ecdee   Mauro Carvalho Chehab   V4L/DVB (4349): V...
420

502b71b57   Hans Verkuil   V4L/DVB (10884): ...
421
422
423
  	if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
  		return -EINVAL;
  	gemtek_setfreq(gt, f->frequency);
e9bb9c643   Douglas Landgraf   V4L/DVB (5556): R...
424
425
  	return 0;
  }
d1c4ecdee   Mauro Carvalho Chehab   V4L/DVB (4349): V...
426

e9bb9c643   Douglas Landgraf   V4L/DVB (5556): R...
427
  static int vidioc_queryctrl(struct file *file, void *priv,
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
428
  			    struct v4l2_queryctrl *qc)
e9bb9c643   Douglas Landgraf   V4L/DVB (5556): R...
429
  {
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
430
431
432
433
434
  	switch (qc->id) {
  	case V4L2_CID_AUDIO_MUTE:
  		return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
  	default:
  		return -EINVAL;
e9bb9c643   Douglas Landgraf   V4L/DVB (5556): R...
435
  	}
e9bb9c643   Douglas Landgraf   V4L/DVB (5556): R...
436
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
437

e9bb9c643   Douglas Landgraf   V4L/DVB (5556): R...
438
  static int vidioc_g_ctrl(struct file *file, void *priv,
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
439
  			 struct v4l2_control *ctrl)
e9bb9c643   Douglas Landgraf   V4L/DVB (5556): R...
440
  {
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
441
  	struct gemtek *gt = video_drvdata(file);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
442

e9bb9c643   Douglas Landgraf   V4L/DVB (5556): R...
443
444
  	switch (ctrl->id) {
  	case V4L2_CID_AUDIO_MUTE:
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
445
  		ctrl->value = gt->muted;
e9bb9c643   Douglas Landgraf   V4L/DVB (5556): R...
446
447
448
449
450
451
  		return 0;
  	}
  	return -EINVAL;
  }
  
  static int vidioc_s_ctrl(struct file *file, void *priv,
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
452
  			 struct v4l2_control *ctrl)
e9bb9c643   Douglas Landgraf   V4L/DVB (5556): R...
453
  {
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
454
  	struct gemtek *gt = video_drvdata(file);
e9bb9c643   Douglas Landgraf   V4L/DVB (5556): R...
455
456
457
458
  
  	switch (ctrl->id) {
  	case V4L2_CID_AUDIO_MUTE:
  		if (ctrl->value)
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
459
  			gemtek_mute(gt);
e9bb9c643   Douglas Landgraf   V4L/DVB (5556): R...
460
  		else
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
461
  			gemtek_unmute(gt);
e9bb9c643   Douglas Landgraf   V4L/DVB (5556): R...
462
  		return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
463
  	}
e9bb9c643   Douglas Landgraf   V4L/DVB (5556): R...
464
  	return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
465
  }
e9bb9c643   Douglas Landgraf   V4L/DVB (5556): R...
466
467
468
469
470
471
472
473
  static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
  {
  	*i = 0;
  	return 0;
  }
  
  static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
  {
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
474
475
476
477
478
479
480
481
  	return (i != 0) ? -EINVAL : 0;
  }
  
  static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
  {
  	a->index = 0;
  	strlcpy(a->name, "Radio", sizeof(a->name));
  	a->capability = V4L2_AUDCAP_STEREO;
e9bb9c643   Douglas Landgraf   V4L/DVB (5556): R...
482
483
  	return 0;
  }
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
484
  static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
e9bb9c643   Douglas Landgraf   V4L/DVB (5556): R...
485
  {
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
486
  	return (a->index != 0) ? -EINVAL : 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
487
  }
a399810ca   Hans Verkuil   V4L/DVB (8482): v...
488
  static const struct v4l2_ioctl_ops gemtek_ioctl_ops = {
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
489
490
491
492
493
494
495
496
497
498
499
500
  	.vidioc_querycap	= vidioc_querycap,
  	.vidioc_g_tuner		= vidioc_g_tuner,
  	.vidioc_s_tuner		= vidioc_s_tuner,
  	.vidioc_g_audio		= vidioc_g_audio,
  	.vidioc_s_audio		= vidioc_s_audio,
  	.vidioc_g_input		= vidioc_g_input,
  	.vidioc_s_input		= vidioc_s_input,
  	.vidioc_g_frequency	= vidioc_g_frequency,
  	.vidioc_s_frequency	= vidioc_s_frequency,
  	.vidioc_queryctrl	= vidioc_queryctrl,
  	.vidioc_g_ctrl		= vidioc_g_ctrl,
  	.vidioc_s_ctrl		= vidioc_s_ctrl
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
501
  };
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
502
503
504
  /*
   * Initialization / cleanup related stuff.
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
505
506
507
  
  static int __init gemtek_init(void)
  {
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
508
509
510
  	struct gemtek *gt = &gemtek_card;
  	struct v4l2_device *v4l2_dev = &gt->v4l2_dev;
  	int res;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
511

502b71b57   Hans Verkuil   V4L/DVB (10884): ...
512
  	strlcpy(v4l2_dev->name, "gemtek", sizeof(v4l2_dev->name));
4286c6f65   Mauro Carvalho Chehab   V4L/DVB (3753): W...
513

502b71b57   Hans Verkuil   V4L/DVB (10884): ...
514
515
516
517
518
519
520
521
522
523
524
525
  	v4l2_info(v4l2_dev, "GemTek Radio card driver: v0.0.3
  ");
  
  	mutex_init(&gt->lock);
  
  	gt->verified = -1;
  	gt->io = io;
  	gemtek_probe(gt);
  	if (gt->io) {
  		if (!request_region(gt->io, 1, "gemtek")) {
  			v4l2_err(v4l2_dev, "I/O port 0x%x already in use.
  ", gt->io);
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
526
527
  			return -EBUSY;
  		}
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
528
529
  		if (!gemtek_verify(gt, gt->io))
  			v4l2_warn(v4l2_dev, "Card at I/O port 0x%x does not "
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
530
  			       "respond properly, check your "
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
531
532
  			       "configuration.
  ", gt->io);
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
533
  		else
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
534
535
  			v4l2_info(v4l2_dev, "Using I/O port 0x%x.
  ", gt->io);
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
536
  	} else if (probe) {
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
537
  		v4l2_err(v4l2_dev, "Automatic probing failed and no "
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
538
539
540
541
  		       "fixed I/O port defined.
  ");
  		return -ENODEV;
  	} else {
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
542
  		v4l2_err(v4l2_dev, "Automatic probing disabled but no fixed "
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
543
  		       "I/O port defined.");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
544
545
  		return -EINVAL;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
546

502b71b57   Hans Verkuil   V4L/DVB (10884): ...
547
548
549
550
551
552
553
  	res = v4l2_device_register(NULL, v4l2_dev);
  	if (res < 0) {
  		v4l2_err(v4l2_dev, "Could not register v4l2_device
  ");
  		release_region(gt->io, 1);
  		return res;
  	}
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
554

502b71b57   Hans Verkuil   V4L/DVB (10884): ...
555
556
557
558
559
560
  	strlcpy(gt->vdev.name, v4l2_dev->name, sizeof(gt->vdev.name));
  	gt->vdev.v4l2_dev = v4l2_dev;
  	gt->vdev.fops = &gemtek_fops;
  	gt->vdev.ioctl_ops = &gemtek_ioctl_ops;
  	gt->vdev.release = video_device_release_empty;
  	video_set_drvdata(&gt->vdev, gt);
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
561
  	/* Set defaults */
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
562
563
  	gt->lastfreq = GEMTEK_LOWFREQ;
  	gt->bu2614data = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
564

4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
565
  	if (initmute)
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
566
  		gemtek_mute(gt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
567

32958fdd1   Hans Verkuil   [media] BKL: triv...
568
569
570
571
572
  	if (video_register_device(&gt->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
  		v4l2_device_unregister(v4l2_dev);
  		release_region(gt->io, 1);
  		return -EBUSY;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
573
574
  	return 0;
  }
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
575
576
577
578
  /*
   * Module cleanup
   */
  static void __exit gemtek_exit(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
579
  {
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
580
581
  	struct gemtek *gt = &gemtek_card;
  	struct v4l2_device *v4l2_dev = &gt->v4l2_dev;
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
582
583
  	if (shutdown) {
  		hardmute = 1;	/* Turn off PLL */
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
584
  		gemtek_mute(gt);
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
585
  	} else {
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
586
587
  		v4l2_info(v4l2_dev, "Module unloaded but card not muted!
  ");
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
588
  	}
502b71b57   Hans Verkuil   V4L/DVB (10884): ...
589
590
591
  	video_unregister_device(&gt->vdev);
  	v4l2_device_unregister(&gt->v4l2_dev);
  	release_region(gt->io, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
592
593
594
  }
  
  module_init(gemtek_init);
4753647e6   Pekka Seppanen   V4L/DVB (6244): [...
595
  module_exit(gemtek_exit);