📄 at73c213.c
字号:
spin_unlock_irq(&chip->lock); return 0;}static int snd_at73c213_mono_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol){ struct snd_at73c213 *chip = snd_kcontrol_chip(kcontrol); 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, retval; unsigned short val; if (ucontrol->value.integer.value[0]) val = mask; else val = 0; if (invert) val = mask - val; val <<= shift; spin_lock_irq(&chip->lock); val |= (chip->reg_image[reg] & ~(mask << shift)); change = val != chip->reg_image[reg]; retval = snd_at73c213_write_reg(chip, reg, val); spin_unlock_irq(&chip->lock); if (retval) return retval; return change;}static int snd_at73c213_pa_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo){ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; uinfo->value.integer.min = 0; uinfo->value.integer.max = ((kcontrol->private_value >> 16) & 0xff) - 1; return 0;}static int snd_at73c213_line_capture_volume_info( struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo){ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 2; /* When inverted will give values 0x10001 => 0. */ uinfo->value.integer.min = 14; uinfo->value.integer.max = 31; return 0;}static int snd_at73c213_aux_capture_volume_info( struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo){ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; uinfo->count = 1; /* When inverted will give values 0x10001 => 0. */ uinfo->value.integer.min = 14; uinfo->value.integer.max = 31; return 0;}#define AT73C213_MONO_SWITCH(xname, xindex, reg, shift, mask, invert) \{ \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .info = snd_at73c213_mono_switch_info, \ .get = snd_at73c213_mono_switch_get, \ .put = snd_at73c213_mono_switch_put, \ .private_value = (reg | (shift << 8) | (mask << 16) | (invert << 24)) \}#define AT73C213_STEREO(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \{ \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = xname, \ .index = xindex, \ .info = snd_at73c213_stereo_info, \ .get = snd_at73c213_stereo_get, \ .put = snd_at73c213_stereo_put, \ .private_value = (left_reg | (right_reg << 8) \ | (shift_left << 16) | (shift_right << 19) \ | (mask << 24) | (invert << 22)) \}static struct snd_kcontrol_new snd_at73c213_controls[] __devinitdata = {AT73C213_STEREO("Master Playback Volume", 0, DAC_LMPG, DAC_RMPG, 0, 0, 0x1f, 1),AT73C213_STEREO("Master Playback Switch", 0, DAC_LMPG, DAC_RMPG, 5, 5, 1, 1),AT73C213_STEREO("PCM Playback Volume", 0, DAC_LLOG, DAC_RLOG, 0, 0, 0x1f, 1),AT73C213_STEREO("PCM Playback Switch", 0, DAC_LLOG, DAC_RLOG, 5, 5, 1, 1),AT73C213_MONO_SWITCH("Mono PA Playback Switch", 0, DAC_CTRL, DAC_CTRL_ONPADRV, 0x01, 0),{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "PA Playback Volume", .index = 0, .info = snd_at73c213_pa_volume_info, .get = snd_at73c213_mono_get, .put = snd_at73c213_mono_put, .private_value = PA_CTRL | (PA_CTRL_APAGAIN << 8) | \ (0x0f << 16) | (1 << 24),},AT73C213_MONO_SWITCH("PA High Gain Playback Switch", 0, PA_CTRL, PA_CTRL_APALP, 0x01, 1),AT73C213_MONO_SWITCH("PA Playback Switch", 0, PA_CTRL, PA_CTRL_APAON, 0x01, 0),{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Aux Capture Volume", .index = 0, .info = snd_at73c213_aux_capture_volume_info, .get = snd_at73c213_mono_get, .put = snd_at73c213_mono_put, .private_value = DAC_AUXG | (0 << 8) | (0x1f << 16) | (1 << 24),},AT73C213_MONO_SWITCH("Aux Capture Switch", 0, DAC_CTRL, DAC_CTRL_ONAUXIN, 0x01, 0),{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Line Capture Volume", .index = 0, .info = snd_at73c213_line_capture_volume_info, .get = snd_at73c213_stereo_get, .put = snd_at73c213_stereo_put, .private_value = DAC_LLIG | (DAC_RLIG << 8) | (0 << 16) | (0 << 19) | (0x1f << 24) | (1 << 22),},AT73C213_MONO_SWITCH("Line Capture Switch", 0, DAC_CTRL, 0, 0x03, 0),};static int __devinit snd_at73c213_mixer(struct snd_at73c213 *chip){ struct snd_card *card; int errval, idx; if (chip == NULL || chip->pcm == NULL) return -EINVAL; card = chip->card; strcpy(card->mixername, chip->pcm->name); for (idx = 0; idx < ARRAY_SIZE(snd_at73c213_controls); idx++) { errval = snd_ctl_add(card, snd_ctl_new1(&snd_at73c213_controls[idx], chip)); if (errval < 0) goto cleanup; } return 0;cleanup: for (idx = 1; idx < ARRAY_SIZE(snd_at73c213_controls) + 1; idx++) { struct snd_kcontrol *kctl; kctl = snd_ctl_find_numid(card, idx); if (kctl) snd_ctl_remove(card, kctl); } return errval;}/* * Device functions */static int snd_at73c213_ssc_init(struct snd_at73c213 *chip){ /* * Continuous clock output. * Starts on falling TF. * Delay 1 cycle (1 bit). * Periode is 16 bit (16 - 1). */ ssc_writel(chip->ssc->regs, TCMR, SSC_BF(TCMR_CKO, 1) | SSC_BF(TCMR_START, 4) | SSC_BF(TCMR_STTDLY, 1) | SSC_BF(TCMR_PERIOD, 16 - 1)); /* * Data length is 16 bit (16 - 1). * Transmit MSB first. * Transmit 2 words each transfer. * Frame sync length is 16 bit (16 - 1). * Frame starts on negative pulse. */ ssc_writel(chip->ssc->regs, TFMR, SSC_BF(TFMR_DATLEN, 16 - 1) | SSC_BIT(TFMR_MSBF) | SSC_BF(TFMR_DATNB, 1) | SSC_BF(TFMR_FSLEN, 16 - 1) | SSC_BF(TFMR_FSOS, 1)); return 0;}static int snd_at73c213_chip_init(struct snd_at73c213 *chip){ int retval; unsigned char dac_ctrl = 0; retval = snd_at73c213_set_bitrate(chip); if (retval) goto out; /* Enable DAC master clock. */ clk_enable(chip->board->dac_clk); /* Initialize at73c213 on SPI bus. */ retval = snd_at73c213_write_reg(chip, DAC_RST, 0x04); if (retval) goto out_clk; msleep(1); retval = snd_at73c213_write_reg(chip, DAC_RST, 0x03); if (retval) goto out_clk; /* Precharge everything. */ retval = snd_at73c213_write_reg(chip, DAC_PRECH, 0xff); if (retval) goto out_clk; retval = snd_at73c213_write_reg(chip, PA_CTRL, (1<<PA_CTRL_APAPRECH)); if (retval) goto out_clk; retval = snd_at73c213_write_reg(chip, DAC_CTRL, (1<<DAC_CTRL_ONLNOL) | (1<<DAC_CTRL_ONLNOR)); if (retval) goto out_clk; msleep(50); /* Stop precharging PA. */ retval = snd_at73c213_write_reg(chip, PA_CTRL, (1<<PA_CTRL_APALP) | 0x0f); if (retval) goto out_clk; msleep(450); /* Stop precharging DAC, turn on master power. */ retval = snd_at73c213_write_reg(chip, DAC_PRECH, (1<<DAC_PRECH_ONMSTR)); if (retval) goto out_clk; msleep(1); /* Turn on DAC. */ dac_ctrl = (1<<DAC_CTRL_ONDACL) | (1<<DAC_CTRL_ONDACR) | (1<<DAC_CTRL_ONLNOL) | (1<<DAC_CTRL_ONLNOR); retval = snd_at73c213_write_reg(chip, DAC_CTRL, dac_ctrl); if (retval) goto out_clk; /* Mute sound. */ retval = snd_at73c213_write_reg(chip, DAC_LMPG, 0x3f); if (retval) goto out_clk; retval = snd_at73c213_write_reg(chip, DAC_RMPG, 0x3f); if (retval) goto out_clk; retval = snd_at73c213_write_reg(chip, DAC_LLOG, 0x3f); if (retval) goto out_clk; retval = snd_at73c213_write_reg(chip, DAC_RLOG, 0x3f); if (retval) goto out_clk; retval = snd_at73c213_write_reg(chip, DAC_LLIG, 0x11); if (retval) goto out_clk; retval = snd_at73c213_write_reg(chip, DAC_RLIG, 0x11); if (retval) goto out_clk; retval = snd_at73c213_write_reg(chip, DAC_AUXG, 0x11); if (retval) goto out_clk; /* Enable I2S device, i.e. clock output. */ ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXEN)); goto out;out_clk: clk_disable(chip->board->dac_clk);out: return retval;}static int snd_at73c213_dev_free(struct snd_device *device){ struct snd_at73c213 *chip = device->device_data; ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS)); if (chip->irq >= 0) { free_irq(chip->irq, chip); chip->irq = -1; } return 0;}static int __devinit snd_at73c213_dev_init(struct snd_card *card, struct spi_device *spi){ static struct snd_device_ops ops = { .dev_free = snd_at73c213_dev_free, }; struct snd_at73c213 *chip = get_chip(card); int irq, retval; irq = chip->ssc->irq; if (irq < 0) return irq; spin_lock_init(&chip->lock); chip->card = card; chip->irq = -1; retval = request_irq(irq, snd_at73c213_interrupt, 0, "at73c213", chip); if (retval) { dev_dbg(&chip->spi->dev, "unable to request irq %d\n", irq); goto out; } chip->irq = irq; memcpy(&chip->reg_image, &snd_at73c213_original_image, sizeof(snd_at73c213_original_image)); retval = snd_at73c213_ssc_init(chip); if (retval) goto out_irq; retval = snd_at73c213_chip_init(chip); if (retval) goto out_irq; retval = snd_at73c213_pcm_new(chip, 0); if (retval) goto out_irq; retval = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); if (retval) goto out_irq; retval = snd_at73c213_mixer(chip); if (retval) goto out_snd_dev; snd_card_set_dev(card, &spi->dev); goto out;out_snd_dev: snd_device_free(card, chip);out_irq: free_irq(chip->irq, chip); chip->irq = -1;out: return retval;}static int snd_at73c213_probe(struct spi_device *spi){ struct snd_card *card; struct snd_at73c213 *chip; struct at73c213_board_info *board; int retval; char id[16]; board = spi->dev.platform_data; if (!board) { dev_dbg(&spi->dev, "no platform_data\n"); return -ENXIO; } if (!board->dac_clk) { dev_dbg(&spi->dev, "no DAC clk\n"); return -ENXIO; } if (IS_ERR(board->dac_clk)) { dev_dbg(&spi->dev, "no DAC clk\n"); return PTR_ERR(board->dac_clk); } retval = -ENOMEM; /* Allocate "card" using some unused identifiers. */ snprintf(id, sizeof id, "at73c213_%d", board->ssc_id); card = snd_card_new(-1, id, THIS_MODULE, sizeof(struct snd_at73c213)); if (!card) goto out; chip = card->private_data; chip->spi = spi; chip->board = board; chip->ssc = ssc_request(board->ssc_id); if (IS_ERR(chip->ssc)) { dev_dbg(&spi->dev, "could not get ssc%d device\n", board->ssc_id); retval = PTR_ERR(chip->ssc); goto out_card; } retval = snd_at73c213_dev_init(card, spi); if (retval) goto out_ssc; strcpy(card->driver, "at73c213"); strcpy(card->shortname, board->shortname); sprintf(card->longname, "%s on irq %d", card->shortname, chip->irq); retval = snd_card_register(card); if (retval) goto out_ssc; dev_set_drvdata(&spi->dev, card); goto out;out_ssc: ssc_free(chip->ssc);out_card: snd_card_free(card);out: return retval;}static int __devexit snd_at73c213_remove(struct spi_device *spi){ struct snd_card *card = dev_get_drvdata(&spi->dev); struct snd_at73c213 *chip = card->private_data; int retval; /* Stop playback. */ ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS)); /* Mute sound. */ retval = snd_at73c213_write_reg(chip, DAC_LMPG, 0x3f); if (retval) goto out; retval = snd_at73c213_write_reg(chip, DAC_RMPG, 0x3f); if (retval) goto out; retval = snd_at73c213_write_reg(chip, DAC_LLOG, 0x3f); if (retval) goto out; retval = snd_at73c213_write_reg(chip, DAC_RLOG, 0x3f); if (retval) goto out; retval = snd_at73c213_write_reg(chip, DAC_LLIG, 0x11); if (retval) goto out; retval = snd_at73c213_write_reg(chip, DAC_RLIG, 0x11); if (retval) goto out; retval = snd_at73c213_write_reg(chip, DAC_AUXG, 0x11); if (retval) goto out; /* Turn off PA. */ retval = snd_at73c213_write_reg(chip, PA_CTRL, chip->reg_image[PA_CTRL] | 0x0f); if (retval) goto out; msleep(10); retval = snd_at73c213_write_reg(chip, PA_CTRL, (1 << PA_CTRL_APALP) | 0x0f); if (retval) goto out; /* Turn off external DAC. */ retval = snd_at73c213_write_reg(chip, DAC_CTRL, 0x0c); if (retval) goto out; msleep(2); retval = snd_at73c213_write_reg(chip, DAC_CTRL, 0x00); if (retval) goto out; /* Turn off master power. */ retval = snd_at73c213_write_reg(chip, DAC_PRECH, 0x00); if (retval) goto out;out: /* Stop DAC master clock. */ clk_disable(chip->board->dac_clk); ssc_free(chip->ssc); snd_card_free(card); dev_set_drvdata(&spi->dev, NULL); return 0;}#ifdef CONFIG_PMstatic int snd_at73c213_suspend(struct spi_device *spi, pm_message_t msg){ struct snd_card *card = dev_get_drvdata(&spi->dev); struct snd_at73c213 *chip = card->private_data; ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXDIS)); clk_disable(chip->board->dac_clk); return 0;}static int snd_at73c213_resume(struct spi_device *spi){ struct snd_card *card = dev_get_drvdata(&spi->dev); struct snd_at73c213 *chip = card->private_data; clk_enable(chip->board->dac_clk); ssc_writel(chip->ssc->regs, CR, SSC_BIT(CR_TXEN)); return 0;}#else#define snd_at73c213_suspend NULL#define snd_at73c213_resume NULL#endifstatic struct spi_driver at73c213_driver = { .driver = { .name = "at73c213", }, .probe = snd_at73c213_probe, .suspend = snd_at73c213_suspend, .resume = snd_at73c213_resume, .remove = __devexit_p(snd_at73c213_remove),};static int __init at73c213_init(void){ return spi_register_driver(&at73c213_driver);}module_init(at73c213_init);static void __exit at73c213_exit(void){ spi_unregister_driver(&at73c213_driver);}module_exit(at73c213_exit);MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>");MODULE_DESCRIPTION("Sound driver for AT73C213 with Atmel SSC");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -