summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2008-12-31 01:38:44 +0000
committerMichael Sevakis <jethead71@rockbox.org>2008-12-31 01:38:44 +0000
commit2e99b3d9318dbc98c242c9ddbbf23d2fcc5bf2bd (patch)
tree53587fff24b5da8c717cf2e052e16942501a78c4
parent9ac7af749bceb67916b3179a3b7c7eee6ee6443a (diff)
downloadrockbox-2e99b3d9318dbc98c242c9ddbbf23d2fcc5bf2bd.tar.gz
rockbox-2e99b3d9318dbc98c242c9ddbbf23d2fcc5bf2bd.zip
Gigabeat S: Allow recording from FM. Give FM the same volume range as playback. NOTE: This bumps the si4700 output volume to -0dB so other players with that may need a minor adjustment.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@19619 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/keymaps/keymap-gigabeat-s.c21
-rw-r--r--apps/recorder/recording.c46
-rw-r--r--firmware/drivers/audio/wm8978.c244
-rw-r--r--firmware/drivers/tuner/si4700.c4
-rw-r--r--firmware/export/config-gigabeat-s.h13
-rwxr-xr-xfirmware/export/imx31l.h4
-rw-r--r--firmware/export/wm8978.h33
-rw-r--r--firmware/sound.c20
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/audio-gigabeat-s.c56
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c171
10 files changed, 450 insertions, 162 deletions
diff --git a/apps/keymaps/keymap-gigabeat-s.c b/apps/keymaps/keymap-gigabeat-s.c
index b0cc87011f..32c4f22b5b 100644
--- a/apps/keymaps/keymap-gigabeat-s.c
+++ b/apps/keymaps/keymap-gigabeat-s.c
@@ -251,6 +251,23 @@ static const struct button_mapping button_context_pitchscreen[] = {
251 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) 251 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
252}; /* button_context_pitchcreen */ 252}; /* button_context_pitchcreen */
253 253
254/** Recording Screen **/
255static const struct button_mapping button_context_recscreen[] = {
256 { ACTION_REC_PAUSE, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY },
257 { ACTION_STD_CANCEL, BUTTON_BACK|BUTTON_REL, BUTTON_BACK },
258 { ACTION_REC_NEWFILE, BUTTON_NEXT|BUTTON_REL, BUTTON_NEXT },
259 { ACTION_STD_MENU, BUTTON_MENU|BUTTON_REPEAT, BUTTON_NONE },
260 { ACTION_SETTINGS_INC, BUTTON_RIGHT, BUTTON_NONE },
261 { ACTION_SETTINGS_INCREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
262 { ACTION_SETTINGS_DEC, BUTTON_LEFT, BUTTON_NONE },
263 { ACTION_SETTINGS_DECREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
264 { ACTION_STD_PREV, BUTTON_UP, BUTTON_NONE },
265 { ACTION_STD_PREV, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
266 { ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE },
267 { ACTION_STD_NEXT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
268 LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD)
269}; /* button_context_recscreen */
270
254static const struct button_mapping button_context_keyboard[] = { 271static const struct button_mapping button_context_keyboard[] = {
255 { ACTION_KBD_LEFT, BUTTON_LEFT, BUTTON_NONE }, 272 { ACTION_KBD_LEFT, BUTTON_LEFT, BUTTON_NONE },
256 { ACTION_KBD_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE }, 273 { ACTION_KBD_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
@@ -333,6 +350,10 @@ const struct button_mapping* get_context_mapping(int context)
333 return button_context_quickscreen; 350 return button_context_quickscreen;
334 case CONTEXT_PITCHSCREEN: 351 case CONTEXT_PITCHSCREEN:
335 return button_context_pitchscreen; 352 return button_context_pitchscreen;
353 case CONTEXT_RECSCREEN:
354 return button_context_recscreen;
355 case CONTEXT_SETTINGS_RECTRIGGER:
356 return button_context_settings_right_is_inc;
336 case CONTEXT_KEYBOARD: 357 case CONTEXT_KEYBOARD:
337 return button_context_keyboard; 358 return button_context_keyboard;
338 case CONTEXT_FM: 359 case CONTEXT_FM:
diff --git a/apps/recorder/recording.c b/apps/recorder/recording.c
index b2c81cd21b..a8586b9735 100644
--- a/apps/recorder/recording.c
+++ b/apps/recorder/recording.c
@@ -274,12 +274,14 @@ static short agc_maxgain;
274 274
275static void set_gain(void) 275static void set_gain(void)
276{ 276{
277#ifdef HAVE_MIC_REC
277 if(global_settings.rec_source == AUDIO_SRC_MIC) 278 if(global_settings.rec_source == AUDIO_SRC_MIC)
278 { 279 {
279 audio_set_recording_gain(global_settings.rec_mic_gain, 280 audio_set_recording_gain(global_settings.rec_mic_gain,
280 0, AUDIO_GAIN_MIC); 281 0, AUDIO_GAIN_MIC);
281 } 282 }
282 else 283 else
284#endif /* MIC */
283 { 285 {
284 /* AUDIO_SRC_LINEIN, AUDIO_SRC_FMRADIO, AUDIO_SRC_SPDIF */ 286 /* AUDIO_SRC_LINEIN, AUDIO_SRC_FMRADIO, AUDIO_SRC_SPDIF */
285 audio_set_recording_gain(global_settings.rec_left_gain, 287 audio_set_recording_gain(global_settings.rec_left_gain,
@@ -332,15 +334,19 @@ static bool agc_gain_is_max(bool left, bool right)
332 334
333 switch (global_settings.rec_source) 335 switch (global_settings.rec_source)
334 { 336 {
337#if defined(HAVE_LINE_REC) || defined(HAVE_FMRADIO_REC)
335 HAVE_LINE_REC_(case AUDIO_SRC_LINEIN:) 338 HAVE_LINE_REC_(case AUDIO_SRC_LINEIN:)
336 HAVE_FMRADIO_REC_(case AUDIO_SRC_FMRADIO:) 339 HAVE_FMRADIO_REC_(case AUDIO_SRC_FMRADIO:)
337 gain_current_l = global_settings.rec_left_gain; 340 gain_current_l = global_settings.rec_left_gain;
338 gain_current_r = global_settings.rec_right_gain; 341 gain_current_r = global_settings.rec_right_gain;
339 break; 342 break;
343#endif /* LINE, FMRADIO */
344#if defined(HAVE_MIC_REC)
340 case AUDIO_SRC_MIC: 345 case AUDIO_SRC_MIC:
341 default: 346 default:
342 gain_current_l = global_settings.rec_mic_gain; 347 gain_current_l = global_settings.rec_mic_gain;
343 gain_current_r = global_settings.rec_mic_gain; 348 gain_current_r = global_settings.rec_mic_gain;
349#endif /* MIC */
344 } 350 }
345 351
346 return ((left && (gain_current_l >= agc_maxgain)) || 352 return ((left && (gain_current_l >= agc_maxgain)) ||
@@ -353,13 +359,17 @@ static void change_recording_gain(bool increment, bool left, bool right)
353 359
354 switch (global_settings.rec_source) 360 switch (global_settings.rec_source)
355 { 361 {
362#if defined(HAVE_LINE_REC) || defined(HAVE_FMRADIO_REC)
356 HAVE_LINE_REC_(case AUDIO_SRC_LINEIN:) 363 HAVE_LINE_REC_(case AUDIO_SRC_LINEIN:)
357 HAVE_FMRADIO_REC_(case AUDIO_SRC_FMRADIO:) 364 HAVE_FMRADIO_REC_(case AUDIO_SRC_FMRADIO:)
358 if (left) global_settings.rec_left_gain += factor; 365 if (left) global_settings.rec_left_gain += factor;
359 if (right) global_settings.rec_right_gain += factor; 366 if (right) global_settings.rec_right_gain += factor;
360 break; 367 break;
368#endif /* LINE, FMRADIO */
369#if defined(HAVE_MIC_REC)
361 case AUDIO_SRC_MIC: 370 case AUDIO_SRC_MIC:
362 global_settings.rec_mic_gain += factor; 371 global_settings.rec_mic_gain += factor;
372#endif
363 } 373 }
364} 374}
365 375
@@ -858,6 +868,7 @@ static char * reclist_get_name(int selected_item, void * data,
858 buf2, sizeof(buf2))); 868 buf2, sizeof(buf2)));
859 break; 869 break;
860 case ITEM_GAIN: 870 case ITEM_GAIN:
871#ifdef HAVE_MIC_REC
861 if(global_settings.rec_source == AUDIO_SRC_MIC) 872 if(global_settings.rec_source == AUDIO_SRC_MIC)
862 { 873 {
863 /* Draw MIC recording gain */ 874 /* Draw MIC recording gain */
@@ -867,6 +878,7 @@ static char * reclist_get_name(int selected_item, void * data,
867 buf2, sizeof(buf2))); 878 buf2, sizeof(buf2)));
868 } 879 }
869 else 880 else
881#endif /* MIC */
870 { 882 {
871 int avg_gain = (global_settings.rec_left_gain + 883 int avg_gain = (global_settings.rec_left_gain +
872 global_settings.rec_right_gain) / 2; 884 global_settings.rec_right_gain) / 2;
@@ -902,6 +914,7 @@ static char * reclist_get_name(int selected_item, void * data,
902 str(LANG_RECORDING_AGC_MAXGAIN), 914 str(LANG_RECORDING_AGC_MAXGAIN),
903 fmt_gain(SOUND_LEFT_GAIN, 915 fmt_gain(SOUND_LEFT_GAIN,
904 agc_maxgain, buf2, sizeof(buf2))); 916 agc_maxgain, buf2, sizeof(buf2)));
917#ifdef HAVE_MIC_REC
905 else if (global_settings.rec_source == AUDIO_SRC_MIC) 918 else if (global_settings.rec_source == AUDIO_SRC_MIC)
906 snprintf(buffer, buffer_len, "%s: %s (%s)", 919 snprintf(buffer, buffer_len, "%s: %s (%s)",
907 str(LANG_RECORDING_AGC_MAXGAIN), 920 str(LANG_RECORDING_AGC_MAXGAIN),
@@ -911,6 +924,7 @@ static char * reclist_get_name(int selected_item, void * data,
911 agc_maxgain - global_settings.rec_mic_gain, 924 agc_maxgain - global_settings.rec_mic_gain,
912 buf3, sizeof(buf3))); 925 buf3, sizeof(buf3)));
913 else 926 else
927#endif /* MIC */
914 snprintf(buffer, buffer_len, "%s: %s (%s)", 928 snprintf(buffer, buffer_len, "%s: %s (%s)",
915 str(LANG_RECORDING_AGC_MAXGAIN), 929 str(LANG_RECORDING_AGC_MAXGAIN),
916 fmt_gain(SOUND_LEFT_GAIN, 930 fmt_gain(SOUND_LEFT_GAIN,
@@ -1180,15 +1194,18 @@ bool recording_screen(bool no_source)
1180#endif 1194#endif
1181 1195
1182#ifdef HAVE_AGC 1196#ifdef HAVE_AGC
1197#ifdef HAVE_MIC_REC
1183 if (global_settings.rec_source == AUDIO_SRC_MIC) { 1198 if (global_settings.rec_source == AUDIO_SRC_MIC) {
1184 agc_preset = global_settings.rec_agc_preset_mic; 1199 agc_preset = global_settings.rec_agc_preset_mic;
1185 agc_maxgain = global_settings.rec_agc_maxgain_mic; 1200 agc_maxgain = global_settings.rec_agc_maxgain_mic;
1186 } 1201 }
1187 else { 1202 else
1203#endif /* MIC */
1204 {
1188 agc_preset = global_settings.rec_agc_preset_line; 1205 agc_preset = global_settings.rec_agc_preset_line;
1189 agc_maxgain = global_settings.rec_agc_maxgain_line; 1206 agc_maxgain = global_settings.rec_agc_maxgain_line;
1190 } 1207 }
1191#endif 1208#endif /* HAVE_AGC */
1192 1209
1193 set_gain(); 1210 set_gain();
1194 update_countdown = 0; /* Update immediately */ 1211 update_countdown = 0; /* Update immediately */
@@ -1209,7 +1226,7 @@ bool recording_screen(bool no_source)
1209 } 1226 }
1210 else 1227 else
1211#endif 1228#endif
1212 if((global_settings.rec_source == AUDIO_SRC_MIC) || 1229 if(HAVE_MIC_REC_((global_settings.rec_source == AUDIO_SRC_MIC) || )
1213 (global_settings.rec_channels == 1)) 1230 (global_settings.rec_channels == 1))
1214 { 1231 {
1215 listid_to_enum[0] = ITEM_VOLUME_M; 1232 listid_to_enum[0] = ITEM_VOLUME_M;
@@ -1335,6 +1352,7 @@ bool recording_screen(bool no_source)
1335 setvol(); 1352 setvol();
1336 break; 1353 break;
1337 case ITEM_GAIN: 1354 case ITEM_GAIN:
1355#ifdef HAVE_MIC_REC
1338 if(global_settings.rec_source == AUDIO_SRC_MIC) 1356 if(global_settings.rec_source == AUDIO_SRC_MIC)
1339 { 1357 {
1340 if(global_settings.rec_mic_gain < 1358 if(global_settings.rec_mic_gain <
@@ -1342,6 +1360,7 @@ bool recording_screen(bool no_source)
1342 global_settings.rec_mic_gain++; 1360 global_settings.rec_mic_gain++;
1343 } 1361 }
1344 else 1362 else
1363#endif /* MIC */
1345 { 1364 {
1346 if(global_settings.rec_left_gain < 1365 if(global_settings.rec_left_gain <
1347 sound_max(SOUND_LEFT_GAIN)) 1366 sound_max(SOUND_LEFT_GAIN))
@@ -1365,15 +1384,19 @@ bool recording_screen(bool no_source)
1365 case ITEM_AGC_MODE: 1384 case ITEM_AGC_MODE:
1366 agc_preset = MIN(agc_preset + 1, AGC_MODE_SIZE); 1385 agc_preset = MIN(agc_preset + 1, AGC_MODE_SIZE);
1367 agc_enable = (agc_preset != 0); 1386 agc_enable = (agc_preset != 0);
1387#ifdef HAVE_MIC_REC
1368 if (global_settings.rec_source == AUDIO_SRC_MIC) { 1388 if (global_settings.rec_source == AUDIO_SRC_MIC) {
1369 global_settings.rec_agc_preset_mic = agc_preset; 1389 global_settings.rec_agc_preset_mic = agc_preset;
1370 agc_maxgain = global_settings.rec_agc_maxgain_mic; 1390 agc_maxgain = global_settings.rec_agc_maxgain_mic;
1371 } else { 1391 } else
1392#endif /* MIC */
1393 {
1372 global_settings.rec_agc_preset_line = agc_preset; 1394 global_settings.rec_agc_preset_line = agc_preset;
1373 agc_maxgain = global_settings.rec_agc_maxgain_line; 1395 agc_maxgain = global_settings.rec_agc_maxgain_line;
1374 } 1396 }
1375 break; 1397 break;
1376 case ITEM_AGC_MAXDB: 1398 case ITEM_AGC_MAXDB:
1399#ifdef HAVE_MIC_REC
1377 if (global_settings.rec_source == AUDIO_SRC_MIC) 1400 if (global_settings.rec_source == AUDIO_SRC_MIC)
1378 { 1401 {
1379 agc_maxgain = MIN(agc_maxgain + 1, 1402 agc_maxgain = MIN(agc_maxgain + 1,
@@ -1381,6 +1404,7 @@ bool recording_screen(bool no_source)
1381 global_settings.rec_agc_maxgain_mic = agc_maxgain; 1404 global_settings.rec_agc_maxgain_mic = agc_maxgain;
1382 } 1405 }
1383 else 1406 else
1407#endif /* MIC */
1384 { 1408 {
1385 agc_maxgain = MIN(agc_maxgain + 1, 1409 agc_maxgain = MIN(agc_maxgain + 1,
1386 sound_max(SOUND_LEFT_GAIN)); 1410 sound_max(SOUND_LEFT_GAIN));
@@ -1401,6 +1425,7 @@ bool recording_screen(bool no_source)
1401 setvol(); 1425 setvol();
1402 break; 1426 break;
1403 case ITEM_GAIN: 1427 case ITEM_GAIN:
1428#ifdef HAVE_MIC_REC
1404 if(global_settings.rec_source == AUDIO_SRC_MIC) 1429 if(global_settings.rec_source == AUDIO_SRC_MIC)
1405 { 1430 {
1406 if(global_settings.rec_mic_gain > 1431 if(global_settings.rec_mic_gain >
@@ -1408,6 +1433,7 @@ bool recording_screen(bool no_source)
1408 global_settings.rec_mic_gain--; 1433 global_settings.rec_mic_gain--;
1409 } 1434 }
1410 else 1435 else
1436#endif /* MIC */
1411 { 1437 {
1412 if(global_settings.rec_left_gain > 1438 if(global_settings.rec_left_gain >
1413 sound_min(SOUND_LEFT_GAIN)) 1439 sound_min(SOUND_LEFT_GAIN))
@@ -1431,22 +1457,26 @@ bool recording_screen(bool no_source)
1431 case ITEM_AGC_MODE: 1457 case ITEM_AGC_MODE:
1432 agc_preset = MAX(agc_preset - 1, 0); 1458 agc_preset = MAX(agc_preset - 1, 0);
1433 agc_enable = (agc_preset != 0); 1459 agc_enable = (agc_preset != 0);
1460#ifdef HAVE_MIC_REC
1434 if (global_settings.rec_source == AUDIO_SRC_MIC) { 1461 if (global_settings.rec_source == AUDIO_SRC_MIC) {
1435 global_settings.rec_agc_preset_mic = agc_preset; 1462 global_settings.rec_agc_preset_mic = agc_preset;
1436 agc_maxgain = global_settings.rec_agc_maxgain_mic; 1463 agc_maxgain = global_settings.rec_agc_maxgain_mic;
1437 } else { 1464 } else
1465#endif /* MIC */
1466 {
1438 global_settings.rec_agc_preset_line = agc_preset; 1467 global_settings.rec_agc_preset_line = agc_preset;
1439 agc_maxgain = global_settings.rec_agc_maxgain_line; 1468 agc_maxgain = global_settings.rec_agc_maxgain_line;
1440 } 1469 }
1441 break; 1470 break;
1442 case ITEM_AGC_MAXDB: 1471 case ITEM_AGC_MAXDB:
1472#ifdef HAVE_MIC_REC
1443 if (global_settings.rec_source == AUDIO_SRC_MIC) 1473 if (global_settings.rec_source == AUDIO_SRC_MIC)
1444 { 1474 {
1445 agc_maxgain = MAX(agc_maxgain - 1, 1475 agc_maxgain = MAX(agc_maxgain - 1,
1446 sound_min(SOUND_MIC_GAIN)); 1476 sound_min(SOUND_MIC_GAIN));
1447 global_settings.rec_agc_maxgain_mic = agc_maxgain; 1477 global_settings.rec_agc_maxgain_mic = agc_maxgain;
1448 } 1478 } else
1449 else 1479#endif /* MIC */
1450 { 1480 {
1451 agc_maxgain = MAX(agc_maxgain - 1, 1481 agc_maxgain = MAX(agc_maxgain - 1,
1452 sound_min(SOUND_LEFT_GAIN)); 1482 sound_min(SOUND_LEFT_GAIN));
@@ -1796,12 +1826,14 @@ bool recording_screen(bool no_source)
1796 } 1826 }
1797 1827
1798#ifdef HAVE_AGC 1828#ifdef HAVE_AGC
1829#ifdef HAVE_MIC_REC
1799 if (global_settings.rec_source == AUDIO_SRC_MIC) 1830 if (global_settings.rec_source == AUDIO_SRC_MIC)
1800 { 1831 {
1801 if(agc_maxgain < (global_settings.rec_mic_gain)) 1832 if(agc_maxgain < (global_settings.rec_mic_gain))
1802 change_recording_gain(false, true, true); 1833 change_recording_gain(false, true, true);
1803 } 1834 }
1804 else 1835 else
1836#endif /* MIC */
1805 { 1837 {
1806 if(agc_maxgain < (global_settings.rec_left_gain)) 1838 if(agc_maxgain < (global_settings.rec_left_gain))
1807 change_recording_gain(false, true, false); 1839 change_recording_gain(false, true, false);
diff --git a/firmware/drivers/audio/wm8978.c b/firmware/drivers/audio/wm8978.c
index ee881fbf4a..da444be1bf 100644
--- a/firmware/drivers/audio/wm8978.c
+++ b/firmware/drivers/audio/wm8978.c
@@ -28,18 +28,11 @@
28//#define LOGF_ENABLE 28//#define LOGF_ENABLE
29#include "logf.h" 29#include "logf.h"
30 30
31/* #define to help adjust lower volume limit */
32#define HW_VOL_MIN 0
33#define HW_VOL_MUTE 0
34#define HW_VOL_MAX 96
35#define HW_VOL_ANA_MIN 0
36#define HW_VOL_ANA_MAX 63
37#define HW_VOL_DIG_MAX 255
38#define HW_VOL_DIG_THRESHOLD (HW_VOL_MAX - HW_VOL_ANA_MAX)
39#define HW_VOL_DIG_MIN (HW_VOL_DIG_MAX - 2*HW_VOL_DIG_THRESHOLD)
40
41/* TODO: Define/refine an API for special hardware steps outside the 31/* TODO: Define/refine an API for special hardware steps outside the
42 * main codec driver such as special GPIO handling. */ 32 * main codec driver such as special GPIO handling. */
33/* NOTE: Much of the volume code is very interdependent and calibrated for
34 * the Gigabeat S. If you change anything for another device that uses this
35 * file it may break things. */
43extern void audiohw_enable_headphone_jack(bool enable); 36extern void audiohw_enable_headphone_jack(bool enable);
44 37
45const struct sound_settings_info audiohw_settings[] = 38const struct sound_settings_info audiohw_settings[] =
@@ -51,10 +44,14 @@ const struct sound_settings_info audiohw_settings[] =
51 [SOUND_CHANNELS] = {"", 0, 1, 0, 5, 0}, 44 [SOUND_CHANNELS] = {"", 0, 1, 0, 5, 0},
52 [SOUND_STEREO_WIDTH] = {"%", 0, 5, 0, 250, 100}, 45 [SOUND_STEREO_WIDTH] = {"%", 0, 5, 0, 250, 100},
53#ifdef HAVE_RECORDING 46#ifdef HAVE_RECORDING
54 [SOUND_LEFT_GAIN] = {"dB", 1, 1,-128, 96, 0}, 47 /* Digital: -119.0dB to +8.0dB in 0.5dB increments
55 [SOUND_RIGHT_GAIN] = {"dB", 1, 1,-128, 96, 0}, 48 * Analog: Relegated to volume control
49 * Circumstances unfortunately do not allow a great deal of positive
50 * gain. */
51 [SOUND_LEFT_GAIN] = {"dB", 1, 1,-238, 16, 0},
52 [SOUND_RIGHT_GAIN] = {"dB", 1, 1,-238, 16, 0},
56#if 0 53#if 0
57 [SOUND_MIC_GAIN] = {"dB", 1, 1,-128, 108, 16}, 54 [SOUND_MIC_GAIN] = {"dB", 1, 1,-238, 16, 0},
58#endif 55#endif
59#endif 56#endif
60#if 0 57#if 0
@@ -107,8 +104,8 @@ static uint16_t wmc_regs[WMC_NUM_REGISTERS] =
107 [WMC_3D_CONTROL] = 0x000, 104 [WMC_3D_CONTROL] = 0x000,
108 [WMC_BEEP_CONTROL] = 0x000, 105 [WMC_BEEP_CONTROL] = 0x000,
109 [WMC_INPUT_CTRL] = 0x033, 106 [WMC_INPUT_CTRL] = 0x033,
110 [WMC_LEFT_INP_PGA_GAIN_CTRL] = 0x010, 107 [WMC_LEFT_INP_PGA_GAIN_CTRL] = 0x010 | WMC_VU | WMC_ZC,
111 [WMC_RIGHT_INP_PGA_GAIN_CTRL] = 0x010, 108 [WMC_RIGHT_INP_PGA_GAIN_CTRL] = 0x010 | WMC_VU | WMC_ZC,
112 [WMC_LEFT_ADC_BOOST_CTRL] = 0x100, 109 [WMC_LEFT_ADC_BOOST_CTRL] = 0x100,
113 [WMC_RIGHT_ADC_BOOST_CTRL] = 0x100, 110 [WMC_RIGHT_ADC_BOOST_CTRL] = 0x100,
114 [WMC_OUTPUT_CTRL] = 0x002, 111 [WMC_OUTPUT_CTRL] = 0x002,
@@ -129,7 +126,7 @@ struct
129 bool ahw_mute; 126 bool ahw_mute;
130} wmc_vol = 127} wmc_vol =
131{ 128{
132 HW_VOL_MUTE, HW_VOL_MUTE, false 129 0, 0, false
133}; 130};
134 131
135static void wmc_write(unsigned int reg, unsigned int val) 132static void wmc_write(unsigned int reg, unsigned int val)
@@ -180,6 +177,27 @@ int tenthdb2master(int db)
180 } 177 }
181} 178}
182 179
180int sound_val2phys(int setting, int value)
181{
182 int result;
183
184 switch (setting)
185 {
186#ifdef HAVE_RECORDING
187 case SOUND_LEFT_GAIN:
188 case SOUND_RIGHT_GAIN:
189 case SOUND_MIC_GAIN:
190 result = value * 5;
191 break;
192#endif
193
194 default:
195 result = value;
196 }
197
198 return result;
199}
200
183void audiohw_preinit(void) 201void audiohw_preinit(void)
184{ 202{
185 /* 1. Turn on external power supplies. Wait for supply voltage to settle. */ 203 /* 1. Turn on external power supplies. Wait for supply voltage to settle. */
@@ -190,13 +208,12 @@ void audiohw_preinit(void)
190 sleep(HZ/10); 208 sleep(HZ/10);
191 209
192 /* 2. Mute all analogue outputs */ 210 /* 2. Mute all analogue outputs */
193 wmc_set(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE | HW_VOL_ANA_MIN); 211 wmc_set(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE);
194 wmc_set(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE | HW_VOL_ANA_MIN); 212 wmc_set(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE);
195 wmc_set(WMC_LOUT2_SPK_VOLUME_CTRL, WMC_MUTE); 213 wmc_set(WMC_LOUT2_SPK_VOLUME_CTRL, WMC_MUTE);
196 wmc_set(WMC_ROUT2_SPK_VOLUME_CTRL, WMC_MUTE); 214 wmc_set(WMC_ROUT2_SPK_VOLUME_CTRL, WMC_MUTE);
197 wmc_set(WMC_OUT3_MIXER_CTRL, WMC_MUTE); 215 wmc_set(WMC_OUT3_MIXER_CTRL, WMC_MUTE);
198 wmc_set(WMC_OUT4_MONO_MIXER_CTRL, WMC_MUTE); 216 wmc_set(WMC_OUT4_MONO_MIXER_CTRL, WMC_MUTE);
199 wmc_set(WMC_INPUT_CTRL, 0x000);
200 217
201 /* 3. Set L/RMIXEN = 1 and DACENL/R = 1 in register R3. */ 218 /* 3. Set L/RMIXEN = 1 and DACENL/R = 1 in register R3. */
202 wmc_write(WMC_POWER_MANAGEMENT3, 219 wmc_write(WMC_POWER_MANAGEMENT3,
@@ -226,103 +243,105 @@ void audiohw_postinit(void)
226 wmc_write(WMC_AUDIO_INTERFACE, WMC_WL_16 | WMC_FMT_I2S); 243 wmc_write(WMC_AUDIO_INTERFACE, WMC_WL_16 | WMC_FMT_I2S);
227 wmc_write(WMC_DAC_CONTROL, WMC_DACOSR_128 | WMC_AMUTE); 244 wmc_write(WMC_DAC_CONTROL, WMC_DACOSR_128 | WMC_AMUTE);
228 245
229 wmc_set(WMC_INPUT_CTRL, WMC_R2_2INPPGA | WMC_L2_2INPPGA); 246 /* No ADC, no HP filter, no popping */
230 /* set PGA volumes to 0dB and enable zero cross */ 247 wmc_clear(WMC_ADC_CONTROL, WMC_HPFEN);
231 wmc_set(WMC_LEFT_INP_PGA_GAIN_CTRL, 0x10 | 1 << 7); 248
232 wmc_set(WMC_RIGHT_INP_PGA_GAIN_CTRL, 0x10 | 1 << 7); 249 wmc_clear(WMC_LEFT_ADC_BOOST_CTRL, WMC_PGABOOSTL);
233 /* write to INPPGAUPDATE to actually change voulme */ 250 wmc_clear(WMC_RIGHT_ADC_BOOST_CTRL, WMC_PGABOOSTR);
234 wmc_set(WMC_LEFT_INP_PGA_GAIN_CTRL, 1 << 8);
235 wmc_set(WMC_RIGHT_INP_PGA_GAIN_CTRL, 1 << 8);
236 /* set boost gain to 0dB */
237 wmc_set(WMC_LEFT_ADC_BOOST_CTRL, (5 << 4));
238 wmc_set(WMC_RIGHT_ADC_BOOST_CTRL, (5 << 4));
239 251
240 /* Specific to HW clocking */ 252 /* Specific to HW clocking */
241 wmc_write_masked(WMC_CLOCK_GEN_CTRL, WMC_BCLKDIV_4 | WMC_MS, 253 wmc_write_masked(WMC_CLOCK_GEN_CTRL, WMC_BCLKDIV_4 | WMC_MS,
242 WMC_BCLKDIV | WMC_MS | WMC_CLKSEL); 254 WMC_BCLKDIV | WMC_MS | WMC_CLKSEL);
243 audiohw_set_frequency(HW_FREQ_DEFAULT); 255 audiohw_set_frequency(HW_FREQ_DEFAULT);
244 256
245 /* ADC silenced */
246 wmc_write_masked(WMC_LEFT_ADC_DIGITAL_VOL, 0x00, WMC_DVOL);
247 wmc_write_masked(WMC_RIGHT_ADC_DIGITAL_VOL, 0x00, WMC_DVOL);
248
249 audiohw_enable_headphone_jack(true); 257 audiohw_enable_headphone_jack(true);
250} 258}
251 259
252void audiohw_set_headphone_vol(int vol_l, int vol_r) 260static void get_headphone_levels(int val, int *dac_p, int *hp_p,
261 int *mix_p, int *boost_p)
253{ 262{
254 int prev_l = wmc_vol.vol_l; 263 int dac, hp, mix, boost;
255 int prev_r = wmc_vol.vol_r;
256 int dac_l, dac_r;
257
258 wmc_vol.vol_l = vol_l;
259 wmc_vol.vol_r = vol_r;
260 264
261 /* When analogue volume falls below -57dB (0x00) start attenuating the 265 if (val >= 33)
262 * DAC volume */
263 if (vol_l >= HW_VOL_DIG_THRESHOLD)
264 { 266 {
265 if (vol_l > HW_VOL_MAX) 267 dac = 255;
266 vol_l = HW_VOL_MAX; 268 hp = val - 33;
267 269 mix = 7;
268 dac_l = HW_VOL_DIG_MAX; 270 boost = 5;
269 vol_l -= HW_VOL_DIG_THRESHOLD; 271 }
272 else if (val >= 21)
273 {
274 dac = 189 + val / 3 * 6;
275 hp = val % 3;
276 mix = 7;
277 boost = (val - 18) / 3;
270 } 278 }
271 else 279 else
272 { 280 {
273 if (vol_l < HW_VOL_MIN) 281 dac = 189 + val / 3 * 6;
274 vol_l = HW_VOL_MIN; 282 hp = val % 3;
275 283 mix = val / 3;
276 dac_l = 2*vol_l + HW_VOL_DIG_MIN; 284 boost = 1;
277 vol_l = HW_VOL_ANA_MIN;
278 } 285 }
279 286
280 if (vol_r >= HW_VOL_DIG_THRESHOLD) 287 *dac_p = dac;
281 { 288 *hp_p = hp;
282 if (vol_r > HW_VOL_MAX) 289 *mix_p = mix;
283 vol_r = HW_VOL_MAX; 290 *boost_p = boost;
291}
284 292
285 dac_r = HW_VOL_DIG_MAX; 293void audiohw_set_headphone_vol(int vol_l, int vol_r)
286 vol_r -= HW_VOL_DIG_THRESHOLD; 294{
287 } 295 int prev_l = wmc_vol.vol_l;
288 else 296 int prev_r = wmc_vol.vol_r;
289 { 297 int dac_l, dac_r, hp_l, hp_r;
290 if (vol_r < HW_VOL_MIN) 298 int mix_l, mix_r, boost_l, boost_r;
291 vol_r = HW_VOL_MIN;
292 299
293 dac_r = 2*vol_r + HW_VOL_DIG_MIN; 300 wmc_vol.vol_l = vol_l;
294 vol_r = HW_VOL_ANA_MIN; 301 wmc_vol.vol_r = vol_r;
295 } 302
303 /* Mixers are synced to provide full volume range on both the analogue
304 * and digital pathways */
305 get_headphone_levels(vol_l, &dac_l, &hp_l, &mix_l, &boost_l);
306 get_headphone_levels(vol_r, &dac_r, &hp_r, &mix_r, &boost_r);
296 307
297 /* Have to write both channels always to have the latching work */ 308 wmc_write_masked(WMC_LEFT_MIXER_CTRL, WMC_BYPLMIXVOLw(mix_l),
309 WMC_BYPLMIXVOL);
310 wmc_write_masked(WMC_LEFT_ADC_BOOST_CTRL,
311 WMC_L2_2BOOSTVOLw(boost_l), WMC_L2_2BOOSTVOL);
298 wmc_write_masked(WMC_LEFT_DAC_DIGITAL_VOL, dac_l, WMC_DVOL); 312 wmc_write_masked(WMC_LEFT_DAC_DIGITAL_VOL, dac_l, WMC_DVOL);
299 wmc_write_masked(WMC_LOUT1_HP_VOLUME_CTRL, vol_l, WMC_AVOL); 313 wmc_write_masked(WMC_LOUT1_HP_VOLUME_CTRL, hp_l, WMC_AVOL);
314
315 wmc_write_masked(WMC_RIGHT_MIXER_CTRL, WMC_BYPRMIXVOLw(mix_r),
316 WMC_BYPRMIXVOL);
317 wmc_write_masked(WMC_RIGHT_ADC_BOOST_CTRL,
318 WMC_R2_2BOOSTVOLw(boost_r), WMC_R2_2BOOSTVOL);
300 wmc_write_masked(WMC_RIGHT_DAC_DIGITAL_VOL, dac_r, WMC_DVOL); 319 wmc_write_masked(WMC_RIGHT_DAC_DIGITAL_VOL, dac_r, WMC_DVOL);
301 wmc_write_masked(WMC_ROUT1_HP_VOLUME_CTRL, vol_r, WMC_AVOL); 320 wmc_write_masked(WMC_ROUT1_HP_VOLUME_CTRL, hp_r, WMC_AVOL);
302 321
303 if (wmc_vol.vol_l > HW_VOL_MUTE) 322 if (vol_l > 0)
304 { 323 {
305 /* Not muted and going up from mute level? */ 324 /* Not muted and going up from mute level? */
306 if (prev_l <= HW_VOL_MUTE && !wmc_vol.ahw_mute) 325 if (prev_l <= 0 && !wmc_vol.ahw_mute)
307 wmc_clear(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE); 326 wmc_clear(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE);
308 } 327 }
309 else 328 else
310 { 329 {
311 /* Going to mute level? */ 330 /* Going to mute level? */
312 if (prev_l > HW_VOL_MUTE) 331 if (prev_l > 0)
313 wmc_set(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE); 332 wmc_set(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE);
314 } 333 }
315 334
316 if (wmc_vol.vol_r > HW_VOL_MUTE) 335 if (vol_r > 0)
317 { 336 {
318 /* Not muted and going up from mute level? */ 337 /* Not muted and going up from mute level? */
319 if (prev_r <= HW_VOL_MIN && !wmc_vol.ahw_mute) 338 if (prev_r <= 0 && !wmc_vol.ahw_mute)
320 wmc_clear(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE); 339 wmc_clear(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE);
321 } 340 }
322 else 341 else
323 { 342 {
324 /* Going to mute level? */ 343 /* Going to mute level? */
325 if (prev_r > HW_VOL_MUTE) 344 if (prev_r > 0)
326 wmc_set(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE); 345 wmc_set(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE);
327 } 346 }
328} 347}
@@ -358,10 +377,10 @@ void audiohw_mute(bool mute)
358 else 377 else
359 { 378 {
360 /* Unmute outputs not at mute level */ 379 /* Unmute outputs not at mute level */
361 if (wmc_vol.vol_l > HW_VOL_MUTE) 380 if (wmc_vol.vol_l > 0)
362 wmc_clear(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE); 381 wmc_clear(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE);
363 382
364 if (wmc_vol.vol_r > HW_VOL_MUTE) 383 if (wmc_vol.vol_r > 0)
365 wmc_clear(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE); 384 wmc_clear(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE);
366 } 385 }
367} 386}
@@ -491,9 +510,72 @@ void audiohw_set_frequency(int fsel)
491} 510}
492 511
493#ifdef HAVE_RECORDING 512#ifdef HAVE_RECORDING
494/* TODO */ 513void audiohw_set_recsrc(int source, bool recording)
514{
515 switch (source)
516 {
517 case AUDIO_SRC_PLAYBACK:
518 /* Disable all audio paths but DAC */
519 /* Disable ADCs */
520 wmc_clear(WMC_ADC_CONTROL, WMC_HPFEN);
521 wmc_clear(WMC_POWER_MANAGEMENT2, WMC_ADCENL | WMC_ADCENR);
522 /* Disable bypass */
523 wmc_clear(WMC_LEFT_MIXER_CTRL, WMC_BYPL2LMIX);
524 wmc_clear(WMC_RIGHT_MIXER_CTRL, WMC_BYPR2RMIX);
525 /* Disable IP BOOSTMIX and PGA */
526 wmc_clear(WMC_POWER_MANAGEMENT2, WMC_INPPGAENL | WMC_INPPGAENR |
527 WMC_BOOSTENL | WMC_BOOSTENR);
528 wmc_clear(WMC_INPUT_CTRL, WMC_L2_2INPPGA | WMC_R2_2INPPGA);
529 wmc_clear(WMC_LEFT_ADC_BOOST_CTRL, WMC_PGABOOSTL);
530 wmc_clear(WMC_RIGHT_ADC_BOOST_CTRL, WMC_PGABOOSTR);
531 break;
532
533 case AUDIO_SRC_FMRADIO:
534 if (recording)
535 {
536 /* Disable bypass */
537 wmc_clear(WMC_LEFT_MIXER_CTRL, WMC_BYPL2LMIX);
538 wmc_clear(WMC_RIGHT_MIXER_CTRL, WMC_BYPR2RMIX);
539 /* Enable ADCs, IP BOOSTMIX and PGA, route L/R2 through PGA */
540 wmc_set(WMC_POWER_MANAGEMENT2, WMC_ADCENL | WMC_ADCENR |
541 WMC_BOOSTENL | WMC_BOOSTENR | WMC_INPPGAENL |
542 WMC_INPPGAENR);
543 wmc_set(WMC_ADC_CONTROL, WMC_ADCOSR | WMC_HPFEN);
544 /* PGA at 0dB with +20dB boost */
545 wmc_write_masked(WMC_LEFT_INP_PGA_GAIN_CTRL, 0x10, WMC_AVOL);
546 wmc_write_masked(WMC_RIGHT_INP_PGA_GAIN_CTRL, 0x10, WMC_AVOL);
547 wmc_set(WMC_LEFT_ADC_BOOST_CTRL, WMC_PGABOOSTL);
548 wmc_set(WMC_RIGHT_ADC_BOOST_CTRL, WMC_PGABOOSTR);
549 /* Connect L/R2 inputs to PGA */
550 wmc_set(WMC_INPUT_CTRL, WMC_L2_2INPPGA | WMC_R2_2INPPGA);
551 }
552 else
553 {
554 /* Disable PGA and ADC, enable IP BOOSTMIX, route L/R2 directly to
555 * IP BOOSTMIX */
556 wmc_clear(WMC_ADC_CONTROL, WMC_HPFEN);
557 wmc_write_masked(WMC_POWER_MANAGEMENT2, WMC_BOOSTENL | WMC_BOOSTENR,
558 WMC_BOOSTENL | WMC_BOOSTENR | WMC_INPPGAENL |
559 WMC_INPPGAENR | WMC_ADCENL | WMC_ADCENR);
560 wmc_clear(WMC_INPUT_CTRL, WMC_L2_2INPPGA | WMC_R2_2INPPGA);
561 wmc_clear(WMC_LEFT_ADC_BOOST_CTRL, WMC_PGABOOSTL);
562 wmc_clear(WMC_RIGHT_ADC_BOOST_CTRL, WMC_PGABOOSTR);
563 /* Enable bypass to L/R mixers */
564 wmc_set(WMC_LEFT_MIXER_CTRL, WMC_BYPL2LMIX);
565 wmc_set(WMC_RIGHT_MIXER_CTRL, WMC_BYPR2RMIX);
566 }
567 break;
568 }
569}
570
495void audiohw_set_recvol(int left, int right, int type) 571void audiohw_set_recvol(int left, int right, int type)
496{ 572{
497 (void)left; (void)right; (void)type; 573 switch (type)
574 {
575 case AUDIO_GAIN_LINEIN:
576 wmc_write_masked(WMC_LEFT_ADC_DIGITAL_VOL, left + 239, WMC_DVOL);
577 wmc_write_masked(WMC_RIGHT_ADC_DIGITAL_VOL, right + 239, WMC_DVOL);
578 return;
579 }
498} 580}
499#endif 581#endif /* HAVE_RECORDING */
diff --git a/firmware/drivers/tuner/si4700.c b/firmware/drivers/tuner/si4700.c
index fb8fc49749..1e2c1888e0 100644
--- a/firmware/drivers/tuner/si4700.c
+++ b/firmware/drivers/tuner/si4700.c
@@ -47,7 +47,6 @@ extern int si4700_st(void);
47#endif 47#endif
48 48
49#define SEEK_THRESHOLD 0x16 49#define SEEK_THRESHOLD 0x16
50#define TUNER_VOLUME 0xC
51 50
52#define I2C_ADR 0x20 51#define I2C_ADR 0x20
53 52
@@ -332,10 +331,9 @@ static void si4700_sleep(int snooze)
332 SYSCONFIG1_GPIO1 | SYSCONFIG1_GPIO2 | 331 SYSCONFIG1_GPIO1 | SYSCONFIG1_GPIO2 |
333 SYSCONFIG1_GPIO3); 332 SYSCONFIG1_GPIO3);
334#endif 333#endif
335 /* -6dB volume, seek threshold 12 */
336 si4700_write_masked(SYSCONFIG2, 334 si4700_write_masked(SYSCONFIG2,
337 SYSCONFIG2_SKEETHw(SEEK_THRESHOLD) | 335 SYSCONFIG2_SKEETHw(SEEK_THRESHOLD) |
338 SYSCONFIG2_VOLUMEw(TUNER_VOLUME), 336 SYSCONFIG2_VOLUMEw(0xF),
339 SYSCONFIG2_VOLUME | SYSCONFIG2_SEEKTH); 337 SYSCONFIG2_VOLUME | SYSCONFIG2_SEEKTH);
340 } 338 }
341} 339}
diff --git a/firmware/export/config-gigabeat-s.h b/firmware/export/config-gigabeat-s.h
index 69028822f0..64f351c1ce 100644
--- a/firmware/export/config-gigabeat-s.h
+++ b/firmware/export/config-gigabeat-s.h
@@ -79,12 +79,25 @@
79/* Define this if you have the WM8978 audio codec */ 79/* Define this if you have the WM8978 audio codec */
80#define HAVE_WM8978 80#define HAVE_WM8978
81 81
82/* Define bitmask of input sources - recordable bitmask can be defined
83 explicitly if different */
82#define INPUT_SRC_CAPS SRC_CAP_FMRADIO 84#define INPUT_SRC_CAPS SRC_CAP_FMRADIO
83 85
86/* define the bitmask of hardware sample rates */
84#define HW_SAMPR_CAPS (SAMPR_CAP_48 | SAMPR_CAP_44 | SAMPR_CAP_32 | \ 87#define HW_SAMPR_CAPS (SAMPR_CAP_48 | SAMPR_CAP_44 | SAMPR_CAP_32 | \
85 SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16 | \ 88 SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16 | \
86 SAMPR_CAP_12 | SAMPR_CAP_11 | SAMPR_CAP_8) 89 SAMPR_CAP_12 | SAMPR_CAP_11 | SAMPR_CAP_8)
87 90
91/* define the bitmask of recording sample rates */
92#define REC_SAMPR_CAPS HW_SAMPR_CAPS /* Same as playback */
93
94/* define default recording levels */
95#define DEFAULT_REC_LEFT_GAIN 0
96#define DEFAULT_REC_RIGHT_GAIN 0
97
98/* Define this if you have recording capability */
99#define HAVE_RECORDING
100
88/* Define this if your LCD can be put to sleep. */ 101/* Define this if your LCD can be put to sleep. */
89#define HAVE_LCD_SLEEP 102#define HAVE_LCD_SLEEP
90/* We don't use a setting but a fixed delay after the backlight has 103/* We don't use a setting but a fixed delay after the backlight has
diff --git a/firmware/export/imx31l.h b/firmware/export/imx31l.h
index 5a964b8e2e..b572c788a9 100755
--- a/firmware/export/imx31l.h
+++ b/firmware/export/imx31l.h
@@ -777,7 +777,7 @@
777#define AUDMUX_PTCR_TCSEL_PORT6 (0x5 << 22) 777#define AUDMUX_PTCR_TCSEL_PORT6 (0x5 << 22)
778#define AUDMUX_PTCR_TCSEL_PORT7 (0x6 << 22) 778#define AUDMUX_PTCR_TCSEL_PORT7 (0x6 << 22)
779 779
780#define AUDMUX_PTCR_RFSDIR (1 << 21) 780#define AUDMUX_PTCR_RFS_DIR (1 << 21)
781 781
782#define AUDMUX_PTCR_RFSSEL (0xf << 17) 782#define AUDMUX_PTCR_RFSSEL (0xf << 17)
783#define AUDMUX_PTCR_RFSSEL_TXFS (0x0 << 17) 783#define AUDMUX_PTCR_RFSSEL_TXFS (0x0 << 17)
@@ -922,7 +922,7 @@
922#define SSI_SIER_TLS (0x1 << 5) 922#define SSI_SIER_TLS (0x1 << 5)
923#define SSI_SIER_RLS (0x1 << 4) 923#define SSI_SIER_RLS (0x1 << 4)
924#define SSI_SIER_RFF1 (0x1 << 3) 924#define SSI_SIER_RFF1 (0x1 << 3)
925#define SSI_SIER_RFF2 (0x1 << 2) 925#define SSI_SIER_RFF0 (0x1 << 2)
926#define SSI_SIER_TFE1 (0x1 << 1) 926#define SSI_SIER_TFE1 (0x1 << 1)
927#define SSI_SIER_TFE0 (0x1 << 0) 927#define SSI_SIER_TFE0 (0x1 << 0)
928 928
diff --git a/firmware/export/wm8978.h b/firmware/export/wm8978.h
index f444c96a29..5d9c0d01e7 100644
--- a/firmware/export/wm8978.h
+++ b/firmware/export/wm8978.h
@@ -29,6 +29,7 @@
29int tenthdb2master(int db); 29int tenthdb2master(int db);
30void audiohw_set_headphone_vol(int vol_l, int vol_r); 30void audiohw_set_headphone_vol(int vol_l, int vol_r);
31void audiohw_set_frequency(int fsel); 31void audiohw_set_frequency(int fsel);
32void audiohw_set_recsrc(int source, bool recording);
32 33
33void wmc_set(unsigned int reg, unsigned int bits); 34void wmc_set(unsigned int reg, unsigned int bits);
34void wmc_clear(unsigned int reg, unsigned int bits); 35void wmc_clear(unsigned int reg, unsigned int bits);
@@ -108,8 +109,8 @@ void wmc_clear(unsigned int reg, unsigned int bits);
108 109
109/* Volums masks and macros for analogue volumes */ 110/* Volums masks and macros for analogue volumes */
110#define WMC_AVOL 0x3f 111#define WMC_AVOL 0x3f
111#define WMC_AVOLr(x) ((x) & WMC_AVOLUME_MASK) 112#define WMC_AVOLr(x) ((x) & WMC_AVOL)
112#define WMC_AVOLw(x) ((x) & WMC_AVOLUME_MASK) 113#define WMC_AVOLw(x) ((x) & WMC_AVOL)
113 114
114/* WMC_SOFTWARE_RESET (0x00) */ 115/* WMC_SOFTWARE_RESET (0x00) */
115#define WMC_RESET 116#define WMC_RESET
@@ -457,8 +458,8 @@ void wmc_clear(unsigned int reg, unsigned int bits);
457#define WMC_PGABOOSTL (1 << 8) 458#define WMC_PGABOOSTL (1 << 8)
458 /* 000=disabled, 001=-12dB, 010=-9dB...111=+6dB */ 459 /* 000=disabled, 001=-12dB, 010=-9dB...111=+6dB */
459#define WMC_L2_2BOOSTVOL (7 << 4) 460#define WMC_L2_2BOOSTVOL (7 << 4)
460 #define WMC_L2_2BOOSTVOLr(x) ((x) & WMC_L2_2_BOOSTVOL) >> 4) 461 #define WMC_L2_2BOOSTVOLr(x) (((x) & WMC_L2_2BOOSTVOL) >> 4)
461 #define WMC_L2_2BOOSTVOLw(x) ((x) << 4) & WMC_L2_2_BOOSTVOL) 462 #define WMC_L2_2BOOSTVOLw(x) (((x) << 4) & WMC_L2_2BOOSTVOL)
462 /* 000=disabled, 001=-12dB, 010=-9dB...111=+6dB */ 463 /* 000=disabled, 001=-12dB, 010=-9dB...111=+6dB */
463#define WMC_AUXL2BOOSTVOL (7 << 0) 464#define WMC_AUXL2BOOSTVOL (7 << 0)
464 #define WMC_AUXL2BOOSTVOLr(x) ((x) & WMC_AUXL2BOOSTVOL) 465 #define WMC_AUXL2BOOSTVOLr(x) ((x) & WMC_AUXL2BOOSTVOL)
@@ -467,9 +468,9 @@ void wmc_clear(unsigned int reg, unsigned int bits);
467/* WMC_RIGHT_ADC_BOOST_CTRL (0x30) */ 468/* WMC_RIGHT_ADC_BOOST_CTRL (0x30) */
468#define WMC_PGABOOSTR (1 << 8) 469#define WMC_PGABOOSTR (1 << 8)
469 /* 000=disabled, 001=-12dB, 010=-9dB...111=+6dB */ 470 /* 000=disabled, 001=-12dB, 010=-9dB...111=+6dB */
470#define WMC_R2_2_BOOSTVOL (7 << 4) 471#define WMC_R2_2BOOSTVOL (7 << 4)
471 #define WMC_R2_2BOOSTVOLr(x) ((x) & WMC_R2_2_BOOSTVOL) >> 4) 472 #define WMC_R2_2BOOSTVOLr(x) (((x) & WMC_R2_2BOOSTVOL) >> 4)
472 #define WMC_R2_2BOOSTVOLw(x) ((x) << 4) & WMC_R2_2_BOOSTVOL) 473 #define WMC_R2_2BOOSTVOLw(x) (((x) << 4) & WMC_R2_2BOOSTVOL)
473 /* 000=disabled, 001=-12dB, 010=-9dB...111=+6dB */ 474 /* 000=disabled, 001=-12dB, 010=-9dB...111=+6dB */
474#define WMC_AUXR2BOOSTVOL (7 << 0) 475#define WMC_AUXR2BOOSTVOL (7 << 0)
475 #define WMC_AUXR2BOOSTVOLr(x) ((x) & WMC_AUXR2BOOSTVOL) 476 #define WMC_AUXR2BOOSTVOLr(x) ((x) & WMC_AUXR2BOOSTVOL)
@@ -487,26 +488,26 @@ void wmc_clear(unsigned int reg, unsigned int bits);
487/* WMC_LEFT_MIXER_CTRL (0x32) */ 488/* WMC_LEFT_MIXER_CTRL (0x32) */
488 /* 000=-15dB, 001=-12dB...101=0dB, 110=+3dB, 111=+6dB */ 489 /* 000=-15dB, 001=-12dB...101=0dB, 110=+3dB, 111=+6dB */
489#define WMC_AUXLMIXVOL (7 << 6) 490#define WMC_AUXLMIXVOL (7 << 6)
490 #define WMC_AUXLMIXVOLr(x) ((x) & WMC_AUXLMIXVOL) >> 6) 491 #define WMC_AUXLMIXVOLr(x) (((x) & WMC_AUXLMIXVOL) >> 6)
491 #define WMC_AUXLMIXVOLw(x) ((x) << 6) & WMC_AUXLMIXVOL) 492 #define WMC_AUXLMIXVOLw(x) (((x) << 6) & WMC_AUXLMIXVOL)
492#define WMC_AUXL2LMIX (1 << 5) 493#define WMC_AUXL2LMIX (1 << 5)
493 /* 000=-15dB, 001=-12dB...101=0dB, 110=+3dB, 111=+6dB */ 494 /* 000=-15dB, 001=-12dB...101=0dB, 110=+3dB, 111=+6dB */
494#define WMC_BYPLMIXVOL (7 << 2) 495#define WMC_BYPLMIXVOL (7 << 2)
495 #define WMC_BYPLMIXVOLr(x) ((x) & WMC_BYPLMIXVOL) >> 2) 496 #define WMC_BYPLMIXVOLr(x) (((x) & WMC_BYPLMIXVOL) >> 2)
496 #define WMC_BYPLMIXVOLw(x) ((x) << 2) & WMC_BYPLMIXVOL) 497 #define WMC_BYPLMIXVOLw(x) (((x) << 2) & WMC_BYPLMIXVOL)
497#define WMC_BYPL2LMIX (1 << 1) 498#define WMC_BYPL2LMIX (1 << 1)
498#define WMC_DACL2LMIX (1 << 0) 499#define WMC_DACL2LMIX (1 << 0)
499 500
500/* WMC_RIGHT_MIXER_CTRL (0x33) */ 501/* WMC_RIGHT_MIXER_CTRL (0x33) */
501 /* 000=-15dB, 001=-12dB...101=0dB, 110=+3dB, 111=+6dB */ 502 /* 000=-15dB, 001=-12dB...101=0dB, 110=+3dB, 111=+6dB */
502#define WMC_AUXRMIXVOL (7 << 6) 503#define WMC_AUXRMIXVOL (7 << 6)
503 #define WMC_AUXRMIXVOLr(x) ((x) & WMC_AUXRMIXVOL) >> 6) 504 #define WMC_AUXRMIXVOLr(x) (((x) & WMC_AUXRMIXVOL) >> 6)
504 #define WMC_AUXRMIXVOLw(x) ((x) << 6) & WMC_AUXRMIXVOL) 505 #define WMC_AUXRMIXVOLw(x) (((x) << 6) & WMC_AUXRMIXVOL)
505#define WMC_AUXR2RMIX (1 << 5) 506#define WMC_AUXR2RMIX (1 << 5)
506 /* 000=-15dB, 001=-12dB...101=0dB, 110=+3dB, 111=+6dB */ 507 /* 000=-15dB, 001=-12dB...101=0dB, 110=+3dB, 111=+6dB */
507#define WMC_BYPRMIXVOL (7 << 2) 508#define WMC_BYPRMIXVOL (7 << 2)
508 #define WMC_BYPRMIXVOLr(x) ((x) & WMC_BYPRMIXVOL) >> 2) 509 #define WMC_BYPRMIXVOLr(x) (((x) & WMC_BYPRMIXVOL) >> 2)
509 #define WMC_BYPRMIXVOLw(x) ((x) << 2) & WMC_BYPRMIXVOL) 510 #define WMC_BYPRMIXVOLw(x) (((x) << 2) & WMC_BYPRMIXVOL)
510#define WMC_BYPR2RMIX (1 << 1) 511#define WMC_BYPR2RMIX (1 << 1)
511#define WMC_DACR2RMIX (1 << 0) 512#define WMC_DACR2RMIX (1 << 0)
512 513
@@ -518,14 +519,12 @@ void wmc_clear(unsigned int reg, unsigned int bits);
518 /* Uses WMC_AVOL* macros */ 519 /* Uses WMC_AVOL* macros */
519 520
520/* WMC_OUT3_MIXER_CTRL (0x38) */ 521/* WMC_OUT3_MIXER_CTRL (0x38) */
521#define WMC_OUT3MUTE (1 << 6)
522#define WMC_OUT42OUT3 (1 << 3) 522#define WMC_OUT42OUT3 (1 << 3)
523#define WMC_BYPL2OUT3 (1 << 2) 523#define WMC_BYPL2OUT3 (1 << 2)
524#define WMC_LMIX2OUT3 (1 << 1) 524#define WMC_LMIX2OUT3 (1 << 1)
525#define WMC_LDAC2OUT3 (1 << 0) 525#define WMC_LDAC2OUT3 (1 << 0)
526 526
527/* WMC_OUT4_MONO_MIXER_CTRL (0x39) */ 527/* WMC_OUT4_MONO_MIXER_CTRL (0x39) */
528#define WMC_OUT4MUTE (1 << 6)
529#define WMC_HALFSIG (1 << 5) 528#define WMC_HALFSIG (1 << 5)
530#define WMC_LMIX2OUT4 (1 << 4) 529#define WMC_LMIX2OUT4 (1 << 4)
531#define WMC_LDAC2OUT4 (1 << 3) 530#define WMC_LDAC2OUT4 (1 << 3)
diff --git a/firmware/sound.c b/firmware/sound.c
index 60ec1a405b..5125782308 100644
--- a/firmware/sound.c
+++ b/firmware/sound.c
@@ -629,7 +629,7 @@ void sound_set(int setting, int value)
629#if (!defined(HAVE_AS3514) && !defined(HAVE_WM8975) \ 629#if (!defined(HAVE_AS3514) && !defined(HAVE_WM8975) \
630 && !defined(HAVE_WM8758) && !defined(HAVE_TSC2100) \ 630 && !defined(HAVE_WM8758) && !defined(HAVE_TSC2100) \
631 && !defined (HAVE_WM8711) && !defined (HAVE_WM8721) \ 631 && !defined (HAVE_WM8711) && !defined (HAVE_WM8721) \
632 && !defined (HAVE_WM8731)) \ 632 && !defined (HAVE_WM8731) && !defined (HAVE_WM8978)) \
633 || defined(SIMULATOR) 633 || defined(SIMULATOR)
634int sound_val2phys(int setting, int value) 634int sound_val2phys(int setting, int value)
635{ 635{
@@ -708,6 +708,24 @@ int sound_val2phys(int setting, int value)
708 } 708 }
709 709
710 return result; 710 return result;
711#elif defined(HAVE_WM8978)
712 int result;
713
714 switch (setting)
715 {
716#ifdef HAVE_RECORDING
717 case SOUND_LEFT_GAIN:
718 case SOUND_RIGHT_GAIN:
719 case SOUND_MIC_GAIN:
720 result = value * 5;
721 break;
722#endif
723
724 default:
725 result = value;
726 }
727
728 return result;
711#else 729#else
712 (void)setting; 730 (void)setting;
713 return value; 731 return value;
diff --git a/firmware/target/arm/imx31/gigabeat-s/audio-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/audio-gigabeat-s.c
index 6dd90bfdb7..87b59de599 100644
--- a/firmware/target/arm/imx31/gigabeat-s/audio-gigabeat-s.c
+++ b/firmware/target/arm/imx31/gigabeat-s/audio-gigabeat-s.c
@@ -18,39 +18,55 @@
18 * KIND, either express or implied. 18 * KIND, either express or implied.
19 * 19 *
20 ****************************************************************************/ 20 ****************************************************************************/
21#include "wm8978.h" 21#include "config.h"
22#include "system.h"
23#include "audiohw.h"
22#include "audio.h" 24#include "audio.h"
23 25
26/* Set the audio source for IIS TX */
24void audio_set_output_source(int source) 27void audio_set_output_source(int source)
25{ 28{
26 (void)source; /* TODO */ 29 switch (source)
30 {
31 default:
32 case AUDIO_SRC_PLAYBACK:
33 /* Receive data from PORT1 (SSI1) */
34 AUDMUX_PDCR4 = AUDMUX_PDCR_RXDSEL_PORT1;
35 /* wmc_clear(WMC_COMPANDING_CTRL, WMC_LOOPBACK); */
36 break;
37
38 case AUDIO_SRC_FMRADIO:
39 /* External source - receive data from self (loopback to TX) */
40 AUDMUX_PDCR4 = AUDMUX_PDCR_RXDSEL_PORT4;
41 /* wmc_set(WMC_COMPANDING_CTRL, WMC_LOOPBACK); */
42 break;
43 }
27} 44}
28 45
29void audio_input_mux(int source, unsigned int flags) 46void audio_input_mux(int source, unsigned int flags)
30{ 47{
31 (void)flags; 48 /* Prevent pops from unneeded switching */
49 static int last_source = AUDIO_SRC_PLAYBACK;
50 bool recording = flags & SRCF_RECORDING;
51 static bool last_recording = false;
52
32 switch (source) 53 switch (source)
33 { 54 {
34 case AUDIO_SRC_PLAYBACK: 55 default:
35 /* deselect bypass patths and set volume to -15dB */ 56 source = AUDIO_SRC_PLAYBACK;
36 wmc_clear(WMC_LEFT_MIXER_CTRL, (WMC_BYPL2LMIX) | (7<<2)); 57 /* Fallthrough */
37 wmc_clear(WMC_RIGHT_MIXER_CTRL, (WMC_BYPR2RMIX) | (7<<2)); 58 case AUDIO_SRC_PLAYBACK: /* playback - no recording */
38 /* disable L2/R2 inputs and boost stage */ 59 if (source != last_source)
39 wmc_clear(WMC_POWER_MANAGEMENT2, 60 audiohw_set_recsrc(AUDIO_SRC_PLAYBACK, false);
40 WMC_INPPGAENR | WMC_INPPGAENL | WMC_BOOSTENL | WMC_BOOSTENR);
41 break; 61 break;
42 62
43 case AUDIO_SRC_FMRADIO: 63 case AUDIO_SRC_FMRADIO: /* recording and playback */
44 /* enable L2/R2 inputs and boost stage */ 64 if (source != last_source || recording != last_recording)
45 wmc_set(WMC_POWER_MANAGEMENT2, 65 audiohw_set_recsrc(AUDIO_SRC_FMRADIO, recording);
46 WMC_INPPGAENR | WMC_INPPGAENL | WMC_BOOSTENL | WMC_BOOSTENR);
47 /* select bypass patths and set volume to 0dB */
48 wmc_set(WMC_LEFT_MIXER_CTRL, (WMC_BYPL2LMIX) | (5<<2));
49 wmc_set(WMC_RIGHT_MIXER_CTRL, (WMC_BYPR2RMIX) | (5<<2));
50 break; 66 break;
51
52 default:
53 source = AUDIO_SRC_PLAYBACK;
54 } 67 }
68
69 last_source = source;
70 last_recording = recording;
55} 71}
56 72
diff --git a/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c b/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c
index 4710e2d82b..76369a0c6a 100644
--- a/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c
+++ b/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c
@@ -111,17 +111,12 @@ void pcm_play_dma_init(void)
111 SSI_SCR2 &= ~SSI_SCR_SSIEN; 111 SSI_SCR2 &= ~SSI_SCR_SSIEN;
112 SSI_SCR1 &= ~SSI_SCR_SSIEN; 112 SSI_SCR1 &= ~SSI_SCR_SSIEN;
113 113
114 SSI_SIER1 = SSI_SIER_TFE0;
115 SSI_SIER2 = 0;
116
117 /* Set up audio mux */ 114 /* Set up audio mux */
118 115
119 /* Port 1 (internally connected to SSI1) 116 /* Port 1 (internally connected to SSI1)
120 * All clocking is output sourced from port 4 */ 117 * All clocking is output sourced from port 4 */
121 AUDMUX_PTCR1 = AUDMUX_PTCR_TFS_DIR | AUDMUX_PTCR_TFSEL_PORT4 | 118 AUDMUX_PTCR1 = AUDMUX_PTCR_TFS_DIR | AUDMUX_PTCR_TFSEL_PORT4 |
122 AUDMUX_PTCR_TCLKDIR | AUDMUX_PTCR_TCSEL_PORT4 | 119 AUDMUX_PTCR_TCLKDIR | AUDMUX_PTCR_TCSEL_PORT4 |
123 AUDMUX_PTCR_RFSDIR | AUDMUX_PTCR_RFSSEL_PORT4 |
124 AUDMUX_PTCR_RCLKDIR | AUDMUX_PTCR_RCSEL_PORT4 |
125 AUDMUX_PTCR_SYN; 120 AUDMUX_PTCR_SYN;
126 121
127 /* Receive data from port 4 */ 122 /* Receive data from port 4 */
@@ -133,18 +128,22 @@ void pcm_play_dma_init(void)
133 /* Receive data from port 1 */ 128 /* Receive data from port 1 */
134 AUDMUX_PDCR4 = AUDMUX_PDCR_RXDSEL_PORT1; 129 AUDMUX_PDCR4 = AUDMUX_PDCR_RXDSEL_PORT1;
135 130
136 /* Port 2 (internally connected to SSI2) routes clocking to port 5 to 131 /* PORT2 (internally connected to SSI2) routes clocking to PORT5 to
137 * provide MCLK to the codec */ 132 * provide MCLK to the codec */
138 /* All port 2 clocks are inputs taken from SSI2 */ 133 /* TX clocks are inputs taken from SSI2 */
139 AUDMUX_PTCR2 = 0; 134 /* RX clocks are outputs taken from PORT4 */
140 AUDMUX_PDCR2 = 0; 135 AUDMUX_PTCR2 = AUDMUX_PTCR_RFS_DIR | AUDMUX_PTCR_RFSSEL_PORT4 |
141 /* Port 5 outputs TCLK sourced from port 2 */ 136 AUDMUX_PTCR_RCLKDIR | AUDMUX_PTCR_RCSEL_PORT4;
137 /* RX data taken from PORT4 */
138 AUDMUX_PDCR2 = AUDMUX_PDCR_RXDSEL_PORT4;
139
140 /* PORT5 outputs TCLK sourced from PORT2 (SSI2) */
142 AUDMUX_PTCR5 = AUDMUX_PTCR_TCLKDIR | AUDMUX_PTCR_TCSEL_PORT2; 141 AUDMUX_PTCR5 = AUDMUX_PTCR_TCLKDIR | AUDMUX_PTCR_TCSEL_PORT2;
143 AUDMUX_PDCR5 = 0; 142 AUDMUX_PDCR5 = 0;
144 143
145 /* Setup SSIs */ 144 /* Setup SSIs */
146 145
147 /* SSI1 - interface for all I2S data */ 146 /* SSI1 - SoC software interface for all I2S data out */
148 SSI_SCR1 = SSI_SCR_SYN | SSI_SCR_I2S_MODE_SLAVE; 147 SSI_SCR1 = SSI_SCR_SYN | SSI_SCR_I2S_MODE_SLAVE;
149 SSI_STCR1 = SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSI | 148 SSI_STCR1 = SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSI |
150 SSI_STCR_TEFS | SSI_STCR_TFEN0; 149 SSI_STCR_TEFS | SSI_STCR_TFEN0;
@@ -153,26 +152,11 @@ void pcm_play_dma_init(void)
153 SSI_STCCR1 = SSI_STRCCR_WL16 | SSI_STRCCR_DCw(2-1) | 152 SSI_STCCR1 = SSI_STRCCR_WL16 | SSI_STRCCR_DCw(2-1) |
154 SSI_STRCCR_PMw(4-1); 153 SSI_STRCCR_PMw(4-1);
155 154
155 /* Transmit low watermark - 2 samples in FIFO */
156 SSI_SFCSR1 = SSI_SFCSR_TFWM1w(1) | SSI_SFCSR_TFWM0w(2);
156 SSI_STMSK1 = 0; 157 SSI_STMSK1 = 0;
157 158
158 /* Receive */ 159 /* SSI2 - provides MCLK to codec. Receives data from codec. */
159 SSI_SRCR1 = SSI_SRCR_RXBIT0 | SSI_SRCR_RSCKP | SSI_SRCR_RFSI |
160 SSI_SRCR_REFS | SSI_SRCR_RFEN0;
161
162 /* 16 bits per word, 2 words per frame */
163 SSI_SRCCR1 = SSI_STRCCR_WL16 | SSI_STRCCR_DCw(2-1) |
164 SSI_STRCCR_PMw(4-1);
165
166 /* Receive high watermark - 6 samples in FIFO
167 * Transmit low watermark - 2 samples in FIFO */
168 SSI_SFCSR1 = SSI_SFCSR_RFWM1w(8) | SSI_SFCSR_TFWM1w(1) |
169 SSI_SFCSR_RFWM0w(6) | SSI_SFCSR_TFWM0w(2);
170
171 SSI_SRMSK1 = 0;
172
173 /* SSI2 - provides MCLK only */
174 SSI_SCR2 = 0;
175 SSI_SRCR2 = 0;
176 SSI_STCR2 = SSI_STCR_TXDIR; 160 SSI_STCR2 = SSI_STCR_TXDIR;
177 161
178 /* f(INT_BIT_CLK) = 162 /* f(INT_BIT_CLK) =
@@ -189,6 +173,20 @@ void pcm_play_dma_init(void)
189 */ 173 */
190 SSI_STCCR2 = SSI_STRCCR_DIV2 | SSI_STRCCR_PMw(1-1); 174 SSI_STCCR2 = SSI_STRCCR_DIV2 | SSI_STRCCR_PMw(1-1);
191 175
176 /* SSI2 - receive - asynchronous clocks */
177 SSI_SCR2 = SSI_SCR_I2S_MODE_SLAVE;
178
179 SSI_SRCR2 = SSI_SRCR_RXBIT0 | SSI_SRCR_RSCKP | SSI_SRCR_RFSI |
180 SSI_SRCR_REFS;
181
182 /* 16 bits per word, 2 words per frame */
183 SSI_SRCCR2 = SSI_STRCCR_WL16 | SSI_STRCCR_DCw(2-1) |
184 SSI_STRCCR_PMw(4-1);
185
186 /* Receive high watermark - 6 samples in FIFO */
187 SSI_SFCSR2 = SSI_SFCSR_RFWM1w(8) | SSI_SFCSR_RFWM0w(6);
188 SSI_SRMSK2 = 0;
189
192 /* Enable SSI2 (codec clock) */ 190 /* Enable SSI2 (codec clock) */
193 SSI_SCR2 |= SSI_SCR_SSIEN; 191 SSI_SCR2 |= SSI_SCR_SSIEN;
194 192
@@ -210,7 +208,8 @@ static void play_start_pcm(void)
210 dma_play_data.state = 1; 208 dma_play_data.state = 1;
211 209
212 /* Fill the FIFO or start when data is used up */ 210 /* Fill the FIFO or start when data is used up */
213 SSI_SCR1 |= SSI_SCR_SSIEN; /* Enable SSI */ 211 SSI_SCR1 |= SSI_SCR_SSIEN; /* Enable SSI */
212 SSI_STCR1 |= SSI_STCR_TFEN0; /* Enable TX FIFO */
214 213
215 while (1) 214 while (1)
216 { 215 {
@@ -235,6 +234,7 @@ static void play_stop_pcm(void)
235 while (SSI_SFCSR_TFCNT0r(SSI_SFCSR1) > 0); 234 while (SSI_SFCSR_TFCNT0r(SSI_SFCSR1) > 0);
236 235
237 /* Disable transmission */ 236 /* Disable transmission */
237 SSI_STCR1 &= ~SSI_STCR_TFEN0;
238 SSI_SCR1 &= ~(SSI_SCR_TE | SSI_SCR_SSIEN); 238 SSI_SCR1 &= ~(SSI_SCR_TE | SSI_SCR_SSIEN);
239 239
240 /* Do not enable interrupt on unlock */ 240 /* Do not enable interrupt on unlock */
@@ -285,4 +285,113 @@ const void * pcm_play_dma_get_peak_buffer(int *count)
285 return (void *)((addr + 2) & ~3); 285 return (void *)((addr + 2) & ~3);
286} 286}
287 287
288/* Any recording functionality should be implemented similarly */ 288#ifdef HAVE_RECORDING
289static struct dma_data dma_rec_data =
290{
291 /* Initialize to a locked, stopped state */
292 .p = NULL,
293 .size = 0,
294 .locked = 0,
295 .state = 0
296};
297
298static void __attribute__((interrupt("IRQ"))) SSI2_HANDLER(void)
299{
300 register pcm_more_callback_type2 more_ready;
301
302 while (dma_rec_data.size > 0)
303 {
304 if (SSI_SFCSR_RFCNT0r(SSI_SFCSR2) < 2)
305 return;
306
307 *dma_rec_data.p++ = SSI_SRX0_2;
308 *dma_rec_data.p++ = SSI_SRX0_2;
309 dma_rec_data.size -= 4;
310 }
311
312 more_ready = pcm_callback_more_ready;
313
314 if (more_ready == NULL || more_ready(0) < 0) {
315 /* Finished recording */
316 pcm_rec_dma_stop();
317 pcm_rec_dma_stopped_callback();
318 }
319}
320
321void pcm_rec_lock(void)
322{
323 if (++dma_rec_data.locked == 1)
324 {
325 /* Atomically disable receive interrupt */
326 imx31_regclr32(&SSI_SIER2, SSI_SIER_RIE);
327 }
328}
329
330void pcm_rec_unlock(void)
331{
332 if (--dma_rec_data.locked == 0 && dma_rec_data.state != 0)
333 {
334 /* Atomically enable receive interrupt */
335 imx31_regset32(&SSI_SIER2, SSI_SIER_RIE);
336 }
337}
338
339void pcm_record_more(void *start, size_t size)
340{
341 pcm_rec_peak_addr = start; /* Start peaking at dest */
342 dma_rec_data.p = start; /* Start of RX buffer */
343 dma_rec_data.size = size; /* Bytes to transfer */
344}
345
346void pcm_rec_dma_stop(void)
347{
348 /* Stop receiving data */
349 SSI_SCR2 &= ~SSI_SCR_RE; /* Disable RX */
350 SSI_SRCR2 &= ~SSI_SRCR_RFEN0; /* Disable RX FIFO */
351
352 dma_rec_data.state = 0;
353
354 avic_disable_int(SSI2);
355}
356
357void pcm_rec_dma_start(void *addr, size_t size)
358{
359 pcm_rec_dma_stop();
360
361 pcm_rec_peak_addr = addr;
362 dma_rec_data.p = addr;
363 dma_rec_data.size = size;
364
365 dma_rec_data.state = 1;
366
367 avic_enable_int(SSI2, IRQ, 9, SSI2_HANDLER);
368
369 SSI_SRCR2 |= SSI_SRCR_RFEN0; /* Enable RX FIFO */
370
371 /* Ensure clear FIFO */
372 while (SSI_SFCSR2 & SSI_SFCSR_RFCNT0)
373 SSI_SRX0_2;
374
375 /* Enable receive */
376 SSI_SCR2 |= SSI_SCR_RE;
377}
378
379void pcm_rec_dma_close(void)
380{
381 pcm_rec_dma_stop();
382}
383
384void pcm_rec_dma_init(void)
385{
386 pcm_rec_dma_stop();
387}
388
389const void * pcm_rec_dma_get_peak_buffer(int *count)
390{
391 unsigned long addr = (uint32_t)pcm_rec_peak_addr;
392 unsigned long end = (uint32_t)dma_rec_data.p;
393 *count = (end >> 2) - (addr >> 2);
394 return (void *)(addr & ~3);
395}
396
397#endif /* HAVE_RECORDING */