diff options
-rw-r--r-- | apps/keymaps/keymap-gigabeat-s.c | 21 | ||||
-rw-r--r-- | apps/recorder/recording.c | 46 | ||||
-rw-r--r-- | firmware/drivers/audio/wm8978.c | 244 | ||||
-rw-r--r-- | firmware/drivers/tuner/si4700.c | 4 | ||||
-rw-r--r-- | firmware/export/config-gigabeat-s.h | 13 | ||||
-rwxr-xr-x | firmware/export/imx31l.h | 4 | ||||
-rw-r--r-- | firmware/export/wm8978.h | 33 | ||||
-rw-r--r-- | firmware/sound.c | 20 | ||||
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/audio-gigabeat-s.c | 56 | ||||
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c | 171 |
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 **/ | ||
255 | static 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 | |||
254 | static const struct button_mapping button_context_keyboard[] = { | 271 | static 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 | ||
275 | static void set_gain(void) | 275 | static 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. */ | ||
43 | extern void audiohw_enable_headphone_jack(bool enable); | 36 | extern void audiohw_enable_headphone_jack(bool enable); |
44 | 37 | ||
45 | const struct sound_settings_info audiohw_settings[] = | 38 | const 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 | ||
135 | static void wmc_write(unsigned int reg, unsigned int val) | 132 | static void wmc_write(unsigned int reg, unsigned int val) |
@@ -180,6 +177,27 @@ int tenthdb2master(int db) | |||
180 | } | 177 | } |
181 | } | 178 | } |
182 | 179 | ||
180 | int 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 | |||
183 | void audiohw_preinit(void) | 201 | void 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 | ||
252 | void audiohw_set_headphone_vol(int vol_l, int vol_r) | 260 | static 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; | 293 | void 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 */ | 513 | void 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 | |||
495 | void audiohw_set_recvol(int left, int right, int type) | 571 | void 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 @@ | |||
29 | int tenthdb2master(int db); | 29 | int tenthdb2master(int db); |
30 | void audiohw_set_headphone_vol(int vol_l, int vol_r); | 30 | void audiohw_set_headphone_vol(int vol_l, int vol_r); |
31 | void audiohw_set_frequency(int fsel); | 31 | void audiohw_set_frequency(int fsel); |
32 | void audiohw_set_recsrc(int source, bool recording); | ||
32 | 33 | ||
33 | void wmc_set(unsigned int reg, unsigned int bits); | 34 | void wmc_set(unsigned int reg, unsigned int bits); |
34 | void wmc_clear(unsigned int reg, unsigned int bits); | 35 | void 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) |
634 | int sound_val2phys(int setting, int value) | 634 | int 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 */ | ||
24 | void audio_set_output_source(int source) | 27 | void 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 | ||
29 | void audio_input_mux(int source, unsigned int flags) | 46 | void 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 |
289 | static 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 | |||
298 | static 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 | |||
321 | void 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 | |||
330 | void 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 | |||
339 | void 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 | |||
346 | void 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 | |||
357 | void 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 | |||
379 | void pcm_rec_dma_close(void) | ||
380 | { | ||
381 | pcm_rec_dma_stop(); | ||
382 | } | ||
383 | |||
384 | void pcm_rec_dma_init(void) | ||
385 | { | ||
386 | pcm_rec_dma_stop(); | ||
387 | } | ||
388 | |||
389 | const 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 */ | ||