dmasound_awacs.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:82k
- /*
- * 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 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 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;
- int
- setup_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 void
- write_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 int
- read_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 void
- headphone_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 int
- awacs_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 interruptn");
- 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;
- }
- static int
- awacs_tumbler_cleanup(void)
- {
- if (gpio_headphone_irq)
- free_irq(gpio_headphone_irq, 0);
- return 0;
- }
- /*** AE - TUMBLER END *********************************************************/
- /*** Low level stuff *********************************************************/
- /*
- * PCI PowerMac, with AWACS, Screamer, Burgundy, DACA or Tumbler 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, "Built-in Sound misc", 0)
- || request_irq(awacs_tx_irq, pmac_awacs_tx_intr, 0, "Built-in Sound out", 0)
- || request_irq(awacs_rx_irq, pmac_awacs_rx_intr, 0, "Built-in Sound in", 0))
- return 0;
- return 1;
- }
- #ifdef MODULE
- static void PMacIrqCleanup(void)
- {
- /* turn off input & output dma */
- DBDMA_DO_STOP(awacs_txdma);
- DBDMA_DO_STOP(awacs_rxdma);
- /* disable interrupts from awacs interface */
- out_le32(&awacs->control, in_le32(&awacs->control) & 0xfff);
- /* Switch off the sound clock */
- pmac_call_feature(PMAC_FTR_SOUND_CHIP_ENABLE, awacs_node, 0, 0);
- /* Make sure proper bits are set on pismo & tipb */
- if (machine_is_compatible("PowerBook3,1") ||
- machine_is_compatible("PowerBook3,2")) {
- awacs_reg[1] |= MASK_PAROUT0 | MASK_PAROUT1;
- awacs_write(MASK_ADDR1 | awacs_reg[1]);
- wait_ms(200);
- }
- free_irq(awacs_irq, 0);
- free_irq(awacs_tx_irq, 0);
- free_irq(awacs_rx_irq, 0);
- /* all OF versions I've seen use this value */
- iounmap((void *)awacs);
- iounmap((void *)awacs_txdma);
- iounmap((void *)awacs_rxdma);
- release_OF_resource(awacs_node, 0);
- release_OF_resource(awacs_node, 1);
- release_OF_resource(awacs_node, 2);
- if (awacs_tx_cmd_space)
- kfree(awacs_tx_cmd_space);
- if (awacs_rx_cmd_space)
- kfree(awacs_rx_cmd_space);
- if (beep_dbdma_cmd_space)
- kfree(beep_dbdma_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 */
- DBDMA_DO_STOP(awacs_txdma);
- }
- static int tumbler_freqs[2] = { 48000, 44100 } ;
- static int tumbler_freqs_ok[2] = { 1, 1 } ;
- /* don't know what to do really - just have to leave it where
- * OF left things
- */
- static int tumbler_set_frame_rate(void)
- {
- dmasound.hard.speed = 44100 ;
- awacs_rate_index = 0 ;
- return 44100 ;
- }
- /* don't know what to do really - just have to leave it where
- * OF left things
- */
- static int daca_set_frame_rate(void)
- {
- dmasound.hard.speed = 44100 ;
- awacs_rate_index = 0 ;
- return 44100 ;
- }
- 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 int awacs_set_frame_rate(int desired, int catch_r)
- {
- int tolerance, i = 8 ;
- /*
- * 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 only works at 44100 Hz.
- */
- do {
- tolerance = catch_r * awacs_freqs[--i] / 100;
- if (awacs_freqs_ok[i]
- && dmasound.soft.speed <= awacs_freqs[i] + tolerance)
- break;
- } while (i > 0);
- dmasound.hard.speed = awacs_freqs[i];
- awacs_rate_index = i;
- out_le32(&awacs->control, MASK_IEPC | (i << 8) | 0x11 );
- awacs_reg[1] = (awacs_reg[1] & ~MASK_SAMPLERATE) | (i << 3);
- awacs_write(awacs_reg[1] | MASK_ADDR1);
- return dmasound.hard.speed;
- }
- static int burgundy_frame_rates = 1 ;
- static int burgundy_set_frame_rate(void)
- {
- #ifdef DEBUG_DMASOUND
- if (burgundy_frame_rates > 1)
- printk("dmasound_pmac: warning Burgundy had more than one frame raten");
- #endif
- awacs_rate_index = 0 ;
- awacs_reg[1] = (awacs_reg[1] & ~MASK_SAMPLERATE) ;
- /* XXX disable error interrupt on burgundy for now */
- out_le32(&awacs->control, MASK_IEPC | 0 | 0x11 | MASK_IEE);
- return 44100 ;
- }
- static int set_frame_rate(int desired, int catch_r)
- {
- switch (awacs_revision) {
- case AWACS_BURGUNDY:
- dmasound.hard.speed =
- burgundy_set_frame_rate();
- break ;
- case AWACS_TUMBLER:
- dmasound.hard.speed =
- tumbler_set_frame_rate();
- break ;
- case AWACS_DACA:
- dmasound.hard.speed =
- daca_set_frame_rate();
- break ;
- default:
- dmasound.hard.speed =
- awacs_set_frame_rate(desired, catch_r);
- break ;
- }
- return dmasound.hard.speed ;
- }
- static void
- awacs_recalibrate(void)
- {
- /* Sorry for the horrible delays... I hope to get that improved
- * by making the whole PM process asynchronous in a future version
- */
- wait_ms(750);
- awacs_reg[1] |= MASK_CMUTE | MASK_AMUTE;
- awacs_write(awacs_reg[1] | MASK_RECALIBRATE | MASK_ADDR1);
- wait_ms(1000);
- awacs_write(awacs_reg[1] | MASK_ADDR1);
- }
- static void PMacInit(void)
- {
- int tolerance;
- switch (dmasound.soft.format) {
- case AFMT_S16_LE:
- case AFMT_U16_LE:
- if (hw_can_byteswap)
- dmasound.hard.format = AFMT_S16_LE;
- else
- dmasound.hard.format = AFMT_S16_BE;
- break;
- default:
- dmasound.hard.format = AFMT_S16_BE;
- break;
- }
- dmasound.hard.stereo = 1;
- dmasound.hard.size = 16;
- /* set dmasound.hard.speed - on the basis of what we want (soft)
- * and the tolerance we'll allow.
- */
- set_frame_rate(dmasound.soft.speed, catchRadius) ;
- tolerance = (catchRadius * dmasound.hard.speed) / 100;
- if (dmasound.soft.speed >= dmasound.hard.speed - tolerance)
- dmasound.trans_write = &transAwacsNormal;
- else
- dmasound.trans_write = &transAwacsExpand;
- dmasound.trans_read = &transAwacsNormalRead;
- if (hw_can_byteswap && (dmasound.hard.format == AFMT_S16_LE))
- out_le32(&awacs->byteswap, BS_VAL);
- else
- out_le32(&awacs->byteswap, 0);
- expand_bal = -dmasound.soft.speed;
- }
- static int PMacSetFormat(int format)
- {
- int size;
- int req_format = format;
-
- 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_LE:
- if(!hw_can_byteswap)
- format = AFMT_S16_BE;
- case AFMT_S16_BE:
- size = 16;
- break;
- case AFMT_U16_LE:
- if(!hw_can_byteswap)
- format = AFMT_U16_BE;
- case AFMT_U16_BE:
- size = 16;
- break;
- default: /* :-) */
- printk(KERN_ERR "dmasound: unknown format 0x%x, using AFMT_U8n",
- format);
- size = 8;
- format = AFMT_U8;
- }
-
- if (req_format == format) {
- dmasound.soft.format = format;
- dmasound.soft.size = size;
- if (dmasound.minDev == SND_DEV_DSP) {
- dmasound.dsp.format = format;
- dmasound.dsp.size = size;
- }
- }
- 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 next_frg, count;
- unsigned long flags;
- /* CHECK: how much of this *really* needs IRQs masked? */
- save_flags(flags); cli();
- count = 300 ; /* > two cycles at the lowest sample rate */
- /* what we want to send next */
- next_frg = (write_sq.front + write_sq.active) % write_sq.max_count;
- if (awacs_beep_state) {
- /* sound takes precedence over beeps */
- /* stop the dma channel */
- out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
- while ( (in_le32(&awacs_txdma->status) & RUN) && count--)
- udelay(1);
- /* FIXME: check that this is OK for other chip sets */
- out_le32(&awacs->control,
- (in_le32(&awacs->control) & ~0x1f00)
- | (awacs_rate_index << 8));
- if (hw_can_byteswap && (dmasound.hard.format == AFMT_S16_LE))
- out_le32(&awacs->byteswap, BS_VAL);
- else
- out_le32(&awacs->byteswap, 0);
- out_le32(&awacs_txdma->cmdptr,
- virt_to_bus(&(awacs_tx_cmds[next_frg])));
- beep_playing = 0;
- awacs_beep_state = 0;
- }
- /* this won't allow more than two frags to be in the output queue at
- once. (or one, if the max frags is 2 - because count can't exceed
- 2 in that case)
- */
- 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) {
- if (!write_sq.syncing) /* last block not yet filled,*/
- break; /* and we're not syncing or POST-ed */
- else {
- /* pretend the block is full to force a new
- block to be started on the next write */
- write_sq.rear_size = write_sq.block_size ;
- write_sq.syncing &= ~2 ; /* clear POST */
- }
- }
- cp = &awacs_tx_cmds[next_frg];
- st_le16(&cp->req_count, count);
- st_le16(&cp->xfer_status, 0);
- st_le16(&cp->command, OUTPUT_MORE + INTR_ALWAYS);
- /* put a STOP at the end of the queue - but only if we have
- space for it. This means that, if we under-run and we only
- have two fragments, we might re-play sound from an existing
- queued frag. I guess the solution to that is not to set two
- frags if you are likely to under-run...
- */
- if (write_sq.count < write_sq.max_count) {
- if (++next_frg >= write_sq.max_count)
- next_frg = 0 ; /* wrap */
- /* if we get here then we've underrun so we will stop*/
- st_le16(&awacs_tx_cmds[next_frg].command, DBDMA_STOP);
- }
- /* set the dbdma controller going, if it is not already */
- if (write_sq.active == 0)
- out_le32(&awacs_txdma->cmdptr, virt_to_bus(cp));
- (void)in_le32(&awacs_txdma->status);
- out_le32(&awacs_txdma->control, ((RUN|WAKE) << 16) + (RUN|WAKE));
- ++write_sq.active;
- }
- restore_flags(flags);
- }
- static void PMacPlay(void)
- {
- LOCK();
- if (!awacs_sleeping)
- __PMacPlay();
- UNLOCK();
- }
- 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);
- }
- /* if the TX status comes up "DEAD" - reported on some Power Computing machines
- we need to re-start the dbdma - but from a different physical start address
- and with a different transfer length. It would get very messy to do this
- with the normal dbdma_cmd blocks - we would have to re-write the buffer start
- addresses each time. So, we will keep a single dbdma_cmd block which can be
- fiddled with.
- When DEAD status is first reported the content of the faulted dbdma block is
- copied into the emergency buffer and we note that the buffer is in use.
- we then bump the start physical address by the amount that was successfully
- output before it died.
- On any subsequent DEAD result we just do the bump-ups (we know that we are
- already using the emergency dbdma_cmd).
- CHECK: this just tries to "do it". It is possible that we should abandon
- xfers when the number of residual bytes gets below a certain value - I can
- see that this might cause a loop-forever if too small a transfer causes
- DEAD status. However this is a TODO for now - we'll see what gets reported.
- When we get a successful transfer result with the emergency buffer we just
- pretend that it completed using the original dmdma_cmd and carry on. The
- 'next_cmd' field will already point back to the original loop of blocks.
- */
- 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;
- /* != 0 when we are dealing with a DEAD xfer */
- static int emergency_in_use = 0 ;
- while (write_sq.active > 0) { /* we expect to have done something*/
- if (emergency_in_use) /* we are dealing with DEAD xfer */
- cp = emergency_dbdma_cmd ;
- else
- cp = &awacs_tx_cmds[i];
- stat = ld_le16(&cp->xfer_status);
- if (stat & DEAD) {
- unsigned short req, res ;
- unsigned int phy ;
- #ifdef DEBUG_DMASOUND
- printk("dmasound_pmac: tx-irq: xfer died - patching it up...n") ;
- #endif
- /* to clear DEAD status we must first clear RUN
- set it to quiescent to be on the safe side */
- (void)in_le32(&awacs_txdma->status);
- out_le32(&awacs_txdma->control,
- (RUN|PAUSE|FLUSH|WAKE) << 16);
- write_sq.died++ ;
- if (!emergency_in_use) { /* new problem */
- memcpy((void *)emergency_dbdma_cmd, (void *)cp,
- sizeof(struct dbdma_cmd));
- emergency_in_use = 1;
- cp = emergency_dbdma_cmd;
- }
- /* now bump the values to reflect the amount
- we haven't yet shifted */
- req = ld_le16(&cp->req_count);
- res = ld_le16(&cp->res_count);
- phy = ld_le32(&cp->phy_addr);
- phy += (req - res);
- st_le16(&cp->req_count, res);
- st_le16(&cp->res_count, 0);
- st_le16(&cp->xfer_status, 0);
- st_le32(&cp->phy_addr, phy);
- st_le16(&cp->command, OUTPUT_MORE + INTR_ALWAYS);
- /* point at our patched up command block */
- out_le32(&awacs_txdma->cmdptr, virt_to_bus(cp));
- /* we must re-start the controller */
- (void)in_le32(&awacs_txdma->status);
- /* should complete clearing the DEAD status */
- out_le32(&awacs_txdma->control,
- ((RUN|WAKE) << 16) + (RUN|WAKE));
- break; /* this block is still going */
- }
- if ((stat & ACTIVE) == 0)
- break; /* this frame is still going */
- if (emergency_in_use)
- emergency_in_use = 0 ; /* done that */
- --write_sq.count;
- --write_sq.active;
- if (++i >= write_sq.max_count)
- i = 0;
- }
- /* if we stopped and we were not sync-ing - then we under-ran */
- if( write_sq.syncing == 0 ){
- stat = in_le32(&awacs_txdma->status) ;
- /* we hit the dbdma_stop */
- if( (stat & ACTIVE) == 0 ) write_sq.xruns++ ;
- }
- /* if we used some data up then wake the writer to supply some more*/
- if (i != write_sq.front)
- WAKE_UP(write_sq.action_queue);
- write_sq.front = i;
- /* but make sure we funnel what we've already got */
- if (!awacs_sleeping)
- __PMacPlay();
- /* make the wake-on-empty conditional on syncing */
- if (!write_sq.active && (write_sq.syncing & 1))
- WAKE_UP(write_sq.sync_queue); /* any time we're empty */
- }
- static void
- pmac_awacs_rx_intr(int irq, void *devid, struct pt_regs *regs)
- {
- int stat ;
- /* 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 initialized 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 ((stat=awacs_rx_cmds[read_sq.rear].xfer_status)) {
- /* if we got a "DEAD" status then just log it for now.
- and try to restart dma.
- TODO: figure out how best to fix it up
- */
- if (stat & DEAD){
- #ifdef DEBUG_DMASOUND
- printk("dmasound_pmac: rx-irq: DIED - attempting resurectionn");
- #endif
- /* to clear DEAD status we must first clear RUN
- set it to quiescent to be on the safe side */
- (void)in_le32(&awacs_txdma->status);
- out_le32(&awacs_txdma->control,
- (RUN|PAUSE|FLUSH|WAKE) << 16);
- awacs_rx_cmds[read_sq.rear].xfer_status = 0;
- awacs_rx_cmds[read_sq.rear].res_count = 0;
- read_sq.died++ ;
- (void)in_le32(&awacs_txdma->status);
- /* re-start the same block */
- out_le32(&awacs_rxdma->cmdptr,
- virt_to_bus(&awacs_rx_cmds[read_sq.rear]));
- /* we must re-start the controller */
- (void)in_le32(&awacs_rxdma->status);
- /* should complete clearing the DEAD status */
- out_le32(&awacs_rxdma->control,
- ((RUN|WAKE) << 16) + (RUN|WAKE));
- return; /* try this block again */
- }
- /* 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++;
- read_sq.xruns++ ; /* we overan */
- 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;
- /* CHECK: we just swallow burgundy errors at the moment..*/
- if (err != 0 && awacs_revision != AWACS_BURGUNDY)
- printk(KERN_ERR "dmasound_pmac: error %xn", err);
- }
- /* Writing 1s to the CNTLERR and PORTCHG bits clears them... */
- out_le32(&awacs->control, ctrl);
- }
- static void
- awacs_write(int val)
- {
- int count = 300 ;
- if (awacs_revision >= AWACS_DACA)
- return ;
- while ((in_le32(&awacs->codec_ctrl) & MASK_NEWECMD) && count--)
- udelay(1) ; /* timeout is > 2 samples at lowest rate */
- out_le32(&awacs->codec_ctrl, val | (awacs_subframe << 22));
- (void)in_le32(&awacs->byteswap);
- }
- /* this is called when the beep timer expires... it will be called even
- if the beep has been overidden by other sound output.
- */
- static void awacs_nosound(unsigned long xx)
- {
- unsigned long flags;
- int count = 600 ; /* > four samples at lowest rate */
- 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);
- while ((in_le32(&awacs_txdma->status) & RUN) && count--)
- udelay(1);
- /* FIXME: check this is OK for DACA, Tumbler */
- out_le32(&awacs->control,
- (in_le32(&awacs->control) & ~0x1f00)
- | (awacs_rate_index << 8));
- if (hw_can_byteswap && (dmasound.hard.format == AFMT_S16_LE))
- out_le32(&awacs->byteswap, BS_VAL);
- else
- out_le32(&awacs->byteswap, 0);
- beep_playing = 0;
- }
- restore_flags(flags);
- }
- static struct timer_list beep_timer = {
- function: awacs_nosound
- };
- /* we generate the beep with a single dbdma command that loops a buffer
- forever - without generating interrupts.
- So, to stop it you have to stop dma output as per 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;
- if (beep_buf == NULL)
- return;
- /* quick-hack fix for DACA, Burgundy & Tumbler */
- if (awacs_revision >= AWACS_DACA){
- srate = 44100 ;
- } else {
- 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_vol == 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_vol;
- j = (j + f) & 0xffff;
- }
- beep_hz_cache = hz;
- beep_volume_cache = beep_vol;
- 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 */
- int count = 300 ;
- out_le32(&awacs_txdma->control, (RUN|WAKE|FLUSH|PAUSE) << 16);
- while ((in_le32(&awacs_txdma->status) & RUN) && count--)
- udelay(1); /* timeout > 2 samples at lowest rate*/
- /* FIXME: check this is OK on DACA, Tumbler */
- out_le32(&awacs->control,
- (in_le32(&awacs->control) & ~0x1f00)
- | (beep_speed << 8));
- out_le32(&awacs->byteswap, 0); /* force BE */
- out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd));
- (void)in_le32(&awacs_txdma->status);
- out_le32(&awacs_txdma->control, RUN | (RUN << 16));
- }
- restore_flags(flags);
- }
- /* used in init and for wake-up */
- static void
- load_awacs(void)
- {
- 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 (awacs_revision == AWACS_SCREAMER) {
- awacs_write(awacs_reg[5] + MASK_ADDR5);
- wait_ms(100);
- awacs_write(awacs_reg[6] + MASK_ADDR6);
- wait_ms(2);
- awacs_write(awacs_reg[1] + MASK_ADDR1);
- awacs_write(awacs_reg[7] + MASK_ADDR7);
- }
- if (hw_can_byteswap && (dmasound.hard.format == AFMT_S16_LE))
- out_le32(&awacs->byteswap, BS_VAL);
- else
- out_le32(&awacs->byteswap, 0);
- }
- #ifdef CONFIG_PMAC_PBOOK
- /*
- * Save state when going to sleep, restore it afterwards.
- */
- /* FIXME: sort out disabling/re-enabling of read stuff as well */
- static int awacs_sleep_notify(struct pmu_sleep_notifier *self, int when)
- {
- switch (when) {
- case PBOOK_SLEEP_NOW:
- LOCK();
- awacs_sleeping = 1;
- /* Tell the rest of the driver we are now going to sleep */
- mb();
- if (awacs_revision == AWACS_SCREAMER ||
- awacs_revision == AWACS_AWACS) {
- awacs_reg1_save = awacs_reg[1];
- awacs_reg[1] |= MASK_AMUTE | MASK_CMUTE;
- awacs_write(MASK_ADDR1 | awacs_reg[1]);
- }
- PMacSilence();
- /* stop rx - if going - a bit of a daft user... but */
- out_le32(&awacs_rxdma->control, (RUN|WAKE|FLUSH << 16));
- /* deny interrupts */
- switch (awacs_revision) {
- case AWACS_TUMBLER:
- tumbler_enter_sleep(); /* Stub for now */
- break ;
- case AWACS_DACA:
- daca_enter_sleep();
- break ;
- case AWACS_BURGUNDY:
- break ;
- case AWACS_SCREAMER:
- case AWACS_AWACS:
- default:
- out_le32(&awacs->control, 0x11) ;
- break ;
- }
- disable_irq(awacs_irq);
- disable_irq(awacs_tx_irq);
- disable_irq(awacs_rx_irq);
- /* Disable sound clock */
- pmac_call_feature(PMAC_FTR_SOUND_CHIP_ENABLE, awacs_node, 0, 0);
- /* According to Darwin, we do that after turning off the sound
- * chip clock. All this will have to be cleaned up once we properly
- * parse the OF sound-objects
- */
- if (machine_is_compatible("PowerBook3,1") ||
- machine_is_compatible("PowerBook3,2")) {
- awacs_reg[1] |= MASK_PAROUT0 | MASK_PAROUT1;
- awacs_write(MASK_ADDR1 | awacs_reg[1]);
- wait_ms(200);
- }
- break;
- case PBOOK_WAKE:
- /* Enable sound clock */
- pmac_call_feature(PMAC_FTR_SOUND_CHIP_ENABLE, awacs_node, 0, 1);
- if (machine_is_compatible("PowerBook3,1") ||
- machine_is_compatible("PowerBook3,2")) {
- wait_ms(100);
- awacs_reg[1] &= ~(MASK_PAROUT0 | MASK_PAROUT1);
- awacs_write(MASK_ADDR1 | awacs_reg[1]);
- wait_ms(300);
- } else
- wait_ms(1000);
- /* restore settings */
- switch (awacs_revision) {
- case AWACS_TUMBLER:
- headphone_intr(0,0,0);
- tumbler_leave_sleep(); /* Stub for now */
- break;
- case AWACS_DACA:
- wait_ms(10); /* Check this !!! */
- daca_leave_sleep();
- break ; /* dont know how yet */
- case AWACS_BURGUNDY:
- break ;
- case AWACS_SCREAMER:
- case AWACS_AWACS:
- default:
- load_awacs() ;
- break ;
- }
- /* Recalibrate chip */
- if (awacs_revision == AWACS_SCREAMER)
- awacs_recalibrate();
- /* Make sure dma is stopped */
- PMacSilence();
- enable_irq(awacs_irq);
- enable_irq(awacs_tx_irq);
- enable_irq(awacs_rx_irq);
- /* OK, allow ints back again */
- out_le32(&awacs->control, MASK_IEPC
- | (awacs_rate_index << 8) | 0x11
- | (awacs_revision < AWACS_DACA ? MASK_IEE: 0));
- if (macio_base && is_pbook_g3) {
- /* FIXME: should restore the setup we had...*/
- out_8(macio_base + 0x37, 3);
- } else if (is_pbook_3X00) {
- in_8(latch_base + 0x190);
- }
- /* Remove mute */
- if (awacs_revision == AWACS_SCREAMER ||
- awacs_revision == AWACS_AWACS) {
- awacs_reg[1] = awacs_reg1_save;
- awacs_write(MASK_ADDR1 | awacs_reg[1]);
- }
- awacs_sleeping = 0;
- /* Resume pending sounds. */
- /* we don't try to restart input... */
- __PMacPlay();
- UNLOCK();
- }
- 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)
- {
- int count = 50; /* > 2 samples at 44k1 */
- while ((in_le32(&awacs->codec_ctrl) & MASK_NEWECMD) && count--)
- udelay(1) ;
- }
- inline static void
- awacs_burgundy_extend_wait(void)
- {
- int count = 50 ; /* > 2 samples at 44k1 */
- while ((!(in_le32(&awacs->codec_stat) & MASK_EXTEND)) && count--)
- udelay(1) ;
- count = 50;
- while ((in_le32(&awacs->codec_stat) & MASK_EXTEND) && count--)
- udelay(1);
- }
- 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 "dmasound_pmac: burgundy not working :-(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 */
- /* Set up output volumes on machines with the 'perch/whisper' extension card.
- * this has an SGS i2c chip (7433) which is accessed using the cuda.
- *
- * TODO: split this out and make use of the other parts of the SGS chip to
- * do Bass, Treble etc.
- */
- static void
- awacs_enable_amp(int spkr_vol)
- {
- #ifdef CONFIG_ADB_CUDA
- 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();
- #endif /* CONFIG_ADB_CUDA */
- }
- /*** Mid level stuff *********************************************************/
- /*
- * /dev/mixer abstraction
- */
- static void do_line_lev(int data)
- {
- line_lev = data ;
- awacs_reg[0] &= ~MASK_MUX_AUDIN;
- if ((data & 0xff) >= 50)
- awacs_reg[0] |= MASK_MUX_AUDIN;
- awacs_write(MASK_ADDR0 | awacs_reg[0]);
- }
- static void do_ip_gain(int data)
- {
- ip_gain = data ;
- data &= 0xff;
- awacs_reg[0] &= ~MASK_GAINLINE;
- if (awacs_revision == AWACS_SCREAMER) {
- awacs_reg[6] &= ~MASK_MIC_BOOST ;
- if (data >= 33) {
- awacs_reg[0] |= MASK_GAINLINE;
- if( data >= 66)
- awacs_reg[6] |= MASK_MIC_BOOST ;
- }
- awacs_write(MASK_ADDR6 | awacs_reg[6]) ;
- } else {
- if (data >= 50)
- awacs_reg[0] |= MASK_GAINLINE;
- }
- awacs_write(MASK_ADDR0 | awacs_reg[0]);
- }
- static void do_mic_lev(int data)
- {
- mic_lev = data ;
- data &= 0xff;
- awacs_reg[0] &= ~MASK_MUX_MIC;
- if (data >= 50)
- awacs_reg[0] |= MASK_MUX_MIC;
- awacs_write(MASK_ADDR0 | awacs_reg[0]);
- }
- static void do_cd_lev(int data)
- {
- cd_lev = data ;
- awacs_reg[0] &= ~MASK_MUX_CD;
- if ((data & 0xff) >= 50)
- awacs_reg[0] |= MASK_MUX_CD;
- awacs_write(MASK_ADDR0 | awacs_reg[0]);
- }
- static void do_rec_lev(int data)
- {
- int left, right ;
- rec_lev = data ;
- /* need to fudge this to use the volume setter routine */
- left = 100 - (data & 0xff) ; if( left < 0 ) left = 0 ;
- right = 100 - ((data >> 8) & 0xff) ; if( right < 0 ) right = 0 ;
- left |= (right << 8 );
- left = awacs_volume_setter(left, 0, 0, 4);
- }
- static void do_passthru_vol(int data)
- {
- passthru_vol = data ;
- awacs_reg[1] &= ~MASK_LOOPTHRU;
- if (awacs_revision == AWACS_SCREAMER) {
- if( data ) { /* switch it on for non-zero */
- awacs_reg[1] |= MASK_LOOPTHRU;
- awacs_write(MASK_ADDR1 | awacs_reg[1]);
- }
- data = awacs_volume_setter(data, 5, 0, 6) ;
- } else {
- if ((data & 0xff) >= 50)
- awacs_reg[1] |= MASK_LOOPTHRU;
- awacs_write(MASK_ADDR1 | awacs_reg[1]);
- data = (awacs_reg[1] & MASK_LOOPTHRU)? 100: 0;
- }
- }
- static int awacs_mixer_ioctl(u_int cmd, u_long arg)
- {
- int data;
- int rc;
- switch (cmd) {
- case SOUND_MIXER_READ_CAPS:
- /* say we will allow multiple inputs? prob. wrong
- so I'm switching it to single */
- return IOCTL_OUT(arg, 1);
- case SOUND_MIXER_READ_DEVMASK:
- data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER
- | SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD
- | SOUND_MASK_IGAIN | SOUND_MASK_RECLEV
- | SOUND_MASK_ALTPCM
- | SOUND_MASK_MONITOR;
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_READ_RECMASK:
- data = SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD;
- rc = IOCTL_OUT(arg, data);
- break;
- 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;
- rc = IOCTL_OUT(arg, data);
- break;
- 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);
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_READ_STEREODEVS:
- data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER| SOUND_MASK_RECLEV ;
- if (awacs_revision == AWACS_SCREAMER)
- data |= SOUND_MASK_MONITOR ;
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_WRITE_VOLUME:
- IOCTL_IN(arg, data);
- line_vol = data ;
- awacs_volume_setter(data, 2, 0, 6);
- /* fall through */
- case SOUND_MIXER_READ_VOLUME:
- rc = IOCTL_OUT(arg, line_vol);
- break;
- case SOUND_MIXER_WRITE_SPEAKER:
- IOCTL_IN(arg, data);
- spk_vol = data ;
- if (has_perch)
- awacs_enable_amp(data);
- else
- (void)awacs_volume_setter(data, 4, MASK_CMUTE, 6);
- /* fall though */
- case SOUND_MIXER_READ_SPEAKER:
- rc = IOCTL_OUT(arg, spk_vol);
- break;
- case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */
- IOCTL_IN(arg, data);
- beep_vol = data & 0xff;
- /* fall through */
- case SOUND_MIXER_READ_ALTPCM:
- rc = IOCTL_OUT(arg, beep_vol);
- break;
- case SOUND_MIXER_WRITE_LINE:
- IOCTL_IN(arg, data);
- do_line_lev(data) ;
- /* fall through */
- case SOUND_MIXER_READ_LINE:
- rc = IOCTL_OUT(arg, line_lev);
- break;
- case SOUND_MIXER_WRITE_IGAIN:
- IOCTL_IN(arg, data);
- do_ip_gain(data) ;
- /* fall through */
- case SOUND_MIXER_READ_IGAIN:
- rc = IOCTL_OUT(arg, ip_gain);
- break;
- case SOUND_MIXER_WRITE_MIC:
- IOCTL_IN(arg, data);
- do_mic_lev(data);
- /* fall through */
- case SOUND_MIXER_READ_MIC:
- rc = IOCTL_OUT(arg, mic_lev);
- break;
- case SOUND_MIXER_WRITE_CD:
- IOCTL_IN(arg, data);
- do_cd_lev(data);
- /* fall through */
- case SOUND_MIXER_READ_CD:
- rc = IOCTL_OUT(arg, cd_lev);
- break;
- case SOUND_MIXER_WRITE_RECLEV:
- IOCTL_IN(arg, data);
- do_rec_lev(data) ;
- /* fall through */
- case SOUND_MIXER_READ_RECLEV:
- rc = IOCTL_OUT(arg, rec_lev);
- break;
- case MIXER_WRITE(SOUND_MIXER_MONITOR):
- IOCTL_IN(arg, data);
- do_passthru_vol(data) ;
- /* fall through */
- case MIXER_READ(SOUND_MIXER_MONITOR):
- rc = IOCTL_OUT(arg, passthru_vol);
- break;
- default:
- rc = -EINVAL;
- }
-
- return rc;
- }
- static void awacs_mixer_init(void)
- {
- awacs_volume_setter(line_vol, 2, 0, 6);
- if (has_perch)
- awacs_enable_amp(spk_vol);
- else
- (void)awacs_volume_setter(spk_vol, 4, MASK_CMUTE, 6);
- do_line_lev(line_lev) ;
- do_ip_gain(ip_gain) ;
- do_mic_lev(mic_lev) ;
- do_cd_lev(cd_lev) ;
- do_rec_lev(rec_lev) ;
- do_passthru_vol(passthru_vol) ;
- }
- static int burgundy_mixer_ioctl(u_int cmd, u_long arg)
- {
- int data;
- int rc;
- /* 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;
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_READ_RECMASK:
- data = SOUND_MASK_LINE | SOUND_MASK_MIC
- | SOUND_MASK_CD;
- rc = IOCTL_OUT(arg, data);
- break;
- 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;
- rc = IOCTL_OUT(arg, data);
- break;
- 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);
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_READ_STEREODEVS:
- data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER
- | SOUND_MASK_RECLEV | SOUND_MASK_CD
- | SOUND_MASK_LINE;
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_READ_CAPS:
- rc = IOCTL_OUT(arg, 0);
- break;
- 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:
- rc = IOCTL_OUT(arg, awacs_burgundy_read_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME));
- break;
- 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);
- rc = IOCTL_OUT(arg, ~data);
- break;
- case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */
- IOCTL_IN(arg, data);
- beep_vol = data & 0xff;
- /* fall through */
- case SOUND_MIXER_READ_ALTPCM:
- rc = IOCTL_OUT(arg, beep_vol);
- break;
- 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);
- rc = IOCTL_OUT(arg, data);
- break;
- 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;
- rc = IOCTL_OUT(arg, data);
- break;
- 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);
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_WRITE_RECLEV:
- IOCTL_IN(arg, data);
- data = awacs_volume_setter(data, 0, 0, 4);
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_READ_RECLEV:
- data = awacs_get_volume(awacs_reg[0], 4);
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_OUTMASK:
- case SOUND_MIXER_OUTSRC:
- default:
- rc = -EINVAL;
- }
-
- return rc;
- }
- static int tumbler_mixer_ioctl(u_int cmd, u_long arg)
- {
- int data;
- int rc;
- /* We are, we are, we are... Tumbler (and very dumb) */
- /* Ok, we're not THAT dumb anymore, but still pretty dumb :-) */
- switch(cmd) {
- case SOUND_MIXER_READ_DEVMASK:
- data = SOUND_MASK_VOLUME | SOUND_MASK_ALTPCM |
- SOUND_MASK_BASS | SOUND_MASK_TREBLE |
- SOUND_MASK_PCM;
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_READ_RECMASK:
- data = 0;
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_READ_RECSRC:
- data = 0;
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_WRITE_RECSRC:
- IOCTL_IN(arg, data);
- data =0;
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_READ_STEREODEVS:
- data = SOUND_MASK_VOLUME | SOUND_MASK_PCM;
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_READ_CAPS:
- rc = IOCTL_OUT(arg, 0);
- break;
- case SOUND_MIXER_WRITE_BASS:
- IOCTL_IN(arg, data);
- tumbler_set_bass(data);
- /* Fall through */
- case SOUND_MIXER_READ_BASS:
- tumbler_get_bass(&data);
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_WRITE_TREBLE:
- IOCTL_IN(arg, data);
- tumbler_set_treble(data);
- /* Fall through */
- case SOUND_MIXER_READ_TREBLE:
- tumbler_get_treble(&data);
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_WRITE_PCM:
- IOCTL_IN(arg, data);
- tumbler_set_pcm_lvl(data);
- /* Fall through */
- case SOUND_MIXER_READ_PCM:
- tumbler_get_pcm_lvl(&data);
- IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_WRITE_VOLUME:
- IOCTL_IN(arg, data);
- tumbler_set_volume(data, data);
- /* Fall through */
- case SOUND_MIXER_READ_VOLUME:
- tumbler_get_volume(& data, &data);
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */
- IOCTL_IN(arg, data);
- beep_vol = data & 0xff;
- /* fall through */
- case SOUND_MIXER_READ_ALTPCM:
- rc = IOCTL_OUT(arg, beep_vol);
- break;
- case SOUND_MIXER_OUTMASK:
- case SOUND_MIXER_OUTSRC:
- default:
- rc = -EINVAL;
- }
- return rc;
- }
- static int daca_mixer_ioctl(u_int cmd, u_long arg)
- {
- int data;
- int rc;
- /* And the DACA's no genius either! */
- switch(cmd) {
- case SOUND_MIXER_READ_DEVMASK:
- data = SOUND_MASK_VOLUME;
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_READ_RECMASK:
- data = 0;
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_READ_RECSRC:
- data = 0;
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_WRITE_RECSRC:
- IOCTL_IN(arg, data);
- data =0;
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_READ_STEREODEVS:
- data = SOUND_MASK_VOLUME;
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_READ_CAPS:
- rc = IOCTL_OUT(arg, 0);
- break;
- case SOUND_MIXER_WRITE_VOLUME:
- IOCTL_IN(arg, data);
- daca_set_volume(data, data);
- /* Fall through */
- case SOUND_MIXER_READ_VOLUME:
- daca_get_volume(& data, &data);
- rc = IOCTL_OUT(arg, data);
- break;
- case SOUND_MIXER_OUTMASK:
- case SOUND_MIXER_OUTSRC:
- default:
- rc = -EINVAL;
- }
- return rc;
- }
- static int PMacMixerIoctl(u_int cmd, u_long arg)
- {
- int rc;
-
- /* Different IOCTLS for burgundy and, eventually, DACA & Tumbler */
- TRY_LOCK();
-
- switch (awacs_revision){
- case AWACS_BURGUNDY:
- rc = burgundy_mixer_ioctl(cmd, arg);
- break ;
- case AWACS_DACA:
- rc = daca_mixer_ioctl(cmd, arg);
- break;
- case AWACS_TUMBLER:
- rc = tumbler_mixer_ioctl(cmd, arg);
- break ;
- default: /* ;-)) */
- rc = awacs_mixer_ioctl(cmd, arg);
- }
- UNLOCK();
-
- return rc;
- }
- static void PMacMixerInit(void)
- {
- switch (awacs_revision) {
- case AWACS_TUMBLER:
- printk("AE-Init tumbler mixern");
- break ;
-
- case AWACS_DACA:
- case AWACS_BURGUNDY:
- break ; /* don't know yet */
- case AWACS_AWACS:
- case AWACS_SCREAMER:
- default:
- awacs_mixer_init() ;
- break ;
- }
- }
- /* Write/Read sq setup functions:
- Check to see if we have enough (or any) dbdma cmd buffers for the
- user's fragment settings. If not, allocate some. If this fails we will
- point at the beep buffer - as an emergency provision - to stop dma tromping
- on some random bit of memory (if someone lets it go anyway).
- The command buffers are then set up to point to the fragment buffers
- (allocated elsewhere). We need n+1 commands the last of which holds
- a NOP + loop to start.
- */
- static int PMacWriteSqSetup(void)
- {
- int i, count = 600 ;
- volatile struct dbdma_cmd *cp;
- LOCK();
-
- /* stop the controller from doing any output - if it isn't already.
- it _should_ be before this is called anyway */
- out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
- while ((in_le32(&awacs_txdma->status) & RUN) && count--)
- udelay(1);
- #ifdef DEBUG_DMASOUND
- if (count <= 0)
- printk("dmasound_pmac: write sq setup: timeout waiting for dma to stopn");
- #endif
- if ((write_sq.max_count + 1) > number_of_tx_cmd_buffers) {
- if (awacs_tx_cmd_space)
- kfree(awacs_tx_cmd_space);
- number_of_tx_cmd_buffers = 0;
- /* we need nbufs + 1 (for the loop) and we should request + 1
- again because the DBDMA_ALIGN might pull the start up by up
- to sizeof(struct dbdma_cmd) - 4.
- */
- awacs_tx_cmd_space = kmalloc
- ((write_sq.max_count + 1 + 1) * sizeof(struct dbdma_cmd),
- GFP_KERNEL);
- if (awacs_tx_cmd_space == NULL) {
- /* don't leave it dangling - nasty but better than a
- random address */
- out_le32(&awacs_txdma->cmdptr, virt_to_bus(beep_dbdma_cmd));
- printk(KERN_ERR
- "dmasound_pmac: can't allocate dbdma cmd buffers"
- ", driver disabledn");
- UNLOCK();
- return -ENOMEM;
- }
- awacs_tx_cmds = (volatile struct dbdma_cmd *)
- DBDMA_ALIGN(awacs_tx_cmd_space);
- number_of_tx_cmd_buffers = write_sq.max_count + 1;
- }
- cp = awacs_tx_cmds;
- memset((void *)cp, 0, (write_sq.max_count+1) * sizeof(struct dbdma_cmd));
- for (i = 0; i < write_sq.max_count; ++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));
- /* point the controller at the command stack - ready to go */
- out_le32(&awacs_txdma->cmdptr, virt_to_bus(awacs_tx_cmds));
- UNLOCK();
- return 0;
- }
- static int PMacReadSqSetup(void)
- {
- int i, count = 600;
- volatile struct dbdma_cmd *cp;
- LOCK();
-
- /* stop the controller from doing any input - if it isn't already.
- it _should_ be before this is called anyway */
-
- out_le32(&awacs_rxdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
- while ((in_le32(&awacs_rxdma->status) & RUN) && count--)
- udelay(1);
- #ifdef DEBUG_DMASOUND
- if (count <= 0)
- printk("dmasound_pmac: read sq setup: timeout waiting for dma to stopn");
- #endif
- if ((read_sq.max_count+1) > number_of_rx_cmd_buffers ) {
- if (awacs_rx_cmd_space)
- kfree(awacs_rx_cmd_space);
- number_of_rx_cmd_buffers = 0;
- /* we need nbufs + 1 (for the loop) and we should request + 1 again
- because the DBDMA_ALIGN might pull the start up by up to
- sizeof(struct dbdma_cmd) - 4 (assuming kmalloc aligns 32 bits).
- */
- awacs_rx_cmd_space = kmalloc
- ((read_sq.max_count + 1 + 1) * sizeof(struct dbdma_cmd),
- GFP_KERNEL);
- if (awacs_rx_cmd_space == NULL) {
- /* don't leave it dangling - nasty but better than a
- random address */
- out_le32(&awacs_rxdma->cmdptr, virt_to_bus(beep_dbdma_cmd));
- printk(KERN_ERR
- "dmasound_pmac: can't allocate dbdma cmd buffers"
- ", driver disabledn");
- UNLOCK();
- return -ENOMEM;
- }
- awacs_rx_cmds = (volatile struct dbdma_cmd *)
- DBDMA_ALIGN(awacs_rx_cmd_space);
- number_of_rx_cmd_buffers = read_sq.max_count + 1 ;
- }
- cp = awacs_rx_cmds;
- memset((void *)cp, 0, (read_sq.max_count+1) * sizeof(struct dbdma_cmd));
- /* Set dma buffers up in a loop */
- for (i = 0; i < read_sq.max_count; 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));
- /* point the controller at the command stack - ready to go */
- out_le32(&awacs_rxdma->cmdptr, virt_to_bus(awacs_rx_cmds));
- UNLOCK();
- return 0;
- }
- /* TODO: this needs work to guarantee that when it returns DMA has stopped
- but in a more elegant way than is done here....
- */
- static void PMacAbortRead(void)
- {
- int i;
- volatile struct dbdma_cmd *cp;
- LOCK();
- /* give it a chance to update the output and provide the IRQ
- that is expected.
- */
- out_le32(&awacs_rxdma->control, ((FLUSH) << 16) + FLUSH );
- cp = awacs_rx_cmds;
- for (i = 0; i < read_sq.max_count; i++,cp++)
- st_le16(&cp->command, DBDMA_STOP);
- /*
- * We should probably wait for the thing to stop before we
- * release the memory.
- */
- wait_ms(100) ; /* give it a (small) chance to act */
- /* apply the sledgehammer approach - just stop it now */
- out_le32(&awacs_rxdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16);
- UNLOCK();
- }
- extern char *get_afmt_string(int);
- static int PMacStateInfo(char *b, size_t sp)
- {
- int i, len = 0;
- len = sprintf(b,"HW rates: ");
- switch (awacs_revision){
- case AWACS_DACA:
- case AWACS_BURGUNDY:
- len += sprintf(b,"44100 ") ;
- break ;
- case AWACS_TUMBLER:
- for (i=0; i<2; i++){
- if (tumbler_freqs_ok[i])
- len += sprintf(b+len,"%d ", tumbler_freqs[i]) ;
- }
- break ;
- case AWACS_AWACS:
- case AWACS_SCREAMER:
- default:
- for (i=0; i<8; i++){
- if (awacs_freqs_ok[i])
- len += sprintf(b+len,"%d ", awacs_freqs[i]) ;
- }
- break ;
- }
- len += sprintf(b+len,"s/secn") ;
- if (len < sp) {
- len += sprintf(b+len,"HW AFMTS: ");
- i = AFMT_U16_BE ;
- while (i) {
- if (i & dmasound.mach.hardware_afmts)
- len += sprintf(b+len,"%s ",
- get_afmt_string(i & dmasound.mach.hardware_afmts));
- i >>= 1 ;
- }
- len += sprintf(b+len,"n") ;
- }
- return len ;
- }
- /*** Machine definitions *****************************************************/
- static SETTINGS def_hard = {
- format: AFMT_S16_BE,
- stereo: 1,
- size: 16,
- speed: 44100
- } ;
- static SETTINGS def_soft = {
- format: AFMT_S16_BE,
- stereo: 1,
- size: 16,
- speed: 44100
- } ;
- static MACHINE machPMac = {
- name: awacs_name,
- name2: "PowerMac Built-in Sound",
- 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: NULL, /* default to no record */
- mixer_init: PMacMixerInit,
- mixer_ioctl: PMacMixerIoctl,
- write_sq_setup: PMacWriteSqSetup,
- read_sq_setup: PMacReadSqSetup,
- state_info: PMacStateInfo,
- abort_read: PMacAbortRead,
- min_dsp_speed: 7350,
- max_dsp_speed: 44100,
- version: ((DMASOUND_AWACS_REVISION<<8) + DMASOUND_AWACS_EDITION)
- };
- /*** Config & Setup **********************************************************/
- /* Check for pmac models that we care about in terms of special actions.
- */
- void __init
- set_model(void)
- {
- /* portables/lap-tops */
- if (machine_is_compatible("AAPL,3400/2400") ||
- machine_is_compatible("AAPL,3500")) {
- is_pbook_3X00 = 1 ;
- }
- if (machine_is_compatible("PowerBook1,1") || /* lombard */
- machine_is_compatible("AAPL,PowerBook1998")){ /* wallstreet */
- is_pbook_g3 = 1 ;
- return ;
- }
- }
- /* Get the OF node that tells us about the registers, interrupts etc. to use
- for sound IO.
- On most machines the sound IO OF node is the 'davbus' node. On newer pmacs
- with DACA (& Tumbler) the node to use is i2s-a. On much older machines i.e.
- before 9500 there is no davbus node and we have to use the 'awacs' property.
- In the latter case we signal this by setting the codec value - so that the
- code that looks for chip properties knows how to go about it.
- */
- static struct device_node
- __init *get_snd_io_node(void)
- {
- struct device_node *np = NULL;
- /* set up awacs_node for early OF which doesn't have a full set of
- * properties on davbus
- */
- awacs_node = find_devices("awacs");
- if (awacs_node)
- awacs_revision = AWACS_AWACS;
- /* powermac models after 9500 (other than those which use DACA or
- * Tumbler) have a node called "davbus".
- */
- np = find_devices("davbus");
- /*
- * if we didn't find a davbus device, try 'i2s-a' since
- * this seems to be what iBooks (& Tumbler) have.
- */
- if (np == NULL)
- np = find_devices("i2s-a");
- /* if we didn't find this - perhaps we are on an early model
- * which _only_ has an 'awacs' node
- */
- if (np == NULL && awacs_node)
- np = awacs_node ;
- /* if we failed all these return null - this will cause the
- * driver to give up...
- */
- return np ;
- }
- /* Get the OF node that contains the info about the sound chip, inputs s-rates
- etc.
- This node does not exist (or contains much reduced info) on earlier machines
- we have to deduce the info other ways for these.
- */
- static struct device_node
- __init *get_snd_info_node(struct device_node *io)
- {
- struct device_node *info;
- info = find_devices("sound");
- while (info != 0 && info->parent != io)
- info = info->next;
- return info ;
- }
- /* Find out what type of codec we have.
- */
- static int
- __init get_codec_type(struct device_node *info)
- {
- /* already set if pre-davbus model and info will be NULL */
- int codec = awacs_revision ;
- if (info) {
- /* must do awacs first to allow screamer to overide it */
- if (device_is_compatible(info, "awacs"))
- codec = AWACS_AWACS ;
- if (device_is_compatible(info, "screamer"))
- codec = AWACS_SCREAMER;
- if (device_is_compatible(info, "burgundy"))
- codec = AWACS_BURGUNDY ;
- if (device_is_compatible(info, "daca"))
- codec = AWACS_DACA;
- if (device_is_compatible(info, "tumbler"))
- codec = AWACS_TUMBLER;
- }
- return codec ;
- }
- /* find out what type, if any, of expansion card we have
- */
- static void
- __init get_expansion_type(void)
- {
- if (find_devices("perch") != NULL)
- has_perch = 1;
- if (find_devices("pb-ziva-pc") != NULL)
- has_ziva = 1;
- /* need to work out how we deal with iMac SRS module */
- }
- /* set up frame rates.
- * I suspect that these routines don't quite go about it the right way:
- * - where there is more than one rate - I think that the first property
- * value is the number of rates.
- * TODO: check some more device trees and modify accordingly
- * Set dmasound.mach.max_dsp_rate on the basis of these routines.
- */
- static void
- __init init_awacs_frame_rates(unsigned int *prop, unsigned int l)
- {
- int i ;
- if (prop) {
- for (i=0; i<8; i++)
- awacs_freqs_ok[i] = 0 ;
- for (l /= sizeof(int); l > 0; --l) {
- unsigned int r = *prop++;
- /* Apple 'Fixed' format */
- if (r >= 0x10000)
- r >>= 16;
- for (i = 0; i < 8; ++i) {
- if (r == awacs_freqs[i]) {
- awacs_freqs_ok[i] = 1;
- break;
- }
- }
- }
- }
- /* else we assume that all the rates are available */
- }
- static void
- __init init_tumbler_frame_rates(unsigned int *prop, unsigned int l)
- {
- int i ;
- if (prop) {
- for (i=0; i<2; i++)
- tumbler_freqs_ok[i] = 0;
- for (l /= sizeof(int); l > 0; --l) {
- unsigned int r = *prop++;
- /* Apple 'Fixed' format */
- if (r >= 0x10000)
- r >>= 16;
- for (i = 0; i < 2; ++i) {
- if (r == tumbler_freqs[i]) {
- tumbler_freqs_ok[i] = 1;
- break;
- }
- }
- }
- }
- /* else we assume that all the rates are available */
- }
- static void
- __init init_burgundy_frame_rates(unsigned int *prop, unsigned int l)
- {
- int temp[9] ;
- int i = 0 ;
- if (prop) {
- for (l /= sizeof(int); l > 0; --l) {
- unsigned int r = *prop++;
- /* Apple 'Fixed' format */
- if (r >= 0x10000)
- r >>= 16;
- temp[i] = r ;
- i++ ; if(i>=9) i=8;
- }
- }
- #ifdef DEBUG_DMASOUND
- if (i > 1){
- int j;
- printk("dmasound_pmac: burgundy with multiple frame ratesn");
- for(j=0; j<i; j++)
- printk("%d ", temp[j]) ;
- printk("n") ;
- }
- #endif
- }
- static void
- __init init_daca_frame_rates(unsigned int *prop, unsigned int l)
- {
- int temp[9] ;
- int i = 0 ;
- if (prop) {
- for (l /= sizeof(int); l > 0; --l) {
- unsigned int r = *prop++;
- /* Apple 'Fixed' format */
- if (r >= 0x10000)
- r >>= 16;
- temp[i] = r ;
- i++ ; if(i>=9) i=8;
- }
- }
- #ifdef DEBUG_DMASOUND
- if (i > 1){
- int j;
- printk("dmasound_pmac: DACA with multiple frame ratesn");
- for(j=0; j<i; j++)
- printk("%d ", temp[j]) ;
- printk("n") ;
- }
- #endif
- }
- static void
- __init init_frame_rates(unsigned int *prop, unsigned int l)
- {
- switch (awacs_revision){
- case AWACS_TUMBLER:
- init_tumbler_frame_rates(prop, l);
- break ;
- case AWACS_DACA:
- init_daca_frame_rates(prop, l);
- break ;
- case AWACS_BURGUNDY:
- init_burgundy_frame_rates(prop, l);
- break ;
- default: /* ;-))) */
- init_awacs_frame_rates(prop, l);
- break ;
- }
- }
- /* find things/machines that can't do mac-io byteswap
- */
- static void
- __init set_hw_byteswap(struct device_node *io)
- {
- struct device_node *mio ;
- unsigned int *p, kl = 0 ;
- /* if seems that Keylargo can't byte-swap */
- for (mio = io->parent; mio ; mio = mio->parent) {
- if (strcmp(mio->name, "mac-io") == 0) {
- if (device_is_compatible(mio, "Keylargo"))
- kl = 1;
- break;
- }
- }
- hw_can_byteswap = !kl;
- }
- /* Allocate the resources necessary for beep generation. This cannot be (quite)
- done statically (yet) because we cannot do virt_to_bus() on static vars when
- the code is loaded as a module.
- for the sake of saving the possibility that two allocations will incur the
- overhead of two pull-ups in DBDMA_ALIGN() we allocate the 'emergency' dmdma
- command here as well... even tho' it is not part of the beep process.
- */
- int32_t
- __init setup_beep(void)
- {
- /* Initialize beep stuff */
- /* want one cmd buffer for beeps, and a second one for emergencies
- - i.e. dbdma error conditions.
- ask for three to allow for pull up in DBDMA_ALIGN().
- */
- beep_dbdma_cmd_space =
- kmalloc((2 + 1) * sizeof(struct dbdma_cmd), GFP_KERNEL);
- if(beep_dbdma_cmd_space == NULL) {
- printk(KERN_ERR "dmasound_pmac: no beep dbdma cmd spacen") ;
- return -ENOMEM ;
- }
- beep_dbdma_cmd = (volatile struct dbdma_cmd *)
- DBDMA_ALIGN(beep_dbdma_cmd_space);
- /* set up emergency dbdma cmd */
- emergency_dbdma_cmd = beep_dbdma_cmd+1 ;
- beep_buf = (short *) kmalloc(BEEP_BUFLEN * 4, GFP_KERNEL);
- if (beep_buf == NULL) {
- printk(KERN_ERR "dmasound_pmac: no memory for beep buffern");
- if( beep_dbdma_cmd_space ) kfree(beep_dbdma_cmd_space) ;
- return -ENOMEM ;
- }
- /* OK, we should be safe to claim the mksound vector now */
- orig_mksound = kd_mksound;
- kd_mksound = awacs_mksound;
- return 0 ;
- }
- int __init dmasound_awacs_init(void)
- {
- struct device_node *io = NULL, *info = NULL;
- int vol, res;
- if (_machine != _MACH_Pmac)
- return -ENODEV;
- awacs_subframe = 0;
- awacs_revision = 0;
- hw_can_byteswap = 1 ; /* most can */
- /* look for models we need to handle specially */
- set_model() ;
- /* find the OF node that tells us about the dbdma stuff
- */
- io = get_snd_io_node();
- if (io == NULL) {
- #ifdef DEBUG_DMASOUND
- printk("dmasound_pmac: couldn't find sound io OF noden");
- #endif
- return -ENODEV ;
- }
- /* find the OF node that tells us about the sound sub-system
- * this doesn't exist on pre-davbus machines (earlier than 9500)
- */
- if (awacs_revision != AWACS_AWACS) { /* set for pre-davbus */
- info = get_snd_info_node(io) ;
- if (info == NULL){
- #ifdef DEBUG_DMASOUND
- printk("dmasound_pmac: couldn't find 'sound' OF noden");
- #endif
- return -ENODEV ;
- }
- }
- awacs_revision = get_codec_type(info) ;
- if (awacs_revision == 0) {
- #ifdef DEBUG_DMASOUND
- printk("dmasound_pmac: couldn't find a Codec we can handlen");
- #endif
- return -ENODEV ; /* we don't know this type of h/w */
- }
- /* set up perch, ziva, SRS or whatever else we have as sound
- * expansion.
- */
- get_expansion_type();
- /* we've now got enough information to make up the audio topology.
- * we will map the sound part of mac-io now so that we can probe for
- * other info if necessary (early AWACS we want to read chip ids)
- */
- if (io->n_addrs < 3 || io->n_intrs < 3) {
- /* OK - maybe we need to use the 'awacs' node (on earlier
- * machines).
- */
- if (awacs_node) {
- io = awacs_node ;
- if (io->n_addrs < 3 || io->n_intrs < 3) {
- printk("dmasound_pmac: can't use %s"
- " (%d addrs, %d intrs)n",
- io->full_name, io->n_addrs, io->n_intrs);
- return -ENODEV;
- }
- } else {
- printk("dmasound_pmac: can't use %s (%d addrs, %d intrs)n",
- io->full_name, io->n_addrs, io->n_intrs);
- }
- }
- if (!request_OF_resource(io, 0, NULL)) {
- printk(KERN_ERR "dmasound: can't request IO resource !n");
- return -ENODEV;
- }
- if (!request_OF_resource(io, 1, " (tx dma)")) {
- release_OF_resource(io, 0);
- printk(KERN_ERR "dmasound: can't request TX DMA resource !n");
- return -ENODEV;
- }
- if (!request_OF_resource(io, 2, " (rx dma)")) {
- release_OF_resource(io, 0);
- release_OF_resource(io, 1);
- printk(KERN_ERR "dmasound: can't request RX DMA resource !n");
- return -ENODEV;
- }
- /* all OF versions I've seen use this value */
- awacs = (volatile struct awacs_regs *)
- ioremap(io->addrs[0].address, 0x1000);
- awacs_txdma = (volatile struct dbdma_regs *)
- ioremap(io->addrs[1].address, 0x100);
- awacs_rxdma = (volatile struct dbdma_regs *)
- ioremap(io->addrs[2].address, 0x100);
- #ifdef CONFIG_PMAC_PBOOK
- /* first of all make sure that the chip is powered up....*/
- pmac_call_feature(PMAC_FTR_SOUND_CHIP_ENABLE, io, 0, 1);
- if (awacs_revision == AWACS_SCREAMER)
- awacs_recalibrate();
- #endif
- awacs_irq = io->intrs[0].line;
- awacs_tx_irq = io->intrs[1].line;
- awacs_rx_irq = io->intrs[2].line;
- awacs_node = io;
- /* if we have an awacs or screamer - probe the chip to make
- * sure we have the right revision.
- */
- if (awacs_revision <= AWACS_SCREAMER){
- uint32_t temp, rev, mfg ;
- /* find out the awacs revision from the chip */
- temp = in_le32(&awacs->codec_stat);
- rev = (temp >> 12) & 0xf;
- mfg = (temp >> 8) & 0xf;
- #ifdef DEBUG_DMASOUND
- printk("dmasound_pmac: Awacs/Screamer Codec Mfct: %d Rev %dn", mfg, rev);
- #endif
- if (rev >= AWACS_SCREAMER)
- awacs_revision = AWACS_SCREAMER ;
- else
- awacs_revision = rev ;
- }
- dmasound.mach = machPMac;
- /* find out other bits & pieces from OF, these may be present
- only on some models ... so be careful.
- */
- /* in the absence of a frame rates property we will use the defaults
- */
- if (info) {
- unsigned int *prop, l;
- sound_device_id = 0;
- /* device ID appears post g3 b&w */
- prop = (unsigned int *)get_property(info, "device-id", 0);
- if (prop != 0)
- sound_device_id = *prop;
- /* look for a property saying what sample rates
- are available */
- prop = (unsigned int *)get_property(info, "sample-rates", &l);
- if (prop == 0)
- prop = (unsigned int *) get_property
- (info, "output-frame-rates", &l);
- /* if it's there use it to set up frame rates */
- init_frame_rates(prop, l) ;
- }
-
- out_le32(&awacs->control, 0x11); /* set everything quiesent */
- set_hw_byteswap(io) ; /* figure out if the h/w can do it */
- /* get default volume from nvram
- * vol = (~nvram_read_byte(0x1308) & 7) << 1;
- */
- vol = ((pmac_xpram_read( 8 ) & 7 ) << 1 );
- /* set up tracking values */
- spk_vol = vol * 100 ;
- spk_vol /= 7 ; /* get set value to a percentage */
- spk_vol |= (spk_vol << 8) ; /* equal left & right */
- line_vol = passthru_vol = spk_vol ;
- /* fill regs that are shared between AWACS & Burgundy */
- awacs_reg[2] = vol + (vol << 6);
- awacs_reg[4] = vol + (vol << 6);
- awacs_reg[5] = vol + (vol << 6); /* screamer has loopthru vol control */
- awacs_reg[6] = 0; /* maybe should be vol << 3 for PCMCIA speaker */
- awacs_reg[7] = 0;
- awacs_reg[0] = MASK_MUX_CD;
- awacs_reg[1] = MASK_LOOPTHRU;
- /* FIXME: Only machines with external SRS module need MASK_PAROUT */
- if (has_perch || sound_device_id == 0x5
- || /*sound_device_id == 0x8 ||*/ sound_device_id == 0xb)
- awacs_reg[1] |= MASK_PAROUT0 | MASK_PAROUT1;
- switch (awacs_revision) {
- case AWACS_TUMBLER:
- #ifdef CONFIG_KMOD
- request_module("i2c-keywest");
- #endif /* CONFIG_KMOD */
- awacs_tumbler_init();
- tas_init();
- break ;
- case AWACS_DACA:
- #ifdef CONFIG_KMOD
- request_module("i2c-keywest");
- #endif /* CONFIG_KMOD */
- daca_init();
- break ; /* dont know how yet */
- case AWACS_BURGUNDY:
- awacs_burgundy_init();
- break ;
- case AWACS_SCREAMER:
- case AWACS_AWACS:
- default:
- load_awacs() ;
- break ;
- }
- /* enable/set-up external modules - when we know how */
- if (has_perch)
- awacs_enable_amp(100 * 0x101);
- /* Reset dbdma channels */
- out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE|DEAD) << 16);
- while (in_le32(&awacs_txdma->status) & RUN)
- udelay(1);
- out_le32(&awacs_rxdma->control, (RUN|PAUSE|FLUSH|WAKE|DEAD) << 16);
- while (in_le32(&awacs_rxdma->status) & RUN)
- udelay(1);
- /* Initialize beep stuff */
- if ((res=setup_beep()))
- return res ;
- #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 (is_pbook_3X00) {
- /*
- * 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 (is_pbook_g3) {
- struct device_node* mio;
- macio_base = 0;
- for (mio = io->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.
- * The relevant bits for writing to this byte are 0x8f.
- * I haven't found out what the 0x80 bit does.
- * For the 0xf bits, writing 3 or 7 enables the CD
- * input, any other value disables it. Values
- * 1, 3, 5, 7 enable the microphone. Values 0, 2,
- * 4, 6, 8 - f enable the input from the modem.
- * -- paulus.
- */
- if (macio_base)
- out_8(macio_base + 0x37, 3);
- }
- if (hw_can_byteswap)
- dmasound.mach.hardware_afmts = (AFMT_S16_BE | AFMT_S16_LE) ;
- else
- dmasound.mach.hardware_afmts = AFMT_S16_BE ;
- /* shut out chips that do output only.
- may need to extend this to machines which have no inputs - even tho'
- they use screamer - IIRC one of the powerbooks is like this.
- */
- if (awacs_revision != AWACS_TUMBLER && awacs_revision != AWACS_DACA) {
- dmasound.mach.capabilities = DSP_CAP_DUPLEX ;
- dmasound.mach.record = PMacRecord ;
- }
- dmasound.mach.default_hard = def_hard ;
- dmasound.mach.default_soft = def_soft ;
- switch (awacs_revision) {
- case AWACS_BURGUNDY:
- sprintf(awacs_name, "PowerMac Burgundy ") ;
- break ;
- case AWACS_DACA:
- sprintf(awacs_name, "PowerMac DACA ") ;
- break ;
- case AWACS_TUMBLER:
- sprintf(awacs_name, "PowerMac Tumbler ") ;
- break ;
- case AWACS_SCREAMER:
- sprintf(awacs_name, "PowerMac Screamer ") ;
- break ;
- case AWACS_AWACS:
- default:
- sprintf(awacs_name, "PowerMac AWACS rev %d ", awacs_revision) ;
- break ;
- }
- return dmasound_init();
- }
- static void __exit dmasound_awacs_cleanup(void)
- {
- switch (awacs_revision) {
- case AWACS_TUMBLER:
- awacs_tumbler_cleanup();
- tas_cleanup();
- break ;
- case AWACS_DACA:
- daca_cleanup();
- break;
- }
- dmasound_deinit();
- }
- MODULE_DESCRIPTION("PowerMac built-in audio driver.");
- MODULE_LICENSE("GPL");
- module_init(dmasound_awacs_init);
- module_exit(dmasound_awacs_cleanup);