📄 cs4236_lib.c
字号:
/* * Copyright (c) by Jaroslav Kysela <perex@suse.cz> * Routines for control of CS4235/4236B/4237B/4238B/4239 chips * * Note: * ----- * * Bugs: * ----- * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * *//* * Indirect control registers (CS4236B+) * * C0 * D8: WSS reset (all chips) * * C1 (all chips except CS4236) * D7-D5: version * D4-D0: chip id * 11101 - CS4235 * 01011 - CS4236B * 01000 - CS4237B * 01001 - CS4238B * 11110 - CS4239 * * C2 * D7-D4: 3D Space (CS4235,CS4237B,CS4238B,CS4239) * D3-D0: 3D Center (CS4237B); 3D Volume (CS4238B) * * C3 * D7: 3D Enable (CS4237B) * D6: 3D Mono Enable (CS4237B) * D5: 3D Serial Output (CS4237B,CS4238B) * D4: 3D Enable (CS4235,CS4238B,CS4239) * * C4 * D7: consumer serial port enable (CS4237B,CS4238B) * D6: channels status block reset (CS4237B,CS4238B) * D5: user bit in sub-frame of digital audio data (CS4237B,CS4238B) * D4: validity bit bit in sub-frame of digital audio data (CS4237B,CS4238B) * * C5 lower channel status (digital serial data description) (CS4237B,CS4238B) * D7-D6: first two bits of category code * D5: lock * D4-D3: pre-emphasis (0 = none, 1 = 50/15us) * D2: copy/copyright (0 = copy inhibited) * D1: 0 = digital audio / 1 = non-digital audio * * C6 upper channel status (digital serial data description) (CS4237B,CS4238B) * D7-D6: sample frequency (0 = 44.1kHz) * D5: generation status (0 = no indication, 1 = original/commercially precaptureed data) * D4-D0: category code (upper bits) * * C7 reserved (must write 0) * * C8 wavetable control * D7: volume control interrupt enable (CS4235,CS4239) * D6: hardware volume control format (CS4235,CS4239) * D3: wavetable serial port enable (all chips) * D2: DSP serial port switch (all chips) * D1: disable MCLK (all chips) * D0: force BRESET low (all chips) * */#include <sound/driver.h>#include <asm/io.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/time.h>#include <linux/wait.h>#include <sound/core.h>#include <sound/cs4231.h>#include <sound/asoundef.h>MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");MODULE_DESCRIPTION("Routines for control of CS4235/4236B/4237B/4238B/4239 chips");MODULE_LICENSE("GPL");/* * */static unsigned char snd_cs4236_ext_map[18] = { /* CS4236_LEFT_LINE */ 0xff, /* CS4236_RIGHT_LINE */ 0xff, /* CS4236_LEFT_MIC */ 0xdf, /* CS4236_RIGHT_MIC */ 0xdf, /* CS4236_LEFT_MIX_CTRL */ 0xe0 | 0x18, /* CS4236_RIGHT_MIX_CTRL */ 0xe0, /* CS4236_LEFT_FM */ 0xbf, /* CS4236_RIGHT_FM */ 0xbf, /* CS4236_LEFT_DSP */ 0xbf, /* CS4236_RIGHT_DSP */ 0xbf, /* CS4236_RIGHT_LOOPBACK */ 0xbf, /* CS4236_DAC_MUTE */ 0xe0, /* CS4236_ADC_RATE */ 0x01, /* 48kHz */ /* CS4236_DAC_RATE */ 0x01, /* 48kHz */ /* CS4236_LEFT_MASTER */ 0xbf, /* CS4236_RIGHT_MASTER */ 0xbf, /* CS4236_LEFT_WAVE */ 0xbf, /* CS4236_RIGHT_WAVE */ 0xbf};/* * */static void snd_cs4236_ctrl_out(cs4231_t *chip, unsigned char reg, unsigned char val){ outb(reg, chip->cport + 3); outb(chip->cimage[reg] = val, chip->cport + 4);}static unsigned char snd_cs4236_ctrl_in(cs4231_t *chip, unsigned char reg){ outb(reg, chip->cport + 3); return inb(chip->cport + 4);}/* * PCM */#define CLOCKS 8static ratnum_t clocks[CLOCKS] = { { .num = 16934400, .den_min = 353, .den_max = 353, .den_step = 1 }, { .num = 16934400, .den_min = 529, .den_max = 529, .den_step = 1 }, { .num = 16934400, .den_min = 617, .den_max = 617, .den_step = 1 }, { .num = 16934400, .den_min = 1058, .den_max = 1058, .den_step = 1 }, { .num = 16934400, .den_min = 1764, .den_max = 1764, .den_step = 1 }, { .num = 16934400, .den_min = 2117, .den_max = 2117, .den_step = 1 }, { .num = 16934400, .den_min = 2558, .den_max = 2558, .den_step = 1 }, { .num = 16934400/16, .den_min = 21, .den_max = 192, .den_step = 1 }};static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks = { .nrats = CLOCKS, .rats = clocks,};static int snd_cs4236_xrate(snd_pcm_runtime_t *runtime){ return snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_clocks);}static unsigned char divisor_to_rate_register(unsigned int divisor){ switch (divisor) { case 353: return 1; case 529: return 2; case 617: return 3; case 1058: return 4; case 1764: return 5; case 2117: return 6; case 2558: return 7; default: if (divisor < 21 || divisor > 192) { snd_BUG(); return 192; } return divisor; }}static void snd_cs4236_playback_format(cs4231_t *chip, snd_pcm_hw_params_t *params, unsigned char pdfr){ unsigned long flags; unsigned char rate = divisor_to_rate_register(params->rate_den); spin_lock_irqsave(&chip->reg_lock, flags); /* set fast playback format change and clean playback FIFO */ snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x10); snd_cs4231_out(chip, CS4231_PLAYBK_FORMAT, pdfr & 0xf0); snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] & ~0x10); snd_cs4236_ext_out(chip, CS4236_DAC_RATE, rate); spin_unlock_irqrestore(&chip->reg_lock, flags);}static void snd_cs4236_capture_format(cs4231_t *chip, snd_pcm_hw_params_t *params, unsigned char cdfr){ unsigned long flags; unsigned char rate = divisor_to_rate_register(params->rate_den); spin_lock_irqsave(&chip->reg_lock, flags); /* set fast capture format change and clean capture FIFO */ snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] | 0x20); snd_cs4231_out(chip, CS4231_REC_FORMAT, cdfr & 0xf0); snd_cs4231_out(chip, CS4231_ALT_FEATURE_1, chip->image[CS4231_ALT_FEATURE_1] & ~0x20); snd_cs4236_ext_out(chip, CS4236_ADC_RATE, rate); spin_unlock_irqrestore(&chip->reg_lock, flags);}#ifdef CONFIG_PMstatic void snd_cs4236_suspend(cs4231_t *chip){ int reg; unsigned long flags; spin_lock_irqsave(&chip->reg_lock, flags); for (reg = 0; reg < 32; reg++) chip->image[reg] = snd_cs4231_in(chip, reg); for (reg = 0; reg < 18; reg++) chip->eimage[reg] = snd_cs4236_ext_in(chip, CS4236_I23VAL(reg)); for (reg = 2; reg < 9; reg++) chip->cimage[reg] = snd_cs4236_ctrl_in(chip, reg); spin_unlock_irqrestore(&chip->reg_lock, flags);}static void snd_cs4236_resume(cs4231_t *chip){ int reg; unsigned long flags; snd_cs4231_mce_up(chip); spin_lock_irqsave(&chip->reg_lock, flags); for (reg = 0; reg < 32; reg++) { switch (reg) { case CS4236_EXT_REG: case CS4231_VERSION: case 27: /* why? CS4235 - master left */ case 29: /* why? CS4235 - master right */ break; default: snd_cs4231_out(chip, reg, chip->image[reg]); break; } } for (reg = 0; reg < 18; reg++) snd_cs4236_ext_out(chip, CS4236_I23VAL(reg), chip->eimage[reg]); for (reg = 2; reg < 9; reg++) { switch (reg) { case 7: break; default: snd_cs4236_ctrl_out(chip, reg, chip->cimage[reg]); } } spin_unlock_irqrestore(&chip->reg_lock, flags); snd_cs4231_mce_down(chip);}#endif /* CONFIG_PM */int snd_cs4236_create(snd_card_t * card, unsigned long port, unsigned long cport, int irq, int dma1, int dma2, unsigned short hardware, unsigned short hwshare, cs4231_t ** rchip){ cs4231_t *chip; unsigned char ver1, ver2; unsigned int reg; int err; *rchip = NULL; if (hardware == CS4231_HW_DETECT) hardware = CS4231_HW_DETECT3; if (cport < 0x100) { snd_printk("please, specify control port for CS4236+ chips\n"); return -ENODEV; } if ((err = snd_cs4231_create(card, port, cport, irq, dma1, dma2, hardware, hwshare, &chip)) < 0) return err; if (!(chip->hardware & CS4231_HW_CS4236B_MASK)) { snd_printk("CS4236+: MODE3 and extended registers not available, hardware=0x%x\n",chip->hardware); snd_device_free(card, chip); return -ENODEV; }#if 0 { int idx; for (idx = 0; idx < 8; idx++) snd_printk("CD%i = 0x%x\n", idx, inb(chip->cport + idx)); for (idx = 0; idx < 9; idx++) snd_printk("C%i = 0x%x\n", idx, snd_cs4236_ctrl_in(chip, idx)); }#endif ver1 = snd_cs4236_ctrl_in(chip, 1); ver2 = snd_cs4236_ext_in(chip, CS4236_VERSION); snd_printdd("CS4236: [0x%lx] C1 (version) = 0x%x, ext = 0x%x\n", cport, ver1, ver2); if (ver1 != ver2) { snd_printk("CS4236+ chip detected, but control port 0x%lx is not valid\n", cport); snd_device_free(card, chip); return -ENODEV; } snd_cs4236_ctrl_out(chip, 0, 0x00); snd_cs4236_ctrl_out(chip, 2, 0xff); snd_cs4236_ctrl_out(chip, 3, 0x00); snd_cs4236_ctrl_out(chip, 4, 0x80); snd_cs4236_ctrl_out(chip, 5, ((IEC958_AES1_CON_PCM_CODER & 3) << 6) | IEC958_AES0_CON_EMPHASIS_NONE); snd_cs4236_ctrl_out(chip, 6, IEC958_AES1_CON_PCM_CODER >> 2); snd_cs4236_ctrl_out(chip, 7, 0x00); /* 0x8c for C8 is valid for Turtle Beach Malibu - the IEC-958 output */ /* is working with this setup, other hardware should have */ /* different signal paths and this value should be selectable */ /* in the future */ snd_cs4236_ctrl_out(chip, 8, 0x8c); chip->rate_constraint = snd_cs4236_xrate; chip->set_playback_format = snd_cs4236_playback_format; chip->set_capture_format = snd_cs4236_capture_format;#ifdef CONFIG_PM chip->suspend = snd_cs4236_suspend;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -