xref: /minix3/minix/drivers/audio/es1371/AC97.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
1*433d6423SLionel Sambuc #include "AC97.h"
2*433d6423SLionel Sambuc 
3*433d6423SLionel Sambuc 
4*433d6423SLionel Sambuc 
5*433d6423SLionel Sambuc 
6*433d6423SLionel Sambuc 
7*433d6423SLionel Sambuc /* AC97 Mixer and Mode control function prototypes */
8*433d6423SLionel Sambuc 
9*433d6423SLionel Sambuc static int AC97_write(const DEV_STRUCT * pCC, u16_t wAddr, u16_t
10*433d6423SLionel Sambuc 	wData);
11*433d6423SLionel Sambuc static int AC97_write_unsynced(const DEV_STRUCT * pCC, u16_t wAddr,
12*433d6423SLionel Sambuc 	u16_t wData);
13*433d6423SLionel Sambuc static int AC97_read_unsynced(const DEV_STRUCT * pCC, u16_t wAddr,
14*433d6423SLionel Sambuc 	u16_t *data);
15*433d6423SLionel Sambuc static void set_nice_volume(void);
16*433d6423SLionel Sambuc static int AC97_get_volume(struct volume_level *level);
17*433d6423SLionel Sambuc static int AC97_set_volume(const struct volume_level *level);
18*433d6423SLionel Sambuc 
19*433d6423SLionel Sambuc 
20*433d6423SLionel Sambuc 
21*433d6423SLionel Sambuc #define AC97_0DB_GAIN          0x0008
22*433d6423SLionel Sambuc #define AC97_MAX_ATTN          0x003f
23*433d6423SLionel Sambuc #define AC97_MUTE              0x8000U
24*433d6423SLionel Sambuc 
25*433d6423SLionel Sambuc 
26*433d6423SLionel Sambuc /* Control function defines */
27*433d6423SLionel Sambuc #define AC97_CTL_4SPKR         0x00U   /* 4-spkr output mode enable */
28*433d6423SLionel Sambuc #define AC97_CTL_MICBOOST      0x01U   /* Mic boost (+30 dB) enable */
29*433d6423SLionel Sambuc #define AC97_CTL_PWRDOWN       0x02U   /* power-down mode */
30*433d6423SLionel Sambuc #define AC97_CTL_DOSMODE       0x03U   /* A/D sync to DAC1 */
31*433d6423SLionel Sambuc 
32*433d6423SLionel Sambuc                                                 /* Timeout waiting for: */
33*433d6423SLionel Sambuc #define AC97_ERR_WIP_TIMEOUT           -1      /* write in progress complete */
34*433d6423SLionel Sambuc #define AC97_ERR_DATA_TIMEOUT          -2      /* data ready */
35*433d6423SLionel Sambuc #define AC97_ERR_SRC_NOT_BUSY_TIMEOUT  -3      /* SRC not busy */
36*433d6423SLionel Sambuc #define AC97_ERR_SRC_SYNC_TIMEOUT      -4      /* state #1 */
37*433d6423SLionel Sambuc 
38*433d6423SLionel Sambuc 
39*433d6423SLionel Sambuc 
40*433d6423SLionel Sambuc 
41*433d6423SLionel Sambuc 
42*433d6423SLionel Sambuc 
43*433d6423SLionel Sambuc /* Timeouts in milliseconds */
44*433d6423SLionel Sambuc #define WIP_TIMEOUT     250UL
45*433d6423SLionel Sambuc #define DRDY_TIMEOUT    250UL
46*433d6423SLionel Sambuc 
47*433d6423SLionel Sambuc /* The default SRC syncronization state number is 1.  This state occurs
48*433d6423SLionel Sambuc    just after de-assertion of SYNC.  This is supposed to be the safest
49*433d6423SLionel Sambuc    state for accessing the codec with an ES1371 Rev 1.  Later versions
50*433d6423SLionel Sambuc    of the chip allegedly don't require syncronization.  Be very careful
51*433d6423SLionel Sambuc    if you change this ! */
52*433d6423SLionel Sambuc 
53*433d6423SLionel Sambuc #define SRC_UNSYNCED 0xffffffffUL
54*433d6423SLionel Sambuc static u32_t SrcSyncState = 0x00010000UL;
55*433d6423SLionel Sambuc static DEV_STRUCT *dev;
56*433d6423SLionel Sambuc 
57*433d6423SLionel Sambuc 
58*433d6423SLionel Sambuc #if 0
59*433d6423SLionel Sambuc static void set_src_sync_state (int state)
60*433d6423SLionel Sambuc {
61*433d6423SLionel Sambuc     if (state < 0)
62*433d6423SLionel Sambuc         SrcSyncState = SRC_UNSYNCED;
63*433d6423SLionel Sambuc     else {
64*433d6423SLionel Sambuc         SrcSyncState = (u32_t)state << 16;
65*433d6423SLionel Sambuc         SrcSyncState &= 0x00070000Ul;
66*433d6423SLionel Sambuc     }
67*433d6423SLionel Sambuc }
68*433d6423SLionel Sambuc #endif
69*433d6423SLionel Sambuc 
70*433d6423SLionel Sambuc 
AC97_write(const DEV_STRUCT * pCC,u16_t wAddr,u16_t wData)71*433d6423SLionel Sambuc static int AC97_write (const DEV_STRUCT * pCC, u16_t wAddr, u16_t wData)
72*433d6423SLionel Sambuc {
73*433d6423SLionel Sambuc u32_t dtemp, i;
74*433d6423SLionel Sambuc u16_t  wBaseAddr = pCC->base;
75*433d6423SLionel Sambuc 
76*433d6423SLionel Sambuc     /* wait for WIP bit (Write In Progress) to go away */
77*433d6423SLionel Sambuc     /* remember, register CODEC_READ (0x14)
78*433d6423SLionel Sambuc        is a pseudo read-write register */
79*433d6423SLionel Sambuc     if (WaitBitd (wBaseAddr + CODEC_READ, 30, 0, WIP_TIMEOUT)){
80*433d6423SLionel Sambuc         printf("AC97_ERR_WIP_TIMEOUT\n");
81*433d6423SLionel Sambuc         return (AC97_ERR_WIP_TIMEOUT);
82*433d6423SLionel Sambuc     }
83*433d6423SLionel Sambuc     if (SRC_UNSYNCED != SrcSyncState)
84*433d6423SLionel Sambuc     {
85*433d6423SLionel Sambuc         /* enable SRC state data in SRC mux */
86*433d6423SLionel Sambuc         if (WaitBitd (wBaseAddr + SAMPLE_RATE_CONV, SRC_BUSY_BIT, 0, 1000))
87*433d6423SLionel Sambuc             return (AC97_ERR_SRC_NOT_BUSY_TIMEOUT);
88*433d6423SLionel Sambuc 
89*433d6423SLionel Sambuc         /* todo: why are we writing an undefined register? */
90*433d6423SLionel Sambuc         dtemp = pci_inl(wBaseAddr + SAMPLE_RATE_CONV);
91*433d6423SLionel Sambuc         pci_outl(wBaseAddr + SAMPLE_RATE_CONV, (dtemp & SRC_CTLMASK) |
92*433d6423SLionel Sambuc                 0x00010000UL);
93*433d6423SLionel Sambuc 
94*433d6423SLionel Sambuc         /* wait for a SAFE time to write addr/data and then do it */
95*433d6423SLionel Sambuc         /*_disable(); */
96*433d6423SLionel Sambuc         for( i = 0; i < 0x1000UL; ++i )
97*433d6423SLionel Sambuc             if( (pci_inl(wBaseAddr + SAMPLE_RATE_CONV) & 0x00070000UL) ==
98*433d6423SLionel Sambuc                     SrcSyncState )
99*433d6423SLionel Sambuc             break;
100*433d6423SLionel Sambuc 
101*433d6423SLionel Sambuc         if (i >= 0x1000UL) {
102*433d6423SLionel Sambuc             /* _enable(); */
103*433d6423SLionel Sambuc             return (AC97_ERR_SRC_SYNC_TIMEOUT);
104*433d6423SLionel Sambuc         }
105*433d6423SLionel Sambuc     }
106*433d6423SLionel Sambuc 
107*433d6423SLionel Sambuc     /* A test for 5880 - prime the PCI data bus */
108*433d6423SLionel Sambuc     {
109*433d6423SLionel Sambuc         u32_t dat = ((u32_t) wAddr << 16) | wData;
110*433d6423SLionel Sambuc         char page = pci_inb(wBaseAddr + MEM_PAGE);
111*433d6423SLionel Sambuc 
112*433d6423SLionel Sambuc         pci_outl (wBaseAddr + MEM_PAGE, dat);
113*433d6423SLionel Sambuc 
114*433d6423SLionel Sambuc         /* write addr and data */
115*433d6423SLionel Sambuc         pci_outl(wBaseAddr + CODEC_READ, dat);
116*433d6423SLionel Sambuc 
117*433d6423SLionel Sambuc         pci_outb(wBaseAddr + MEM_PAGE, page);  /* restore page reg */
118*433d6423SLionel Sambuc     }
119*433d6423SLionel Sambuc 
120*433d6423SLionel Sambuc     if (SRC_UNSYNCED != SrcSyncState)
121*433d6423SLionel Sambuc     {
122*433d6423SLionel Sambuc          /* _enable(); */
123*433d6423SLionel Sambuc 
124*433d6423SLionel Sambuc         /* restore SRC reg */
125*433d6423SLionel Sambuc         if (WaitBitd (wBaseAddr + SAMPLE_RATE_CONV, SRC_BUSY_BIT, 0, 1000))
126*433d6423SLionel Sambuc             return (AC97_ERR_SRC_NOT_BUSY_TIMEOUT);
127*433d6423SLionel Sambuc 
128*433d6423SLionel Sambuc         pci_outl(wBaseAddr + SAMPLE_RATE_CONV, dtemp & 0xfff8ffffUL);
129*433d6423SLionel Sambuc     }
130*433d6423SLionel Sambuc 
131*433d6423SLionel Sambuc     return 0;
132*433d6423SLionel Sambuc }
133*433d6423SLionel Sambuc 
134*433d6423SLionel Sambuc 
135*433d6423SLionel Sambuc #if 0
136*433d6423SLionel Sambuc static int AC97_read (const DEV_STRUCT * pCC, u16_t wAddr, u16_t *data)
137*433d6423SLionel Sambuc {
138*433d6423SLionel Sambuc u32_t dtemp, i;
139*433d6423SLionel Sambuc u16_t  base = pCC->base;
140*433d6423SLionel Sambuc 
141*433d6423SLionel Sambuc     /* wait for WIP to go away */
142*433d6423SLionel Sambuc     if (WaitBitd (base + CODEC_READ, 30, 0, WIP_TIMEOUT))
143*433d6423SLionel Sambuc         return (AC97_ERR_WIP_TIMEOUT);
144*433d6423SLionel Sambuc 
145*433d6423SLionel Sambuc     if (SRC_UNSYNCED != SrcSyncState)
146*433d6423SLionel Sambuc     {
147*433d6423SLionel Sambuc         /* enable SRC state data in SRC mux */
148*433d6423SLionel Sambuc         if (WaitBitd (base + SAMPLE_RATE_CONV, SRC_BUSY_BIT, 0, 1000))
149*433d6423SLionel Sambuc             return (AC97_ERR_SRC_NOT_BUSY_TIMEOUT);
150*433d6423SLionel Sambuc 
151*433d6423SLionel Sambuc         dtemp = pci_inl(base + SAMPLE_RATE_CONV);
152*433d6423SLionel Sambuc         pci_outl(base + SAMPLE_RATE_CONV, (dtemp & SRC_CTLMASK) |
153*433d6423SLionel Sambuc                 0x00010000UL);
154*433d6423SLionel Sambuc 
155*433d6423SLionel Sambuc         /* wait for a SAFE time to write a read request and then do it */
156*433d6423SLionel Sambuc         /* todo: how do we solve the lock() problem? */
157*433d6423SLionel Sambuc         /* _disable(); */
158*433d6423SLionel Sambuc         for( i = 0; i < 0x1000UL; ++i )
159*433d6423SLionel Sambuc             if( (pci_inl(base + SAMPLE_RATE_CONV) & 0x00070000UL) ==
160*433d6423SLionel Sambuc                     SrcSyncState )
161*433d6423SLionel Sambuc             break;
162*433d6423SLionel Sambuc 
163*433d6423SLionel Sambuc         if (i >= 0x1000UL) {
164*433d6423SLionel Sambuc             /*_enable();*/
165*433d6423SLionel Sambuc             return (AC97_ERR_SRC_SYNC_TIMEOUT);
166*433d6423SLionel Sambuc         }
167*433d6423SLionel Sambuc     }
168*433d6423SLionel Sambuc 
169*433d6423SLionel Sambuc     /* A test for 5880 - prime the PCI data bus */
170*433d6423SLionel Sambuc     {
171*433d6423SLionel Sambuc         /* set bit 23, this means read in stead of write. */
172*433d6423SLionel Sambuc         u32_t dat = ((u32_t) wAddr << 16) | (1UL << 23);
173*433d6423SLionel Sambuc         char page = pci_inb(base + MEM_PAGE);
174*433d6423SLionel Sambuc 
175*433d6423SLionel Sambuc         /* todo: why are we putting data in the mem page register??? */
176*433d6423SLionel Sambuc         pci_outl(base + MEM_PAGE, dat);
177*433d6423SLionel Sambuc 
178*433d6423SLionel Sambuc         /* write addr w/data=0 and assert read request */
179*433d6423SLionel Sambuc         pci_outl(base + CODEC_READ, dat);
180*433d6423SLionel Sambuc 
181*433d6423SLionel Sambuc         pci_outb(base + MEM_PAGE, page);  /* restore page reg */
182*433d6423SLionel Sambuc 
183*433d6423SLionel Sambuc     }
184*433d6423SLionel Sambuc     if (SRC_UNSYNCED != SrcSyncState)
185*433d6423SLionel Sambuc     {
186*433d6423SLionel Sambuc 
187*433d6423SLionel Sambuc         /*_enable();*/
188*433d6423SLionel Sambuc 
189*433d6423SLionel Sambuc         /* restore SRC reg */
190*433d6423SLionel Sambuc         if (WaitBitd (base + SAMPLE_RATE_CONV, SRC_BUSY_BIT, 0, 1000))
191*433d6423SLionel Sambuc             return (AC97_ERR_SRC_NOT_BUSY_TIMEOUT);
192*433d6423SLionel Sambuc 
193*433d6423SLionel Sambuc         pci_outl(base + SAMPLE_RATE_CONV, dtemp & 0xfff8ffffUL);
194*433d6423SLionel Sambuc     }
195*433d6423SLionel Sambuc 
196*433d6423SLionel Sambuc     /* now wait for the stinkin' data (DRDY = data ready) */
197*433d6423SLionel Sambuc     if (WaitBitd (base + CODEC_READ, 31, 1, DRDY_TIMEOUT))
198*433d6423SLionel Sambuc         return (AC97_ERR_DATA_TIMEOUT);
199*433d6423SLionel Sambuc 
200*433d6423SLionel Sambuc     dtemp = pci_inl(base + CODEC_READ);
201*433d6423SLionel Sambuc 
202*433d6423SLionel Sambuc     if (data)
203*433d6423SLionel Sambuc         *data = (u16_t) dtemp;
204*433d6423SLionel Sambuc 
205*433d6423SLionel Sambuc     return 0;
206*433d6423SLionel Sambuc }
207*433d6423SLionel Sambuc #endif
208*433d6423SLionel Sambuc 
209*433d6423SLionel Sambuc 
AC97_write_unsynced(const DEV_STRUCT * pCC,u16_t wAddr,u16_t wData)210*433d6423SLionel Sambuc static int AC97_write_unsynced (const DEV_STRUCT * pCC, u16_t wAddr,
211*433d6423SLionel Sambuc     u16_t wData)
212*433d6423SLionel Sambuc {
213*433d6423SLionel Sambuc     /* wait for WIP to go away */
214*433d6423SLionel Sambuc     if (WaitBitd (pCC->base + CODEC_READ, 30, 0, WIP_TIMEOUT))
215*433d6423SLionel Sambuc         return (AC97_ERR_WIP_TIMEOUT);
216*433d6423SLionel Sambuc 
217*433d6423SLionel Sambuc     /* write addr and data */
218*433d6423SLionel Sambuc     pci_outl(pCC->base + CODEC_READ, ((u32_t) wAddr << 16) | wData);
219*433d6423SLionel Sambuc     return 0;
220*433d6423SLionel Sambuc }
221*433d6423SLionel Sambuc 
222*433d6423SLionel Sambuc 
AC97_read_unsynced(const DEV_STRUCT * pCC,u16_t wAddr,u16_t * data)223*433d6423SLionel Sambuc static int AC97_read_unsynced (const DEV_STRUCT * pCC, u16_t wAddr,
224*433d6423SLionel Sambuc     u16_t *data)
225*433d6423SLionel Sambuc {
226*433d6423SLionel Sambuc u32_t dtemp;
227*433d6423SLionel Sambuc 
228*433d6423SLionel Sambuc     /* wait for WIP to go away */
229*433d6423SLionel Sambuc     if (WaitBitd (pCC->base + CODEC_READ, 30, 0, WIP_TIMEOUT))
230*433d6423SLionel Sambuc         return (AC97_ERR_WIP_TIMEOUT);
231*433d6423SLionel Sambuc 
232*433d6423SLionel Sambuc     /* write addr w/data=0 and assert read request */
233*433d6423SLionel Sambuc     pci_outl(pCC->base + CODEC_READ, ((u32_t) wAddr << 16) | (1UL << 23));
234*433d6423SLionel Sambuc 
235*433d6423SLionel Sambuc     /* now wait for the stinkin' data (RDY) */
236*433d6423SLionel Sambuc     if (WaitBitd (pCC->base + CODEC_READ, 31, 1, DRDY_TIMEOUT))
237*433d6423SLionel Sambuc         return (AC97_ERR_DATA_TIMEOUT);
238*433d6423SLionel Sambuc 
239*433d6423SLionel Sambuc     dtemp = pci_inl(pCC->base + CODEC_READ);
240*433d6423SLionel Sambuc 
241*433d6423SLionel Sambuc     if (data)
242*433d6423SLionel Sambuc         *data = (u16_t) dtemp;
243*433d6423SLionel Sambuc 
244*433d6423SLionel Sambuc     return 0;
245*433d6423SLionel Sambuc }
246*433d6423SLionel Sambuc 
247*433d6423SLionel Sambuc 
AC97_init(DEV_STRUCT * pCC)248*433d6423SLionel Sambuc int AC97_init( DEV_STRUCT * pCC ) {
249*433d6423SLionel Sambuc 	int retVal;
250*433d6423SLionel Sambuc     /* All powerdown modes: off */
251*433d6423SLionel Sambuc 
252*433d6423SLionel Sambuc 	dev = pCC;
253*433d6423SLionel Sambuc 
254*433d6423SLionel Sambuc     retVal = AC97_write (pCC, AC97_POWERDOWN_CONTROL_STAT,  0x0000U);
255*433d6423SLionel Sambuc     if (OK != retVal)
256*433d6423SLionel Sambuc         return (retVal);
257*433d6423SLionel Sambuc 
258*433d6423SLionel Sambuc     /* Mute Line Out & set to 0dB attenuation */
259*433d6423SLionel Sambuc 
260*433d6423SLionel Sambuc     retVal = AC97_write (pCC, AC97_MASTER_VOLUME, 0x0000U);
261*433d6423SLionel Sambuc     if (OK != retVal)
262*433d6423SLionel Sambuc         return (retVal);
263*433d6423SLionel Sambuc 
264*433d6423SLionel Sambuc 
265*433d6423SLionel Sambuc     retVal = AC97_write (pCC, AC97_MONO_VOLUME,   0x8000U);
266*433d6423SLionel Sambuc     if (OK != retVal)
267*433d6423SLionel Sambuc         return (retVal);
268*433d6423SLionel Sambuc 
269*433d6423SLionel Sambuc     retVal = AC97_write (pCC, AC97_PHONE_VOLUME,  0x8008U);
270*433d6423SLionel Sambuc     if (OK != retVal)
271*433d6423SLionel Sambuc         return (retVal);
272*433d6423SLionel Sambuc 
273*433d6423SLionel Sambuc     retVal = AC97_write (pCC, AC97_MIC_VOLUME,    0x0008U);
274*433d6423SLionel Sambuc     if (OK != retVal)
275*433d6423SLionel Sambuc         return (retVal);
276*433d6423SLionel Sambuc 
277*433d6423SLionel Sambuc     retVal = AC97_write (pCC, AC97_LINE_IN_VOLUME,   0x0808U);
278*433d6423SLionel Sambuc     if (OK != retVal)
279*433d6423SLionel Sambuc         return (retVal);
280*433d6423SLionel Sambuc 
281*433d6423SLionel Sambuc     retVal = AC97_write (pCC, AC97_CD_VOLUME,     0x0808U);
282*433d6423SLionel Sambuc     if (OK != retVal)
283*433d6423SLionel Sambuc         return (retVal);
284*433d6423SLionel Sambuc 
285*433d6423SLionel Sambuc     retVal = AC97_write (pCC, AC97_AUX_IN_VOLUME,    0x0808U);
286*433d6423SLionel Sambuc     if (OK != retVal)
287*433d6423SLionel Sambuc         return (retVal);
288*433d6423SLionel Sambuc 
289*433d6423SLionel Sambuc     retVal = AC97_write (pCC, AC97_PCM_OUT_VOLUME,    0x0808U);
290*433d6423SLionel Sambuc     if (OK != retVal)
291*433d6423SLionel Sambuc         return (retVal);
292*433d6423SLionel Sambuc 
293*433d6423SLionel Sambuc     retVal = AC97_write (pCC, AC97_RECORD_GAIN_VOLUME, 0x0000U);
294*433d6423SLionel Sambuc     if (OK != retVal)
295*433d6423SLionel Sambuc         return (retVal);
296*433d6423SLionel Sambuc 
297*433d6423SLionel Sambuc     /* Connect Line In to ADC */
298*433d6423SLionel Sambuc     retVal = AC97_write (pCC, AC97_RECORD_SELECT, 0x0404U);
299*433d6423SLionel Sambuc     if (OK != retVal)
300*433d6423SLionel Sambuc         return (retVal);
301*433d6423SLionel Sambuc 
302*433d6423SLionel Sambuc     retVal = AC97_write (pCC, AC97_GENERAL_PURPOSE, 0x0000U);
303*433d6423SLionel Sambuc     if (OK != retVal)
304*433d6423SLionel Sambuc         return (retVal);
305*433d6423SLionel Sambuc 
306*433d6423SLionel Sambuc 	set_nice_volume();
307*433d6423SLionel Sambuc 
308*433d6423SLionel Sambuc     return OK;
309*433d6423SLionel Sambuc }
310*433d6423SLionel Sambuc 
311*433d6423SLionel Sambuc 
set_nice_volume(void)312*433d6423SLionel Sambuc static void set_nice_volume(void) {
313*433d6423SLionel Sambuc   /* goofy code to set the DAC1 channel to an audibe volume
314*433d6423SLionel Sambuc      to be able to test it without using the mixer */
315*433d6423SLionel Sambuc 
316*433d6423SLionel Sambuc   AC97_write_unsynced(dev, AC97_PCM_OUT_VOLUME, 0x0808);/* the higher,
317*433d6423SLionel Sambuc 														   the softer */
318*433d6423SLionel Sambuc   AC97_write_unsynced(dev, AC97_MASTER_VOLUME, 0x0101);
319*433d6423SLionel Sambuc   AC97_write_unsynced(dev, 0x38, 0);                    /* not crucial */
320*433d6423SLionel Sambuc 
321*433d6423SLionel Sambuc   AC97_write_unsynced(dev, AC97_LINE_IN_VOLUME, 0x0303);
322*433d6423SLionel Sambuc   AC97_write_unsynced(dev, AC97_MIC_VOLUME, 0x005f);
323*433d6423SLionel Sambuc 
324*433d6423SLionel Sambuc   /* mute record gain */
325*433d6423SLionel Sambuc   AC97_write_unsynced(dev, AC97_RECORD_GAIN_VOLUME, 0xFFFF);
326*433d6423SLionel Sambuc   /* mic record volume high */
327*433d6423SLionel Sambuc   AC97_write_unsynced(dev, AC97_RECORD_GAIN_MIC_VOL, 0x0000);
328*433d6423SLionel Sambuc 
329*433d6423SLionel Sambuc    /* Also, to be able test recording without mixer:
330*433d6423SLionel Sambuc      select ONE channel as input below. */
331*433d6423SLionel Sambuc 
332*433d6423SLionel Sambuc   /* select LINE IN */
333*433d6423SLionel Sambuc   /*AC97_write_unsynced(dev, AC97_RECORD_SELECT, 0x0404);*/
334*433d6423SLionel Sambuc 
335*433d6423SLionel Sambuc   /* select MIC */
336*433d6423SLionel Sambuc   AC97_write_unsynced(dev, AC97_RECORD_SELECT, 0x0000);
337*433d6423SLionel Sambuc 
338*433d6423SLionel Sambuc   /* unmute record gain */
339*433d6423SLionel Sambuc   AC97_write_unsynced(dev, AC97_RECORD_GAIN_VOLUME, 0x0000);
340*433d6423SLionel Sambuc }
341*433d6423SLionel Sambuc 
342*433d6423SLionel Sambuc 
get_volume(u8_t * left,u8_t * right,int cmd)343*433d6423SLionel Sambuc static int get_volume(u8_t *left, u8_t *right, int cmd) {
344*433d6423SLionel Sambuc 	u16_t value = 0;
345*433d6423SLionel Sambuc 
346*433d6423SLionel Sambuc 	AC97_read_unsynced(dev, (u16_t)cmd, &value);
347*433d6423SLionel Sambuc 
348*433d6423SLionel Sambuc 	*left = value>>8;
349*433d6423SLionel Sambuc 	*right = value&0xff;
350*433d6423SLionel Sambuc 
351*433d6423SLionel Sambuc 	return OK;
352*433d6423SLionel Sambuc }
353*433d6423SLionel Sambuc 
354*433d6423SLionel Sambuc 
set_volume(int left,int right,int cmd)355*433d6423SLionel Sambuc static int set_volume(int left, int right, int cmd) {
356*433d6423SLionel Sambuc 	u16_t waarde;
357*433d6423SLionel Sambuc 
358*433d6423SLionel Sambuc 	waarde = (u16_t)((left<<8)|right);
359*433d6423SLionel Sambuc 
360*433d6423SLionel Sambuc 	AC97_write_unsynced(dev, (u16_t)cmd, waarde);
361*433d6423SLionel Sambuc 
362*433d6423SLionel Sambuc 	return OK;
363*433d6423SLionel Sambuc }
364*433d6423SLionel Sambuc 
365*433d6423SLionel Sambuc 
convert(int left_in,int right_in,int max_in,int * left_out,int * right_out,int max_out,int swaplr)366*433d6423SLionel Sambuc void convert(int left_in, int right_in, int max_in, int *left_out,
367*433d6423SLionel Sambuc 		int *right_out, int max_out, int swaplr) {
368*433d6423SLionel Sambuc 	int tmp;
369*433d6423SLionel Sambuc 
370*433d6423SLionel Sambuc 	if(left_in < 0) left_in = 0;
371*433d6423SLionel Sambuc 	else if(left_in > max_in) left_in = max_in;
372*433d6423SLionel Sambuc 	if(right_in < 0) right_in = 0;
373*433d6423SLionel Sambuc 	else if(right_in > max_in) right_in = max_in;
374*433d6423SLionel Sambuc 
375*433d6423SLionel Sambuc 	if (swaplr) {
376*433d6423SLionel Sambuc 		tmp = left_in;
377*433d6423SLionel Sambuc 		left_in = right_in;
378*433d6423SLionel Sambuc 		right_in = tmp;
379*433d6423SLionel Sambuc 	}
380*433d6423SLionel Sambuc 
381*433d6423SLionel Sambuc 	*left_out = (-left_in) + max_out;
382*433d6423SLionel Sambuc 	*right_out = (-right_in) + max_out;
383*433d6423SLionel Sambuc }
384*433d6423SLionel Sambuc 
385*433d6423SLionel Sambuc 
AC97_get_set_volume(struct volume_level * level,int flag)386*433d6423SLionel Sambuc int AC97_get_set_volume(struct volume_level *level, int flag) {
387*433d6423SLionel Sambuc 	if (flag) {
388*433d6423SLionel Sambuc 		return AC97_set_volume(level);
389*433d6423SLionel Sambuc 	}
390*433d6423SLionel Sambuc 	else {
391*433d6423SLionel Sambuc 		return AC97_get_volume(level);
392*433d6423SLionel Sambuc 	}
393*433d6423SLionel Sambuc }
394*433d6423SLionel Sambuc 
395*433d6423SLionel Sambuc 
AC97_get_volume(struct volume_level * level)396*433d6423SLionel Sambuc static int AC97_get_volume(struct volume_level *level) {
397*433d6423SLionel Sambuc 	int cmd;
398*433d6423SLionel Sambuc 	u8_t left;
399*433d6423SLionel Sambuc 	u8_t right;
400*433d6423SLionel Sambuc 
401*433d6423SLionel Sambuc 	switch(level->device) {
402*433d6423SLionel Sambuc 		case Master:
403*433d6423SLionel Sambuc 			cmd = AC97_MASTER_VOLUME;
404*433d6423SLionel Sambuc 			get_volume(&left, &right, cmd);
405*433d6423SLionel Sambuc 			convert(left, right, 0x1f,
406*433d6423SLionel Sambuc 					&(level->left), &(level->right), 0x1f, 0);
407*433d6423SLionel Sambuc 			break;
408*433d6423SLionel Sambuc 		case Dac:
409*433d6423SLionel Sambuc 			return EINVAL;
410*433d6423SLionel Sambuc 			break;
411*433d6423SLionel Sambuc 		case Fm:
412*433d6423SLionel Sambuc 			cmd = AC97_PCM_OUT_VOLUME;
413*433d6423SLionel Sambuc 			get_volume(&left, &right, cmd);
414*433d6423SLionel Sambuc 			convert(left, right, 0x1f,
415*433d6423SLionel Sambuc 					&(level->left), &(level->right), 0x1f, 0);
416*433d6423SLionel Sambuc 			break;
417*433d6423SLionel Sambuc 		case Cd:
418*433d6423SLionel Sambuc 			cmd = AC97_CD_VOLUME;
419*433d6423SLionel Sambuc 			get_volume(&left, &right, cmd);
420*433d6423SLionel Sambuc 			convert(left, right, 0x1f,
421*433d6423SLionel Sambuc 					&(level->left), &(level->right), 0x1f, 0);
422*433d6423SLionel Sambuc 			break;
423*433d6423SLionel Sambuc 		case Line:
424*433d6423SLionel Sambuc 			cmd = AC97_LINE_IN_VOLUME;
425*433d6423SLionel Sambuc 			get_volume(&left, &right, cmd);
426*433d6423SLionel Sambuc 			convert(left, right, 0x1f,
427*433d6423SLionel Sambuc 					&(level->left), &(level->right), 0x1f, 0);
428*433d6423SLionel Sambuc 			break;
429*433d6423SLionel Sambuc 		case Mic:
430*433d6423SLionel Sambuc 			cmd = AC97_MIC_VOLUME;
431*433d6423SLionel Sambuc 			get_volume(&left, &right, cmd);
432*433d6423SLionel Sambuc 			convert(left, right, 0x1f,
433*433d6423SLionel Sambuc 					&(level->left), &(level->right), 0x1f, 1);
434*433d6423SLionel Sambuc 			break;
435*433d6423SLionel Sambuc 		case Speaker:
436*433d6423SLionel Sambuc 			return EINVAL;
437*433d6423SLionel Sambuc 		case Treble:
438*433d6423SLionel Sambuc 			cmd = AC97_MASTER_TONE;
439*433d6423SLionel Sambuc 			get_volume(&left, &right, cmd);
440*433d6423SLionel Sambuc 			convert(left, right, 0xf,
441*433d6423SLionel Sambuc 					&(level->left), &(level->right), 0xf, 1);
442*433d6423SLionel Sambuc 			break;
443*433d6423SLionel Sambuc 		case Bass:
444*433d6423SLionel Sambuc 			cmd = AC97_MASTER_TONE;
445*433d6423SLionel Sambuc 			get_volume(&left, &right, cmd);
446*433d6423SLionel Sambuc 			convert(left, right, 0xf,
447*433d6423SLionel Sambuc 					&(level->left), &(level->right), 0xf, 1);
448*433d6423SLionel Sambuc 			break;
449*433d6423SLionel Sambuc 		default:
450*433d6423SLionel Sambuc 			return EINVAL;
451*433d6423SLionel Sambuc 	}
452*433d6423SLionel Sambuc 	return OK;
453*433d6423SLionel Sambuc }
454*433d6423SLionel Sambuc 
455*433d6423SLionel Sambuc 
AC97_set_volume(const struct volume_level * level)456*433d6423SLionel Sambuc static int AC97_set_volume(const struct volume_level *level) {
457*433d6423SLionel Sambuc 	int cmd;
458*433d6423SLionel Sambuc 	int left;
459*433d6423SLionel Sambuc 	int right;
460*433d6423SLionel Sambuc 
461*433d6423SLionel Sambuc 	switch(level->device) {
462*433d6423SLionel Sambuc 		case Master:
463*433d6423SLionel Sambuc 			cmd = AC97_MASTER_VOLUME;
464*433d6423SLionel Sambuc 			convert(level->left, level->right, 0x1f, &left, &right, 0x1f, 0);
465*433d6423SLionel Sambuc 			break;
466*433d6423SLionel Sambuc 		case Dac:
467*433d6423SLionel Sambuc 			return EINVAL;
468*433d6423SLionel Sambuc 		case Fm:
469*433d6423SLionel Sambuc 			cmd = AC97_PCM_OUT_VOLUME;
470*433d6423SLionel Sambuc 			convert(level->left, level->right, 0x1f, &left, &right, 0x1f, 0);
471*433d6423SLionel Sambuc 			break;
472*433d6423SLionel Sambuc 		case Cd:
473*433d6423SLionel Sambuc 			cmd = AC97_CD_VOLUME;
474*433d6423SLionel Sambuc 			convert(level->left, level->right, 0x1f, &left, &right, 0x1f, 0);
475*433d6423SLionel Sambuc 			break;
476*433d6423SLionel Sambuc 		case Line:
477*433d6423SLionel Sambuc 			cmd = AC97_LINE_IN_VOLUME;
478*433d6423SLionel Sambuc 			convert(level->left, level->right, 0x1f, &left, &right, 0x1f, 0);
479*433d6423SLionel Sambuc 			break;
480*433d6423SLionel Sambuc 		case Mic:
481*433d6423SLionel Sambuc 			cmd = AC97_MIC_VOLUME;
482*433d6423SLionel Sambuc 			convert(level->left, level->right, 0x1f, &left, &right, 0x1f, 1);
483*433d6423SLionel Sambuc 			break;
484*433d6423SLionel Sambuc 		case Speaker:
485*433d6423SLionel Sambuc 			return EINVAL;
486*433d6423SLionel Sambuc 		case Treble:
487*433d6423SLionel Sambuc 			return EINVAL;
488*433d6423SLionel Sambuc 		case Bass:
489*433d6423SLionel Sambuc 			return EINVAL;
490*433d6423SLionel Sambuc 		default:
491*433d6423SLionel Sambuc 			return EINVAL;
492*433d6423SLionel Sambuc 	}
493*433d6423SLionel Sambuc 	set_volume(left, right, cmd);
494*433d6423SLionel Sambuc 
495*433d6423SLionel Sambuc 	return OK;
496*433d6423SLionel Sambuc }
497