// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Beep using pcm
*
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
*/
#include <linux/io.h>
#include <asm /irq.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <sound/core.h>
#include <sound/control.h>
#include "pmac.h"
struct pmac_beep {
int running; /* boolean */
int volume; /* mixer volume: 0-100 */
int volume_play; /* currently playing volume */
int hz;
int nsamples;
short *buf; /* allocated wave buffer */
dma_addr_t addr; /* physical address of buffer */
struct input_dev *dev;
};
/*
* stop beep if running
*/
void snd_pmac_beep_stop(struct snd_pmac *chip)
{
struct pmac_beep *beep = chip->beep;
if (beep && beep->running) {
beep->running = 0 ;
snd_pmac_beep_dma_stop(chip);
}
}
/*
* Stuff for outputting a beep. The values range from -327 to +327
* so we can multiply by an amplitude in the range 0..100 to get a
* signed short value to put in the output buffer.
*/
static const short beep_wform[256 ] = {
0 , 40 , 79 , 117 , 153 , 187 , 218 , 245 ,
269 , 288 , 304 , 316 , 323 , 327 , 327 , 324 ,
318 , 310 , 299 , 288 , 275 , 262 , 249 , 236 ,
224 , 213 , 204 , 196 , 190 , 186 , 183 , 182 ,
182 , 183 , 186 , 189 , 192 , 196 , 200 , 203 ,
206 , 208 , 209 , 209 , 209 , 207 , 204 , 201 ,
197 , 193 , 188 , 183 , 179 , 174 , 170 , 166 ,
163 , 161 , 160 , 159 , 159 , 160 , 161 , 162 ,
164 , 166 , 168 , 169 , 171 , 171 , 171 , 170 ,
169 , 167 , 163 , 159 , 155 , 150 , 144 , 139 ,
133 , 128 , 122 , 117 , 113 , 110 , 107 , 105 ,
103 , 103 , 103 , 103 , 104 , 104 , 105 , 105 ,
105 , 103 , 101 , 97 , 92 , 86 , 78 , 68 ,
58 , 45 , 32 , 18 , 3 , -11 , -26 , -41 ,
-55 , -68 , -79 , -88 , -95 , -100 , -102 , -102 ,
-99 , -93 , -85 , -75 , -62 , -48 , -33 , -16 ,
0 , 16 , 33 , 48 , 62 , 75 , 85 , 93 ,
99 , 102 , 102 , 100 , 95 , 88 , 79 , 68 ,
55 , 41 , 26 , 11 , -3 , -18 , -32 , -45 ,
-58 , -68 , -78 , -86 , -92 , -97 , -101 , -103 ,
-105 , -105 , -105 , -104 , -104 , -103 , -103 , -103 ,
-103 , -105 , -107 , -110 , -113 , -117 , -122 , -128 ,
-133 , -139 , -144 , -150 , -155 , -159 , -163 , -167 ,
-169 , -170 , -171 , -171 , -171 , -169 , -168 , -166 ,
-164 , -162 , -161 , -160 , -159 , -159 , -160 , -161 ,
-163 , -166 , -170 , -174 , -179 , -183 , -188 , -193 ,
-197 , -201 , -204 , -207 , -209 , -209 , -209 , -208 ,
-206 , -203 , -200 , -196 , -192 , -189 , -186 , -183 ,
-182 , -182 , -183 , -186 , -190 , -196 , -204 , -213 ,
-224 , -236 , -249 , -262 , -275 , -288 , -299 , -310 ,
-318 , -324 , -327 , -327 , -323 , -316 , -304 , -288 ,
-269 , -245 , -218 , -187 , -153 , -117 , -79 , -40 ,
};
#define BEEP_SRATE 22050 /* 22050 Hz sample rate */
#define BEEP_BUFLEN 512
#define BEEP_VOLUME 15 /* 0 - 100 */
static int snd_pmac_beep_event(struct input_dev *dev, unsigned int type,
unsigned int code, int hz)
{
struct snd_pmac *chip;
struct pmac_beep *beep;
unsigned long flags;
int beep_speed = 0 ;
int srate;
int period, ncycles, nsamples;
int i, j, f;
short *p;
if (type != EV_SND)
return -1 ;
switch (code) {
case SND_BELL: if (hz) hz = 1000 ; break ;
case SND_TONE: break ;
default : return -1 ;
}
chip = input_get_drvdata(dev);
if (!chip)
return -1 ;
beep = chip->beep;
if (!beep)
return -1 ;
if (! hz) {
spin_lock_irqsave(&chip->reg_lock, flags);
if (beep->running)
snd_pmac_beep_stop(chip);
spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0 ;
}
beep_speed = snd_pmac_rate_index(chip, &chip->playback, BEEP_SRATE);
srate = chip->freq_table[beep_speed];
if (hz <= srate / BEEP_BUFLEN || hz > srate / 2 )
hz = 1000 ;
spin_lock_irqsave(&chip->reg_lock, flags);
if (chip->playback.running || chip->capture.running || beep->running) {
spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0 ;
}
beep->running = 1 ;
spin_unlock_irqrestore(&chip->reg_lock, flags);
if (hz == beep->hz && beep->volume == beep->volume_play) {
nsamples = beep->nsamples;
} else {
period = srate * 256 / hz; /* fixed point */
ncycles = BEEP_BUFLEN * 256 / period;
nsamples = (period * ncycles) >> 8 ;
f = ncycles * 65536 / nsamples;
j = 0 ;
p = beep->buf;
for (i = 0 ; i < nsamples; ++i, p += 2 ) {
p[0 ] = p[1 ] = beep_wform[j >> 8 ] * beep->volume;
j = (j + f) & 0 xffff;
}
beep->hz = hz;
beep->volume_play = beep->volume;
beep->nsamples = nsamples;
}
spin_lock_irqsave(&chip->reg_lock, flags);
snd_pmac_beep_dma_start(chip, beep->nsamples * 4 , beep->addr, beep_speed);
spin_unlock_irqrestore(&chip->reg_lock, flags);
return 0 ;
}
/*
* beep volume mixer
*/
static int snd_pmac_info_beep(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1 ;
uinfo->value.integer.min = 0 ;
uinfo->value.integer.max = 100 ;
return 0 ;
}
static int snd_pmac_get_beep(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
if (snd_BUG_ON(!chip->beep))
return -ENXIO;
ucontrol->value.integer.value[0 ] = chip->beep->volume;
return 0 ;
}
static int snd_pmac_put_beep(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
unsigned int oval, nval;
if (snd_BUG_ON(!chip->beep))
return -ENXIO;
oval = chip->beep->volume;
nval = ucontrol->value.integer.value[0 ];
if (nval > 100 )
return -EINVAL;
chip->beep->volume = nval;
return oval != chip->beep->volume;
}
static const struct snd_kcontrol_new snd_pmac_beep_mixer = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Beep Playback Volume" ,
.info = snd_pmac_info_beep,
.get = snd_pmac_get_beep,
.put = snd_pmac_put_beep,
};
/* Initialize beep stuff */
int snd_pmac_attach_beep(struct snd_pmac *chip)
{
struct pmac_beep *beep;
struct input_dev *input_dev;
struct snd_kcontrol *beep_ctl;
void *dmabuf;
int err = -ENOMEM;
beep = kzalloc(sizeof (*beep), GFP_KERNEL);
if (! beep)
return -ENOMEM;
dmabuf = dma_alloc_coherent(&chip->pdev->dev, BEEP_BUFLEN * 4 ,
&beep->addr, GFP_KERNEL);
input_dev = input_allocate_device();
if (! dmabuf || ! input_dev)
goto fail1;
/* FIXME: set more better values */
input_dev->name = "PowerMac Beep" ;
input_dev->phys = "powermac/beep" ;
input_dev->id.bustype = BUS_ADB;
input_dev->id.vendor = 0 x001f;
input_dev->id.product = 0 x0001;
input_dev->id.version = 0 x0100;
input_dev->evbit[0 ] = BIT_MASK(EV_SND);
input_dev->sndbit[0 ] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
input_dev->event = snd_pmac_beep_event;
input_dev->dev.parent = &chip->pdev->dev;
input_set_drvdata(input_dev, chip);
beep->dev = input_dev;
beep->buf = dmabuf;
beep->volume = BEEP_VOLUME;
beep->running = 0 ;
beep_ctl = snd_ctl_new1(&snd_pmac_beep_mixer, chip);
err = snd_ctl_add(chip->card, beep_ctl);
if (err < 0 )
goto fail1;
chip->beep = beep;
err = input_register_device(beep->dev);
if (err)
goto fail2;
return 0 ;
fail2: snd_ctl_remove(chip->card, beep_ctl);
fail1: input_free_device(input_dev);
if (dmabuf)
dma_free_coherent(&chip->pdev->dev, BEEP_BUFLEN * 4 ,
dmabuf, beep->addr);
kfree(beep);
return err;
}
void snd_pmac_detach_beep(struct snd_pmac *chip)
{
if (chip->beep) {
input_unregister_device(chip->beep->dev);
dma_free_coherent(&chip->pdev->dev, BEEP_BUFLEN * 4 ,
chip->beep->buf, chip->beep->addr);
kfree(chip->beep);
chip->beep = NULL;
}
}
Messung V0.5 in Prozent C=95 H=91 G=92
¤ Dauer der Verarbeitung: 0.9 Sekunden
(vorverarbeitet am 2026-06-08)
¤
*© Formatika GbR, Deutschland