Implement a C version lcd-as-memframe.c and move it and the asm to firmware/asm.
[maemo-rb.git] / firmware / drivers / audio / aic3x.c
blob97eb17ebefd5bc65744e55fedca6e6d0a9b89d4f
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id: $
10 * Copyright (C) 2011 by Tomasz Moń
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include "config.h"
22 #include "logf.h"
23 #include "system.h"
24 #include "string.h"
25 #include "audio.h"
26 #if CONFIG_I2C == I2C_DM320
27 #include "i2c-dm320.h"
28 #endif
29 #include "audiohw.h"
31 /* (7-bit) address is 0x18, the LSB is read/write flag */
32 #define AIC3X_ADDR (0x18 << 1)
34 static char volume_left = 0, volume_right = 0;
36 const struct sound_settings_info audiohw_settings[] = {
37 [SOUND_VOLUME] = {"dB", 0, 1, VOLUME_MIN/10, VOLUME_MAX/10, -25},
38 /* HAVE_SW_TONE_CONTROLS */
39 [SOUND_BASS] = {"dB", 0, 1, -24, 24, 0},
40 [SOUND_TREBLE] = {"dB", 0, 1, -24, 24, 0},
41 [SOUND_BALANCE] = {"%", 0, 1,-100, 100, 0},
42 [SOUND_CHANNELS] = {"", 0, 1, 0, 5, 0},
43 [SOUND_STEREO_WIDTH] = {"%", 0, 5, 0, 250, 100},
46 /* convert tenth of dB volume to master volume register value */
47 int tenthdb2master(int db)
49 /* 0 to -63.0dB in 1dB steps, aic3x can goto -63.5 in 0.5dB steps */
50 if (db < VOLUME_MIN)
52 return 0x7E;
54 else if (db >= VOLUME_MAX)
56 return 0x00;
58 else
60 return (-((db)/5)); /* VOLUME_MIN is negative */
64 static void aic3x_write_reg(unsigned reg, unsigned value)
66 unsigned char data[2];
68 data[0] = reg;
69 data[1] = value;
71 #if CONFIG_I2C == I2C_DM320
72 if (i2c_write(AIC3X_ADDR, data, 2) != 0)
73 #else
74 #warning Implement aic3x_write_reg()
75 #endif
77 logf("AIC3X error reg=0x%x", reg);
78 return;
82 static unsigned char aic3x_read_reg(unsigned reg)
84 unsigned char data;
86 #if CONFIG_I2C == I2C_DM320
87 if (i2c_read_bytes(AIC3X_ADDR, reg, &data, 1))
88 #else
89 #warning Implement aic3x_read_reg()
90 #endif
92 logf("AIC3X read error reg=0x%0x", reg);
93 data = 0;
96 return data;
99 static void aic3x_change_reg(unsigned reg, unsigned char or_mask,
100 unsigned char and_mask)
102 unsigned char data;
104 data = aic3x_read_reg(reg);
106 data &= and_mask;
107 data |= or_mask;
109 aic3x_write_reg(reg, data);
112 static void aic3x_apply_volume(void)
114 unsigned char data[3];
116 #if 0 /* handle page switching once we use first page at all */
117 aic3x_write_reg(0, 0); /* switch to page 0 */
118 #endif
120 data[0] = AIC3X_LEFT_VOL;
121 data[1] = volume_left;
122 data[2] = volume_right;
124 /* use autoincrement write */
125 #if CONFIG_I2C == I2C_DM320
126 if (i2c_write(AIC3X_ADDR, data, 3) != 0)
127 #else
128 #warning Implement aic3x_apply_volume()
129 #endif
131 logf("AIC3X error in apply volume");
132 return;
137 static void audiohw_mute(bool mute)
139 if (mute)
141 /* DAC_L1 routed to HPLOUT, mute */
142 aic3x_write_reg(AIC3X_DAC_L1_VOL, 0xF6);
143 /* DAC_R1 routed to HPROUT, mute */
144 aic3x_write_reg(AIC3X_DAC_R1_VOL, 0xF6);
145 /* DAC_L1 routed to MONO_LOP/M, mute */
146 aic3x_write_reg(AIC3X_DAC_L1_MONO_LOP_M_VOL, 0xF6);
147 /* DAC_R1 routed to MONO_LOP/M, mute */
148 aic3x_write_reg(AIC3X_DAC_R1_MONO_LOP_M_VOL, 0xF6);
150 volume_left |= 0x80;
151 volume_right |= 0x80;
153 else
155 /* DAC_L1 routed to HPLOUT, volume analog gain 0xC (-6.0dB) */
156 aic3x_write_reg(AIC3X_DAC_L1_VOL, 0x8C);
157 /* DAC_R1 routed to HPROUT, volume analog gain 0xC (-6.0 dB) */
158 aic3x_write_reg(AIC3X_DAC_R1_VOL, 0x8C);
159 /* DAC_L1 routed to MONO_LOP/M, gain 0x2 (-1.0dB) */
160 aic3x_write_reg(AIC3X_DAC_L1_MONO_LOP_M_VOL, 0x92);
161 /* DAC_R1 routed to MONO_LOP/M, gain 0x2 (-1.0dB) */
162 aic3x_write_reg(AIC3X_DAC_R1_MONO_LOP_M_VOL, 0x92);
164 volume_left &= 0x7F;
165 volume_right &= 0x7F;
168 aic3x_apply_volume();
171 /* public functions */
174 * Init our tlv with default values
176 void audiohw_init(void)
178 logf("AIC3X init");
180 /* Do software reset (self-clearing) */
181 aic3x_write_reg(AIC3X_SOFT_RESET, 0x80);
183 /* driver power-on time 200 ms, ramp-up step time 4 ms */
184 aic3x_write_reg(AIC3X_POP_REDUCT, 0x7C);
186 /* Output common-move voltage 1.35V, disable LINE2[LR] bypass */
187 /* Output soft-stepping = one step per fs */
188 aic3x_write_reg(AIC3X_POWER_OUT, 0x00);
190 /* Audio data interface */
191 /* GPIO1 used for audio serial data bus ADC word clock */
192 aic3x_write_reg(AIC3X_GPIO1_CTRL, 0x10);
193 /* BCLK and WCLK are outputs (master mode) */
194 aic3x_write_reg(AIC3X_DATA_REG_A, 0xC0);
195 /* right-justified mode */
196 aic3x_write_reg(AIC3X_DATA_REG_B, 0x80);
197 /* data offset = 0 clocks */
198 aic3x_write_reg(AIC3X_DATA_REG_C, 0);
200 /* Left DAC plays left channel, Right DAC plays right channel */
201 aic3x_write_reg(AIC3X_DATAPATH, 0xA);
203 /* power left and right DAC, HPLCOM constant VCM output */
204 aic3x_write_reg(AIC3X_DAC_POWER, 0xD0);
205 /* HPRCOM as constant VCM output. Enable short-circuit protection
206 (limit current) */
207 aic3x_write_reg(AIC3X_HIGH_POWER, 0xC);
209 /* DAC_L1 routed to HPLOUT */
210 aic3x_write_reg(AIC3X_DAC_L1_VOL, 0x80);
211 /* DAC_R1 routed to HPROUT */
212 aic3x_write_reg(AIC3X_DAC_R1_VOL, 0x80);
214 /* DAC_L1 routed to MONO_LOP/M */
215 aic3x_write_reg(AIC3X_DAC_L1_MONO_LOP_M_VOL, 0x80);
216 /* DAC_R1 routed to MONO_LOP/M */
217 aic3x_write_reg(AIC3X_DAC_R1_MONO_LOP_M_VOL, 0x80);
219 /* DAC_L1 routed to LEFT_LOP/M */
220 aic3x_write_reg(AIC3X_DAC_L1_LEFT_LOP_M_VOL, 0x80);
221 /* DAC_R1 routed to RIGHT_LOP/M */
222 aic3x_write_reg(AIC3X_DAC_R1_RIGHT_LOP_M_VOL, 0x80);
224 /* LEFT_LOP/M output level 0dB, not muted */
225 aic3x_write_reg(AIC3X_LEFT_LOP_M_LVL, 0x8);
226 /* RIGHT_LOP/M output level 0dB, not muted */
227 aic3x_write_reg(AIC3X_RIGHT_LOP_M_LVL, 0x8);
229 /* Enable PLL. Set Q=16, P=1 */
230 aic3x_write_reg(AIC3X_PLL_REG_A, 0x81);
231 /* PLL J = 53 */
232 aic3x_write_reg(AIC3X_PLL_REG_B, 0xD4);
233 /* PLL D = 5211 */
234 aic3x_write_reg(AIC3X_PLL_REG_C, 0x51);
235 aic3x_write_reg(AIC3X_PLL_REG_D, 0x6C);
236 /* PLL R = 1 */
237 aic3x_write_reg(AIC3X_OVERFLOW, 0x01);
239 /* ADC fs = fs(ref)/5.5; DAC fs = fs(ref) */
240 aic3x_write_reg(AIC3X_SMPL_RATE, 0x90);
242 /* HPLOUT output level 0dB, muted, high impedance */
243 aic3x_write_reg(AIC3X_HPLOUT_LVL, 0x04);
244 /* HPROUT output level 0dB, muted, high impedance */
245 aic3x_write_reg(AIC3X_HPROUT_LVL, 0x04);
247 /* HPLCOM is high impedance when powered down, not fully powered up */
248 aic3x_write_reg(AIC3X_HPLCOM_LVL, 0x04);
251 void audiohw_postinit(void)
253 audiohw_mute(false);
255 /* HPLOUT output level 0dB, not muted, fully powered up */
256 aic3x_write_reg(AIC3X_HPLOUT_LVL, 0x09);
257 /* HPROUT output level 0dB, not muted, fully powered up */
258 aic3x_write_reg(AIC3X_HPROUT_LVL, 0x09);
260 /* MONO_LOP output level 6dB, not muted */
261 aic3x_write_reg(AIC3X_MONO_LOP_M_LVL, 0x69);
263 /* PGA_R is not routed to MONO_LOP/M, analog gain -52.7dB */
264 aic3x_write_reg(AIC3X_PGA_R_MONO_LOP_M_VOL, 0x69);
267 void audiohw_set_frequency(int fsel)
269 (void)fsel;
270 /* TODO */
273 void audiohw_set_headphone_vol(int vol_l, int vol_r)
275 if ((volume_left & 0x7F) == (vol_l & 0x7F) &&
276 (volume_right & 0x7F) == (vol_r & 0x7F))
278 /* Volume already set to this value */
279 return;
282 volume_left &= 0x80; /* preserve mute bit */
283 volume_left |= (vol_l & 0x7F); /* set gain */
285 volume_right &= 0x80; /* preserve mute bit */
286 volume_right |= (vol_r & 0x7F); /* set gain */
288 aic3x_apply_volume();
291 /* Nice shutdown of AIC3X codec */
292 void audiohw_close(void)
294 /* HPLOUT, HPROUT, HPLCOM not fully powered up */
295 aic3x_change_reg(AIC3X_HPLOUT_LVL, 0x00, 0xFE);
296 aic3x_change_reg(AIC3X_HPROUT_LVL, 0x00, 0xFE);
297 aic3x_change_reg(AIC3X_HPLCOM_LVL, 0x00, 0xFC);
299 /* MONO_LOP/M, LEFT_LOP/M, RIGHT_LOP/M muted, not fully powered up */
300 aic3x_change_reg(AIC3X_MONO_LOP_M_LVL, 0x00, 0xF6);
301 aic3x_change_reg(AIC3X_LEFT_LOP_M_LVL, 0x00, 0xF6);
302 aic3x_change_reg(AIC3X_RIGHT_LOP_M_LVL, 0x00, 0xF6);
304 /* Power down left and right DAC */
305 aic3x_change_reg(AIC3X_DAC_POWER, 0x00, 0x30);
307 /* Disable PLL */
308 aic3x_change_reg(AIC3X_PLL_REG_A, 0x00, 0x7F);
311 void aic3x_switch_output(bool stereo)
313 if (stereo)
315 /* mute MONO_LOP/M */
316 aic3x_change_reg(AIC3X_MONO_LOP_M_LVL, 0x00, 0xF6);
317 /* HPLOUT fully powered up */
318 aic3x_change_reg(AIC3X_HPLOUT_LVL, 0x01, 0xFF);
319 /* HPROUT fully powered up */
320 aic3x_change_reg(AIC3X_HPROUT_LVL, 0x01, 0xFF);
321 /* HPLCOM fully powered up */
322 aic3x_change_reg(AIC3X_HPLCOM_LVL, 0x01, 0xFF);
324 else
326 /* MONO_LOP/M not muted */
327 aic3x_change_reg(AIC3X_MONO_LOP_M_LVL, 0x09, 0xFF);
328 /* HPLOUT not fully powered up */
329 aic3x_change_reg(AIC3X_HPLOUT_LVL, 0x00, 0xFE);
330 /* HPROUT not fully powered up */
331 aic3x_change_reg(AIC3X_HPROUT_LVL, 0x00, 0xFE);
332 /* HPLCOM not fully powered up */
333 aic3x_change_reg(AIC3X_HPLCOM_LVL, 0x00, 0xFE);