aboutsummaryrefslogtreecommitdiffstats
path: root/sound/oss/emu10k1/ecard.c
blob: 4ae635fe140230b6952de32b40d46d929ccee3e8 (plain) (blame)
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);
}