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
;
38 static struct pcm20 pcm20_card
= {
43 static int pcm20_mute(struct pcm20
*dev
, unsigned char mute
)
46 return snd_aci_cmd(dev
->aci
, ACI_SET_TUNERMUTE
, mute
, -1);
49 static int pcm20_stereo(struct pcm20
*dev
, unsigned char stereo
)
51 return snd_aci_cmd(dev
->aci
, ACI_SET_TUNERMONO
, !stereo
, -1);
54 static int pcm20_setfreq(struct pcm20
*dev
, unsigned long freq
)
58 struct snd_miro_aci
*aci
= dev
->aci
;
63 if (!(aci
->aci_version
== 0x07 || aci
->aci_version
>= 0xb0))
64 freq
/= 10; /* I don't know exactly which version
69 pcm20_stereo(dev
, !mono
);
70 return snd_aci_cmd(aci
, ACI_WRITE_TUNE
, freql
, freqh
);
73 static const struct v4l2_file_operations pcm20_fops
= {
75 .ioctl
= video_ioctl2
,
78 static int vidioc_querycap(struct file
*file
, void *priv
,
79 struct v4l2_capability
*v
)
81 strlcpy(v
->driver
, "Miro PCM20", sizeof(v
->driver
));
82 strlcpy(v
->card
, "Miro PCM20", sizeof(v
->card
));
83 strlcpy(v
->bus_info
, "ISA", sizeof(v
->bus_info
));
85 v
->capabilities
= V4L2_CAP_TUNER
| V4L2_CAP_RADIO
;
89 static int vidioc_g_tuner(struct file
*file
, void *priv
,
92 if (v
->index
) /* Only 1 tuner */
94 strlcpy(v
->name
, "FM", sizeof(v
->name
));
95 v
->type
= V4L2_TUNER_RADIO
;
96 v
->rangelow
= 87*16000;
97 v
->rangehigh
= 108*16000;
99 v
->rxsubchans
= V4L2_TUNER_SUB_MONO
| V4L2_TUNER_SUB_STEREO
;
100 v
->capability
= V4L2_TUNER_CAP_LOW
;
101 v
->audmode
= V4L2_TUNER_MODE_MONO
;
105 static int vidioc_s_tuner(struct file
*file
, void *priv
,
106 struct v4l2_tuner
*v
)
108 return v
->index
? -EINVAL
: 0;
111 static int vidioc_g_frequency(struct file
*file
, void *priv
,
112 struct v4l2_frequency
*f
)
114 struct pcm20
*dev
= video_drvdata(file
);
119 f
->type
= V4L2_TUNER_RADIO
;
120 f
->frequency
= dev
->freq
;
125 static int vidioc_s_frequency(struct file
*file
, void *priv
,
126 struct v4l2_frequency
*f
)
128 struct pcm20
*dev
= video_drvdata(file
);
130 if (f
->tuner
!= 0 || f
->type
!= V4L2_TUNER_RADIO
)
133 dev
->freq
= f
->frequency
;
134 pcm20_setfreq(dev
, f
->frequency
);
138 static int vidioc_queryctrl(struct file
*file
, void *priv
,
139 struct v4l2_queryctrl
*qc
)
142 case V4L2_CID_AUDIO_MUTE
:
143 return v4l2_ctrl_query_fill(qc
, 0, 1, 1, 1);
148 static int vidioc_g_ctrl(struct file
*file
, void *priv
,
149 struct v4l2_control
*ctrl
)
151 struct pcm20
*dev
= video_drvdata(file
);
154 case V4L2_CID_AUDIO_MUTE
:
155 ctrl
->value
= dev
->muted
;
163 static int vidioc_s_ctrl(struct file
*file
, void *priv
,
164 struct v4l2_control
*ctrl
)
166 struct pcm20
*dev
= video_drvdata(file
);
169 case V4L2_CID_AUDIO_MUTE
:
170 pcm20_mute(dev
, ctrl
->value
);
178 static int vidioc_g_input(struct file
*filp
, void *priv
, unsigned int *i
)
184 static int vidioc_s_input(struct file
*filp
, void *priv
, unsigned int i
)
186 return i
? -EINVAL
: 0;
189 static int vidioc_g_audio(struct file
*file
, void *priv
,
190 struct v4l2_audio
*a
)
193 strlcpy(a
->name
, "Radio", sizeof(a
->name
));
194 a
->capability
= V4L2_AUDCAP_STEREO
;
198 static int vidioc_s_audio(struct file
*file
, void *priv
,
199 struct v4l2_audio
*a
)
201 return a
->index
? -EINVAL
: 0;
204 static const struct v4l2_ioctl_ops pcm20_ioctl_ops
= {
205 .vidioc_querycap
= vidioc_querycap
,
206 .vidioc_g_tuner
= vidioc_g_tuner
,
207 .vidioc_s_tuner
= vidioc_s_tuner
,
208 .vidioc_g_frequency
= vidioc_g_frequency
,
209 .vidioc_s_frequency
= vidioc_s_frequency
,
210 .vidioc_queryctrl
= vidioc_queryctrl
,
211 .vidioc_g_ctrl
= vidioc_g_ctrl
,
212 .vidioc_s_ctrl
= vidioc_s_ctrl
,
213 .vidioc_g_audio
= vidioc_g_audio
,
214 .vidioc_s_audio
= vidioc_s_audio
,
215 .vidioc_g_input
= vidioc_g_input
,
216 .vidioc_s_input
= vidioc_s_input
,
219 static int __init
pcm20_init(void)
221 struct pcm20
*dev
= &pcm20_card
;
222 struct v4l2_device
*v4l2_dev
= &dev
->v4l2_dev
;
225 dev
->aci
= snd_aci_get_aci();
226 if (dev
->aci
== NULL
) {
228 "you must load the snd-miro driver first!\n");
231 strlcpy(v4l2_dev
->name
, "miropcm20", sizeof(v4l2_dev
->name
));
234 res
= v4l2_device_register(NULL
, v4l2_dev
);
236 v4l2_err(v4l2_dev
, "could not register v4l2_device\n");
240 strlcpy(dev
->vdev
.name
, v4l2_dev
->name
, sizeof(dev
->vdev
.name
));
241 dev
->vdev
.v4l2_dev
= v4l2_dev
;
242 dev
->vdev
.fops
= &pcm20_fops
;
243 dev
->vdev
.ioctl_ops
= &pcm20_ioctl_ops
;
244 dev
->vdev
.release
= video_device_release_empty
;
245 video_set_drvdata(&dev
->vdev
, dev
);
247 if (video_register_device(&dev
->vdev
, VFL_TYPE_RADIO
, radio_nr
) < 0)
250 v4l2_info(v4l2_dev
, "Mirosound PCM20 Radio tuner\n");
253 v4l2_device_unregister(v4l2_dev
);
257 MODULE_AUTHOR("Ruurd Reitsma, Krzysztof Helt");
258 MODULE_DESCRIPTION("A driver for the Miro PCM20 radio card.");
259 MODULE_LICENSE("GPL");
261 static void __exit
pcm20_cleanup(void)
263 struct pcm20
*dev
= &pcm20_card
;
265 video_unregister_device(&dev
->vdev
);
266 v4l2_device_unregister(&dev
->v4l2_dev
);
269 module_init(pcm20_init
);
270 module_exit(pcm20_cleanup
);