opl3sa2.c
来自「linux 内核源代码」· C语言 代码 · 共 1,006 行 · 第 1/3 页
C
1,006 行
#define OPL3SA2_SINGLE(xname, xindex, reg, shift, mask, invert) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ .info = snd_opl3sa2_info_single, \ .get = snd_opl3sa2_get_single, .put = snd_opl3sa2_put_single, \ .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }#define OPL3SA2_SINGLE_TLV(xname, xindex, reg, shift, mask, invert, xtlv) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ .name = xname, .index = xindex, \ .info = snd_opl3sa2_info_single, \ .get = snd_opl3sa2_get_single, .put = snd_opl3sa2_put_single, \ .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24), \ .tlv = { .p = (xtlv) } }static int snd_opl3sa2_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo){ int mask = (kcontrol->private_value >> 16) & 0xff; uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = mask; return 0;}static int snd_opl3sa2_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_opl3sa2 *chip = snd_kcontrol_chip(kcontrol); unsigned long flags; int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0xff; int mask = (kcontrol->private_value >> 16) & 0xff; int invert = (kcontrol->private_value >> 24) & 0xff; spin_lock_irqsave(&chip->reg_lock, flags); ucontrol->value.integer.value[0] = (chip->ctlregs[reg] >> shift) & mask; spin_unlock_irqrestore(&chip->reg_lock, flags); if (invert) ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; return 0;}static int snd_opl3sa2_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_opl3sa2 *chip = snd_kcontrol_chip(kcontrol); unsigned long flags; int reg = kcontrol->private_value & 0xff; int shift = (kcontrol->private_value >> 8) & 0xff; int mask = (kcontrol->private_value >> 16) & 0xff; int invert = (kcontrol->private_value >> 24) & 0xff; int change; unsigned short val, oval; val = (ucontrol->value.integer.value[0] & mask); if (invert) val = mask - val; val <<= shift; spin_lock_irqsave(&chip->reg_lock, flags); oval = chip->ctlregs[reg]; val = (oval & ~(mask << shift)) | val; change = val != oval; __snd_opl3sa2_write(chip, reg, val); spin_unlock_irqrestore(&chip->reg_lock, flags); return change;}#define OPL3SA2_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ .info = snd_opl3sa2_info_double, \ .get = snd_opl3sa2_get_double, .put = snd_opl3sa2_put_double, \ .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }#define OPL3SA2_DOUBLE_TLV(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert, xtlv) \{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \ .name = xname, .index = xindex, \ .info = snd_opl3sa2_info_double, \ .get = snd_opl3sa2_get_double, .put = snd_opl3sa2_put_double, \ .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22), \ .tlv = { .p = (xtlv) } }static int snd_opl3sa2_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo){ int mask = (kcontrol->private_value >> 24) & 0xff; uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; uinfo->value.integer.min = 0; uinfo->value.integer.max = mask; return 0;}static int snd_opl3sa2_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_opl3sa2 *chip = snd_kcontrol_chip(kcontrol); unsigned long flags; int left_reg = kcontrol->private_value & 0xff; int right_reg = (kcontrol->private_value >> 8) & 0xff; int shift_left = (kcontrol->private_value >> 16) & 0x07; int shift_right = (kcontrol->private_value >> 19) & 0x07; int mask = (kcontrol->private_value >> 24) & 0xff; int invert = (kcontrol->private_value >> 22) & 1; spin_lock_irqsave(&chip->reg_lock, flags); ucontrol->value.integer.value[0] = (chip->ctlregs[left_reg] >> shift_left) & mask; ucontrol->value.integer.value[1] = (chip->ctlregs[right_reg] >> shift_right) & mask; spin_unlock_irqrestore(&chip->reg_lock, flags); if (invert) { ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0]; ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1]; } return 0;}static int snd_opl3sa2_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_opl3sa2 *chip = snd_kcontrol_chip(kcontrol); unsigned long flags; int left_reg = kcontrol->private_value & 0xff; int right_reg = (kcontrol->private_value >> 8) & 0xff; int shift_left = (kcontrol->private_value >> 16) & 0x07; int shift_right = (kcontrol->private_value >> 19) & 0x07; int mask = (kcontrol->private_value >> 24) & 0xff; int invert = (kcontrol->private_value >> 22) & 1; int change; unsigned short val1, val2, oval1, oval2; val1 = ucontrol->value.integer.value[0] & mask; val2 = ucontrol->value.integer.value[1] & mask; if (invert) { val1 = mask - val1; val2 = mask - val2; } val1 <<= shift_left; val2 <<= shift_right; spin_lock_irqsave(&chip->reg_lock, flags); if (left_reg != right_reg) { oval1 = chip->ctlregs[left_reg]; oval2 = chip->ctlregs[right_reg]; val1 = (oval1 & ~(mask << shift_left)) | val1; val2 = (oval2 & ~(mask << shift_right)) | val2; change = val1 != oval1 || val2 != oval2; __snd_opl3sa2_write(chip, left_reg, val1); __snd_opl3sa2_write(chip, right_reg, val2); } else { oval1 = chip->ctlregs[left_reg]; val1 = (oval1 & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2; change = val1 != oval1; __snd_opl3sa2_write(chip, left_reg, val1); } spin_unlock_irqrestore(&chip->reg_lock, flags); return change;}static const DECLARE_TLV_DB_SCALE(db_scale_master, -3000, 200, 0);static const DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);static struct snd_kcontrol_new snd_opl3sa2_controls[] = {OPL3SA2_DOUBLE("Master Playback Switch", 0, 0x07, 0x08, 7, 7, 1, 1),OPL3SA2_DOUBLE_TLV("Master Playback Volume", 0, 0x07, 0x08, 0, 0, 15, 1, db_scale_master),OPL3SA2_SINGLE("Mic Playback Switch", 0, 0x09, 7, 1, 1),OPL3SA2_SINGLE_TLV("Mic Playback Volume", 0, 0x09, 0, 31, 1, db_scale_5bit_12db_max),};static struct snd_kcontrol_new snd_opl3sa2_tone_controls[] = {OPL3SA2_DOUBLE("3D Control - Wide", 0, 0x14, 0x14, 4, 0, 7, 0),OPL3SA2_DOUBLE("Tone Control - Bass", 0, 0x15, 0x15, 4, 0, 7, 0),OPL3SA2_DOUBLE("Tone Control - Treble", 0, 0x16, 0x16, 4, 0, 7, 0)};static void snd_opl3sa2_master_free(struct snd_kcontrol *kcontrol){ struct snd_opl3sa2 *chip = snd_kcontrol_chip(kcontrol); chip->master_switch = NULL; chip->master_volume = NULL;}static int __devinit snd_opl3sa2_mixer(struct snd_opl3sa2 *chip){ struct snd_card *card = chip->card; struct snd_ctl_elem_id id1, id2; struct snd_kcontrol *kctl; unsigned int idx; int err; memset(&id1, 0, sizeof(id1)); memset(&id2, 0, sizeof(id2)); id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER; /* reassign AUX0 to CD */ strcpy(id1.name, "Aux Playback Switch"); strcpy(id2.name, "CD Playback Switch"); if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) { snd_printk(KERN_ERR "Cannot rename opl3sa2 control\n"); return err; } strcpy(id1.name, "Aux Playback Volume"); strcpy(id2.name, "CD Playback Volume"); if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) { snd_printk(KERN_ERR "Cannot rename opl3sa2 control\n"); return err; } /* reassign AUX1 to FM */ strcpy(id1.name, "Aux Playback Switch"); id1.index = 1; strcpy(id2.name, "FM Playback Switch"); if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) { snd_printk(KERN_ERR "Cannot rename opl3sa2 control\n"); return err; } strcpy(id1.name, "Aux Playback Volume"); strcpy(id2.name, "FM Playback Volume"); if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0) { snd_printk(KERN_ERR "Cannot rename opl3sa2 control\n"); return err; } /* add OPL3SA2 controls */ for (idx = 0; idx < ARRAY_SIZE(snd_opl3sa2_controls); idx++) { if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_opl3sa2_controls[idx], chip))) < 0) return err; switch (idx) { case 0: chip->master_switch = kctl; kctl->private_free = snd_opl3sa2_master_free; break; case 1: chip->master_volume = kctl; kctl->private_free = snd_opl3sa2_master_free; break; } } if (chip->version > 2) { for (idx = 0; idx < ARRAY_SIZE(snd_opl3sa2_tone_controls); idx++) if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_opl3sa2_tone_controls[idx], chip))) < 0) return err; } return 0;}/* Power Management support functions */#ifdef CONFIG_PMstatic int snd_opl3sa2_suspend(struct snd_card *card, pm_message_t state){ struct snd_opl3sa2 *chip = card->private_data; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); chip->cs4231->suspend(chip->cs4231); /* power down */ snd_opl3sa2_write(chip, OPL3SA2_PM_CTRL, OPL3SA2_PM_D3); return 0;}static int snd_opl3sa2_resume(struct snd_card *card){ struct snd_opl3sa2 *chip = card->private_data; int i; /* power up */ snd_opl3sa2_write(chip, OPL3SA2_PM_CTRL, OPL3SA2_PM_D0); /* restore registers */ for (i = 2; i <= 0x0a; i++) { if (i != OPL3SA2_IRQ_STATUS) snd_opl3sa2_write(chip, i, chip->ctlregs[i]); } if (chip->version > 2) { for (i = 0x12; i <= 0x16; i++) snd_opl3sa2_write(chip, i, chip->ctlregs[i]); } /* restore cs4231 */ chip->cs4231->resume(chip->cs4231); snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0;}#endif /* CONFIG_PM */#ifdef CONFIG_PNPstatic int __devinit snd_opl3sa2_pnp(int dev, struct snd_opl3sa2 *chip, struct pnp_dev *pdev){ struct pnp_resource_table * cfg; int err; cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL); if (!cfg) { snd_printk(KERN_ERR PFX "cannot allocate pnp cfg\n"); return -ENOMEM; } /* PnP initialization */ pnp_init_resource_table(cfg); if (sb_port[dev] != SNDRV_AUTO_PORT) pnp_resource_change(&cfg->port_resource[0], sb_port[dev], 16); if (wss_port[dev] != SNDRV_AUTO_PORT) pnp_resource_change(&cfg->port_resource[1], wss_port[dev], 8); if (fm_port[dev] != SNDRV_AUTO_PORT) pnp_resource_change(&cfg->port_resource[2], fm_port[dev], 4); if (midi_port[dev] != SNDRV_AUTO_PORT) pnp_resource_change(&cfg->port_resource[3], midi_port[dev], 2); if (port[dev] != SNDRV_AUTO_PORT) pnp_resource_change(&cfg->port_resource[4], port[dev], 2); if (dma1[dev] != SNDRV_AUTO_DMA) pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1); if (dma2[dev] != SNDRV_AUTO_DMA) pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1); if (irq[dev] != SNDRV_AUTO_IRQ) pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1); err = pnp_manual_config_dev(pdev, cfg, 0); if (err < 0) snd_printk(KERN_WARNING "PnP manual resources are invalid, using auto config\n"); err = pnp_activate_dev(pdev); if (err < 0) { kfree(cfg); snd_printk(KERN_ERR "PnP configure failure (out of resources?) err = %d\n", err); return -EBUSY; } sb_port[dev] = pnp_port_start(pdev, 0); wss_port[dev] = pnp_port_start(pdev, 1); fm_port[dev] = pnp_port_start(pdev, 2); midi_port[dev] = pnp_port_start(pdev, 3); port[dev] = pnp_port_start(pdev, 4); dma1[dev] = pnp_dma(pdev, 0); dma2[dev] = pnp_dma(pdev, 1); irq[dev] = pnp_irq(pdev, 0); snd_printdd("%sPnP OPL3-SA: sb port=0x%lx, wss port=0x%lx, fm port=0x%lx, midi port=0x%lx\n", pnp_device_is_pnpbios(pdev) ? "BIOS" : "ISA", sb_port[dev], wss_port[dev], fm_port[dev], midi_port[dev]); snd_printdd("%sPnP OPL3-SA: control port=0x%lx, dma1=%i, dma2=%i, irq=%i\n", pnp_device_is_pnpbios(pdev) ? "BIOS" : "ISA", port[dev], dma1[dev], dma2[dev], irq[dev]); kfree(cfg); return 0;}#endif /* CONFIG_PNP */static void snd_opl3sa2_free(struct snd_card *card){ struct snd_opl3sa2 *chip = card->private_data; if (chip->irq >= 0) free_irq(chip->irq, (void *)chip); release_and_free_resource(chip->res_port);}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?