📄 harmony.c
字号:
return bytes_to_frames(rt, caught);}static int snd_harmony_playback_open(struct snd_pcm_substream *ss){ struct snd_harmony *h = snd_pcm_substream_chip(ss); struct snd_pcm_runtime *rt = ss->runtime; int err; h->psubs = ss; rt->hw = snd_harmony_playback; snd_pcm_hw_constraint_list(rt, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraint_rates); err = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS); if (err < 0) return err; return 0;}static intsnd_harmony_capture_open(struct snd_pcm_substream *ss){ struct snd_harmony *h = snd_pcm_substream_chip(ss); struct snd_pcm_runtime *rt = ss->runtime; int err; h->csubs = ss; rt->hw = snd_harmony_capture; snd_pcm_hw_constraint_list(rt, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraint_rates); err = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS); if (err < 0) return err; return 0;}static int snd_harmony_playback_close(struct snd_pcm_substream *ss){ struct snd_harmony *h = snd_pcm_substream_chip(ss); h->psubs = NULL; return 0;}static intsnd_harmony_capture_close(struct snd_pcm_substream *ss){ struct snd_harmony *h = snd_pcm_substream_chip(ss); h->csubs = NULL; return 0;}static int snd_harmony_hw_params(struct snd_pcm_substream *ss, struct snd_pcm_hw_params *hw){ int err; struct snd_harmony *h = snd_pcm_substream_chip(ss); err = snd_pcm_lib_malloc_pages(ss, params_buffer_bytes(hw)); if (err > 0 && h->dma.type == SNDRV_DMA_TYPE_CONTINUOUS) ss->runtime->dma_addr = __pa(ss->runtime->dma_area); return err;}static int snd_harmony_hw_free(struct snd_pcm_substream *ss) { return snd_pcm_lib_free_pages(ss);}static struct snd_pcm_ops snd_harmony_playback_ops = { .open = snd_harmony_playback_open, .close = snd_harmony_playback_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_harmony_hw_params, .hw_free = snd_harmony_hw_free, .prepare = snd_harmony_playback_prepare, .trigger = snd_harmony_playback_trigger, .pointer = snd_harmony_playback_pointer,};static struct snd_pcm_ops snd_harmony_capture_ops = { .open = snd_harmony_capture_open, .close = snd_harmony_capture_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_harmony_hw_params, .hw_free = snd_harmony_hw_free, .prepare = snd_harmony_capture_prepare, .trigger = snd_harmony_capture_trigger, .pointer = snd_harmony_capture_pointer,};static int snd_harmony_pcm_init(struct snd_harmony *h){ struct snd_pcm *pcm; int err; harmony_disable_interrupts(h); err = snd_pcm_new(h->card, "harmony", 0, 1, 1, &pcm); if (err < 0) return err; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_harmony_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_harmony_capture_ops); pcm->private_data = h; pcm->info_flags = 0; strcpy(pcm->name, "harmony"); h->pcm = pcm; h->psubs = NULL; h->csubs = NULL; /* initialize graveyard buffer */ h->dma.type = SNDRV_DMA_TYPE_DEV; h->dma.dev = &h->dev->dev; err = snd_dma_alloc_pages(h->dma.type, h->dma.dev, BUF_SIZE*GRAVEYARD_BUFS, &h->gdma); if (err < 0) { printk(KERN_ERR PFX "cannot allocate graveyard buffer!\n"); return err; } /* initialize silence buffers */ err = snd_dma_alloc_pages(h->dma.type, h->dma.dev, BUF_SIZE*SILENCE_BUFS, &h->sdma); if (err < 0) { printk(KERN_ERR PFX "cannot allocate silence buffer!\n"); return err; } /* pre-allocate space for DMA */ err = snd_pcm_lib_preallocate_pages_for_all(pcm, h->dma.type, h->dma.dev, MAX_BUF_SIZE, MAX_BUF_SIZE); if (err < 0) { printk(KERN_ERR PFX "buffer allocation error: %d\n", err); return err; } h->st.format = snd_harmony_set_data_format(h, SNDRV_PCM_FORMAT_S16_BE, 1); return 0;}static void snd_harmony_set_new_gain(struct snd_harmony *h){ harmony_wait_for_control(h); harmony_write(h, HARMONY_GAINCTL, h->st.gain);}static int snd_harmony_mixercontrol_info(struct snd_kcontrol *kc, struct snd_ctl_elem_info *uinfo){ int mask = (kc->private_value >> 16) & 0xff; int left_shift = (kc->private_value) & 0xff; int right_shift = (kc->private_value >> 8) & 0xff; uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = left_shift == right_shift ? 1 : 2; uinfo->value.integer.min = 0; uinfo->value.integer.max = mask; return 0;}static int snd_harmony_volume_get(struct snd_kcontrol *kc, struct snd_ctl_elem_value *ucontrol){ struct snd_harmony *h = snd_kcontrol_chip(kc); int shift_left = (kc->private_value) & 0xff; int shift_right = (kc->private_value >> 8) & 0xff; int mask = (kc->private_value >> 16) & 0xff; int invert = (kc->private_value >> 24) & 0xff; int left, right; spin_lock_irq(&h->mixer_lock); left = (h->st.gain >> shift_left) & mask; right = (h->st.gain >> shift_right) & mask; if (invert) { left = mask - left; right = mask - right; } ucontrol->value.integer.value[0] = left; if (shift_left != shift_right) ucontrol->value.integer.value[1] = right; spin_unlock_irq(&h->mixer_lock); return 0;} static int snd_harmony_volume_put(struct snd_kcontrol *kc, struct snd_ctl_elem_value *ucontrol){ struct snd_harmony *h = snd_kcontrol_chip(kc); int shift_left = (kc->private_value) & 0xff; int shift_right = (kc->private_value >> 8) & 0xff; int mask = (kc->private_value >> 16) & 0xff; int invert = (kc->private_value >> 24) & 0xff; int left, right; int old_gain = h->st.gain; spin_lock_irq(&h->mixer_lock); left = ucontrol->value.integer.value[0] & mask; if (invert) left = mask - left; h->st.gain &= ~( (mask << shift_left ) ); h->st.gain |= (left << shift_left); if (shift_left != shift_right) { right = ucontrol->value.integer.value[1] & mask; if (invert) right = mask - right; h->st.gain &= ~( (mask << shift_right) ); h->st.gain |= (right << shift_right); } snd_harmony_set_new_gain(h); spin_unlock_irq(&h->mixer_lock); return h->st.gain != old_gain;}static int snd_harmony_captureroute_info(struct snd_kcontrol *kc, struct snd_ctl_elem_info *uinfo){ static char *texts[2] = { "Line", "Mic" }; uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = 2; if (uinfo->value.enumerated.item > 1) uinfo->value.enumerated.item = 1; strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); return 0;}static int snd_harmony_captureroute_get(struct snd_kcontrol *kc, struct snd_ctl_elem_value *ucontrol){ struct snd_harmony *h = snd_kcontrol_chip(kc); int value; spin_lock_irq(&h->mixer_lock); value = (h->st.gain >> HARMONY_GAIN_IS_SHIFT) & 1; ucontrol->value.enumerated.item[0] = value; spin_unlock_irq(&h->mixer_lock); return 0;} static int snd_harmony_captureroute_put(struct snd_kcontrol *kc, struct snd_ctl_elem_value *ucontrol){ struct snd_harmony *h = snd_kcontrol_chip(kc); int value; int old_gain = h->st.gain; spin_lock_irq(&h->mixer_lock); value = ucontrol->value.enumerated.item[0] & 1; h->st.gain &= ~HARMONY_GAIN_IS_MASK; h->st.gain |= value << HARMONY_GAIN_IS_SHIFT; snd_harmony_set_new_gain(h); spin_unlock_irq(&h->mixer_lock); return h->st.gain != old_gain;}#define HARMONY_CONTROLS ARRAY_SIZE(snd_harmony_controls)#define HARMONY_VOLUME(xname, left_shift, right_shift, mask, invert) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_harmony_mixercontrol_info, \ .get = snd_harmony_volume_get, .put = snd_harmony_volume_put, \ .private_value = ((left_shift) | ((right_shift) << 8) | \ ((mask) << 16) | ((invert) << 24)) }static struct snd_kcontrol_new snd_harmony_controls[] = { HARMONY_VOLUME("Master Playback Volume", HARMONY_GAIN_LO_SHIFT, HARMONY_GAIN_RO_SHIFT, HARMONY_GAIN_OUT, 1), HARMONY_VOLUME("Capture Volume", HARMONY_GAIN_LI_SHIFT, HARMONY_GAIN_RI_SHIFT, HARMONY_GAIN_IN, 0), HARMONY_VOLUME("Monitor Volume", HARMONY_GAIN_MA_SHIFT, HARMONY_GAIN_MA_SHIFT, HARMONY_GAIN_MA, 1), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Input Route", .info = snd_harmony_captureroute_info, .get = snd_harmony_captureroute_get, .put = snd_harmony_captureroute_put }, HARMONY_VOLUME("Internal Speaker Switch", HARMONY_GAIN_SE_SHIFT, HARMONY_GAIN_SE_SHIFT, 1, 0), HARMONY_VOLUME("Line-Out Switch", HARMONY_GAIN_LE_SHIFT, HARMONY_GAIN_LE_SHIFT, 1, 0), HARMONY_VOLUME("Headphones Switch", HARMONY_GAIN_HE_SHIFT, HARMONY_GAIN_HE_SHIFT, 1, 0),};static void __devinitsnd_harmony_mixer_reset(struct snd_harmony *h){ harmony_mute(h); harmony_reset(h); h->st.gain = HARMONY_GAIN_DEFAULT; harmony_unmute(h);}static int __devinitsnd_harmony_mixer_init(struct snd_harmony *h){ struct snd_card *card = h->card; int idx, err; snd_assert(h != NULL, return -EINVAL); strcpy(card->mixername, "Harmony Gain control interface"); for (idx = 0; idx < HARMONY_CONTROLS; idx++) { err = snd_ctl_add(card, snd_ctl_new1(&snd_harmony_controls[idx], h)); if (err < 0) return err; } snd_harmony_mixer_reset(h); return 0;}static intsnd_harmony_free(struct snd_harmony *h){ if (h->gdma.addr) snd_dma_free_pages(&h->gdma); if (h->sdma.addr) snd_dma_free_pages(&h->sdma); if (h->irq >= 0) free_irq(h->irq, h); if (h->iobase) iounmap(h->iobase); parisc_set_drvdata(h->dev, NULL); kfree(h); return 0;}static intsnd_harmony_dev_free(struct snd_device *dev){ struct snd_harmony *h = dev->device_data; return snd_harmony_free(h);}static int __devinitsnd_harmony_create(struct snd_card *card, struct parisc_device *padev, struct snd_harmony **rchip){ int err; struct snd_harmony *h; static struct snd_device_ops ops = { .dev_free = snd_harmony_dev_free, }; *rchip = NULL; h = kzalloc(sizeof(*h), GFP_KERNEL); if (h == NULL) return -ENOMEM; h->hpa = padev->hpa.start; h->card = card; h->dev = padev; h->irq = -1; h->iobase = ioremap_nocache(padev->hpa.start, HARMONY_SIZE); if (h->iobase == NULL) { printk(KERN_ERR PFX "unable to remap hpa 0x%lx\n", padev->hpa.start); err = -EBUSY; goto free_and_ret; } err = request_irq(padev->irq, snd_harmony_interrupt, 0, "harmony", h); if (err) { printk(KERN_ERR PFX "could not obtain interrupt %d", padev->irq); goto free_and_ret; } h->irq = padev->irq; spin_lock_init(&h->mixer_lock); spin_lock_init(&h->lock); if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, h, &ops)) < 0) { goto free_and_ret; } snd_card_set_dev(card, &padev->dev); *rchip = h; return 0;free_and_ret: snd_harmony_free(h); return err;}static int __devinitsnd_harmony_probe(struct parisc_device *padev){ int err; struct snd_card *card; struct snd_harmony *h; card = snd_card_new(index, id, THIS_MODULE, 0); if (card == NULL) return -ENOMEM; err = snd_harmony_create(card, padev, &h); if (err < 0) goto free_and_ret; err = snd_harmony_pcm_init(h); if (err < 0) goto free_and_ret; err = snd_harmony_mixer_init(h); if (err < 0) goto free_and_ret; strcpy(card->driver, "harmony"); strcpy(card->shortname, "Harmony"); sprintf(card->longname, "%s at 0x%lx, irq %i", card->shortname, h->hpa, h->irq); err = snd_card_register(card); if (err < 0) goto free_and_ret; parisc_set_drvdata(padev, card); return 0;free_and_ret: snd_card_free(card); return err;}static int __devexitsnd_harmony_remove(struct parisc_device *padev){ snd_card_free(parisc_get_drvdata(padev)); parisc_set_drvdata(padev, NULL); return 0;}static struct parisc_driver snd_harmony_driver = { .name = "harmony", .id_table = snd_harmony_devtable, .probe = snd_harmony_probe, .remove = snd_harmony_remove,};static int __init alsa_harmony_init(void){ return register_parisc_driver(&snd_harmony_driver);}static void __exitalsa_harmony_fini(void){ unregister_parisc_driver(&snd_harmony_driver);}MODULE_LICENSE("GPL");MODULE_AUTHOR("Kyle McMartin <kyle@parisc-linux.org>");MODULE_DESCRIPTION("Harmony sound driver");module_init(alsa_harmony_init);module_exit(alsa_harmony_fini);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -