diff options
author | Hans Verkuil <hans.verkuil@cisco.com> | 2012-01-16 03:15:09 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-02-14 14:11:11 -0500 |
commit | 32c518364a03d336113a9133410af6d8c8dc0107 (patch) | |
tree | fcd33f31f36efc6199cf91eb269a11bb010f42c1 /drivers/media/radio/radio-terratec.c | |
parent | 8bd7ef5ae615cce5cde2e8b40445cfe4a20c3c03 (diff) |
[media] radio-terratec: 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/media/radio/radio-terratec.c')
-rw-r--r-- | drivers/media/radio/radio-terratec.c | 364 |
1 files changed, 59 insertions, 305 deletions
diff --git a/drivers/media/radio/radio-terratec.c b/drivers/media/radio/radio-terratec.c index f2ed9cc3cf3b..2b82dd76a83b 100644 --- a/drivers/media/radio/radio-terratec.c +++ b/drivers/media/radio/radio-terratec.c | |||
@@ -16,11 +16,7 @@ | |||
16 | * Frequency control is done digitally -- ie out(port,encodefreq(95.8)); | 16 | * Frequency control is done digitally -- ie out(port,encodefreq(95.8)); |
17 | * Volume Control is done digitally | 17 | * Volume Control is done digitally |
18 | * | 18 | * |
19 | * there is a I2C controlled RDS decoder (SAA6588) onboard, which i would like to support someday | 19 | * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@cisco.com> |
20 | * (as soon i have understand how to get started :) | ||
21 | * If you can help me out with that, please contact me!! | ||
22 | * | ||
23 | * | ||
24 | * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> | 20 | * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> |
25 | */ | 21 | */ |
26 | 22 | ||
@@ -32,41 +28,21 @@ | |||
32 | #include <linux/io.h> /* outb, outb_p */ | 28 | #include <linux/io.h> /* outb, outb_p */ |
33 | #include <media/v4l2-device.h> | 29 | #include <media/v4l2-device.h> |
34 | #include <media/v4l2-ioctl.h> | 30 | #include <media/v4l2-ioctl.h> |
31 | #include "radio-isa.h" | ||
35 | 32 | ||
36 | MODULE_AUTHOR("R.OFFERMANNS & others"); | 33 | MODULE_AUTHOR("R. Offermans & others"); |
37 | MODULE_DESCRIPTION("A driver for the TerraTec ActiveRadio Standalone radio card."); | 34 | MODULE_DESCRIPTION("A driver for the TerraTec ActiveRadio Standalone radio card."); |
38 | MODULE_LICENSE("GPL"); | 35 | MODULE_LICENSE("GPL"); |
39 | MODULE_VERSION("0.0.3"); | 36 | MODULE_VERSION("0.1.99"); |
40 | |||
41 | #ifndef CONFIG_RADIO_TERRATEC_PORT | ||
42 | #define CONFIG_RADIO_TERRATEC_PORT 0x590 | ||
43 | #endif | ||
44 | 37 | ||
45 | static int io = CONFIG_RADIO_TERRATEC_PORT; | 38 | /* Note: there seems to be only one possible port (0x590), but without |
39 | hardware this is hard to verify. For now, this is the only one we will | ||
40 | support. */ | ||
41 | static int io = 0x590; | ||
46 | static int radio_nr = -1; | 42 | static int radio_nr = -1; |
47 | 43 | ||
48 | module_param(io, int, 0); | 44 | module_param(radio_nr, int, 0444); |
49 | MODULE_PARM_DESC(io, "I/O address of the TerraTec ActiveRadio card (0x590 or 0x591)"); | 45 | MODULE_PARM_DESC(radio_nr, "Radio device number"); |
50 | module_param(radio_nr, int, 0); | ||
51 | |||
52 | static struct v4l2_queryctrl radio_qctrl[] = { | ||
53 | { | ||
54 | .id = V4L2_CID_AUDIO_MUTE, | ||
55 | .name = "Mute", | ||
56 | .minimum = 0, | ||
57 | .maximum = 1, | ||
58 | .default_value = 1, | ||
59 | .type = V4L2_CTRL_TYPE_BOOLEAN, | ||
60 | },{ | ||
61 | .id = V4L2_CID_AUDIO_VOLUME, | ||
62 | .name = "Volume", | ||
63 | .minimum = 0, | ||
64 | .maximum = 0xff, | ||
65 | .step = 1, | ||
66 | .default_value = 0xff, | ||
67 | .type = V4L2_CTRL_TYPE_INTEGER, | ||
68 | } | ||
69 | }; | ||
70 | 46 | ||
71 | #define WRT_DIS 0x00 | 47 | #define WRT_DIS 0x00 |
72 | #define CLK_OFF 0x00 | 48 | #define CLK_OFF 0x00 |
@@ -76,63 +52,24 @@ static struct v4l2_queryctrl radio_qctrl[] = { | |||
76 | #define CLK_ON 0x08 | 52 | #define CLK_ON 0x08 |
77 | #define WRT_EN 0x10 | 53 | #define WRT_EN 0x10 |
78 | 54 | ||
79 | struct terratec | 55 | static struct radio_isa_card *terratec_alloc(void) |
80 | { | 56 | { |
81 | struct v4l2_device v4l2_dev; | 57 | return kzalloc(sizeof(struct radio_isa_card), GFP_KERNEL); |
82 | struct video_device vdev; | 58 | } |
83 | int io; | ||
84 | int curvol; | ||
85 | unsigned long curfreq; | ||
86 | int muted; | ||
87 | struct mutex lock; | ||
88 | }; | ||
89 | |||
90 | static struct terratec terratec_card; | ||
91 | |||
92 | /* local things */ | ||
93 | 59 | ||
94 | static void tt_write_vol(struct terratec *tt, int volume) | 60 | static int terratec_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol) |
95 | { | 61 | { |
96 | int i; | 62 | int i; |
97 | 63 | ||
98 | volume = volume + (volume * 32); /* change both channels */ | 64 | if (mute) |
99 | mutex_lock(&tt->lock); | 65 | vol = 0; |
66 | vol = vol + (vol * 32); /* change both channels */ | ||
100 | for (i = 0; i < 8; i++) { | 67 | for (i = 0; i < 8; i++) { |
101 | if (volume & (0x80 >> i)) | 68 | if (vol & (0x80 >> i)) |
102 | outb(0x80, tt->io + 1); | 69 | outb(0x80, isa->io + 1); |
103 | else | 70 | else |
104 | outb(0x00, tt->io + 1); | 71 | outb(0x00, isa->io + 1); |
105 | } | ||
106 | mutex_unlock(&tt->lock); | ||
107 | } | ||
108 | |||
109 | |||
110 | |||
111 | static void tt_mute(struct terratec *tt) | ||
112 | { | ||
113 | tt->muted = 1; | ||
114 | tt_write_vol(tt, 0); | ||
115 | } | ||
116 | |||
117 | static int tt_setvol(struct terratec *tt, int vol) | ||
118 | { | ||
119 | if (vol == tt->curvol) { /* requested volume = current */ | ||
120 | if (tt->muted) { /* user is unmuting the card */ | ||
121 | tt->muted = 0; | ||
122 | tt_write_vol(tt, vol); /* enable card */ | ||
123 | } | ||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | if (vol == 0) { /* volume = 0 means mute the card */ | ||
128 | tt_write_vol(tt, 0); /* "turn off card" by setting vol to 0 */ | ||
129 | tt->curvol = vol; /* track the volume state! */ | ||
130 | return 0; | ||
131 | } | 72 | } |
132 | |||
133 | tt->muted = 0; | ||
134 | tt_write_vol(tt, vol); | ||
135 | tt->curvol = vol; | ||
136 | return 0; | 73 | return 0; |
137 | } | 74 | } |
138 | 75 | ||
@@ -140,20 +77,15 @@ static int tt_setvol(struct terratec *tt, int vol) | |||
140 | /* this is the worst part in this driver */ | 77 | /* this is the worst part in this driver */ |
141 | /* many more or less strange things are going on here, but hey, it works :) */ | 78 | /* many more or less strange things are going on here, but hey, it works :) */ |
142 | 79 | ||
143 | static int tt_setfreq(struct terratec *tt, unsigned long freq1) | 80 | static int terratec_s_frequency(struct radio_isa_card *isa, u32 freq) |
144 | { | 81 | { |
145 | int freq; | ||
146 | int i; | 82 | int i; |
147 | int p; | 83 | int p; |
148 | int temp; | 84 | int temp; |
149 | long rest; | 85 | long rest; |
150 | unsigned char buffer[25]; /* we have to bit shift 25 registers */ | 86 | unsigned char buffer[25]; /* we have to bit shift 25 registers */ |
151 | 87 | ||
152 | mutex_lock(&tt->lock); | 88 | freq = freq / 160; /* convert the freq. to a nice to handle value */ |
153 | |||
154 | tt->curfreq = freq1; | ||
155 | |||
156 | freq = freq1 / 160; /* convert the freq. to a nice to handle value */ | ||
157 | memset(buffer, 0, sizeof(buffer)); | 89 | memset(buffer, 0, sizeof(buffer)); |
158 | 90 | ||
159 | rest = freq * 10 + 10700; /* I once had understood what is going on here */ | 91 | rest = freq * 10 + 10700; /* I once had understood what is going on here */ |
@@ -175,239 +107,61 @@ static int tt_setfreq(struct terratec *tt, unsigned long freq1) | |||
175 | 107 | ||
176 | for (i = 24; i > -1; i--) { /* bit shift the values to the radiocard */ | 108 | for (i = 24; i > -1; i--) { /* bit shift the values to the radiocard */ |
177 | if (buffer[i] == 1) { | 109 | if (buffer[i] == 1) { |
178 | outb(WRT_EN | DATA, tt->io); | 110 | outb(WRT_EN | DATA, isa->io); |
179 | outb(WRT_EN | DATA | CLK_ON, tt->io); | 111 | outb(WRT_EN | DATA | CLK_ON, isa->io); |
180 | outb(WRT_EN | DATA, tt->io); | 112 | outb(WRT_EN | DATA, isa->io); |
181 | } else { | 113 | } else { |
182 | outb(WRT_EN | 0x00, tt->io); | 114 | outb(WRT_EN | 0x00, isa->io); |
183 | outb(WRT_EN | 0x00 | CLK_ON, tt->io); | 115 | outb(WRT_EN | 0x00 | CLK_ON, isa->io); |
184 | } | ||
185 | } | ||
186 | outb(0x00, tt->io); | ||
187 | |||
188 | mutex_unlock(&tt->lock); | ||
189 | |||
190 | return 0; | ||
191 | } | ||
192 | |||
193 | static int tt_getsigstr(struct terratec *tt) | ||
194 | { | ||
195 | if (inb(tt->io) & 2) /* bit set = no signal present */ | ||
196 | return 0; | ||
197 | return 1; /* signal present */ | ||
198 | } | ||
199 | |||
200 | static int vidioc_querycap(struct file *file, void *priv, | ||
201 | struct v4l2_capability *v) | ||
202 | { | ||
203 | strlcpy(v->driver, "radio-terratec", sizeof(v->driver)); | ||
204 | strlcpy(v->card, "ActiveRadio", sizeof(v->card)); | ||
205 | strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); | ||
206 | v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; | ||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | static int vidioc_g_tuner(struct file *file, void *priv, | ||
211 | struct v4l2_tuner *v) | ||
212 | { | ||
213 | struct terratec *tt = video_drvdata(file); | ||
214 | |||
215 | if (v->index > 0) | ||
216 | return -EINVAL; | ||
217 | |||
218 | strlcpy(v->name, "FM", sizeof(v->name)); | ||
219 | v->type = V4L2_TUNER_RADIO; | ||
220 | v->rangelow = 87 * 16000; | ||
221 | v->rangehigh = 108 * 16000; | ||
222 | v->rxsubchans = V4L2_TUNER_SUB_MONO; | ||
223 | v->capability = V4L2_TUNER_CAP_LOW; | ||
224 | v->audmode = V4L2_TUNER_MODE_MONO; | ||
225 | v->signal = 0xFFFF * tt_getsigstr(tt); | ||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | static int vidioc_s_tuner(struct file *file, void *priv, | ||
230 | struct v4l2_tuner *v) | ||
231 | { | ||
232 | return v->index ? -EINVAL : 0; | ||
233 | } | ||
234 | |||
235 | static int vidioc_s_frequency(struct file *file, void *priv, | ||
236 | struct v4l2_frequency *f) | ||
237 | { | ||
238 | struct terratec *tt = video_drvdata(file); | ||
239 | |||
240 | if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) | ||
241 | return -EINVAL; | ||
242 | tt_setfreq(tt, f->frequency); | ||
243 | return 0; | ||
244 | } | ||
245 | |||
246 | static int vidioc_g_frequency(struct file *file, void *priv, | ||
247 | struct v4l2_frequency *f) | ||
248 | { | ||
249 | struct terratec *tt = video_drvdata(file); | ||
250 | |||
251 | if (f->tuner != 0) | ||
252 | return -EINVAL; | ||
253 | f->type = V4L2_TUNER_RADIO; | ||
254 | f->frequency = tt->curfreq; | ||
255 | return 0; | ||
256 | } | ||
257 | |||
258 | static int vidioc_queryctrl(struct file *file, void *priv, | ||
259 | struct v4l2_queryctrl *qc) | ||
260 | { | ||
261 | int i; | ||
262 | |||
263 | for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { | ||
264 | if (qc->id && qc->id == radio_qctrl[i].id) { | ||
265 | memcpy(qc, &(radio_qctrl[i]), sizeof(*qc)); | ||
266 | return 0; | ||
267 | } | 116 | } |
268 | } | 117 | } |
269 | return -EINVAL; | 118 | outb(0x00, isa->io); |
270 | } | ||
271 | |||
272 | static int vidioc_g_ctrl(struct file *file, void *priv, | ||
273 | struct v4l2_control *ctrl) | ||
274 | { | ||
275 | struct terratec *tt = video_drvdata(file); | ||
276 | |||
277 | switch (ctrl->id) { | ||
278 | case V4L2_CID_AUDIO_MUTE: | ||
279 | if (tt->muted) | ||
280 | ctrl->value = 1; | ||
281 | else | ||
282 | ctrl->value = 0; | ||
283 | return 0; | ||
284 | case V4L2_CID_AUDIO_VOLUME: | ||
285 | ctrl->value = tt->curvol * 6554; | ||
286 | return 0; | ||
287 | } | ||
288 | return -EINVAL; | ||
289 | } | ||
290 | |||
291 | static int vidioc_s_ctrl(struct file *file, void *priv, | ||
292 | struct v4l2_control *ctrl) | ||
293 | { | ||
294 | struct terratec *tt = video_drvdata(file); | ||
295 | |||
296 | switch (ctrl->id) { | ||
297 | case V4L2_CID_AUDIO_MUTE: | ||
298 | if (ctrl->value) | ||
299 | tt_mute(tt); | ||
300 | else | ||
301 | tt_setvol(tt,tt->curvol); | ||
302 | return 0; | ||
303 | case V4L2_CID_AUDIO_VOLUME: | ||
304 | tt_setvol(tt,ctrl->value); | ||
305 | return 0; | ||
306 | } | ||
307 | return -EINVAL; | ||
308 | } | ||
309 | |||
310 | static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) | ||
311 | { | ||
312 | *i = 0; | ||
313 | return 0; | ||
314 | } | ||
315 | |||
316 | static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) | ||
317 | { | ||
318 | return i ? -EINVAL : 0; | ||
319 | } | ||
320 | |||
321 | static int vidioc_g_audio(struct file *file, void *priv, | ||
322 | struct v4l2_audio *a) | ||
323 | { | ||
324 | a->index = 0; | ||
325 | strlcpy(a->name, "Radio", sizeof(a->name)); | ||
326 | a->capability = V4L2_AUDCAP_STEREO; | ||
327 | return 0; | 119 | return 0; |
328 | } | 120 | } |
329 | 121 | ||
330 | static int vidioc_s_audio(struct file *file, void *priv, | 122 | static u32 terratec_g_signal(struct radio_isa_card *isa) |
331 | struct v4l2_audio *a) | ||
332 | { | 123 | { |
333 | return a->index ? -EINVAL : 0; | 124 | /* bit set = no signal present */ |
125 | return (inb(isa->io) & 2) ? 0 : 0xffff; | ||
334 | } | 126 | } |
335 | 127 | ||
336 | static const struct v4l2_file_operations terratec_fops = { | 128 | static const struct radio_isa_ops terratec_ops = { |
337 | .owner = THIS_MODULE, | 129 | .alloc = terratec_alloc, |
338 | .unlocked_ioctl = video_ioctl2, | 130 | .s_mute_volume = terratec_s_mute_volume, |
131 | .s_frequency = terratec_s_frequency, | ||
132 | .g_signal = terratec_g_signal, | ||
339 | }; | 133 | }; |
340 | 134 | ||
341 | static const struct v4l2_ioctl_ops terratec_ioctl_ops = { | 135 | static const int terratec_ioports[] = { 0x590 }; |
342 | .vidioc_querycap = vidioc_querycap, | 136 | |
343 | .vidioc_g_tuner = vidioc_g_tuner, | 137 | static struct radio_isa_driver terratec_driver = { |
344 | .vidioc_s_tuner = vidioc_s_tuner, | 138 | .driver = { |
345 | .vidioc_g_frequency = vidioc_g_frequency, | 139 | .match = radio_isa_match, |
346 | .vidioc_s_frequency = vidioc_s_frequency, | 140 | .probe = radio_isa_probe, |
347 | .vidioc_queryctrl = vidioc_queryctrl, | 141 | .remove = radio_isa_remove, |
348 | .vidioc_g_ctrl = vidioc_g_ctrl, | 142 | .driver = { |
349 | .vidioc_s_ctrl = vidioc_s_ctrl, | 143 | .name = "radio-terratec", |
350 | .vidioc_g_audio = vidioc_g_audio, | 144 | }, |
351 | .vidioc_s_audio = vidioc_s_audio, | 145 | }, |
352 | .vidioc_g_input = vidioc_g_input, | 146 | .io_params = &io, |
353 | .vidioc_s_input = vidioc_s_input, | 147 | .radio_nr_params = &radio_nr, |
148 | .io_ports = terratec_ioports, | ||
149 | .num_of_io_ports = ARRAY_SIZE(terratec_ioports), | ||
150 | .region_size = 2, | ||
151 | .card = "TerraTec ActiveRadio", | ||
152 | .ops = &terratec_ops, | ||
153 | .has_stereo = true, | ||
154 | .max_volume = 10, | ||
354 | }; | 155 | }; |
355 | 156 | ||
356 | static int __init terratec_init(void) | 157 | static int __init terratec_init(void) |
357 | { | 158 | { |
358 | struct terratec *tt = &terratec_card; | 159 | return isa_register_driver(&terratec_driver.driver, 1); |
359 | struct v4l2_device *v4l2_dev = &tt->v4l2_dev; | ||
360 | int res; | ||
361 | |||
362 | strlcpy(v4l2_dev->name, "terratec", sizeof(v4l2_dev->name)); | ||
363 | tt->io = io; | ||
364 | if (tt->io == -1) { | ||
365 | v4l2_err(v4l2_dev, "you must set an I/O address with io=0x590 or 0x591\n"); | ||
366 | return -EINVAL; | ||
367 | } | ||
368 | if (!request_region(tt->io, 2, "terratec")) { | ||
369 | v4l2_err(v4l2_dev, "port 0x%x already in use\n", io); | ||
370 | return -EBUSY; | ||
371 | } | ||
372 | |||
373 | res = v4l2_device_register(NULL, v4l2_dev); | ||
374 | if (res < 0) { | ||
375 | release_region(tt->io, 2); | ||
376 | v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); | ||
377 | return res; | ||
378 | } | ||
379 | |||
380 | strlcpy(tt->vdev.name, v4l2_dev->name, sizeof(tt->vdev.name)); | ||
381 | tt->vdev.v4l2_dev = v4l2_dev; | ||
382 | tt->vdev.fops = &terratec_fops; | ||
383 | tt->vdev.ioctl_ops = &terratec_ioctl_ops; | ||
384 | tt->vdev.release = video_device_release_empty; | ||
385 | video_set_drvdata(&tt->vdev, tt); | ||
386 | |||
387 | mutex_init(&tt->lock); | ||
388 | |||
389 | /* mute card - prevents noisy bootups */ | ||
390 | tt_write_vol(tt, 0); | ||
391 | |||
392 | if (video_register_device(&tt->vdev, VFL_TYPE_RADIO, radio_nr) < 0) { | ||
393 | v4l2_device_unregister(&tt->v4l2_dev); | ||
394 | release_region(tt->io, 2); | ||
395 | return -EINVAL; | ||
396 | } | ||
397 | |||
398 | v4l2_info(v4l2_dev, "TERRATEC ActivRadio Standalone card driver.\n"); | ||
399 | return 0; | ||
400 | } | 160 | } |
401 | 161 | ||
402 | static void __exit terratec_exit(void) | 162 | static void __exit terratec_exit(void) |
403 | { | 163 | { |
404 | struct terratec *tt = &terratec_card; | 164 | isa_unregister_driver(&terratec_driver.driver); |
405 | struct v4l2_device *v4l2_dev = &tt->v4l2_dev; | ||
406 | |||
407 | video_unregister_device(&tt->vdev); | ||
408 | v4l2_device_unregister(&tt->v4l2_dev); | ||
409 | release_region(tt->io, 2); | ||
410 | v4l2_info(v4l2_dev, "TERRATEC ActivRadio Standalone card driver unloaded.\n"); | ||
411 | } | 165 | } |
412 | 166 | ||
413 | module_init(terratec_init); | 167 | module_init(terratec_init); |