aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorHans Verkuil <hans.verkuil@cisco.com>2012-01-16 03:17:32 -0500
committerMauro Carvalho Chehab <mchehab@redhat.com>2012-02-14 14:11:35 -0500
commitda1ff351320aa1b7384f7708d652f27397929978 (patch)
treef696d0c04721eec044a15e1bde48a2ac6b89f460 /drivers
parent1d211f26b5542da66cf48ff8ef783aceeb7e8099 (diff)
[media] radio-typhoon: Convert to radio-isa
Tested with v4l2-compliance, but not with actual hardware. Contact the linux-media mailinglist if you have this card! 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/Kconfig8
-rw-r--r--drivers/media/radio/radio-typhoon.c365
2 files changed, 73 insertions, 300 deletions
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index a81d15ccf067..2a5b17d837c8 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -400,14 +400,14 @@ config RADIO_TRUST_PORT
400config RADIO_TYPHOON 400config RADIO_TYPHOON
401 tristate "Typhoon Radio (a.k.a. EcoRadio)" 401 tristate "Typhoon Radio (a.k.a. EcoRadio)"
402 depends on ISA && VIDEO_V4L2 402 depends on ISA && VIDEO_V4L2
403 select RADIO_ISA
403 ---help--- 404 ---help---
404 Choose Y here if you have one of these FM radio cards, and then fill 405 Choose Y here if you have one of these FM radio cards, and then fill
405 in the port address and the frequency used for muting below. 406 in the port address and the frequency used for muting below.
406 407
407 In order to control your radio card, you will need to use programs 408 Note: this driver hasn't been tested since a long time due to lack
408 that are compatible with the Video For Linux API. Information on 409 of hardware. If you have this hardware, then please contact the
409 this API and pointers to "v4l" programs may be found at 410 linux-media mailinglist.
410 <file:Documentation/video4linux/API.html>.
411 411
412 To compile this driver as a module, choose M here: the 412 To compile this driver as a module, choose M here: the
413 module will be called radio-typhoon. 413 module will be called radio-typhoon.
diff --git a/drivers/media/radio/radio-typhoon.c b/drivers/media/radio/radio-typhoon.c
index 398726abc0c8..145d10c08556 100644
--- a/drivers/media/radio/radio-typhoon.c
+++ b/drivers/media/radio/radio-typhoon.c
@@ -35,61 +35,50 @@
35#include <linux/io.h> /* outb, outb_p */ 35#include <linux/io.h> /* outb, outb_p */
36#include <media/v4l2-device.h> 36#include <media/v4l2-device.h>
37#include <media/v4l2-ioctl.h> 37#include <media/v4l2-ioctl.h>
38#include "radio-isa.h"
38 39
39#define DRIVER_VERSION "0.1.2" 40#define DRIVER_VERSION "0.1.2"
40 41
41MODULE_AUTHOR("Dr. Henrik Seidel"); 42MODULE_AUTHOR("Dr. Henrik Seidel");
42MODULE_DESCRIPTION("A driver for the Typhoon radio card (a.k.a. EcoRadio)."); 43MODULE_DESCRIPTION("A driver for the Typhoon radio card (a.k.a. EcoRadio).");
43MODULE_LICENSE("GPL"); 44MODULE_LICENSE("GPL");
44MODULE_VERSION(DRIVER_VERSION); 45MODULE_VERSION("0.1.99");
45 46
46#ifndef CONFIG_RADIO_TYPHOON_PORT 47#ifndef CONFIG_RADIO_TYPHOON_PORT
47#define CONFIG_RADIO_TYPHOON_PORT -1 48#define CONFIG_RADIO_TYPHOON_PORT -1
48#endif 49#endif
49 50
50#ifndef CONFIG_RADIO_TYPHOON_MUTEFREQ 51#ifndef CONFIG_RADIO_TYPHOON_MUTEFREQ
51#define CONFIG_RADIO_TYPHOON_MUTEFREQ 0 52#define CONFIG_RADIO_TYPHOON_MUTEFREQ 87000
52#endif 53#endif
53 54
54static int io = CONFIG_RADIO_TYPHOON_PORT; 55#define TYPHOON_MAX 2
55static int radio_nr = -1;
56
57module_param(io, int, 0);
58MODULE_PARM_DESC(io, "I/O address of the Typhoon card (0x316 or 0x336)");
59
60module_param(radio_nr, int, 0);
61 56
57static int io[TYPHOON_MAX] = { [0] = CONFIG_RADIO_TYPHOON_PORT,
58 [1 ... (TYPHOON_MAX - 1)] = -1 };
59static int radio_nr[TYPHOON_MAX] = { [0 ... (TYPHOON_MAX - 1)] = -1 };
62static unsigned long mutefreq = CONFIG_RADIO_TYPHOON_MUTEFREQ; 60static unsigned long mutefreq = CONFIG_RADIO_TYPHOON_MUTEFREQ;
61
62module_param_array(io, int, NULL, 0444);
63MODULE_PARM_DESC(io, "I/O addresses of the Typhoon card (0x316 or 0x336)");
64module_param_array(radio_nr, int, NULL, 0444);
65MODULE_PARM_DESC(radio_nr, "Radio device numbers");
63module_param(mutefreq, ulong, 0); 66module_param(mutefreq, ulong, 0);
64MODULE_PARM_DESC(mutefreq, "Frequency used when muting the card (in kHz)"); 67MODULE_PARM_DESC(mutefreq, "Frequency used when muting the card (in kHz)");
65 68
66#define BANNER "Typhoon Radio Card driver v" DRIVER_VERSION "\n"
67
68struct typhoon { 69struct typhoon {
69 struct v4l2_device v4l2_dev; 70 struct radio_isa_card isa;
70 struct video_device vdev;
71 int io;
72 int curvol;
73 int muted; 71 int muted;
74 unsigned long curfreq;
75 unsigned long mutefreq;
76 struct mutex lock;
77}; 72};
78 73
79static struct typhoon typhoon_card; 74static struct radio_isa_card *typhoon_alloc(void)
80
81static void typhoon_setvol_generic(struct typhoon *dev, int vol)
82{ 75{
83 mutex_lock(&dev->lock); 76 struct typhoon *ty = kzalloc(sizeof(*ty), GFP_KERNEL);
84 vol >>= 14; /* Map 16 bit to 2 bit */ 77
85 vol &= 3; 78 return ty ? &ty->isa : NULL;
86 outb_p(vol / 2, dev->io); /* Set the volume, high bit. */
87 outb_p(vol % 2, dev->io + 2); /* Set the volume, low bit. */
88 mutex_unlock(&dev->lock);
89} 79}
90 80
91static int typhoon_setfreq_generic(struct typhoon *dev, 81static int typhoon_s_frequency(struct radio_isa_card *isa, u32 freq)
92 unsigned long frequency)
93{ 82{
94 unsigned long outval; 83 unsigned long outval;
95 unsigned long x; 84 unsigned long x;
@@ -105,302 +94,86 @@ static int typhoon_setfreq_generic(struct typhoon *dev,
105 * 94 *
106 */ 95 */
107 96
108 mutex_lock(&dev->lock); 97 x = freq / 160;
109 x = frequency / 160;
110 outval = (x * x + 2500) / 5000; 98 outval = (x * x + 2500) / 5000;
111 outval = (outval * x + 5000) / 10000; 99 outval = (outval * x + 5000) / 10000;
112 outval -= (10 * x * x + 10433) / 20866; 100 outval -= (10 * x * x + 10433) / 20866;
113 outval += 4 * x - 11505; 101 outval += 4 * x - 11505;
114 102
115 outb_p((outval >> 8) & 0x01, dev->io + 4); 103 outb_p((outval >> 8) & 0x01, isa->io + 4);
116 outb_p(outval >> 9, dev->io + 6); 104 outb_p(outval >> 9, isa->io + 6);
117 outb_p(outval & 0xff, dev->io + 8); 105 outb_p(outval & 0xff, isa->io + 8);
118 mutex_unlock(&dev->lock);
119
120 return 0;
121}
122
123static int typhoon_setfreq(struct typhoon *dev, unsigned long frequency)
124{
125 typhoon_setfreq_generic(dev, frequency);
126 dev->curfreq = frequency;
127 return 0;
128}
129
130static void typhoon_mute(struct typhoon *dev)
131{
132 if (dev->muted == 1)
133 return;
134 typhoon_setvol_generic(dev, 0);
135 typhoon_setfreq_generic(dev, dev->mutefreq);
136 dev->muted = 1;
137}
138
139static void typhoon_unmute(struct typhoon *dev)
140{
141 if (dev->muted == 0)
142 return;
143 typhoon_setfreq_generic(dev, dev->curfreq);
144 typhoon_setvol_generic(dev, dev->curvol);
145 dev->muted = 0;
146}
147
148static int typhoon_setvol(struct typhoon *dev, int vol)
149{
150 if (dev->muted && vol != 0) { /* user is unmuting the card */
151 dev->curvol = vol;
152 typhoon_unmute(dev);
153 return 0;
154 }
155 if (vol == dev->curvol) /* requested volume == current */
156 return 0;
157
158 if (vol == 0) { /* volume == 0 means mute the card */
159 typhoon_mute(dev);
160 dev->curvol = vol;
161 return 0;
162 }
163 typhoon_setvol_generic(dev, vol);
164 dev->curvol = vol;
165 return 0;
166}
167
168static int vidioc_querycap(struct file *file, void *priv,
169 struct v4l2_capability *v)
170{
171 strlcpy(v->driver, "radio-typhoon", sizeof(v->driver));
172 strlcpy(v->card, "Typhoon Radio", sizeof(v->card));
173 strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
174 v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
175 return 0;
176}
177
178static int vidioc_g_tuner(struct file *file, void *priv,
179 struct v4l2_tuner *v)
180{
181 if (v->index > 0)
182 return -EINVAL;
183
184 strlcpy(v->name, "FM", sizeof(v->name));
185 v->type = V4L2_TUNER_RADIO;
186 v->rangelow = 87.5 * 16000;
187 v->rangehigh = 108 * 16000;
188 v->rxsubchans = V4L2_TUNER_SUB_MONO;
189 v->capability = V4L2_TUNER_CAP_LOW;
190 v->audmode = V4L2_TUNER_MODE_MONO;
191 v->signal = 0xFFFF; /* We can't get the signal strength */
192 return 0;
193}
194
195static int vidioc_s_tuner(struct file *file, void *priv,
196 struct v4l2_tuner *v)
197{
198 return v->index ? -EINVAL : 0;
199}
200
201static int vidioc_g_frequency(struct file *file, void *priv,
202 struct v4l2_frequency *f)
203{
204 struct typhoon *dev = video_drvdata(file);
205
206 if (f->tuner != 0)
207 return -EINVAL;
208 f->type = V4L2_TUNER_RADIO;
209 f->frequency = dev->curfreq;
210 return 0;
211}
212
213static int vidioc_s_frequency(struct file *file, void *priv,
214 struct v4l2_frequency *f)
215{
216 struct typhoon *dev = video_drvdata(file);
217
218 if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
219 return -EINVAL;
220 dev->curfreq = f->frequency;
221 typhoon_setfreq(dev, dev->curfreq);
222 return 0; 106 return 0;
223} 107}
224 108
225static int vidioc_queryctrl(struct file *file, void *priv, 109static int typhoon_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
226 struct v4l2_queryctrl *qc)
227{ 110{
228 switch (qc->id) { 111 struct typhoon *ty = container_of(isa, struct typhoon, isa);
229 case V4L2_CID_AUDIO_MUTE:
230 return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
231 case V4L2_CID_AUDIO_VOLUME:
232 return v4l2_ctrl_query_fill(qc, 0, 65535, 16384, 65535);
233 }
234 return -EINVAL;
235}
236 112
237static int vidioc_g_ctrl(struct file *file, void *priv, 113 if (mute)
238 struct v4l2_control *ctrl) 114 vol = 0;
239{ 115 vol >>= 14; /* Map 16 bit to 2 bit */
240 struct typhoon *dev = video_drvdata(file); 116 vol &= 3;
117 outb_p(vol / 2, isa->io); /* Set the volume, high bit. */
118 outb_p(vol % 2, isa->io + 2); /* Set the volume, low bit. */
241 119
242 switch (ctrl->id) { 120 if (vol == 0 && !ty->muted) {
243 case V4L2_CID_AUDIO_MUTE: 121 ty->muted = true;
244 ctrl->value = dev->muted; 122 return typhoon_s_frequency(isa, mutefreq << 4);
245 return 0;
246 case V4L2_CID_AUDIO_VOLUME:
247 ctrl->value = dev->curvol;
248 return 0;
249 } 123 }
250 return -EINVAL; 124 if (vol && ty->muted) {
251} 125 ty->muted = false;
252 126 return typhoon_s_frequency(isa, isa->freq);
253static int vidioc_s_ctrl (struct file *file, void *priv,
254 struct v4l2_control *ctrl)
255{
256 struct typhoon *dev = video_drvdata(file);
257
258 switch (ctrl->id) {
259 case V4L2_CID_AUDIO_MUTE:
260 if (ctrl->value)
261 typhoon_mute(dev);
262 else
263 typhoon_unmute(dev);
264 return 0;
265 case V4L2_CID_AUDIO_VOLUME:
266 typhoon_setvol(dev, ctrl->value);
267 return 0;
268 } 127 }
269 return -EINVAL;
270}
271
272static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
273{
274 *i = 0;
275 return 0; 128 return 0;
276} 129}
277 130
278static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) 131static const struct radio_isa_ops typhoon_ops = {
279{ 132 .alloc = typhoon_alloc,
280 return i ? -EINVAL : 0; 133 .s_mute_volume = typhoon_s_mute_volume,
281} 134 .s_frequency = typhoon_s_frequency,
282
283static int vidioc_g_audio(struct file *file, void *priv,
284 struct v4l2_audio *a)
285{
286 a->index = 0;
287 strlcpy(a->name, "Radio", sizeof(a->name));
288 a->capability = V4L2_AUDCAP_STEREO;
289 return 0;
290}
291
292static int vidioc_s_audio(struct file *file, void *priv,
293 struct v4l2_audio *a)
294{
295 return a->index ? -EINVAL : 0;
296}
297
298static int vidioc_log_status(struct file *file, void *priv)
299{
300 struct typhoon *dev = video_drvdata(file);
301 struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
302
303 v4l2_info(v4l2_dev, BANNER);
304#ifdef MODULE
305 v4l2_info(v4l2_dev, "Load type: Driver loaded as a module\n\n");
306#else
307 v4l2_info(v4l2_dev, "Load type: Driver compiled into kernel\n\n");
308#endif
309 v4l2_info(v4l2_dev, "frequency = %lu kHz\n", dev->curfreq >> 4);
310 v4l2_info(v4l2_dev, "volume = %d\n", dev->curvol);
311 v4l2_info(v4l2_dev, "mute = %s\n", dev->muted ? "on" : "off");
312 v4l2_info(v4l2_dev, "io = 0x%x\n", dev->io);
313 v4l2_info(v4l2_dev, "mute frequency = %lu kHz\n", dev->mutefreq >> 4);
314 return 0;
315}
316
317static const struct v4l2_file_operations typhoon_fops = {
318 .owner = THIS_MODULE,
319 .unlocked_ioctl = video_ioctl2,
320}; 135};
321 136
322static const struct v4l2_ioctl_ops typhoon_ioctl_ops = { 137static const int typhoon_ioports[] = { 0x316, 0x336 };
323 .vidioc_log_status = vidioc_log_status, 138
324 .vidioc_querycap = vidioc_querycap, 139static struct radio_isa_driver typhoon_driver = {
325 .vidioc_g_tuner = vidioc_g_tuner, 140 .driver = {
326 .vidioc_s_tuner = vidioc_s_tuner, 141 .match = radio_isa_match,
327 .vidioc_g_audio = vidioc_g_audio, 142 .probe = radio_isa_probe,
328 .vidioc_s_audio = vidioc_s_audio, 143 .remove = radio_isa_remove,
329 .vidioc_g_input = vidioc_g_input, 144 .driver = {
330 .vidioc_s_input = vidioc_s_input, 145 .name = "radio-typhoon",
331 .vidioc_g_frequency = vidioc_g_frequency, 146 },
332 .vidioc_s_frequency = vidioc_s_frequency, 147 },
333 .vidioc_queryctrl = vidioc_queryctrl, 148 .io_params = io,
334 .vidioc_g_ctrl = vidioc_g_ctrl, 149 .radio_nr_params = radio_nr,
335 .vidioc_s_ctrl = vidioc_s_ctrl, 150 .io_ports = typhoon_ioports,
151 .num_of_io_ports = ARRAY_SIZE(typhoon_ioports),
152 .region_size = 8,
153 .card = "Typhoon Radio",
154 .ops = &typhoon_ops,
155 .has_stereo = true,
156 .max_volume = 3,
336}; 157};
337 158
338static int __init typhoon_init(void) 159static int __init typhoon_init(void)
339{ 160{
340 struct typhoon *dev = &typhoon_card; 161 if (mutefreq < 87000 || mutefreq > 108000) {
341 struct v4l2_device *v4l2_dev = &dev->v4l2_dev; 162 printk(KERN_ERR "%s: You must set a frequency (in kHz) used when muting the card,\n",
342 int res; 163 typhoon_driver.driver.driver.name);
343 164 printk(KERN_ERR "%s: e.g. with \"mutefreq=87500\" (87000 <= mutefreq <= 108000)\n",
344 strlcpy(v4l2_dev->name, "typhoon", sizeof(v4l2_dev->name)); 165 typhoon_driver.driver.driver.name);
345 dev->io = io; 166 return -ENODEV;
346
347 if (dev->io == -1) {
348 v4l2_err(v4l2_dev, "You must set an I/O address with io=0x316 or io=0x336\n");
349 return -EINVAL;
350 }
351
352 if (mutefreq < 87000 || mutefreq > 108500) {
353 v4l2_err(v4l2_dev, "You must set a frequency (in kHz) used when muting the card,\n");
354 v4l2_err(v4l2_dev, "e.g. with \"mutefreq=87500\" (87000 <= mutefreq <= 108500)\n");
355 return -EINVAL;
356 }
357 dev->curfreq = dev->mutefreq = mutefreq << 4;
358
359 mutex_init(&dev->lock);
360 if (!request_region(dev->io, 8, "typhoon")) {
361 v4l2_err(v4l2_dev, "port 0x%x already in use\n",
362 dev->io);
363 return -EBUSY;
364 }
365
366 res = v4l2_device_register(NULL, v4l2_dev);
367 if (res < 0) {
368 release_region(dev->io, 8);
369 v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
370 return res;
371 } 167 }
372 v4l2_info(v4l2_dev, BANNER); 168 return isa_register_driver(&typhoon_driver.driver, TYPHOON_MAX);
373
374 strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
375 dev->vdev.v4l2_dev = v4l2_dev;
376 dev->vdev.fops = &typhoon_fops;
377 dev->vdev.ioctl_ops = &typhoon_ioctl_ops;
378 dev->vdev.release = video_device_release_empty;
379 video_set_drvdata(&dev->vdev, dev);
380
381 /* mute card - prevents noisy bootups */
382 typhoon_mute(dev);
383
384 if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
385 v4l2_device_unregister(&dev->v4l2_dev);
386 release_region(dev->io, 8);
387 return -EINVAL;
388 }
389 v4l2_info(v4l2_dev, "port 0x%x.\n", dev->io);
390 v4l2_info(v4l2_dev, "mute frequency is %lu kHz.\n", mutefreq);
391
392 return 0;
393} 169}
394 170
395static void __exit typhoon_exit(void) 171static void __exit typhoon_exit(void)
396{ 172{
397 struct typhoon *dev = &typhoon_card; 173 isa_unregister_driver(&typhoon_driver.driver);
398
399 video_unregister_device(&dev->vdev);
400 v4l2_device_unregister(&dev->v4l2_dev);
401 release_region(dev->io, 8);
402} 174}
403 175
176
404module_init(typhoon_init); 177module_init(typhoon_init);
405module_exit(typhoon_exit); 178module_exit(typhoon_exit);
406 179