2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Beep using pcm
|
|
|
|
*
|
|
|
|
* Copyright (c) by Takashi Iwai <tiwai@suse.de>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sound/driver.h>
|
|
|
|
#include <asm/io.h>
|
|
|
|
#include <asm/irq.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/input.h>
|
[PATCH] ppc64: very basic desktop g5 sound support
This patch hacks the current PowerMac Alsa driver to add some basic support
of analog sound output to some desktop G5s. It has severe limitations
though:
- Only 44100Khz 16 bits
- Only work on G5 models using a TAS3004 analog code, that is early
single CPU desktops and all dual CPU desktops at this date, but none
of the more recent ones like iMac G5.
- It does analog only, no digital/SPDIF support at all, no native
AC3 support
Better support would require a complete rewrite of the driver (which I am
working on, but don't hold your breath), to properly support the diversity
of apple sound HW setup, including dual codecs, several i2s busses, all the
new codecs used in the new machines, proper clock switching with digital,
etc etc etc...
This patch applies on top of the other PowerMac sound patches I posted in
the past couple of days (new powerbook support and sleep fixes).
Note: This is a FAQ entry for PowerMac sound support with TI codecs: They
have a feature called "DRC" which is automatically enabled for the internal
speaker (at least when auto mute control is enabled) which will cause your
sound to fade out to nothing after half a second of playback if you don't
set a proper "DRC Range" in the mixer. So if you have a problem like that,
check alsamixer and raise your DRC Range to something reasonable.
Note2: This patch will also add auto-mute of the speaker when line-out jack
is used on some earlier desktop G4s (and on the G5) in addition to the
headphone jack. If that behaviour isn't what you want, just disable
auto-muting and use the manual mute controls in alsamixer.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-04-17 06:24:32 +08:00
|
|
|
#include <linux/pci.h>
|
|
|
|
#include <linux/dma-mapping.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <sound/core.h>
|
|
|
|
#include <sound/control.h>
|
|
|
|
#include "pmac.h"
|
|
|
|
|
2005-11-17 22:09:46 +08:00
|
|
|
struct pmac_beep {
|
2005-09-15 15:01:49 +08:00
|
|
|
int running; /* boolean */
|
|
|
|
int volume; /* mixer volume: 0-100 */
|
2005-04-17 06:20:36 +08:00
|
|
|
int volume_play; /* currently playing volume */
|
|
|
|
int hz;
|
|
|
|
int nsamples;
|
|
|
|
short *buf; /* allocated wave buffer */
|
[PATCH] ppc64: very basic desktop g5 sound support
This patch hacks the current PowerMac Alsa driver to add some basic support
of analog sound output to some desktop G5s. It has severe limitations
though:
- Only 44100Khz 16 bits
- Only work on G5 models using a TAS3004 analog code, that is early
single CPU desktops and all dual CPU desktops at this date, but none
of the more recent ones like iMac G5.
- It does analog only, no digital/SPDIF support at all, no native
AC3 support
Better support would require a complete rewrite of the driver (which I am
working on, but don't hold your breath), to properly support the diversity
of apple sound HW setup, including dual codecs, several i2s busses, all the
new codecs used in the new machines, proper clock switching with digital,
etc etc etc...
This patch applies on top of the other PowerMac sound patches I posted in
the past couple of days (new powerbook support and sleep fixes).
Note: This is a FAQ entry for PowerMac sound support with TI codecs: They
have a feature called "DRC" which is automatically enabled for the internal
speaker (at least when auto mute control is enabled) which will cause your
sound to fade out to nothing after half a second of playback if you don't
set a proper "DRC Range" in the mixer. So if you have a problem like that,
check alsamixer and raise your DRC Range to something reasonable.
Note2: This patch will also add auto-mute of the speaker when line-out jack
is used on some earlier desktop G4s (and on the G5) in addition to the
headphone jack. If that behaviour isn't what you want, just disable
auto-muting and use the manual mute controls in alsamixer.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-04-17 06:24:32 +08:00
|
|
|
dma_addr_t addr; /* physical address of buffer */
|
2005-09-15 15:01:49 +08:00
|
|
|
struct input_dev *dev;
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* stop beep if running
|
|
|
|
*/
|
2005-11-17 22:09:46 +08:00
|
|
|
void snd_pmac_beep_stop(struct snd_pmac *chip)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 22:09:46 +08:00
|
|
|
struct pmac_beep *beep = chip->beep;
|
2005-04-17 06:20:36 +08:00
|
|
|
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 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 */
|
|
|
|
|
2005-11-17 22:09:46 +08:00
|
|
|
static int snd_pmac_beep_event(struct input_dev *dev, unsigned int type,
|
|
|
|
unsigned int code, int hz)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 22:09:46 +08:00
|
|
|
struct snd_pmac *chip;
|
|
|
|
struct pmac_beep *beep;
|
2005-04-17 06:20:36 +08:00
|
|
|
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;
|
|
|
|
case SND_TONE: break;
|
|
|
|
default: return -1;
|
|
|
|
}
|
|
|
|
|
2007-06-15 11:58:51 +08:00
|
|
|
chip = input_get_drvdata(dev);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (! chip || (beep = chip->beep) == NULL)
|
|
|
|
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) & 0xffff;
|
|
|
|
}
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
|
2005-11-17 22:09:46 +08:00
|
|
|
static int snd_pmac_info_beep(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_info *uinfo)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
|
|
|
uinfo->count = 1;
|
|
|
|
uinfo->value.integer.min = 0;
|
|
|
|
uinfo->value.integer.max = 100;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 22:09:46 +08:00
|
|
|
static int snd_pmac_get_beep(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 22:09:46 +08:00
|
|
|
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
|
2005-04-17 06:20:36 +08:00
|
|
|
snd_assert(chip->beep, return -ENXIO);
|
|
|
|
ucontrol->value.integer.value[0] = chip->beep->volume;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 22:09:46 +08:00
|
|
|
static int snd_pmac_put_beep(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 22:09:46 +08:00
|
|
|
struct snd_pmac *chip = snd_kcontrol_chip(kcontrol);
|
2005-04-17 06:20:36 +08:00
|
|
|
int oval;
|
|
|
|
snd_assert(chip->beep, return -ENXIO);
|
|
|
|
oval = chip->beep->volume;
|
|
|
|
chip->beep->volume = ucontrol->value.integer.value[0];
|
|
|
|
return oval != chip->beep->volume;
|
|
|
|
}
|
|
|
|
|
2005-11-17 22:09:46 +08:00
|
|
|
static struct snd_kcontrol_new snd_pmac_beep_mixer = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.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 */
|
2005-11-17 22:09:46 +08:00
|
|
|
int __init snd_pmac_attach_beep(struct snd_pmac *chip)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 22:09:46 +08:00
|
|
|
struct pmac_beep *beep;
|
2005-09-15 15:01:49 +08:00
|
|
|
struct input_dev *input_dev;
|
2006-08-03 21:06:14 +08:00
|
|
|
struct snd_kcontrol *beep_ctl;
|
2005-09-15 15:01:49 +08:00
|
|
|
void *dmabuf;
|
|
|
|
int err = -ENOMEM;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-09-15 15:01:49 +08:00
|
|
|
beep = kzalloc(sizeof(*beep), GFP_KERNEL);
|
2006-08-03 21:06:14 +08:00
|
|
|
if (! beep)
|
|
|
|
return -ENOMEM;
|
2005-09-15 15:01:49 +08:00
|
|
|
dmabuf = dma_alloc_coherent(&chip->pdev->dev, BEEP_BUFLEN * 4,
|
|
|
|
&beep->addr, GFP_KERNEL);
|
|
|
|
input_dev = input_allocate_device();
|
2006-08-03 21:06:14 +08:00
|
|
|
if (! dmabuf || ! input_dev)
|
|
|
|
goto fail1;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* FIXME: set more better values */
|
2005-09-15 15:01:49 +08:00
|
|
|
input_dev->name = "PowerMac Beep";
|
|
|
|
input_dev->phys = "powermac/beep";
|
|
|
|
input_dev->id.bustype = BUS_ADB;
|
|
|
|
input_dev->id.vendor = 0x001f;
|
|
|
|
input_dev->id.product = 0x0001;
|
|
|
|
input_dev->id.version = 0x0100;
|
|
|
|
|
2007-10-19 14:40:32 +08:00
|
|
|
input_dev->evbit[0] = BIT_MASK(EV_SND);
|
|
|
|
input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
|
2005-09-15 15:01:49 +08:00
|
|
|
input_dev->event = snd_pmac_beep_event;
|
2007-06-15 11:58:51 +08:00
|
|
|
input_dev->dev.parent = &chip->pdev->dev;
|
|
|
|
input_set_drvdata(input_dev, chip);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-09-15 15:01:49 +08:00
|
|
|
beep->dev = input_dev;
|
|
|
|
beep->buf = dmabuf;
|
2005-04-17 06:20:36 +08:00
|
|
|
beep->volume = BEEP_VOLUME;
|
|
|
|
beep->running = 0;
|
2005-09-15 15:01:49 +08:00
|
|
|
|
2006-08-03 21:06:14 +08:00
|
|
|
beep_ctl = snd_ctl_new1(&snd_pmac_beep_mixer, chip);
|
|
|
|
err = snd_ctl_add(chip->card, beep_ctl);
|
2005-09-15 15:01:49 +08:00
|
|
|
if (err < 0)
|
2006-08-03 21:06:14 +08:00
|
|
|
goto fail1;
|
2007-06-15 11:58:51 +08:00
|
|
|
|
|
|
|
chip->beep = beep;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-08-03 21:06:14 +08:00
|
|
|
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);
|
2005-09-15 15:01:49 +08:00
|
|
|
kfree(beep);
|
|
|
|
return err;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2005-11-17 22:09:46 +08:00
|
|
|
void snd_pmac_detach_beep(struct snd_pmac *chip)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
if (chip->beep) {
|
2005-09-15 15:01:49 +08:00
|
|
|
input_unregister_device(chip->beep->dev);
|
[PATCH] ppc64: very basic desktop g5 sound support
This patch hacks the current PowerMac Alsa driver to add some basic support
of analog sound output to some desktop G5s. It has severe limitations
though:
- Only 44100Khz 16 bits
- Only work on G5 models using a TAS3004 analog code, that is early
single CPU desktops and all dual CPU desktops at this date, but none
of the more recent ones like iMac G5.
- It does analog only, no digital/SPDIF support at all, no native
AC3 support
Better support would require a complete rewrite of the driver (which I am
working on, but don't hold your breath), to properly support the diversity
of apple sound HW setup, including dual codecs, several i2s busses, all the
new codecs used in the new machines, proper clock switching with digital,
etc etc etc...
This patch applies on top of the other PowerMac sound patches I posted in
the past couple of days (new powerbook support and sleep fixes).
Note: This is a FAQ entry for PowerMac sound support with TI codecs: They
have a feature called "DRC" which is automatically enabled for the internal
speaker (at least when auto mute control is enabled) which will cause your
sound to fade out to nothing after half a second of playback if you don't
set a proper "DRC Range" in the mixer. So if you have a problem like that,
check alsamixer and raise your DRC Range to something reasonable.
Note2: This patch will also add auto-mute of the speaker when line-out jack
is used on some earlier desktop G4s (and on the G5) in addition to the
headphone jack. If that behaviour isn't what you want, just disable
auto-muting and use the manual mute controls in alsamixer.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2005-04-17 06:24:32 +08:00
|
|
|
dma_free_coherent(&chip->pdev->dev, BEEP_BUFLEN * 4,
|
|
|
|
chip->beep->buf, chip->beep->addr);
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree(chip->beep);
|
|
|
|
chip->beep = NULL;
|
|
|
|
}
|
|
|
|
}
|