📄 harmony.c
字号:
.hw_free = snd_card_harmony_hw_free, .prepare = snd_card_harmony_playback_prepare, .trigger = snd_card_harmony_playback_trigger, .pointer = snd_card_harmony_playback_pointer,};static snd_pcm_ops_t snd_card_harmony_capture_ops = { .open = snd_card_harmony_capture_open, .close = snd_card_harmony_capture_close, .ioctl = snd_card_harmony_capture_ioctl, .hw_params = snd_card_harmony_hw_params, .hw_free = snd_card_harmony_hw_free, .prepare = snd_card_harmony_capture_prepare, .trigger = snd_card_harmony_capture_trigger, .pointer = snd_card_harmony_capture_pointer,};static int snd_card_harmony_pcm_init(snd_card_harmony_t *harmony){ snd_pcm_t *pcm; int err; /* Request that IRQ */ if (request_irq(harmony->irq, snd_card_harmony_interrupt, 0 ,"harmony", harmony)) { printk(KERN_ERR PFX "Error requesting irq %d.\n", harmony->irq); return -EFAULT; } snd_harmony_disable_interrupts(harmony); if ((err = snd_pcm_new(harmony->card, "Harmony", 0, 1, 1, &pcm)) < 0) return err; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_card_harmony_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_harmony_capture_ops); pcm->private_data = harmony; pcm->info_flags = 0; strcpy(pcm->name, "Harmony"); harmony->pcm = pcm; /* initialize graveyard buffer */ harmony->dma_dev.type = SNDRV_DMA_TYPE_DEV; harmony->dma_dev.dev = &harmony->pa_dev->dev; err = snd_dma_alloc_pages(harmony->dma_dev.type, harmony->dma_dev.dev, HARMONY_BUF_SIZE*GRAVEYARD_BUFS, &harmony->graveyard_dma); if (err == -ENOMEM) { /* use continuous buffers */ harmony->dma_dev.type = SNDRV_DMA_TYPE_CONTINUOUS; harmony->dma_dev.dev = snd_dma_continuous_data(GFP_KERNEL); err = snd_dma_alloc_pages(harmony->dma_dev.type, harmony->dma_dev.dev, HARMONY_BUF_SIZE*GRAVEYARD_BUFS, &harmony->graveyard_dma); } if (err < 0) { printk(KERN_ERR PFX "can't allocate graveyard buffer\n"); return err; } harmony->graveyard_count = 0; /* initialize silence buffers */ err = snd_dma_alloc_pages(harmony->dma_dev.type, harmony->dma_dev.dev, HARMONY_BUF_SIZE*SILENCE_BUFS, &harmony->silence_dma); if (err < 0) { printk(KERN_ERR PFX "can't allocate silence buffer\n"); return err; } harmony->silence_count = 0; if (harmony->dma_dev.type == SNDRV_DMA_TYPE_CONTINUOUS) { harmony->graveyard_dma.addr = __pa(harmony->graveyard_dma.area); harmony->silence_dma.addr = __pa(harmony->silence_dma.area); } harmony->ply_stopped = harmony->cap_stopped = 1; harmony->playback_substream = NULL; harmony->capture_substream = NULL; harmony->graveyard_count = 0; err = snd_pcm_lib_preallocate_pages_for_all(pcm, harmony->dma_dev.type, harmony->dma_dev.dev, MAX_BUFFER_SIZE, MAX_BUFFER_SIZE); if (err < 0) { printk(KERN_ERR PFX "buffer allocation error %d\n", err); // return err; } return 0;}/* * mixer routines */static void snd_harmony_set_new_gain(snd_card_harmony_t *harmony){ DPRINTK(KERN_INFO PFX "Setting new gain %x at %lx\n", harmony->current_gain, harmony->hpa+REG_GAINCTL); /* Wait until we're out of control mode */ snd_harmony_wait_cntl(harmony); gsc_writel(harmony->current_gain, harmony->hpa+REG_GAINCTL);}#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 int snd_harmony_mixercontrol_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo){ int mask = (kcontrol->private_value >> 16) & 0xff; int left_shift = (kcontrol->private_value) & 0xff; int right_shift = (kcontrol->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(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ snd_card_harmony_t *harmony = snd_kcontrol_chip(kcontrol); int shift_left = (kcontrol->private_value) & 0xff; int shift_right = (kcontrol->private_value >> 8) & 0xff; int mask = (kcontrol->private_value >> 16) & 0xff; int invert = (kcontrol->private_value >> 24) & 0xff; unsigned long flags; int left, right; spin_lock_irqsave(&harmony->mixer_lock, flags); left = (harmony->current_gain >> shift_left) & mask; right = (harmony->current_gain >> shift_right) & mask; if (invert) { left = mask - left; right = mask - right; } ucontrol->value.integer.value[0] = left; ucontrol->value.integer.value[1] = right; spin_unlock_irqrestore(&harmony->mixer_lock, flags); return 0;} static int snd_harmony_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol){ snd_card_harmony_t *harmony = snd_kcontrol_chip(kcontrol); int shift_left = (kcontrol->private_value) & 0xff; int shift_right = (kcontrol->private_value >> 8) & 0xff; int mask = (kcontrol->private_value >> 16) & 0xff; int invert = (kcontrol->private_value >> 24) & 0xff; unsigned long flags; int left, right; int old_gain = harmony->current_gain; left = ucontrol->value.integer.value[0] & mask; right = ucontrol->value.integer.value[1] & mask; if (invert) { left = mask - left; right = mask - right; } spin_lock_irqsave(&harmony->mixer_lock, flags); harmony->current_gain = harmony->current_gain & ~( (mask << shift_right) | (mask << shift_left)); harmony->current_gain = harmony->current_gain | ((left << shift_left) | (right << shift_right) ); snd_harmony_set_new_gain(harmony); spin_unlock_irqrestore(&harmony->mixer_lock, flags); return (old_gain - harmony->current_gain);}#define HARMONY_CONTROLS (sizeof(snd_harmony_controls)/sizeof(snd_kcontrol_new_t))static snd_kcontrol_new_t snd_harmony_controls[] = {HARMONY_VOLUME("PCM Capture Volume", 12, 16, 0x0f, 0),HARMONY_VOLUME("Master Volume", 20, 20, 0x0f, 1),HARMONY_VOLUME("PCM Playback Volume", 6, 0, 0x3f, 1),};static void __init snd_harmony_reset_codec(snd_card_harmony_t *harmony){ snd_harmony_wait_cntl(harmony); gsc_writel(1, harmony->hpa+REG_RESET); mdelay(50); /* wait 50 ms */ gsc_writel(0, harmony->hpa+REG_RESET);}/* * Mute all the output and reset Harmony. */static void __init snd_harmony_mixer_reset(snd_card_harmony_t *harmony){ harmony->current_gain = HARMONY_GAIN_TOTAL_SILENCE; snd_harmony_set_new_gain(harmony); snd_harmony_reset_codec(harmony); harmony->current_gain = HARMONY_GAIN_DEFAULT; snd_harmony_set_new_gain(harmony);}static int __init snd_card_harmony_mixer_init(snd_card_harmony_t *harmony){ snd_card_t *card = harmony->card; int idx, err; snd_assert(harmony != NULL, return -EINVAL); strcpy(card->mixername, "Harmony Gain control interface"); for (idx = 0; idx < HARMONY_CONTROLS; idx++) { if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_harmony_controls[idx], harmony))) < 0) return err; } snd_harmony_mixer_reset(harmony); return 0;}static int snd_card_harmony_create(snd_card_t *card, struct parisc_device *pa_dev, snd_card_harmony_t *harmony){ u32 cntl; harmony->card = card; harmony->pa_dev = pa_dev; /* Set the HPA of harmony */ harmony->hpa = pa_dev->hpa; harmony->irq = pa_dev->irq; if (!harmony->irq) { printk(KERN_ERR PFX "no irq found\n"); return -ENODEV; } /* Grab the ID and revision from the device */ harmony->id = (gsc_readl(harmony->hpa+REG_ID)&0x00ff0000) >> 16; if ((harmony->id | 1) != 0x15) { printk(KERN_WARNING PFX "wrong harmony id 0x%02x\n", harmony->id); return -EBUSY; } cntl = gsc_readl(harmony->hpa+REG_CNTL); harmony->rev = (cntl>>20) & 0xff; printk(KERN_INFO "Lasi Harmony Audio driver h/w id %i, rev. %i at 0x%lx, IRQ %i\n", harmony->id, harmony->rev, pa_dev->hpa, harmony->irq); /* Make sure the control bit isn't set, although I don't think it ever is. */ if (cntl & HARMONY_CNTL_C) { printk(KERN_WARNING PFX "CNTL busy\n"); harmony->hpa = 0; return -EBUSY; } return 0;} static int __init snd_card_harmony_probe(struct parisc_device *pa_dev){ static int dev; snd_card_harmony_t *chip; snd_card_t *card; int err; if (dev >= SNDRV_CARDS) return -ENODEV; if (!enable[dev]) { dev++; return -ENOENT; } snd_harmony_cards[dev] = snd_card_new(index[dev], id[dev], THIS_MODULE, sizeof(snd_card_harmony_t)); card = snd_harmony_cards[dev]; if (card == NULL) return -ENOMEM; chip = (struct snd_card_harmony *)card->private_data; spin_lock_init(&chip->control_lock); spin_lock_init(&chip->mixer_lock); if ((err = snd_card_harmony_create(card, pa_dev, chip)) < 0) { printk(KERN_ERR PFX "Creation failed\n"); snd_card_free(card); return err; } if ((err = snd_card_harmony_pcm_init(chip)) < 0) { printk(KERN_ERR PFX "PCM Init failed\n"); snd_card_free(card); return err; } if ((err = snd_card_harmony_mixer_init(chip)) < 0) { printk(KERN_ERR PFX "Mixer init failed\n"); snd_card_free(card); return err; } snd_harmony_proc_init(chip); strcpy(card->driver, "Harmony"); strcpy(card->shortname, "ALSA driver for LASI Harmony"); sprintf(card->longname, "%s at h/w, id %i, rev. %i hpa 0x%lx, IRQ %i\n",card->shortname, chip->id, chip->rev, pa_dev->hpa, chip->irq); if ((err = snd_card_register(card)) < 0) { snd_card_free(card); return err; } printk(KERN_DEBUG PFX "Successfully registered harmony pcm backend & mixer %d\n", dev); dev++; return 0;}static struct parisc_device_id snd_card_harmony_devicetbl[] = { { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007A }, /* Bushmaster/Flounder */ { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007B }, /* 712/715 Audio */ { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007E }, /* Pace Audio */ { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0007F }, /* Outfield / Coral II */ { 0, }};MODULE_DEVICE_TABLE(parisc, snd_card_harmony_devicetbl);/* * bloc device parisc. c'est une structure qui definit un device * que l'on trouve sur parisc. * On y trouve les differents numeros HVERSION correspondant au device * en question (ce qui permet a l'inventory de l'identifier) et la fonction * d'initialisation du chose */static struct parisc_driver snd_card_harmony_driver = { .name = "Lasi ALSA Harmony", .id_table = snd_card_harmony_devicetbl, .probe = snd_card_harmony_probe,};static int __init alsa_card_harmony_init(void){ int err; if ((err = register_parisc_driver(&snd_card_harmony_driver)) < 0) { printk(KERN_ERR "Harmony soundcard not found or device busy\n"); return err; } return 0;}static void __exit alsa_card_harmony_exit(void){ int idx; snd_card_harmony_t *harmony; for (idx = 0; idx < SNDRV_CARDS; idx++) { if (snd_harmony_cards[idx] != NULL) { DPRINTK(KERN_INFO PFX "Freeing card %d\n", idx); harmony = snd_harmony_cards[idx]->private_data; free_irq(harmony->irq, snd_card_harmony_interrupt); printk(KERN_INFO PFX "Card unloaded %d, irq=%d\n", idx, harmony->irq); snd_card_free(snd_harmony_cards[idx]); } } }module_init(alsa_card_harmony_init)module_exit(alsa_card_harmony_exit)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -