diff options
Diffstat (limited to 'firmware/drivers/audio/tlv320.c')
-rw-r--r-- | firmware/drivers/audio/tlv320.c | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/firmware/drivers/audio/tlv320.c b/firmware/drivers/audio/tlv320.c new file mode 100644 index 0000000000..3e27d22817 --- /dev/null +++ b/firmware/drivers/audio/tlv320.c | |||
@@ -0,0 +1,305 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2005 by Christian Gmeiner | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | #include "lcd.h" | ||
20 | #include "cpu.h" | ||
21 | #include "kernel.h" | ||
22 | #include "thread.h" | ||
23 | #include "power.h" | ||
24 | #include "logf.h" | ||
25 | #include "system.h" | ||
26 | #include "sprintf.h" | ||
27 | #include "button.h" | ||
28 | #include "string.h" | ||
29 | #include "file.h" | ||
30 | #include "buffer.h" | ||
31 | #include "audio.h" | ||
32 | |||
33 | #include "i2c-coldfire.h" | ||
34 | #include "tlv320.h" | ||
35 | |||
36 | /* convert tenth of dB volume (-840..0) to master volume register value */ | ||
37 | int tenthdb2master(int db) | ||
38 | { | ||
39 | /* +6 to -73dB 1dB steps (plus mute == 80levels) 7bits */ | ||
40 | /* 1111111 == +6dB (0x7f) */ | ||
41 | /* 1111001 == 0dB (0x79) */ | ||
42 | /* 0110000 == -73dB (0x30 */ | ||
43 | /* 0101111 == mute (0x2f) */ | ||
44 | |||
45 | if (db < VOLUME_MIN) { | ||
46 | return 0x2f; | ||
47 | } else { | ||
48 | return((db/10)+73+0x30); | ||
49 | } | ||
50 | } | ||
51 | |||
52 | /* local functions and definations */ | ||
53 | #define TLV320_ADDR 0x34 | ||
54 | |||
55 | struct tlv320_info | ||
56 | { | ||
57 | int vol_l; | ||
58 | int vol_r; | ||
59 | } tlv320; | ||
60 | |||
61 | /* Shadow registers */ | ||
62 | unsigned tlv320_regs[0xf]; | ||
63 | |||
64 | void tlv320_write_reg(unsigned reg, unsigned value) | ||
65 | { | ||
66 | unsigned char data[2]; | ||
67 | |||
68 | /* The register address is the high 7 bits and the data the low 9 bits */ | ||
69 | data[0] = (reg << 1) | ((value >> 8) & 1); | ||
70 | data[1] = value; | ||
71 | |||
72 | if (i2c_write(I2C_IFACE_0, TLV320_ADDR, data, 2) != 2) | ||
73 | { | ||
74 | logf("tlv320 error reg=0x%x", reg); | ||
75 | return; | ||
76 | } | ||
77 | |||
78 | tlv320_regs[reg] = value; | ||
79 | } | ||
80 | |||
81 | /* public functions */ | ||
82 | |||
83 | /** | ||
84 | * Init our tlv with default values | ||
85 | */ | ||
86 | void audiohw_init(void) | ||
87 | { | ||
88 | memset(tlv320_regs, 0, sizeof(tlv320_regs)); | ||
89 | |||
90 | /* Initialize all registers */ | ||
91 | |||
92 | /* All ON except OUT, ADC, MIC and LINE */ | ||
93 | tlv320_write_reg(REG_PC, PC_OUT | PC_ADC | PC_MIC | PC_LINE); | ||
94 | audiohw_set_recvol(0, 0, AUDIO_GAIN_MIC); | ||
95 | audiohw_set_recvol(0, 0, AUDIO_GAIN_LINEIN); | ||
96 | audiohw_mute(true); | ||
97 | tlv320_write_reg(REG_AAP, AAP_DAC | AAP_MICM); | ||
98 | tlv320_write_reg(REG_DAP, 0x00); /* No deemphasis */ | ||
99 | tlv320_write_reg(REG_DAIF, DAIF_IWL_16 | DAIF_FOR_I2S); | ||
100 | tlv320_write_reg(REG_DIA, DIA_ACT); | ||
101 | audiohw_set_frequency(-1); /* default */ | ||
102 | } | ||
103 | |||
104 | /** | ||
105 | * Switch outputs ON | ||
106 | */ | ||
107 | void audiohw_postinit(void) | ||
108 | { | ||
109 | /* All ON except ADC, MIC and LINE */ | ||
110 | sleep(HZ); | ||
111 | tlv320_write_reg(REG_PC, PC_ADC | PC_MIC | PC_LINE); | ||
112 | sleep(HZ/4); | ||
113 | audiohw_mute(false); | ||
114 | } | ||
115 | |||
116 | /** | ||
117 | * Resets tlv320 to default values | ||
118 | */ | ||
119 | void audiohw_reset(void) | ||
120 | { | ||
121 | tlv320_write_reg(REG_RR, RR_RESET); | ||
122 | } | ||
123 | |||
124 | /** | ||
125 | * Sets internal sample rate for DAC and ADC relative to MCLK | ||
126 | * Selection for frequency: | ||
127 | * Fs: tlv: with: | ||
128 | * 11025: 0 = MCLK/2 MCLK/2 SCLK, LRCK: Audio Clk / 16 | ||
129 | * 22050: 0 = MCLK/2 MCLK SCLK, LRCK: Audio Clk / 8 | ||
130 | * 44100: 1 = MCLK MCLK SCLK, LRCK: Audio Clk / 4 (default) | ||
131 | * 88200: 2 = MCLK*2 MCLK SCLK, LRCK: Audio Clk / 2 | ||
132 | */ | ||
133 | void audiohw_set_frequency(unsigned fsel) | ||
134 | { | ||
135 | /* All rates available for 11.2896MHz besides 8.021 */ | ||
136 | unsigned char values_src[3] = | ||
137 | { | ||
138 | /* Fs: */ | ||
139 | (0x8 << 2) | SRC_CLKIN, /* 11025, 22050 */ | ||
140 | (0x8 << 2), /* 44100 */ | ||
141 | (0xf << 2), /* 88200 */ | ||
142 | }; | ||
143 | |||
144 | unsigned value_dap, value_pc; | ||
145 | |||
146 | if (fsel >= ARRAYLEN(values_src)) | ||
147 | fsel = 1; | ||
148 | |||
149 | /* Temporarily turn off the DAC and ADC before switching sample | ||
150 | rates or they don't choose their filters correctly */ | ||
151 | value_dap = tlv320_regs[REG_DAP]; | ||
152 | value_pc = tlv320_regs[REG_PC]; | ||
153 | |||
154 | tlv320_write_reg(REG_DAP, value_dap | DAP_DACM); | ||
155 | tlv320_write_reg(REG_PC, value_pc | PC_DAC | PC_ADC); | ||
156 | tlv320_write_reg(REG_SRC, values_src[fsel]); | ||
157 | tlv320_write_reg(REG_PC, value_pc | PC_DAC); | ||
158 | tlv320_write_reg(REG_PC, value_pc); | ||
159 | tlv320_write_reg(REG_DAP, value_dap); | ||
160 | } | ||
161 | |||
162 | /** | ||
163 | * Sets left and right headphone volume | ||
164 | * | ||
165 | * Left & Right: 48 .. 121 .. 127 => Volume -73dB (mute) .. +0 dB .. +6 dB | ||
166 | */ | ||
167 | void audiohw_set_headphone_vol(int vol_l, int vol_r) | ||
168 | { | ||
169 | unsigned value_dap = tlv320_regs[REG_DAP]; | ||
170 | unsigned value_dap_last = value_dap; | ||
171 | unsigned value_l = LHV_LHV(vol_l); | ||
172 | unsigned value_r = RHV_RHV(vol_r); | ||
173 | |||
174 | /* keep track of current setting */ | ||
175 | tlv320.vol_l = vol_l; | ||
176 | tlv320.vol_r = vol_r; | ||
177 | |||
178 | if (value_l > HEADPHONE_MUTE || value_r > HEADPHONE_MUTE) | ||
179 | value_dap &= ~DAP_DACM; | ||
180 | else | ||
181 | value_dap |= DAP_DACM; | ||
182 | |||
183 | /* update */ | ||
184 | tlv320_write_reg(REG_LHV, LHV_LZC | value_l); | ||
185 | tlv320_write_reg(REG_RHV, RHV_RZC | value_r); | ||
186 | if (value_dap != value_dap_last) | ||
187 | tlv320_write_reg(REG_DAP, value_dap); | ||
188 | } | ||
189 | |||
190 | /** | ||
191 | * Set recording volume | ||
192 | * | ||
193 | * Line in : 0 .. 31 => Volume -34.5 .. +12 dB | ||
194 | * Mic (left): 0 .. 1 => Volume +0, +20 dB | ||
195 | * | ||
196 | */ | ||
197 | void audiohw_set_recvol(int left, int right, int type) | ||
198 | { | ||
199 | if (type == AUDIO_GAIN_MIC) | ||
200 | { | ||
201 | unsigned value_aap = tlv320_regs[REG_AAP]; | ||
202 | |||
203 | if (left) | ||
204 | value_aap |= AAP_MICB; /* Enable mic boost (20dB) */ | ||
205 | else | ||
206 | value_aap &= ~AAP_MICB; | ||
207 | |||
208 | tlv320_write_reg(REG_AAP, value_aap); | ||
209 | } | ||
210 | else if (type == AUDIO_GAIN_LINEIN) | ||
211 | { | ||
212 | tlv320_write_reg(REG_LLIV, LLIV_LIV(left)); | ||
213 | tlv320_write_reg(REG_RLIV, RLIV_RIV(right)); | ||
214 | } | ||
215 | } | ||
216 | |||
217 | /** | ||
218 | * Mute (mute=true) or enable sound (mute=false) | ||
219 | * | ||
220 | */ | ||
221 | void audiohw_mute(bool mute) | ||
222 | { | ||
223 | unsigned value_dap = tlv320_regs[REG_DAP]; | ||
224 | unsigned value_l, value_r; | ||
225 | |||
226 | if (mute) | ||
227 | { | ||
228 | value_l = LHV_LHV(HEADPHONE_MUTE); | ||
229 | value_r = RHV_RHV(HEADPHONE_MUTE); | ||
230 | value_dap |= DAP_DACM; | ||
231 | } | ||
232 | else | ||
233 | { | ||
234 | value_l = LHV_LHV(tlv320.vol_l); | ||
235 | value_r = RHV_RHV(tlv320.vol_r); | ||
236 | if (value_l > HEADPHONE_MUTE || value_r > HEADPHONE_MUTE) | ||
237 | value_dap &= ~DAP_DACM; | ||
238 | } | ||
239 | |||
240 | tlv320_write_reg(REG_LHV, LHV_LZC | value_l); | ||
241 | tlv320_write_reg(REG_RHV, RHV_RZC | value_r); | ||
242 | tlv320_write_reg(REG_DAP, value_dap); | ||
243 | } | ||
244 | |||
245 | /* Nice shutdown of TLV320 codec */ | ||
246 | void audiohw_close(void) | ||
247 | { | ||
248 | audiohw_mute(true); | ||
249 | sleep(HZ/8); | ||
250 | |||
251 | tlv320_write_reg(REG_PC, PC_OFF | PC_CLK | PC_OSC | PC_OUT | | ||
252 | PC_DAC | PC_ADC | PC_MIC | PC_LINE); /* All OFF */ | ||
253 | } | ||
254 | |||
255 | void audiohw_enable_recording(bool source_mic) | ||
256 | { | ||
257 | unsigned value_aap, value_pc; | ||
258 | |||
259 | if (source_mic) | ||
260 | { | ||
261 | /* select MIC and enable mic boost (20 dB) */ | ||
262 | value_aap = AAP_DAC | AAP_INSEL | AAP_MICB; | ||
263 | value_pc = PC_LINE; /* power down LINE */ | ||
264 | } | ||
265 | else | ||
266 | { | ||
267 | value_aap = AAP_DAC | AAP_MICM; | ||
268 | value_pc = PC_MIC; /* power down MIC */ | ||
269 | } | ||
270 | |||
271 | tlv320_write_reg(REG_PC, value_pc); | ||
272 | tlv320_write_reg(REG_AAP, value_aap); | ||
273 | } | ||
274 | |||
275 | void audiohw_disable_recording(void) | ||
276 | { | ||
277 | unsigned value_pc = tlv320_regs[REG_PC]; | ||
278 | unsigned value_aap = tlv320_regs[REG_AAP]; | ||
279 | |||
280 | value_aap |= AAP_MICM; /* mute MIC */ | ||
281 | tlv320_write_reg(REG_PC, value_aap); | ||
282 | |||
283 | value_pc |= PC_ADC | PC_MIC | PC_LINE; /* ADC, MIC and LINE off */ | ||
284 | tlv320_write_reg(REG_PC, value_pc); | ||
285 | } | ||
286 | |||
287 | void audiohw_set_monitor(bool enable) | ||
288 | { | ||
289 | unsigned value_aap, value_pc; | ||
290 | |||
291 | if (enable) | ||
292 | { | ||
293 | /* Keep DAC on to allow mixing of voice with analog audio */ | ||
294 | value_aap = AAP_DAC | AAP_BYPASS | AAP_MICM; | ||
295 | value_pc = PC_ADC | PC_MIC; /* ADC and MIC off */ | ||
296 | } | ||
297 | else | ||
298 | { | ||
299 | value_aap = AAP_DAC | AAP_MICM; | ||
300 | value_pc = PC_ADC | PC_MIC | PC_LINE; /* ADC, MIC and LINE off */ | ||
301 | } | ||
302 | |||
303 | tlv320_write_reg(REG_AAP, value_aap); | ||
304 | tlv320_write_reg(REG_PC, value_pc); | ||
305 | } | ||