1 /* Miro PCM20 radio driver for Linux radio support
2 * (c) 1998 Ruurd Reitsma <R.A.Reitsma@wbmt.tudelft.nl>
3 * Thanks to Norberto Pellici for the ACI device interface specification
4 * The API part is based on the radiotrack driver by M. Kirkwood
5 * This driver relies on the aci mixer provided by the snd-miro
7 * Look there for further info...
10 /* What ever you think about the ACI, version 0x07 is not very well!
11 * I can't get frequency, 'tuner status', 'tuner flags' or mute/mono
12 * conditions... Robert
15 #include <linux/module.h>
16 #include <linux/init.h>
17 #include <linux/videodev2.h>
18 #include <media/v4l2-device.h>
19 #include <media/v4l2-ioctl.h>
20 #include <sound/aci.h>
22 static int radio_nr
= -1;
23 module_param(radio_nr
, int, 0);
24 MODULE_PARM_DESC(radio_nr
, "Set radio device number (/dev/radioX). Default: -1 (autodetect)");
27 module_param(mono
, bool, 0);
28 MODULE_PARM_DESC(mono
, "Force tuner into mono mode.");
31 struct v4l2_device v4l2_dev
;
32 struct video_device vdev
;
35 struct snd_miro_aci
*aci
;
39 static struct pcm20 pcm20_card
= {
44 static int pcm20_mute(struct pcm20
*dev
, unsigned char mute
)
47 return snd_aci_cmd(dev
->aci
, ACI_SET_TUNERMUTE
, mute
, -1);
50 static int pcm20_stereo(struct pcm20
*dev
, unsigned char stereo
)
52 return snd_aci_cmd(dev
->aci
, ACI_SET_TUNERMONO
, !stereo
, -1);
55 static int pcm20_setfreq(struct pcm20
*dev
, unsigned long freq
)
59 struct snd_miro_aci
*aci
= dev
->aci
;
64 if (!(aci
->aci_version
== 0x07 || aci
->aci_version
>= 0xb0))
65 freq
/= 10; /* I don't know exactly which version
70 pcm20_stereo(dev
, !mono
);
71 return snd_aci_cmd(aci
, ACI_WRITE_TUNE
, freql
, freqh
);
74 static const struct v4l2_file_operations pcm20_fops
= {
76 .unlocked_ioctl
= video_ioctl2
,
79 static int vidioc_querycap(struct file
*file
, void *priv
,
80 struct v4l2_capability
*v
)
82 strlcpy(v
->driver
, "Miro PCM20", sizeof(v
->driver
));
83 strlcpy(v
->card
, "Miro PCM20", sizeof(v
->card
));
84 strlcpy(v
->bus_info
, "ISA", sizeof(v
->bus_info
));
86 v
->capabilities
= V4L2_CAP_TUNER
| V4L2_CAP_RADIO
;
90 static int vidioc_g_tuner(struct file
*file
, void *priv
,
93 if (v
->index
) /* Only 1 tuner */
95 strlcpy(v
->name
, "FM", sizeof(v
->name
));
96 v
->type
= V4L2_TUNER_RADIO
;
97 v
->rangelow
= 87*16000;
98 v
->rangehigh
= 108*16000;
100 v
->rxsubchans
= V4L2_TUNER_SUB_MONO
| V4L2_TUNER_SUB_STEREO
;
101 v
->capability
= V4L2_TUNER_CAP_LOW
;
102 v
->audmode
= V4L2_TUNER_MODE_MONO
;
106 static int vidioc_s_tuner(struct file
*file
, void *priv
,
107 struct v4l2_tuner
*v
)
109 return v
->index
? -EINVAL
: 0;
112 static int vidioc_g_frequency(struct file
*file
, void *priv
,
113 struct v4l2_frequency
*f
)
115 struct pcm20
*dev
= video_drvdata(file
);
120 f
->type
= V4L2_TUNER_RADIO
;
121 f
->frequency
= dev
->freq
;
126 static int vidioc_s_frequency(struct file
*file
, void *priv
,
127 struct v4l2_frequency
*f
)
129 struct pcm20
*dev
= video_drvdata(file
);
131 if (f
->tuner
!= 0 || f
->type
!= V4L2_TUNER_RADIO
)
134 dev
->freq
= f
->frequency
;
135 pcm20_setfreq(dev
, f
->frequency
);
139 static int vidioc_queryctrl(struct file
*file
, void *priv
,
140 struct v4l2_queryctrl
*qc
)
143 case V4L2_CID_AUDIO_MUTE
:
144 return v4l2_ctrl_query_fill(qc
, 0, 1, 1, 1);
149 static int vidioc_g_ctrl(struct file
*file
, void *priv
,
150 struct v4l2_control
*ctrl
)
152 struct pcm20
*dev
= video_drvdata(file
);
155 case V4L2_CID_AUDIO_MUTE
:
156 ctrl
->value
= dev
->muted
;
164 static int vidioc_s_ctrl(struct file
*file
, void *priv
,
165 struct v4l2_control
*ctrl
)
167 struct pcm20
*dev
= video_drvdata(file
);
170 case V4L2_CID_AUDIO_MUTE
:
171 pcm20_mute(dev
, ctrl
->value
);
179 static int vidioc_g_input(struct file
*filp
, void *priv
, unsigned int *i
)
185 static int vidioc_s_input(struct file
*filp
, void *priv
, unsigned int i
)
187 return i
? -EINVAL
: 0;
190 static int vidioc_g_audio(struct file
*file
, void *priv
,
191 struct v4l2_audio
*a
)
194 strlcpy(a
->name
, "Radio", sizeof(a
->name
));
195 a
->capability
= V4L2_AUDCAP_STEREO
;
199 static int vidioc_s_audio(struct file
*file
, void *priv
,
200 struct v4l2_audio
*a
)
202 return a
->index
? -EINVAL
: 0;
205 static const struct v4l2_ioctl_ops pcm20_ioctl_ops
= {
206 .vidioc_querycap
= vidioc_querycap
,
207 .vidioc_g_tuner
= vidioc_g_tuner
,
208 .vidioc_s_tuner
= vidioc_s_tuner
,
209 .vidioc_g_frequency
= vidioc_g_frequency
,
210 .vidioc_s_frequency
= vidioc_s_frequency
,
211 .vidioc_queryctrl
= vidioc_queryctrl
,
212 .vidioc_g_ctrl
= vidioc_g_ctrl
,
213 .vidioc_s_ctrl
= vidioc_s_ctrl
,
214 .vidioc_g_audio
= vidioc_g_audio
,
215 .vidioc_s_audio
= vidioc_s_audio
,
216 .vidioc_g_input
= vidioc_g_input
,
217 .vidioc_s_input
= vidioc_s_input
,
220 static int __init
pcm20_init(void)
222 struct pcm20
*dev
= &pcm20_card
;
223 struct v4l2_device
*v4l2_dev
= &dev
->v4l2_dev
;
226 dev
->aci
= snd_aci_get_aci();
227 if (dev
->aci
== NULL
) {
229 "you must load the snd-miro driver first!\n");
232 strlcpy(v4l2_dev
->name
, "miropcm20", sizeof(v4l2_dev
->name
));
233 mutex_init(&dev
->lock
);
235 res
= v4l2_device_register(NULL
, v4l2_dev
);
237 v4l2_err(v4l2_dev
, "could not register v4l2_device\n");
241 strlcpy(dev
->vdev
.name
, v4l2_dev
->name
, sizeof(dev
->vdev
.name
));
242 dev
->vdev
.v4l2_dev
= v4l2_dev
;
243 dev
->vdev
.fops
= &pcm20_fops
;
244 dev
->vdev
.ioctl_ops
= &pcm20_ioctl_ops
;
245 dev
->vdev
.release
= video_device_release_empty
;
246 dev
->vdev
.lock
= &dev
->lock
;
247 video_set_drvdata(&dev
->vdev
, dev
);
249 if (video_register_device(&dev
->vdev
, VFL_TYPE_RADIO
, radio_nr
) < 0)
252 v4l2_info(v4l2_dev
, "Mirosound PCM20 Radio tuner\n");
255 v4l2_device_unregister(v4l2_dev
);
259 MODULE_AUTHOR("Ruurd Reitsma, Krzysztof Helt");
260 MODULE_DESCRIPTION("A driver for the Miro PCM20 radio card.");
261 MODULE_LICENSE("GPL");
263 static void __exit
pcm20_cleanup(void)
265 struct pcm20
*dev
= &pcm20_card
;
267 video_unregister_device(&dev
->vdev
);
268 v4l2_device_unregister(&dev
->v4l2_dev
);
271 module_init(pcm20_init
);
272 module_exit(pcm20_cleanup
);