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 <media/v4l2-ctrls.h>
21 #include <media/v4l2-fh.h>
22 #include <media/v4l2-event.h>
23 #include <sound/aci.h>
25 static int radio_nr
= -1;
26 module_param(radio_nr
, int, 0);
27 MODULE_PARM_DESC(radio_nr
, "Set radio device number (/dev/radioX). Default: -1 (autodetect)");
30 struct v4l2_device v4l2_dev
;
31 struct video_device vdev
;
32 struct v4l2_ctrl_handler ctrl_handler
;
35 struct snd_miro_aci
*aci
;
39 static struct pcm20 pcm20_card
= {
41 .audmode
= V4L2_TUNER_MODE_STEREO
,
44 static int pcm20_setfreq(struct pcm20
*dev
, unsigned long freq
)
48 struct snd_miro_aci
*aci
= dev
->aci
;
51 if (!(aci
->aci_version
== 0x07 || aci
->aci_version
>= 0xb0))
52 freq
/= 10; /* I don't know exactly which version
57 return snd_aci_cmd(aci
, ACI_WRITE_TUNE
, freql
, freqh
);
60 static const struct v4l2_file_operations pcm20_fops
= {
63 .poll
= v4l2_ctrl_poll
,
64 .release
= v4l2_fh_release
,
65 .unlocked_ioctl
= video_ioctl2
,
68 static int vidioc_querycap(struct file
*file
, void *priv
,
69 struct v4l2_capability
*v
)
71 struct pcm20
*dev
= video_drvdata(file
);
73 strlcpy(v
->driver
, "Miro PCM20", sizeof(v
->driver
));
74 strlcpy(v
->card
, "Miro PCM20", sizeof(v
->card
));
75 snprintf(v
->bus_info
, sizeof(v
->bus_info
), "ISA:%s", dev
->v4l2_dev
.name
);
76 v
->device_caps
= V4L2_CAP_TUNER
| V4L2_CAP_RADIO
;
77 v
->capabilities
= v
->device_caps
| V4L2_CAP_DEVICE_CAPS
;
81 static int vidioc_g_tuner(struct file
*file
, void *priv
,
84 struct pcm20
*dev
= video_drvdata(file
);
89 strlcpy(v
->name
, "FM", sizeof(v
->name
));
90 v
->type
= V4L2_TUNER_RADIO
;
91 v
->rangelow
= 87*16000;
92 v
->rangehigh
= 108*16000;
93 res
= snd_aci_cmd(dev
->aci
, ACI_READ_TUNERSTATION
, -1, -1);
94 v
->signal
= (res
& 0x80) ? 0 : 0xffff;
95 /* Note: stereo detection does not work if the audio is muted,
96 it will default to mono in that case. */
97 res
= snd_aci_cmd(dev
->aci
, ACI_READ_TUNERSTEREO
, -1, -1);
98 v
->rxsubchans
= (res
& 0x40) ? V4L2_TUNER_SUB_MONO
:
99 V4L2_TUNER_SUB_STEREO
;
100 v
->capability
= V4L2_TUNER_CAP_LOW
| V4L2_TUNER_CAP_STEREO
;
101 v
->audmode
= dev
->audmode
;
105 static int vidioc_s_tuner(struct file
*file
, void *priv
,
106 const struct v4l2_tuner
*v
)
108 struct pcm20
*dev
= video_drvdata(file
);
112 if (v
->audmode
> V4L2_TUNER_MODE_STEREO
)
113 dev
->audmode
= V4L2_TUNER_MODE_STEREO
;
115 dev
->audmode
= v
->audmode
;
116 snd_aci_cmd(dev
->aci
, ACI_SET_TUNERMONO
,
117 dev
->audmode
== V4L2_TUNER_MODE_MONO
, -1);
121 static int vidioc_g_frequency(struct file
*file
, void *priv
,
122 struct v4l2_frequency
*f
)
124 struct pcm20
*dev
= video_drvdata(file
);
129 f
->type
= V4L2_TUNER_RADIO
;
130 f
->frequency
= dev
->freq
;
135 static int vidioc_s_frequency(struct file
*file
, void *priv
,
136 const struct v4l2_frequency
*f
)
138 struct pcm20
*dev
= video_drvdata(file
);
140 if (f
->tuner
!= 0 || f
->type
!= V4L2_TUNER_RADIO
)
143 dev
->freq
= clamp_t(u32
, f
->frequency
, 87 * 16000U, 108 * 16000U);
144 pcm20_setfreq(dev
, dev
->freq
);
148 static int pcm20_s_ctrl(struct v4l2_ctrl
*ctrl
)
150 struct pcm20
*dev
= container_of(ctrl
->handler
, struct pcm20
, ctrl_handler
);
153 case V4L2_CID_AUDIO_MUTE
:
154 snd_aci_cmd(dev
->aci
, ACI_SET_TUNERMUTE
, ctrl
->val
, -1);
160 static const struct v4l2_ioctl_ops pcm20_ioctl_ops
= {
161 .vidioc_querycap
= vidioc_querycap
,
162 .vidioc_g_tuner
= vidioc_g_tuner
,
163 .vidioc_s_tuner
= vidioc_s_tuner
,
164 .vidioc_g_frequency
= vidioc_g_frequency
,
165 .vidioc_s_frequency
= vidioc_s_frequency
,
166 .vidioc_log_status
= v4l2_ctrl_log_status
,
167 .vidioc_subscribe_event
= v4l2_ctrl_subscribe_event
,
168 .vidioc_unsubscribe_event
= v4l2_event_unsubscribe
,
171 static const struct v4l2_ctrl_ops pcm20_ctrl_ops
= {
172 .s_ctrl
= pcm20_s_ctrl
,
175 static int __init
pcm20_init(void)
177 struct pcm20
*dev
= &pcm20_card
;
178 struct v4l2_device
*v4l2_dev
= &dev
->v4l2_dev
;
179 struct v4l2_ctrl_handler
*hdl
;
182 dev
->aci
= snd_aci_get_aci();
183 if (dev
->aci
== NULL
) {
185 "you must load the snd-miro driver first!\n");
188 strlcpy(v4l2_dev
->name
, "radio-miropcm20", sizeof(v4l2_dev
->name
));
189 mutex_init(&dev
->lock
);
191 res
= v4l2_device_register(NULL
, v4l2_dev
);
193 v4l2_err(v4l2_dev
, "could not register v4l2_device\n");
197 hdl
= &dev
->ctrl_handler
;
198 v4l2_ctrl_handler_init(hdl
, 1);
199 v4l2_ctrl_new_std(hdl
, &pcm20_ctrl_ops
,
200 V4L2_CID_AUDIO_MUTE
, 0, 1, 1, 1);
201 v4l2_dev
->ctrl_handler
= hdl
;
204 v4l2_err(v4l2_dev
, "Could not register control\n");
207 strlcpy(dev
->vdev
.name
, v4l2_dev
->name
, sizeof(dev
->vdev
.name
));
208 dev
->vdev
.v4l2_dev
= v4l2_dev
;
209 dev
->vdev
.fops
= &pcm20_fops
;
210 dev
->vdev
.ioctl_ops
= &pcm20_ioctl_ops
;
211 dev
->vdev
.release
= video_device_release_empty
;
212 dev
->vdev
.lock
= &dev
->lock
;
213 set_bit(V4L2_FL_USE_FH_PRIO
, &dev
->vdev
.flags
);
214 video_set_drvdata(&dev
->vdev
, dev
);
215 snd_aci_cmd(dev
->aci
, ACI_SET_TUNERMONO
,
216 dev
->audmode
== V4L2_TUNER_MODE_MONO
, -1);
217 pcm20_setfreq(dev
, dev
->freq
);
219 if (video_register_device(&dev
->vdev
, VFL_TYPE_RADIO
, radio_nr
) < 0)
222 v4l2_info(v4l2_dev
, "Mirosound PCM20 Radio tuner\n");
225 v4l2_ctrl_handler_free(hdl
);
226 v4l2_device_unregister(v4l2_dev
);
230 MODULE_AUTHOR("Ruurd Reitsma, Krzysztof Helt");
231 MODULE_DESCRIPTION("A driver for the Miro PCM20 radio card.");
232 MODULE_LICENSE("GPL");
234 static void __exit
pcm20_cleanup(void)
236 struct pcm20
*dev
= &pcm20_card
;
238 video_unregister_device(&dev
->vdev
);
239 snd_aci_cmd(dev
->aci
, ACI_SET_TUNERMUTE
, 1, -1);
240 v4l2_ctrl_handler_free(&dev
->ctrl_handler
);
241 v4l2_device_unregister(&dev
->v4l2_dev
);
244 module_init(pcm20_init
);
245 module_exit(pcm20_cleanup
);