📄 pmac.c
字号:
SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_RESUME), .formats = SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_8000_44100, .rate_min = 7350, .rate_max = 44100, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = 131072, .period_bytes_min = 256, .period_bytes_max = 16384, .periods_min = 3, .periods_max = PMAC_MAX_FRAGS,};#if 0 // NYIstatic int snd_pmac_hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule){ struct snd_pmac *chip = rule->private; struct pmac_stream *rec = snd_pmac_get_stream(chip, rule->deps[0]); int i, freq_table[8], num_freqs; if (! rec) return -EINVAL; num_freqs = 0; for (i = chip->num_freqs - 1; i >= 0; i--) { if (rec->cur_freqs & (1 << i)) freq_table[num_freqs++] = chip->freq_table[i]; } return snd_interval_list(hw_param_interval(params, rule->var), num_freqs, freq_table, 0);}static int snd_pmac_hw_rule_format(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule){ struct snd_pmac *chip = rule->private; struct pmac_stream *rec = snd_pmac_get_stream(chip, rule->deps[0]); if (! rec) return -EINVAL; return snd_mask_refine_set(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), rec->cur_formats);}#endif // NYIstatic int snd_pmac_pcm_open(struct snd_pmac *chip, struct pmac_stream *rec, struct snd_pcm_substream *subs){ struct snd_pcm_runtime *runtime = subs->runtime; int i; /* look up frequency table and fill bit mask */ runtime->hw.rates = 0; for (i = 0; i < chip->num_freqs; i++) if (chip->freqs_ok & (1 << i)) runtime->hw.rates |= snd_pcm_rate_to_rate_bit(chip->freq_table[i]); /* check for minimum and maximum rates */ for (i = 0; i < chip->num_freqs; i++) { if (chip->freqs_ok & (1 << i)) { runtime->hw.rate_max = chip->freq_table[i]; break; } } for (i = chip->num_freqs - 1; i >= 0; i--) { if (chip->freqs_ok & (1 << i)) { runtime->hw.rate_min = chip->freq_table[i]; break; } } runtime->hw.formats = chip->formats_ok; if (chip->can_capture) { if (! chip->can_duplex) runtime->hw.info |= SNDRV_PCM_INFO_HALF_DUPLEX; runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX; } runtime->private_data = rec; rec->substream = subs;#if 0 /* FIXME: still under development.. */ snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, snd_pmac_hw_rule_rate, chip, rec->stream, -1); snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, snd_pmac_hw_rule_format, chip, rec->stream, -1);#endif runtime->hw.periods_max = rec->cmd.size - 1; /* constraints to fix choppy sound */ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); return 0;}static int snd_pmac_pcm_close(struct snd_pmac *chip, struct pmac_stream *rec, struct snd_pcm_substream *subs){ struct pmac_stream *astr; snd_pmac_dma_stop(rec); astr = snd_pmac_get_stream(chip, another_stream(rec->stream)); if (! astr) return -EINVAL; /* reset constraints */ astr->cur_freqs = chip->freqs_ok; astr->cur_formats = chip->formats_ok; return 0;}static int snd_pmac_playback_open(struct snd_pcm_substream *subs){ struct snd_pmac *chip = snd_pcm_substream_chip(subs); subs->runtime->hw = snd_pmac_playback; return snd_pmac_pcm_open(chip, &chip->playback, subs);}static int snd_pmac_capture_open(struct snd_pcm_substream *subs){ struct snd_pmac *chip = snd_pcm_substream_chip(subs); subs->runtime->hw = snd_pmac_capture; return snd_pmac_pcm_open(chip, &chip->capture, subs);}static int snd_pmac_playback_close(struct snd_pcm_substream *subs){ struct snd_pmac *chip = snd_pcm_substream_chip(subs); return snd_pmac_pcm_close(chip, &chip->playback, subs);}static int snd_pmac_capture_close(struct snd_pcm_substream *subs){ struct snd_pmac *chip = snd_pcm_substream_chip(subs); return snd_pmac_pcm_close(chip, &chip->capture, subs);}/* */static struct snd_pcm_ops snd_pmac_playback_ops = { .open = snd_pmac_playback_open, .close = snd_pmac_playback_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_pmac_pcm_hw_params, .hw_free = snd_pmac_pcm_hw_free, .prepare = snd_pmac_playback_prepare, .trigger = snd_pmac_playback_trigger, .pointer = snd_pmac_playback_pointer,};static struct snd_pcm_ops snd_pmac_capture_ops = { .open = snd_pmac_capture_open, .close = snd_pmac_capture_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_pmac_pcm_hw_params, .hw_free = snd_pmac_pcm_hw_free, .prepare = snd_pmac_capture_prepare, .trigger = snd_pmac_capture_trigger, .pointer = snd_pmac_capture_pointer,};int __init snd_pmac_pcm_new(struct snd_pmac *chip){ struct snd_pcm *pcm; int err; int num_captures = 1; if (! chip->can_capture) num_captures = 0; err = snd_pcm_new(chip->card, chip->card->driver, 0, 1, num_captures, &pcm); if (err < 0) return err; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_pmac_playback_ops); if (chip->can_capture) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_pmac_capture_ops); pcm->private_data = chip; pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; strcpy(pcm->name, chip->card->shortname); chip->pcm = pcm; chip->formats_ok = SNDRV_PCM_FMTBIT_S16_BE; if (chip->can_byte_swap) chip->formats_ok |= SNDRV_PCM_FMTBIT_S16_LE; chip->playback.cur_formats = chip->formats_ok; chip->capture.cur_formats = chip->formats_ok; chip->playback.cur_freqs = chip->freqs_ok; chip->capture.cur_freqs = chip->freqs_ok; /* preallocate 64k buffer */ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, &chip->pdev->dev, 64 * 1024, 64 * 1024); return 0;}static void snd_pmac_dbdma_reset(struct snd_pmac *chip){ out_le32(&chip->playback.dma->control, (RUN|PAUSE|FLUSH|WAKE|DEAD) << 16); snd_pmac_wait_ack(&chip->playback); out_le32(&chip->capture.dma->control, (RUN|PAUSE|FLUSH|WAKE|DEAD) << 16); snd_pmac_wait_ack(&chip->capture);}/* * handling beep */void snd_pmac_beep_dma_start(struct snd_pmac *chip, int bytes, unsigned long addr, int speed){ struct pmac_stream *rec = &chip->playback; snd_pmac_dma_stop(rec); st_le16(&chip->extra_dma.cmds->req_count, bytes); st_le16(&chip->extra_dma.cmds->xfer_status, 0); st_le32(&chip->extra_dma.cmds->cmd_dep, chip->extra_dma.addr); st_le32(&chip->extra_dma.cmds->phy_addr, addr); st_le16(&chip->extra_dma.cmds->command, OUTPUT_MORE + BR_ALWAYS); out_le32(&chip->awacs->control, (in_le32(&chip->awacs->control) & ~0x1f00) | (speed << 8)); out_le32(&chip->awacs->byteswap, 0); snd_pmac_dma_set_command(rec, &chip->extra_dma); snd_pmac_dma_run(rec, RUN);}void snd_pmac_beep_dma_stop(struct snd_pmac *chip){ snd_pmac_dma_stop(&chip->playback); st_le16(&chip->extra_dma.cmds->command, DBDMA_STOP); snd_pmac_pcm_set_format(chip); /* reset format */}/* * interrupt handlers */static irqreturn_tsnd_pmac_tx_intr(int irq, void *devid){ struct snd_pmac *chip = devid; snd_pmac_pcm_update(chip, &chip->playback); return IRQ_HANDLED;}static irqreturn_tsnd_pmac_rx_intr(int irq, void *devid){ struct snd_pmac *chip = devid; snd_pmac_pcm_update(chip, &chip->capture); return IRQ_HANDLED;}static irqreturn_tsnd_pmac_ctrl_intr(int irq, void *devid){ struct snd_pmac *chip = devid; int ctrl = in_le32(&chip->awacs->control); /*printk("pmac: control interrupt.. 0x%x\n", ctrl);*/ if (ctrl & MASK_PORTCHG) { /* do something when headphone is plugged/unplugged? */ if (chip->update_automute) chip->update_automute(chip, 1); } if (ctrl & MASK_CNTLERR) { int err = (in_le32(&chip->awacs->codec_stat) & MASK_ERRCODE) >> 16; if (err && chip->model <= PMAC_SCREAMER) snd_printk(KERN_DEBUG "error %x\n", err); } /* Writing 1s to the CNTLERR and PORTCHG bits clears them... */ out_le32(&chip->awacs->control, ctrl); return IRQ_HANDLED;}/* * a wrapper to feature call for compatibility */static void snd_pmac_sound_feature(struct snd_pmac *chip, int enable){ if (ppc_md.feature_call) ppc_md.feature_call(PMAC_FTR_SOUND_CHIP_ENABLE, chip->node, 0, enable);}/* * release resources */static int snd_pmac_free(struct snd_pmac *chip){ /* stop sounds */ if (chip->initialized) { snd_pmac_dbdma_reset(chip); /* disable interrupts from awacs interface */ out_le32(&chip->awacs->control, in_le32(&chip->awacs->control) & 0xfff); } if (chip->node) snd_pmac_sound_feature(chip, 0); /* clean up mixer if any */ if (chip->mixer_free) chip->mixer_free(chip); snd_pmac_detach_beep(chip); /* release resources */ if (chip->irq >= 0) free_irq(chip->irq, (void*)chip); if (chip->tx_irq >= 0) free_irq(chip->tx_irq, (void*)chip); if (chip->rx_irq >= 0) free_irq(chip->rx_irq, (void*)chip); snd_pmac_dbdma_free(chip, &chip->playback.cmd); snd_pmac_dbdma_free(chip, &chip->capture.cmd); snd_pmac_dbdma_free(chip, &chip->extra_dma); if (chip->macio_base) iounmap(chip->macio_base); if (chip->latch_base) iounmap(chip->latch_base); if (chip->awacs) iounmap(chip->awacs); if (chip->playback.dma) iounmap(chip->playback.dma); if (chip->capture.dma) iounmap(chip->capture.dma); if (chip->node) { int i; for (i = 0; i < 3; i++) { if (chip->requested & (1 << i)) release_mem_region(chip->rsrc[i].start, chip->rsrc[i].end - chip->rsrc[i].start + 1); } } if (chip->pdev) pci_dev_put(chip->pdev); of_node_put(chip->node); kfree(chip); return 0;}/* * free the device */static int snd_pmac_dev_free(struct snd_device *device){ struct snd_pmac *chip = device->device_data; return snd_pmac_free(chip);}/* * check the machine support byteswap (little-endian) */static void __init detect_byte_swap(struct snd_pmac *chip){ struct device_node *mio; /* if seems that Keylargo can't byte-swap */ for (mio = chip->node->parent; mio; mio = mio->parent) { if (strcmp(mio->name, "mac-io") == 0) { if (of_device_is_compatible(mio, "Keylargo")) chip->can_byte_swap = 0; break; } } /* it seems the Pismo & iBook can't byte-swap in hardware. */ if (machine_is_compatible("PowerBook3,1") || machine_is_compatible("PowerBook2,1")) chip->can_byte_swap = 0 ; if (machine_is_compatible("PowerBook2,1")) chip->can_duplex = 0;}/* * detect a sound chip */static int __init snd_pmac_detect(struct snd_pmac *chip){ struct device_node *sound; struct device_node *dn; const unsigned int *prop; unsigned int l; struct macio_chip* macio; if (!machine_is(powermac)) return -ENODEV; chip->subframe = 0; chip->revision = 0; chip->freqs_ok = 0xff; /* all ok */ chip->model = PMAC_AWACS; chip->can_byte_swap = 1; chip->can_duplex = 1; chip->can_capture = 1; chip->num_freqs = ARRAY_SIZE(awacs_freqs); chip->freq_table = awacs_freqs; chip->pdev = NULL; chip->control_mask = MASK_IEPC | MASK_IEE | 0x11; /* default */ /* check machine type */ if (machine_is_compatible("AAPL,3400/2400") || machine_is_compatible("AAPL,3500")) chip->is_pbook_3400 = 1; else if (machine_is_compatible("PowerBook1,1") || machine_is_compatible("AAPL,PowerBook1998")) chip->is_pbook_G3 = 1; chip->node = of_find_node_by_name(NULL, "awacs"); sound = of_node_get(chip->node); /* * powermac G3 models have a node called "davbus" * with a child called "sound".
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -