diff options
| -rw-r--r-- | drivers/media/radio/Kconfig | 18 | ||||
| -rw-r--r-- | drivers/media/radio/Makefile | 1 | ||||
| -rw-r--r-- | drivers/media/radio/radio-miropcm20.c | 270 |
3 files changed, 289 insertions, 0 deletions
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index a87a477c87f2..b134553eb3b5 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig | |||
| @@ -195,6 +195,24 @@ config RADIO_MAESTRO | |||
| 195 | To compile this driver as a module, choose M here: the | 195 | To compile this driver as a module, choose M here: the |
| 196 | module will be called radio-maestro. | 196 | module will be called radio-maestro. |
| 197 | 197 | ||
| 198 | config RADIO_MIROPCM20 | ||
| 199 | tristate "miroSOUND PCM20 radio" | ||
| 200 | depends on ISA && VIDEO_V4L2 | ||
| 201 | select SND_MIRO | ||
| 202 | ---help--- | ||
| 203 | Choose Y here if you have this FM radio card. You also need to enable | ||
| 204 | the ALSA sound system. This choice automatically selects the ALSA | ||
| 205 | sound card driver "Miro miroSOUND PCM1pro/PCM12/PCM20radio" as this | ||
| 206 | is required for the radio-miropcm20. | ||
| 207 | |||
| 208 | In order to control your radio card, you will need to use programs | ||
| 209 | that are compatible with the Video For Linux API. Information on | ||
| 210 | this API and pointers to "v4l" programs may be found at | ||
| 211 | <file:Documentation/video4linux/API.html>. | ||
| 212 | |||
| 213 | To compile this driver as a module, choose M here: the | ||
| 214 | module will be called radio-miropcm20. | ||
| 215 | |||
| 198 | config RADIO_SF16FMI | 216 | config RADIO_SF16FMI |
| 199 | tristate "SF16FMI Radio" | 217 | tristate "SF16FMI Radio" |
| 200 | depends on ISA && VIDEO_V4L2 | 218 | depends on ISA && VIDEO_V4L2 |
diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index 2a1be3bf4f7c..8a63d543ae41 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile | |||
| @@ -18,6 +18,7 @@ obj-$(CONFIG_RADIO_TRUST) += radio-trust.o | |||
| 18 | obj-$(CONFIG_I2C_SI4713) += si4713-i2c.o | 18 | obj-$(CONFIG_I2C_SI4713) += si4713-i2c.o |
| 19 | obj-$(CONFIG_RADIO_SI4713) += radio-si4713.o | 19 | obj-$(CONFIG_RADIO_SI4713) += radio-si4713.o |
| 20 | obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o | 20 | obj-$(CONFIG_RADIO_MAESTRO) += radio-maestro.o |
| 21 | obj-$(CONFIG_RADIO_MIROPCM20) += radio-miropcm20.o | ||
| 21 | obj-$(CONFIG_USB_DSBR) += dsbr100.o | 22 | obj-$(CONFIG_USB_DSBR) += dsbr100.o |
| 22 | obj-$(CONFIG_RADIO_SI470X) += si470x/ | 23 | obj-$(CONFIG_RADIO_SI470X) += si470x/ |
| 23 | obj-$(CONFIG_USB_MR800) += radio-mr800.o | 24 | obj-$(CONFIG_USB_MR800) += radio-mr800.o |
diff --git a/drivers/media/radio/radio-miropcm20.c b/drivers/media/radio/radio-miropcm20.c new file mode 100644 index 000000000000..4ff885445fd4 --- /dev/null +++ b/drivers/media/radio/radio-miropcm20.c | |||
| @@ -0,0 +1,270 @@ | |||
| 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 | ||
| 6 | * ALSA driver. | ||
| 7 | * Look there for further info... | ||
| 8 | */ | ||
| 9 | |||
| 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 | ||
| 13 | */ | ||
| 14 | |||
| 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> | ||
| 21 | |||
| 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)"); | ||
| 25 | |||
| 26 | static int mono; | ||
| 27 | module_param(mono, bool, 0); | ||
| 28 | MODULE_PARM_DESC(mono, "Force tuner into mono mode."); | ||
| 29 | |||
| 30 | struct pcm20 { | ||
| 31 | struct v4l2_device v4l2_dev; | ||
| 32 | struct video_device vdev; | ||
| 33 | unsigned long freq; | ||
| 34 | int muted; | ||
| 35 | struct snd_miro_aci *aci; | ||
| 36 | }; | ||
| 37 | |||
| 38 | static struct pcm20 pcm20_card = { | ||
| 39 | .freq = 87*16000, | ||
| 40 | .muted = 1, | ||
| 41 | }; | ||
| 42 | |||
| 43 | static int pcm20_mute(struct pcm20 *dev, unsigned char mute) | ||
| 44 | { | ||
| 45 | dev->muted = mute; | ||
| 46 | return snd_aci_cmd(dev->aci, ACI_SET_TUNERMUTE, mute, -1); | ||
| 47 | } | ||
| 48 | |||
| 49 | static int pcm20_stereo(struct pcm20 *dev, unsigned char stereo) | ||
| 50 | { | ||
| 51 | return snd_aci_cmd(dev->aci, ACI_SET_TUNERMONO, !stereo, -1); | ||
| 52 | } | ||
| 53 | |||
| 54 | static int pcm20_setfreq(struct pcm20 *dev, unsigned long freq) | ||
| 55 | { | ||
| 56 | unsigned char freql; | ||
| 57 | unsigned char freqh; | ||
| 58 | struct snd_miro_aci *aci = dev->aci; | ||
| 59 | |||
| 60 | dev->freq = freq; | ||
| 61 | |||
| 62 | freq /= 160; | ||
| 63 | if (!(aci->aci_version == 0x07 || aci->aci_version >= 0xb0)) | ||
| 64 | freq /= 10; /* I don't know exactly which version | ||
| 65 | * needs this hack */ | ||
| 66 | freql = freq & 0xff; | ||
| 67 | freqh = freq >> 8; | ||
| 68 | |||
| 69 | pcm20_stereo(dev, !mono); | ||
| 70 | return snd_aci_cmd(aci, ACI_WRITE_TUNE, freql, freqh); | ||
| 71 | } | ||
| 72 | |||
| 73 | static const struct v4l2_file_operations pcm20_fops = { | ||
| 74 | .owner = THIS_MODULE, | ||
| 75 | .ioctl = video_ioctl2, | ||
| 76 | }; | ||
| 77 | |||
| 78 | static int vidioc_querycap(struct file *file, void *priv, | ||
| 79 | struct v4l2_capability *v) | ||
| 80 | { | ||
| 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)); | ||
| 84 | v->version = 0x1; | ||
| 85 | v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; | ||
| 86 | return 0; | ||
| 87 | } | ||
| 88 | |||
| 89 | static int vidioc_g_tuner(struct file *file, void *priv, | ||
| 90 | struct v4l2_tuner *v) | ||
| 91 | { | ||
| 92 | if (v->index) /* Only 1 tuner */ | ||
| 93 | return -EINVAL; | ||
| 94 | strlcpy(v->name, "FM", sizeof(v->name)); | ||
| 95 | v->type = V4L2_TUNER_RADIO; | ||
| 96 | v->rangelow = 87*16000; | ||
| 97 | v->rangehigh = 108*16000; | ||
| 98 | v->signal = 0xffff; | ||
| 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; | ||
| 102 | return 0; | ||
| 103 | } | ||
| 104 | |||
| 105 | static int vidioc_s_tuner(struct file *file, void *priv, | ||
| 106 | struct v4l2_tuner *v) | ||
| 107 | { | ||
| 108 | return v->index ? -EINVAL : 0; | ||
| 109 | } | ||
| 110 | |||
| 111 | static int vidioc_g_frequency(struct file *file, void *priv, | ||
| 112 | struct v4l2_frequency *f) | ||
| 113 | { | ||
| 114 | struct pcm20 *dev = video_drvdata(file); | ||
| 115 | |||
| 116 | if (f->tuner != 0) | ||
| 117 | return -EINVAL; | ||
| 118 | |||
| 119 | f->type = V4L2_TUNER_RADIO; | ||
| 120 | f->frequency = dev->freq; | ||
| 121 | return 0; | ||
| 122 | } | ||
| 123 | |||
| 124 | |||
| 125 | static int vidioc_s_frequency(struct file *file, void *priv, | ||
| 126 | struct v4l2_frequency *f) | ||
| 127 | { | ||
| 128 | struct pcm20 *dev = video_drvdata(file); | ||
| 129 | |||
| 130 | if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) | ||
| 131 | return -EINVAL; | ||
| 132 | |||
| 133 | dev->freq = f->frequency; | ||
| 134 | pcm20_setfreq(dev, f->frequency); | ||
| 135 | return 0; | ||
| 136 | } | ||
| 137 | |||
| 138 | static int vidioc_queryctrl(struct file *file, void *priv, | ||
| 139 | struct v4l2_queryctrl *qc) | ||
| 140 | { | ||
| 141 | switch (qc->id) { | ||
| 142 | case V4L2_CID_AUDIO_MUTE: | ||
| 143 | return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); | ||
| 144 | } | ||
| 145 | return -EINVAL; | ||
| 146 | } | ||
| 147 | |||
| 148 | static int vidioc_g_ctrl(struct file *file, void *priv, | ||
| 149 | struct v4l2_control *ctrl) | ||
| 150 | { | ||
| 151 | struct pcm20 *dev = video_drvdata(file); | ||
| 152 | |||
| 153 | switch (ctrl->id) { | ||
| 154 | case V4L2_CID_AUDIO_MUTE: | ||
| 155 | ctrl->value = dev->muted; | ||
| 156 | break; | ||
| 157 | default: | ||
| 158 | return -EINVAL; | ||
| 159 | } | ||
| 160 | return 0; | ||
| 161 | } | ||
| 162 | |||
| 163 | static int vidioc_s_ctrl(struct file *file, void *priv, | ||
| 164 | struct v4l2_control *ctrl) | ||
| 165 | { | ||
| 166 | struct pcm20 *dev = video_drvdata(file); | ||
| 167 | |||
| 168 | switch (ctrl->id) { | ||
| 169 | case V4L2_CID_AUDIO_MUTE: | ||
| 170 | pcm20_mute(dev, ctrl->value); | ||
| 171 | break; | ||
| 172 | default: | ||
| 173 | return -EINVAL; | ||
| 174 | } | ||
| 175 | return 0; | ||
| 176 | } | ||
| 177 | |||
| 178 | static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) | ||
| 179 | { | ||
| 180 | *i = 0; | ||
| 181 | return 0; | ||
| 182 | } | ||
| 183 | |||
| 184 | static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) | ||
| 185 | { | ||
| 186 | return i ? -EINVAL : 0; | ||
| 187 | } | ||
| 188 | |||
| 189 | static int vidioc_g_audio(struct file *file, void *priv, | ||
| 190 | struct v4l2_audio *a) | ||
| 191 | { | ||
| 192 | a->index = 0; | ||
| 193 | strlcpy(a->name, "Radio", sizeof(a->name)); | ||
| 194 | a->capability = V4L2_AUDCAP_STEREO; | ||
| 195 | return 0; | ||
| 196 | } | ||
| 197 | |||
| 198 | static int vidioc_s_audio(struct file *file, void *priv, | ||
| 199 | struct v4l2_audio *a) | ||
| 200 | { | ||
| 201 | return a->index ? -EINVAL : 0; | ||
| 202 | } | ||
| 203 | |||
| 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, | ||
| 217 | }; | ||
| 218 | |||
| 219 | static int __init pcm20_init(void) | ||
| 220 | { | ||
| 221 | struct pcm20 *dev = &pcm20_card; | ||
| 222 | struct v4l2_device *v4l2_dev = &dev->v4l2_dev; | ||
| 223 | int res; | ||
| 224 | |||
| 225 | dev->aci = snd_aci_get_aci(); | ||
| 226 | if (dev->aci == NULL) { | ||
| 227 | v4l2_err(v4l2_dev, | ||
| 228 | "you must load the snd-miro driver first!\n"); | ||
| 229 | return -ENODEV; | ||
| 230 | } | ||
| 231 | strlcpy(v4l2_dev->name, "miropcm20", sizeof(v4l2_dev->name)); | ||
| 232 | |||
| 233 | |||
| 234 | res = v4l2_device_register(NULL, v4l2_dev); | ||
| 235 | if (res < 0) { | ||
| 236 | v4l2_err(v4l2_dev, "could not register v4l2_device\n"); | ||
| 237 | return -EINVAL; | ||
| 238 | } | ||
| 239 | |||
| 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); | ||
| 246 | |||
| 247 | if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) | ||
| 248 | goto fail; | ||
| 249 | |||
| 250 | v4l2_info(v4l2_dev, "Mirosound PCM20 Radio tuner\n"); | ||
| 251 | return 0; | ||
| 252 | fail: | ||
| 253 | v4l2_device_unregister(v4l2_dev); | ||
| 254 | return -EINVAL; | ||
| 255 | } | ||
| 256 | |||
| 257 | MODULE_AUTHOR("Ruurd Reitsma, Krzysztof Helt"); | ||
| 258 | MODULE_DESCRIPTION("A driver for the Miro PCM20 radio card."); | ||
| 259 | MODULE_LICENSE("GPL"); | ||
| 260 | |||
| 261 | static void __exit pcm20_cleanup(void) | ||
| 262 | { | ||
| 263 | struct pcm20 *dev = &pcm20_card; | ||
| 264 | |||
| 265 | video_unregister_device(&dev->vdev); | ||
| 266 | v4l2_device_unregister(&dev->v4l2_dev); | ||
| 267 | } | ||
| 268 | |||
| 269 | module_init(pcm20_init); | ||
| 270 | module_exit(pcm20_cleanup); | ||
