aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans Verkuil <hans.verkuil@cisco.com>2012-02-03 06:28:56 -0500
committerMauro Carvalho Chehab <mchehab@redhat.com>2012-02-14 14:01:13 -0500
commit137c579c12bbb47ac1822e1a959aa15d0fcb76c1 (patch)
tree9b2ab60f813734ebf31126eda3bf60466573310a
parent83ec8225b6aecfddafd3b1d40cf79c0d4615a84c (diff)
[media] radio-isa: add framework for ISA radio drivers
We have quite a few ISA radio drivers, which are all very similar. This framework makes it possible to reduce the code size of those drivers and makes it much easier to keep them up to date with the latest V4L2 API developments. Drivers rewritten to use this framework fully pass the v4l2-compliance tests and are properly using the ISA bus (so they can be found under /sys/bus/isa). It is now also possible to support multiple cards using the same driver (tested with two radio-gemtek cards). Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r--drivers/media/radio/Kconfig4
-rw-r--r--drivers/media/radio/Makefile1
-rw-r--r--drivers/media/radio/radio-isa.c339
-rw-r--r--drivers/media/radio/radio-isa.h105
4 files changed, 449 insertions, 0 deletions
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index 48747df59453..e291e0e2669f 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -177,6 +177,10 @@ menuconfig V4L_RADIO_ISA_DRIVERS
177 177
178if V4L_RADIO_ISA_DRIVERS 178if V4L_RADIO_ISA_DRIVERS
179 179
180config RADIO_ISA
181 depends on ISA
182 tristate
183
180config RADIO_CADET 184config RADIO_CADET
181 tristate "ADS Cadet AM/FM Tuner" 185 tristate "ADS Cadet AM/FM Tuner"
182 depends on ISA && VIDEO_V4L2 186 depends on ISA && VIDEO_V4L2
diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile
index aec5f6fa592f..ca8c7d134b95 100644
--- a/drivers/media/radio/Makefile
+++ b/drivers/media/radio/Makefile
@@ -2,6 +2,7 @@
2# Makefile for the kernel character device drivers. 2# Makefile for the kernel character device drivers.
3# 3#
4 4
5obj-$(CONFIG_RADIO_ISA) += radio-isa.o
5obj-$(CONFIG_RADIO_AZTECH) += radio-aztech.o 6obj-$(CONFIG_RADIO_AZTECH) += radio-aztech.o
6obj-$(CONFIG_RADIO_RTRACK2) += radio-rtrack2.o 7obj-$(CONFIG_RADIO_RTRACK2) += radio-rtrack2.o
7obj-$(CONFIG_RADIO_SF16FMI) += radio-sf16fmi.o 8obj-$(CONFIG_RADIO_SF16FMI) += radio-sf16fmi.o
diff --git a/drivers/media/radio/radio-isa.c b/drivers/media/radio/radio-isa.c
new file mode 100644
index 000000000000..02bcead8b087
--- /dev/null
+++ b/drivers/media/radio/radio-isa.c
@@ -0,0 +1,339 @@
1/*
2 * Framework for ISA radio drivers.
3 * This takes care of all the V4L2 scaffolding, allowing the ISA drivers
4 * to concentrate on the actual hardware operation.
5 *
6 * Copyright (C) 2012 Hans Verkuil <hans.verkuil@cisco.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * version 2 as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA
21 */
22
23#include <linux/module.h>
24#include <linux/init.h>
25#include <linux/ioport.h>
26#include <linux/delay.h>
27#include <linux/videodev2.h>
28#include <linux/io.h>
29#include <media/v4l2-device.h>
30#include <media/v4l2-ioctl.h>
31#include <media/v4l2-fh.h>
32#include <media/v4l2-ctrls.h>
33#include <media/v4l2-event.h>
34
35#include "radio-isa.h"
36
37MODULE_AUTHOR("Hans Verkuil");
38MODULE_DESCRIPTION("A framework for ISA radio drivers.");
39MODULE_LICENSE("GPL");
40
41#define FREQ_LOW (87U * 16000U)
42#define FREQ_HIGH (108U * 16000U)
43
44static int radio_isa_querycap(struct file *file, void *priv,
45 struct v4l2_capability *v)
46{
47 struct radio_isa_card *isa = video_drvdata(file);
48
49 strlcpy(v->driver, isa->drv->driver.driver.name, sizeof(v->driver));
50 strlcpy(v->card, isa->drv->card, sizeof(v->card));
51 snprintf(v->bus_info, sizeof(v->bus_info), "ISA:%s", isa->v4l2_dev.name);
52
53 v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
54 v->device_caps = v->capabilities | V4L2_CAP_DEVICE_CAPS;
55 return 0;
56}
57
58static int radio_isa_g_tuner(struct file *file, void *priv,
59 struct v4l2_tuner *v)
60{
61 struct radio_isa_card *isa = video_drvdata(file);
62 const struct radio_isa_ops *ops = isa->drv->ops;
63
64 if (v->index > 0)
65 return -EINVAL;
66
67 strlcpy(v->name, "FM", sizeof(v->name));
68 v->type = V4L2_TUNER_RADIO;
69 v->rangelow = FREQ_LOW;
70 v->rangehigh = FREQ_HIGH;
71 v->capability = V4L2_TUNER_CAP_LOW;
72 if (isa->drv->has_stereo)
73 v->capability |= V4L2_TUNER_CAP_STEREO;
74
75 if (ops->g_rxsubchans)
76 v->rxsubchans = ops->g_rxsubchans(isa);
77 else
78 v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
79 v->audmode = isa->stereo ? V4L2_TUNER_MODE_STEREO : V4L2_TUNER_MODE_MONO;
80 if (ops->g_signal)
81 v->signal = ops->g_signal(isa);
82 else
83 v->signal = (v->rxsubchans & V4L2_TUNER_SUB_STEREO) ?
84 0xffff : 0;
85 return 0;
86}
87
88static int radio_isa_s_tuner(struct file *file, void *priv,
89 struct v4l2_tuner *v)
90{
91 struct radio_isa_card *isa = video_drvdata(file);
92 const struct radio_isa_ops *ops = isa->drv->ops;
93
94 if (v->index)
95 return -EINVAL;
96 if (ops->s_stereo) {
97 isa->stereo = (v->audmode == V4L2_TUNER_MODE_STEREO);
98 return ops->s_stereo(isa, isa->stereo);
99 }
100 return 0;
101}
102
103static int radio_isa_s_frequency(struct file *file, void *priv,
104 struct v4l2_frequency *f)
105{
106 struct radio_isa_card *isa = video_drvdata(file);
107 int res;
108
109 if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
110 return -EINVAL;
111 f->frequency = clamp(f->frequency, FREQ_LOW, FREQ_HIGH);
112 res = isa->drv->ops->s_frequency(isa, f->frequency);
113 if (res == 0)
114 isa->freq = f->frequency;
115 return res;
116}
117
118static int radio_isa_g_frequency(struct file *file, void *priv,
119 struct v4l2_frequency *f)
120{
121 struct radio_isa_card *isa = video_drvdata(file);
122
123 if (f->tuner != 0)
124 return -EINVAL;
125 f->type = V4L2_TUNER_RADIO;
126 f->frequency = isa->freq;
127 return 0;
128}
129
130static int radio_isa_s_ctrl(struct v4l2_ctrl *ctrl)
131{
132 struct radio_isa_card *isa =
133 container_of(ctrl->handler, struct radio_isa_card, hdl);
134
135 switch (ctrl->id) {
136 case V4L2_CID_AUDIO_MUTE:
137 return isa->drv->ops->s_mute_volume(isa, ctrl->val,
138 isa->volume ? isa->volume->val : 0);
139 }
140 return -EINVAL;
141}
142
143static int radio_isa_log_status(struct file *file, void *priv)
144{
145 struct radio_isa_card *isa = video_drvdata(file);
146
147 v4l2_info(&isa->v4l2_dev, "I/O Port = 0x%03x\n", isa->io);
148 v4l2_ctrl_handler_log_status(&isa->hdl, isa->v4l2_dev.name);
149 return 0;
150}
151
152static int radio_isa_subscribe_event(struct v4l2_fh *fh,
153 struct v4l2_event_subscription *sub)
154{
155 if (sub->type == V4L2_EVENT_CTRL)
156 return v4l2_event_subscribe(fh, sub, 0);
157 return -EINVAL;
158}
159
160static const struct v4l2_ctrl_ops radio_isa_ctrl_ops = {
161 .s_ctrl = radio_isa_s_ctrl,
162};
163
164static const struct v4l2_file_operations radio_isa_fops = {
165 .owner = THIS_MODULE,
166 .open = v4l2_fh_open,
167 .release = v4l2_fh_release,
168 .poll = v4l2_ctrl_poll,
169 .unlocked_ioctl = video_ioctl2,
170};
171
172static const struct v4l2_ioctl_ops radio_isa_ioctl_ops = {
173 .vidioc_querycap = radio_isa_querycap,
174 .vidioc_g_tuner = radio_isa_g_tuner,
175 .vidioc_s_tuner = radio_isa_s_tuner,
176 .vidioc_g_frequency = radio_isa_g_frequency,
177 .vidioc_s_frequency = radio_isa_s_frequency,
178 .vidioc_log_status = radio_isa_log_status,
179 .vidioc_subscribe_event = radio_isa_subscribe_event,
180 .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
181};
182
183int radio_isa_match(struct device *pdev, unsigned int dev)
184{
185 struct radio_isa_driver *drv = pdev->platform_data;
186
187 return drv->probe || drv->io_params[dev] >= 0;
188}
189EXPORT_SYMBOL_GPL(radio_isa_match);
190
191static bool radio_isa_valid_io(const struct radio_isa_driver *drv, int io)
192{
193 int i;
194
195 for (i = 0; i < drv->num_of_io_ports; i++)
196 if (drv->io_ports[i] == io)
197 return true;
198 return false;
199}
200
201int radio_isa_probe(struct device *pdev, unsigned int dev)
202{
203 struct radio_isa_driver *drv = pdev->platform_data;
204 const struct radio_isa_ops *ops = drv->ops;
205 struct v4l2_device *v4l2_dev;
206 struct radio_isa_card *isa;
207 int res;
208
209 isa = drv->ops->alloc();
210 if (isa == NULL)
211 return -ENOMEM;
212 dev_set_drvdata(pdev, isa);
213 isa->drv = drv;
214 isa->io = drv->io_params[dev];
215 v4l2_dev = &isa->v4l2_dev;
216 strlcpy(v4l2_dev->name, dev_name(pdev), sizeof(v4l2_dev->name));
217
218 if (drv->probe && ops->probe) {
219 int i;
220
221 for (i = 0; i < drv->num_of_io_ports; ++i) {
222 int io = drv->io_ports[i];
223
224 if (request_region(io, drv->region_size, v4l2_dev->name)) {
225 bool found = ops->probe(isa, io);
226
227 release_region(io, drv->region_size);
228 if (found) {
229 isa->io = io;
230 break;
231 }
232 }
233 }
234 }
235
236 if (!radio_isa_valid_io(drv, isa->io)) {
237 int i;
238
239 if (isa->io < 0)
240 return -ENODEV;
241 v4l2_err(v4l2_dev, "you must set an I/O address with io=0x%03x",
242 drv->io_ports[0]);
243 for (i = 1; i < drv->num_of_io_ports; i++)
244 printk(KERN_CONT "/0x%03x", drv->io_ports[i]);
245 printk(KERN_CONT ".\n");
246 kfree(isa);
247 return -EINVAL;
248 }
249
250 if (!request_region(isa->io, drv->region_size, v4l2_dev->name)) {
251 v4l2_err(v4l2_dev, "port 0x%x already in use\n", isa->io);
252 kfree(isa);
253 return -EBUSY;
254 }
255
256 res = v4l2_device_register(pdev, v4l2_dev);
257 if (res < 0) {
258 v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
259 goto err_dev_reg;
260 }
261
262 v4l2_ctrl_handler_init(&isa->hdl, 1);
263 isa->mute = v4l2_ctrl_new_std(&isa->hdl, &radio_isa_ctrl_ops,
264 V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
265 if (drv->max_volume)
266 isa->volume = v4l2_ctrl_new_std(&isa->hdl, &radio_isa_ctrl_ops,
267 V4L2_CID_AUDIO_VOLUME, 0, drv->max_volume, 1,
268 drv->max_volume);
269 v4l2_dev->ctrl_handler = &isa->hdl;
270 if (isa->hdl.error) {
271 res = isa->hdl.error;
272 v4l2_err(v4l2_dev, "Could not register controls\n");
273 goto err_hdl;
274 }
275 if (drv->max_volume)
276 v4l2_ctrl_cluster(2, &isa->mute);
277 v4l2_dev->ctrl_handler = &isa->hdl;
278
279 mutex_init(&isa->lock);
280 isa->vdev.lock = &isa->lock;
281 strlcpy(isa->vdev.name, v4l2_dev->name, sizeof(isa->vdev.name));
282 isa->vdev.v4l2_dev = v4l2_dev;
283 isa->vdev.fops = &radio_isa_fops;
284 isa->vdev.ioctl_ops = &radio_isa_ioctl_ops;
285 isa->vdev.release = video_device_release_empty;
286 set_bit(V4L2_FL_USE_FH_PRIO, &isa->vdev.flags);
287 video_set_drvdata(&isa->vdev, isa);
288 isa->freq = FREQ_LOW;
289 isa->stereo = drv->has_stereo;
290
291 if (ops->init)
292 res = ops->init(isa);
293 if (!res)
294 res = v4l2_ctrl_handler_setup(&isa->hdl);
295 if (!res)
296 res = ops->s_frequency(isa, isa->freq);
297 if (!res && ops->s_stereo)
298 res = ops->s_stereo(isa, isa->stereo);
299 if (res < 0) {
300 v4l2_err(v4l2_dev, "Could not setup card\n");
301 goto err_node_reg;
302 }
303 res = video_register_device(&isa->vdev, VFL_TYPE_RADIO,
304 drv->radio_nr_params[dev]);
305 if (res < 0) {
306 v4l2_err(v4l2_dev, "Could not register device node\n");
307 goto err_node_reg;
308 }
309
310 v4l2_info(v4l2_dev, "Initialized radio card %s on port 0x%03x\n",
311 drv->card, isa->io);
312 return 0;
313
314err_node_reg:
315 v4l2_ctrl_handler_free(&isa->hdl);
316err_hdl:
317 v4l2_device_unregister(&isa->v4l2_dev);
318err_dev_reg:
319 release_region(isa->io, drv->region_size);
320 kfree(isa);
321 return res;
322}
323EXPORT_SYMBOL_GPL(radio_isa_probe);
324
325int radio_isa_remove(struct device *pdev, unsigned int dev)
326{
327 struct radio_isa_card *isa = dev_get_drvdata(pdev);
328 const struct radio_isa_ops *ops = isa->drv->ops;
329
330 ops->s_mute_volume(isa, true, isa->volume ? isa->volume->cur.val : 0);
331 video_unregister_device(&isa->vdev);
332 v4l2_ctrl_handler_free(&isa->hdl);
333 v4l2_device_unregister(&isa->v4l2_dev);
334 release_region(isa->io, isa->drv->region_size);
335 v4l2_info(&isa->v4l2_dev, "Removed radio card %s\n", isa->drv->card);
336 kfree(isa);
337 return 0;
338}
339EXPORT_SYMBOL_GPL(radio_isa_remove);
diff --git a/drivers/media/radio/radio-isa.h b/drivers/media/radio/radio-isa.h
new file mode 100644
index 000000000000..8a0ea84d86de
--- /dev/null
+++ b/drivers/media/radio/radio-isa.h
@@ -0,0 +1,105 @@
1/*
2 * Framework for ISA radio drivers.
3 * This takes care of all the V4L2 scaffolding, allowing the ISA drivers
4 * to concentrate on the actual hardware operation.
5 *
6 * Copyright (C) 2012 Hans Verkuil <hans.verkuil@cisco.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * version 2 as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA
21 */
22
23#ifndef _RADIO_ISA_H_
24#define _RADIO_ISA_H_
25
26#include <linux/isa.h>
27#include <linux/videodev2.h>
28#include <media/v4l2-device.h>
29#include <media/v4l2-ctrls.h>
30
31struct radio_isa_driver;
32struct radio_isa_ops;
33
34/* Core structure for radio ISA cards */
35struct radio_isa_card {
36 const struct radio_isa_driver *drv;
37 struct v4l2_device v4l2_dev;
38 struct v4l2_ctrl_handler hdl;
39 struct video_device vdev;
40 struct mutex lock;
41 const struct radio_isa_ops *ops;
42 struct { /* mute/volume cluster */
43 struct v4l2_ctrl *mute;
44 struct v4l2_ctrl *volume;
45 };
46 /* I/O port */
47 int io;
48
49 /* Card is in stereo audio mode */
50 bool stereo;
51 /* Current frequency */
52 u32 freq;
53};
54
55struct radio_isa_ops {
56 /* Allocate and initialize a radio_isa_card struct */
57 struct radio_isa_card *(*alloc)(void);
58 /* Probe whether a card is present at the given port */
59 bool (*probe)(struct radio_isa_card *isa, int io);
60 /* Special card initialization can be done here, this is called after
61 * the standard controls are registered, but before they are setup,
62 * thus allowing drivers to add their own controls here. */
63 int (*init)(struct radio_isa_card *isa);
64 /* Set mute and volume. */
65 int (*s_mute_volume)(struct radio_isa_card *isa, bool mute, int volume);
66 /* Set frequency */
67 int (*s_frequency)(struct radio_isa_card *isa, u32 freq);
68 /* Set stereo/mono audio mode */
69 int (*s_stereo)(struct radio_isa_card *isa, bool stereo);
70 /* Get rxsubchans value for VIDIOC_G_TUNER */
71 u32 (*g_rxsubchans)(struct radio_isa_card *isa);
72 /* Get the signal strength for VIDIOC_G_TUNER */
73 u32 (*g_signal)(struct radio_isa_card *isa);
74};
75
76/* Top level structure needed to instantiate the cards */
77struct radio_isa_driver {
78 struct isa_driver driver;
79 const struct radio_isa_ops *ops;
80 /* The module_param_array with the specified I/O ports */
81 int *io_params;
82 /* The module_param_array with the radio_nr values */
83 int *radio_nr_params;
84 /* Whether we should probe for possible cards */
85 bool probe;
86 /* The list of possible I/O ports */
87 const int *io_ports;
88 /* The size of that list */
89 int num_of_io_ports;
90 /* The region size to request */
91 unsigned region_size;
92 /* The name of the card */
93 const char *card;
94 /* Card can capture stereo audio */
95 bool has_stereo;
96 /* The maximum volume for the volume control. If 0, then there
97 is no volume control possible. */
98 int max_volume;
99};
100
101int radio_isa_match(struct device *pdev, unsigned int dev);
102int radio_isa_probe(struct device *pdev, unsigned int dev);
103int radio_isa_remove(struct device *pdev, unsigned int dev);
104
105#endif