aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/radio
diff options
context:
space:
mode:
authorHans Verkuil <hans.verkuil@cisco.com>2012-01-16 02:58:15 -0500
committerMauro Carvalho Chehab <mchehab@redhat.com>2012-02-14 14:09:27 -0500
commit3088fba877ee8bf284b12a73332b813b5478a64d (patch)
tree33d16a91c93876545ba25c3c5c3b4d02d7a568ed /drivers/media/radio
parentcc3c6df16b3fb953818780b52c86fc7a9f08b337 (diff)
[media] radio-aztech: Convert to radio-isa
Tested with actual hardware and the Keene USB FM Transmitter. Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/radio')
-rw-r--r--drivers/media/radio/Kconfig6
-rw-r--r--drivers/media/radio/radio-aztech.c371
2 files changed, 80 insertions, 297 deletions
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index 618c33295ad1..645fe5716170 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -256,15 +256,11 @@ config RADIO_RTRACK2_PORT
256config RADIO_AZTECH 256config RADIO_AZTECH
257 tristate "Aztech/Packard Bell Radio" 257 tristate "Aztech/Packard Bell Radio"
258 depends on ISA && VIDEO_V4L2 258 depends on ISA && VIDEO_V4L2
259 select RADIO_ISA
259 ---help--- 260 ---help---
260 Choose Y here if you have one of these FM radio cards, and then fill 261 Choose Y here if you have one of these FM radio cards, and then fill
261 in the port address below. 262 in the port address below.
262 263
263 In order to control your radio card, you will need to use programs
264 that are compatible with the Video For Linux API. Information on
265 this API and pointers to "v4l" programs may be found at
266 <file:Documentation/video4linux/API.html>.
267
268 To compile this driver as a module, choose M here: the 264 To compile this driver as a module, choose M here: the
269 module will be called radio-aztech. 265 module will be called radio-aztech.
270 266
diff --git a/drivers/media/radio/radio-aztech.c b/drivers/media/radio/radio-aztech.c
index eed7b0840734..8117fdf9ed4d 100644
--- a/drivers/media/radio/radio-aztech.c
+++ b/drivers/media/radio/radio-aztech.c
@@ -1,5 +1,7 @@
1/* radio-aztech.c - Aztech radio card driver for Linux 2.2 1/*
2 * radio-aztech.c - Aztech radio card driver
2 * 3 *
4 * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@xs4all.nl>
3 * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> 5 * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
4 * Adapted to support the Video for Linux API by 6 * Adapted to support the Video for Linux API by
5 * Russell Kroll <rkroll@exploits.org>. Based on original tuner code by: 7 * Russell Kroll <rkroll@exploits.org>. Based on original tuner code by:
@@ -10,19 +12,7 @@
10 * Scott McGrath (smcgrath@twilight.vtc.vsc.edu) 12 * Scott McGrath (smcgrath@twilight.vtc.vsc.edu)
11 * William McGrath (wmcgrath@twilight.vtc.vsc.edu) 13 * William McGrath (wmcgrath@twilight.vtc.vsc.edu)
12 * 14 *
13 * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/ 15 * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool.
14 * along with more information on the card itself.
15 *
16 * History:
17 * 1999-02-24 Russell Kroll <rkroll@exploits.org>
18 * Fine tuning/VIDEO_TUNER_LOW
19 * Range expanded to 87-108 MHz (from 87.9-107.8)
20 *
21 * Notable changes from the original source:
22 * - includes stripped down to the essentials
23 * - for loops used as delays replaced with udelay()
24 * - #defines removed, changed to static values
25 * - tuning structure changed - no more character arrays, other changes
26*/ 16*/
27 17
28#include <linux/module.h> /* Modules */ 18#include <linux/module.h> /* Modules */
@@ -33,124 +23,69 @@
33#include <linux/io.h> /* outb, outb_p */ 23#include <linux/io.h> /* outb, outb_p */
34#include <media/v4l2-device.h> 24#include <media/v4l2-device.h>
35#include <media/v4l2-ioctl.h> 25#include <media/v4l2-ioctl.h>
26#include <media/v4l2-ctrls.h>
27#include "radio-isa.h"
36 28
37MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath"); 29MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
38MODULE_DESCRIPTION("A driver for the Aztech radio card."); 30MODULE_DESCRIPTION("A driver for the Aztech radio card.");
39MODULE_LICENSE("GPL"); 31MODULE_LICENSE("GPL");
40MODULE_VERSION("0.0.3"); 32MODULE_VERSION("1.0.0");
41 33
42/* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */ 34/* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
43
44#ifndef CONFIG_RADIO_AZTECH_PORT 35#ifndef CONFIG_RADIO_AZTECH_PORT
45#define CONFIG_RADIO_AZTECH_PORT -1 36#define CONFIG_RADIO_AZTECH_PORT -1
46#endif 37#endif
47 38
48static int io = CONFIG_RADIO_AZTECH_PORT; 39#define AZTECH_MAX 2
49static int radio_nr = -1;
50static int radio_wait_time = 1000;
51 40
52module_param(io, int, 0); 41static int io[AZTECH_MAX] = { [0] = CONFIG_RADIO_AZTECH_PORT,
53module_param(radio_nr, int, 0); 42 [1 ... (AZTECH_MAX - 1)] = -1 };
54MODULE_PARM_DESC(io, "I/O address of the Aztech card (0x350 or 0x358)"); 43static int radio_nr[AZTECH_MAX] = { [0 ... (AZTECH_MAX - 1)] = -1 };
44static const int radio_wait_time = 1000;
55 45
56struct aztech 46module_param_array(io, int, NULL, 0444);
57{ 47MODULE_PARM_DESC(io, "I/O addresses of the Aztech card (0x350 or 0x358)");
58 struct v4l2_device v4l2_dev; 48module_param_array(radio_nr, int, NULL, 0444);
59 struct video_device vdev; 49MODULE_PARM_DESC(radio_nr, "Radio device numbers");
60 int io; 50
51struct aztech {
52 struct radio_isa_card isa;
61 int curvol; 53 int curvol;
62 unsigned long curfreq;
63 int stereo;
64 struct mutex lock;
65}; 54};
66 55
67static struct aztech aztech_card;
68
69static int volconvert(int level)
70{
71 level >>= 14; /* Map 16bits down to 2 bit */
72 level &= 3;
73
74 /* convert to card-friendly values */
75 switch (level) {
76 case 0:
77 return 0;
78 case 1:
79 return 1;
80 case 2:
81 return 4;
82 case 3:
83 return 5;
84 }
85 return 0; /* Quieten gcc */
86}
87
88static void send_0_byte(struct aztech *az) 56static void send_0_byte(struct aztech *az)
89{ 57{
90 udelay(radio_wait_time); 58 udelay(radio_wait_time);
91 outb_p(2 + volconvert(az->curvol), az->io); 59 outb_p(2 + az->curvol, az->isa.io);
92 outb_p(64 + 2 + volconvert(az->curvol), az->io); 60 outb_p(64 + 2 + az->curvol, az->isa.io);
93} 61}
94 62
95static void send_1_byte(struct aztech *az) 63static void send_1_byte(struct aztech *az)
96{ 64{
97 udelay (radio_wait_time); 65 udelay(radio_wait_time);
98 outb_p(128 + 2 + volconvert(az->curvol), az->io); 66 outb_p(128 + 2 + az->curvol, az->isa.io);
99 outb_p(128 + 64 + 2 + volconvert(az->curvol), az->io); 67 outb_p(128 + 64 + 2 + az->curvol, az->isa.io);
100}
101
102static int az_setvol(struct aztech *az, int vol)
103{
104 mutex_lock(&az->lock);
105 outb(volconvert(vol), az->io);
106 mutex_unlock(&az->lock);
107 return 0;
108}
109
110/* thanks to Michael Dwyer for giving me a dose of clues in
111 * the signal strength department..
112 *
113 * This card has a stereo bit - bit 0 set = mono, not set = stereo
114 * It also has a "signal" bit - bit 1 set = bad signal, not set = good
115 *
116 */
117
118static int az_getsigstr(struct aztech *az)
119{
120 int sig = 1;
121
122 mutex_lock(&az->lock);
123 if (inb(az->io) & 2) /* bit set = no signal present */
124 sig = 0;
125 mutex_unlock(&az->lock);
126 return sig;
127} 68}
128 69
129static int az_getstereo(struct aztech *az) 70static struct radio_isa_card *aztech_alloc(void)
130{ 71{
131 int stereo = 1; 72 struct aztech *az = kzalloc(sizeof(*az), GFP_KERNEL);
132 73
133 mutex_lock(&az->lock); 74 return az ? &az->isa : NULL;
134 if (inb(az->io) & 1) /* bit set = mono */
135 stereo = 0;
136 mutex_unlock(&az->lock);
137 return stereo;
138} 75}
139 76
140static int az_setfreq(struct aztech *az, unsigned long frequency) 77static int aztech_s_frequency(struct radio_isa_card *isa, u32 freq)
141{ 78{
79 struct aztech *az = container_of(isa, struct aztech, isa);
142 int i; 80 int i;
143 81
144 mutex_lock(&az->lock); 82 freq += 171200; /* Add 10.7 MHz IF */
145 83 freq /= 800; /* Convert to 50 kHz units */
146 az->curfreq = frequency;
147 frequency += 171200; /* Add 10.7 MHz IF */
148 frequency /= 800; /* Convert to 50 kHz units */
149 84
150 send_0_byte(az); /* 0: LSB of frequency */ 85 send_0_byte(az); /* 0: LSB of frequency */
151 86
152 for (i = 0; i < 13; i++) /* : frequency bits (1-13) */ 87 for (i = 0; i < 13; i++) /* : frequency bits (1-13) */
153 if (frequency & (1 << i)) 88 if (freq & (1 << i))
154 send_1_byte(az); 89 send_1_byte(az);
155 else 90 else
156 send_0_byte(az); 91 send_0_byte(az);
@@ -158,7 +93,7 @@ static int az_setfreq(struct aztech *az, unsigned long frequency)
158 send_0_byte(az); /* 14: test bit - always 0 */ 93 send_0_byte(az); /* 14: test bit - always 0 */
159 send_0_byte(az); /* 15: test bit - always 0 */ 94 send_0_byte(az); /* 15: test bit - always 0 */
160 send_0_byte(az); /* 16: band data 0 - always 0 */ 95 send_0_byte(az); /* 16: band data 0 - always 0 */
161 if (az->stereo) /* 17: stereo (1 to enable) */ 96 if (isa->stereo) /* 17: stereo (1 to enable) */
162 send_1_byte(az); 97 send_1_byte(az);
163 else 98 else
164 send_0_byte(az); 99 send_0_byte(az);
@@ -173,225 +108,77 @@ static int az_setfreq(struct aztech *az, unsigned long frequency)
173 /* latch frequency */ 108 /* latch frequency */
174 109
175 udelay(radio_wait_time); 110 udelay(radio_wait_time);
176 outb_p(128 + 64 + volconvert(az->curvol), az->io); 111 outb_p(128 + 64 + az->curvol, az->isa.io);
177
178 mutex_unlock(&az->lock);
179
180 return 0;
181}
182
183static int vidioc_querycap(struct file *file, void *priv,
184 struct v4l2_capability *v)
185{
186 strlcpy(v->driver, "radio-aztech", sizeof(v->driver));
187 strlcpy(v->card, "Aztech Radio", sizeof(v->card));
188 strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
189 v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
190 return 0;
191}
192
193static int vidioc_g_tuner(struct file *file, void *priv,
194 struct v4l2_tuner *v)
195{
196 struct aztech *az = video_drvdata(file);
197
198 if (v->index > 0)
199 return -EINVAL;
200
201 strlcpy(v->name, "FM", sizeof(v->name));
202 v->type = V4L2_TUNER_RADIO;
203
204 v->rangelow = 87 * 16000;
205 v->rangehigh = 108 * 16000;
206 v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
207 v->capability = V4L2_TUNER_CAP_LOW;
208 if (az_getstereo(az))
209 v->audmode = V4L2_TUNER_MODE_STEREO;
210 else
211 v->audmode = V4L2_TUNER_MODE_MONO;
212 v->signal = 0xFFFF * az_getsigstr(az);
213
214 return 0;
215}
216
217static int vidioc_s_tuner(struct file *file, void *priv,
218 struct v4l2_tuner *v)
219{
220 return v->index ? -EINVAL : 0;
221}
222 112
223static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
224{
225 *i = 0;
226 return 0; 113 return 0;
227} 114}
228 115
229static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) 116/* thanks to Michael Dwyer for giving me a dose of clues in
230{ 117 * the signal strength department..
231 return i ? -EINVAL : 0; 118 *
232} 119 * This card has a stereo bit - bit 0 set = mono, not set = stereo
233 120 */
234static int vidioc_g_audio(struct file *file, void *priv, 121static u32 aztech_g_rxsubchans(struct radio_isa_card *isa)
235 struct v4l2_audio *a)
236{
237 a->index = 0;
238 strlcpy(a->name, "Radio", sizeof(a->name));
239 a->capability = V4L2_AUDCAP_STEREO;
240 return 0;
241}
242
243static int vidioc_s_audio(struct file *file, void *priv,
244 struct v4l2_audio *a)
245{ 122{
246 return a->index ? -EINVAL : 0; 123 if (inb(isa->io) & 1)
124 return V4L2_TUNER_SUB_MONO;
125 return V4L2_TUNER_SUB_STEREO;
247} 126}
248 127
249static int vidioc_s_frequency(struct file *file, void *priv, 128static int aztech_s_stereo(struct radio_isa_card *isa, bool stereo)
250 struct v4l2_frequency *f)
251{ 129{
252 struct aztech *az = video_drvdata(file); 130 return aztech_s_frequency(isa, isa->freq);
253
254 if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
255 return -EINVAL;
256 az_setfreq(az, f->frequency);
257 return 0;
258} 131}
259 132
260static int vidioc_g_frequency(struct file *file, void *priv, 133static int aztech_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
261 struct v4l2_frequency *f)
262{ 134{
263 struct aztech *az = video_drvdata(file); 135 struct aztech *az = container_of(isa, struct aztech, isa);
264 136
265 if (f->tuner != 0) 137 if (mute)
266 return -EINVAL; 138 vol = 0;
267 f->type = V4L2_TUNER_RADIO; 139 az->curvol = (vol & 1) + ((vol & 2) << 1);
268 f->frequency = az->curfreq; 140 outb(az->curvol, isa->io);
269 return 0; 141 return 0;
270} 142}
271 143
272static int vidioc_queryctrl(struct file *file, void *priv, 144static const struct radio_isa_ops aztech_ops = {
273 struct v4l2_queryctrl *qc) 145 .alloc = aztech_alloc,
274{ 146 .s_mute_volume = aztech_s_mute_volume,
275 switch (qc->id) { 147 .s_frequency = aztech_s_frequency,
276 case V4L2_CID_AUDIO_MUTE: 148 .s_stereo = aztech_s_stereo,
277 return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); 149 .g_rxsubchans = aztech_g_rxsubchans,
278 case V4L2_CID_AUDIO_VOLUME:
279 return v4l2_ctrl_query_fill(qc, 0, 0xff, 1, 0xff);
280 }
281 return -EINVAL;
282}
283
284static int vidioc_g_ctrl(struct file *file, void *priv,
285 struct v4l2_control *ctrl)
286{
287 struct aztech *az = video_drvdata(file);
288
289 switch (ctrl->id) {
290 case V4L2_CID_AUDIO_MUTE:
291 if (az->curvol == 0)
292 ctrl->value = 1;
293 else
294 ctrl->value = 0;
295 return 0;
296 case V4L2_CID_AUDIO_VOLUME:
297 ctrl->value = az->curvol * 6554;
298 return 0;
299 }
300 return -EINVAL;
301}
302
303static int vidioc_s_ctrl(struct file *file, void *priv,
304 struct v4l2_control *ctrl)
305{
306 struct aztech *az = video_drvdata(file);
307
308 switch (ctrl->id) {
309 case V4L2_CID_AUDIO_MUTE:
310 if (ctrl->value)
311 az_setvol(az, 0);
312 else
313 az_setvol(az, az->curvol);
314 return 0;
315 case V4L2_CID_AUDIO_VOLUME:
316 az_setvol(az, ctrl->value);
317 return 0;
318 }
319 return -EINVAL;
320}
321
322static const struct v4l2_file_operations aztech_fops = {
323 .owner = THIS_MODULE,
324 .unlocked_ioctl = video_ioctl2,
325}; 150};
326 151
327static const struct v4l2_ioctl_ops aztech_ioctl_ops = { 152static const int aztech_ioports[] = { 0x350, 0x358 };
328 .vidioc_querycap = vidioc_querycap, 153
329 .vidioc_g_tuner = vidioc_g_tuner, 154static struct radio_isa_driver aztech_driver = {
330 .vidioc_s_tuner = vidioc_s_tuner, 155 .driver = {
331 .vidioc_g_audio = vidioc_g_audio, 156 .match = radio_isa_match,
332 .vidioc_s_audio = vidioc_s_audio, 157 .probe = radio_isa_probe,
333 .vidioc_g_input = vidioc_g_input, 158 .remove = radio_isa_remove,
334 .vidioc_s_input = vidioc_s_input, 159 .driver = {
335 .vidioc_g_frequency = vidioc_g_frequency, 160 .name = "radio-aztech",
336 .vidioc_s_frequency = vidioc_s_frequency, 161 },
337 .vidioc_queryctrl = vidioc_queryctrl, 162 },
338 .vidioc_g_ctrl = vidioc_g_ctrl, 163 .io_params = io,
339 .vidioc_s_ctrl = vidioc_s_ctrl, 164 .radio_nr_params = radio_nr,
165 .io_ports = aztech_ioports,
166 .num_of_io_ports = ARRAY_SIZE(aztech_ioports),
167 .region_size = 2,
168 .card = "Aztech Radio",
169 .ops = &aztech_ops,
170 .has_stereo = true,
171 .max_volume = 3,
340}; 172};
341 173
342static int __init aztech_init(void) 174static int __init aztech_init(void)
343{ 175{
344 struct aztech *az = &aztech_card; 176 return isa_register_driver(&aztech_driver.driver, AZTECH_MAX);
345 struct v4l2_device *v4l2_dev = &az->v4l2_dev;
346 int res;
347
348 strlcpy(v4l2_dev->name, "aztech", sizeof(v4l2_dev->name));
349 az->io = io;
350
351 if (az->io == -1) {
352 v4l2_err(v4l2_dev, "you must set an I/O address with io=0x350 or 0x358\n");
353 return -EINVAL;
354 }
355
356 if (!request_region(az->io, 2, "aztech")) {
357 v4l2_err(v4l2_dev, "port 0x%x already in use\n", az->io);
358 return -EBUSY;
359 }
360
361 res = v4l2_device_register(NULL, v4l2_dev);
362 if (res < 0) {
363 release_region(az->io, 2);
364 v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
365 return res;
366 }
367
368 mutex_init(&az->lock);
369 strlcpy(az->vdev.name, v4l2_dev->name, sizeof(az->vdev.name));
370 az->vdev.v4l2_dev = v4l2_dev;
371 az->vdev.fops = &aztech_fops;
372 az->vdev.ioctl_ops = &aztech_ioctl_ops;
373 az->vdev.release = video_device_release_empty;
374 video_set_drvdata(&az->vdev, az);
375 /* mute card - prevents noisy bootups */
376 outb(0, az->io);
377
378 if (video_register_device(&az->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
379 v4l2_device_unregister(v4l2_dev);
380 release_region(az->io, 2);
381 return -EINVAL;
382 }
383
384 v4l2_info(v4l2_dev, "Aztech radio card driver v1.00/19990224 rkroll@exploits.org\n");
385 return 0;
386} 177}
387 178
388static void __exit aztech_exit(void) 179static void __exit aztech_exit(void)
389{ 180{
390 struct aztech *az = &aztech_card; 181 isa_unregister_driver(&aztech_driver.driver);
391
392 video_unregister_device(&az->vdev);
393 v4l2_device_unregister(&az->v4l2_dev);
394 release_region(az->io, 2);
395} 182}
396 183
397module_init(aztech_init); 184module_init(aztech_init);