linux/drivers/media/radio/radio-aztech.c
<<
>>
Prefs
   1/* radio-aztech.c - Aztech radio card driver for Linux 2.2
   2 *
   3 * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
   4 * Adapted to support the Video for Linux API by
   5 * Russell Kroll <rkroll@exploits.org>.  Based on original tuner code by:
   6 *
   7 * Quay Ly
   8 * Donald Song
   9 * Jason Lewis      (jlewis@twilight.vtc.vsc.edu)
  10 * Scott McGrath    (smcgrath@twilight.vtc.vsc.edu)
  11 * William McGrath  (wmcgrath@twilight.vtc.vsc.edu)
  12 *
  13 * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/
  14 * along with more information on the card itself.
  15 *
  16 * History:
  17 * 1999-02-24   Russell Kroll <rkroll@exploits.org>
  18 *              Fine tuning/VIDEO_TUNER_LOW
  19 *              Range expanded to 87-108 MHz (from 87.9-107.8)
  20 *
  21 * Notable changes from the original source:
  22 * - includes stripped down to the essentials
  23 * - for loops used as delays replaced with udelay()
  24 * - #defines removed, changed to static values
  25 * - tuning structure changed - no more character arrays, other changes
  26*/
  27
  28#include <linux/module.h>       /* Modules                      */
  29#include <linux/init.h>         /* Initdata                     */
  30#include <linux/ioport.h>       /* request_region               */
  31#include <linux/delay.h>        /* udelay                       */
  32#include <asm/io.h>             /* outb, outb_p                 */
  33#include <asm/uaccess.h>        /* copy to/from user            */
  34#include <linux/videodev2.h>    /* kernel radio structs         */
  35#include <media/v4l2-common.h>
  36#include <media/v4l2-ioctl.h>
  37
  38#include <linux/version.h>      /* for KERNEL_VERSION MACRO     */
  39#define RADIO_VERSION KERNEL_VERSION(0,0,2)
  40
  41static struct v4l2_queryctrl radio_qctrl[] = {
  42        {
  43                .id            = V4L2_CID_AUDIO_MUTE,
  44                .name          = "Mute",
  45                .minimum       = 0,
  46                .maximum       = 1,
  47                .default_value = 1,
  48                .type          = V4L2_CTRL_TYPE_BOOLEAN,
  49        },{
  50                .id            = V4L2_CID_AUDIO_VOLUME,
  51                .name          = "Volume",
  52                .minimum       = 0,
  53                .maximum       = 0xff,
  54                .step          = 1,
  55                .default_value = 0xff,
  56                .type          = V4L2_CTRL_TYPE_INTEGER,
  57        }
  58};
  59
  60/* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
  61
  62#ifndef CONFIG_RADIO_AZTECH_PORT
  63#define CONFIG_RADIO_AZTECH_PORT -1
  64#endif
  65
  66static int io = CONFIG_RADIO_AZTECH_PORT;
  67static int radio_nr = -1;
  68static int radio_wait_time = 1000;
  69static struct mutex lock;
  70
  71struct az_device
  72{
  73        unsigned long in_use;
  74        int curvol;
  75        unsigned long curfreq;
  76        int stereo;
  77};
  78
  79static int volconvert(int level)
  80{
  81        level>>=14;             /* Map 16bits down to 2 bit */
  82        level&=3;
  83
  84        /* convert to card-friendly values */
  85        switch (level)
  86        {
  87                case 0:
  88                        return 0;
  89                case 1:
  90                        return 1;
  91                case 2:
  92                        return 4;
  93                case 3:
  94                        return 5;
  95        }
  96        return 0;       /* Quieten gcc */
  97}
  98
  99static void send_0_byte (struct az_device *dev)
 100{
 101        udelay(radio_wait_time);
 102        outb_p(2+volconvert(dev->curvol), io);
 103        outb_p(64+2+volconvert(dev->curvol), io);
 104}
 105
 106static void send_1_byte (struct az_device *dev)
 107{
 108        udelay (radio_wait_time);
 109        outb_p(128+2+volconvert(dev->curvol), io);
 110        outb_p(128+64+2+volconvert(dev->curvol), io);
 111}
 112
 113static int az_setvol(struct az_device *dev, int vol)
 114{
 115        mutex_lock(&lock);
 116        outb (volconvert(vol), io);
 117        mutex_unlock(&lock);
 118        return 0;
 119}
 120
 121/* thanks to Michael Dwyer for giving me a dose of clues in
 122 * the signal strength department..
 123 *
 124 * This card has a stereo bit - bit 0 set = mono, not set = stereo
 125 * It also has a "signal" bit - bit 1 set = bad signal, not set = good
 126 *
 127 */
 128
 129static int az_getsigstr(struct az_device *dev)
 130{
 131        if (inb(io) & 2)        /* bit set = no signal present */
 132                return 0;
 133        return 1;               /* signal present */
 134}
 135
 136static int az_getstereo(struct az_device *dev)
 137{
 138        if (inb(io) & 1)        /* bit set = mono */
 139                return 0;
 140        return 1;               /* stereo */
 141}
 142
 143static int az_setfreq(struct az_device *dev, unsigned long frequency)
 144{
 145        int  i;
 146
 147        frequency += 171200;            /* Add 10.7 MHz IF              */
 148        frequency /= 800;               /* Convert to 50 kHz units      */
 149
 150        mutex_lock(&lock);
 151
 152        send_0_byte (dev);              /*  0: LSB of frequency       */
 153
 154        for (i = 0; i < 13; i++)        /*   : frequency bits (1-13)  */
 155                if (frequency & (1 << i))
 156                        send_1_byte (dev);
 157                else
 158                        send_0_byte (dev);
 159
 160        send_0_byte (dev);              /* 14: test bit - always 0    */
 161        send_0_byte (dev);              /* 15: test bit - always 0    */
 162        send_0_byte (dev);              /* 16: band data 0 - always 0 */
 163        if (dev->stereo)                /* 17: stereo (1 to enable)   */
 164                send_1_byte (dev);
 165        else
 166                send_0_byte (dev);
 167
 168        send_1_byte (dev);              /* 18: band data 1 - unknown  */
 169        send_0_byte (dev);              /* 19: time base - always 0   */
 170        send_0_byte (dev);              /* 20: spacing (0 = 25 kHz)   */
 171        send_1_byte (dev);              /* 21: spacing (1 = 25 kHz)   */
 172        send_0_byte (dev);              /* 22: spacing (0 = 25 kHz)   */
 173        send_1_byte (dev);              /* 23: AM/FM (FM = 1, always) */
 174
 175        /* latch frequency */
 176
 177        udelay (radio_wait_time);
 178        outb_p(128+64+volconvert(dev->curvol), io);
 179
 180        mutex_unlock(&lock);
 181
 182        return 0;
 183}
 184
 185static int vidioc_querycap (struct file *file, void  *priv,
 186                                        struct v4l2_capability *v)
 187{
 188        strlcpy(v->driver, "radio-aztech", sizeof (v->driver));
 189        strlcpy(v->card, "Aztech Radio", sizeof (v->card));
 190        sprintf(v->bus_info,"ISA");
 191        v->version = RADIO_VERSION;
 192        v->capabilities = V4L2_CAP_TUNER;
 193        return 0;
 194}
 195
 196static int vidioc_g_tuner (struct file *file, void *priv,
 197                                struct v4l2_tuner *v)
 198{
 199        struct az_device *az = video_drvdata(file);
 200
 201        if (v->index > 0)
 202                return -EINVAL;
 203
 204        strcpy(v->name, "FM");
 205        v->type = V4L2_TUNER_RADIO;
 206
 207        v->rangelow=(87*16000);
 208        v->rangehigh=(108*16000);
 209        v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
 210        v->capability=V4L2_TUNER_CAP_LOW;
 211        if(az_getstereo(az))
 212                v->audmode = V4L2_TUNER_MODE_STEREO;
 213        else
 214                v->audmode = V4L2_TUNER_MODE_MONO;
 215        v->signal=0xFFFF*az_getsigstr(az);
 216
 217        return 0;
 218}
 219
 220
 221static int vidioc_s_tuner (struct file *file, void *priv,
 222                                struct v4l2_tuner *v)
 223{
 224        if (v->index > 0)
 225                return -EINVAL;
 226
 227        return 0;
 228}
 229
 230static int vidioc_g_audio (struct file *file, void *priv,
 231                           struct v4l2_audio *a)
 232{
 233        if (a->index > 1)
 234                return -EINVAL;
 235
 236        strcpy(a->name, "Radio");
 237        a->capability = V4L2_AUDCAP_STEREO;
 238        return 0;
 239}
 240
 241static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
 242{
 243        *i = 0;
 244        return 0;
 245}
 246
 247static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
 248{
 249        if (i != 0)
 250                return -EINVAL;
 251        return 0;
 252}
 253
 254
 255static int vidioc_s_audio (struct file *file, void *priv,
 256                           struct v4l2_audio *a)
 257{
 258        if (a->index != 0)
 259                return -EINVAL;
 260
 261        return 0;
 262}
 263
 264static int vidioc_s_frequency (struct file *file, void *priv,
 265                                struct v4l2_frequency *f)
 266{
 267        struct az_device *az = video_drvdata(file);
 268
 269        az->curfreq = f->frequency;
 270        az_setfreq(az, az->curfreq);
 271        return 0;
 272}
 273
 274static int vidioc_g_frequency (struct file *file, void *priv,
 275                                struct v4l2_frequency *f)
 276{
 277        struct az_device *az = video_drvdata(file);
 278
 279        f->type = V4L2_TUNER_RADIO;
 280        f->frequency = az->curfreq;
 281
 282        return 0;
 283}
 284
 285static int vidioc_queryctrl (struct file *file, void *priv,
 286                            struct v4l2_queryctrl *qc)
 287{
 288        int i;
 289
 290        for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
 291                if (qc->id && qc->id == radio_qctrl[i].id) {
 292                        memcpy(qc, &(radio_qctrl[i]),
 293                                                sizeof(*qc));
 294                        return (0);
 295                }
 296        }
 297        return -EINVAL;
 298}
 299
 300static int vidioc_g_ctrl (struct file *file, void *priv,
 301                            struct v4l2_control *ctrl)
 302{
 303        struct az_device *az = video_drvdata(file);
 304
 305        switch (ctrl->id) {
 306                case V4L2_CID_AUDIO_MUTE:
 307                        if (az->curvol==0)
 308                                ctrl->value=1;
 309                        else
 310                                ctrl->value=0;
 311                        return (0);
 312                case V4L2_CID_AUDIO_VOLUME:
 313                        ctrl->value=az->curvol * 6554;
 314                        return (0);
 315        }
 316        return -EINVAL;
 317}
 318
 319static int vidioc_s_ctrl (struct file *file, void *priv,
 320                            struct v4l2_control *ctrl)
 321{
 322        struct az_device *az = video_drvdata(file);
 323
 324        switch (ctrl->id) {
 325                case V4L2_CID_AUDIO_MUTE:
 326                        if (ctrl->value) {
 327                                az_setvol(az,0);
 328                        } else {
 329                                az_setvol(az,az->curvol);
 330                        }
 331                        return (0);
 332                case V4L2_CID_AUDIO_VOLUME:
 333                        az_setvol(az,ctrl->value);
 334                        return (0);
 335        }
 336        return -EINVAL;
 337}
 338
 339static struct az_device aztech_unit;
 340
 341static int aztech_exclusive_open(struct inode *inode, struct file *file)
 342{
 343        return test_and_set_bit(0, &aztech_unit.in_use) ? -EBUSY : 0;
 344}
 345
 346static int aztech_exclusive_release(struct inode *inode, struct file *file)
 347{
 348        clear_bit(0, &aztech_unit.in_use);
 349        return 0;
 350}
 351
 352static const struct file_operations aztech_fops = {
 353        .owner          = THIS_MODULE,
 354        .open           = aztech_exclusive_open,
 355        .release        = aztech_exclusive_release,
 356        .ioctl          = video_ioctl2,
 357#ifdef CONFIG_COMPAT
 358        .compat_ioctl   = v4l_compat_ioctl32,
 359#endif
 360        .llseek         = no_llseek,
 361};
 362
 363static const struct v4l2_ioctl_ops aztech_ioctl_ops = {
 364        .vidioc_querycap    = vidioc_querycap,
 365        .vidioc_g_tuner     = vidioc_g_tuner,
 366        .vidioc_s_tuner     = vidioc_s_tuner,
 367        .vidioc_g_audio     = vidioc_g_audio,
 368        .vidioc_s_audio     = vidioc_s_audio,
 369        .vidioc_g_input     = vidioc_g_input,
 370        .vidioc_s_input     = vidioc_s_input,
 371        .vidioc_g_frequency = vidioc_g_frequency,
 372        .vidioc_s_frequency = vidioc_s_frequency,
 373        .vidioc_queryctrl   = vidioc_queryctrl,
 374        .vidioc_g_ctrl      = vidioc_g_ctrl,
 375        .vidioc_s_ctrl      = vidioc_s_ctrl,
 376};
 377
 378static struct video_device aztech_radio = {
 379        .name           = "Aztech radio",
 380        .fops           = &aztech_fops,
 381        .ioctl_ops      = &aztech_ioctl_ops,
 382        .release        = video_device_release_empty,
 383};
 384
 385module_param_named(debug,aztech_radio.debug, int, 0644);
 386MODULE_PARM_DESC(debug,"activates debug info");
 387
 388static int __init aztech_init(void)
 389{
 390        if(io==-1)
 391        {
 392                printk(KERN_ERR "You must set an I/O address with io=0x???\n");
 393                return -EINVAL;
 394        }
 395
 396        if (!request_region(io, 2, "aztech"))
 397        {
 398                printk(KERN_ERR "aztech: port 0x%x already in use\n", io);
 399                return -EBUSY;
 400        }
 401
 402        mutex_init(&lock);
 403        video_set_drvdata(&aztech_radio, &aztech_unit);
 404
 405        if (video_register_device(&aztech_radio, VFL_TYPE_RADIO, radio_nr) < 0) {
 406                release_region(io,2);
 407                return -EINVAL;
 408        }
 409
 410        printk(KERN_INFO "Aztech radio card driver v1.00/19990224 rkroll@exploits.org\n");
 411        /* mute card - prevents noisy bootups */
 412        outb (0, io);
 413        return 0;
 414}
 415
 416MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
 417MODULE_DESCRIPTION("A driver for the Aztech radio card.");
 418MODULE_LICENSE("GPL");
 419
 420module_param(io, int, 0);
 421module_param(radio_nr, int, 0);
 422MODULE_PARM_DESC(io, "I/O address of the Aztech card (0x350 or 0x358)");
 423
 424static void __exit aztech_cleanup(void)
 425{
 426        video_unregister_device(&aztech_radio);
 427        release_region(io,2);
 428}
 429
 430module_init(aztech_init);
 431module_exit(aztech_cleanup);
 432