diff options
Diffstat (limited to 'drivers/media/radio/radio-zoltrix.c')
-rw-r--r-- | drivers/media/radio/radio-zoltrix.c | 442 |
1 files changed, 112 insertions, 330 deletions
diff --git a/drivers/media/radio/radio-zoltrix.c b/drivers/media/radio/radio-zoltrix.c index f5613b948203..026e88eef29c 100644 --- a/drivers/media/radio/radio-zoltrix.c +++ b/drivers/media/radio/radio-zoltrix.c | |||
@@ -1,5 +1,6 @@ | |||
1 | /* zoltrix radio plus driver for Linux radio support | 1 | /* |
2 | * (c) 1998 C. van Schaik <carl@leg.uct.ac.za> | 2 | * Zoltrix Radio Plus driver |
3 | * Copyright 1998 C. van Schaik <carl@leg.uct.ac.za> | ||
3 | * | 4 | * |
4 | * BUGS | 5 | * BUGS |
5 | * Due to the inconsistency in reading from the signal flags | 6 | * Due to the inconsistency in reading from the signal flags |
@@ -27,6 +28,14 @@ | |||
27 | * | 28 | * |
28 | * 2006-07-24 - Converted to V4L2 API | 29 | * 2006-07-24 - Converted to V4L2 API |
29 | * by Mauro Carvalho Chehab <mchehab@infradead.org> | 30 | * by Mauro Carvalho Chehab <mchehab@infradead.org> |
31 | * | ||
32 | * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@cisco.com> | ||
33 | * | ||
34 | * Note that this is the driver for the Zoltrix Radio Plus. | ||
35 | * This driver does not work for the Zoltrix Radio Plus 108 or the | ||
36 | * Zoltrix Radio Plus for Windows. | ||
37 | * | ||
38 | * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool. | ||
30 | */ | 39 | */ |
31 | 40 | ||
32 | #include <linux/module.h> /* Modules */ | 41 | #include <linux/module.h> /* Modules */ |
@@ -36,82 +45,70 @@ | |||
36 | #include <linux/videodev2.h> /* kernel radio structs */ | 45 | #include <linux/videodev2.h> /* kernel radio structs */ |
37 | #include <linux/mutex.h> | 46 | #include <linux/mutex.h> |
38 | #include <linux/io.h> /* outb, outb_p */ | 47 | #include <linux/io.h> /* outb, outb_p */ |
48 | #include <linux/slab.h> | ||
39 | #include <media/v4l2-device.h> | 49 | #include <media/v4l2-device.h> |
40 | #include <media/v4l2-ioctl.h> | 50 | #include <media/v4l2-ioctl.h> |
51 | #include "radio-isa.h" | ||
41 | 52 | ||
42 | MODULE_AUTHOR("C.van Schaik"); | 53 | MODULE_AUTHOR("C. van Schaik"); |
43 | MODULE_DESCRIPTION("A driver for the Zoltrix Radio Plus."); | 54 | MODULE_DESCRIPTION("A driver for the Zoltrix Radio Plus."); |
44 | MODULE_LICENSE("GPL"); | 55 | MODULE_LICENSE("GPL"); |
45 | MODULE_VERSION("0.0.3"); | 56 | MODULE_VERSION("0.1.99"); |
46 | 57 | ||
47 | #ifndef CONFIG_RADIO_ZOLTRIX_PORT | 58 | #ifndef CONFIG_RADIO_ZOLTRIX_PORT |
48 | #define CONFIG_RADIO_ZOLTRIX_PORT -1 | 59 | #define CONFIG_RADIO_ZOLTRIX_PORT -1 |
49 | #endif | 60 | #endif |
50 | 61 | ||
51 | static int io = CONFIG_RADIO_ZOLTRIX_PORT; | 62 | #define ZOLTRIX_MAX 2 |
52 | static int radio_nr = -1; | 63 | |
64 | static int io[ZOLTRIX_MAX] = { [0] = CONFIG_RADIO_ZOLTRIX_PORT, | ||
65 | [1 ... (ZOLTRIX_MAX - 1)] = -1 }; | ||
66 | static int radio_nr[ZOLTRIX_MAX] = { [0 ... (ZOLTRIX_MAX - 1)] = -1 }; | ||
53 | 67 | ||
54 | module_param(io, int, 0); | 68 | module_param_array(io, int, NULL, 0444); |
55 | MODULE_PARM_DESC(io, "I/O address of the Zoltrix Radio Plus (0x20c or 0x30c)"); | 69 | MODULE_PARM_DESC(io, "I/O addresses of the Zoltrix Radio Plus card (0x20c or 0x30c)"); |
56 | module_param(radio_nr, int, 0); | 70 | module_param_array(radio_nr, int, NULL, 0444); |
71 | MODULE_PARM_DESC(radio_nr, "Radio device numbers"); | ||
57 | 72 | ||
58 | struct zoltrix { | 73 | struct zoltrix { |
59 | struct v4l2_device v4l2_dev; | 74 | struct radio_isa_card isa; |
60 | struct video_device vdev; | ||
61 | int io; | ||
62 | int curvol; | 75 | int curvol; |
63 | unsigned long curfreq; | 76 | bool muted; |
64 | int muted; | ||
65 | unsigned int stereo; | ||
66 | struct mutex lock; | ||
67 | }; | 77 | }; |
68 | 78 | ||
69 | static struct zoltrix zoltrix_card; | 79 | static struct radio_isa_card *zoltrix_alloc(void) |
80 | { | ||
81 | struct zoltrix *zol = kzalloc(sizeof(*zol), GFP_KERNEL); | ||
82 | |||
83 | return zol ? &zol->isa : NULL; | ||
84 | } | ||
70 | 85 | ||
71 | static int zol_setvol(struct zoltrix *zol, int vol) | 86 | static int zoltrix_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol) |
72 | { | 87 | { |
73 | zol->curvol = vol; | 88 | struct zoltrix *zol = container_of(isa, struct zoltrix, isa); |
74 | if (zol->muted) | ||
75 | return 0; | ||
76 | 89 | ||
77 | mutex_lock(&zol->lock); | 90 | zol->curvol = vol; |
78 | if (vol == 0) { | 91 | zol->muted = mute; |
79 | outb(0, zol->io); | 92 | if (mute || vol == 0) { |
80 | outb(0, zol->io); | 93 | outb(0, isa->io); |
81 | inb(zol->io + 3); /* Zoltrix needs to be read to confirm */ | 94 | outb(0, isa->io); |
82 | mutex_unlock(&zol->lock); | 95 | inb(isa->io + 3); /* Zoltrix needs to be read to confirm */ |
83 | return 0; | 96 | return 0; |
84 | } | 97 | } |
85 | 98 | ||
86 | outb(zol->curvol-1, zol->io); | 99 | outb(vol - 1, isa->io); |
87 | msleep(10); | 100 | msleep(10); |
88 | inb(zol->io + 2); | 101 | inb(isa->io + 2); |
89 | mutex_unlock(&zol->lock); | ||
90 | return 0; | 102 | return 0; |
91 | } | 103 | } |
92 | 104 | ||
93 | static void zol_mute(struct zoltrix *zol) | 105 | /* tunes the radio to the desired frequency */ |
94 | { | 106 | static int zoltrix_s_frequency(struct radio_isa_card *isa, u32 freq) |
95 | zol->muted = 1; | ||
96 | mutex_lock(&zol->lock); | ||
97 | outb(0, zol->io); | ||
98 | outb(0, zol->io); | ||
99 | inb(zol->io + 3); /* Zoltrix needs to be read to confirm */ | ||
100 | mutex_unlock(&zol->lock); | ||
101 | } | ||
102 | |||
103 | static void zol_unmute(struct zoltrix *zol) | ||
104 | { | ||
105 | zol->muted = 0; | ||
106 | zol_setvol(zol, zol->curvol); | ||
107 | } | ||
108 | |||
109 | static int zol_setfreq(struct zoltrix *zol, unsigned long freq) | ||
110 | { | 107 | { |
111 | /* tunes the radio to the desired frequency */ | 108 | struct zoltrix *zol = container_of(isa, struct zoltrix, isa); |
112 | struct v4l2_device *v4l2_dev = &zol->v4l2_dev; | 109 | struct v4l2_device *v4l2_dev = &isa->v4l2_dev; |
113 | unsigned long long bitmask, f, m; | 110 | unsigned long long bitmask, f, m; |
114 | unsigned int stereo = zol->stereo; | 111 | bool stereo = isa->stereo; |
115 | int i; | 112 | int i; |
116 | 113 | ||
117 | if (freq == 0) { | 114 | if (freq == 0) { |
@@ -125,340 +122,125 @@ static int zol_setfreq(struct zoltrix *zol, unsigned long freq) | |||
125 | bitmask = 0xc480402c10080000ull; | 122 | bitmask = 0xc480402c10080000ull; |
126 | i = 45; | 123 | i = 45; |
127 | 124 | ||
128 | mutex_lock(&zol->lock); | 125 | outb(0, isa->io); |
129 | 126 | outb(0, isa->io); | |
130 | zol->curfreq = freq; | 127 | inb(isa->io + 3); /* Zoltrix needs to be read to confirm */ |
131 | |||
132 | outb(0, zol->io); | ||
133 | outb(0, zol->io); | ||
134 | inb(zol->io + 3); /* Zoltrix needs to be read to confirm */ | ||
135 | 128 | ||
136 | outb(0x40, zol->io); | 129 | outb(0x40, isa->io); |
137 | outb(0xc0, zol->io); | 130 | outb(0xc0, isa->io); |
138 | 131 | ||
139 | bitmask = (bitmask ^ ((f & 0xff) << 47) ^ ((f & 0xff00) << 30) ^ (stereo << 31)); | 132 | bitmask = (bitmask ^ ((f & 0xff) << 47) ^ ((f & 0xff00) << 30) ^ (stereo << 31)); |
140 | while (i--) { | 133 | while (i--) { |
141 | if ((bitmask & 0x8000000000000000ull) != 0) { | 134 | if ((bitmask & 0x8000000000000000ull) != 0) { |
142 | outb(0x80, zol->io); | 135 | outb(0x80, isa->io); |
143 | udelay(50); | 136 | udelay(50); |
144 | outb(0x00, zol->io); | 137 | outb(0x00, isa->io); |
145 | udelay(50); | 138 | udelay(50); |
146 | outb(0x80, zol->io); | 139 | outb(0x80, isa->io); |
147 | udelay(50); | 140 | udelay(50); |
148 | } else { | 141 | } else { |
149 | outb(0xc0, zol->io); | 142 | outb(0xc0, isa->io); |
150 | udelay(50); | 143 | udelay(50); |
151 | outb(0x40, zol->io); | 144 | outb(0x40, isa->io); |
152 | udelay(50); | 145 | udelay(50); |
153 | outb(0xc0, zol->io); | 146 | outb(0xc0, isa->io); |
154 | udelay(50); | 147 | udelay(50); |
155 | } | 148 | } |
156 | bitmask *= 2; | 149 | bitmask *= 2; |
157 | } | 150 | } |
158 | /* termination sequence */ | 151 | /* termination sequence */ |
159 | outb(0x80, zol->io); | 152 | outb(0x80, isa->io); |
160 | outb(0xc0, zol->io); | 153 | outb(0xc0, isa->io); |
161 | outb(0x40, zol->io); | 154 | outb(0x40, isa->io); |
162 | udelay(1000); | 155 | udelay(1000); |
163 | inb(zol->io + 2); | 156 | inb(isa->io + 2); |
164 | |||
165 | udelay(1000); | 157 | udelay(1000); |
166 | 158 | ||
167 | if (zol->muted) { | 159 | return zoltrix_s_mute_volume(isa, zol->muted, zol->curvol); |
168 | outb(0, zol->io); | ||
169 | outb(0, zol->io); | ||
170 | inb(zol->io + 3); | ||
171 | udelay(1000); | ||
172 | } | ||
173 | |||
174 | mutex_unlock(&zol->lock); | ||
175 | |||
176 | if (!zol->muted) | ||
177 | zol_setvol(zol, zol->curvol); | ||
178 | return 0; | ||
179 | } | 160 | } |
180 | 161 | ||
181 | /* Get signal strength */ | 162 | /* Get signal strength */ |
182 | static int zol_getsigstr(struct zoltrix *zol) | 163 | static u32 zoltrix_g_rxsubchans(struct radio_isa_card *isa) |
183 | { | 164 | { |
165 | struct zoltrix *zol = container_of(isa, struct zoltrix, isa); | ||
184 | int a, b; | 166 | int a, b; |
185 | 167 | ||
186 | mutex_lock(&zol->lock); | 168 | outb(0x00, isa->io); /* This stuff I found to do nothing */ |
187 | outb(0x00, zol->io); /* This stuff I found to do nothing */ | 169 | outb(zol->curvol, isa->io); |
188 | outb(zol->curvol, zol->io); | ||
189 | msleep(20); | 170 | msleep(20); |
190 | 171 | ||
191 | a = inb(zol->io); | 172 | a = inb(isa->io); |
192 | msleep(10); | 173 | msleep(10); |
193 | b = inb(zol->io); | 174 | b = inb(isa->io); |
194 | 175 | ||
195 | mutex_unlock(&zol->lock); | 176 | return (a == b && a == 0xcf) ? |
196 | 177 | V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO; | |
197 | if (a != b) | ||
198 | return 0; | ||
199 | |||
200 | /* I found this out by playing with a binary scanner on the card io */ | ||
201 | return a == 0xcf || a == 0xdf || a == 0xef; | ||
202 | } | 178 | } |
203 | 179 | ||
204 | static int zol_is_stereo(struct zoltrix *zol) | 180 | static u32 zoltrix_g_signal(struct radio_isa_card *isa) |
205 | { | 181 | { |
206 | int x1, x2; | 182 | struct zoltrix *zol = container_of(isa, struct zoltrix, isa); |
207 | 183 | int a, b; | |
208 | mutex_lock(&zol->lock); | ||
209 | 184 | ||
210 | outb(0x00, zol->io); | 185 | outb(0x00, isa->io); /* This stuff I found to do nothing */ |
211 | outb(zol->curvol, zol->io); | 186 | outb(zol->curvol, isa->io); |
212 | msleep(20); | 187 | msleep(20); |
213 | 188 | ||
214 | x1 = inb(zol->io); | 189 | a = inb(isa->io); |
215 | msleep(10); | 190 | msleep(10); |
216 | x2 = inb(zol->io); | 191 | b = inb(isa->io); |
217 | |||
218 | mutex_unlock(&zol->lock); | ||
219 | |||
220 | return x1 == x2 && x1 == 0xcf; | ||
221 | } | ||
222 | |||
223 | static int vidioc_querycap(struct file *file, void *priv, | ||
224 | struct v4l2_capability *v) | ||
225 | { | ||
226 | strlcpy(v->driver, "radio-zoltrix", sizeof(v->driver)); | ||
227 | strlcpy(v->card, "Zoltrix Radio", sizeof(v->card)); | ||
228 | strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); | ||
229 | v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; | ||
230 | return 0; | ||
231 | } | ||
232 | |||
233 | static int vidioc_g_tuner(struct file *file, void *priv, | ||
234 | struct v4l2_tuner *v) | ||
235 | { | ||
236 | struct zoltrix *zol = video_drvdata(file); | ||
237 | |||
238 | if (v->index > 0) | ||
239 | return -EINVAL; | ||
240 | |||
241 | strlcpy(v->name, "FM", sizeof(v->name)); | ||
242 | v->type = V4L2_TUNER_RADIO; | ||
243 | v->rangelow = 88 * 16000; | ||
244 | v->rangehigh = 108 * 16000; | ||
245 | v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; | ||
246 | v->capability = V4L2_TUNER_CAP_LOW; | ||
247 | if (zol_is_stereo(zol)) | ||
248 | v->audmode = V4L2_TUNER_MODE_STEREO; | ||
249 | else | ||
250 | v->audmode = V4L2_TUNER_MODE_MONO; | ||
251 | v->signal = 0xFFFF * zol_getsigstr(zol); | ||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | static int vidioc_s_tuner(struct file *file, void *priv, | ||
256 | struct v4l2_tuner *v) | ||
257 | { | ||
258 | return v->index ? -EINVAL : 0; | ||
259 | } | ||
260 | |||
261 | static int vidioc_s_frequency(struct file *file, void *priv, | ||
262 | struct v4l2_frequency *f) | ||
263 | { | ||
264 | struct zoltrix *zol = video_drvdata(file); | ||
265 | |||
266 | if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) | ||
267 | return -EINVAL; | ||
268 | if (zol_setfreq(zol, f->frequency) != 0) | ||
269 | return -EINVAL; | ||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | static int vidioc_g_frequency(struct file *file, void *priv, | ||
274 | struct v4l2_frequency *f) | ||
275 | { | ||
276 | struct zoltrix *zol = video_drvdata(file); | ||
277 | 192 | ||
278 | if (f->tuner != 0) | 193 | if (a != b) |
279 | return -EINVAL; | ||
280 | f->type = V4L2_TUNER_RADIO; | ||
281 | f->frequency = zol->curfreq; | ||
282 | return 0; | ||
283 | } | ||
284 | |||
285 | static int vidioc_queryctrl(struct file *file, void *priv, | ||
286 | struct v4l2_queryctrl *qc) | ||
287 | { | ||
288 | switch (qc->id) { | ||
289 | case V4L2_CID_AUDIO_MUTE: | ||
290 | return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); | ||
291 | case V4L2_CID_AUDIO_VOLUME: | ||
292 | return v4l2_ctrl_query_fill(qc, 0, 65535, 4096, 65535); | ||
293 | } | ||
294 | return -EINVAL; | ||
295 | } | ||
296 | |||
297 | static int vidioc_g_ctrl(struct file *file, void *priv, | ||
298 | struct v4l2_control *ctrl) | ||
299 | { | ||
300 | struct zoltrix *zol = video_drvdata(file); | ||
301 | |||
302 | switch (ctrl->id) { | ||
303 | case V4L2_CID_AUDIO_MUTE: | ||
304 | ctrl->value = zol->muted; | ||
305 | return 0; | ||
306 | case V4L2_CID_AUDIO_VOLUME: | ||
307 | ctrl->value = zol->curvol * 4096; | ||
308 | return 0; | ||
309 | } | ||
310 | return -EINVAL; | ||
311 | } | ||
312 | |||
313 | static int vidioc_s_ctrl(struct file *file, void *priv, | ||
314 | struct v4l2_control *ctrl) | ||
315 | { | ||
316 | struct zoltrix *zol = video_drvdata(file); | ||
317 | |||
318 | switch (ctrl->id) { | ||
319 | case V4L2_CID_AUDIO_MUTE: | ||
320 | if (ctrl->value) | ||
321 | zol_mute(zol); | ||
322 | else { | ||
323 | zol_unmute(zol); | ||
324 | zol_setvol(zol, zol->curvol); | ||
325 | } | ||
326 | return 0; | ||
327 | case V4L2_CID_AUDIO_VOLUME: | ||
328 | zol_setvol(zol, ctrl->value / 4096); | ||
329 | return 0; | 194 | return 0; |
330 | } | ||
331 | zol->stereo = 1; | ||
332 | if (zol_setfreq(zol, zol->curfreq) != 0) | ||
333 | return -EINVAL; | ||
334 | #if 0 | ||
335 | /* FIXME: Implement stereo/mono switch on V4L2 */ | ||
336 | if (v->mode & VIDEO_SOUND_STEREO) { | ||
337 | zol->stereo = 1; | ||
338 | zol_setfreq(zol, zol->curfreq); | ||
339 | } | ||
340 | if (v->mode & VIDEO_SOUND_MONO) { | ||
341 | zol->stereo = 0; | ||
342 | zol_setfreq(zol, zol->curfreq); | ||
343 | } | ||
344 | #endif | ||
345 | return -EINVAL; | ||
346 | } | ||
347 | |||
348 | static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) | ||
349 | { | ||
350 | *i = 0; | ||
351 | return 0; | ||
352 | } | ||
353 | |||
354 | static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) | ||
355 | { | ||
356 | return i ? -EINVAL : 0; | ||
357 | } | ||
358 | 195 | ||
359 | static int vidioc_g_audio(struct file *file, void *priv, | 196 | /* I found this out by playing with a binary scanner on the card io */ |
360 | struct v4l2_audio *a) | 197 | return (a == 0xcf || a == 0xdf || a == 0xef) ? 0xffff : 0; |
361 | { | ||
362 | a->index = 0; | ||
363 | strlcpy(a->name, "Radio", sizeof(a->name)); | ||
364 | a->capability = V4L2_AUDCAP_STEREO; | ||
365 | return 0; | ||
366 | } | 198 | } |
367 | 199 | ||
368 | static int vidioc_s_audio(struct file *file, void *priv, | 200 | static int zoltrix_s_stereo(struct radio_isa_card *isa, bool stereo) |
369 | struct v4l2_audio *a) | ||
370 | { | 201 | { |
371 | return a->index ? -EINVAL : 0; | 202 | return zoltrix_s_frequency(isa, isa->freq); |
372 | } | 203 | } |
373 | 204 | ||
374 | static const struct v4l2_file_operations zoltrix_fops = | 205 | static const struct radio_isa_ops zoltrix_ops = { |
375 | { | 206 | .alloc = zoltrix_alloc, |
376 | .owner = THIS_MODULE, | 207 | .s_mute_volume = zoltrix_s_mute_volume, |
377 | .unlocked_ioctl = video_ioctl2, | 208 | .s_frequency = zoltrix_s_frequency, |
209 | .s_stereo = zoltrix_s_stereo, | ||
210 | .g_rxsubchans = zoltrix_g_rxsubchans, | ||
211 | .g_signal = zoltrix_g_signal, | ||
378 | }; | 212 | }; |
379 | 213 | ||
380 | static const struct v4l2_ioctl_ops zoltrix_ioctl_ops = { | 214 | static const int zoltrix_ioports[] = { 0x20c, 0x30c }; |
381 | .vidioc_querycap = vidioc_querycap, | 215 | |
382 | .vidioc_g_tuner = vidioc_g_tuner, | 216 | static struct radio_isa_driver zoltrix_driver = { |
383 | .vidioc_s_tuner = vidioc_s_tuner, | 217 | .driver = { |
384 | .vidioc_g_audio = vidioc_g_audio, | 218 | .match = radio_isa_match, |
385 | .vidioc_s_audio = vidioc_s_audio, | 219 | .probe = radio_isa_probe, |
386 | .vidioc_g_input = vidioc_g_input, | 220 | .remove = radio_isa_remove, |
387 | .vidioc_s_input = vidioc_s_input, | 221 | .driver = { |
388 | .vidioc_g_frequency = vidioc_g_frequency, | 222 | .name = "radio-zoltrix", |
389 | .vidioc_s_frequency = vidioc_s_frequency, | 223 | }, |
390 | .vidioc_queryctrl = vidioc_queryctrl, | 224 | }, |
391 | .vidioc_g_ctrl = vidioc_g_ctrl, | 225 | .io_params = io, |
392 | .vidioc_s_ctrl = vidioc_s_ctrl, | 226 | .radio_nr_params = radio_nr, |
227 | .io_ports = zoltrix_ioports, | ||
228 | .num_of_io_ports = ARRAY_SIZE(zoltrix_ioports), | ||
229 | .region_size = 2, | ||
230 | .card = "Zoltrix Radio Plus", | ||
231 | .ops = &zoltrix_ops, | ||
232 | .has_stereo = true, | ||
233 | .max_volume = 15, | ||
393 | }; | 234 | }; |
394 | 235 | ||
395 | static int __init zoltrix_init(void) | 236 | static int __init zoltrix_init(void) |
396 | { | 237 | { |
397 | struct zoltrix *zol = &zoltrix_card; | 238 | return isa_register_driver(&zoltrix_driver.driver, ZOLTRIX_MAX); |
398 | struct v4l2_device *v4l2_dev = &zol->v4l2_dev; | ||
399 | int res; | ||
400 | |||
401 | strlcpy(v4l2_dev->name, "zoltrix", sizeof(v4l2_dev->name)); | ||
402 | zol->io = io; | ||
403 | if (zol->io == -1) { | ||
404 | v4l2_err(v4l2_dev, "You must set an I/O address with io=0x20c or 0x30c\n"); | ||
405 | return -EINVAL; | ||
406 | } | ||
407 | if (zol->io != 0x20c && zol->io != 0x30c) { | ||
408 | v4l2_err(v4l2_dev, "invalid port, try 0x20c or 0x30c\n"); | ||
409 | return -ENXIO; | ||
410 | } | ||
411 | |||
412 | if (!request_region(zol->io, 2, "zoltrix")) { | ||
413 | v4l2_err(v4l2_dev, "port 0x%x already in use\n", zol->io); | ||
414 | return -EBUSY; | ||
415 | } | ||
416 | |||
417 | res = v4l2_device_register(NULL, v4l2_dev); | ||
418 | if (res < 0) { | ||
419 | release_region(zol->io, 2); | ||
420 | v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); | ||
421 | return res; | ||
422 | } | ||
423 | |||
424 | mutex_init(&zol->lock); | ||
425 | |||
426 | /* mute card - prevents noisy bootups */ | ||
427 | |||
428 | /* this ensures that the volume is all the way down */ | ||
429 | |||
430 | outb(0, zol->io); | ||
431 | outb(0, zol->io); | ||
432 | msleep(20); | ||
433 | inb(zol->io + 3); | ||
434 | |||
435 | zol->curvol = 0; | ||
436 | zol->stereo = 1; | ||
437 | |||
438 | strlcpy(zol->vdev.name, v4l2_dev->name, sizeof(zol->vdev.name)); | ||
439 | zol->vdev.v4l2_dev = v4l2_dev; | ||
440 | zol->vdev.fops = &zoltrix_fops; | ||
441 | zol->vdev.ioctl_ops = &zoltrix_ioctl_ops; | ||
442 | zol->vdev.release = video_device_release_empty; | ||
443 | video_set_drvdata(&zol->vdev, zol); | ||
444 | |||
445 | if (video_register_device(&zol->vdev, VFL_TYPE_RADIO, radio_nr) < 0) { | ||
446 | v4l2_device_unregister(v4l2_dev); | ||
447 | release_region(zol->io, 2); | ||
448 | return -EINVAL; | ||
449 | } | ||
450 | v4l2_info(v4l2_dev, "Zoltrix Radio Plus card driver.\n"); | ||
451 | |||
452 | return 0; | ||
453 | } | 239 | } |
454 | 240 | ||
455 | static void __exit zoltrix_exit(void) | 241 | static void __exit zoltrix_exit(void) |
456 | { | 242 | { |
457 | struct zoltrix *zol = &zoltrix_card; | 243 | isa_unregister_driver(&zoltrix_driver.driver); |
458 | |||
459 | video_unregister_device(&zol->vdev); | ||
460 | v4l2_device_unregister(&zol->v4l2_dev); | ||
461 | release_region(zol->io, 2); | ||
462 | } | 244 | } |
463 | 245 | ||
464 | module_init(zoltrix_init); | 246 | module_init(zoltrix_init); |