summaryrefslogtreecommitdiff
path: root/firmware/drivers/audio/aic3x.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/drivers/audio/aic3x.c')
-rw-r--r--firmware/drivers/audio/aic3x.c247
1 files changed, 247 insertions, 0 deletions
diff --git a/firmware/drivers/audio/aic3x.c b/firmware/drivers/audio/aic3x.c
new file mode 100644
index 0000000000..3284326565
--- /dev/null
+++ b/firmware/drivers/audio/aic3x.c
@@ -0,0 +1,247 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id: $
9 *
10 * Copyright (C) 2011 by Tomasz Moń
11 *
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.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include "config.h"
22#include "logf.h"
23#include "system.h"
24#include "string.h"
25#include "audio.h"
26
27#ifdef SANSA_CONNECT
28#include "avr-sansaconnect.h"
29#endif
30
31#if CONFIG_I2C == I2C_DM320
32#include "i2c-dm320.h"
33#endif
34#include "audiohw.h"
35
36/* (7-bit) address is 0x18, the LSB is read/write flag */
37#define AIC3X_ADDR (0x18 << 1)
38
39static char volume_left = 0, volume_right = 0;
40
41const struct sound_settings_info audiohw_settings[] = {
42 [SOUND_VOLUME] = {"dB", 0, 1, VOLUME_MIN/10, VOLUME_MAX/10, -25},
43 /* HAVE_SW_TONE_CONTROLS */
44 [SOUND_BASS] = {"dB", 0, 1, -24, 24, 0},
45 [SOUND_TREBLE] = {"dB", 0, 1, -24, 24, 0},
46 [SOUND_BALANCE] = {"%", 0, 1,-100, 100, 0},
47 [SOUND_CHANNELS] = {"", 0, 1, 0, 5, 0},
48 [SOUND_STEREO_WIDTH] = {"%", 0, 5, 0, 250, 100},
49};
50
51/* convert tenth of dB volume to master volume register value */
52int tenthdb2master(int db)
53{
54 /* 0 to -63.0dB in 1dB steps, aic3x can goto -63.5 in 0.5dB steps */
55 if (db < VOLUME_MIN)
56 {
57 return 0x7E;
58 }
59 else if (db >= VOLUME_MAX)
60 {
61 return 0x00;
62 }
63 else
64 {
65 return (-((db)/5)); /* VOLUME_MIN is negative */
66 }
67}
68
69static void aic3x_write_reg(unsigned reg, unsigned value)
70{
71 unsigned char data[2];
72
73 data[0] = reg;
74 data[1] = value;
75
76#if CONFIG_I2C == I2C_DM320
77 if (i2c_write(AIC3X_ADDR, data, 2) != 0)
78#else
79 #warning Implement aic3x_write_reg()
80#endif
81 {
82 logf("AIC3X error reg=0x%x", reg);
83 return;
84 }
85}
86
87static void aic3x_apply_volume(void)
88{
89 unsigned char data[3];
90
91#if 0 /* handle page switching onve we use first page at all */
92 aic3x_write_reg(0, 0); /* switch to page 0 */
93#endif
94
95 data[0] = AIC3X_LEFT_VOL;
96 data[1] = volume_left;
97 data[2] = volume_right;
98
99 /* use autoincrement write */
100#if CONFIG_I2C == I2C_DM320
101 if (i2c_write(AIC3X_ADDR, data, 3) != 0)
102#else
103 #warning Implement aic3x_apply_volume()
104#endif
105 {
106 logf("AIC3X error in apply volume");
107 return;
108 }
109}
110
111
112static void audiohw_mute(bool mute)
113{
114 if (mute)
115 {
116 volume_left |= 0x80;
117 volume_right |= 0x80;
118 }
119 else
120 {
121 volume_left &= 0x7F;
122 volume_right &= 0x7F;
123 }
124
125 aic3x_apply_volume();
126}
127
128/* public functions */
129
130/**
131 * Init our tlv with default values
132 */
133void audiohw_init(void)
134{
135 logf("AIC3X init");
136
137 /* Do software reset (self-clearing) */
138 aic3x_write_reg(AIC3X_SOFT_RESET, 0x80);
139
140 /* ADC fs = fs(ref)/5.5; DAC fs = fs(ref) */
141 aic3x_write_reg(AIC3X_SMPL_RATE, 0x90);
142
143 /* Enable PLL. Set Q=16, P=1 */
144 aic3x_write_reg(AIC3X_PLL_REG_A, 0x81);
145 /* PLL J = 53 */
146 aic3x_write_reg(AIC3X_PLL_REG_B, 0xD4);
147 /* PLL D = 5211 */
148 aic3x_write_reg(AIC3X_PLL_REG_C, 0x51);
149 aic3x_write_reg(AIC3X_PLL_REG_D, 0x6C); /* PLL D = 5211 */
150
151 /* Left DAC plays left channel, Right DAC plays right channel */
152 aic3x_write_reg(AIC3X_DATAPATH, 0xA);
153
154 /* Audio data interface */
155 /* BCLK and WCLK are outputs (master mode) */
156 aic3x_write_reg(AIC3X_DATA_REG_A, 0xC0);
157 /* right-justified mode */
158 aic3x_write_reg(AIC3X_DATA_REG_B, 0x80);
159 /* data offset = 0 clocks */
160 aic3x_write_reg(AIC3X_DATA_REG_C, 0);
161
162 /* GPIO1 used for audio serial data bus ADC word clock */
163 aic3x_write_reg(AIC3X_GPIO1_CTRL, 0x10);
164
165 /* power left and right DAC, HPLCOM constant VCM output */
166 aic3x_write_reg(AIC3X_DAC_POWER, 0xD0);
167 /* HPRCOM as constant VCM output. Enable short-circuit protection
168 (limit current) */
169 aic3x_write_reg(AIC3X_HIGH_POWER, 0xC);
170
171 /* driver power-on time 200 ms, ramp-up step time 4 ms */
172 aic3x_write_reg(AIC3X_POP_REDUCT, 0x7C);
173
174 /* DAC_L1 routed to HPLOUT, volume analog gain 0xC (-6.0dB) */
175 aic3x_write_reg(AIC3X_DAC_L1_VOL, 0x8C);
176 /* HPLOUT output level 0dB, not muted, fully powered up */
177 aic3x_write_reg(AIC3X_HPLOUT_LVL, 0xB);
178
179 /* HPLCOM is muted */
180 aic3x_write_reg(AIC3X_HPLCOM_LVL, 0x7);
181
182 /* DAC_R1 routed to HPROUT, volume analog gain 0xC (-6.0 dB) */
183 aic3x_write_reg(AIC3X_DAC_R1_VOL, 0x8C);
184 /* HPROUT output level 0dB, not muted, fully powered up */
185 aic3x_write_reg(AIC3X_HPROUT_LVL, 0xB);
186
187 /* DAC_L1 routed to MONO_LOP/M, gain 0x2 (-1.0dB) */
188 aic3x_write_reg(AIC3X_DAC_L1_MONO_LOP_M_VOL, 0x92);
189 /* DAC_R1 routed to MONO_LOP/M, gain 0x2 (-1.0dB) */
190 aic3x_write_reg(AIC3X_DAC_R1_MONO_LOP_M_VOL, 0x92);
191
192 /* MONO_LOP output level 6dB, not muted, fully powered up */
193 aic3x_write_reg(AIC3X_MONO_LOP_M_LVL, 0x6b);
194
195 /* DAC_L1 routed to LEFT_LOP/M */
196 aic3x_write_reg(AIC3X_DAC_L1_LEFT_LOP_M_VOL, 0x80);
197 /* LEFT_LOP/M output level 0dB, not muted */
198 aic3x_write_reg(AIC3X_LEFT_LOP_M_LVL, 0xB);
199
200 /* DAC_R1 routed to RIGHT_LOP/M */
201 aic3x_write_reg(AIC3X_DAC_R1_RIGHT_LOP_M_VOL, 0x80);
202 /* RIGHT_LOP/M output level 0dB, not muted */
203 aic3x_write_reg(AIC3X_RIGHT_LOP_M_LVL, 0xB);
204}
205
206void audiohw_postinit(void)
207{
208 audiohw_mute(false);
209
210 /* Power up Left, Right DAC/LOP, HPLOUT and HPROUT */
211 aic3x_write_reg(AIC3X_MOD_POWER, 0xFE);
212}
213
214void audiohw_set_frequency(int fsel)
215{
216 (void)fsel;
217 /* TODO */
218}
219
220void audiohw_set_headphone_vol(int vol_l, int vol_r)
221{
222 if ((volume_left & 0x7F) == (vol_l & 0x7F) &&
223 (volume_right & 0x7F) == (vol_r & 0x7F))
224 {
225 /* Volume already set to this value */
226 return;
227 }
228
229 volume_left &= 0x80; /* preserve mute bit */
230 volume_left |= (vol_l & 0x7F); /* set gain */
231
232 volume_right &= 0x80; /* preserve mute bit */
233 volume_right |= (vol_r & 0x7F); /* set gain */
234
235 aic3x_apply_volume();
236}
237
238/* Nice shutdown of AIC3X codec */
239void audiohw_close(void)
240{
241 audiohw_mute(true);
242#ifdef SANSA_CONNECT
243 avr_hid_reset_codec();
244#endif
245}
246
247