internal speakers on Pismo
Hi,
after three days of fiddling with hardware registers, I managed to make
it sound!
I'll work with Iain Sandoe to clean it up and merge it into the
mainstream kernel.
For the really impatient:
- use Ben Herrenschidt's 2.2.17pre10-ben2 kernel source
- apply Iain's dmasound patch
- replace drivers/sound/dmasound/dmasound_awacs.c and .../awacs_defs.h
with the attached versions
Note that this contains ugly hacks, lots of debugging code, may crash on
any machine other than Pismo, ... you got the picture.
Matthias
--
Matthias Pfisterer <mailto:Matthias.Pfisterer@gmx.de>
Share your knowledge.
It's a way to achieve immortality.
(from a nepalese mantra)
Java Sound Examples:
http://rupert.informatik.uni-stuttgart.de/~pfistere/jsexamples/
Tritonus, the open source implementation of the Java Sound API:
http://tritonus.sourceforge.net/
--------------------------------------------------------------
/*
* linux/drivers/sound/dmasound/dmasound_awacs.c
*
* PowerMac `AWACS' and `Burgundy' DMA Sound Driver
*
* See linux/drivers/sound/dmasound/dmasound_core.c for copyright and credits
*/
#include <linux/module.h>
#include <linux/config.h>
#include <linux/malloc.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/soundcard.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
#define COMPILING_2_4_X 1
#endif
#include <linux/nvram.h>
#include <linux/vt_kern.h>
#ifdef COMPILING_2_4_X
#include <linux/adb.h>
#include <linux/cuda.h>
#include <linux/pmu.h>
#else
#include <asm/adb.h>
#include <asm/cuda.h>
#include <asm/pmu.h>
#endif
#include <asm/uaccess.h>
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
#include <asm/dbdma.h>
#include <asm/feature.h>
#include <asm/irq.h>
#include "awacs_defs.h"
#include "dmasound.h"
#ifdef COMPILING_2_4_X
/*
* reference to the CUDA controller type
*/
extern sys_ctrler_t sys_ctrler ;
#endif
/*
* Interrupt numbers and addresses, obtained from the device tree.
*/
static int awacs_irq, awacs_tx_irq, awacs_rx_irq;
static volatile struct awacs_regs *awacs;
static volatile struct dbdma_regs *awacs_txdma, *awacs_rxdma;
static int awacs_rate_index;
static int awacs_subframe;
static int awacs_spkr_vol;
static struct device_node* awacs_node;
static char awacs_name[64];
static int awacs_revision;
#define AWACS_BURGUNDY 100 /* fake revision # for burgundy */
/*
* Space for the DBDMA command blocks.
*/
static void *awacs_tx_cmd_space;
static volatile struct dbdma_cmd *awacs_tx_cmds;
static void *awacs_rx_cmd_space;
static volatile struct dbdma_cmd *awacs_rx_cmds;
/*
* Cached values of AWACS registers (we can't read them).
* Except on the burgundy. XXX
*/
// screamer has additional registers 5, 6 and 7.
int awacs_reg[8];
int is_screamer = 1;
#define HAS_16BIT_TABLES
#undef HAS_8BIT_TABLES
/*
* 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 */
static int beep_volume = BEEP_VOLUME;
static int beep_playing = 0;
static int awacs_beep_state = 0;
static short *beep_buf;
static volatile struct dbdma_cmd *beep_dbdma_cmd;
static void (*orig_mksound)(unsigned int, unsigned int);
static int is_pbook_3400;
static unsigned char *latch_base;
static int is_pbook_G3;
static unsigned char *macio_base;
/* Burgundy functions */
static void awacs_burgundy_wcw(unsigned addr,unsigned newval);
static unsigned awacs_burgundy_rcw(unsigned addr);
static void awacs_burgundy_write_volume(unsigned address, int volume);
static int awacs_burgundy_read_volume(unsigned address);
static void awacs_burgundy_write_mvolume(unsigned address, int volume);
static int awacs_burgundy_read_mvolume(unsigned address);
#ifdef CONFIG_PMAC_PBOOK
/*
* Stuff for restoring after a sleep.
*/
static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when);
struct pmu_sleep_notifier awacs_sleep_notifier = {
awacs_sleep_notify, SLEEP_LEVEL_SOUND,
};
#endif /* CONFIG_PMAC_PBOOK */
static int expand_bal; /* Balance factor for expanding (not volume!) */
static int expand_data; /* Data for expanding */
/*** Translations ************************************************************/
/* ++TeSche: radically changed for new expanding purposes...
*
* These two routines now deal with copying/expanding/translating the samples
* from user space into our buffer at the right frequency. They take care about
* how much data there's actually to read, how much buffer space there is and
* to convert samples into the right frequency/encoding. They will only work on
* complete samples so it may happen they leave some bytes in the input stream
* if the user didn't write a multiple of the current sample size. They both
* return the number of bytes they've used from both streams so you may detect
* such a situation. Luckily all programs should be able to cope with that.
*
* I think I've optimized anything as far as one can do in plain C, all
* variables should fit in registers and the loops are really short. There's
* one loop for every possible situation. Writing a more generalized and thus
* parameterized loop would only produce slower code. Feel free to optimize
* this in assembler if you like. :)
*
* I think these routines belong here because they're not yet really hardware
* independent, especially the fact that the Falcon can play 16bit samples
* only in stereo is hardcoded in both of them!
*
* ++geert: split in even more functions (one per format)
*/
static ssize_t pmac_ct_law(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ct_s8(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ct_u8(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ct_s16(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ct_u16(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ctx_law(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ctx_s8(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ctx_u8(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ctx_s16(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ctx_u16(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ct_s16_read(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
static ssize_t pmac_ct_u16_read(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft);
/*** Low level stuff *********************************************************/
static void PMacOpen(void);
static void PMacRelease(void);
static void *PMacAlloc(unsigned int size, int flags);
static void PMacFree(void *ptr, unsigned int size);
static int PMacIrqInit(void);
#ifdef MODULE
static void PMacIrqCleanup(void);
#endif
static void PMacSilence(void);
static void PMacInit(void);
static int PMacSetFormat(int format);
static int PMacSetVolume(int volume);
static void PMacPlay(void);
static void PMacRecord(void);
static void pmac_awacs_tx_intr(int irq, void *devid, struct pt_regs *regs);
static void pmac_awacs_rx_intr(int irq, void *devid, struct pt_regs *regs);
static void pmac_awacs_intr(int irq, void *devid, struct pt_regs *regs);
static void awacs_write(int val);
static int awacs_get_volume(int reg, int lshift);
static int awacs_volume_setter(int volume, int n, int mute, int lshift);
static void awacs_mksound(unsigned int hz, unsigned int ticks);
static void awacs_nosound(unsigned long xx);
/*** Mid level stuff **********************************************************/
static int PMacMixerIoctl(u_int cmd, u_long arg);
static void PMacWriteSqSetup(void);
static void PMacReadSqSetup(void);
static void PMacAbortRead(void);
/*** Translations ************************************************************/
static ssize_t pmac_ct_law(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
short *table = dmasound.soft.format == AFMT_MU_LAW
? dmasound_ulaw2dma16 : dmasound_alaw2dma16;
ssize_t count, used;
short *p = (short *) &frame[*frameUsed];
int val, stereo = dmasound.soft.stereo;
frameLeft >>= 2;
if (stereo)
userCount >>= 1;
used = count = min(userCount, frameLeft);
while (count > 0) {
u_char data;
if (get_user(data, userPtr++))
return -EFAULT;
val = table[data];
*p++ = val;
if (stereo) {
if (get_user(data, userPtr++))
return -EFAULT;
val = table[data];
}
*p++ = val;
count--;
}
*frameUsed += used * 4;
return stereo? used * 2: used;
}
static ssize_t pmac_ct_s8(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
ssize_t count, used;
short *p = (short *) &frame[*frameUsed];
int val, stereo = dmasound.soft.stereo;
frameLeft >>= 2;
if (stereo)
userCount >>= 1;
used = count = min(userCount, frameLeft);
while (count > 0) {
u_char data;
if (get_user(data, userPtr++))
return -EFAULT;
val = data << 8;
*p++ = val;
if (stereo) {
if (get_user(data, userPtr++))
return -EFAULT;
val = data << 8;
}
*p++ = val;
count--;
}
*frameUsed += used * 4;
return stereo? used * 2: used;
}
static ssize_t pmac_ct_u8(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
ssize_t count, used;
short *p = (short *) &frame[*frameUsed];
int val, stereo = dmasound.soft.stereo;
frameLeft >>= 2;
if (stereo)
userCount >>= 1;
used = count = min(userCount, frameLeft);
while (count > 0) {
u_char data;
if (get_user(data, userPtr++))
return -EFAULT;
val = (data ^ 0x80) << 8;
*p++ = val;
if (stereo) {
if (get_user(data, userPtr++))
return -EFAULT;
val = (data ^ 0x80) << 8;
}
*p++ = val;
count--;
}
*frameUsed += used * 4;
return stereo? used * 2: used;
}
static ssize_t pmac_ct_s16(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
ssize_t count, used;
int stereo = dmasound.soft.stereo;
short *fp = (short *) &frame[*frameUsed];
frameLeft >>= 2;
userCount >>= (stereo? 2: 1);
used = count = min(userCount, frameLeft);
if (!stereo) {
short *up = (short *) userPtr;
while (count > 0) {
short data;
if (get_user(data, up++))
return -EFAULT;
*fp++ = data;
*fp++ = data;
count--;
}
} else {
if (copy_from_user(fp, userPtr, count * 4))
return -EFAULT;
}
*frameUsed += used * 4;
return stereo? used * 4: used * 2;
}
static ssize_t pmac_ct_u16(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
ssize_t count, used;
int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
int stereo = dmasound.soft.stereo;
short *fp = (short *) &frame[*frameUsed];
short *up = (short *) userPtr;
frameLeft >>= 2;
userCount >>= (stereo? 2: 1);
used = count = min(userCount, frameLeft);
while (count > 0) {
int data;
if (get_user(data, up++))
return -EFAULT;
data ^= mask;
*fp++ = data;
if (stereo) {
if (get_user(data, up++))
return -EFAULT;
data ^= mask;
}
*fp++ = data;
count--;
}
*frameUsed += used * 4;
return stereo? used * 4: used * 2;
}
static ssize_t pmac_ctx_law(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
unsigned short *table = (unsigned short *)
(dmasound.soft.format == AFMT_MU_LAW
? dmasound_ulaw2dma16 : dmasound_alaw2dma16);
unsigned int data = expand_data;
unsigned int *p = (unsigned int *) &frame[*frameUsed];
int bal = expand_bal;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int utotal, ftotal;
int stereo = dmasound.soft.stereo;
frameLeft >>= 2;
if (stereo)
userCount >>= 1;
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
u_char c;
if (bal < 0) {
if (userCount == 0)
break;
if (get_user(c, userPtr++))
return -EFAULT;
data = table[c];
if (stereo) {
if (get_user(c, userPtr++))
return -EFAULT;
data = (data << 16) + table[c];
} else
data = (data << 16) + data;
userCount--;
bal += hSpeed;
}
*p++ = data;
frameLeft--;
bal -= sSpeed;
}
expand_bal = bal;
expand_data = data;
*frameUsed += (ftotal - frameLeft) * 4;
utotal -= userCount;
return stereo? utotal * 2: utotal;
}
static ssize_t pmac_ctx_s8(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
unsigned int *p = (unsigned int *) &frame[*frameUsed];
unsigned int data = expand_data;
int bal = expand_bal;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int stereo = dmasound.soft.stereo;
int utotal, ftotal;
frameLeft >>= 2;
if (stereo)
userCount >>= 1;
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
u_char c;
if (bal < 0) {
if (userCount == 0)
break;
if (get_user(c, userPtr++))
return -EFAULT;
data = c << 8;
if (stereo) {
if (get_user(c, userPtr++))
return -EFAULT;
data = (data << 16) + (c << 8);
} else
data = (data << 16) + data;
userCount--;
bal += hSpeed;
}
*p++ = data;
frameLeft--;
bal -= sSpeed;
}
expand_bal = bal;
expand_data = data;
*frameUsed += (ftotal - frameLeft) * 4;
utotal -= userCount;
return stereo? utotal * 2: utotal;
}
static ssize_t pmac_ctx_u8(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
unsigned int *p = (unsigned int *) &frame[*frameUsed];
unsigned int data = expand_data;
int bal = expand_bal;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int stereo = dmasound.soft.stereo;
int utotal, ftotal;
frameLeft >>= 2;
if (stereo)
userCount >>= 1;
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
u_char c;
if (bal < 0) {
if (userCount == 0)
break;
if (get_user(c, userPtr++))
return -EFAULT;
data = (c ^ 0x80) << 8;
if (stereo) {
if (get_user(c, userPtr++))
return -EFAULT;
data = (data << 16) + ((c ^ 0x80) << 8);
} else
data = (data << 16) + data;
userCount--;
bal += hSpeed;
}
*p++ = data;
frameLeft--;
bal -= sSpeed;
}
expand_bal = bal;
expand_data = data;
*frameUsed += (ftotal - frameLeft) * 4;
utotal -= userCount;
return stereo? utotal * 2: utotal;
}
static ssize_t pmac_ctx_s16(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
unsigned int *p = (unsigned int *) &frame[*frameUsed];
unsigned int data = expand_data;
unsigned short *up = (unsigned short *) userPtr;
int bal = expand_bal;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int stereo = dmasound.soft.stereo;
int utotal, ftotal;
frameLeft >>= 2;
userCount >>= (stereo? 2: 1);
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
unsigned short c;
if (bal < 0) {
if (userCount == 0)
break;
if (get_user(data, up++))
return -EFAULT;
if (stereo) {
if (get_user(c, up++))
return -EFAULT;
data = (data << 16) + c;
} else
data = (data << 16) + data;
userCount--;
bal += hSpeed;
}
*p++ = data;
frameLeft--;
bal -= sSpeed;
}
expand_bal = bal;
expand_data = data;
*frameUsed += (ftotal - frameLeft) * 4;
utotal -= userCount;
return stereo? utotal * 4: utotal * 2;
}
static ssize_t pmac_ctx_u16(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
unsigned int *p = (unsigned int *) &frame[*frameUsed];
unsigned int data = expand_data;
unsigned short *up = (unsigned short *) userPtr;
int bal = expand_bal;
int hSpeed = dmasound.hard.speed, sSpeed = dmasound.soft.speed;
int stereo = dmasound.soft.stereo;
int utotal, ftotal;
frameLeft >>= 2;
userCount >>= (stereo? 2: 1);
ftotal = frameLeft;
utotal = userCount;
while (frameLeft) {
unsigned short c;
if (bal < 0) {
if (userCount == 0)
break;
if (get_user(data, up++))
return -EFAULT;
data ^= mask;
if (stereo) {
if (get_user(c, up++))
return -EFAULT;
data = (data << 16) + (c ^ mask);
} else
data = (data << 16) + data;
userCount--;
bal += hSpeed;
}
*p++ = data;
frameLeft--;
bal -= sSpeed;
}
expand_bal = bal;
expand_data = data;
*frameUsed += (ftotal - frameLeft) * 4;
utotal -= userCount;
return stereo? utotal * 4: utotal * 2;
}
static ssize_t pmac_ct_s8_read(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
ssize_t count, used;
short *p = (short *) &frame[*frameUsed];
int val, stereo = dmasound.soft.stereo;
frameLeft >>= 2;
if (stereo)
userCount >>= 1;
used = count = min(userCount, frameLeft);
while (count > 0) {
u_char data;
val = *p++;
data = val >> 8;
if (put_user(data, (u_char *)userPtr++))
return -EFAULT;
if (stereo) {
val = *p;
data = val >> 8;
if (put_user(data, (u_char *)userPtr++))
return -EFAULT;
}
p++;
count--;
}
*frameUsed += used * 4;
return stereo? used * 2: used;
}
static ssize_t pmac_ct_u8_read(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
ssize_t count, used;
short *p = (short *) &frame[*frameUsed];
int val, stereo = dmasound.soft.stereo;
frameLeft >>= 2;
if (stereo)
userCount >>= 1;
used = count = min(userCount, frameLeft);
while (count > 0) {
u_char data;
val = *p++;
data = (val >> 8) ^ 0x80;
if (put_user(data, (u_char *)userPtr++))
return -EFAULT;
if (stereo) {
val = *p;
data = (val >> 8) ^ 0x80;
if (put_user(data, (u_char *)userPtr++))
return -EFAULT;
}
p++;
count--;
}
*frameUsed += used * 4;
return stereo? used * 2: used;
}
static ssize_t pmac_ct_s16_read(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
ssize_t count, used;
int stereo = dmasound.soft.stereo;
short *fp = (short *) &frame[*frameUsed];
frameLeft >>= 2;
userCount >>= (stereo? 2: 1);
used = count = min(userCount, frameLeft);
if (!stereo) {
short *up = (short *) userPtr;
while (count > 0) {
short data;
data = *fp;
if (put_user(data, up++))
return -EFAULT;
fp+=2;
count--;
}
} else {
if (copy_to_user((u_char *)userPtr, fp, count * 4))
return -EFAULT;
}
*frameUsed += used * 4;
return stereo? used * 4: used * 2;
}
static ssize_t pmac_ct_u16_read(const u_char *userPtr, size_t userCount,
u_char frame[], ssize_t *frameUsed,
ssize_t frameLeft)
{
ssize_t count, used;
int mask = (dmasound.soft.format == AFMT_U16_LE? 0x0080: 0x8000);
int stereo = dmasound.soft.stereo;
short *fp = (short *) &frame[*frameUsed];
short *up = (short *) userPtr;
frameLeft >>= 2;
userCount >>= (stereo? 2: 1);
used = count = min(userCount, frameLeft);
while (count > 0) {
int data;
data = *fp++;
data ^= mask;
if (put_user(data, up++))
return -EFAULT;
if (stereo) {
data = *fp;
data ^= mask;
if (put_user(data, up++))
return -EFAULT;
}
fp++;
count--;
}
*frameUsed += used * 4;
return stereo? used * 4: used * 2;
}
static TRANS transAwacsNormal = {
ct_ulaw: pmac_ct_law,
ct_alaw: pmac_ct_law,
ct_s8: pmac_ct_s8,
ct_u8: pmac_ct_u8,
ct_s16be: pmac_ct_s16,
ct_u16be: pmac_ct_u16,
ct_s16le: pmac_ct_s16,
ct_u16le: pmac_ct_u16,
};
static TRANS transAwacsExpand = {
ct_ulaw: pmac_ctx_law,
ct_alaw: pmac_ctx_law,
ct_s8: pmac_ctx_s8,
ct_u8: pmac_ctx_u8,
ct_s16be: pmac_ctx_s16,
ct_u16be: pmac_ctx_u16,
ct_s16le: pmac_ctx_s16,
ct_u16le: pmac_ctx_u16,
};
static TRANS transAwacsNormalRead = {
ct_s8: pmac_ct_s8_read,
ct_u8: pmac_ct_u8_read,
ct_s16be: pmac_ct_s16_read,
ct_u16be: pmac_ct_u16_read,
ct_s16le: pmac_ct_s16_read,
ct_u16le: pmac_ct_u16_read,
};
/*** Low level stuff *********************************************************/
/*
* PCI PowerMac, with AWACS and DBDMA.
*/
static void PMacOpen(void)
{
MOD_INC_USE_COUNT;
}
static void PMacRelease(void)
{
MOD_DEC_USE_COUNT;
}
static void *PMacAlloc(unsigned int size, int flags)
{
return kmalloc(size, flags);
}
static void PMacFree(void *ptr, unsigned int size)
{
kfree(ptr);
}
static int __init PMacIrqInit(void)
{
if (request_irq(awacs_irq, pmac_awacs_intr, 0, "AWACS", 0)
|| request_irq(awacs_tx_irq, pmac_awacs_tx_intr, 0, "AWACS out", 0)
|| request_irq(awacs_rx_irq, pmac_awacs_rx_intr, 0, "AWACS in", 0))
return 0;
return 1;
}
#ifdef MODULE
static void PMacIrqCleanup(void)
{
/* turn off output dma */
out_le32(&awacs_txdma->control, RUN<<16);
/* disable interrupts from awacs interface */
out_le32(&awacs->control, in_le32(&awacs->control) & 0xfff);
#ifdef CONFIG_PMAC_PBOOK
if (is_pbook_G3) {
feature_clear(awacs_node, FEATURE_Sound_power);
feature_clear(awacs_node, FEATURE_Sound_CLK_enable);
}
#endif
free_irq(awacs_irq, 0);
free_irq(awacs_tx_irq, 0);
free_irq(awacs_rx_irq, 0);
kfree(awacs_tx_cmd_space);
if (awacs_rx_cmd_space)
kfree(awacs_rx_cmd_space);
if (beep_buf)
kfree(beep_buf);
kd_mksound = orig_mksound;
#ifdef CONFIG_PMAC_PBOOK
pmu_unregister_sleep_notifier(&awacs_sleep_notifier);
#endif
}
#endif /* MODULE */
static void PMacSilence(void)
{
/* turn off output dma */
out_le32(&awacs_txdma->control, RUN<<16);
}
static int awacs_freqs[8] = {
44100, 29400, 22050, 17640, 14700, 11025, 8820, 7350
};
static int awacs_freqs_ok[8] = { 1, 1, 1, 1, 1, 1, 1, 1 };
static void PMacInit(void)
{
int i, tolerance;
switch (dmasound.soft.format) {
case AFMT_S16_LE:
case AFMT_U16_LE:
dmasound.hard.format = AFMT_S16_LE;
break;
default:
dmasound.hard.format = AFMT_S16_BE;
break;
}
dmasound.hard.stereo = 1;
dmasound.hard.size = 16;
/*
* If we have a sample rate which is within catchRadius percent
* of the requested value, we don't have to expand the samples.
* Otherwise choose the next higher rate.
* N.B.: burgundy awacs (iMac and later) only works at 44100 Hz.
*/
i = 8;
do {
tolerance = catchRadius * awacs_freqs[--i] / 100;
if (awacs_freqs_ok[i]
&& dmasound.soft.speed <= awacs_freqs[i] + tolerance)
break;
} while (i > 0);
if (dmasound.soft.speed >= awacs_freqs[i] - tolerance)
dmasound.trans_write = &transAwacsNormal;
else
dmasound.trans_write = &transAwacsExpand;
dmasound.trans_read = &transAwacsNormalRead;
dmasound.hard.speed = awacs_freqs[i];
awacs_rate_index = i;
/* XXX disable error interrupt on burgundy for now */
out_le32(&awacs->control, MASK_IEPC | (i << 8) | 0x11
| (awacs_revision < AWACS_BURGUNDY? MASK_IEE: 0));
awacs_reg[1] = (awacs_reg[1] & ~MASK_SAMPLERATE) | (i << 3);
awacs_write(awacs_reg[1] | MASK_ADDR1);
out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE);
/* We really want to execute a DMA stop command, after the AWACS
* is initialized.
* For reasons I don't understand, it stops the hissing noise
* common to many PowerBook G3 systems (like mine :-).
*/
out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16);
st_le16(&beep_dbdma_cmd->command, DBDMA_STOP);
out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd));
out_le32(&awacs_txdma->control, RUN | (RUN << 16));
expand_bal = -dmasound.soft.speed;
}
static int PMacSetFormat(int format)
{
int size;
switch (format) {
case AFMT_QUERY:
return dmasound.soft.format;
case AFMT_MU_LAW:
case AFMT_A_LAW:
case AFMT_U8:
case AFMT_S8:
size = 8;
break;
case AFMT_S16_BE:
case AFMT_U16_BE:
case AFMT_S16_LE:
case AFMT_U16_LE:
size = 16;
break;
default: /* :-) */
printk(KERN_ERR "dmasound: unknown format 0x%x, using AFMT_U8\n",
format);
size = 8;
format = AFMT_U8;
}
dmasound.soft.format = format;
dmasound.soft.size = size;
if (dmasound.minDev == SND_DEV_DSP) {
dmasound.dsp.format = format;
dmasound.dsp.size = size;
}
PMacInit();
return format;
}
#define AWACS_VOLUME_TO_MASK(x) (15 - ((((x) - 1) * 15) / 99))
#define AWACS_MASK_TO_VOLUME(y) (100 - ((y) * 99 / 15))
static int awacs_get_volume(int reg, int lshift)
{
int volume;
volume = AWACS_MASK_TO_VOLUME((reg >> lshift) & 0xf);
volume |= AWACS_MASK_TO_VOLUME(reg & 0xf) << 8;
return volume;
}
static int awacs_volume_setter(int volume, int n, int mute, int lshift)
{
int r1, rn;
if (mute && volume == 0) {
r1 = awacs_reg[1] | mute;
} else {
r1 = awacs_reg[1] & ~mute;
rn = awacs_reg[n] & ~(0xf | (0xf << lshift));
rn |= ((AWACS_VOLUME_TO_MASK(volume & 0xff) & 0xf) << lshift);
rn |= AWACS_VOLUME_TO_MASK((volume >> 8) & 0xff) & 0xf;
awacs_reg[n] = rn;
awacs_write((n << 12) | rn);
volume = awacs_get_volume(rn, lshift);
}
if (r1 != awacs_reg[1]) {
awacs_reg[1] = r1;
awacs_write(r1 | MASK_ADDR1);
}
return volume;
}
static int PMacSetVolume(int volume)
{
return awacs_volume_setter(volume, 2, MASK_AMUTE, 6);
}
static void PMacPlay(void)
{
volatile struct dbdma_cmd *cp;
int i, count;
unsigned long flags;
save_flags(flags); cli();
if (awacs_beep_state) {
/* sound takes precedence over beeps */
out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
out_le32(&awacs->control,
(in_le32(&awacs->control) & ~0x1f00)
| (awacs_rate_index << 8));
out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE);
out_le32(&awacs_txdma->cmdptr, virt_to_bus(&(awacs_tx_cmds[(write_sq.front+write_sq.active) % write_sq.max_count])));
beep_playing = 0;
awacs_beep_state = 0;
}
i = write_sq.front + write_sq.active;
if (i >= write_sq.max_count)
i -= write_sq.max_count;
while (write_sq.active < 2 && write_sq.active < write_sq.count) {
count = (write_sq.count == write_sq.active + 1)?write_sq.rear_size:write_sq.block_size;
if (count < write_sq.block_size && !write_sq.syncing)
/* last block not yet filled, and we're not syncing. */
break;
cp = &awacs_tx_cmds[i];
st_le16(&cp->req_count, count);
st_le16(&cp->xfer_status, 0);
if (++i >= write_sq.max_count)
i = 0;
out_le16(&awacs_tx_cmds[i].command, DBDMA_STOP);
out_le16(&cp->command, OUTPUT_MORE + INTR_ALWAYS);
if (write_sq.active == 0)
out_le32(&awacs_txdma->cmdptr, virt_to_bus(cp));
out_le32(&awacs_txdma->control, ((RUN|WAKE) << 16) + (RUN|WAKE));
++write_sq.active;
}
restore_flags(flags);
}
static void PMacRecord(void)
{
unsigned long flags;
if (read_sq.active)
return;
save_flags(flags); cli();
/* This is all we have to do......Just start it up.
*/
out_le32(&awacs_rxdma->control, ((RUN|WAKE) << 16) + (RUN|WAKE));
read_sq.active = 1;
restore_flags(flags);
}
static void
pmac_awacs_tx_intr(int irq, void *devid, struct pt_regs *regs)
{
int i = write_sq.front;
int stat;
volatile struct dbdma_cmd *cp;
while (write_sq.active > 0) {
cp = &awacs_tx_cmds[i];
stat = ld_le16(&cp->xfer_status);
if ((stat & ACTIVE) == 0)
break; /* this frame is still going */
--write_sq.count;
--write_sq.active;
if (++i >= write_sq.max_count)
i = 0;
}
if (i != write_sq.front)
WAKE_UP(write_sq.action_queue);
write_sq.front = i;
PMacPlay();
if (!write_sq.active)
WAKE_UP(write_sq.sync_queue);
}
static void
pmac_awacs_rx_intr(int irq, void *devid, struct pt_regs *regs)
{
/* For some reason on my PowerBook G3, I get one interrupt
* when the interrupt vector is installed (like something is
* pending). This happens before the dbdma is initialize by
* us, so I just check the command pointer and if it is zero,
* just blow it off.
*/
if (in_le32(&awacs_rxdma->cmdptr) == 0)
return;
/* We also want to blow 'em off when shutting down.
*/
if (read_sq.active == 0)
return;
/* Check multiple buffers in case we were held off from
* interrupt processing for a long time. Geeze, I really hope
* this doesn't happen.
*/
while (awacs_rx_cmds[read_sq.rear].xfer_status) {
/* Clear status and move on to next buffer.
*/
awacs_rx_cmds[read_sq.rear].xfer_status = 0;
read_sq.rear++;
/* Wrap the buffer ring.
*/
if (read_sq.rear >= read_sq.max_active)
read_sq.rear = 0;
/* If we have caught up to the front buffer, bump it.
* This will cause weird (but not fatal) results if the
* read loop is currently using this buffer. The user is
* behind in this case anyway, so weird things are going
* to happen.
*/
if (read_sq.rear == read_sq.front) {
read_sq.front++;
if (read_sq.front >= read_sq.max_active)
read_sq.front = 0;
}
}
WAKE_UP(read_sq.action_queue);
}
static void
pmac_awacs_intr(int irq, void *devid, struct pt_regs *regs)
{
int ctrl = in_le32(&awacs->control);
if (ctrl & MASK_PORTCHG) {
/* do something when headphone is plugged/unplugged? */
}
if (ctrl & MASK_CNTLERR) {
int err = (in_le32(&awacs->codec_stat) & MASK_ERRCODE) >> 16;
if (err != 0 && awacs_revision < AWACS_BURGUNDY)
printk(KERN_ERR "AWACS: error %x\n", err);
}
/* Writing 1s to the CNTLERR and PORTCHG bits clears them... */
out_le32(&awacs->control, ctrl);
}
static void
awacs_write(int val)
{
if (awacs_revision >= AWACS_BURGUNDY)
return;
printk(__FILE__ ": writing to codec control register: %X\n", val);
while (in_le32(&awacs->codec_ctrl) & MASK_NEWECMD)
; /* XXX should have timeout */
out_le32(&awacs->codec_ctrl, val | (awacs_subframe << 22));
}
static void awacs_nosound(unsigned long xx)
{
unsigned long flags;
save_flags(flags); cli();
if (beep_playing) {
st_le16(&beep_dbdma_cmd->command, DBDMA_STOP);
out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
out_le32(&awacs->control,
(in_le32(&awacs->control) & ~0x1f00)
| (awacs_rate_index << 8));
out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE);
beep_playing = 0;
}
restore_flags(flags);
}
static struct timer_list beep_timer = {
function: awacs_nosound
};
static void awacs_mksound(unsigned int hz, unsigned int ticks)
{
unsigned long flags;
int beep_speed = 0;
int srate;
int period, ncycles, nsamples;
int i, j, f;
short *p;
static int beep_hz_cache;
static int beep_nsamples_cache;
static int beep_volume_cache;
for (i = 0; i < 8 && awacs_freqs[i] >= BEEP_SRATE; ++i)
if (awacs_freqs_ok[i])
beep_speed = i;
srate = awacs_freqs[beep_speed];
if (hz <= srate / BEEP_BUFLEN || hz > srate / 2) {
#if 1
/* this is a hack for broken X server code */
hz = 750;
ticks = 12;
#else
/* cancel beep currently playing */
awacs_nosound(0);
return;
#endif
}
save_flags(flags); cli();
del_timer(&beep_timer);
if (ticks) {
beep_timer.expires = jiffies + ticks;
add_timer(&beep_timer);
}
if (beep_playing || write_sq.active || beep_buf == NULL) {
restore_flags(flags);
return; /* too hard, sorry :-( */
}
beep_playing = 1;
st_le16(&beep_dbdma_cmd->command, OUTPUT_MORE + BR_ALWAYS);
restore_flags(flags);
if (hz == beep_hz_cache && beep_volume == beep_volume_cache) {
nsamples = beep_nsamples_cache;
} 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_cache = hz;
beep_volume_cache = beep_volume;
beep_nsamples_cache = nsamples;
}
st_le16(&beep_dbdma_cmd->req_count, nsamples*4);
st_le16(&beep_dbdma_cmd->xfer_status, 0);
st_le32(&beep_dbdma_cmd->cmd_dep, virt_to_bus(beep_dbdma_cmd));
st_le32(&beep_dbdma_cmd->phy_addr, virt_to_bus(beep_buf));
awacs_beep_state = 1;
save_flags(flags); cli();
if (beep_playing) { /* i.e. haven't been terminated already */
out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16);
out_le32(&awacs->control,
(in_le32(&awacs->control) & ~0x1f00)
| (beep_speed << 8));
out_le32(&awacs->byteswap, 0);
out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd));
out_le32(&awacs_txdma->control, RUN | (RUN << 16));
}
restore_flags(flags);
}
#ifdef CONFIG_PMAC_PBOOK
/*
* Save state when going to sleep, restore it afterwards.
*/
static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when)
{
switch (when) {
case PBOOK_SLEEP_NOW:
/* XXX we should stop any dma in progress when going to sleep
and restart it when we wake. */
PMacSilence();
disable_irq(awacs_irq);
disable_irq(awacs_tx_irq);
if (is_pbook_G3) {
feature_clear(awacs_node, FEATURE_Sound_CLK_enable);
feature_clear(awacs_node, FEATURE_Sound_power);
}
break;
case PBOOK_WAKE:
/* There is still a problem on wake. Sound seems to work fine
if I launch mpg123 and resumes fine if mpg123 was playing,
but the console beep is dead until I do something with the
mixer. Probably yet another timing issue */
if (!feature_test(awacs_node, FEATURE_Sound_CLK_enable)
|| !feature_test(awacs_node, FEATURE_Sound_power)) {
/* these aren't present on the 3400 AFAIK -- paulus */
feature_set(awacs_node, FEATURE_Sound_CLK_enable);
feature_set(awacs_node, FEATURE_Sound_power);
mdelay(1000);
}
out_le32(&awacs->control, MASK_IEPC
| (awacs_rate_index << 8) | 0x11
| (awacs_revision < AWACS_BURGUNDY? MASK_IEE: 0));
awacs_write(awacs_reg[0] | MASK_ADDR0);
awacs_write(awacs_reg[1] | MASK_ADDR1);
awacs_write(awacs_reg[2] | MASK_ADDR2);
awacs_write(awacs_reg[4] | MASK_ADDR4);
// TODO: additional screamer registers
out_le32(&awacs->byteswap, dmasound.hard.format != AFMT_S16_BE);
enable_irq(awacs_irq);
enable_irq(awacs_tx_irq);
if (awacs_revision == 3) {
mdelay(100);
awacs_write(0x6000);
mdelay(2);
awacs_write(awacs_reg[1] | MASK_ADDR1);
}
/* enable CD sound input */
if (macio_base && is_pbook_G3) {
out_8(macio_base + 0x37, 3);
} else if (is_pbook_3400) {
feature_set(awacs_node, FEATURE_IOBUS_enable);
udelay(10);
in_8(latch_base + 0x190);
}
/* Resume pending sounds. */
PMacPlay();
}
return PBOOK_SLEEP_OK;
}
#endif /* CONFIG_PMAC_PBOOK */
/* All the burgundy functions: */
/* Waits for busy flag to clear */
inline static void
awacs_burgundy_busy_wait(void)
{
while (in_le32(&awacs->codec_ctrl) & MASK_NEWECMD)
;
}
inline static void
awacs_burgundy_extend_wait(void)
{
while (!(in_le32(&awacs->codec_stat) & MASK_EXTEND))
;
while (in_le32(&awacs->codec_stat) & MASK_EXTEND)
;
}
static void
awacs_burgundy_wcw(unsigned addr, unsigned val)
{
out_le32(&awacs->codec_ctrl, addr + 0x200c00 + (val & 0xff));
awacs_burgundy_busy_wait();
out_le32(&awacs->codec_ctrl, addr + 0x200d00 +((val>>8) & 0xff));
awacs_burgundy_busy_wait();
out_le32(&awacs->codec_ctrl, addr + 0x200e00 +((val>>16) & 0xff));
awacs_burgundy_busy_wait();
out_le32(&awacs->codec_ctrl, addr + 0x200f00 +((val>>24) & 0xff));
awacs_burgundy_busy_wait();
}
static unsigned
awacs_burgundy_rcw(unsigned addr)
{
unsigned val = 0;
unsigned long flags;
/* should have timeouts here */
save_flags(flags); cli();
out_le32(&awacs->codec_ctrl, addr + 0x100000);
awacs_burgundy_busy_wait();
awacs_burgundy_extend_wait();
val += (in_le32(&awacs->codec_stat) >> 4) & 0xff;
out_le32(&awacs->codec_ctrl, addr + 0x100100);
awacs_burgundy_busy_wait();
awacs_burgundy_extend_wait();
val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<8;
out_le32(&awacs->codec_ctrl, addr + 0x100200);
awacs_burgundy_busy_wait();
awacs_burgundy_extend_wait();
val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<16;
out_le32(&awacs->codec_ctrl, addr + 0x100300);
awacs_burgundy_busy_wait();
awacs_burgundy_extend_wait();
val += ((in_le32(&awacs->codec_stat)>>4) & 0xff) <<24;
restore_flags(flags);
return val;
}
static void
awacs_burgundy_wcb(unsigned addr, unsigned val)
{
out_le32(&awacs->codec_ctrl, addr + 0x300000 + (val & 0xff));
awacs_burgundy_busy_wait();
}
static unsigned
awacs_burgundy_rcb(unsigned addr)
{
unsigned val = 0;
unsigned long flags;
/* should have timeouts here */
save_flags(flags); cli();
out_le32(&awacs->codec_ctrl, addr + 0x100000);
awacs_burgundy_busy_wait();
awacs_burgundy_extend_wait();
val += (in_le32(&awacs->codec_stat) >> 4) & 0xff;
restore_flags(flags);
return val;
}
static int
awacs_burgundy_check(void)
{
/* Checks to see the chip is alive and kicking */
int error = in_le32(&awacs->codec_ctrl) & MASK_ERRCODE;
return error == 0xf0000;
}
static int
awacs_burgundy_init(void)
{
if (awacs_burgundy_check()) {
printk(KERN_WARNING "AWACS: disabled by MacOS :-(\n");
return 1;
}
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_OUTPUTENABLES,
DEF_BURGUNDY_OUTPUTENABLES);
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
DEF_BURGUNDY_MORE_OUTPUTENABLES);
awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_OUTPUTSELECTS,
DEF_BURGUNDY_OUTPUTSELECTS);
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_INPSEL21,
DEF_BURGUNDY_INPSEL21);
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_INPSEL3,
DEF_BURGUNDY_INPSEL3);
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINCD,
DEF_BURGUNDY_GAINCD);
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINLINE,
DEF_BURGUNDY_GAINLINE);
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINMIC,
DEF_BURGUNDY_GAINMIC);
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_GAINMODEM,
DEF_BURGUNDY_GAINMODEM);
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER,
DEF_BURGUNDY_ATTENSPEAKER);
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENLINEOUT,
DEF_BURGUNDY_ATTENLINEOUT);
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENHP,
DEF_BURGUNDY_ATTENHP);
awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_MASTER_VOLUME,
DEF_BURGUNDY_MASTER_VOLUME);
awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLCD,
DEF_BURGUNDY_VOLCD);
awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLLINE,
DEF_BURGUNDY_VOLLINE);
awacs_burgundy_wcw(MASK_ADDR_BURGUNDY_VOLMIC,
DEF_BURGUNDY_VOLMIC);
return 0;
}
static void
awacs_burgundy_write_volume(unsigned address, int volume)
{
int hardvolume,lvolume,rvolume;
lvolume = (volume & 0xff) ? (volume & 0xff) + 155 : 0;
rvolume = ((volume >>8)&0xff) ? ((volume >> 8)&0xff ) + 155 : 0;
hardvolume = lvolume + (rvolume << 16);
awacs_burgundy_wcw(address, hardvolume);
}
static int
awacs_burgundy_read_volume(unsigned address)
{
int softvolume,wvolume;
wvolume = awacs_burgundy_rcw(address);
softvolume = (wvolume & 0xff) - 155;
softvolume += (((wvolume >> 16) & 0xff) - 155)<<8;
return softvolume > 0 ? softvolume : 0;
}
static int
awacs_burgundy_read_mvolume(unsigned address)
{
int lvolume,rvolume,wvolume;
wvolume = awacs_burgundy_rcw(address);
wvolume &= 0xffff;
rvolume = (wvolume & 0xff) - 155;
lvolume = ((wvolume & 0xff00)>>8) - 155;
return lvolume + (rvolume << 8);
}
static void
awacs_burgundy_write_mvolume(unsigned address, int volume)
{
int lvolume,rvolume,hardvolume;
lvolume = (volume &0xff) ? (volume & 0xff) + 155 :0;
rvolume = ((volume >>8) & 0xff) ? (volume >> 8) + 155 :0;
hardvolume = lvolume + (rvolume << 8);
hardvolume += (hardvolume << 16);
awacs_burgundy_wcw(address, hardvolume);
}
/* End burgundy functions */
/* Turn on sound output, needed on G3 desktop powermacs */
static void
awacs_enable_amp(int spkr_vol)
{
struct adb_request req;
awacs_spkr_vol = spkr_vol;
#ifdef COMPILING_2_4_X
if (sys_ctrler != SYS_CTRLER_CUDA){
printk(KERN_INFO "Didn't find a CUDA resource to switch sound on\n") ;
return; }
#else
if( adb_hardware != ADB_VIACUDA) {
printk(KERN_INFO "Didn't find the ADB resources to switch sound on\n");
return;
}
#endif
/* turn on headphones */
cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
0x8a, 4, 0);
while (!req.complete) cuda_poll();
cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
0x8a, 6, 0);
while (!req.complete) cuda_poll();
/* turn on speaker */
cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
0x8a, 3, (100 - (spkr_vol & 0xff)) * 32 / 100);
while (!req.complete) cuda_poll();
cuda_request(&req, NULL, 5, CUDA_PACKET, CUDA_GET_SET_IIC,
0x8a, 5, (100 - ((spkr_vol >> 8) & 0xff)) * 32 / 100);
while (!req.complete) cuda_poll();
cuda_request(&req, NULL, 5, CUDA_PACKET,
CUDA_GET_SET_IIC, 0x8a, 1, 0x29);
while (!req.complete) cuda_poll();
}
/*** Mid level stuff *********************************************************/
/*
* /dev/mixer abstraction
*/
static int awacs_mixer_ioctl(u_int cmd, u_long arg)
{
int data;
// printk(__FILE__ ": awacs_mixer_ioctl()\n");
switch (cmd) {
case SOUND_MIXER_READ_DEVMASK:
data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER
| SOUND_MASK_LINE | SOUND_MASK_MIC
| SOUND_MASK_CD | SOUND_MASK_RECLEV
| SOUND_MASK_ALTPCM
| SOUND_MASK_MONITOR;
return IOCTL_OUT(arg, data);
case SOUND_MIXER_READ_RECMASK:
data = SOUND_MASK_LINE | SOUND_MASK_MIC
| SOUND_MASK_CD;
return IOCTL_OUT(arg, data);
case SOUND_MIXER_READ_RECSRC:
printk(__FILE__ ": mixer: read recsrc\n");
data = 0;
if (awacs_reg[0] & MASK_MUX_AUDIN)
data |= SOUND_MASK_LINE;
if (awacs_reg[0] & MASK_MUX_MIC)
data |= SOUND_MASK_MIC;
if (awacs_reg[0] & MASK_MUX_CD)
data |= SOUND_MASK_CD;
if (awacs_reg[1] & MASK_LOOPTHRU)
data |= SOUND_MASK_MONITOR;
return IOCTL_OUT(arg, data);
case SOUND_MIXER_WRITE_RECSRC:
IOCTL_IN(arg, data);
printk(__FILE__ ": mixer: write recsrc: %d\n", data);
data &= (SOUND_MASK_LINE
| SOUND_MASK_MIC | SOUND_MASK_CD
| SOUND_MASK_MONITOR);
awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC
| MASK_MUX_AUDIN);
awacs_reg[1] &= ~MASK_LOOPTHRU;
if (data & SOUND_MASK_LINE)
awacs_reg[0] |= MASK_MUX_AUDIN;
if (data & SOUND_MASK_MIC)
awacs_reg[0] |= MASK_MUX_MIC;
if (data & SOUND_MASK_CD)
awacs_reg[0] |= MASK_MUX_CD;
if (data & SOUND_MASK_MONITOR)
awacs_reg[1] |= MASK_LOOPTHRU;
awacs_write(awacs_reg[0] | MASK_ADDR0);
awacs_write(awacs_reg[1] | MASK_ADDR1);
return IOCTL_OUT(arg, data);
case SOUND_MIXER_READ_STEREODEVS:
data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER
| SOUND_MASK_RECLEV;
printk(__FILE__ ": mixer: read stereodevs: %d\n", data);
return IOCTL_OUT(arg, data);
case SOUND_MIXER_READ_CAPS:
printk(__FILE__ ": mixer: read caps: 0\n");
return IOCTL_OUT(arg, 0);
case SOUND_MIXER_READ_VOLUME:
data = (awacs_reg[1] & MASK_AMUTE)? 0:
awacs_get_volume(awacs_reg[2], 6);
printk(__FILE__ ": mixer: read volume: %d\n", data);
return IOCTL_OUT(arg, data);
case SOUND_MIXER_WRITE_VOLUME:
IOCTL_IN(arg, data);
printk(__FILE__ ": mixer: write volume: %d\n", data);
return IOCTL_OUT(arg, PMacSetVolume(data));
case SOUND_MIXER_READ_SPEAKER:
if (awacs_revision == 3
#ifdef COMPILING_2_4_X
&& sys_ctrler == SYS_CTRLER_CUDA
#else
&& (adb_hardware == ADB_VIACUDA)
#endif
)
{
data = awacs_spkr_vol;
// printk(__FILE__ ": mixer: speaker a\n");
}
else
{
data = (awacs_reg[1] & MASK_CMUTE)? 0:
awacs_get_volume(awacs_reg[4], 6);
// printk(__FILE__ ": mixer: speaker b\n");
}
printk(__FILE__ ": mixer: read speaker: %d\n", data);
return IOCTL_OUT(arg, data);
case SOUND_MIXER_WRITE_SPEAKER:
IOCTL_IN(arg, data);
if (awacs_revision == 3
#ifdef COMPILING_2_4_X
&& sys_ctrler == SYS_CTRLER_CUDA
#else
&& (adb_hardware == ADB_VIACUDA)
#endif
)
awacs_enable_amp(data);
else
{
printk(__FILE__ ": mixer: write speaker: %d\n", data);
data = awacs_volume_setter(data, 4, MASK_CMUTE, 6);
}
return IOCTL_OUT(arg, data);
case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */
IOCTL_IN(arg, data);
beep_volume = data & 0xff;
/* fall through */
case SOUND_MIXER_READ_ALTPCM:
return IOCTL_OUT(arg, beep_volume);
case SOUND_MIXER_WRITE_LINE:
printk(__FILE__ ": mixer: write line\n");
IOCTL_IN(arg, data);
awacs_reg[0] &= ~MASK_MUX_AUDIN;
if ((data & 0xff) >= 50)
awacs_reg[0] |= MASK_MUX_AUDIN;
awacs_write(MASK_ADDR0 | awacs_reg[0]);
/* fall through */
case SOUND_MIXER_READ_LINE:
printk(__FILE__ ": mixer: read line\n");
data = (awacs_reg[0] & MASK_MUX_AUDIN)? 100: 0;
return IOCTL_OUT(arg, data);
case SOUND_MIXER_WRITE_MIC:
printk(__FILE__ ": mixer: write mic\n");
IOCTL_IN(arg, data);
data &= 0xff;
awacs_reg[0] &= ~(MASK_MUX_MIC | MASK_GAINLINE);
if (data >= 25) {
awacs_reg[0] |= MASK_MUX_MIC;
if (data >= 75)
awacs_reg[0] |= MASK_GAINLINE;
}
awacs_write(MASK_ADDR0 | awacs_reg[0]);
/* fall through */
case SOUND_MIXER_READ_MIC:
data = (awacs_reg[0] & MASK_MUX_MIC)?
(awacs_reg[0] & MASK_GAINLINE? 100: 50): 0;
printk(__FILE__ ": mixer: read mic\n");
return IOCTL_OUT(arg, data);
case SOUND_MIXER_WRITE_CD:
IOCTL_IN(arg, data);
awacs_reg[0] &= ~MASK_MUX_CD;
if ((data & 0xff) >= 50)
awacs_reg[0] |= MASK_MUX_CD;
awacs_write(MASK_ADDR0 | awacs_reg[0]);
/* fall through */
case SOUND_MIXER_READ_CD:
data = (awacs_reg[0] & MASK_MUX_CD)? 100: 0;
return IOCTL_OUT(arg, data);
case SOUND_MIXER_WRITE_RECLEV:
IOCTL_IN(arg, data);
data = awacs_volume_setter(data, 0, 0, 4);
return IOCTL_OUT(arg, data);
case SOUND_MIXER_READ_RECLEV:
data = awacs_get_volume(awacs_reg[0], 4);
return IOCTL_OUT(arg, data);
case MIXER_WRITE(SOUND_MIXER_MONITOR):
/*
* TODO: test if we have a screamer. In this case,
* set real loopthrough level.
*/
IOCTL_IN(arg, data);
awacs_reg[1] &= ~MASK_LOOPTHRU;
if ((data & 0xff) >= 50)
awacs_reg[1] |= MASK_LOOPTHRU;
awacs_write(MASK_ADDR1 | awacs_reg[1]);
/* fall through */
case MIXER_READ(SOUND_MIXER_MONITOR):
data = (awacs_reg[1] & MASK_LOOPTHRU)? 100: 0;
return IOCTL_OUT(arg, data);
}
return -EINVAL;
}
static int burgundy_mixer_ioctl(u_int cmd, u_long arg)
{
int data;
printk(__FILE__ ": burgundy_mixer_ioctl()\n");
/* We are, we are, we are... Burgundy or better */
switch(cmd) {
case SOUND_MIXER_READ_DEVMASK:
data = SOUND_MASK_VOLUME | SOUND_MASK_CD |
SOUND_MASK_LINE | SOUND_MASK_MIC |
SOUND_MASK_SPEAKER | SOUND_MASK_ALTPCM;
return IOCTL_OUT(arg, data);
case SOUND_MIXER_READ_RECMASK:
data = SOUND_MASK_LINE | SOUND_MASK_MIC
| SOUND_MASK_CD;
return IOCTL_OUT(arg, data);
case SOUND_MIXER_READ_RECSRC:
data = 0;
if (awacs_reg[0] & MASK_MUX_AUDIN)
data |= SOUND_MASK_LINE;
if (awacs_reg[0] & MASK_MUX_MIC)
data |= SOUND_MASK_MIC;
if (awacs_reg[0] & MASK_MUX_CD)
data |= SOUND_MASK_CD;
return IOCTL_OUT(arg, data);
case SOUND_MIXER_WRITE_RECSRC:
IOCTL_IN(arg, data);
data &= (SOUND_MASK_LINE
| SOUND_MASK_MIC | SOUND_MASK_CD);
awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC
| MASK_MUX_AUDIN);
if (data & SOUND_MASK_LINE)
awacs_reg[0] |= MASK_MUX_AUDIN;
if (data & SOUND_MASK_MIC)
awacs_reg[0] |= MASK_MUX_MIC;
if (data & SOUND_MASK_CD)
awacs_reg[0] |= MASK_MUX_CD;
awacs_write(awacs_reg[0] | MASK_ADDR0);
return IOCTL_OUT(arg, data);
case SOUND_MIXER_READ_STEREODEVS:
data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER
| SOUND_MASK_RECLEV | SOUND_MASK_CD
| SOUND_MASK_LINE;
return IOCTL_OUT(arg, data);
case SOUND_MIXER_READ_CAPS:
return IOCTL_OUT(arg, 0);
case SOUND_MIXER_WRITE_VOLUME:
IOCTL_IN(arg, data);
awacs_burgundy_write_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME, data);
/* Fall through */
case SOUND_MIXER_READ_VOLUME:
return IOCTL_OUT(arg, awacs_burgundy_read_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME));
case SOUND_MIXER_WRITE_SPEAKER:
IOCTL_IN(arg, data);
if (!(data & 0xff)) {
/* Mute the left speaker */
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) & ~0x2);
} else {
/* Unmute the left speaker */
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) | 0x2);
}
if (!(data & 0xff00)) {
/* Mute the right speaker */
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) & ~0x4);
} else {
/* Unmute the right speaker */
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) | 0x4);
}
data = (((data&0xff)*16)/100 > 0xf ? 0xf :
(((data&0xff)*16)/100)) +
((((data>>8)*16)/100 > 0xf ? 0xf :
((((data>>8)*16)/100)))<<4);
awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER, ~data);
/* Fall through */
case SOUND_MIXER_READ_SPEAKER:
data = awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER);
data = (((data & 0xf)*100)/16) + ((((data>>4)*100)/16)<<8);
return IOCTL_OUT(arg, ~data);
case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */
IOCTL_IN(arg, data);
beep_volume = data & 0xff;
/* fall through */
case SOUND_MIXER_READ_ALTPCM:
return IOCTL_OUT(arg, beep_volume);
case SOUND_MIXER_WRITE_LINE:
IOCTL_IN(arg, data);
awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLLINE, data);
/* fall through */
case SOUND_MIXER_READ_LINE:
data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLLINE);
return IOCTL_OUT(arg, data);
case SOUND_MIXER_WRITE_MIC:
IOCTL_IN(arg, data);
/* Mic is mono device */
data = (data << 8) + (data << 24);
awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLMIC, data);
/* fall through */
case SOUND_MIXER_READ_MIC:
data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLMIC);
data <<= 24;
return IOCTL_OUT(arg, data);
case SOUND_MIXER_WRITE_CD:
IOCTL_IN(arg, data);
awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLCD, data);
/* fall through */
case SOUND_MIXER_READ_CD:
data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLCD);
return IOCTL_OUT(arg, data);
case SOUND_MIXER_WRITE_RECLEV:
IOCTL_IN(arg, data);
data = awacs_volume_setter(data, 0, 0, 4);
return IOCTL_OUT(arg, data);
case SOUND_MIXER_READ_RECLEV:
data = awacs_get_volume(awacs_reg[0], 4);
return IOCTL_OUT(arg, data);
case SOUND_MIXER_OUTMASK:
break;
case SOUND_MIXER_OUTSRC:
break;
}
return -EINVAL;
}
static int PMacMixerIoctl(u_int cmd, u_long arg)
{
/* Different IOCTLS for burgundy*/
/* printk(__FILE__ ": avacs_revision = %d\n", awacs_revision); */
if (awacs_revision >= AWACS_BURGUNDY)
return burgundy_mixer_ioctl(cmd, arg);
return awacs_mixer_ioctl(cmd, arg);
}
static void PMacWriteSqSetup(void)
{
int i;
volatile struct dbdma_cmd *cp;
cp = awacs_tx_cmds;
memset((void *)cp, 0, (write_sq.numBufs+1) * sizeof(struct dbdma_cmd));
for (i = 0; i < write_sq.numBufs; ++i, ++cp) {
st_le32(&cp->phy_addr, virt_to_bus(write_sq.buffers[i]));
}
st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS);
st_le32(&cp->cmd_dep, virt_to_bus(awacs_tx_cmds));
out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
out_le32(&awacs_txdma->cmdptr, virt_to_bus(awacs_tx_cmds));
}
static void PMacReadSqSetup(void)
{
int i;
volatile struct dbdma_cmd *cp;
cp = awacs_rx_cmds;
memset((void *)cp, 0, (read_sq.numBufs+1) * sizeof(struct dbdma_cmd));
/* Set dma buffers up in a loop */
for (i = 0; i < read_sq.numBufs; i++,cp++) {
st_le32(&cp->phy_addr, virt_to_bus(read_sq.buffers[i]));
st_le16(&cp->command, INPUT_MORE + INTR_ALWAYS);
st_le16(&cp->req_count, read_sq.block_size);
st_le16(&cp->xfer_status, 0);
}
/* The next two lines make the thing loop around.
*/
st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS);
st_le32(&cp->cmd_dep, virt_to_bus(awacs_rx_cmds));
/* Don't start until the first read is done.
* This will also abort any operations in progress if the DMA
* happens to be running (and it shouldn't).
*/
out_le32(&awacs_rxdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
out_le32(&awacs_rxdma->cmdptr, virt_to_bus(awacs_rx_cmds));
}
static void PMacAbortRead(void)
{
int i;
volatile struct dbdma_cmd *cp;
cp = awacs_rx_cmds;
for (i = 0; i < read_sq.numBufs; i++,cp++)
st_le16(&cp->command, DBDMA_STOP);
/*
* We should probably wait for the thing to stop before we
* release the memory
*/
}
/*** Machine definitions *****************************************************/
static MACHINE machPMac = {
name: awacs_name,
name2: "AWACS",
open: PMacOpen,
release: PMacRelease,
dma_alloc: PMacAlloc,
dma_free: PMacFree,
irqinit: PMacIrqInit,
#ifdef MODULE
irqcleanup: PMacIrqCleanup,
#endif /* MODULE */
init: PMacInit,
silence: PMacSilence,
setFormat: PMacSetFormat,
setVolume: PMacSetVolume,
play: PMacPlay,
record: PMacRecord,
mixer_ioctl: PMacMixerIoctl,
write_sq_setup: PMacWriteSqSetup,
read_sq_setup: PMacReadSqSetup,
abort_read: PMacAbortRead,
min_dsp_speed: 8000
};
/*** Config & Setup **********************************************************/
int __init dmasound_awacs_init(void)
{
struct device_node *np;
printk(__FILE__ ": dmasound_awacs_init()\n");
if (_machine != _MACH_Pmac)
return -ENODEV;
awacs_subframe = 0;
awacs_revision = 0;
np = find_devices("awacs");
if (np == 0) {
/*
* powermac G3 models have a node called "davbus"
* with a child called "sound".
*/
struct device_node *sound;
printk(__FILE__ ": device node awacs not found!\n");
np = find_devices("davbus");
sound = find_devices("sound");
if (sound != 0 && sound->parent == np) {
unsigned int *prop, l, i;
printk(__FILE__ ": device nodes davbus and sound found!\n");
prop = (unsigned int *)
get_property(sound, "sub-frame", 0);
if (prop != 0 && *prop >= 0 && *prop < 16)
{
printk(__FILE__ ": subframe: %d\n", *prop);
awacs_subframe = *prop;
}
if (device_is_compatible(sound, "burgundy"))
{
printk(__FILE__ ": burgundy recognized!\n");
awacs_revision = AWACS_BURGUNDY;
}
/* look for a property saying what sample rates
are available */
for (i = 0; i < 8; ++i)
awacs_freqs_ok[i] = 0;
prop = (unsigned int *) get_property
(sound, "sample-rates", &l);
if (prop == 0)
prop = (unsigned int *) get_property
(sound, "output-frame-rates", &l);
if (prop != 0) {
for (l /= sizeof(int); l > 0; --l) {
/* sometimes the rate is in the
high-order 16 bits (?) */
unsigned int r = *prop++;
if (r >= 0x10000)
r >>= 16;
for (i = 0; i < 8; ++i) {
if (r == awacs_freqs[i]) {
awacs_freqs_ok[i] = 1;
break;
}
}
}
} else {
/* assume just 44.1k is OK */
awacs_freqs_ok[0] = 1;
machPMac.min_dsp_speed = 44100 ;
}
}
}
if (np != NULL && np->n_addrs >= 3 && np->n_intrs >= 3) {
int vol;
dmasound.mach = machPMac;
printk("doing IO remap of awacs regs.\n");
awacs = (volatile struct awacs_regs *)
ioremap(np->addrs[0].address, 0x80);
awacs_txdma = (volatile struct dbdma_regs *)
ioremap(np->addrs[1].address, 0x100);
awacs_rxdma = (volatile struct dbdma_regs *)
ioremap(np->addrs[2].address, 0x100);
awacs_irq = np->intrs[0].line;
awacs_tx_irq = np->intrs[1].line;
awacs_rx_irq = np->intrs[2].line;
awacs_tx_cmd_space = kmalloc((write_sq.numBufs + 4) * sizeof(struct dbdma_cmd),
GFP_KERNEL);
if (awacs_tx_cmd_space == NULL) {
printk(KERN_ERR "DMA sound driver: Not enough buffer memory, driver disabled!\n");
return -ENOMEM;
}
awacs_node = np;
#ifdef CONFIG_PMAC_PBOOK
if (machine_is_compatible("PowerBook1,1")
|| machine_is_compatible("AAPL,PowerBook1998")) {
feature_set(np, FEATURE_Sound_CLK_enable);
feature_set(np, FEATURE_Sound_power);
/* Shorter delay will not work */
mdelay(1000);
}
#endif
awacs_tx_cmds = (volatile struct dbdma_cmd *)
DBDMA_ALIGN(awacs_tx_cmd_space);
awacs_rx_cmd_space = kmalloc((read_sq.numBufs + 4) * sizeof(struct dbdma_cmd),
GFP_KERNEL);
if (awacs_rx_cmd_space == NULL) {
printk(KERN_ERR "DMA sound driver: No memory for input, driver disabled\n");
return -ENOMEM;
}
awacs_rx_cmds = (volatile struct dbdma_cmd *)
DBDMA_ALIGN(awacs_rx_cmd_space);
awacs_reg[0] = MASK_MUX_CD;
awacs_reg[1] = MASK_LOOPTHRU; /* | MASK_PAROUT;*/
/* get default volume from nvram */
vol = (~nvram_read_byte(0x1308) & 7) << 1;
awacs_reg[2] = vol + (vol << 6);
awacs_reg[4] = vol + (vol << 6);
awacs_reg[5] = 0;
awacs_reg[6] = 0;
awacs_reg[7] = 0;
out_le32(&awacs->control, 0x11);
awacs_write(awacs_reg[0] + MASK_ADDR0);
awacs_write(awacs_reg[1] + MASK_ADDR1);
awacs_write(awacs_reg[2] + MASK_ADDR2);
awacs_write(awacs_reg[4] + MASK_ADDR4);
if (is_screamer)
{
awacs_write(awacs_reg[5] + MASK_ADDR5);
awacs_write(awacs_reg[6] + MASK_ADDR6);
awacs_write(awacs_reg[7] + MASK_ADDR7);
}
/* Initialize recent versions of the awacs */
if (awacs_revision == 0) {
awacs_revision =
(in_le32(&awacs->codec_stat) >> 12) & 0xf;
if (awacs_revision == 3) {
mdelay(100);
awacs_write(0x6000); /* !!!! */
mdelay(2);
awacs_write(awacs_reg[1] + MASK_ADDR1);
awacs_enable_amp(100 * 0x101);
}
}
if (awacs_revision >= AWACS_BURGUNDY)
awacs_burgundy_init();
/* Initialize beep stuff */
beep_dbdma_cmd = awacs_tx_cmds + (write_sq.numBufs + 1);
orig_mksound = kd_mksound;
kd_mksound = awacs_mksound;
beep_buf = (short *) kmalloc(BEEP_BUFLEN * 4, GFP_KERNEL);
if (beep_buf == NULL)
printk(KERN_WARNING "dmasound: no memory for "
"beep buffer\n");
#ifdef CONFIG_PMAC_PBOOK
pmu_register_sleep_notifier(&awacs_sleep_notifier);
#endif /* CONFIG_PMAC_PBOOK */
/* Powerbooks have odd ways of enabling inputs such as
an expansion-bay CD or sound from an internal modem
or a PC-card modem. */
printk("trying to detect PB...");
if (machine_is_compatible("AAPL,3400/2400")
|| machine_is_compatible("AAPL,3500")) {
printk("3400\n");
is_pbook_3400 = 1;
/*
* Enable CD and PC-card sound inputs.
* This is done by reading from address
* f301a000, + 0x10 to enable the expansion-bay
* CD sound input, + 0x80 to enable the PC-card
* sound input. The 0x100 enables the SCSI bus
* terminator power.
*/
latch_base = (unsigned char *) ioremap
(0xf301a000, 0x1000);
in_8(latch_base + 0x190);
} else if (machine_is_compatible("PowerBook1,1")
|| machine_is_compatible("PowerBook3,1")
|| machine_is_compatible("AAPL,PowerBook1998")) {
struct device_node* mio;
printk("G3\n");
macio_base = 0;
is_pbook_G3 = 1;
for (mio = np->parent; mio; mio = mio->parent) {
if (strcmp(mio->name, "mac-io") == 0
&& mio->n_addrs > 0) {
macio_base = (unsigned char *) ioremap
(mio->addrs[0].address, 0x40);
break;
}
}
/* enable CD sound input */
if (macio_base)
{
out_8(macio_base + 0x37, 3);
printk("CD sound enabled\n");
}
}
else
{
printk("none\n");
}
sprintf(awacs_name, "PowerMac (AWACS rev %d) ",
awacs_revision);
return dmasound_init();
}
return -ENODEV;
}
#ifdef COMPILING_2_4_X
static void __exit dmasound_awacs_cleanup(void)
{
dmasound_deinit();
}
module_init(dmasound_awacs_init);
module_exit(dmasound_awacs_cleanup);
#else
#ifdef MODULE
int init_module(void)
{
return dmasound_awacs_init() ;
}
int cleanup_module(void)
{
dmasound_deinit() ;
return 0 ;
}
#endif
#endif
/*********************************************************/
/* This file was written by someone, somewhere, sometime */
/* And is released into the Public Domain */
/*********************************************************/
#ifndef _AWACS_DEFS_H_
#define _AWACS_DEFS_H_
/*******************************/
/* AWACs Audio Register Layout */
/*******************************/
struct awacs_regs {
unsigned control; /* Audio control register */
unsigned pad0[3];
unsigned codec_ctrl; /* Codec control register */
unsigned pad1[3];
unsigned codec_stat; /* Codec status register */
unsigned pad2[3];
unsigned clip_count; /* Clipping count register */
unsigned pad3[3];
unsigned byteswap; /* Data is little-endian if 1 */
};
/*******************/
/* Audio Bit Masks */
/*******************/
/* Audio Control Reg Bit Masks */
/* ----- ------- --- --- ----- */
#define MASK_ISFSEL (0xf) /* Input SubFrame Select */
#define MASK_OSFSEL (0xf << 4) /* Output SubFrame Select */
#define MASK_RATE (0x7 << 8) /* Sound Rate */
#define MASK_CNTLERR (0x1 << 11) /* Error */
#define MASK_PORTCHG (0x1 << 12) /* Port Change */
#define MASK_IEE (0x1 << 13) /* Enable Interrupt on Error */
#define MASK_IEPC (0x1 << 14) /* Enable Interrupt on Port Change */
#define MASK_SSFSEL (0x3 << 15) /* Status SubFrame Select */
/* Audio Codec Control Reg Bit Masks */
/* ----- ----- ------- --- --- ----- */
#define MASK_NEWECMD (0x1 << 24) /* Lock: don't write to reg when 1 */
#define MASK_EMODESEL (0x3 << 22) /* Send info out on which frame? */
#define MASK_EXMODEADDR (0x3ff << 12) /* Extended Mode Address -- 10 bits */
#define MASK_EXMODEDATA (0xfff) /* Extended Mode Data -- 12 bits */
/* Audio Codec Control Address Values / Masks */
/* ----- ----- ------- ------- ------ - ----- */
#define MASK_ADDR0 (0x0 << 12) /* Expanded Data Mode Address 0 */
#define MASK_ADDR_MUX MASK_ADDR0 /* Mux Control */
#define MASK_ADDR_GAIN MASK_ADDR0
#define MASK_ADDR1 (0x1 << 12) /* Expanded Data Mode Address 1 */
#define MASK_ADDR_MUTE MASK_ADDR1
#define MASK_ADDR_RATE MASK_ADDR1
#define MASK_ADDR2 (0x2 << 12) /* Expanded Data Mode Address 2 */
#define MASK_ADDR_VOLA MASK_ADDR2 /* Volume Control A -- Headphones */
#define MASK_ADDR_VOLHD MASK_ADDR2
#define MASK_ADDR4 (0x4 << 12) /* Expanded Data Mode Address 4 */
#define MASK_ADDR_VOLC MASK_ADDR4 /* Volume Control C -- Speaker */
#define MASK_ADDR_VOLSPK MASK_ADDR4
/* additional registers of screamer */
#define MASK_ADDR5 (0x5 << 12) /* Expanded Data Mode Address 5 */
#define MASK_ADDR6 (0x6 << 12) /* Expanded Data Mode Address 6 */
#define MASK_ADDR7 (0x7 << 12) /* Expanded Data Mode Address 7 */
/* Address 0 Bit Masks & Macros */
/* ------- - --- ----- - ------ */
#define MASK_GAINRIGHT (0xf) /* Gain Right Mask */
#define MASK_GAINLEFT (0xf << 4) /* Gain Left Mask */
#define MASK_GAINLINE (0x1 << 8) /* Change Gain for Line??? */
#define MASK_GAINMIC (0x0 << 8) /* Change Gain for Mic??? */
#define MASK_MUX_CD (0x1 << 9) /* Select CD in MUX */
#define MASK_MUX_AUDIN (0x1 << 10) /* Select Audio In in MUX */
#define MASK_MUX_MIC (0x1 << 11) /* Select Mic in MUX */
#define MASK_MUX_LINE MASK_MUX_AUDIN
#define GAINRIGHT(x) ((x) & MASK_GAINRIGHT)
#define GAINLEFT(x) (((x) << 4) & MASK_GAINLEFT)
/* Address 1 Bit Masks */
/* ------- - --- ----- */
#define MASK_ADDR1RES1 (0x3) /* Reserved */
#define MASK_RECALIBRATE (0x1 << 2) /* Recalibrate */
#define MASK_SAMPLERATE (0x7 << 3) /* Sample Rate: */
#define MASK_LOOPTHRU (0x1 << 6) /* Loopthrough Enable */
#define MASK_CMUTE (0x1 << 7) /* Output C (Speaker) Mute when 1 */
#define MASK_SPKMUTE MASK_CMUTE
#define MASK_ADDR1RES2 (0x1 << 8) /* Reserved */
#define MASK_AMUTE (0x1 << 9) /* Output A (Headphone) Mute when 1 */
#define MASK_HDMUTE MASK_AMUTE
#define MASK_PAROUT (0x3 << 10) /* Parallel Out (???) */
#define SAMPLERATE_48000 (0x0 << 3) /* 48 or 44.1 kHz */
#define SAMPLERATE_32000 (0x1 << 3) /* 32 or 29.4 kHz */
#define SAMPLERATE_24000 (0x2 << 3) /* 24 or 22.05 kHz */
#define SAMPLERATE_19200 (0x3 << 3) /* 19.2 or 17.64 kHz */
#define SAMPLERATE_16000 (0x4 << 3) /* 16 or 14.7 kHz */
#define SAMPLERATE_12000 (0x5 << 3) /* 12 or 11.025 kHz */
#define SAMPLERATE_9600 (0x6 << 3) /* 9.6 or 8.82 kHz */
#define SAMPLERATE_8000 (0x7 << 3) /* 8 or 7.35 kHz */
/* Address 2 & 4 Bit Masks & Macros */
/* ------- - - - --- ----- - ------ */
#define MASK_OUTVOLRIGHT (0xf) /* Output Right Volume */
#define MASK_ADDR2RES1 (0x2 << 4) /* Reserved */
#define MASK_ADDR4RES1 MASK_ADDR2RES1
#define MASK_OUTVOLLEFT (0xf << 6) /* Output Left Volume */
#define MASK_ADDR2RES2 (0x2 << 10) /* Reserved */
#define MASK_ADDR4RES2 MASK_ADDR2RES2
#define VOLRIGHT(x) (((~(x)) & MASK_OUTVOLRIGHT))
#define VOLLEFT(x) (((~(x)) << 6) & MASK_OUTVOLLEFT)
/* Audio Codec Status Reg Bit Masks */
/* ----- ----- ------ --- --- ----- */
#define MASK_EXTEND (0x1 << 23) /* Extend */
#define MASK_VALID (0x1 << 22) /* Valid Data? */
#define MASK_OFLEFT (0x1 << 21) /* Overflow Left */
#define MASK_OFRIGHT (0x1 << 20) /* Overflow Right */
#define MASK_ERRCODE (0xf << 16) /* Error Code */
#define MASK_REVISION (0xf << 12) /* Revision Number */
#define MASK_MFGID (0xf << 8) /* Mfg. ID */
#define MASK_CODSTATRES (0xf << 4) /* bits 4 - 7 reserved */
#define MASK_INPPORT (0xf) /* Input Port */
#define MASK_HDPCONN 8 /* headphone plugged in */
/* Clipping Count Reg Bit Masks */
/* -------- ----- --- --- ----- */
#define MASK_CLIPLEFT (0xff << 7) /* Clipping Count, Left Channel */
#define MASK_CLIPRIGHT (0xff) /* Clipping Count, Right Channel */
/* DBDMA ChannelStatus Bit Masks */
/* ----- ------------- --- ----- */
#define MASK_CSERR (0x1 << 7) /* Error */
#define MASK_EOI (0x1 << 6) /* End of Input -- only for Input Channel */
#define MASK_CSUNUSED (0x1f << 1) /* bits 1-5 not used */
#define MASK_WAIT (0x1) /* Wait */
/* Various Rates */
/* ------- ----- */
#define RATE_48000 (0x0 << 8) /* 48 kHz */
#define RATE_44100 (0x0 << 8) /* 44.1 kHz */
#define RATE_32000 (0x1 << 8) /* 32 kHz */
#define RATE_29400 (0x1 << 8) /* 29.4 kHz */
#define RATE_24000 (0x2 << 8) /* 24 kHz */
#define RATE_22050 (0x2 << 8) /* 22.05 kHz */
#define RATE_19200 (0x3 << 8) /* 19.2 kHz */
#define RATE_17640 (0x3 << 8) /* 17.64 kHz */
#define RATE_16000 (0x4 << 8) /* 16 kHz */
#define RATE_14700 (0x4 << 8) /* 14.7 kHz */
#define RATE_12000 (0x5 << 8) /* 12 kHz */
#define RATE_11025 (0x5 << 8) /* 11.025 kHz */
#define RATE_9600 (0x6 << 8) /* 9.6 kHz */
#define RATE_8820 (0x6 << 8) /* 8.82 kHz */
#define RATE_8000 (0x7 << 8) /* 8 kHz */
#define RATE_7350 (0x7 << 8) /* 7.35 kHz */
#define RATE_LOW 1 /* HIGH = 48kHz, etc; LOW = 44.1kHz, etc. */
/* Burgundy values */
#define MASK_ADDR_BURGUNDY_INPSEL21 (0x11 << 12)
#define MASK_ADDR_BURGUNDY_INPSEL3 (0x12 << 12)
#define MASK_ADDR_BURGUNDY_GAINCH1 (0x13 << 12)
#define MASK_ADDR_BURGUNDY_GAINCH2 (0x14 << 12)
#define MASK_ADDR_BURGUNDY_GAINCH3 (0x15 << 12)
#define MASK_ADDR_BURGUNDY_GAINCH4 (0x16 << 12)
#define MASK_ADDR_BURGUNDY_VOLCH1 (0x20 << 12)
#define MASK_ADDR_BURGUNDY_VOLCH2 (0x21 << 12)
#define MASK_ADDR_BURGUNDY_VOLCH3 (0x22 << 12)
#define MASK_ADDR_BURGUNDY_VOLCH4 (0x23 << 12)
#define MASK_ADDR_BURGUNDY_OUTPUTSELECTS (0x2B << 12)
#define MASK_ADDR_BURGUNDY_OUTPUTENABLES (0x2F << 12)
#define MASK_ADDR_BURGUNDY_MASTER_VOLUME (0x30 << 12)
#define MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES (0x60 << 12)
#define MASK_ADDR_BURGUNDY_ATTENSPEAKER (0x62 << 12)
#define MASK_ADDR_BURGUNDY_ATTENLINEOUT (0x63 << 12)
#define MASK_ADDR_BURGUNDY_ATTENHP (0x64 << 12)
#define MASK_ADDR_BURGUNDY_VOLCD (MASK_ADDR_BURGUNDY_VOLCH1)
#define MASK_ADDR_BURGUNDY_VOLLINE (MASK_ADDR_BURGUNDY_VOLCH2)
#define MASK_ADDR_BURGUNDY_VOLMIC (MASK_ADDR_BURGUNDY_VOLCH3)
#define MASK_ADDR_BURGUNDY_VOLMODEM (MASK_ADDR_BURGUNDY_VOLCH4)
#define MASK_ADDR_BURGUNDY_GAINCD (MASK_ADDR_BURGUNDY_GAINCH1)
#define MASK_ADDR_BURGUNDY_GAINLINE (MASK_ADDR_BURGUNDY_GAINCH2)
#define MASK_ADDR_BURGUNDY_GAINMIC (MASK_ADDR_BURGUNDY_GAINCH3)
#define MASK_ADDR_BURGUNDY_GAINMODEM (MASK_ADDR_BURGUNDY_VOLCH4)
/* These are all default values for the burgundy */
#define DEF_BURGUNDY_INPSEL21 (0xAA)
#define DEF_BURGUNDY_INPSEL3 (0x0A)
#define DEF_BURGUNDY_GAINCD (0x33)
#define DEF_BURGUNDY_GAINLINE (0x44)
#define DEF_BURGUNDY_GAINMIC (0x44)
#define DEF_BURGUNDY_GAINMODEM (0x06)
/* Remember: lowest volume here is 0x9b */
#define DEF_BURGUNDY_VOLCD (0xCCCCCCCC)
#define DEF_BURGUNDY_VOLLINE (0x00000000)
#define DEF_BURGUNDY_VOLMIC (0x00000000)
#define DEF_BURGUNDY_VOLMODEM (0xCCCCCCCC)
#define DEF_BURGUNDY_OUTPUTSELECTS (0x010f010f)
#define DEF_BURGUNDY_OUTPUTENABLES (0x0A)
#define DEF_BURGUNDY_MASTER_VOLUME (0xFFFFFFFF)
#define DEF_BURGUNDY_MORE_OUTPUTENABLES (0x7E)
#define DEF_BURGUNDY_ATTENSPEAKER (0x44)
#define DEF_BURGUNDY_ATTENLINEOUT (0xCC)
#define DEF_BURGUNDY_ATTENHP (0xCC)
#endif /* _AWACS_DEFS_H_ */
Reply to: