📄 amd7930.c
字号:
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_amd7930_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_amd7930_capture_ops); pcm->private_data = amd; pcm->info_flags = 0; strcpy(pcm->name, amd->card->shortname); amd->pcm = pcm; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, snd_dma_continuous_data(GFP_KERNEL), 64*1024, 64*1024); return 0;}#define VOLUME_MONITOR 0#define VOLUME_CAPTURE 1#define VOLUME_PLAYBACK 2static int snd_amd7930_info_volume(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *uinfo){ int type = kctl->private_value; snd_assert(type == VOLUME_MONITOR || type == VOLUME_CAPTURE || type == VOLUME_PLAYBACK, return -EINVAL); (void) type; uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = 255; return 0;}static int snd_amd7930_get_volume(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol){ struct snd_amd7930 *amd = snd_kcontrol_chip(kctl); int type = kctl->private_value; int *swval; snd_assert(type == VOLUME_MONITOR || type == VOLUME_CAPTURE || type == VOLUME_PLAYBACK, return -EINVAL); switch (type) { case VOLUME_MONITOR: swval = &amd->mgain; break; case VOLUME_CAPTURE: swval = &amd->rgain; break; case VOLUME_PLAYBACK: default: swval = &amd->pgain; break; }; ucontrol->value.integer.value[0] = *swval; return 0;}static int snd_amd7930_put_volume(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *ucontrol){ struct snd_amd7930 *amd = snd_kcontrol_chip(kctl); unsigned long flags; int type = kctl->private_value; int *swval, change; snd_assert(type == VOLUME_MONITOR || type == VOLUME_CAPTURE || type == VOLUME_PLAYBACK, return -EINVAL); switch (type) { case VOLUME_MONITOR: swval = &amd->mgain; break; case VOLUME_CAPTURE: swval = &amd->rgain; break; case VOLUME_PLAYBACK: default: swval = &amd->pgain; break; }; spin_lock_irqsave(&amd->lock, flags); if (*swval != ucontrol->value.integer.value[0]) { *swval = ucontrol->value.integer.value[0]; __amd7930_update_map(amd); change = 1; } else change = 0; spin_unlock_irqrestore(&amd->lock, flags); return change;}static struct snd_kcontrol_new amd7930_controls[] __devinitdata = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Monitor Volume", .index = 0, .info = snd_amd7930_info_volume, .get = snd_amd7930_get_volume, .put = snd_amd7930_put_volume, .private_value = VOLUME_MONITOR, }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Capture Volume", .index = 0, .info = snd_amd7930_info_volume, .get = snd_amd7930_get_volume, .put = snd_amd7930_put_volume, .private_value = VOLUME_CAPTURE, }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Playback Volume", .index = 0, .info = snd_amd7930_info_volume, .get = snd_amd7930_get_volume, .put = snd_amd7930_put_volume, .private_value = VOLUME_PLAYBACK, },};static int __devinit snd_amd7930_mixer(struct snd_amd7930 *amd){ struct snd_card *card; int idx, err; snd_assert(amd != NULL && amd->card != NULL, return -EINVAL); card = amd->card; strcpy(card->mixername, card->shortname); for (idx = 0; idx < ARRAY_SIZE(amd7930_controls); idx++) { if ((err = snd_ctl_add(card, snd_ctl_new1(&amd7930_controls[idx], amd))) < 0) return err; } return 0;}static int snd_amd7930_free(struct snd_amd7930 *amd){ amd7930_idle(amd); if (amd->irq) free_irq(amd->irq, amd); if (amd->regs) sbus_iounmap(amd->regs, amd->regs_size); kfree(amd); return 0;}static int snd_amd7930_dev_free(struct snd_device *device){ struct snd_amd7930 *amd = device->device_data; return snd_amd7930_free(amd);}static struct snd_device_ops snd_amd7930_dev_ops = { .dev_free = snd_amd7930_dev_free,};static int __devinit snd_amd7930_create(struct snd_card *card, struct resource *rp, unsigned int reg_size, int irq, int dev, struct snd_amd7930 **ramd){ unsigned long flags; struct snd_amd7930 *amd; int err; *ramd = NULL; amd = kzalloc(sizeof(*amd), GFP_KERNEL); if (amd == NULL) return -ENOMEM; spin_lock_init(&amd->lock); amd->card = card; amd->regs_size = reg_size; amd->regs = sbus_ioremap(rp, 0, amd->regs_size, "amd7930"); if (!amd->regs) { snd_printk("amd7930-%d: Unable to map chip registers.\n", dev); return -EIO; } amd7930_idle(amd); if (request_irq(irq, snd_amd7930_interrupt, IRQF_DISABLED | IRQF_SHARED, "amd7930", amd)) { snd_printk("amd7930-%d: Unable to grab IRQ %d\n", dev, irq); snd_amd7930_free(amd); return -EBUSY; } amd->irq = irq; amd7930_enable_ints(amd); spin_lock_irqsave(&amd->lock, flags); amd->rgain = 128; amd->pgain = 200; amd->mgain = 0; memset(&amd->map, 0, sizeof(amd->map)); amd->map.mmr1 = (AM_MAP_MMR1_GX | AM_MAP_MMR1_GER | AM_MAP_MMR1_GR | AM_MAP_MMR1_STG); amd->map.mmr2 = (AM_MAP_MMR2_LS | AM_MAP_MMR2_AINB); __amd7930_update_map(amd); /* Always MUX audio (Ba) to channel Bb. */ sbus_writeb(AMR_MUX_MCR1, amd->regs + AMD7930_CR); sbus_writeb(AM_MUX_CHANNEL_Ba | (AM_MUX_CHANNEL_Bb << 4), amd->regs + AMD7930_DR); spin_unlock_irqrestore(&amd->lock, flags); if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, amd, &snd_amd7930_dev_ops)) < 0) { snd_amd7930_free(amd); return err; } *ramd = amd; return 0;}static int __devinit amd7930_attach_common(struct resource *rp, int irq){ static int dev_num; struct snd_card *card; struct snd_amd7930 *amd; int err; if (dev_num >= SNDRV_CARDS) return -ENODEV; if (!enable[dev_num]) { dev_num++; return -ENOENT; } card = snd_card_new(index[dev_num], id[dev_num], THIS_MODULE, 0); if (card == NULL) return -ENOMEM; strcpy(card->driver, "AMD7930"); strcpy(card->shortname, "Sun AMD7930"); sprintf(card->longname, "%s at 0x%02lx:0x%08Lx, irq %d", card->shortname, rp->flags & 0xffL, (unsigned long long)rp->start, irq); if ((err = snd_amd7930_create(card, rp, (rp->end - rp->start) + 1, irq, dev_num, &amd)) < 0) goto out_err; if ((err = snd_amd7930_pcm(amd)) < 0) goto out_err; if ((err = snd_amd7930_mixer(amd)) < 0) goto out_err; if ((err = snd_card_register(card)) < 0) goto out_err; amd->next = amd7930_list; amd7930_list = amd; dev_num++; return 0;out_err: snd_card_free(card); return err;}static int __devinit amd7930_obio_attach(struct device_node *dp){ const struct linux_prom_registers *regs; const struct linux_prom_irqs *irqp; struct resource res, *rp; int len; irqp = of_get_property(dp, "intr", &len); if (!irqp) { snd_printk("%s: Firmware node lacks IRQ property.\n", dp->full_name); return -ENODEV; } regs = of_get_property(dp, "reg", &len); if (!regs) { snd_printk("%s: Firmware node lacks register property.\n", dp->full_name); return -ENODEV; } rp = &res; rp->start = regs->phys_addr; rp->end = rp->start + regs->reg_size - 1; rp->flags = IORESOURCE_IO | (regs->which_io & 0xff); return amd7930_attach_common(rp, irqp->pri);}static int __devinit amd7930_sbus_probe(struct of_device *dev, const struct of_device_id *match){ struct sbus_dev *sdev = to_sbus_device(&dev->dev); return amd7930_attach_common(&sdev->resource[0], sdev->irqs[0]);}static struct of_device_id amd7930_match[] = { { .name = "audio", }, {},};static struct of_platform_driver amd7930_sbus_driver = { .name = "audio", .match_table = amd7930_match, .probe = amd7930_sbus_probe,};static int __init amd7930_init(void){ struct device_node *dp; /* Try to find the sun4c "audio" node first. */ dp = of_find_node_by_path("/"); dp = dp->child; while (dp) { if (!strcmp(dp->name, "audio")) amd7930_obio_attach(dp); dp = dp->sibling; } /* Probe each SBUS for amd7930 chips. */ return of_register_driver(&amd7930_sbus_driver, &sbus_bus_type);}static void __exit amd7930_exit(void){ struct snd_amd7930 *p = amd7930_list; while (p != NULL) { struct snd_amd7930 *next = p->next; snd_card_free(p->card); p = next; } amd7930_list = NULL; of_unregister_driver(&amd7930_sbus_driver);}module_init(amd7930_init);module_exit(amd7930_exit);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -