📄 dmasound_awacs.c
字号:
awacs_reg[1] |= MASK_LOOPTHRU; awacs_write(awacs_reg[0] | MASK_ADDR0); awacs_write(awacs_reg[1] | MASK_ADDR1); return IOCTL_OUT(arg, data); case SOUND_MIXER_READ_STEREODEVS: data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER | SOUND_MASK_RECLEV; return IOCTL_OUT(arg, data); case SOUND_MIXER_READ_CAPS: return IOCTL_OUT(arg, 0); case SOUND_MIXER_READ_VOLUME: data = (awacs_reg[1] & MASK_AMUTE)? 0: awacs_get_volume(awacs_reg[2], 6); return IOCTL_OUT(arg, data); case SOUND_MIXER_WRITE_VOLUME: IOCTL_IN(arg, data); return IOCTL_OUT(arg, PMacSetVolume(data)); case SOUND_MIXER_READ_SPEAKER: if (awacs_revision == 3 && sys_ctrler == SYS_CTRLER_CUDA) data = awacs_spkr_vol; else data = (awacs_reg[1] & MASK_CMUTE)? 0: awacs_get_volume(awacs_reg[4], 6); return IOCTL_OUT(arg, data); case SOUND_MIXER_WRITE_SPEAKER: IOCTL_IN(arg, data); if (awacs_revision == 3 && sys_ctrler == SYS_CTRLER_CUDA) awacs_enable_amp(data); else data = awacs_volume_setter(data, 4, MASK_CMUTE, 6); return IOCTL_OUT(arg, data); case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */ IOCTL_IN(arg, data); beep_volume = data & 0xff; /* fall through */ case SOUND_MIXER_READ_ALTPCM: return IOCTL_OUT(arg, beep_volume); case SOUND_MIXER_WRITE_LINE: IOCTL_IN(arg, data); awacs_reg[0] &= ~MASK_MUX_AUDIN; if ((data & 0xff) >= 50) awacs_reg[0] |= MASK_MUX_AUDIN; awacs_write(MASK_ADDR0 | awacs_reg[0]); /* fall through */ case SOUND_MIXER_READ_LINE: data = (awacs_reg[0] & MASK_MUX_AUDIN)? 100: 0; return IOCTL_OUT(arg, data); case SOUND_MIXER_WRITE_MIC: IOCTL_IN(arg, data); data &= 0xff; awacs_reg[0] &= ~(MASK_MUX_MIC | MASK_GAINLINE); if (data >= 25) { awacs_reg[0] |= MASK_MUX_MIC; if (data >= 75) awacs_reg[0] |= MASK_GAINLINE; } awacs_write(MASK_ADDR0 | awacs_reg[0]); /* fall through */ case SOUND_MIXER_READ_MIC: data = (awacs_reg[0] & MASK_MUX_MIC)? (awacs_reg[0] & MASK_GAINLINE? 100: 50): 0; return IOCTL_OUT(arg, data); case SOUND_MIXER_WRITE_CD: IOCTL_IN(arg, data); awacs_reg[0] &= ~MASK_MUX_CD; if ((data & 0xff) >= 50) awacs_reg[0] |= MASK_MUX_CD; awacs_write(MASK_ADDR0 | awacs_reg[0]); /* fall through */ case SOUND_MIXER_READ_CD: data = (awacs_reg[0] & MASK_MUX_CD)? 100: 0; return IOCTL_OUT(arg, data); case SOUND_MIXER_WRITE_RECLEV: IOCTL_IN(arg, data); data = awacs_volume_setter(data, 0, 0, 4); return IOCTL_OUT(arg, data); case SOUND_MIXER_READ_RECLEV: data = awacs_get_volume(awacs_reg[0], 4); return IOCTL_OUT(arg, data); case MIXER_WRITE(SOUND_MIXER_MONITOR): IOCTL_IN(arg, data); awacs_reg[1] &= ~MASK_LOOPTHRU; if ((data & 0xff) >= 50) awacs_reg[1] |= MASK_LOOPTHRU; awacs_write(MASK_ADDR1 | awacs_reg[1]); /* fall through */ case MIXER_READ(SOUND_MIXER_MONITOR): data = (awacs_reg[1] & MASK_LOOPTHRU)? 100: 0; return IOCTL_OUT(arg, data); } return -EINVAL;}static int burgundy_mixer_ioctl(u_int cmd, u_long arg){ int data; /* We are, we are, we are... Burgundy or better */ switch(cmd) { case SOUND_MIXER_READ_DEVMASK: data = SOUND_MASK_VOLUME | SOUND_MASK_CD | SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_ALTPCM; return IOCTL_OUT(arg, data); case SOUND_MIXER_READ_RECMASK: data = SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD; return IOCTL_OUT(arg, data); case SOUND_MIXER_READ_RECSRC: data = 0; if (awacs_reg[0] & MASK_MUX_AUDIN) data |= SOUND_MASK_LINE; if (awacs_reg[0] & MASK_MUX_MIC) data |= SOUND_MASK_MIC; if (awacs_reg[0] & MASK_MUX_CD) data |= SOUND_MASK_CD; return IOCTL_OUT(arg, data); case SOUND_MIXER_WRITE_RECSRC: IOCTL_IN(arg, data); data &= (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD); awacs_reg[0] &= ~(MASK_MUX_CD | MASK_MUX_MIC | MASK_MUX_AUDIN); if (data & SOUND_MASK_LINE) awacs_reg[0] |= MASK_MUX_AUDIN; if (data & SOUND_MASK_MIC) awacs_reg[0] |= MASK_MUX_MIC; if (data & SOUND_MASK_CD) awacs_reg[0] |= MASK_MUX_CD; awacs_write(awacs_reg[0] | MASK_ADDR0); return IOCTL_OUT(arg, data); case SOUND_MIXER_READ_STEREODEVS: data = SOUND_MASK_VOLUME | SOUND_MASK_SPEAKER | SOUND_MASK_RECLEV | SOUND_MASK_CD | SOUND_MASK_LINE; return IOCTL_OUT(arg, data); case SOUND_MIXER_READ_CAPS: return IOCTL_OUT(arg, 0); case SOUND_MIXER_WRITE_VOLUME: IOCTL_IN(arg, data); awacs_burgundy_write_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME, data); /* Fall through */ case SOUND_MIXER_READ_VOLUME: return IOCTL_OUT(arg, awacs_burgundy_read_mvolume(MASK_ADDR_BURGUNDY_MASTER_VOLUME)); case SOUND_MIXER_WRITE_SPEAKER: IOCTL_IN(arg, data); if (!(data & 0xff)) { /* Mute the left speaker */ awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) & ~0x2); } else { /* Unmute the left speaker */ awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) | 0x2); } if (!(data & 0xff00)) { /* Mute the right speaker */ awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) & ~0x4); } else { /* Unmute the right speaker */ awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES, awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES) | 0x4); } data = (((data&0xff)*16)/100 > 0xf ? 0xf : (((data&0xff)*16)/100)) + ((((data>>8)*16)/100 > 0xf ? 0xf : ((((data>>8)*16)/100)))<<4); awacs_burgundy_wcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER, ~data); /* Fall through */ case SOUND_MIXER_READ_SPEAKER: data = awacs_burgundy_rcb(MASK_ADDR_BURGUNDY_ATTENSPEAKER); data = (((data & 0xf)*100)/16) + ((((data>>4)*100)/16)<<8); return IOCTL_OUT(arg, ~data); case SOUND_MIXER_WRITE_ALTPCM: /* really bell volume */ IOCTL_IN(arg, data); beep_volume = data & 0xff; /* fall through */ case SOUND_MIXER_READ_ALTPCM: return IOCTL_OUT(arg, beep_volume); case SOUND_MIXER_WRITE_LINE: IOCTL_IN(arg, data); awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLLINE, data); /* fall through */ case SOUND_MIXER_READ_LINE: data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLLINE); return IOCTL_OUT(arg, data); case SOUND_MIXER_WRITE_MIC: IOCTL_IN(arg, data); /* Mic is mono device */ data = (data << 8) + (data << 24); awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLMIC, data); /* fall through */ case SOUND_MIXER_READ_MIC: data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLMIC); data <<= 24; return IOCTL_OUT(arg, data); case SOUND_MIXER_WRITE_CD: IOCTL_IN(arg, data); awacs_burgundy_write_volume(MASK_ADDR_BURGUNDY_VOLCD, data); /* fall through */ case SOUND_MIXER_READ_CD: data = awacs_burgundy_read_volume(MASK_ADDR_BURGUNDY_VOLCD); return IOCTL_OUT(arg, data); case SOUND_MIXER_WRITE_RECLEV: IOCTL_IN(arg, data); data = awacs_volume_setter(data, 0, 0, 4); return IOCTL_OUT(arg, data); case SOUND_MIXER_READ_RECLEV: data = awacs_get_volume(awacs_reg[0], 4); return IOCTL_OUT(arg, data); case SOUND_MIXER_OUTMASK: break; case SOUND_MIXER_OUTSRC: break; } return -EINVAL;}static int PMacMixerIoctl(u_int cmd, u_long arg){ /* Different IOCTLS for burgundy*/ if (awacs_revision >= AWACS_BURGUNDY) return burgundy_mixer_ioctl(cmd, arg); return awacs_mixer_ioctl(cmd, arg);}static void PMacWriteSqSetup(void){ int i; volatile struct dbdma_cmd *cp; cp = awacs_tx_cmds; memset((void *)cp, 0, (write_sq.numBufs+1) * sizeof(struct dbdma_cmd)); for (i = 0; i < write_sq.numBufs; ++i, ++cp) { st_le32(&cp->phy_addr, virt_to_bus(write_sq.buffers[i])); } st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS); st_le32(&cp->cmd_dep, virt_to_bus(awacs_tx_cmds)); out_le32(&awacs_txdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); out_le32(&awacs_txdma->cmdptr, virt_to_bus(awacs_tx_cmds));}static void PMacReadSqSetup(void){ int i; volatile struct dbdma_cmd *cp; cp = awacs_rx_cmds; memset((void *)cp, 0, (read_sq.numBufs+1) * sizeof(struct dbdma_cmd)); /* Set dma buffers up in a loop */ for (i = 0; i < read_sq.numBufs; i++,cp++) { st_le32(&cp->phy_addr, virt_to_bus(read_sq.buffers[i])); st_le16(&cp->command, INPUT_MORE + INTR_ALWAYS); st_le16(&cp->req_count, read_sq.block_size); st_le16(&cp->xfer_status, 0); } /* The next two lines make the thing loop around. */ st_le16(&cp->command, DBDMA_NOP + BR_ALWAYS); st_le32(&cp->cmd_dep, virt_to_bus(awacs_rx_cmds)); /* Don't start until the first read is done. * This will also abort any operations in progress if the DMA * happens to be running (and it shouldn't). */ out_le32(&awacs_rxdma->control, (RUN|PAUSE|FLUSH|WAKE) << 16); out_le32(&awacs_rxdma->cmdptr, virt_to_bus(awacs_rx_cmds));}static void PMacAbortRead(void){ int i; volatile struct dbdma_cmd *cp; cp = awacs_rx_cmds; for (i = 0; i < read_sq.numBufs; i++,cp++) st_le16(&cp->command, DBDMA_STOP); /* * We should probably wait for the thing to stop before we * release the memory */}/*** Machine definitions *****************************************************/static MACHINE machPMac = { name: awacs_name, name2: "AWACS", open: PMacOpen, release: PMacRelease, dma_alloc: PMacAlloc, dma_free: PMacFree, irqinit: PMacIrqInit,#ifdef MODULE irqcleanup: PMacIrqCleanup,#endif /* MODULE */ init: PMacInit, silence: PMacSilence, setFormat: PMacSetFormat, setVolume: PMacSetVolume, play: PMacPlay, record: PMacRecord, mixer_ioctl: PMacMixerIoctl, write_sq_setup: PMacWriteSqSetup, read_sq_setup: PMacReadSqSetup, abort_read: PMacAbortRead, min_dsp_speed: 8000};/*** Config & Setup **********************************************************/int __init dmasound_awacs_init(void){ struct device_node *np; if (_machine != _MACH_Pmac) return -ENODEV; awacs_subframe = 0; awacs_revision = 0; np = find_devices("awacs"); if (np == 0) { /* * powermac G3 models have a node called "davbus" * with a child called "sound". */ struct device_node *sound; np = find_devices("davbus"); sound = find_devices("sound"); if (sound != 0 && sound->parent == np) { unsigned int *prop, l, i; prop = (unsigned int *) get_property(sound, "sub-frame", 0); if (prop != 0 && *prop >= 0 && *prop < 16) awacs_subframe = *prop; if (device_is_compatible(sound, "burgundy")) awacs_revision = AWACS_BURGUNDY; /* This should be verified on older screamers */ if (device_is_compatible(sound, "screamer")) awacs_is_screamer = 1; prop = (unsigned int *)get_property(sound, "device-id", 0); if (prop != 0) awacs_device_id = *prop; awacs_has_iic = (find_devices("perch") != NULL); /* look for a property saying what sample rates are available */ for (i = 0; i < 8; ++i) awacs_freqs_ok[i] = 0; prop = (unsigned int *) get_property (sound, "sample-rates", &l); if (prop == 0) prop = (unsigned int *) get_property (sound, "output-frame-rates", &l); if (prop != 0) { for (l /= sizeof(int); l > 0; --l) { /* sometimes the rate is in the high-order 16 bits (?) */ unsigned int r = *prop++; if (r >= 0x10000) r >>= 16; for (i = 0; i < 8; ++i) { if (r == awacs_freqs[i]) { awacs_freqs_ok[i] = 1; break; } } } } else { /* assume just 44.1k is OK */ awacs_freqs_ok[0] = 1; } } } if (np != NULL && np->n_addrs >= 3 && np->n_intrs >= 3) { int vol; dmasound.mach = machPMac; awacs = (volatile struct awacs_regs *) ioremap(np->addrs[0].address, 0x80); awacs_txdma = (volatile struct dbdma_regs *) ioremap(np->addrs[1].address, 0x100); awacs_rxdma = (volatile struct dbdma_regs *) ioremap(np->addrs[2].address, 0x100); awacs_irq = np->intrs[0].line; awacs_tx_irq = np->intrs[1].line; awacs_rx_irq = np->intrs[2].line; awacs_tx_cmd_space = kmalloc((write_sq.numBufs + 4) * sizeof(struct dbdma_cmd), GFP_KERNEL); if (awacs_tx_cmd_space == NULL) { printk(KERN_ERR "DMA sound driver: Not enough buffer memory, driver disabled!\n"); return -ENOMEM; } awacs_node = np;#ifdef CONFIG_PMAC_PBOOK if (machine_is_compatible("PowerBook1,1") || machine_is_compatible("AAPL,PowerBook1998")) { pmu_suspend(); feature_set(np, FEATURE_Sound_CLK_enable); feature_set(np, FEATURE_Sound_power); /* Shorter delay will not work */ mdelay(1000); pmu_resume(); }#endif awacs_tx_cmds = (volatile struct dbdma_cmd *) DBDMA_ALIGN(awacs_tx_cmd_space); awacs_rx_cmd_space = kmalloc((read_sq.numBufs + 4) * sizeof(struct dbdma_cmd), GFP_KERNEL); if (awacs_rx_cmd_space == NULL) { printk("DMA sound driver: No memory for input"); } awacs_rx_cmds = (volatile struct dbdma_cmd *) DBDMA_ALIGN(awacs_rx_cmd_space); awacs_reg[0] = MASK_MUX_CD; /* FIXME: Only machines with external SRS module need MASK_PAROUT */ awacs_reg[1] = MASK_LOOPTHRU; if (awacs_has_iic || awacs_device_id == 0x5 || /*awacs_device_id == 0x8 || */awacs_device_id == 0xb) awacs_reg[1] |= MASK_PAROUT; /* get default volume from nvram */ vol = (~nvram_read_byte(0x1308) & 7) << 1; awacs_reg[2] = vol + (vol << 6); awacs_reg[4] = vol + (vol << 6); awacs_reg[5] = 0; awacs_reg[6] = 0; awacs_reg[7] = 0; out_le32(&awacs->control, 0x11); awacs_write(awacs_reg[0] + MASK_ADDR0); awacs_write(awacs_reg[1] + MASK_ADDR1); awacs_write(awacs_reg[2] + MASK_ADDR2); awacs_write(awacs_reg[4] + MASK_ADDR4); if (awacs_is_screamer) { awacs_write(awacs_reg[5] + MASK_ADDR5); awacs_write(awacs_reg[6] + MASK_ADDR6); awacs_write(awacs_reg[7] + MASK_ADDR7); } /* Initialize recent versions of the awacs */ if (awacs_revision == 0) { awacs_revision = (in_le32(&awacs->codec_stat) >> 12) & 0xf; if (awacs_revision == 3) { mdelay(100); awacs_write(0x6000); mdelay(2); awacs_write(awacs_reg[1] + MASK_ADDR1); awacs_enable_amp(100 * 0x101); } } if (awacs_revision >= AWACS_BURGUNDY) awacs_burgundy_init(); /* Initialize beep stuff */ beep_dbdma_cmd = awacs_tx_cmds + (write_sq.numBufs + 1); orig_mksound = kd_mksound; kd_mksound = awacs_mksound; beep_buf = (short *) kmalloc(BEEP_BUFLEN * 4, GFP_KERNEL); if (beep_buf == NULL) printk(KERN_WARNING "dmasound: no memory for " "beep buffer\n");#ifdef CONFIG_PMAC_PBOOK pmu_register_sleep_notifier(&awacs_sleep_notifier);#endif /* CONFIG_PMAC_PBOOK */ /* Powerbooks have odd ways of enabling inputs such as an expansion-bay CD or sound from an internal modem or a PC-card modem. */ if (machine_is_compatible("AAPL,3400/2400") || machine_is_compatible("AAPL,3500")) { is_pbook_3400 = 1; /* * Enable CD and PC-card sound inputs. * This is done by reading from address * f301a000, + 0x10 to enable the expansion-bay * CD sound input, + 0x80 to enable the PC-card * sound input. The 0x100 enables the SCSI bus * terminator power. */ latch_base = (unsigned char *) ioremap (0xf301a000, 0x1000); in_8(latch_base + 0x190); } else if (machine_is_compatible("PowerBook1,1") || machine_is_compatible("AAPL,PowerBook1998")) { struct device_node* mio; macio_base = 0; is_pbook_G3 = 1; for (mio = np->parent; mio; mio = mio->parent) { if (strcmp(mio->name, "mac-io") == 0 && mio->n_addrs > 0) { macio_base = (unsigned char *) ioremap (mio->addrs[0].address, 0x40); break; } } /* * Enable CD sound input. * 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. */ if (macio_base) out_8(macio_base + 0x37, 3); } sprintf(awacs_name, "PowerMac (AWACS rev %d) ", awacs_revision); return dmasound_init(); } return -ENODEV;}static void __exit dmasound_awacs_cleanup(void){ dmasound_deinit();}module_init(dmasound_awacs_init);module_exit(dmasound_awacs_cleanup);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -