linux/drivers/media/radio/radio-sf16fmi.c
<<
>>
Prefs
   1/* SF16FMI radio driver for Linux radio support
   2 * heavily based on rtrack driver...
   3 * (c) 1997 M. Kirkwood
   4 * (c) 1998 Petr Vandrovec, vandrove@vc.cvut.cz
   5 *
   6 * Fitted to new interface by Alan Cox <alan@lxorguk.ukuu.org.uk>
   7 * Made working and cleaned up functions <mikael.hedin@irf.se>
   8 * Support for ISAPnP by Ladislav Michl <ladis@psi.cz>
   9 *
  10 * Notes on the hardware
  11 *
  12 *  Frequency control is done digitally -- ie out(port,encodefreq(95.8));
  13 *  No volume control - only mute/unmute - you have to use line volume
  14 *  control on SB-part of SF16FMI
  15 *
  16 * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
  17 */
  18
  19#include <linux/version.h>
  20#include <linux/kernel.h>       /* __setup                      */
  21#include <linux/module.h>       /* Modules                      */
  22#include <linux/init.h>         /* Initdata                     */
  23#include <linux/ioport.h>       /* request_region               */
  24#include <linux/delay.h>        /* udelay                       */
  25#include <linux/isapnp.h>
  26#include <linux/mutex.h>
  27#include <linux/videodev2.h>    /* kernel radio structs         */
  28#include <linux/io.h>           /* outb, outb_p                 */
  29#include <media/v4l2-device.h>
  30#include <media/v4l2-ioctl.h>
  31
  32MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood");
  33MODULE_DESCRIPTION("A driver for the SF16MI radio.");
  34MODULE_LICENSE("GPL");
  35
  36static int io = -1;
  37static int radio_nr = -1;
  38
  39module_param(io, int, 0);
  40MODULE_PARM_DESC(io, "I/O address of the SF16MI card (0x284 or 0x384)");
  41module_param(radio_nr, int, 0);
  42
  43#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
  44
  45struct fmi
  46{
  47        struct v4l2_device v4l2_dev;
  48        struct video_device vdev;
  49        int io;
  50        int curvol; /* 1 or 0 */
  51        unsigned long curfreq; /* freq in kHz */
  52        struct mutex lock;
  53};
  54
  55static struct fmi fmi_card;
  56static struct pnp_dev *dev;
  57
  58/* freq is in 1/16 kHz to internal number, hw precision is 50 kHz */
  59/* It is only useful to give freq in interval of 800 (=0.05Mhz),
  60 * other bits will be truncated, e.g 92.7400016 -> 92.7, but
  61 * 92.7400017 -> 92.75
  62 */
  63#define RSF16_ENCODE(x) ((x) / 800 + 214)
  64#define RSF16_MINFREQ (87 * 16000)
  65#define RSF16_MAXFREQ (108 * 16000)
  66
  67static void outbits(int bits, unsigned int data, int io)
  68{
  69        while (bits--) {
  70                if (data & 1) {
  71                        outb(5, io);
  72                        udelay(6);
  73                        outb(7, io);
  74                        udelay(6);
  75                } else {
  76                        outb(1, io);
  77                        udelay(6);
  78                        outb(3, io);
  79                        udelay(6);
  80                }
  81                data >>= 1;
  82        }
  83}
  84
  85static inline void fmi_mute(struct fmi *fmi)
  86{
  87        mutex_lock(&fmi->lock);
  88        outb(0x00, fmi->io);
  89        mutex_unlock(&fmi->lock);
  90}
  91
  92static inline void fmi_unmute(struct fmi *fmi)
  93{
  94        mutex_lock(&fmi->lock);
  95        outb(0x08, fmi->io);
  96        mutex_unlock(&fmi->lock);
  97}
  98
  99static inline int fmi_setfreq(struct fmi *fmi, unsigned long freq)
 100{
 101        mutex_lock(&fmi->lock);
 102        fmi->curfreq = freq;
 103
 104        outbits(16, RSF16_ENCODE(freq), fmi->io);
 105        outbits(8, 0xC0, fmi->io);
 106        msleep(143);            /* was schedule_timeout(HZ/7) */
 107        mutex_unlock(&fmi->lock);
 108        if (fmi->curvol)
 109                fmi_unmute(fmi);
 110        return 0;
 111}
 112
 113static inline int fmi_getsigstr(struct fmi *fmi)
 114{
 115        int val;
 116        int res;
 117
 118        mutex_lock(&fmi->lock);
 119        val = fmi->curvol ? 0x08 : 0x00;        /* unmute/mute */
 120        outb(val, fmi->io);
 121        outb(val | 0x10, fmi->io);
 122        msleep(143);            /* was schedule_timeout(HZ/7) */
 123        res = (int)inb(fmi->io + 1);
 124        outb(val, fmi->io);
 125
 126        mutex_unlock(&fmi->lock);
 127        return (res & 2) ? 0 : 0xFFFF;
 128}
 129
 130static int vidioc_querycap(struct file *file, void  *priv,
 131                                        struct v4l2_capability *v)
 132{
 133        strlcpy(v->driver, "radio-sf16fmi", sizeof(v->driver));
 134        strlcpy(v->card, "SF16-FMx radio", sizeof(v->card));
 135        strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
 136        v->version = RADIO_VERSION;
 137        v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
 138        return 0;
 139}
 140
 141static int vidioc_g_tuner(struct file *file, void *priv,
 142                                        struct v4l2_tuner *v)
 143{
 144        struct fmi *fmi = video_drvdata(file);
 145
 146        if (v->index > 0)
 147                return -EINVAL;
 148
 149        strlcpy(v->name, "FM", sizeof(v->name));
 150        v->type = V4L2_TUNER_RADIO;
 151        v->rangelow = RSF16_MINFREQ;
 152        v->rangehigh = RSF16_MAXFREQ;
 153        v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
 154        v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW;
 155        v->audmode = V4L2_TUNER_MODE_STEREO;
 156        v->signal = fmi_getsigstr(fmi);
 157        return 0;
 158}
 159
 160static int vidioc_s_tuner(struct file *file, void *priv,
 161                                        struct v4l2_tuner *v)
 162{
 163        return v->index ? -EINVAL : 0;
 164}
 165
 166static int vidioc_s_frequency(struct file *file, void *priv,
 167                                        struct v4l2_frequency *f)
 168{
 169        struct fmi *fmi = video_drvdata(file);
 170
 171        if (f->frequency < RSF16_MINFREQ ||
 172                        f->frequency > RSF16_MAXFREQ)
 173                return -EINVAL;
 174        /* rounding in steps of 800 to match the freq
 175           that will be used */
 176        fmi_setfreq(fmi, (f->frequency / 800) * 800);
 177        return 0;
 178}
 179
 180static int vidioc_g_frequency(struct file *file, void *priv,
 181                                        struct v4l2_frequency *f)
 182{
 183        struct fmi *fmi = video_drvdata(file);
 184
 185        f->type = V4L2_TUNER_RADIO;
 186        f->frequency = fmi->curfreq;
 187        return 0;
 188}
 189
 190static int vidioc_queryctrl(struct file *file, void *priv,
 191                                        struct v4l2_queryctrl *qc)
 192{
 193        switch (qc->id) {
 194        case V4L2_CID_AUDIO_MUTE:
 195                return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
 196        }
 197        return -EINVAL;
 198}
 199
 200static int vidioc_g_ctrl(struct file *file, void *priv,
 201                                        struct v4l2_control *ctrl)
 202{
 203        struct fmi *fmi = video_drvdata(file);
 204
 205        switch (ctrl->id) {
 206        case V4L2_CID_AUDIO_MUTE:
 207                ctrl->value = fmi->curvol;
 208                return 0;
 209        }
 210        return -EINVAL;
 211}
 212
 213static int vidioc_s_ctrl(struct file *file, void *priv,
 214                                        struct v4l2_control *ctrl)
 215{
 216        struct fmi *fmi = video_drvdata(file);
 217
 218        switch (ctrl->id) {
 219        case V4L2_CID_AUDIO_MUTE:
 220                if (ctrl->value)
 221                        fmi_mute(fmi);
 222                else
 223                        fmi_unmute(fmi);
 224                fmi->curvol = ctrl->value;
 225                return 0;
 226        }
 227        return -EINVAL;
 228}
 229
 230static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
 231{
 232        *i = 0;
 233        return 0;
 234}
 235
 236static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
 237{
 238        return i ? -EINVAL : 0;
 239}
 240
 241static int vidioc_g_audio(struct file *file, void *priv,
 242                                        struct v4l2_audio *a)
 243{
 244        a->index = 0;
 245        strlcpy(a->name, "Radio", sizeof(a->name));
 246        a->capability = V4L2_AUDCAP_STEREO;
 247        return 0;
 248}
 249
 250static int vidioc_s_audio(struct file *file, void *priv,
 251                                        struct v4l2_audio *a)
 252{
 253        return a->index ? -EINVAL : 0;
 254}
 255
 256static const struct v4l2_file_operations fmi_fops = {
 257        .owner          = THIS_MODULE,
 258        .ioctl          = video_ioctl2,
 259};
 260
 261static const struct v4l2_ioctl_ops fmi_ioctl_ops = {
 262        .vidioc_querycap    = vidioc_querycap,
 263        .vidioc_g_tuner     = vidioc_g_tuner,
 264        .vidioc_s_tuner     = vidioc_s_tuner,
 265        .vidioc_g_audio     = vidioc_g_audio,
 266        .vidioc_s_audio     = vidioc_s_audio,
 267        .vidioc_g_input     = vidioc_g_input,
 268        .vidioc_s_input     = vidioc_s_input,
 269        .vidioc_g_frequency = vidioc_g_frequency,
 270        .vidioc_s_frequency = vidioc_s_frequency,
 271        .vidioc_queryctrl   = vidioc_queryctrl,
 272        .vidioc_g_ctrl      = vidioc_g_ctrl,
 273        .vidioc_s_ctrl      = vidioc_s_ctrl,
 274};
 275
 276/* ladis: this is my card. does any other types exist? */
 277static struct isapnp_device_id id_table[] __devinitdata = {
 278        {       ISAPNP_ANY_ID, ISAPNP_ANY_ID,
 279                ISAPNP_VENDOR('M','F','R'), ISAPNP_FUNCTION(0xad10), 0},
 280        {       ISAPNP_CARD_END, },
 281};
 282
 283MODULE_DEVICE_TABLE(isapnp, id_table);
 284
 285static int __init isapnp_fmi_probe(void)
 286{
 287        int i = 0;
 288
 289        while (id_table[i].card_vendor != 0 && dev == NULL) {
 290                dev = pnp_find_dev(NULL, id_table[i].vendor,
 291                                   id_table[i].function, NULL);
 292                i++;
 293        }
 294
 295        if (!dev)
 296                return -ENODEV;
 297        if (pnp_device_attach(dev) < 0)
 298                return -EAGAIN;
 299        if (pnp_activate_dev(dev) < 0) {
 300                printk(KERN_ERR "radio-sf16fmi: PnP configure failed (out of resources?)\n");
 301                pnp_device_detach(dev);
 302                return -ENOMEM;
 303        }
 304        if (!pnp_port_valid(dev, 0)) {
 305                pnp_device_detach(dev);
 306                return -ENODEV;
 307        }
 308
 309        i = pnp_port_start(dev, 0);
 310        printk(KERN_INFO "radio-sf16fmi: PnP reports card at %#x\n", i);
 311
 312        return i;
 313}
 314
 315static int __init fmi_init(void)
 316{
 317        struct fmi *fmi = &fmi_card;
 318        struct v4l2_device *v4l2_dev = &fmi->v4l2_dev;
 319        int res;
 320
 321        if (io < 0)
 322                io = isapnp_fmi_probe();
 323        strlcpy(v4l2_dev->name, "sf16fmi", sizeof(v4l2_dev->name));
 324        fmi->io = io;
 325        if (fmi->io < 0) {
 326                v4l2_err(v4l2_dev, "No PnP card found.\n");
 327                return fmi->io;
 328        }
 329        if (!request_region(io, 2, "radio-sf16fmi")) {
 330                v4l2_err(v4l2_dev, "port 0x%x already in use\n", fmi->io);
 331                pnp_device_detach(dev);
 332                return -EBUSY;
 333        }
 334
 335        res = v4l2_device_register(NULL, v4l2_dev);
 336        if (res < 0) {
 337                release_region(fmi->io, 2);
 338                pnp_device_detach(dev);
 339                v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
 340                return res;
 341        }
 342
 343        strlcpy(fmi->vdev.name, v4l2_dev->name, sizeof(fmi->vdev.name));
 344        fmi->vdev.v4l2_dev = v4l2_dev;
 345        fmi->vdev.fops = &fmi_fops;
 346        fmi->vdev.ioctl_ops = &fmi_ioctl_ops;
 347        fmi->vdev.release = video_device_release_empty;
 348        video_set_drvdata(&fmi->vdev, fmi);
 349
 350        mutex_init(&fmi->lock);
 351
 352        if (video_register_device(&fmi->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
 353                v4l2_device_unregister(v4l2_dev);
 354                release_region(fmi->io, 2);
 355                pnp_device_detach(dev);
 356                return -EINVAL;
 357        }
 358
 359        v4l2_info(v4l2_dev, "card driver at 0x%x\n", fmi->io);
 360        /* mute card - prevents noisy bootups */
 361        fmi_mute(fmi);
 362        return 0;
 363}
 364
 365static void __exit fmi_exit(void)
 366{
 367        struct fmi *fmi = &fmi_card;
 368
 369        video_unregister_device(&fmi->vdev);
 370        v4l2_device_unregister(&fmi->v4l2_dev);
 371        release_region(fmi->io, 2);
 372        if (dev)
 373                pnp_device_detach(dev);
 374}
 375
 376module_init(fmi_init);
 377module_exit(fmi_exit);
 378