linux/drivers/media/radio/radio-sf16fmi.c
<<
>>
Prefs
   1/* SF16-FMI and SF16-FMP 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 SF16-FMI/SF16-FMP
  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 SF16-FMI and SF16-FMP 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 SF16-FMI or SF16-FMP 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        bool mute;
  51        unsigned long curfreq; /* freq in kHz */
  52        struct mutex lock;
  53};
  54
  55static struct fmi fmi_card;
  56static struct pnp_dev *dev;
  57bool pnp_attached;
  58
  59/* freq is in 1/16 kHz to internal number, hw precision is 50 kHz */
  60/* It is only useful to give freq in interval of 800 (=0.05Mhz),
  61 * other bits will be truncated, e.g 92.7400016 -> 92.7, but
  62 * 92.7400017 -> 92.75
  63 */
  64#define RSF16_ENCODE(x) ((x) / 800 + 214)
  65#define RSF16_MINFREQ (87 * 16000)
  66#define RSF16_MAXFREQ (108 * 16000)
  67
  68static void outbits(int bits, unsigned int data, int io)
  69{
  70        while (bits--) {
  71                if (data & 1) {
  72                        outb(5, io);
  73                        udelay(6);
  74                        outb(7, io);
  75                        udelay(6);
  76                } else {
  77                        outb(1, io);
  78                        udelay(6);
  79                        outb(3, io);
  80                        udelay(6);
  81                }
  82                data >>= 1;
  83        }
  84}
  85
  86static inline void fmi_mute(struct fmi *fmi)
  87{
  88        mutex_lock(&fmi->lock);
  89        outb(0x00, fmi->io);
  90        mutex_unlock(&fmi->lock);
  91}
  92
  93static inline void fmi_unmute(struct fmi *fmi)
  94{
  95        mutex_lock(&fmi->lock);
  96        outb(0x08, fmi->io);
  97        mutex_unlock(&fmi->lock);
  98}
  99
 100static inline int fmi_setfreq(struct fmi *fmi, unsigned long freq)
 101{
 102        mutex_lock(&fmi->lock);
 103        fmi->curfreq = freq;
 104
 105        outbits(16, RSF16_ENCODE(freq), fmi->io);
 106        outbits(8, 0xC0, fmi->io);
 107        msleep(143);            /* was schedule_timeout(HZ/7) */
 108        mutex_unlock(&fmi->lock);
 109        if (!fmi->mute)
 110                fmi_unmute(fmi);
 111        return 0;
 112}
 113
 114static inline int fmi_getsigstr(struct fmi *fmi)
 115{
 116        int val;
 117        int res;
 118
 119        mutex_lock(&fmi->lock);
 120        val = fmi->mute ? 0x00 : 0x08;  /* mute/unmute */
 121        outb(val, fmi->io);
 122        outb(val | 0x10, fmi->io);
 123        msleep(143);            /* was schedule_timeout(HZ/7) */
 124        res = (int)inb(fmi->io + 1);
 125        outb(val, fmi->io);
 126
 127        mutex_unlock(&fmi->lock);
 128        return (res & 2) ? 0 : 0xFFFF;
 129}
 130
 131static int vidioc_querycap(struct file *file, void  *priv,
 132                                        struct v4l2_capability *v)
 133{
 134        strlcpy(v->driver, "radio-sf16fmi", sizeof(v->driver));
 135        strlcpy(v->card, "SF16-FMx radio", sizeof(v->card));
 136        strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
 137        v->version = RADIO_VERSION;
 138        v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
 139        return 0;
 140}
 141
 142static int vidioc_g_tuner(struct file *file, void *priv,
 143                                        struct v4l2_tuner *v)
 144{
 145        struct fmi *fmi = video_drvdata(file);
 146
 147        if (v->index > 0)
 148                return -EINVAL;
 149
 150        strlcpy(v->name, "FM", sizeof(v->name));
 151        v->type = V4L2_TUNER_RADIO;
 152        v->rangelow = RSF16_MINFREQ;
 153        v->rangehigh = RSF16_MAXFREQ;
 154        v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
 155        v->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LOW;
 156        v->audmode = V4L2_TUNER_MODE_STEREO;
 157        v->signal = fmi_getsigstr(fmi);
 158        return 0;
 159}
 160
 161static int vidioc_s_tuner(struct file *file, void *priv,
 162                                        struct v4l2_tuner *v)
 163{
 164        return v->index ? -EINVAL : 0;
 165}
 166
 167static int vidioc_s_frequency(struct file *file, void *priv,
 168                                        struct v4l2_frequency *f)
 169{
 170        struct fmi *fmi = video_drvdata(file);
 171
 172        if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
 173                return -EINVAL;
 174        if (f->frequency < RSF16_MINFREQ ||
 175                        f->frequency > RSF16_MAXFREQ)
 176                return -EINVAL;
 177        /* rounding in steps of 800 to match the freq
 178           that will be used */
 179        fmi_setfreq(fmi, (f->frequency / 800) * 800);
 180        return 0;
 181}
 182
 183static int vidioc_g_frequency(struct file *file, void *priv,
 184                                        struct v4l2_frequency *f)
 185{
 186        struct fmi *fmi = video_drvdata(file);
 187
 188        if (f->tuner != 0)
 189                return -EINVAL;
 190        f->type = V4L2_TUNER_RADIO;
 191        f->frequency = fmi->curfreq;
 192        return 0;
 193}
 194
 195static int vidioc_queryctrl(struct file *file, void *priv,
 196                                        struct v4l2_queryctrl *qc)
 197{
 198        switch (qc->id) {
 199        case V4L2_CID_AUDIO_MUTE:
 200                return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
 201        }
 202        return -EINVAL;
 203}
 204
 205static int vidioc_g_ctrl(struct file *file, void *priv,
 206                                        struct v4l2_control *ctrl)
 207{
 208        struct fmi *fmi = video_drvdata(file);
 209
 210        switch (ctrl->id) {
 211        case V4L2_CID_AUDIO_MUTE:
 212                ctrl->value = fmi->mute;
 213                return 0;
 214        }
 215        return -EINVAL;
 216}
 217
 218static int vidioc_s_ctrl(struct file *file, void *priv,
 219                                        struct v4l2_control *ctrl)
 220{
 221        struct fmi *fmi = video_drvdata(file);
 222
 223        switch (ctrl->id) {
 224        case V4L2_CID_AUDIO_MUTE:
 225                if (ctrl->value)
 226                        fmi_mute(fmi);
 227                else
 228                        fmi_unmute(fmi);
 229                fmi->mute = ctrl->value;
 230                return 0;
 231        }
 232        return -EINVAL;
 233}
 234
 235static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
 236{
 237        *i = 0;
 238        return 0;
 239}
 240
 241static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
 242{
 243        return i ? -EINVAL : 0;
 244}
 245
 246static int vidioc_g_audio(struct file *file, void *priv,
 247                                        struct v4l2_audio *a)
 248{
 249        a->index = 0;
 250        strlcpy(a->name, "Radio", sizeof(a->name));
 251        a->capability = V4L2_AUDCAP_STEREO;
 252        return 0;
 253}
 254
 255static int vidioc_s_audio(struct file *file, void *priv,
 256                                        struct v4l2_audio *a)
 257{
 258        return a->index ? -EINVAL : 0;
 259}
 260
 261static const struct v4l2_file_operations fmi_fops = {
 262        .owner          = THIS_MODULE,
 263        .ioctl          = video_ioctl2,
 264};
 265
 266static const struct v4l2_ioctl_ops fmi_ioctl_ops = {
 267        .vidioc_querycap    = vidioc_querycap,
 268        .vidioc_g_tuner     = vidioc_g_tuner,
 269        .vidioc_s_tuner     = vidioc_s_tuner,
 270        .vidioc_g_audio     = vidioc_g_audio,
 271        .vidioc_s_audio     = vidioc_s_audio,
 272        .vidioc_g_input     = vidioc_g_input,
 273        .vidioc_s_input     = vidioc_s_input,
 274        .vidioc_g_frequency = vidioc_g_frequency,
 275        .vidioc_s_frequency = vidioc_s_frequency,
 276        .vidioc_queryctrl   = vidioc_queryctrl,
 277        .vidioc_g_ctrl      = vidioc_g_ctrl,
 278        .vidioc_s_ctrl      = vidioc_s_ctrl,
 279};
 280
 281/* ladis: this is my card. does any other types exist? */
 282static struct isapnp_device_id id_table[] __devinitdata = {
 283        {       ISAPNP_ANY_ID, ISAPNP_ANY_ID,
 284                ISAPNP_VENDOR('M','F','R'), ISAPNP_FUNCTION(0xad10), 0},
 285        {       ISAPNP_CARD_END, },
 286};
 287
 288MODULE_DEVICE_TABLE(isapnp, id_table);
 289
 290static int __init isapnp_fmi_probe(void)
 291{
 292        int i = 0;
 293
 294        while (id_table[i].card_vendor != 0 && dev == NULL) {
 295                dev = pnp_find_dev(NULL, id_table[i].vendor,
 296                                   id_table[i].function, NULL);
 297                i++;
 298        }
 299
 300        if (!dev)
 301                return -ENODEV;
 302        if (pnp_device_attach(dev) < 0)
 303                return -EAGAIN;
 304        if (pnp_activate_dev(dev) < 0) {
 305                printk(KERN_ERR "radio-sf16fmi: PnP configure failed (out of resources?)\n");
 306                pnp_device_detach(dev);
 307                return -ENOMEM;
 308        }
 309        if (!pnp_port_valid(dev, 0)) {
 310                pnp_device_detach(dev);
 311                return -ENODEV;
 312        }
 313
 314        i = pnp_port_start(dev, 0);
 315        printk(KERN_INFO "radio-sf16fmi: PnP reports card at %#x\n", i);
 316
 317        return i;
 318}
 319
 320static int __init fmi_init(void)
 321{
 322        struct fmi *fmi = &fmi_card;
 323        struct v4l2_device *v4l2_dev = &fmi->v4l2_dev;
 324        int res, i;
 325        int probe_ports[] = { 0, 0x284, 0x384 };
 326
 327        if (io < 0) {
 328                for (i = 0; i < ARRAY_SIZE(probe_ports); i++) {
 329                        io = probe_ports[i];
 330                        if (io == 0) {
 331                                io = isapnp_fmi_probe();
 332                                if (io < 0)
 333                                        continue;
 334                                pnp_attached = 1;
 335                        }
 336                        if (!request_region(io, 2, "radio-sf16fmi")) {
 337                                if (pnp_attached)
 338                                        pnp_device_detach(dev);
 339                                io = -1;
 340                                continue;
 341                        }
 342                        if (pnp_attached ||
 343                            ((inb(io) & 0xf9) == 0xf9 && (inb(io) & 0x4) == 0))
 344                                break;
 345                        release_region(io, 2);
 346                        io = -1;
 347                }
 348        } else {
 349                if (!request_region(io, 2, "radio-sf16fmi")) {
 350                        printk(KERN_ERR "radio-sf16fmi: port %#x already in use\n", io);
 351                        return -EBUSY;
 352                }
 353                if (inb(io) == 0xff) {
 354                        printk(KERN_ERR "radio-sf16fmi: card not present at %#x\n", io);
 355                        release_region(io, 2);
 356                        return -ENODEV;
 357                }
 358        }
 359        if (io < 0) {
 360                printk(KERN_ERR "radio-sf16fmi: no cards found\n");
 361                return -ENODEV;
 362        }
 363
 364        strlcpy(v4l2_dev->name, "sf16fmi", sizeof(v4l2_dev->name));
 365        fmi->io = io;
 366
 367        res = v4l2_device_register(NULL, v4l2_dev);
 368        if (res < 0) {
 369                release_region(fmi->io, 2);
 370                if (pnp_attached)
 371                        pnp_device_detach(dev);
 372                v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
 373                return res;
 374        }
 375
 376        strlcpy(fmi->vdev.name, v4l2_dev->name, sizeof(fmi->vdev.name));
 377        fmi->vdev.v4l2_dev = v4l2_dev;
 378        fmi->vdev.fops = &fmi_fops;
 379        fmi->vdev.ioctl_ops = &fmi_ioctl_ops;
 380        fmi->vdev.release = video_device_release_empty;
 381        video_set_drvdata(&fmi->vdev, fmi);
 382
 383        mutex_init(&fmi->lock);
 384
 385        if (video_register_device(&fmi->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
 386                v4l2_device_unregister(v4l2_dev);
 387                release_region(fmi->io, 2);
 388                if (pnp_attached)
 389                        pnp_device_detach(dev);
 390                return -EINVAL;
 391        }
 392
 393        v4l2_info(v4l2_dev, "card driver at 0x%x\n", fmi->io);
 394        /* mute card - prevents noisy bootups */
 395        fmi_mute(fmi);
 396        return 0;
 397}
 398
 399static void __exit fmi_exit(void)
 400{
 401        struct fmi *fmi = &fmi_card;
 402
 403        video_unregister_device(&fmi->vdev);
 404        v4l2_device_unregister(&fmi->v4l2_dev);
 405        release_region(fmi->io, 2);
 406        if (dev && pnp_attached)
 407                pnp_device_detach(dev);
 408}
 409
 410module_init(fmi_init);
 411module_exit(fmi_exit);
 412
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.