linux/sound/oss/vidc.c
<<
>>
Prefs
   1/*
   2 *  linux/drivers/sound/vidc.c
   3 *
   4 *  Copyright (C) 1997-2000 by Russell King <rmk@arm.linux.org.uk>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 *
  10 *  VIDC20 audio driver.
  11 *
  12 * The VIDC20 sound hardware consists of the VIDC20 itself, a DAC and a DMA
  13 * engine.  The DMA transfers fixed-format (16-bit little-endian linear)
  14 * samples to the VIDC20, which then transfers this data serially to the
  15 * DACs.  The samplerate is controlled by the VIDC.
  16 *
  17 * We currently support a mixer device, but it is currently non-functional.
  18 */
  19
  20#include <linux/gfp.h>
  21#include <linux/init.h>
  22#include <linux/module.h>
  23#include <linux/kernel.h>
  24#include <linux/interrupt.h>
  25
  26#include <mach/hardware.h>
  27#include <asm/dma.h>
  28#include <asm/io.h>
  29#include <asm/hardware/iomd.h>
  30#include <asm/irq.h>
  31
  32#include "sound_config.h"
  33#include "vidc.h"
  34
  35#ifndef _SIOC_TYPE
  36#define _SIOC_TYPE(x)   _IOC_TYPE(x)
  37#endif
  38#ifndef _SIOC_NR
  39#define _SIOC_NR(x)     _IOC_NR(x)
  40#endif
  41
  42#define VIDC_SOUND_CLOCK        (250000)
  43#define VIDC_SOUND_CLOCK_EXT    (176400)
  44
  45/*
  46 * When using SERIAL SOUND mode (external DAC), the number of physical
  47 * channels is fixed at 2.
  48 */
  49static int              vidc_busy;
  50static int              vidc_adev;
  51static int              vidc_audio_rate;
  52static char             vidc_audio_format;
  53static char             vidc_audio_channels;
  54
  55static unsigned char    vidc_level_l[SOUND_MIXER_NRDEVICES] = {
  56        85,             /* master       */
  57        50,             /* bass         */
  58        50,             /* treble       */
  59        0,              /* synth        */
  60        75,             /* pcm          */
  61        0,              /* speaker      */
  62        100,            /* ext line     */
  63        0,              /* mic          */
  64        100,            /* CD           */
  65        0,
  66};
  67
  68static unsigned char    vidc_level_r[SOUND_MIXER_NRDEVICES] = {
  69        85,             /* master       */
  70        50,             /* bass         */
  71        50,             /* treble       */
  72        0,              /* synth        */
  73        75,             /* pcm          */
  74        0,              /* speaker      */
  75        100,            /* ext line     */
  76        0,              /* mic          */
  77        100,            /* CD           */
  78        0,
  79};
  80
  81static unsigned int     vidc_audio_volume_l;    /* left PCM vol, 0 - 65536 */
  82static unsigned int     vidc_audio_volume_r;    /* right PCM vol, 0 - 65536 */
  83
  84extern void     vidc_update_filler(int bits, int channels);
  85extern int      softoss_dev;
  86
  87static void
  88vidc_mixer_set(int mdev, unsigned int level)
  89{
  90        unsigned int lev_l = level & 0x007f;
  91        unsigned int lev_r = (level & 0x7f00) >> 8;
  92        unsigned int mlev_l, mlev_r;
  93
  94        if (lev_l > 100)
  95                lev_l = 100;
  96        if (lev_r > 100)
  97                lev_r = 100;
  98
  99#define SCALE(lev,master)       ((lev) * (master) * 65536 / 10000)
 100
 101        mlev_l = vidc_level_l[SOUND_MIXER_VOLUME];
 102        mlev_r = vidc_level_r[SOUND_MIXER_VOLUME];
 103
 104        switch (mdev) {
 105        case SOUND_MIXER_VOLUME:
 106        case SOUND_MIXER_PCM:
 107                vidc_level_l[mdev] = lev_l;
 108                vidc_level_r[mdev] = lev_r;
 109
 110                vidc_audio_volume_l = SCALE(lev_l, mlev_l);
 111                vidc_audio_volume_r = SCALE(lev_r, mlev_r);
 112/*printk("VIDC: PCM vol %05X %05X\n", vidc_audio_volume_l, vidc_audio_volume_r);*/
 113                break;
 114        }
 115#undef SCALE
 116}
 117
 118static int vidc_mixer_ioctl(int dev, unsigned int cmd, void __user *arg)
 119{
 120        unsigned int val;
 121        unsigned int mdev;
 122
 123        if (_SIOC_TYPE(cmd) != 'M')
 124                return -EINVAL;
 125
 126        mdev = _SIOC_NR(cmd);
 127
 128        if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
 129                if (get_user(val, (unsigned int __user *)arg))
 130                        return -EFAULT;
 131
 132                if (mdev < SOUND_MIXER_NRDEVICES)
 133                        vidc_mixer_set(mdev, val);
 134                else
 135                        return -EINVAL;
 136        }
 137
 138        /*
 139         * Return parameters
 140         */
 141        switch (mdev) {
 142        case SOUND_MIXER_RECSRC:
 143                val = 0;
 144                break;
 145
 146        case SOUND_MIXER_DEVMASK:
 147                val = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH;
 148                break;
 149
 150        case SOUND_MIXER_STEREODEVS:
 151                val = SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH;
 152                break;
 153
 154        case SOUND_MIXER_RECMASK:
 155                val = 0;
 156                break;
 157
 158        case SOUND_MIXER_CAPS:
 159                val = 0;
 160                break;
 161
 162        default:
 163                if (mdev < SOUND_MIXER_NRDEVICES)
 164                        val = vidc_level_l[mdev] | vidc_level_r[mdev] << 8;
 165                else
 166                        return -EINVAL;
 167        }
 168
 169        return put_user(val, (unsigned int __user *)arg) ? -EFAULT : 0;
 170}
 171
 172static unsigned int vidc_audio_set_format(int dev, unsigned int fmt)
 173{
 174        switch (fmt) {
 175        default:
 176                fmt = AFMT_S16_LE;
 177        case AFMT_U8:
 178        case AFMT_S8:
 179        case AFMT_S16_LE:
 180                vidc_audio_format = fmt;
 181                vidc_update_filler(vidc_audio_format, vidc_audio_channels);
 182        case AFMT_QUERY:
 183                break;
 184        }
 185        return vidc_audio_format;
 186}
 187
 188#define my_abs(i) ((i)<0 ? -(i) : (i))
 189
 190static int vidc_audio_set_speed(int dev, int rate)
 191{
 192        if (rate) {
 193                unsigned int hwctrl, hwrate, hwrate_ext, rate_int, rate_ext;
 194                unsigned int diff_int, diff_ext;
 195                unsigned int newsize, new2size;
 196
 197                hwctrl = 0x00000003;
 198
 199                /* Using internal clock */
 200                hwrate = (((VIDC_SOUND_CLOCK * 2) / rate) + 1) >> 1;
 201                if (hwrate < 3)
 202                        hwrate = 3;
 203                if (hwrate > 255)
 204                        hwrate = 255;
 205
 206                /* Using exernal clock */
 207                hwrate_ext = (((VIDC_SOUND_CLOCK_EXT * 2) / rate) + 1) >> 1;
 208                if (hwrate_ext < 3)
 209                        hwrate_ext = 3;
 210                if (hwrate_ext > 255)
 211                        hwrate_ext = 255;
 212
 213                rate_int = VIDC_SOUND_CLOCK / hwrate;
 214                rate_ext = VIDC_SOUND_CLOCK_EXT / hwrate_ext;
 215
 216                /* Chose between external and internal clock */
 217                diff_int = my_abs(rate_ext-rate);
 218                diff_ext = my_abs(rate_int-rate);
 219                if (diff_ext < diff_int) {
 220                        /*printk("VIDC: external %d %d %d\n", rate, rate_ext, hwrate_ext);*/
 221                        hwrate=hwrate_ext;
 222                        hwctrl=0x00000002;
 223                        /* Allow roughly 0.4% tolerance */
 224                        if (diff_ext > (rate/256))
 225                                rate=rate_ext;
 226                } else {
 227                        /*printk("VIDC: internal %d %d %d\n", rate, rate_int, hwrate);*/
 228                        hwctrl=0x00000003;
 229                        /* Allow roughly 0.4% tolerance */
 230                        if (diff_int > (rate/256))
 231                                rate=rate_int;
 232                }
 233
 234                vidc_writel(0xb0000000 | (hwrate - 2));
 235                vidc_writel(0xb1000000 | hwctrl);
 236
 237                newsize = (10000 / hwrate) & ~3;
 238                if (newsize < 208)
 239                        newsize = 208;
 240                if (newsize > 4096)
 241                        newsize = 4096;
 242                for (new2size = 128; new2size < newsize; new2size <<= 1);
 243                if (new2size - newsize > newsize - (new2size >> 1))
 244                        new2size >>= 1;
 245                if (new2size > 4096) {
 246                        printk(KERN_ERR "VIDC: error: dma buffer (%d) %d > 4K\n",
 247                                newsize, new2size);
 248                        new2size = 4096;
 249                }
 250                /*printk("VIDC: dma size %d\n", new2size);*/
 251                dma_bufsize = new2size;
 252                vidc_audio_rate = rate;
 253        }
 254        return vidc_audio_rate;
 255}
 256
 257static short vidc_audio_set_channels(int dev, short channels)
 258{
 259        switch (channels) {
 260        default:
 261                channels = 2;
 262        case 1:
 263        case 2:
 264                vidc_audio_channels = channels;
 265                vidc_update_filler(vidc_audio_format, vidc_audio_channels);
 266        case 0:
 267                break;
 268        }
 269        return vidc_audio_channels;
 270}
 271
 272/*
 273 * Open the device
 274 */
 275static int vidc_audio_open(int dev, int mode)
 276{
 277        /* This audio device does not have recording capability */
 278        if (mode == OPEN_READ)
 279                return -EPERM;
 280
 281        if (vidc_busy)
 282                return -EBUSY;
 283
 284        vidc_busy = 1;
 285        return 0;
 286}
 287
 288/*
 289 * Close the device
 290 */
 291static void vidc_audio_close(int dev)
 292{
 293        vidc_busy = 0;
 294}
 295
 296/*
 297 * Output a block via DMA to sound device.
 298 *
 299 * We just set the DMA start and count; the DMA interrupt routine
 300 * will take care of formatting the samples (via the appropriate
 301 * vidc_filler routine), and flag via vidc_audio_dma_interrupt when
 302 * more data is required.
 303 */
 304static void
 305vidc_audio_output_block(int dev, unsigned long buf, int total_count, int one)
 306{
 307        struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
 308        unsigned long flags;
 309
 310        local_irq_save(flags);
 311        dma_start = buf - (unsigned long)dmap->raw_buf_phys + (unsigned long)dmap->raw_buf;
 312        dma_count = total_count;
 313        local_irq_restore(flags);
 314}
 315
 316static void
 317vidc_audio_start_input(int dev, unsigned long buf, int count, int intrflag)
 318{
 319}
 320
 321static int vidc_audio_prepare_for_input(int dev, int bsize, int bcount)
 322{
 323        return -EINVAL;
 324}
 325
 326static irqreturn_t vidc_audio_dma_interrupt(void)
 327{
 328        DMAbuf_outputintr(vidc_adev, 1);
 329        return IRQ_HANDLED;
 330}
 331
 332/*
 333 * Prepare for outputting samples.
 334 *
 335 * Each buffer that will be passed will be `bsize' bytes long,
 336 * with a total of `bcount' buffers.
 337 */
 338static int vidc_audio_prepare_for_output(int dev, int bsize, int bcount)
 339{
 340        struct audio_operations *adev = audio_devs[dev];
 341
 342        dma_interrupt = NULL;
 343        adev->dmap_out->flags |= DMA_NODMA;
 344
 345        return 0;
 346}
 347
 348/*
 349 * Stop our current operation.
 350 */
 351static void vidc_audio_reset(int dev)
 352{
 353        dma_interrupt = NULL;
 354}
 355
 356static int vidc_audio_local_qlen(int dev)
 357{
 358        return /*dma_count !=*/ 0;
 359}
 360
 361static void vidc_audio_trigger(int dev, int enable_bits)
 362{
 363        struct audio_operations *adev = audio_devs[dev];
 364
 365        if (enable_bits & PCM_ENABLE_OUTPUT) {
 366                if (!(adev->dmap_out->flags & DMA_ACTIVE)) {
 367                        unsigned long flags;
 368
 369                        local_irq_save(flags);
 370
 371                        /* prevent recusion */
 372                        adev->dmap_out->flags |= DMA_ACTIVE;
 373
 374                        dma_interrupt = vidc_audio_dma_interrupt;
 375                        vidc_sound_dma_irq(0, NULL);
 376                        iomd_writeb(DMA_CR_E | 0x10, IOMD_SD0CR);
 377
 378                        local_irq_restore(flags);
 379                }
 380        }
 381}
 382
 383static struct audio_driver vidc_audio_driver =
 384{
 385        .owner                  = THIS_MODULE,
 386        .open                   = vidc_audio_open,
 387        .close                  = vidc_audio_close,
 388        .output_block           = vidc_audio_output_block,
 389        .start_input            = vidc_audio_start_input,
 390        .prepare_for_input      = vidc_audio_prepare_for_input,
 391        .prepare_for_output     = vidc_audio_prepare_for_output,
 392        .halt_io                = vidc_audio_reset,
 393        .local_qlen             = vidc_audio_local_qlen,
 394        .trigger                = vidc_audio_trigger,
 395        .set_speed              = vidc_audio_set_speed,
 396        .set_bits               = vidc_audio_set_format,
 397        .set_channels           = vidc_audio_set_channels
 398};
 399
 400static struct mixer_operations vidc_mixer_operations = {
 401        .owner          = THIS_MODULE,
 402        .id             = "VIDC",
 403        .name           = "VIDCsound",
 404        .ioctl          = vidc_mixer_ioctl
 405};
 406
 407void vidc_update_filler(int format, int channels)
 408{
 409#define TYPE(fmt,ch) (((fmt)<<2) | ((ch)&3))
 410
 411        switch (TYPE(format, channels)) {
 412        default:
 413        case TYPE(AFMT_U8, 1):
 414                vidc_filler = vidc_fill_1x8_u;
 415                break;
 416
 417        case TYPE(AFMT_U8, 2):
 418                vidc_filler = vidc_fill_2x8_u;
 419                break;
 420
 421        case TYPE(AFMT_S8, 1):
 422                vidc_filler = vidc_fill_1x8_s;
 423                break;
 424
 425        case TYPE(AFMT_S8, 2):
 426                vidc_filler = vidc_fill_2x8_s;
 427                break;
 428
 429        case TYPE(AFMT_S16_LE, 1):
 430                vidc_filler = vidc_fill_1x16_s;
 431                break;
 432
 433        case TYPE(AFMT_S16_LE, 2):
 434                vidc_filler = vidc_fill_2x16_s;
 435                break;
 436        }
 437}
 438
 439static void __init attach_vidc(struct address_info *hw_config)
 440{
 441        char name[32];
 442        int i, adev;
 443
 444        sprintf(name, "VIDC %d-bit sound", hw_config->card_subtype);
 445        conf_printf(name, hw_config);
 446        memset(dma_buf, 0, sizeof(dma_buf));
 447
 448        adev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, name,
 449                        &vidc_audio_driver, sizeof(vidc_audio_driver),
 450                        DMA_AUTOMODE, AFMT_U8 | AFMT_S8 | AFMT_S16_LE,
 451                        NULL, hw_config->dma, hw_config->dma2);
 452
 453        if (adev < 0)
 454                goto audio_failed;
 455
 456        /*
 457         * 1024 bytes => 64 buffers
 458         */
 459        audio_devs[adev]->min_fragment = 10;
 460        audio_devs[adev]->mixer_dev = num_mixers;
 461
 462        audio_devs[adev]->mixer_dev =
 463                sound_install_mixer(MIXER_DRIVER_VERSION,
 464                                name, &vidc_mixer_operations,
 465                                sizeof(vidc_mixer_operations), NULL);
 466
 467        if (audio_devs[adev]->mixer_dev < 0)
 468                goto mixer_failed;
 469
 470        for (i = 0; i < 2; i++) {
 471                dma_buf[i] = get_zeroed_page(GFP_KERNEL);
 472                if (!dma_buf[i]) {
 473                        printk(KERN_ERR "%s: can't allocate required buffers\n",
 474                                name);
 475                        goto mem_failed;
 476                }
 477                dma_pbuf[i] = virt_to_phys((void *)dma_buf[i]);
 478        }
 479
 480        if (sound_alloc_dma(hw_config->dma, hw_config->name)) {
 481                printk(KERN_ERR "%s: DMA %d is in  use\n", name, hw_config->dma);
 482                goto dma_failed;
 483        }
 484
 485        if (request_irq(hw_config->irq, vidc_sound_dma_irq, 0,
 486                        hw_config->name, &dma_start)) {
 487                printk(KERN_ERR "%s: IRQ %d is in use\n", name, hw_config->irq);
 488                goto irq_failed;
 489        }
 490        vidc_adev = adev;
 491        vidc_mixer_set(SOUND_MIXER_VOLUME, (85 | 85 << 8));
 492
 493        return;
 494
 495irq_failed:
 496        sound_free_dma(hw_config->dma);
 497dma_failed:
 498mem_failed:
 499        for (i = 0; i < 2; i++)
 500                free_page(dma_buf[i]);
 501        sound_unload_mixerdev(audio_devs[adev]->mixer_dev);
 502mixer_failed:
 503        sound_unload_audiodev(adev);
 504audio_failed:
 505        return;
 506}
 507
 508static int __init probe_vidc(struct address_info *hw_config)
 509{
 510        hw_config->irq          = IRQ_DMAS0;
 511        hw_config->dma          = DMA_VIRTUAL_SOUND;
 512        hw_config->dma2         = -1;
 513        hw_config->card_subtype = 16;
 514        hw_config->name         = "VIDC20";
 515        return 1;
 516}
 517
 518static void __exit unload_vidc(struct address_info *hw_config)
 519{
 520        int i, adev = vidc_adev;
 521
 522        vidc_adev = -1;
 523
 524        free_irq(hw_config->irq, &dma_start);
 525        sound_free_dma(hw_config->dma);
 526
 527        if (adev >= 0) {
 528                sound_unload_mixerdev(audio_devs[adev]->mixer_dev);
 529                sound_unload_audiodev(adev);
 530                for (i = 0; i < 2; i++)
 531                        free_page(dma_buf[i]);
 532        }
 533}
 534
 535static struct address_info cfg;
 536
 537static int __init init_vidc(void)
 538{
 539        if (probe_vidc(&cfg) == 0)
 540                return -ENODEV;
 541
 542        attach_vidc(&cfg);
 543
 544        return 0;
 545}
 546
 547static void __exit cleanup_vidc(void)
 548{
 549        unload_vidc(&cfg);
 550}
 551
 552module_init(init_vidc);
 553module_exit(cleanup_vidc);
 554
 555MODULE_AUTHOR("Russell King");
 556MODULE_DESCRIPTION("VIDC20 audio driver");
 557MODULE_LICENSE("GPL");
 558
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.