📄 dmasound_awacs.c
字号:
/* * linux/drivers/sound/dmasound/dmasound_awacs.c * * PowerMac `AWACS' and `Burgundy' DMA Sound Driver * with some limited support for DACA & Tumbler * * See linux/drivers/sound/dmasound/dmasound_core.c for copyright and * history prior to 2001/01/26. * * 26/01/2001 ed 0.1 Iain Sandoe * - added version info. * - moved dbdma command buffer allocation to PMacXXXSqSetup() * - fixed up beep dbdma cmd buffers * * 08/02/2001 [0.2] * - make SNDCTL_DSP_GETFMTS return the correct info for the h/w * - move soft format translations to a separate file * - [0.3] make SNDCTL_DSP_GETCAPS return correct info. * - [0.4] more informative machine name strings. * - [0.5] * - record changes. * - made the default_hard/soft entries. * 04/04/2001 [0.6] * - minor correction to bit assignments in awacs_defs.h * - incorporate mixer changes from 2.2.x back-port. * - take out passthru as a rec input (it isn't). * - make Input Gain slider work the 'right way up'. * - try to make the mixer sliders more logical - so now the * input selectors are just two-state (>50% == ON) and the * Input Gain slider handles the rest of the gain issues. * - try to pick slider representations that most closely match * the actual use - e.g. IGain for input gain... * - first stab at over/under-run detection. * - minor cosmetic changes to IRQ identification. * - fix bug where rates > max would be reported as supported. * - first stab at over/under-run detection. * - make use of i2c for mixer settings conditional on perch * rather than cuda (some machines without perch have cuda). * - fix bug where TX stops when dbdma status comes up "DEAD" * so far only reported on PowerComputing clones ... but. * - put in AWACS/Screamer register write timeouts. * - part way to partitioning the init() stuff * - first pass at 'tumbler' stuff (not support - just an attempt * to allow the driver to load on new G4s). * 01/02/2002 [0.7] - BenH * - all sort of minor bits went in since the latest update, I * bumped the version number for that reason*//* GENERAL FIXME/TODO: check that the assumptions about what is written to mac-io is valid for DACA & Tumbler. This driver is in bad need of a rewrite. The dbdma code has to be split, some proper device-tree parsing code has to be written, etc...*/#include <linux/types.h>#include <linux/module.h>#include <linux/config.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/soundcard.h>#include <linux/adb.h>#include <linux/nvram.h>#include <linux/tty.h>#include <linux/vt_kern.h>#include <linux/irq.h>#include <linux/kmod.h>#include <asm/semaphore.h>#ifdef CONFIG_ADB_CUDA#include <linux/cuda.h>#endif#ifdef CONFIG_ADB_PMU#include <linux/pmu.h>#endif#include <linux/i2c-dev.h>#include <asm/uaccess.h>#include <asm/prom.h>#include <asm/machdep.h>#include <asm/io.h>#include <asm/dbdma.h>#include <asm/pmac_feature.h>#include <asm/irq.h>#include <asm/nvram.h>#include "awacs_defs.h"#include "dmasound.h"#define DMASOUND_AWACS_REVISION 0#define DMASOUND_AWACS_EDITION 7#define AWACS_BURGUNDY 100 /* fake revision # for burgundy */#define AWACS_TUMBLER 90 /* fake revision # for tumbler */#define AWACS_DACA 80 /* fake revision # for daca (ibook) */#define AWACS_AWACS 2 /* holding revision for AWACS */#define AWACS_SCREAMER 3 /* holding revision for Screamer *//* * Interrupt numbers and addresses, & info 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;static int awacs_sleeping;static DECLARE_MUTEX(dmasound_sem);static int sound_device_id; /* exists after iMac revA */static int hw_can_byteswap = 1 ; /* most pmac sound h/w can *//* model info *//* To be replaced with better interaction with pmac_feature.c */static int is_pbook_3X00;static int is_pbook_g3;/* expansion info */static int has_perch;static int has_ziva;/* for earlier powerbooks which need fiddling with mac-io to enable * cd etc.*/static unsigned char *latch_base;static unsigned char *macio_base;/* * Space for the DBDMA command blocks. */static void *awacs_tx_cmd_space;static volatile struct dbdma_cmd *awacs_tx_cmds;static int number_of_tx_cmd_buffers = 0;static void *awacs_rx_cmd_space;static volatile struct dbdma_cmd *awacs_rx_cmds;static int number_of_rx_cmd_buffers = 0;/* * Cached values of AWACS registers (we can't read them). * Except on the burgundy (and screamer). XXX */int awacs_reg[8];int awacs_reg1_save;/* tracking values for the mixer contents*/static int spk_vol = 0 ;static int line_vol = 0 ;static int passthru_vol = 0 ;static int ip_gain = 0 ; /* mic preamp settings */static int rec_lev = 0x4545 ; /* default CD gain 69 % */static int mic_lev = 0 ;static int cd_lev = 0x6363 ; /* 99 % */static int line_lev = 0 ;/* * 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,};/* beep support */#define BEEP_SRATE 22050 /* 22050 Hz sample rate */#define BEEP_BUFLEN 512#define BEEP_VOLUME 15 /* 0 - 100 */static int beep_vol = BEEP_VOLUME;static int beep_playing = 0;static int awacs_beep_state = 0;static short *beep_buf;static void *beep_dbdma_cmd_space;static volatile struct dbdma_cmd *beep_dbdma_cmd;static void (*orig_mksound)(unsigned int, unsigned int);/* 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);/* we will allocate a single 'emergency' dbdma cmd block to use if the tx status comes up "DEAD". This happens on some PowerComputing Pmac clones, either owing to a bug in dbdma or some interaction between IDE and sound. However, this measure would deal with DEAD status if if appeared elsewhere. for the sake of memory efficiency we'll allocate this cmd as part of the beep cmd stuff.*/static volatile struct dbdma_cmd *emergency_dbdma_cmd;#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 *//* for (soft) sample rate translations */int expand_bal; /* Balance factor for expanding (not volume!) *//*** 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 int PMacWriteSqSetup(void);static int PMacReadSqSetup(void);static void PMacAbortRead(void);extern TRANS transAwacsNormal ;extern TRANS transAwacsExpand ;extern TRANS transAwacsNormalRead ;extern int daca_init(void);extern int daca_cleanup(void);extern int daca_set_volume(uint left_vol, uint right_vol);extern void daca_get_volume(uint * left_vol, uint *right_vol);extern int daca_enter_sleep(void);extern int daca_leave_sleep(void);extern int tas_init(void);extern int tas_cleanup(void);extern int tumbler_set_volume(uint left_vol, uint right_vol);extern void tumbler_get_volume(uint * left_vol, uint *right_vol);extern void tumbler_set_treble(int treble);extern void tumbler_get_treble(int *treble);extern void tumbler_set_bass(int bass);extern void tumbler_get_bass(int *bass);extern void tumbler_set_pcm_lvl(int pcm_lvl);extern void tumbler_get_pcm_lvl(int *pcm_lvl);extern int tumbler_enter_sleep(void);extern int tumbler_leave_sleep(void);#define TRY_LOCK() \ if ((rc = down_interruptible(&dmasound_sem)) != 0) \ return rc;#define LOCK() down(&dmasound_sem);#define UNLOCK() up(&dmasound_sem);/* We use different versions that the ones provided in dmasound.h * * FIXME: Use different names ;) */#undef IOCTL_IN#undef IOCTL_OUT#define IOCTL_IN(arg, ret) \ rc = get_user(ret, (int *)(arg)); \ if (rc) break;#define IOCTL_OUT(arg, ret) \ ioctl_return2((int *)(arg), ret)static inline int ioctl_return2(int *addr, int value){ return value < 0 ? value : put_user(value, addr);}/*** AE - TUMBLER START *********************************************************/int gpio_audio_reset, gpio_audio_reset_pol;int gpio_amp_mute, gpio_amp_mute_pol;int gpio_headphone_mute, gpio_headphone_mute_pol;int gpio_headphone_detect, gpio_headphone_detect_pol;int gpio_headphone_irq;intsetup_audio_gpio(const char *name, const char* compatible, int *gpio_addr, int* gpio_pol){ struct device_node *np; u32* pp; np = find_devices("gpio"); if (!np) return -ENODEV; np = np->child; while(np != 0) { if (name) { char *property = get_property(np,"audio-gpio",NULL); if (property != 0 && strcmp(property,name) == 0) break; } else if (compatible && device_is_compatible(np, compatible)) break; np = np->sibling; } if (!np) return -ENODEV; pp = (u32 *)get_property(np, "AAPL,address", NULL); if (!pp) return -ENODEV; *gpio_addr = (*pp) & 0x0000ffff; pp = (u32 *)get_property(np, "audio-gpio-active-state", NULL); if (pp) *gpio_pol = *pp; else *gpio_pol = 1; if (np->n_intrs > 0) return np->intrs[0].line; return 0;}static inline voidwrite_audio_gpio(int gpio_addr, int data){ if (!gpio_addr) return; pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, gpio_addr, data ? 0x05 : 0x04);}static inline intread_audio_gpio(int gpio_addr){ if (!gpio_addr) return 0; return ((pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, gpio_addr, 0) & 0x02) !=0);}static voidheadphone_intr(int irq, void *devid, struct pt_regs *regs){ if (read_audio_gpio(gpio_headphone_detect) == gpio_headphone_detect_pol) { printk(KERN_INFO "Audio jack plugged, muting speakers.\n"); write_audio_gpio(gpio_amp_mute, gpio_amp_mute_pol); write_audio_gpio(gpio_headphone_mute, !gpio_headphone_mute_pol); } else { printk(KERN_INFO "Audio jack unplugged, enabling speakers.\n"); write_audio_gpio(gpio_amp_mute, !gpio_amp_mute_pol); write_audio_gpio(gpio_headphone_mute, gpio_headphone_mute_pol); }}/* Initialize tumbler */static intawacs_tumbler_init(void){ setup_audio_gpio( "audio-hw-reset", NULL, &gpio_audio_reset, &gpio_audio_reset_pol); setup_audio_gpio( "amp-mute", NULL, &gpio_amp_mute, &gpio_amp_mute_pol); setup_audio_gpio("headphone-mute", NULL, &gpio_headphone_mute, &gpio_headphone_mute_pol); gpio_headphone_irq = setup_audio_gpio( "headphone-detect", NULL, &gpio_headphone_detect, &gpio_headphone_detect_pol); /* Fix some broken OF entries in desktop machines */ if (!gpio_headphone_irq) gpio_headphone_irq = setup_audio_gpio( NULL, "keywest-gpio15", &gpio_headphone_detect, &gpio_headphone_detect_pol); write_audio_gpio(gpio_audio_reset, gpio_audio_reset_pol); wait_ms(100); write_audio_gpio(gpio_audio_reset, !gpio_audio_reset_pol); wait_ms(100); if (gpio_headphone_irq) { if (request_irq(gpio_headphone_irq,headphone_intr,0,"Headphone detect",0) < 0) { printk(KERN_ERR "tumbler: Can't request headphone interrupt\n"); gpio_headphone_irq = 0; } else { u8 val; /* Activate headphone status interrupts */ val = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, gpio_headphone_detect, 0); pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, gpio_headphone_detect, val | 0x80); /* Trigger it */ headphone_intr(0,0,0); } } if (!gpio_headphone_irq) { /* Some machine enter this case ? */ printk(KERN_WARNING "tumbler: Headphone detect IRQ not found, enabling all outputs !\n"); write_audio_gpio(gpio_amp_mute, !gpio_amp_mute_pol); write_audio_gpio(gpio_headphone_mute, !gpio_headphone_mute_pol); } return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -