Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | /* ********************************************************************** * ecard.c - E-card initialization code * Copyright 1999, 2000 Creative Labs, Inc. * ********************************************************************** * * Date Author Summary of changes * ---- ------ ------------------ * October 20, 1999 Bertrand Lee base code release * ********************************************************************** * * 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., 675 Mass Ave, Cambridge, MA 02139, * USA. * ********************************************************************** */ #include "ecard.h" #include "hwaccess.h" /* Private routines */ static void ecard_setadcgain(struct emu10k1_card *, struct ecard_state *, u16); static void ecard_write(struct emu10k1_card *, u32); /************************************************************************** * @func Set the gain of the ECARD's CS3310 Trim/gain controller. The * trim value consists of a 16bit value which is composed of two * 8 bit gain/trim values, one for the left channel and one for the * right channel. The following table maps from the Gain/Attenuation * value in decibels into the corresponding bit pattern for a single * channel. */ static void ecard_setadcgain(struct emu10k1_card *card, struct ecard_state *ecard, u16 gain) { u32 currbit; ecard->adc_gain = gain; /* Enable writing to the TRIM registers */ ecard_write(card, ecard->control_bits & ~EC_TRIM_CSN); /* Do it again to insure that we meet hold time requirements */ ecard_write(card, ecard->control_bits & ~EC_TRIM_CSN); for (currbit = (1L << 15); currbit; currbit >>= 1) { u32 value = ecard->control_bits & ~(EC_TRIM_CSN|EC_TRIM_SDATA); if (gain & currbit) value |= EC_TRIM_SDATA; /* Clock the bit */ ecard_write(card, value); ecard_write(card, value | EC_TRIM_SCLK); ecard_write(card, value); } ecard_write(card, ecard->control_bits); } /************************************************************************** * @func Clock bits into the Ecard's control latch. The Ecard uses a * control latch will is loaded bit-serially by toggling the Modem control * lines from function 2 on the E8010. This function hides these details * and presents the illusion that we are actually writing to a distinct * register. */ static void ecard_write(struct emu10k1_card *card, u32 value) { u16 count; u32 data, hcvalue; unsigned long flags; spin_lock_irqsave(&card->lock, flags); hcvalue = inl(card->iobase + HCFG) & ~(HOOKN_BIT|HANDN_BIT|PULSEN_BIT); outl(card->iobase + HCFG, hcvalue); for (count = 0 ; count < EC_NUM_CONTROL_BITS; count++) { /* Set up the value */ data = ((value & 0x1) ? PULSEN_BIT : 0); value >>= 1; outl(card->iobase + HCFG, hcvalue | data); /* Clock the shift register */ outl(card->iobase + HCFG, hcvalue | data | HANDN_BIT); outl(card->iobase + HCFG, hcvalue | data); } /* Latch the bits */ outl(card->iobase + HCFG, hcvalue | HOOKN_BIT); outl(card->iobase + HCFG, hcvalue); spin_unlock_irqrestore(&card->lock, flags); } void __devinit emu10k1_ecard_init(struct emu10k1_card *card) { u32 hcvalue; struct ecard_state ecard; /* Set up the initial settings */ ecard.mux0_setting = EC_DEFAULT_SPDIF0_SEL; ecard.mux1_setting = EC_DEFAULT_SPDIF1_SEL; ecard.mux2_setting = 0; ecard.adc_gain = EC_DEFAULT_ADC_GAIN; ecard.control_bits = EC_RAW_RUN_MODE | EC_SPDIF0_SELECT(ecard.mux0_setting) | EC_SPDIF1_SELECT(ecard.mux1_setting); /* Step 0: Set the codec type in the hardware control register * and enable audio output */ hcvalue = emu10k1_readfn0(card, HCFG); emu10k1_writefn0(card, HCFG, hcvalue | HCFG_AUDIOENABLE | HCFG_CODECFORMAT_I2S); /* Step 1: Turn off the led and deassert TRIM_CS */ ecard_write(card, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN); /* Step 2: Calibrate the ADC and DAC */ ecard_write(card, EC_DACCAL | EC_LEDN | EC_TRIM_CSN); /* Step 3: Wait for awhile; FIXME: Is this correct? */ current->state = TASK_INTERRUPTIBLE; schedule_timeout(HZ); /* Step 4: Switch off the DAC and ADC calibration. Note * That ADC_CAL is actually an inverted signal, so we assert * it here to stop calibration. */ ecard_write(card, EC_ADCCAL | EC_LEDN | EC_TRIM_CSN); /* Step 4: Switch into run mode */ ecard_write(card, ecard.control_bits); /* Step 5: Set the analog input gain */ ecard_setadcgain(card, &ecard, ecard.adc_gain); } |