📄 dmasound_awacs.c
字号:
/* * 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>#include <linux/adb.h>#include <linux/nvram.h>#include <linux/vt_kern.h>#ifdef CONFIG_ADB_CUDA#include <linux/cuda.h>#endif#ifdef CONFIG_ADB_PMU#include <linux/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"/* * 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;int awacs_is_screamer = 0;int awacs_device_id = 0;int awacs_has_iic = 0;#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[8];#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 MODULEstatic void PMacIrqCleanup(void);#endifstatic 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -