linux/sound/ppc/beep.c
<<
>>
Prefs
   1/*
   2 * Beep using pcm
   3 *
   4 * Copyright (c) by Takashi Iwai <tiwai@suse.de>
   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 as published by
   8 *   the Free Software Foundation; either version 2 of the License, or
   9 *   (at your option) any later version.
  10 *
  11 *   This program is distributed in the hope that it will be useful,
  12 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 *   GNU General Public License for more details.
  15 *
  16 *   You should have received a copy of the GNU General Public License
  17 *   along with this program; if not, write to the Free Software
  18 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  19 */
  20
  21#include <asm/io.h>
  22#include <asm/irq.h>
  23#include <linux/init.h>
  24#include <linux/slab.h>
  25#include <linux/input.h>
  26#include <linux/pci.h>
  27#include <linux/dma-mapping.h>
  28#include <sound/core.h>
  29#include <sound/control.h>
  30#include "pmac.h"
  31
  32struct pmac_beep {
  33        int running;            /* boolean */
  34        int volume;             /* mixer volume: 0-100 */
  35        int volume_play;        /* currently playing volume */
  36        int hz;
  37        int nsamples;
  38        short *buf;             /* allocated wave buffer */
  39        dma_addr_t addr;        /* physical address of buffer */
  40        struct input_dev *dev;
  41};
  42
  43/*
  44 * stop beep if running
  45 */
  46void snd_pmac_beep_stop(struct snd_pmac *chip)
  47{
  48        struct pmac_beep *beep = chip->beep;
  49        if (beep && beep->running) {
  50                beep->running = 0;
  51                snd_pmac_beep_dma_stop(chip);
  52        }
  53}
  54
  55/*
  56 * Stuff for outputting a beep.  The values range from -327 to +327
  57 * so we can multiply by an amplitude in the range 0..100 to get a
  58 * signed short value to put in the output buffer.
  59 */
  60static short beep_wform[256] = {
  61        0,      40,     79,     117,    153,    187,    218,    245,
  62        269,    288,    304,    316,    323,    327,    327,    324,
  63        318,    310,    299,    288,    275,    262,    249,    236,
  64        224,    213,    204,    196,    190,    186,    183,    182,
  65        182,    183,    186,    189,    192,    196,    200,    203,
  66        206,    208,    209,    209,    209,    207,    204,    201,
  67        197,    193,    188,    183,    179,    174,    170,    166,
  68        163,    161,    160,    159,    159,    160,    161,    162,
  69        164,    166,    168,    169,    171,    171,    171,    170,
  70        169,    167,    163,    159,    155,    150,    144,    139,
  71        133,    128,    122,    117,    113,    110,    107,    105,
  72        103,    103,    103,    103,    104,    104,    105,    105,
  73        105,    103,    101,    97,     92,     86,     78,     68,
  74        58,     45,     32,     18,     3,      -11,    -26,    -41,
  75        -55,    -68,    -79,    -88,    -95,    -100,   -102,   -102,
  76        -99,    -93,    -85,    -75,    -62,    -48,    -33,    -16,
  77        0,      16,     33,     48,     62,     75,     85,     93,
  78        99,     102,    102,    100,    95,     88,     79,     68,
  79        55,     41,     26,     11,     -3,     -18,    -32,    -45,
  80        -58,    -68,    -78,    -86,    -92,    -97,    -101,   -103,
  81        -105,   -105,   -105,   -104,   -104,   -103,   -103,   -103,
  82        -103,   -105,   -107,   -110,   -113,   -117,   -122,   -128,
  83        -133,   -139,   -144,   -150,   -155,   -159,   -163,   -167,
  84        -169,   -170,   -171,   -171,   -171,   -169,   -168,   -166,
  85        -164,   -162,   -161,   -160,   -159,   -159,   -160,   -161,
  86        -163,   -166,   -170,   -174,   -179,   -183,   -188,   -193,
  87        -197,   -201,   -204,   -207,   -209,   -209,   -209,   -208,
  88        -206,   -203,   -200,   -196,   -192,   -189,   -186,   -183,
  89        -182,   -182,   -183,   -186,   -190,   -196,   -204,   -213,
  90        -224,   -236,   -249,   -262,   -275,   -288,   -299,   -310,
  91        -318,   -324,   -327,   -327,   -323,   -316,   -304,   -288,
  92        -269,   -245,   -218,   -187,   -153,   -117,   -79,    -40,
  93};
  94
  95#define BEEP_SRATE      22050   /* 22050 Hz sample rate */
  96#define BEEP_BUFLEN     512
  97#define BEEP_VOLUME     15      /* 0 - 100 */
  98
  99static int snd_pmac_beep_event(struct input_dev *dev, unsigned int type,
 100                               unsigned int code, int hz)
 101{
 102        struct snd_pmac *chip;
 103        struct pmac_beep *beep;
 104        unsigned long flags;
 105        int beep_speed = 0;
 106        int srate;
 107        int period, ncycles, nsamples;
 108        int i, j, f;
 109        short *p;
 110
 111        if (type != EV_SND)
 112                return -1;
 113
 114        switch (code) {
 115        case SND_BELL: if (hz) hz = 1000;
 116        case SND_TONE: break;
 117        default: return -1;
 118        }
 119
 120        chip = input_get_drvdata(dev);
 121        if (! chip || (beep = chip->beep) == NULL)
 122                return -1;
 123
 124        if (! hz) {
 125                spin_lock_irqsave(&chip->reg_lock, flags);
 126                if (beep->running)
 127                        snd_pmac_beep_stop(chip);
 128                spin_unlock_irqrestore(&chip->reg_lock, flags);
 129                return 0;
 130        }
 131
 132        beep_speed = snd_pmac_rate_index(chip, &chip->playback, BEEP_SRATE);
 133        srate = chip->freq_table[beep_speed];
 134
 135        if (hz <= srate / BEEP_BUFLEN || hz > srate / 2)
 136                hz = 1000;
 137
 138        spin_lock_irqsave(&chip->reg_lock, flags);
 139        if (chip->playback.running || chip->capture.running || beep->running) {
 140                spin_unlock_irqrestore(&chip->reg_lock, flags);
 141                return 0;
 142        }
 143        beep->running = 1;
 144        spin_unlock_irqrestore(&chip->reg_lock, flags);
 145
 146        if (hz == beep->hz && beep->volume == beep->volume_play) {
 147                nsamples = beep->nsamples;
 148        } else {
 149                period = srate * 256 / hz;      /* fixed point */
 150                ncycles = BEEP_BUFLEN * 256 / period;
 151                nsamples = (period * ncycles) >> 8;
 152                f = ncycles * 65536 / nsamples;
 153                j = 0;
 154                p = beep->buf;
 155                for (i = 0; i < nsamples; ++i, p += 2) {
 156                        p[0] = p[1] = beep_wform[j >> 8] * beep->volume;
 157                        j = (j + f) & 0xffff;
 158                }
 159                beep->hz = hz;
 160                beep->volume_play = beep->volume;
 161                beep->nsamples = nsamples;
 162        }
 163
 164        spin_lock_irqsave(&chip->reg_lock, flags);
 165        snd_pmac_beep_dma_start(chip, beep->nsamples * 4, beep->addr, beep_speed);
 166        spin_unlock_irqrestore(&chip->reg_lock, flags);
 167        return 0;
 168}
 169
 170/*
 171 * beep volume mixer
 172 */
 173
 174static int snd_pmac_info_beep(struct snd_kcontrol *kcontrol,
 175                              struct snd_ctl_elem_info *uinfo)
 176{
 177        uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 178        uinfo->count = 1;
 179        uinfo->value.integer.min = 0;
 180        uinfo->value.integer.max = 100;
 181        return 0;
 182}
 183
 184static int snd_pmac_get_beep(struct snd_kcontrol *kcontrol,
 185                             struct snd_ctl_elem_value *ucontrol)
 186{
 187        struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
 188        if (snd_BUG_ON(!chip->beep))
 189                return -ENXIO;
 190        ucontrol->value.integer.value[0] = chip->beep->volume;
 191        return 0;
 192}
 193
 194static int snd_pmac_put_beep(struct snd_kcontrol *kcontrol,
 195                             struct snd_ctl_elem_value *ucontrol)
 196{
 197        struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
 198        unsigned int oval, nval;
 199        if (snd_BUG_ON(!chip->beep))
 200                return -ENXIO;
 201        oval = chip->beep->volume;
 202        nval = ucontrol->value.integer.value[0];
 203        if (nval > 100)
 204                return -EINVAL;
 205        chip->beep->volume = nval;
 206        return oval != chip->beep->volume;
 207}
 208
 209static struct snd_kcontrol_new snd_pmac_beep_mixer = {
 210        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 211        .name = "Beep Playback Volume",
 212        .info = snd_pmac_info_beep,
 213        .get = snd_pmac_get_beep,
 214        .put = snd_pmac_put_beep,
 215};
 216
 217/* Initialize beep stuff */
 218int snd_pmac_attach_beep(struct snd_pmac *chip)
 219{
 220        struct pmac_beep *beep;
 221        struct input_dev *input_dev;
 222        struct snd_kcontrol *beep_ctl;
 223        void *dmabuf;
 224        int err = -ENOMEM;
 225
 226        beep = kzalloc(sizeof(*beep), GFP_KERNEL);
 227        if (! beep)
 228                return -ENOMEM;
 229        dmabuf = dma_alloc_coherent(&chip->pdev->dev, BEEP_BUFLEN * 4,
 230                                    &beep->addr, GFP_KERNEL);
 231        input_dev = input_allocate_device();
 232        if (! dmabuf || ! input_dev)
 233                goto fail1;
 234
 235        /* FIXME: set more better values */
 236        input_dev->name = "PowerMac Beep";
 237        input_dev->phys = "powermac/beep";
 238        input_dev->id.bustype = BUS_ADB;
 239        input_dev->id.vendor = 0x001f;
 240        input_dev->id.product = 0x0001;
 241        input_dev->id.version = 0x0100;
 242
 243        input_dev->evbit[0] = BIT_MASK(EV_SND);
 244        input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
 245        input_dev->event = snd_pmac_beep_event;
 246        input_dev->dev.parent = &chip->pdev->dev;
 247        input_set_drvdata(input_dev, chip);
 248
 249        beep->dev = input_dev;
 250        beep->buf = dmabuf;
 251        beep->volume = BEEP_VOLUME;
 252        beep->running = 0;
 253
 254        beep_ctl = snd_ctl_new1(&snd_pmac_beep_mixer, chip);
 255        err = snd_ctl_add(chip->card, beep_ctl);
 256        if (err < 0)
 257                goto fail1;
 258
 259        chip->beep = beep;
 260
 261        err = input_register_device(beep->dev);
 262        if (err)
 263                goto fail2;
 264 
 265        return 0;
 266 
 267 fail2: snd_ctl_remove(chip->card, beep_ctl);
 268 fail1: input_free_device(input_dev);
 269        if (dmabuf)
 270                dma_free_coherent(&chip->pdev->dev, BEEP_BUFLEN * 4,
 271                                  dmabuf, beep->addr);
 272        kfree(beep);
 273        return err;
 274}
 275
 276void snd_pmac_detach_beep(struct snd_pmac *chip)
 277{
 278        if (chip->beep) {
 279                input_unregister_device(chip->beep->dev);
 280                dma_free_coherent(&chip->pdev->dev, BEEP_BUFLEN * 4,
 281                                  chip->beep->buf, chip->beep->addr);
 282                kfree(chip->beep);
 283                chip->beep = NULL;
 284        }
 285}
 286
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.