diff options
Diffstat (limited to 'drivers/media/radio')
-rw-r--r-- | drivers/media/radio/radio-gemtek.c | 397 |
1 files changed, 187 insertions, 210 deletions
diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c index 2b68be773f13..91448c47b0cf 100644 --- a/drivers/media/radio/radio-gemtek.c +++ b/drivers/media/radio/radio-gemtek.c | |||
@@ -20,16 +20,15 @@ | |||
20 | #include <linux/init.h> /* Initdata */ | 20 | #include <linux/init.h> /* Initdata */ |
21 | #include <linux/ioport.h> /* request_region */ | 21 | #include <linux/ioport.h> /* request_region */ |
22 | #include <linux/delay.h> /* udelay */ | 22 | #include <linux/delay.h> /* udelay */ |
23 | #include <asm/io.h> /* outb, outb_p */ | ||
24 | #include <asm/uaccess.h> /* copy to/from user */ | ||
25 | #include <linux/videodev2.h> /* kernel radio structs */ | 23 | #include <linux/videodev2.h> /* kernel radio structs */ |
24 | #include <linux/version.h> /* for KERNEL_VERSION MACRO */ | ||
25 | #include <linux/mutex.h> | ||
26 | #include <linux/io.h> /* outb, outb_p */ | ||
27 | #include <linux/uaccess.h> /* copy to/from user */ | ||
26 | #include <media/v4l2-ioctl.h> | 28 | #include <media/v4l2-ioctl.h> |
27 | #include <media/v4l2-common.h> | 29 | #include <media/v4l2-device.h> |
28 | #include <linux/spinlock.h> | ||
29 | 30 | ||
30 | #include <linux/version.h> /* for KERNEL_VERSION MACRO */ | 31 | #define RADIO_VERSION KERNEL_VERSION(0, 0, 3) |
31 | #define RADIO_VERSION KERNEL_VERSION(0,0,3) | ||
32 | #define RADIO_BANNER "GemTek Radio card driver: v0.0.3" | ||
33 | 32 | ||
34 | /* | 33 | /* |
35 | * Module info. | 34 | * Module info. |
@@ -57,7 +56,6 @@ static int shutdown = 1; | |||
57 | static int keepmuted = 1; | 56 | static int keepmuted = 1; |
58 | static int initmute = 1; | 57 | static int initmute = 1; |
59 | static int radio_nr = -1; | 58 | static int radio_nr = -1; |
60 | static unsigned long in_use; | ||
61 | 59 | ||
62 | module_param(io, int, 0444); | 60 | module_param(io, int, 0444); |
63 | MODULE_PARM_DESC(io, "Force I/O port for the GemTek Radio card if automatic " | 61 | MODULE_PARM_DESC(io, "Force I/O port for the GemTek Radio card if automatic " |
@@ -112,12 +110,19 @@ module_param(radio_nr, int, 0444); | |||
112 | #define SHORT_DELAY 5 /* usec */ | 110 | #define SHORT_DELAY 5 /* usec */ |
113 | #define LONG_DELAY 75 /* usec */ | 111 | #define LONG_DELAY 75 /* usec */ |
114 | 112 | ||
115 | struct gemtek_device { | 113 | struct gemtek { |
114 | struct v4l2_device v4l2_dev; | ||
115 | struct video_device vdev; | ||
116 | struct mutex lock; | ||
116 | unsigned long lastfreq; | 117 | unsigned long lastfreq; |
117 | int muted; | 118 | int muted; |
119 | int verified; | ||
120 | int io; | ||
118 | u32 bu2614data; | 121 | u32 bu2614data; |
119 | }; | 122 | }; |
120 | 123 | ||
124 | static struct gemtek gemtek_card; | ||
125 | |||
121 | #define BU2614_FREQ_BITS 16 /* D0..D15, Frequency data */ | 126 | #define BU2614_FREQ_BITS 16 /* D0..D15, Frequency data */ |
122 | #define BU2614_PORT_BITS 3 /* P0..P2, Output port control data */ | 127 | #define BU2614_PORT_BITS 3 /* P0..P2, Output port control data */ |
123 | #define BU2614_VOID_BITS 4 /* unused */ | 128 | #define BU2614_VOID_BITS 4 /* unused */ |
@@ -153,10 +158,6 @@ struct gemtek_device { | |||
153 | #define BU2614_FMUN_MASK MKMASK(FMUN) | 158 | #define BU2614_FMUN_MASK MKMASK(FMUN) |
154 | #define BU2614_TEST_MASK MKMASK(TEST) | 159 | #define BU2614_TEST_MASK MKMASK(TEST) |
155 | 160 | ||
156 | static struct gemtek_device gemtek_unit; | ||
157 | |||
158 | static spinlock_t lock; | ||
159 | |||
160 | /* | 161 | /* |
161 | * Set data which will be sent to BU2614FS. | 162 | * Set data which will be sent to BU2614FS. |
162 | */ | 163 | */ |
@@ -166,33 +167,33 @@ static spinlock_t lock; | |||
166 | /* | 167 | /* |
167 | * Transmit settings to BU2614FS over GemTek IC. | 168 | * Transmit settings to BU2614FS over GemTek IC. |
168 | */ | 169 | */ |
169 | static void gemtek_bu2614_transmit(struct gemtek_device *dev) | 170 | static void gemtek_bu2614_transmit(struct gemtek *gt) |
170 | { | 171 | { |
171 | int i, bit, q, mute; | 172 | int i, bit, q, mute; |
172 | 173 | ||
173 | spin_lock(&lock); | 174 | mutex_lock(>->lock); |
174 | 175 | ||
175 | mute = dev->muted ? GEMTEK_MT : 0x00; | 176 | mute = gt->muted ? GEMTEK_MT : 0x00; |
176 | 177 | ||
177 | outb_p(mute | GEMTEK_DA | GEMTEK_CK, io); | 178 | outb_p(mute | GEMTEK_DA | GEMTEK_CK, gt->io); |
178 | udelay(SHORT_DELAY); | 179 | udelay(SHORT_DELAY); |
179 | outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, io); | 180 | outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, gt->io); |
180 | udelay(LONG_DELAY); | 181 | udelay(LONG_DELAY); |
181 | 182 | ||
182 | for (i = 0, q = dev->bu2614data; i < 32; i++, q >>= 1) { | 183 | for (i = 0, q = gt->bu2614data; i < 32; i++, q >>= 1) { |
183 | bit = (q & 1) ? GEMTEK_DA : 0; | 184 | bit = (q & 1) ? GEMTEK_DA : 0; |
184 | outb_p(mute | GEMTEK_CE | bit, io); | 185 | outb_p(mute | GEMTEK_CE | bit, gt->io); |
185 | udelay(SHORT_DELAY); | 186 | udelay(SHORT_DELAY); |
186 | outb_p(mute | GEMTEK_CE | bit | GEMTEK_CK, io); | 187 | outb_p(mute | GEMTEK_CE | bit | GEMTEK_CK, gt->io); |
187 | udelay(SHORT_DELAY); | 188 | udelay(SHORT_DELAY); |
188 | } | 189 | } |
189 | 190 | ||
190 | outb_p(mute | GEMTEK_DA | GEMTEK_CK, io); | 191 | outb_p(mute | GEMTEK_DA | GEMTEK_CK, gt->io); |
191 | udelay(SHORT_DELAY); | 192 | udelay(SHORT_DELAY); |
192 | outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, io); | 193 | outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, gt->io); |
193 | udelay(LONG_DELAY); | 194 | udelay(LONG_DELAY); |
194 | 195 | ||
195 | spin_unlock(&lock); | 196 | mutex_unlock(>->lock); |
196 | } | 197 | } |
197 | 198 | ||
198 | /* | 199 | /* |
@@ -206,107 +207,109 @@ static unsigned long gemtek_convfreq(unsigned long freq) | |||
206 | /* | 207 | /* |
207 | * Set FM-frequency. | 208 | * Set FM-frequency. |
208 | */ | 209 | */ |
209 | static void gemtek_setfreq(struct gemtek_device *dev, unsigned long freq) | 210 | static void gemtek_setfreq(struct gemtek *gt, unsigned long freq) |
210 | { | 211 | { |
211 | 212 | if (keepmuted && hardmute && gt->muted) | |
212 | if (keepmuted && hardmute && dev->muted) | ||
213 | return; | 213 | return; |
214 | 214 | ||
215 | if (freq < GEMTEK_LOWFREQ) | 215 | freq = clamp_val(freq, GEMTEK_LOWFREQ, GEMTEK_HIGHFREQ); |
216 | freq = GEMTEK_LOWFREQ; | ||
217 | else if (freq > GEMTEK_HIGHFREQ) | ||
218 | freq = GEMTEK_HIGHFREQ; | ||
219 | 216 | ||
220 | dev->lastfreq = freq; | 217 | gt->lastfreq = freq; |
221 | dev->muted = 0; | 218 | gt->muted = 0; |
222 | 219 | ||
223 | gemtek_bu2614_set(dev, BU2614_PORT, 0); | 220 | gemtek_bu2614_set(gt, BU2614_PORT, 0); |
224 | gemtek_bu2614_set(dev, BU2614_FMES, 0); | 221 | gemtek_bu2614_set(gt, BU2614_FMES, 0); |
225 | gemtek_bu2614_set(dev, BU2614_SWIN, 0); /* FM-mode */ | 222 | gemtek_bu2614_set(gt, BU2614_SWIN, 0); /* FM-mode */ |
226 | gemtek_bu2614_set(dev, BU2614_SWAL, 0); | 223 | gemtek_bu2614_set(gt, BU2614_SWAL, 0); |
227 | gemtek_bu2614_set(dev, BU2614_FMUN, 1); /* GT bit set */ | 224 | gemtek_bu2614_set(gt, BU2614_FMUN, 1); /* GT bit set */ |
228 | gemtek_bu2614_set(dev, BU2614_TEST, 0); | 225 | gemtek_bu2614_set(gt, BU2614_TEST, 0); |
229 | 226 | ||
230 | gemtek_bu2614_set(dev, BU2614_STDF, GEMTEK_STDF_3_125_KHZ); | 227 | gemtek_bu2614_set(gt, BU2614_STDF, GEMTEK_STDF_3_125_KHZ); |
231 | gemtek_bu2614_set(dev, BU2614_FREQ, gemtek_convfreq(freq)); | 228 | gemtek_bu2614_set(gt, BU2614_FREQ, gemtek_convfreq(freq)); |
232 | 229 | ||
233 | gemtek_bu2614_transmit(dev); | 230 | gemtek_bu2614_transmit(gt); |
234 | } | 231 | } |
235 | 232 | ||
236 | /* | 233 | /* |
237 | * Set mute flag. | 234 | * Set mute flag. |
238 | */ | 235 | */ |
239 | static void gemtek_mute(struct gemtek_device *dev) | 236 | static void gemtek_mute(struct gemtek *gt) |
240 | { | 237 | { |
241 | int i; | 238 | int i; |
242 | dev->muted = 1; | 239 | |
240 | gt->muted = 1; | ||
243 | 241 | ||
244 | if (hardmute) { | 242 | if (hardmute) { |
245 | /* Turn off PLL, disable data output */ | 243 | /* Turn off PLL, disable data output */ |
246 | gemtek_bu2614_set(dev, BU2614_PORT, 0); | 244 | gemtek_bu2614_set(gt, BU2614_PORT, 0); |
247 | gemtek_bu2614_set(dev, BU2614_FMES, 0); /* CT bit off */ | 245 | gemtek_bu2614_set(gt, BU2614_FMES, 0); /* CT bit off */ |
248 | gemtek_bu2614_set(dev, BU2614_SWIN, 0); /* FM-mode */ | 246 | gemtek_bu2614_set(gt, BU2614_SWIN, 0); /* FM-mode */ |
249 | gemtek_bu2614_set(dev, BU2614_SWAL, 0); | 247 | gemtek_bu2614_set(gt, BU2614_SWAL, 0); |
250 | gemtek_bu2614_set(dev, BU2614_FMUN, 0); /* GT bit off */ | 248 | gemtek_bu2614_set(gt, BU2614_FMUN, 0); /* GT bit off */ |
251 | gemtek_bu2614_set(dev, BU2614_TEST, 0); | 249 | gemtek_bu2614_set(gt, BU2614_TEST, 0); |
252 | gemtek_bu2614_set(dev, BU2614_STDF, GEMTEK_PLL_OFF); | 250 | gemtek_bu2614_set(gt, BU2614_STDF, GEMTEK_PLL_OFF); |
253 | gemtek_bu2614_set(dev, BU2614_FREQ, 0); | 251 | gemtek_bu2614_set(gt, BU2614_FREQ, 0); |
254 | gemtek_bu2614_transmit(dev); | 252 | gemtek_bu2614_transmit(gt); |
255 | } else { | 253 | return; |
256 | spin_lock(&lock); | 254 | } |
257 | 255 | ||
258 | /* Read bus contents (CE, CK and DA). */ | 256 | mutex_lock(>->lock); |
259 | i = inb_p(io); | ||
260 | /* Write it back with mute flag set. */ | ||
261 | outb_p((i >> 5) | GEMTEK_MT, io); | ||
262 | udelay(SHORT_DELAY); | ||
263 | 257 | ||
264 | spin_unlock(&lock); | 258 | /* Read bus contents (CE, CK and DA). */ |
265 | } | 259 | i = inb_p(gt->io); |
260 | /* Write it back with mute flag set. */ | ||
261 | outb_p((i >> 5) | GEMTEK_MT, gt->io); | ||
262 | udelay(SHORT_DELAY); | ||
263 | |||
264 | mutex_unlock(>->lock); | ||
266 | } | 265 | } |
267 | 266 | ||
268 | /* | 267 | /* |
269 | * Unset mute flag. | 268 | * Unset mute flag. |
270 | */ | 269 | */ |
271 | static void gemtek_unmute(struct gemtek_device *dev) | 270 | static void gemtek_unmute(struct gemtek *gt) |
272 | { | 271 | { |
273 | int i; | 272 | int i; |
274 | dev->muted = 0; | ||
275 | 273 | ||
274 | gt->muted = 0; | ||
276 | if (hardmute) { | 275 | if (hardmute) { |
277 | /* Turn PLL back on. */ | 276 | /* Turn PLL back on. */ |
278 | gemtek_setfreq(dev, dev->lastfreq); | 277 | gemtek_setfreq(gt, gt->lastfreq); |
279 | } else { | 278 | return; |
280 | spin_lock(&lock); | 279 | } |
280 | mutex_lock(>->lock); | ||
281 | 281 | ||
282 | i = inb_p(io); | 282 | i = inb_p(gt->io); |
283 | outb_p(i >> 5, io); | 283 | outb_p(i >> 5, gt->io); |
284 | udelay(SHORT_DELAY); | 284 | udelay(SHORT_DELAY); |
285 | 285 | ||
286 | spin_unlock(&lock); | 286 | mutex_unlock(>->lock); |
287 | } | ||
288 | } | 287 | } |
289 | 288 | ||
290 | /* | 289 | /* |
291 | * Get signal strength (= stereo status). | 290 | * Get signal strength (= stereo status). |
292 | */ | 291 | */ |
293 | static inline int gemtek_getsigstr(void) | 292 | static inline int gemtek_getsigstr(struct gemtek *gt) |
294 | { | 293 | { |
295 | return inb_p(io) & GEMTEK_NS ? 0 : 1; | 294 | int sig; |
295 | |||
296 | mutex_lock(>->lock); | ||
297 | sig = inb_p(gt->io) & GEMTEK_NS ? 0 : 1; | ||
298 | mutex_unlock(>->lock); | ||
299 | return sig; | ||
296 | } | 300 | } |
297 | 301 | ||
298 | /* | 302 | /* |
299 | * Check if requested card acts like GemTek Radio card. | 303 | * Check if requested card acts like GemTek Radio card. |
300 | */ | 304 | */ |
301 | static int gemtek_verify(int port) | 305 | static int gemtek_verify(struct gemtek *gt, int port) |
302 | { | 306 | { |
303 | static int verified = -1; | ||
304 | int i, q; | 307 | int i, q; |
305 | 308 | ||
306 | if (verified == port) | 309 | if (gt->verified == port) |
307 | return 1; | 310 | return 1; |
308 | 311 | ||
309 | spin_lock(&lock); | 312 | mutex_lock(>->lock); |
310 | 313 | ||
311 | q = inb_p(port); /* Read bus contents before probing. */ | 314 | q = inb_p(port); /* Read bus contents before probing. */ |
312 | /* Try to turn on CE, CK and DA respectively and check if card responds | 315 | /* Try to turn on CE, CK and DA respectively and check if card responds |
@@ -316,15 +319,15 @@ static int gemtek_verify(int port) | |||
316 | udelay(SHORT_DELAY); | 319 | udelay(SHORT_DELAY); |
317 | 320 | ||
318 | if ((inb_p(port) & (~GEMTEK_NS)) != (0x17 | (1 << (i + 5)))) { | 321 | if ((inb_p(port) & (~GEMTEK_NS)) != (0x17 | (1 << (i + 5)))) { |
319 | spin_unlock(&lock); | 322 | mutex_unlock(>->lock); |
320 | return 0; | 323 | return 0; |
321 | } | 324 | } |
322 | } | 325 | } |
323 | outb_p(q >> 5, port); /* Write bus contents back. */ | 326 | outb_p(q >> 5, port); /* Write bus contents back. */ |
324 | udelay(SHORT_DELAY); | 327 | udelay(SHORT_DELAY); |
325 | 328 | ||
326 | spin_unlock(&lock); | 329 | mutex_unlock(>->lock); |
327 | verified = port; | 330 | gt->verified = port; |
328 | 331 | ||
329 | return 1; | 332 | return 1; |
330 | } | 333 | } |
@@ -332,83 +335,61 @@ static int gemtek_verify(int port) | |||
332 | /* | 335 | /* |
333 | * Automatic probing for card. | 336 | * Automatic probing for card. |
334 | */ | 337 | */ |
335 | static int gemtek_probe(void) | 338 | static int gemtek_probe(struct gemtek *gt) |
336 | { | 339 | { |
340 | struct v4l2_device *v4l2_dev = >->v4l2_dev; | ||
337 | int ioports[] = { 0x20c, 0x30c, 0x24c, 0x34c, 0x248, 0x28c }; | 341 | int ioports[] = { 0x20c, 0x30c, 0x24c, 0x34c, 0x248, 0x28c }; |
338 | int i; | 342 | int i; |
339 | 343 | ||
340 | if (!probe) { | 344 | if (!probe) { |
341 | printk(KERN_INFO "Automatic device probing disabled.\n"); | 345 | v4l2_info(v4l2_dev, "Automatic device probing disabled.\n"); |
342 | return -1; | 346 | return -1; |
343 | } | 347 | } |
344 | 348 | ||
345 | printk(KERN_INFO "Automatic device probing enabled.\n"); | 349 | v4l2_info(v4l2_dev, "Automatic device probing enabled.\n"); |
346 | 350 | ||
347 | for (i = 0; i < ARRAY_SIZE(ioports); ++i) { | 351 | for (i = 0; i < ARRAY_SIZE(ioports); ++i) { |
348 | printk(KERN_INFO "Trying I/O port 0x%x...\n", ioports[i]); | 352 | v4l2_info(v4l2_dev, "Trying I/O port 0x%x...\n", ioports[i]); |
349 | 353 | ||
350 | if (!request_region(ioports[i], 1, "gemtek-probe")) { | 354 | if (!request_region(ioports[i], 1, "gemtek-probe")) { |
351 | printk(KERN_WARNING "I/O port 0x%x busy!\n", | 355 | v4l2_warn(v4l2_dev, "I/O port 0x%x busy!\n", |
352 | ioports[i]); | 356 | ioports[i]); |
353 | continue; | 357 | continue; |
354 | } | 358 | } |
355 | 359 | ||
356 | if (gemtek_verify(ioports[i])) { | 360 | if (gemtek_verify(gt, ioports[i])) { |
357 | printk(KERN_INFO "Card found from I/O port " | 361 | v4l2_info(v4l2_dev, "Card found from I/O port " |
358 | "0x%x!\n", ioports[i]); | 362 | "0x%x!\n", ioports[i]); |
359 | 363 | ||
360 | release_region(ioports[i], 1); | 364 | release_region(ioports[i], 1); |
361 | 365 | gt->io = ioports[i]; | |
362 | io = ioports[i]; | 366 | return gt->io; |
363 | return io; | ||
364 | } | 367 | } |
365 | 368 | ||
366 | release_region(ioports[i], 1); | 369 | release_region(ioports[i], 1); |
367 | } | 370 | } |
368 | 371 | ||
369 | printk(KERN_ERR "Automatic probing failed!\n"); | 372 | v4l2_err(v4l2_dev, "Automatic probing failed!\n"); |
370 | |||
371 | return -1; | 373 | return -1; |
372 | } | 374 | } |
373 | 375 | ||
374 | /* | 376 | /* |
375 | * Video 4 Linux stuff. | 377 | * Video 4 Linux stuff. |
376 | */ | 378 | */ |
377 | 379 | static int gemtek_open(struct file *file) | |
378 | static struct v4l2_queryctrl radio_qctrl[] = { | ||
379 | { | ||
380 | .id = V4L2_CID_AUDIO_MUTE, | ||
381 | .name = "Mute", | ||
382 | .minimum = 0, | ||
383 | .maximum = 1, | ||
384 | .default_value = 1, | ||
385 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
386 | }, { | ||
387 | .id = V4L2_CID_AUDIO_VOLUME, | ||
388 | .name = "Volume", | ||
389 | .minimum = 0, | ||
390 | .maximum = 65535, | ||
391 | .step = 65535, | ||
392 | .default_value = 0xff, | ||
393 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
394 | } | ||
395 | }; | ||
396 | |||
397 | static int gemtek_exclusive_open(struct file *file) | ||
398 | { | 380 | { |
399 | return test_and_set_bit(0, &in_use) ? -EBUSY : 0; | 381 | return 0; |
400 | } | 382 | } |
401 | 383 | ||
402 | static int gemtek_exclusive_release(struct file *file) | 384 | static int gemtek_release(struct file *file) |
403 | { | 385 | { |
404 | clear_bit(0, &in_use); | ||
405 | return 0; | 386 | return 0; |
406 | } | 387 | } |
407 | 388 | ||
408 | static const struct v4l2_file_operations gemtek_fops = { | 389 | static const struct v4l2_file_operations gemtek_fops = { |
409 | .owner = THIS_MODULE, | 390 | .owner = THIS_MODULE, |
410 | .open = gemtek_exclusive_open, | 391 | .open = gemtek_open, |
411 | .release = gemtek_exclusive_release, | 392 | .release = gemtek_release, |
412 | .ioctl = video_ioctl2, | 393 | .ioctl = video_ioctl2, |
413 | }; | 394 | }; |
414 | 395 | ||
@@ -417,23 +398,25 @@ static int vidioc_querycap(struct file *file, void *priv, | |||
417 | { | 398 | { |
418 | strlcpy(v->driver, "radio-gemtek", sizeof(v->driver)); | 399 | strlcpy(v->driver, "radio-gemtek", sizeof(v->driver)); |
419 | strlcpy(v->card, "GemTek", sizeof(v->card)); | 400 | strlcpy(v->card, "GemTek", sizeof(v->card)); |
420 | sprintf(v->bus_info, "ISA"); | 401 | strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); |
421 | v->version = RADIO_VERSION; | 402 | v->version = RADIO_VERSION; |
422 | v->capabilities = V4L2_CAP_TUNER; | 403 | v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; |
423 | return 0; | 404 | return 0; |
424 | } | 405 | } |
425 | 406 | ||
426 | static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) | 407 | static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) |
427 | { | 408 | { |
409 | struct gemtek *gt = video_drvdata(file); | ||
410 | |||
428 | if (v->index > 0) | 411 | if (v->index > 0) |
429 | return -EINVAL; | 412 | return -EINVAL; |
430 | 413 | ||
431 | strcpy(v->name, "FM"); | 414 | strlcpy(v->name, "FM", sizeof(v->name)); |
432 | v->type = V4L2_TUNER_RADIO; | 415 | v->type = V4L2_TUNER_RADIO; |
433 | v->rangelow = GEMTEK_LOWFREQ; | 416 | v->rangelow = GEMTEK_LOWFREQ; |
434 | v->rangehigh = GEMTEK_HIGHFREQ; | 417 | v->rangehigh = GEMTEK_HIGHFREQ; |
435 | v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; | 418 | v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; |
436 | v->signal = 0xffff * gemtek_getsigstr(); | 419 | v->signal = 0xffff * gemtek_getsigstr(gt); |
437 | if (v->signal) { | 420 | if (v->signal) { |
438 | v->audmode = V4L2_TUNER_MODE_STEREO; | 421 | v->audmode = V4L2_TUNER_MODE_STEREO; |
439 | v->rxsubchans = V4L2_TUNER_SUB_STEREO; | 422 | v->rxsubchans = V4L2_TUNER_SUB_STEREO; |
@@ -441,65 +424,56 @@ static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) | |||
441 | v->audmode = V4L2_TUNER_MODE_MONO; | 424 | v->audmode = V4L2_TUNER_MODE_MONO; |
442 | v->rxsubchans = V4L2_TUNER_SUB_MONO; | 425 | v->rxsubchans = V4L2_TUNER_SUB_MONO; |
443 | } | 426 | } |
444 | |||
445 | return 0; | 427 | return 0; |
446 | } | 428 | } |
447 | 429 | ||
448 | static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *v) | 430 | static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *v) |
449 | { | 431 | { |
450 | if (v->index > 0) | 432 | return (v->index != 0) ? -EINVAL : 0; |
451 | return -EINVAL; | ||
452 | return 0; | ||
453 | } | 433 | } |
454 | 434 | ||
455 | static int vidioc_s_frequency(struct file *file, void *priv, | 435 | static int vidioc_g_frequency(struct file *file, void *priv, |
456 | struct v4l2_frequency *f) | 436 | struct v4l2_frequency *f) |
457 | { | 437 | { |
458 | struct gemtek_device *rt = video_drvdata(file); | 438 | struct gemtek *gt = video_drvdata(file); |
459 | |||
460 | gemtek_setfreq(rt, f->frequency); | ||
461 | 439 | ||
440 | if (f->tuner != 0) | ||
441 | return -EINVAL; | ||
442 | f->type = V4L2_TUNER_RADIO; | ||
443 | f->frequency = gt->lastfreq; | ||
462 | return 0; | 444 | return 0; |
463 | } | 445 | } |
464 | 446 | ||
465 | static int vidioc_g_frequency(struct file *file, void *priv, | 447 | static int vidioc_s_frequency(struct file *file, void *priv, |
466 | struct v4l2_frequency *f) | 448 | struct v4l2_frequency *f) |
467 | { | 449 | { |
468 | struct gemtek_device *rt = video_drvdata(file); | 450 | struct gemtek *gt = video_drvdata(file); |
469 | 451 | ||
470 | f->type = V4L2_TUNER_RADIO; | 452 | if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) |
471 | f->frequency = rt->lastfreq; | 453 | return -EINVAL; |
454 | gemtek_setfreq(gt, f->frequency); | ||
472 | return 0; | 455 | return 0; |
473 | } | 456 | } |
474 | 457 | ||
475 | static int vidioc_queryctrl(struct file *file, void *priv, | 458 | static int vidioc_queryctrl(struct file *file, void *priv, |
476 | struct v4l2_queryctrl *qc) | 459 | struct v4l2_queryctrl *qc) |
477 | { | 460 | { |
478 | int i; | 461 | switch (qc->id) { |
479 | 462 | case V4L2_CID_AUDIO_MUTE: | |
480 | for (i = 0; i < ARRAY_SIZE(radio_qctrl); ++i) { | 463 | return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); |
481 | if (qc->id && qc->id == radio_qctrl[i].id) { | 464 | default: |
482 | memcpy(qc, &(radio_qctrl[i]), sizeof(*qc)); | 465 | return -EINVAL; |
483 | return 0; | ||
484 | } | ||
485 | } | 466 | } |
486 | return -EINVAL; | ||
487 | } | 467 | } |
488 | 468 | ||
489 | static int vidioc_g_ctrl(struct file *file, void *priv, | 469 | static int vidioc_g_ctrl(struct file *file, void *priv, |
490 | struct v4l2_control *ctrl) | 470 | struct v4l2_control *ctrl) |
491 | { | 471 | { |
492 | struct gemtek_device *rt = video_drvdata(file); | 472 | struct gemtek *gt = video_drvdata(file); |
493 | 473 | ||
494 | switch (ctrl->id) { | 474 | switch (ctrl->id) { |
495 | case V4L2_CID_AUDIO_MUTE: | 475 | case V4L2_CID_AUDIO_MUTE: |
496 | ctrl->value = rt->muted; | 476 | ctrl->value = gt->muted; |
497 | return 0; | ||
498 | case V4L2_CID_AUDIO_VOLUME: | ||
499 | if (rt->muted) | ||
500 | ctrl->value = 0; | ||
501 | else | ||
502 | ctrl->value = 65535; | ||
503 | return 0; | 477 | return 0; |
504 | } | 478 | } |
505 | return -EINVAL; | 479 | return -EINVAL; |
@@ -508,35 +482,19 @@ static int vidioc_g_ctrl(struct file *file, void *priv, | |||
508 | static int vidioc_s_ctrl(struct file *file, void *priv, | 482 | static int vidioc_s_ctrl(struct file *file, void *priv, |
509 | struct v4l2_control *ctrl) | 483 | struct v4l2_control *ctrl) |
510 | { | 484 | { |
511 | struct gemtek_device *rt = video_drvdata(file); | 485 | struct gemtek *gt = video_drvdata(file); |
512 | 486 | ||
513 | switch (ctrl->id) { | 487 | switch (ctrl->id) { |
514 | case V4L2_CID_AUDIO_MUTE: | 488 | case V4L2_CID_AUDIO_MUTE: |
515 | if (ctrl->value) | 489 | if (ctrl->value) |
516 | gemtek_mute(rt); | 490 | gemtek_mute(gt); |
517 | else | ||
518 | gemtek_unmute(rt); | ||
519 | return 0; | ||
520 | case V4L2_CID_AUDIO_VOLUME: | ||
521 | if (ctrl->value) | ||
522 | gemtek_unmute(rt); | ||
523 | else | 491 | else |
524 | gemtek_mute(rt); | 492 | gemtek_unmute(gt); |
525 | return 0; | 493 | return 0; |
526 | } | 494 | } |
527 | return -EINVAL; | 495 | return -EINVAL; |
528 | } | 496 | } |
529 | 497 | ||
530 | static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) | ||
531 | { | ||
532 | if (a->index > 1) | ||
533 | return -EINVAL; | ||
534 | |||
535 | strcpy(a->name, "Radio"); | ||
536 | a->capability = V4L2_AUDCAP_STEREO; | ||
537 | return 0; | ||
538 | } | ||
539 | |||
540 | static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) | 498 | static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) |
541 | { | 499 | { |
542 | *i = 0; | 500 | *i = 0; |
@@ -545,16 +503,20 @@ static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) | |||
545 | 503 | ||
546 | static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) | 504 | static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) |
547 | { | 505 | { |
548 | if (i != 0) | 506 | return (i != 0) ? -EINVAL : 0; |
549 | return -EINVAL; | 507 | } |
508 | |||
509 | static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) | ||
510 | { | ||
511 | a->index = 0; | ||
512 | strlcpy(a->name, "Radio", sizeof(a->name)); | ||
513 | a->capability = V4L2_AUDCAP_STEREO; | ||
550 | return 0; | 514 | return 0; |
551 | } | 515 | } |
552 | 516 | ||
553 | static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) | 517 | static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) |
554 | { | 518 | { |
555 | if (a->index != 0) | 519 | return (a->index != 0) ? -EINVAL : 0; |
556 | return -EINVAL; | ||
557 | return 0; | ||
558 | } | 520 | } |
559 | 521 | ||
560 | static const struct v4l2_ioctl_ops gemtek_ioctl_ops = { | 522 | static const struct v4l2_ioctl_ops gemtek_ioctl_ops = { |
@@ -572,62 +534,73 @@ static const struct v4l2_ioctl_ops gemtek_ioctl_ops = { | |||
572 | .vidioc_s_ctrl = vidioc_s_ctrl | 534 | .vidioc_s_ctrl = vidioc_s_ctrl |
573 | }; | 535 | }; |
574 | 536 | ||
575 | static struct video_device gemtek_radio = { | ||
576 | .name = "GemTek Radio card", | ||
577 | .fops = &gemtek_fops, | ||
578 | .ioctl_ops = &gemtek_ioctl_ops, | ||
579 | .release = video_device_release_empty, | ||
580 | }; | ||
581 | |||
582 | /* | 537 | /* |
583 | * Initialization / cleanup related stuff. | 538 | * Initialization / cleanup related stuff. |
584 | */ | 539 | */ |
585 | 540 | ||
586 | /* | ||
587 | * Initilize card. | ||
588 | */ | ||
589 | static int __init gemtek_init(void) | 541 | static int __init gemtek_init(void) |
590 | { | 542 | { |
591 | printk(KERN_INFO RADIO_BANNER "\n"); | 543 | struct gemtek *gt = &gemtek_card; |
544 | struct v4l2_device *v4l2_dev = >->v4l2_dev; | ||
545 | int res; | ||
592 | 546 | ||
593 | spin_lock_init(&lock); | 547 | strlcpy(v4l2_dev->name, "gemtek", sizeof(v4l2_dev->name)); |
594 | 548 | ||
595 | gemtek_probe(); | 549 | v4l2_info(v4l2_dev, "GemTek Radio card driver: v0.0.3\n"); |
596 | if (io) { | 550 | |
597 | if (!request_region(io, 1, "gemtek")) { | 551 | mutex_init(>->lock); |
598 | printk(KERN_ERR "I/O port 0x%x already in use.\n", io); | 552 | |
553 | gt->verified = -1; | ||
554 | gt->io = io; | ||
555 | gemtek_probe(gt); | ||
556 | if (gt->io) { | ||
557 | if (!request_region(gt->io, 1, "gemtek")) { | ||
558 | v4l2_err(v4l2_dev, "I/O port 0x%x already in use.\n", gt->io); | ||
599 | return -EBUSY; | 559 | return -EBUSY; |
600 | } | 560 | } |
601 | 561 | ||
602 | if (!gemtek_verify(io)) | 562 | if (!gemtek_verify(gt, gt->io)) |
603 | printk(KERN_WARNING "Card at I/O port 0x%x does not " | 563 | v4l2_warn(v4l2_dev, "Card at I/O port 0x%x does not " |
604 | "respond properly, check your " | 564 | "respond properly, check your " |
605 | "configuration.\n", io); | 565 | "configuration.\n", gt->io); |
606 | else | 566 | else |
607 | printk(KERN_INFO "Using I/O port 0x%x.\n", io); | 567 | v4l2_info(v4l2_dev, "Using I/O port 0x%x.\n", gt->io); |
608 | } else if (probe) { | 568 | } else if (probe) { |
609 | printk(KERN_ERR "Automatic probing failed and no " | 569 | v4l2_err(v4l2_dev, "Automatic probing failed and no " |
610 | "fixed I/O port defined.\n"); | 570 | "fixed I/O port defined.\n"); |
611 | return -ENODEV; | 571 | return -ENODEV; |
612 | } else { | 572 | } else { |
613 | printk(KERN_ERR "Automatic probing disabled but no fixed " | 573 | v4l2_err(v4l2_dev, "Automatic probing disabled but no fixed " |
614 | "I/O port defined."); | 574 | "I/O port defined."); |
615 | return -EINVAL; | 575 | return -EINVAL; |
616 | } | 576 | } |
617 | 577 | ||
618 | video_set_drvdata(&gemtek_radio, &gemtek_unit); | 578 | res = v4l2_device_register(NULL, v4l2_dev); |
579 | if (res < 0) { | ||
580 | v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); | ||
581 | release_region(gt->io, 1); | ||
582 | return res; | ||
583 | } | ||
619 | 584 | ||
620 | if (video_register_device(&gemtek_radio, VFL_TYPE_RADIO, radio_nr) < 0) { | 585 | strlcpy(gt->vdev.name, v4l2_dev->name, sizeof(gt->vdev.name)); |
621 | release_region(io, 1); | 586 | gt->vdev.v4l2_dev = v4l2_dev; |
587 | gt->vdev.fops = &gemtek_fops; | ||
588 | gt->vdev.ioctl_ops = &gemtek_ioctl_ops; | ||
589 | gt->vdev.release = video_device_release_empty; | ||
590 | video_set_drvdata(>->vdev, gt); | ||
591 | |||
592 | if (video_register_device(>->vdev, VFL_TYPE_RADIO, radio_nr) < 0) { | ||
593 | v4l2_device_unregister(v4l2_dev); | ||
594 | release_region(gt->io, 1); | ||
622 | return -EBUSY; | 595 | return -EBUSY; |
623 | } | 596 | } |
624 | 597 | ||
625 | /* Set defaults */ | 598 | /* Set defaults */ |
626 | gemtek_unit.lastfreq = GEMTEK_LOWFREQ; | 599 | gt->lastfreq = GEMTEK_LOWFREQ; |
627 | gemtek_unit.bu2614data = 0; | 600 | gt->bu2614data = 0; |
628 | 601 | ||
629 | if (initmute) | 602 | if (initmute) |
630 | gemtek_mute(&gemtek_unit); | 603 | gemtek_mute(gt); |
631 | 604 | ||
632 | return 0; | 605 | return 0; |
633 | } | 606 | } |
@@ -637,15 +610,19 @@ static int __init gemtek_init(void) | |||
637 | */ | 610 | */ |
638 | static void __exit gemtek_exit(void) | 611 | static void __exit gemtek_exit(void) |
639 | { | 612 | { |
613 | struct gemtek *gt = &gemtek_card; | ||
614 | struct v4l2_device *v4l2_dev = >->v4l2_dev; | ||
615 | |||
640 | if (shutdown) { | 616 | if (shutdown) { |
641 | hardmute = 1; /* Turn off PLL */ | 617 | hardmute = 1; /* Turn off PLL */ |
642 | gemtek_mute(&gemtek_unit); | 618 | gemtek_mute(gt); |
643 | } else { | 619 | } else { |
644 | printk(KERN_INFO "Module unloaded but card not muted!\n"); | 620 | v4l2_info(v4l2_dev, "Module unloaded but card not muted!\n"); |
645 | } | 621 | } |
646 | 622 | ||
647 | video_unregister_device(&gemtek_radio); | 623 | video_unregister_device(>->vdev); |
648 | release_region(io, 1); | 624 | v4l2_device_unregister(>->v4l2_dev); |
625 | release_region(gt->io, 1); | ||
649 | } | 626 | } |
650 | 627 | ||
651 | module_init(gemtek_init); | 628 | module_init(gemtek_init); |