patch-2.3.99-pre4 linux/drivers/sound/dmasound/dmasound_awacs.c
Next file: linux/drivers/sound/dmasound/dmasound_core.c
Previous file: linux/drivers/sound/dmasound/dmasound_atari.c
Back to the patch index
Back to the overall index
- Lines: 2114
- Date:
Mon Apr 10 22:58:59 2000
- Orig file:
v2.3.99-pre3/linux/drivers/sound/dmasound/dmasound_awacs.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.3.99-pre3/linux/drivers/sound/dmasound/dmasound_awacs.c linux/drivers/sound/dmasound/dmasound_awacs.c
@@ -0,0 +1,2113 @@
+
+/*
+ * linux/drivers/sound/dmasound_awacs.c
+ *
+ * PowerMac DMA Sound Driver
+ *
+ * See linux/drivers/sound/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>
+#include <linux/adb.h>
+#include <linux/nvram.h>
+#include <linux/vt_kern.h>
+#include <linux/cuda.h>
+
+#include <asm/uaccess.h>
+#include <asm/prom.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+#include <asm/dbdma.h>
+
+#include "awacs_defs.h"
+#include "dmasound.h"
+
+
+/*
+ * 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
+ */
+int awacs_reg[5];
+
+#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;
+ 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 = {
+ NULL, NULL, 0, 0, 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);
+ 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;
+ if (sys_ctrler != SYS_CTRLER_CUDA)
+ return;
+
+ /* 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 PMacMixerIoctl(u_int cmd, u_long arg)
+{
+ int data;
+ /* Different IOCTLS for burgundy*/
+ if (awacs_revision < AWACS_BURGUNDY) {
+ 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:
+ 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);
+ 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;
+ return IOCTL_OUT(arg, data);
+ case SOUND_MIXER_READ_CAPS:
+ return IOCTL_OUT(arg, 0);
+ case SOUND_MIXER_READ_VOLUME:
+ data = (awacs_reg[1] & MASK_AMUTE)? 0:
+ awacs_get_volume(awacs_reg[2], 6);
+ return IOCTL_OUT(arg, data);
+ case SOUND_MIXER_WRITE_VOLUME:
+ IOCTL_IN(arg, data);
+ return IOCTL_OUT(arg, dmasound_set_volume(data));
+ case SOUND_MIXER_READ_SPEAKER:
+ if (awacs_revision == 3
+ && sys_ctrler == SYS_CTRLER_CUDA)
+ data = awacs_spkr_vol;
+ else
+ data = (awacs_reg[1] & MASK_CMUTE)? 0:
+ awacs_get_volume(awacs_reg[4], 6);
+ return IOCTL_OUT(arg, data);
+ case SOUND_MIXER_WRITE_SPEAKER:
+ IOCTL_IN(arg, data);
+ if (awacs_revision == 3
+ && sys_ctrler == SYS_CTRLER_CUDA)
+ awacs_enable_amp(data);
+ else
+ 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:
+ 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:
+ data = (awacs_reg[0] & MASK_MUX_AUDIN)? 100: 0;
+ return IOCTL_OUT(arg, data);
+ case SOUND_MIXER_WRITE_MIC:
+ 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;
+ 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);
+ }
+ } else {
+ /* 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 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;
+
+ 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;
+ np = find_devices("davbus");
+ sound = find_devices("sound");
+ if (sound != 0 && sound->parent == np) {
+ unsigned int *prop, l, i;
+ prop = (unsigned int *)
+ get_property(sound, "sub-frame", 0);
+ if (prop != 0 && *prop >= 0 && *prop < 16)
+ awacs_subframe = *prop;
+ if (device_is_compatible(sound, "burgundy"))
+ 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;
+ }
+ }
+ }
+ if (np != NULL && np->n_addrs >= 3 && np->n_intrs >= 3) {
+ int vol;
+ dmasound.mach = machPMac;
+
+ 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("DMA sound driver: No memory for input");
+ }
+ 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);
+ 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);
+
+ /* 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. */
+ if (machine_is_compatible("AAPL,3400/2400")
+ || machine_is_compatible("AAPL,3500")) {
+ 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("AAPL,PowerBook1998")) {
+ struct device_node* mio;
+ 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);
+ }
+ sprintf(awacs_name, "PowerMac (AWACS rev %d) ",
+ awacs_revision);
+ return dmasound_init();
+ }
+ return -ENODEV;
+}
+
+static void __exit dmasound_awacs_cleanup(void)
+{
+ dmasound_deinit();
+}
+
+module_init(dmasound_awacs_init);
+module_exit(dmasound_awacs_cleanup);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)