aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorHans Verkuil <hans.verkuil@cisco.com>2012-01-16 02:55:10 -0500
committerMauro Carvalho Chehab <mchehab@redhat.com>2012-02-14 14:08:52 -0500
commitcc3c6df16b3fb953818780b52c86fc7a9f08b337 (patch)
treec21f0663ba8a96ceedeb462f8d6b29d0eb9d7d2b /drivers
parent137c579c12bbb47ac1822e1a959aa15d0fcb76c1 (diff)
[media] radio-aimslab: Convert to radio-isa
Tested with actual hardware and the Keene USB FM Transmitter. Improved the volume handling delays through trial and error. Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/media/radio/Kconfig9
-rw-r--r--drivers/media/radio/radio-aimslab.c439
2 files changed, 110 insertions, 338 deletions
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index e291e0e2669f..618c33295ad1 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -202,6 +202,7 @@ config RADIO_CADET
202config RADIO_RTRACK 202config RADIO_RTRACK
203 tristate "AIMSlab RadioTrack (aka RadioReveal) support" 203 tristate "AIMSlab RadioTrack (aka RadioReveal) support"
204 depends on ISA && VIDEO_V4L2 204 depends on ISA && VIDEO_V4L2
205 select RADIO_ISA
205 ---help--- 206 ---help---
206 Choose Y here if you have one of these FM radio cards, and then fill 207 Choose Y here if you have one of these FM radio cards, and then fill
207 in the port address below. 208 in the port address below.
@@ -215,11 +216,7 @@ config RADIO_RTRACK
215 You must also pass the module a suitable io parameter, 0x248 has 216 You must also pass the module a suitable io parameter, 0x248 has
216 been reported to be used by these cards. 217 been reported to be used by these cards.
217 218
218 In order to control your radio card, you will need to use programs 219 More information is contained in the file
219 that are compatible with the Video For Linux API. Information on
220 this API and pointers to "v4l" programs may be found at
221 <file:Documentation/video4linux/API.html>. More information is
222 contained in the file
223 <file:Documentation/video4linux/radiotrack.txt>. 220 <file:Documentation/video4linux/radiotrack.txt>.
224 221
225 To compile this driver as a module, choose M here: the 222 To compile this driver as a module, choose M here: the
@@ -228,7 +225,7 @@ config RADIO_RTRACK
228config RADIO_RTRACK_PORT 225config RADIO_RTRACK_PORT
229 hex "RadioTrack i/o port (0x20f or 0x30f)" 226 hex "RadioTrack i/o port (0x20f or 0x30f)"
230 depends on RADIO_RTRACK=y 227 depends on RADIO_RTRACK=y
231 default "20f" 228 default "30f"
232 help 229 help
233 Enter either 0x30f or 0x20f here. The card default is 0x30f, if you 230 Enter either 0x30f or 0x20f here. The card default is 0x30f, if you
234 haven't changed the jumper setting on the card. 231 haven't changed the jumper setting on the card.
diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c
index 1c3f8440a55c..862dfce1f2fd 100644
--- a/drivers/media/radio/radio-aimslab.c
+++ b/drivers/media/radio/radio-aimslab.c
@@ -1,16 +1,13 @@
1/* radiotrack (radioreveal) driver for Linux radio support 1/*
2 * (c) 1997 M. Kirkwood 2 * AimsLab RadioTrack (aka RadioVeveal) driver
3 *
4 * Copyright 1997 M. Kirkwood
5 *
6 * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@cisco.com>
3 * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> 7 * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
4 * Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk> 8 * Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk>
5 * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org> 9 * Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
6 * 10 *
7 * History:
8 * 1999-02-24 Russell Kroll <rkroll@exploits.org>
9 * Fine tuning/VIDEO_TUNER_LOW
10 * Frequency range expanded to start at 87 MHz
11 *
12 * TODO: Allow for more than one of these foolish entities :-)
13 *
14 * Notes on the hardware (reverse engineered from other peoples' 11 * Notes on the hardware (reverse engineered from other peoples'
15 * reverse engineering of AIMS' code :-) 12 * reverse engineering of AIMS' code :-)
16 * 13 *
@@ -26,6 +23,7 @@
26 * wait(a_wee_while); 23 * wait(a_wee_while);
27 * out(port, stop_changing_the_volume); 24 * out(port, stop_changing_the_volume);
28 * 25 *
26 * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool.
29 */ 27 */
30 28
31#include <linux/module.h> /* Modules */ 29#include <linux/module.h> /* Modules */
@@ -36,399 +34,176 @@
36#include <linux/io.h> /* outb, outb_p */ 34#include <linux/io.h> /* outb, outb_p */
37#include <media/v4l2-device.h> 35#include <media/v4l2-device.h>
38#include <media/v4l2-ioctl.h> 36#include <media/v4l2-ioctl.h>
37#include <media/v4l2-ctrls.h>
38#include "radio-isa.h"
39 39
40MODULE_AUTHOR("M.Kirkwood"); 40MODULE_AUTHOR("M. Kirkwood");
41MODULE_DESCRIPTION("A driver for the RadioTrack/RadioReveal radio card."); 41MODULE_DESCRIPTION("A driver for the RadioTrack/RadioReveal radio card.");
42MODULE_LICENSE("GPL"); 42MODULE_LICENSE("GPL");
43MODULE_VERSION("0.0.3"); 43MODULE_VERSION("1.0.0");
44 44
45#ifndef CONFIG_RADIO_RTRACK_PORT 45#ifndef CONFIG_RADIO_RTRACK_PORT
46#define CONFIG_RADIO_RTRACK_PORT -1 46#define CONFIG_RADIO_RTRACK_PORT -1
47#endif 47#endif
48 48
49static int io = CONFIG_RADIO_RTRACK_PORT; 49#define RTRACK_MAX 2
50static int radio_nr = -1;
51 50
52module_param(io, int, 0); 51static int io[RTRACK_MAX] = { [0] = CONFIG_RADIO_RTRACK_PORT,
53MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20f or 0x30f)"); 52 [1 ... (RTRACK_MAX - 1)] = -1 };
54module_param(radio_nr, int, 0); 53static int radio_nr[RTRACK_MAX] = { [0 ... (RTRACK_MAX - 1)] = -1 };
55 54
56struct rtrack 55module_param_array(io, int, NULL, 0444);
57{ 56MODULE_PARM_DESC(io, "I/O addresses of the RadioTrack card (0x20f or 0x30f)");
58 struct v4l2_device v4l2_dev; 57module_param_array(radio_nr, int, NULL, 0444);
59 struct video_device vdev; 58MODULE_PARM_DESC(radio_nr, "Radio device numbers");
60 int port; 59
60struct rtrack {
61 struct radio_isa_card isa;
61 int curvol; 62 int curvol;
62 unsigned long curfreq;
63 int muted;
64 int io;
65 struct mutex lock;
66}; 63};
67 64
68static struct rtrack rtrack_card; 65static struct radio_isa_card *rtrack_alloc(void)
69
70/* local things */
71
72static void rt_decvol(struct rtrack *rt)
73{
74 outb(0x58, rt->io); /* volume down + sigstr + on */
75 msleep(100);
76 outb(0xd8, rt->io); /* volume steady + sigstr + on */
77}
78
79static void rt_incvol(struct rtrack *rt)
80{
81 outb(0x98, rt->io); /* volume up + sigstr + on */
82 msleep(100);
83 outb(0xd8, rt->io); /* volume steady + sigstr + on */
84}
85
86static void rt_mute(struct rtrack *rt)
87{
88 rt->muted = 1;
89 mutex_lock(&rt->lock);
90 outb(0xd0, rt->io); /* volume steady, off */
91 mutex_unlock(&rt->lock);
92}
93
94static int rt_setvol(struct rtrack *rt, int vol)
95{ 66{
96 int i; 67 struct rtrack *rt = kzalloc(sizeof(struct rtrack), GFP_KERNEL);
97
98 mutex_lock(&rt->lock);
99
100 if (vol == rt->curvol) { /* requested volume = current */
101 if (rt->muted) { /* user is unmuting the card */
102 rt->muted = 0;
103 outb(0xd8, rt->io); /* enable card */
104 }
105 mutex_unlock(&rt->lock);
106 return 0;
107 }
108
109 if (vol == 0) { /* volume = 0 means mute the card */
110 outb(0x48, rt->io); /* volume down but still "on" */
111 msleep(2000); /* make sure it's totally down */
112 outb(0xd0, rt->io); /* volume steady, off */
113 rt->curvol = 0; /* track the volume state! */
114 mutex_unlock(&rt->lock);
115 return 0;
116 }
117 68
118 rt->muted = 0; 69 if (rt)
119 if (vol > rt->curvol) 70 rt->curvol = 0xff;
120 for (i = rt->curvol; i < vol; i++) 71 return rt ? &rt->isa : NULL;
121 rt_incvol(rt);
122 else
123 for (i = rt->curvol; i > vol; i--)
124 rt_decvol(rt);
125
126 rt->curvol = vol;
127 mutex_unlock(&rt->lock);
128 return 0;
129} 72}
130 73
131/* the 128+64 on these outb's is to keep the volume stable while tuning 74/* The 128+64 on these outb's is to keep the volume stable while tuning.
132 * without them, the volume _will_ creep up with each frequency change 75 * Without them, the volume _will_ creep up with each frequency change
133 * and bit 4 (+16) is to keep the signal strength meter enabled 76 * and bit 4 (+16) is to keep the signal strength meter enabled.
134 */ 77 */
135 78
136static void send_0_byte(struct rtrack *rt) 79static void send_0_byte(struct radio_isa_card *isa, int on)
137{ 80{
138 if (rt->curvol == 0 || rt->muted) { 81 outb_p(128+64+16+on+1, isa->io); /* wr-enable + data low */
139 outb_p(128+64+16+ 1, rt->io); /* wr-enable + data low */ 82 outb_p(128+64+16+on+2+1, isa->io); /* clock */
140 outb_p(128+64+16+2+1, rt->io); /* clock */
141 }
142 else {
143 outb_p(128+64+16+8+ 1, rt->io); /* on + wr-enable + data low */
144 outb_p(128+64+16+8+2+1, rt->io); /* clock */
145 }
146 msleep(1); 83 msleep(1);
147} 84}
148 85
149static void send_1_byte(struct rtrack *rt) 86static void send_1_byte(struct radio_isa_card *isa, int on)
150{ 87{
151 if (rt->curvol == 0 || rt->muted) { 88 outb_p(128+64+16+on+4+1, isa->io); /* wr-enable+data high */
152 outb_p(128+64+16+4 +1, rt->io); /* wr-enable+data high */ 89 outb_p(128+64+16+on+4+2+1, isa->io); /* clock */
153 outb_p(128+64+16+4+2+1, rt->io); /* clock */
154 }
155 else {
156 outb_p(128+64+16+8+4 +1, rt->io); /* on+wr-enable+data high */
157 outb_p(128+64+16+8+4+2+1, rt->io); /* clock */
158 }
159
160 msleep(1); 90 msleep(1);
161} 91}
162 92
163static int rt_setfreq(struct rtrack *rt, unsigned long freq) 93static int rtrack_s_frequency(struct radio_isa_card *isa, u32 freq)
164{ 94{
95 int on = v4l2_ctrl_g_ctrl(isa->mute) ? 0 : 8;
165 int i; 96 int i;
166 97
167 mutex_lock(&rt->lock); /* Stop other ops interfering */
168
169 rt->curfreq = freq;
170
171 /* now uses VIDEO_TUNER_LOW for fine tuning */
172
173 freq += 171200; /* Add 10.7 MHz IF */ 98 freq += 171200; /* Add 10.7 MHz IF */
174 freq /= 800; /* Convert to 50 kHz units */ 99 freq /= 800; /* Convert to 50 kHz units */
175 100
176 send_0_byte(rt); /* 0: LSB of frequency */ 101 send_0_byte(isa, on); /* 0: LSB of frequency */
177 102
178 for (i = 0; i < 13; i++) /* : frequency bits (1-13) */ 103 for (i = 0; i < 13; i++) /* : frequency bits (1-13) */
179 if (freq & (1 << i)) 104 if (freq & (1 << i))
180 send_1_byte(rt); 105 send_1_byte(isa, on);
181 else 106 else
182 send_0_byte(rt); 107 send_0_byte(isa, on);
183
184 send_0_byte(rt); /* 14: test bit - always 0 */
185 send_0_byte(rt); /* 15: test bit - always 0 */
186
187 send_0_byte(rt); /* 16: band data 0 - always 0 */
188 send_0_byte(rt); /* 17: band data 1 - always 0 */
189 send_0_byte(rt); /* 18: band data 2 - always 0 */
190 send_0_byte(rt); /* 19: time base - always 0 */
191
192 send_0_byte(rt); /* 20: spacing (0 = 25 kHz) */
193 send_1_byte(rt); /* 21: spacing (1 = 25 kHz) */
194 send_0_byte(rt); /* 22: spacing (0 = 25 kHz) */
195 send_1_byte(rt); /* 23: AM/FM (FM = 1, always) */
196
197 if (rt->curvol == 0 || rt->muted)
198 outb(0xd0, rt->io); /* volume steady + sigstr */
199 else
200 outb(0xd8, rt->io); /* volume steady + sigstr + on */
201
202 mutex_unlock(&rt->lock);
203
204 return 0;
205}
206
207static int rt_getsigstr(struct rtrack *rt)
208{
209 int sig = 1;
210
211 mutex_lock(&rt->lock);
212 if (inb(rt->io) & 2) /* bit set = no signal present */
213 sig = 0;
214 mutex_unlock(&rt->lock);
215 return sig;
216}
217
218static int vidioc_querycap(struct file *file, void *priv,
219 struct v4l2_capability *v)
220{
221 strlcpy(v->driver, "radio-aimslab", sizeof(v->driver));
222 strlcpy(v->card, "RadioTrack", sizeof(v->card));
223 strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
224 v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
225 return 0;
226}
227
228static int vidioc_g_tuner(struct file *file, void *priv,
229 struct v4l2_tuner *v)
230{
231 struct rtrack *rt = video_drvdata(file);
232 108
233 if (v->index > 0) 109 send_0_byte(isa, on); /* 14: test bit - always 0 */
234 return -EINVAL; 110 send_0_byte(isa, on); /* 15: test bit - always 0 */
235 111
236 strlcpy(v->name, "FM", sizeof(v->name)); 112 send_0_byte(isa, on); /* 16: band data 0 - always 0 */
237 v->type = V4L2_TUNER_RADIO; 113 send_0_byte(isa, on); /* 17: band data 1 - always 0 */
238 v->rangelow = 87 * 16000; 114 send_0_byte(isa, on); /* 18: band data 2 - always 0 */
239 v->rangehigh = 108 * 16000; 115 send_0_byte(isa, on); /* 19: time base - always 0 */
240 v->rxsubchans = V4L2_TUNER_SUB_MONO;
241 v->capability = V4L2_TUNER_CAP_LOW;
242 v->audmode = V4L2_TUNER_MODE_MONO;
243 v->signal = 0xffff * rt_getsigstr(rt);
244 return 0;
245}
246
247static int vidioc_s_tuner(struct file *file, void *priv,
248 struct v4l2_tuner *v)
249{
250 return v->index ? -EINVAL : 0;
251}
252
253static int vidioc_s_frequency(struct file *file, void *priv,
254 struct v4l2_frequency *f)
255{
256 struct rtrack *rt = video_drvdata(file);
257
258 if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
259 return -EINVAL;
260 rt_setfreq(rt, f->frequency);
261 return 0;
262}
263 116
264static int vidioc_g_frequency(struct file *file, void *priv, 117 send_0_byte(isa, on); /* 20: spacing (0 = 25 kHz) */
265 struct v4l2_frequency *f) 118 send_1_byte(isa, on); /* 21: spacing (1 = 25 kHz) */
266{ 119 send_0_byte(isa, on); /* 22: spacing (0 = 25 kHz) */
267 struct rtrack *rt = video_drvdata(file); 120 send_1_byte(isa, on); /* 23: AM/FM (FM = 1, always) */
268 121
269 if (f->tuner != 0) 122 outb(0xd0 + on, isa->io); /* volume steady + sigstr */
270 return -EINVAL;
271 f->type = V4L2_TUNER_RADIO;
272 f->frequency = rt->curfreq;
273 return 0; 123 return 0;
274} 124}
275 125
276static int vidioc_queryctrl(struct file *file, void *priv, 126static u32 rtrack_g_signal(struct radio_isa_card *isa)
277 struct v4l2_queryctrl *qc)
278{ 127{
279 switch (qc->id) { 128 /* bit set = no signal present */
280 case V4L2_CID_AUDIO_MUTE: 129 return 0xffff * !(inb(isa->io) & 2);
281 return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
282 case V4L2_CID_AUDIO_VOLUME:
283 return v4l2_ctrl_query_fill(qc, 0, 0xff, 1, 0xff);
284 }
285 return -EINVAL;
286} 130}
287 131
288static int vidioc_g_ctrl(struct file *file, void *priv, 132static int rtrack_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
289 struct v4l2_control *ctrl)
290{ 133{
291 struct rtrack *rt = video_drvdata(file); 134 struct rtrack *rt = container_of(isa, struct rtrack, isa);
135 int curvol = rt->curvol;
292 136
293 switch (ctrl->id) { 137 if (mute) {
294 case V4L2_CID_AUDIO_MUTE: 138 outb(0xd0, isa->io); /* volume steady + sigstr + off */
295 ctrl->value = rt->muted;
296 return 0;
297 case V4L2_CID_AUDIO_VOLUME:
298 ctrl->value = rt->curvol;
299 return 0; 139 return 0;
300 } 140 }
301 return -EINVAL; 141 if (vol == 0) { /* volume = 0 means mute the card */
302} 142 outb(0x48, isa->io); /* volume down but still "on" */
303 143 msleep(curvol * 3); /* make sure it's totally down */
304static int vidioc_s_ctrl(struct file *file, void *priv, 144 } else if (curvol < vol) {
305 struct v4l2_control *ctrl) 145 outb(0x98, isa->io); /* volume up + sigstr + on */
306{ 146 for (; curvol < vol; curvol++)
307 struct rtrack *rt = video_drvdata(file); 147 udelay(3000);
308 148 } else if (curvol > vol) {
309 switch (ctrl->id) { 149 outb(0x58, isa->io); /* volume down + sigstr + on */
310 case V4L2_CID_AUDIO_MUTE: 150 for (; curvol > vol; curvol--)
311 if (ctrl->value) 151 udelay(3000);
312 rt_mute(rt);
313 else
314 rt_setvol(rt, rt->curvol);
315 return 0;
316 case V4L2_CID_AUDIO_VOLUME:
317 rt_setvol(rt, ctrl->value);
318 return 0;
319 } 152 }
320 return -EINVAL; 153 outb(0xd8, isa->io); /* volume steady + sigstr + on */
321} 154 rt->curvol = vol;
322
323static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
324{
325 *i = 0;
326 return 0; 155 return 0;
327} 156}
328 157
329static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) 158/* Mute card - prevents noisy bootups */
159static int rtrack_initialize(struct radio_isa_card *isa)
330{ 160{
331 return i ? -EINVAL : 0; 161 /* this ensures that the volume is all the way up */
332} 162 outb(0x90, isa->io); /* volume up but still "on" */
333 163 msleep(3000); /* make sure it's totally up */
334static int vidioc_g_audio(struct file *file, void *priv, 164 outb(0xc0, isa->io); /* steady volume, mute card */
335 struct v4l2_audio *a)
336{
337 a->index = 0;
338 strlcpy(a->name, "Radio", sizeof(a->name));
339 a->capability = V4L2_AUDCAP_STEREO;
340 return 0; 165 return 0;
341} 166}
342 167
343static int vidioc_s_audio(struct file *file, void *priv, 168static const struct radio_isa_ops rtrack_ops = {
344 struct v4l2_audio *a) 169 .alloc = rtrack_alloc,
345{ 170 .init = rtrack_initialize,
346 return a->index ? -EINVAL : 0; 171 .s_mute_volume = rtrack_s_mute_volume,
347} 172 .s_frequency = rtrack_s_frequency,
348 173 .g_signal = rtrack_g_signal,
349static const struct v4l2_file_operations rtrack_fops = {
350 .owner = THIS_MODULE,
351 .unlocked_ioctl = video_ioctl2,
352}; 174};
353 175
354static const struct v4l2_ioctl_ops rtrack_ioctl_ops = { 176static const int rtrack_ioports[] = { 0x20f, 0x30f };
355 .vidioc_querycap = vidioc_querycap, 177
356 .vidioc_g_tuner = vidioc_g_tuner, 178static struct radio_isa_driver rtrack_driver = {
357 .vidioc_s_tuner = vidioc_s_tuner, 179 .driver = {
358 .vidioc_g_audio = vidioc_g_audio, 180 .match = radio_isa_match,
359 .vidioc_s_audio = vidioc_s_audio, 181 .probe = radio_isa_probe,
360 .vidioc_g_input = vidioc_g_input, 182 .remove = radio_isa_remove,
361 .vidioc_s_input = vidioc_s_input, 183 .driver = {
362 .vidioc_g_frequency = vidioc_g_frequency, 184 .name = "radio-aimslab",
363 .vidioc_s_frequency = vidioc_s_frequency, 185 },
364 .vidioc_queryctrl = vidioc_queryctrl, 186 },
365 .vidioc_g_ctrl = vidioc_g_ctrl, 187 .io_params = io,
366 .vidioc_s_ctrl = vidioc_s_ctrl, 188 .radio_nr_params = radio_nr,
189 .io_ports = rtrack_ioports,
190 .num_of_io_ports = ARRAY_SIZE(rtrack_ioports),
191 .region_size = 2,
192 .card = "AIMSlab RadioTrack/RadioReveal",
193 .ops = &rtrack_ops,
194 .has_stereo = true,
195 .max_volume = 0xff,
367}; 196};
368 197
369static int __init rtrack_init(void) 198static int __init rtrack_init(void)
370{ 199{
371 struct rtrack *rt = &rtrack_card; 200 return isa_register_driver(&rtrack_driver.driver, RTRACK_MAX);
372 struct v4l2_device *v4l2_dev = &rt->v4l2_dev;
373 int res;
374
375 strlcpy(v4l2_dev->name, "rtrack", sizeof(v4l2_dev->name));
376 rt->io = io;
377
378 if (rt->io == -1) {
379 v4l2_err(v4l2_dev, "you must set an I/O address with io=0x20f or 0x30f\n");
380 return -EINVAL;
381 }
382
383 if (!request_region(rt->io, 2, "rtrack")) {
384 v4l2_err(v4l2_dev, "port 0x%x already in use\n", rt->io);
385 return -EBUSY;
386 }
387
388 res = v4l2_device_register(NULL, v4l2_dev);
389 if (res < 0) {
390 release_region(rt->io, 2);
391 v4l2_err(v4l2_dev, "could not register v4l2_device\n");
392 return res;
393 }
394
395 strlcpy(rt->vdev.name, v4l2_dev->name, sizeof(rt->vdev.name));
396 rt->vdev.v4l2_dev = v4l2_dev;
397 rt->vdev.fops = &rtrack_fops;
398 rt->vdev.ioctl_ops = &rtrack_ioctl_ops;
399 rt->vdev.release = video_device_release_empty;
400 video_set_drvdata(&rt->vdev, rt);
401
402 /* Set up the I/O locking */
403
404 mutex_init(&rt->lock);
405
406 /* mute card - prevents noisy bootups */
407
408 /* this ensures that the volume is all the way down */
409 outb(0x48, rt->io); /* volume down but still "on" */
410 msleep(2000); /* make sure it's totally down */
411 outb(0xc0, rt->io); /* steady volume, mute card */
412
413 if (video_register_device(&rt->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
414 v4l2_device_unregister(&rt->v4l2_dev);
415 release_region(rt->io, 2);
416 return -EINVAL;
417 }
418 v4l2_info(v4l2_dev, "AIMSlab RadioTrack/RadioReveal card driver.\n");
419
420 return 0;
421} 201}
422 202
423static void __exit rtrack_exit(void) 203static void __exit rtrack_exit(void)
424{ 204{
425 struct rtrack *rt = &rtrack_card; 205 isa_unregister_driver(&rtrack_driver.driver);
426
427 video_unregister_device(&rt->vdev);
428 v4l2_device_unregister(&rt->v4l2_dev);
429 release_region(rt->io, 2);
430} 206}
431 207
432module_init(rtrack_init); 208module_init(rtrack_init);
433module_exit(rtrack_exit); 209module_exit(rtrack_exit);
434