summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2007-05-02 22:33:24 +0000
committerMichael Sevakis <jethead71@rockbox.org>2007-05-02 22:33:24 +0000
commit3c38fe420426695ae33b4f579eb283d95fc17f04 (patch)
tree8e630707768e021523fb171237fa5edd55c108d2
parentaa220d5acdbfd8178580e7eb503c205406e2be74 (diff)
downloadrockbox-3c38fe420426695ae33b4f579eb283d95fc17f04.tar.gz
rockbox-3c38fe420426695ae33b4f579eb283d95fc17f04.zip
Gigabeat: Separate driver for audio codec. Tweak pcm driver to comply with intended interface.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@13307 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--firmware/SOURCES4
-rw-r--r--firmware/drivers/audio/wm8751.c195
-rw-r--r--firmware/export/config-gigabeat.h4
-rw-r--r--firmware/export/sound.h4
-rw-r--r--firmware/export/wm8751.h209
-rw-r--r--firmware/sound.c18
-rw-r--r--firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c285
-rw-r--r--firmware/target/arm/system-arm.h19
8 files changed, 585 insertions, 153 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES
index d281e54fa5..aeb98a3562 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -195,7 +195,9 @@ drivers/mas.c
195#ifndef SIMULATOR 195#ifndef SIMULATOR
196#if defined(HAVE_UDA1380) 196#if defined(HAVE_UDA1380)
197drivers/audio/uda1380.c 197drivers/audio/uda1380.c
198#elif defined(HAVE_WM8975) || defined(HAVE_WM8751) 198#elif defined(HAVE_WM8751)
199drivers/audio/wm8751.c
200#elif defined(HAVE_WM8975)
199drivers/audio/wm8975.c 201drivers/audio/wm8975.c
200#elif defined(HAVE_WM8758) 202#elif defined(HAVE_WM8758)
201drivers/audio/wm8758.c 203drivers/audio/wm8758.c
diff --git a/firmware/drivers/audio/wm8751.c b/firmware/drivers/audio/wm8751.c
new file mode 100644
index 0000000000..21ff4728b3
--- /dev/null
+++ b/firmware/drivers/audio/wm8751.c
@@ -0,0 +1,195 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Driver for WM8751 audio codec
11 *
12 * Based on code from the ipodlinux project - http://ipodlinux.org/
13 * Adapted for Rockbox in December 2005
14 *
15 * Original file: linux/arch/armnommu/mach-ipod/audio.c
16 *
17 * Copyright (c) 2003-2005 Bernard Leach (leachbj@bouncycastle.org)
18 *
19 * All files in this archive are subject to the GNU General Public License.
20 * See the file COPYING in the source tree root for full license agreement.
21 *
22 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
23 * KIND, either express or implied.
24 *
25 ****************************************************************************/
26#include "kernel.h"
27#include "wmcodec.h"
28#include "i2s.h"
29#include "audio.h"
30#include "sound.h"
31
32/* Flags used in combination with settings */
33
34/* use zero crossing to reduce clicks during volume changes */
35#define LOUT1_BITS (LOUT1_LO1ZC)
36/* latch left volume first then update left+right together */
37#define ROUT1_BITS (ROUT1_RO1ZC | ROUT1_RO1VU)
38#define LOUT2_BITS (LOUT2_LO2ZC)
39#define ROUT2_BITS (ROUT2_RO2ZC | ROUT2_RO2VU)
40/* We use linear bass control with 200 Hz cutoff */
41#define BASSCTRL_BITS (BASSCTRL_BC)
42/* We use linear treble control with 4 kHz cutoff */
43#define TREBCTRL_BITS (TREBCTRL_TC)
44
45/* convert tenth of dB volume (-730..60) to master volume register value */
46int tenthdb2master(int db)
47{
48 /* +6 to -73dB 1dB steps (plus mute == 80levels) 7bits */
49 /* 1111111 == +6dB (0x7f) */
50 /* 1111001 == 0dB (0x79) */
51 /* 0110000 == -73dB (0x30) */
52 /* 0101111..0000000 == mute (<= 0x2f) */
53 if (db < VOLUME_MIN)
54 return 0x0;
55 else
56 return (db / 10) + 73 + 0x30;
57}
58
59/* convert tenth of dB volume (-780..0) to mixer volume register value */
60int tenthdb2mixer(int db)
61{
62 (void)db;
63 return 0;
64}
65
66static int tone_tenthdb2hw(int value)
67{
68 /* -6.0db..+0db..+9.0db step 1.5db - translate -60..+0..+90 step 15
69 to 10..6..0 step -1.
70 */
71 value = 10 - (value + 60) / 15;
72
73 if (value == 6)
74 value = 0xf; /* 0db -> off */
75
76 return value;
77}
78
79void audiohw_reset(void);
80
81/* Silently enable / disable audio output */
82void audiohw_enable_output(bool enable)
83{
84 if (enable)
85 {
86 /* reset the I2S controller into known state */
87 i2s_reset();
88
89 /*
90 * 1. Switch on power supplies.
91 * By default the WM87551L is in Standby Mode, the DAC is
92 * digitally muted and the Audio Interface, Line outputs
93 * and Headphone outputs are all OFF (DACMU = 1 Power
94 * Management registers 1 and 2 are all zeros).
95 */
96 wmcodec_write(RESET, RESET_RESET); /*Reset*/
97
98 /* 2. Enable Vmid and VREF. */
99 wmcodec_write(PWRMGMT1, PWRMGMT1_VREF | PWRMGMT1_VMIDSEL_500K);
100
101 /* From app notes: allow Vref to stabilize to reduce clicks */
102 sleep(HZ/2);
103
104 /* 3. Enable DACs as required. */
105 wmcodec_write(PWRMGMT2, PWRMGMT2_DACL | PWRMGMT2_DACR);
106
107 /* 4. Enable line and / or headphone output buffers as required. */
108 wmcodec_write(PWRMGMT2, PWRMGMT2_DACL | PWRMGMT2_DACR |
109 PWRMGMT2_LOUT1 | PWRMGMT2_ROUT1 | PWRMGMT2_LOUT2 |
110 PWRMGMT2_ROUT2);
111
112 /* BCLKINV=0(Dont invert BCLK) MS=1(Enable Master) LRSWAP=0 LRP=0 */
113 /* IWL=00(16 bit) FORMAT=10(I2S format) */
114 wmcodec_write(AINTFCE, AINTFCE_MS | AINTFCE_WL_16 |
115 AINTFCE_FORMAT_I2S);
116
117 /* Keep it quiet */
118 wmcodec_write(LOUT1, LOUT1_BITS | OUTPUT_MUTED);
119 wmcodec_write(ROUT1, ROUT1_BITS | OUTPUT_MUTED);
120 wmcodec_write(LOUT2, LOUT2_BITS | OUTPUT_MUTED);
121 wmcodec_write(ROUT2, ROUT2_BITS | OUTPUT_MUTED);
122
123 wmcodec_write(LEFTMIX1, LEFTMIX1_LD2LO | LEFTMIX1_LI2LO_DEFAULT);
124 wmcodec_write(RIGHTMIX2, RIGHTMIX2_RD2RO | RIGHTMIX2_RI2RO_DEFAULT);
125 }
126
127 audiohw_mute(!enable);
128}
129
130int audiohw_set_master_vol(int vol_l, int vol_r)
131{
132 /* +6 to -73dB 1dB steps (plus mute == 80levels) 7bits */
133 /* 1111111 == +6dB */
134 /* 1111001 == 0dB */
135 /* 0110000 == -73dB */
136 /* 0101111 == mute (0x2f) */
137
138 wmcodec_write(LOUT1, LOUT1_BITS | LOUT1_LOUT1VOL(vol_l));
139 wmcodec_write(ROUT1, ROUT1_BITS | ROUT1_ROUT1VOL(vol_r));
140 return 0;
141}
142
143int audiohw_set_lineout_vol(int vol_l, int vol_r)
144{
145 wmcodec_write(LOUT2, LOUT2_BITS | LOUT2_LOUT2VOL(vol_l));
146 wmcodec_write(ROUT2, ROUT2_BITS | ROUT2_ROUT2VOL(vol_r));
147 return 0;
148}
149
150int audiohw_set_mixer_vol(int channel1, int channel2)
151{
152 (void)channel1;
153 (void)channel2;
154
155 return 0;
156}
157
158void audiohw_set_bass(int value)
159{
160 wmcodec_write(BASSCTRL, BASSCTRL_BITS |
161 BASSCTRL_BASS(tone_tenthdb2hw(value)));
162}
163
164void audiohw_set_treble(int value)
165{
166 wmcodec_write(TREBCTRL, TREBCTRL_BITS |
167 TREBCTRL_TREB(tone_tenthdb2hw(value)));
168}
169
170int audiohw_mute(int mute)
171{
172 /* Mute: Set DACMU = 1 to soft-mute the audio DACs. */
173 /* Unmute: Set DACMU = 0 to soft-un-mute the audio DACs. */
174 wmcodec_write(DACCTRL, mute ? DACCTRL_DACMU : 0);
175 return 0;
176}
177
178/* Nice shutdown of WM8751 codec */
179void audiohw_close(void)
180{
181 /* 1. Set DACMU = 1 to soft-mute the audio DACs. */
182 audiohw_mute(true);
183
184 /* 2. Disable all output buffers. */
185 wmcodec_write(PWRMGMT2, 0x0);
186
187 /* 3. Switch off the power supplies. */
188 wmcodec_write(PWRMGMT1, 0x0);
189}
190
191/* Note: Disable output before calling this function */
192void audiohw_set_frequency(int fsel)
193{
194 wmcodec_write(CLOCKING, fsel);
195}
diff --git a/firmware/export/config-gigabeat.h b/firmware/export/config-gigabeat.h
index 6a6e1d2977..cd2fac360c 100644
--- a/firmware/export/config-gigabeat.h
+++ b/firmware/export/config-gigabeat.h
@@ -133,9 +133,7 @@
133#define BOOTFILE "rockbox." BOOTFILE_EXT 133#define BOOTFILE "rockbox." BOOTFILE_EXT
134#define BOOTDIR "/.rockbox" 134#define BOOTDIR "/.rockbox"
135 135
136#define HW_SAMPR_CAPS (SAMPR_CAP_96 | SAMPR_CAP_88 | SAMPR_CAP_48 | \ 136#define HW_SAMPR_CAPS (SAMPR_CAP_88 | SAMPR_CAP_44 | SAMPR_CAP_22 | \
137 SAMPR_CAP_44 | SAMPR_CAP_32 | SAMPR_CAP_24 | \
138 SAMPR_CAP_22 | SAMPR_CAP_16 | SAMPR_CAP_12 | \
139 SAMPR_CAP_11 | SAMPR_CAP_8) 137 SAMPR_CAP_11 | SAMPR_CAP_8)
140 138
141#endif 139#endif
diff --git a/firmware/export/sound.h b/firmware/export/sound.h
index 7589338da9..b060b97312 100644
--- a/firmware/export/sound.h
+++ b/firmware/export/sound.h
@@ -22,7 +22,9 @@
22#include <inttypes.h> 22#include <inttypes.h>
23#ifdef HAVE_UDA1380 23#ifdef HAVE_UDA1380
24#include "uda1380.h" 24#include "uda1380.h"
25#elif defined(HAVE_WM8975) || defined(HAVE_WM8751) 25#elif defined(HAVE_WM8751)
26#include "wm8751.h"
27#elif defined(HAVE_WM8975)
26#include "wm8975.h" 28#include "wm8975.h"
27#elif defined(HAVE_WM8758) 29#elif defined(HAVE_WM8758)
28#include "wm8758.h" 30#include "wm8758.h"
diff --git a/firmware/export/wm8751.h b/firmware/export/wm8751.h
new file mode 100644
index 0000000000..244d378b26
--- /dev/null
+++ b/firmware/export/wm8751.h
@@ -0,0 +1,209 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 by Dave Chapman
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19#ifndef _WM8751_H
20#define _WM8751_H
21
22/* volume/balance/treble/bass interdependency */
23#define VOLUME_MIN -730
24#define VOLUME_MAX 60
25
26extern int tenthdb2master(int db);
27extern int tenthdb2mixer(int db);
28
29extern void audiohw_reset(void);
30extern int audiohw_init(void);
31extern void audiohw_enable_output(bool enable);
32extern int audiohw_set_master_vol(int vol_l, int vol_r);
33extern int audiohw_set_lineout_vol(int vol_l, int vol_r);
34extern int audiohw_set_mixer_vol(int channel1, int channel2);
35extern void audiohw_set_bass(int value);
36extern void audiohw_set_treble(int value);
37extern int audiohw_mute(int mute);
38extern void audiohw_close(void);
39extern void audiohw_set_frequency(int fsel);
40
41/* Register addresses and bits */
42#define OUTPUT_MUTED 0x2f
43#define OUTPUT_0DB 0x79
44
45#define LOUT1 0x02
46#define LOUT1_LOUT1VOL_MASK (0x07f << 0)
47#define LOUT1_LOUT1VOL(x) ((x) & 0x7f)
48#define LOUT1_LO1ZC (1 << 7)
49#define LOUT1_LO1VU (1 << 8)
50
51#define ROUT1 0x03
52#define ROUT1_ROUT1VOL(x) ((x) & 0x7f)
53#define ROUT1_RO1ZC (1 << 7)
54#define ROUT1_RO1VU (1 << 8)
55
56#define DACCTRL 0x05
57#define DACCTRL_DEEMPH_NONE (0 << 1)
58#define DACCTRL_DEEMPH_32 (1 << 1)
59#define DACCTRL_DEEMPH_44 (2 << 1)
60#define DACCTRL_DEEMPH_48 (3 << 1)
61#define DACCTRL_DEEMPH(x) ((x) & (0x3 << 1))
62#define DACCTRL_DACMU (1 << 3)
63#define DACCTRL_DAT (1 << 7)
64
65#define AINTFCE 0x07
66#define AINTFCE_FORMAT_RJUST (0 << 0)
67#define AINTFCE_FORMAT_LJUST (1 << 0)
68#define AINTFCE_FORMAT_I2S (2 << 0)
69#define AINTFCE_FORMAT_DSP (3 << 0)
70#define AINTFCE_FORMAT(x) ((x) & 0x3)
71#define AINTFCE_WL_16 (0 << 2)
72#define AINTFCE_WL_20 (1 << 2)
73#define AINTFCE_WL_24 (2 << 2)
74#define AINTFCE_WL_32 (3 << 2)
75#define AINTFCE_WL(x) ((x) & (0x3 << 2))
76#define AINTFCE_LRP (1 << 4)
77#define AINTFCE_LRSWAP (1 << 5)
78#define AINTFCE_MS (1 << 6)
79#define AINTFCE_BCLKINV (1 << 7)
80
81#define CLOCKING 0x08
82#define CLOCKING_SR_USB (1 << 0)
83/* Register settings for the supported samplerates: */
84#define WM8975_8000HZ 0x4d
85#define WM8975_12000HZ 0x61
86#define WM8975_16000HZ 0x55
87#define WM8975_22050HZ 0x77
88#define WM8975_24000HZ 0x79
89#define WM8975_32000HZ 0x59
90#define WM8975_44100HZ 0x63
91#define WM8975_48000HZ 0x41
92#define WM8975_88200HZ 0x7f
93#define WM8975_96000HZ 0x5d
94#define CLOCKING_SR(x) ((x) & (0x1f << 1))
95#define CLOCKING_MCLK_DIV2 (1 << 6)
96#define CLOCKING_BCLK_DIV2 (1 << 7)
97
98#define LEFTGAIN 0x0a
99#define LEFTGAIN_LDACVOL(x) ((x) & 0xff)
100#define LEFTGAIN_LDVU (1 << 8)
101
102#define RIGHTGAIN 0x0b
103#define RIGHTGAIN_LDACVOL(x) ((x) & 0xff)
104#define RIGHTGAIN_LDVU (1 << 8)
105
106#define BASSCTRL 0x0c
107#define BASSCTRL_BASS(x) ((x) & 0xf)
108#define BASSCTRL_BC (1 << 7)
109#define BASSCTRL_BB (1 << 8)
110
111#define TREBCTRL 0x0d
112#define TREBCTRL_TREB(x) ((x) & 0xf)
113#define TREBCTRL_TC (1 << 7)
114
115#define RESET 0x0f
116#define RESET_RESET 0x000
117
118#define ADDITIONAL1 0x17
119#define ADDITIONAL1_TOEN (1 << 0)
120#define ADDITIONAL1_DACINV (1 << 1)
121#define ADDITIONAL1_DMONOMIX_LLRR (0 << 4)
122#define ADDITIONAL1_DMONOMIX_ML0R (1 << 4)
123#define ADDITIONAL1_DMONOMIX_0LMR (2 << 4)
124#define ADDITIONAL1_DMONOMIX_MLMR (3 << 4)
125#define ADDITIONAL1_DMONOMIX(x) ((x) & (0x03 << 4))
126#define ADDITIONAL1_VSEL_LOWEST (0 << 6)
127#define ADDITIONAL1_VSEL_LOW (1 << 6)
128#define ADDITIONAL1_VSEL_DEFAULT2 (2 << 6)
129#define ADDITIONAL1_VSEL_DEFAULT (3 << 6)
130#define ADDITIONAL1_VSEL(x) ((x) & (0x3 << 6))
131#define ADDITIONAL1_TSDEN (1 << 7)
132
133#define ADDITIONAL2 0x18
134#define ADDITIONAL2_ROUT2INV (1 << 4)
135#define ADDITIONAL2_DACOSR (1 << 0)
136
137#define PWRMGMT1 0x19
138#define PWRMGMT1_DIGENB (1 << 0)
139#define PWRMGMT1_VREF (1 << 6)
140#define PWRMGMT1_VMIDSEL_DISABLED (0 << 7)
141#define PWRMGMT1_VMIDSEL_50K (1 << 7)
142#define PWRMGMT1_VMIDSEL_500K (2 << 7)
143#define PWRMGMT1_VMIDSEL_5K (3 << 7)
144#define PWRMGMT1_VMIDSEL(x) ((x) & (0x3 << 7))
145
146#define PWRMGMT2 0x1a
147#define PWRMGMT2_DACL (1 << 8)
148#define PWRMGMT2_DACR (1 << 7)
149#define PWRMGMT2_LOUT1 (1 << 6)
150#define PWRMGMT2_ROUT1 (1 << 5)
151#define PWRMGMT2_LOUT2 (1 << 4)
152#define PWRMGMT2_ROUT2 (1 << 3)
153#define PWRMGMT2_MOUT (1 << 2)
154#define PWRMGMT2_OUT3 (1 << 1)
155
156#define ADDITIONAL3 0x1b
157#define ADDITIONAL3_ADCLRM ((x) & (0x3 << 7))
158#define ADDITIONAL3_VROI (1 << 6)
159#define ADDITIONAL3_HPFLREN (1 << 5)
160
161#define LEFTMIX1 0x22
162#define LEFTMIX1_LD2LO (1 << 8)
163#define LEFTMIX1_LI2LO (1 << 7)
164#define LEFTMIX1_LI2LO_DEFAULT (5 << 4)
165#define LEFTMIX1_LI2LOVOL(x) ((x) & (0x7 << 4))
166
167#define LEFTMIX2 0x23
168#define LEFTMIX2_RD2LO (1 << 8)
169#define LEFTMIX2_MI2LO (1 << 7)
170#define LEFTMIX2_MI2LO_DEFAULT (5 << 4)
171#define LEFTMIX2_MI2LOVOL(x) ((x) & (0x7 << 4))
172
173#define RIGHTMIX1 0x24
174#define RIGHTMIX1_LD2RO (1 << 8)
175#define RIGHTMIX1_MI2RO (1 << 7)
176#define RIGHTMIX1_MI2RO_DEFAULT (5 << 4)
177#define RIGHTMIX1_MI2ROVOL(x) ((x) & (0x7 << 4))
178
179#define RIGHTMIX2 0x25
180#define RIGHTMIX2_RD2RO (1 << 8)
181#define RIGHTMIX2_RI2RO (1 << 7)
182#define RIGHTMIX2_RI2RO_DEFAULT (5 << 4)
183#define RIGHTMIX2_RI2ROVOL(x) ((x) & (0x7 << 4))
184
185#define MONOMIX1 0x26
186#define MONOMIX1_LI2MOVOL(x) ((x) & (0x7 << 4))
187#define MONOMIX1_LI2MO (1 << 7)
188#define MONOMIX1_LD2MO (1 << 8)
189#define MONOMIX1_DMEN (1 << 0)
190
191#define MONOMIX2 0x27
192#define MONOMIX2_RD2MO (1 << 8)
193#define MONOMIX2_RI2MO (1 << 7)
194#define MONOMIX2_RI2MOVOL(x) ((x) & (0x7 << 4))
195
196#define LOUT2 0x28
197#define LOUT2_LOUT2VOL(x) ((x) & 0x7f)
198#define LOUT2_LO2ZC (1 << 7)
199#define LOUT2_LO2VU (1 << 8)
200
201#define ROUT2 0x29
202#define ROUT2_ROUT2VOL(x) ((x) & 0x7f)
203#define ROUT2_RO2ZC (1 << 7)
204#define ROUT2_RO2VU (1 << 8)
205
206#define MONOOUT 0x2a
207#define MONOOUT_MOZC (1 << 7)
208
209#endif /* _WM8751_H */
diff --git a/firmware/sound.c b/firmware/sound.c
index 9835711b54..2589306290 100644
--- a/firmware/sound.c
+++ b/firmware/sound.c
@@ -64,7 +64,11 @@ static const struct sound_settings_info sound_settings_table[] = {
64 [SOUND_TREBLE] = {"dB", 0, 2, 0, 6, 0, sound_set_treble}, 64 [SOUND_TREBLE] = {"dB", 0, 2, 0, 6, 0, sound_set_treble},
65#elif defined(HAVE_TLV320) 65#elif defined(HAVE_TLV320)
66 [SOUND_VOLUME] = {"dB", 0, 1, -73, 6, -20, sound_set_volume}, 66 [SOUND_VOLUME] = {"dB", 0, 1, -73, 6, -20, sound_set_volume},
67#elif defined(HAVE_WM8975) || defined(HAVE_WM8751) 67#elif defined(HAVE_WM8751)
68 [SOUND_VOLUME] = {"dB", 0, 1, -74, 6, -25, sound_set_volume},
69 [SOUND_BASS] = {"dB", 1, 15, -60, 90, 0, sound_set_bass},
70 [SOUND_TREBLE] = {"dB", 1, 15, -60, 90, 0, sound_set_treble},
71#elif defined(HAVE_WM8975)
68 [SOUND_VOLUME] = {"dB", 0, 1, -74, 6, -25, sound_set_volume}, 72 [SOUND_VOLUME] = {"dB", 0, 1, -74, 6, -25, sound_set_volume},
69 [SOUND_BASS] = {"dB", 0, 1, -6, 9, 0, sound_set_bass}, 73 [SOUND_BASS] = {"dB", 0, 1, -6, 9, 0, sound_set_bass},
70 [SOUND_TREBLE] = {"dB", 0, 1, -6, 9, 0, sound_set_treble}, 74 [SOUND_TREBLE] = {"dB", 0, 1, -6, 9, 0, sound_set_treble},
@@ -514,8 +518,12 @@ void sound_set_bass(int value)
514 mas_writereg(MAS_REG_KBASS, bass_table[value+15]); 518 mas_writereg(MAS_REG_KBASS, bass_table[value+15]);
515 current_bass = value * 10; 519 current_bass = value * 10;
516 set_prescaled_volume(); 520 set_prescaled_volume();
521#elif defined(HAVE_WM8751)
522 current_bass = value;
523 audiohw_set_bass(value);
524 set_prescaled_volume();
517#elif defined HAVE_WM8975 || defined HAVE_WM8758 || defined(HAVE_UDA1380) \ 525#elif defined HAVE_WM8975 || defined HAVE_WM8758 || defined(HAVE_UDA1380) \
518 || defined HAVE_WM8731 || defined(HAVE_WM8721) || defined(HAVE_WM8751) 526 || defined HAVE_WM8731 || defined(HAVE_WM8721)
519 current_bass = value * 10; 527 current_bass = value * 10;
520 audiohw_set_bass(value); 528 audiohw_set_bass(value);
521 set_prescaled_volume(); 529 set_prescaled_volume();
@@ -540,8 +548,12 @@ void sound_set_treble(int value)
540 mas_writereg(MAS_REG_KTREBLE, treble_table[value+15]); 548 mas_writereg(MAS_REG_KTREBLE, treble_table[value+15]);
541 current_treble = value * 10; 549 current_treble = value * 10;
542 set_prescaled_volume(); 550 set_prescaled_volume();
551#elif defined(HAVE_WM8751)
552 audiohw_set_treble(value);
553 current_treble = value;
554 set_prescaled_volume();
543#elif defined(HAVE_WM8975) || defined(HAVE_WM8758) || defined(HAVE_UDA1380) \ 555#elif defined(HAVE_WM8975) || defined(HAVE_WM8758) || defined(HAVE_UDA1380) \
544 || defined(HAVE_WM8731) || defined(HAVE_WM8721) || defined(HAVE_WM8751) 556 || defined(HAVE_WM8731) || defined(HAVE_WM8721)
545 audiohw_set_treble(value); 557 audiohw_set_treble(value);
546 current_treble = value * 10; 558 current_treble = value * 10;
547 set_prescaled_volume(); 559 set_prescaled_volume();
diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c b/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c
index 0f22aa5c5c..2b4842f880 100644
--- a/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c
+++ b/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c
@@ -16,6 +16,7 @@
16 * KIND, either express or implied. 16 * KIND, either express or implied.
17 * 17 *
18 ****************************************************************************/ 18 ****************************************************************************/
19#include <stdlib.h>
19#include "system.h" 20#include "system.h"
20#include "kernel.h" 21#include "kernel.h"
21#include "logf.h" 22#include "logf.h"
@@ -24,20 +25,14 @@
24#include "file.h" 25#include "file.h"
25#include "mmu-meg-fx.h" 26#include "mmu-meg-fx.h"
26 27
27static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */ 28#define GIGABEAT_8000HZ (0x02 << 1)
28 29#define GIGABEAT_11025HZ (0x19 << 1)
29#define GIGABEAT_8000HZ 0x4d 30#define GIGABEAT_22050HZ (0x1b << 1)
30#define GIGABEAT_11025HZ 0x32 31#define GIGABEAT_44100HZ (0x11 << 1)
31#define GIGABEAT_12000HZ 0x61 32#define GIGABEAT_88200HZ (0x1f << 1)
32#define GIGABEAT_16000HZ 0x55
33#define GIGABEAT_22050HZ 0x36
34#define GIGABEAT_24000HZ 0x79
35#define GIGABEAT_32000HZ 0x59
36#define GIGABEAT_44100HZ 0x22
37#define GIGABEAT_48000HZ 0x41
38#define GIGABEAT_88200HZ 0x3e
39#define GIGABEAT_96000HZ 0x5d
40 33
34static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */
35static int sr_ctrl = GIGABEAT_44100HZ;
41#define FIFO_COUNT ((IISFCON >> 6) & 0x01F) 36#define FIFO_COUNT ((IISFCON >> 6) & 0x01F)
42 37
43/* number of bytes in FIFO */ 38/* number of bytes in FIFO */
@@ -49,50 +44,28 @@ static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */
49unsigned short * p; 44unsigned short * p;
50size_t p_size; 45size_t p_size;
51 46
52
53
54/* DMA count has hit zero - no more data */ 47/* DMA count has hit zero - no more data */
55/* Get more data from the callback and top off the FIFO */ 48/* Get more data from the callback and top off the FIFO */
56//void fiq(void) __attribute__ ((interrupt ("naked"))); 49//void fiq(void) __attribute__ ((interrupt ("naked")));
57void fiq(void) ICODE_ATTR __attribute__ ((interrupt ("FIQ"))); 50void fiq(void) ICODE_ATTR __attribute__ ((interrupt ("FIQ")));
58void fiq(void)
59{
60 /* clear any pending interrupt */
61 SRCPND = (1<<19);
62 51
63 /* Buffer empty. Try to get more. */ 52static void _pcm_apply_settings(void)
64 if (pcm_callback_for_more) 53{
65 { 54 static int last_freqency = 0;
66 pcm_callback_for_more((unsigned char**)&p, &p_size);
67 }
68 else
69 {
70 /* callback func is missing? */
71 pcm_play_dma_stop();
72 return;
73 }
74 55
75 if (p_size) 56 if (pcm_freq != last_freqency)
76 { 57 {
77 /* Flush any pending cache writes */ 58 last_freqency = pcm_freq;
78 clean_dcache_range(p, p_size); 59 audiohw_set_frequency(sr_ctrl);
79
80 /* set the new DMA values */
81 DCON2 = DMA_CONTROL_SETUP | (p_size >> 1);
82 DISRC2 = (int)p + 0x30000000;
83
84 /* Re-Activate the channel */
85 DMASKTRIG2 = 0x2;
86 } 60 }
87 else
88 {
89 /* No more DMA to do */
90 pcm_play_dma_stop();
91 }
92
93} 61}
94 62
95 63void pcm_apply_settings(void)
64{
65 int oldstatus = set_fiq_status(FIQ_DISABLED);
66 _pcm_apply_settings();
67 set_fiq_status(oldstatus);
68}
96 69
97void pcm_init(void) 70void pcm_init(void)
98{ 71{
@@ -103,8 +76,6 @@ void pcm_init(void)
103 audiohw_init(); 76 audiohw_init();
104 audiohw_enable_output(true); 77 audiohw_enable_output(true);
105 78
106 /* cannot use the WM8975 defaults since our clock is not the same */
107 /* the input master clock is 16.9344MHz - we can divide exact for that */
108 pcm_set_frequency(SAMPR_44); 79 pcm_set_frequency(SAMPR_44);
109 80
110 /* init GPIO */ 81 /* init GPIO */
@@ -130,6 +101,8 @@ void pcm_play_dma_start(const void *addr, size_t size)
130 /* sanity check: bad pointer or too small file */ 101 /* sanity check: bad pointer or too small file */
131 if (NULL == addr || size <= IIS_FIFO_SIZE) return; 102 if (NULL == addr || size <= IIS_FIFO_SIZE) return;
132 103
104 disable_fiq();
105
133 p = (unsigned short *)addr; 106 p = (unsigned short *)addr;
134 p_size = size; 107 p_size = size;
135 108
@@ -163,8 +136,11 @@ void pcm_play_dma_start(const void *addr, size_t size)
163 /* clear pending DMA interrupt */ 136 /* clear pending DMA interrupt */
164 SRCPND = 1<<19; 137 SRCPND = 1<<19;
165 138
139 pcm_playing = true;
140
141 _pcm_apply_settings();
142
166 set_fiq_handler(fiq); 143 set_fiq_handler(fiq);
167 enable_fiq();
168 144
169 /* unmask the DMA interrupt */ 145 /* unmask the DMA interrupt */
170 INTMSK &= ~(1<<19); 146 INTMSK &= ~(1<<19);
@@ -178,17 +154,13 @@ void pcm_play_dma_start(const void *addr, size_t size)
178 /* turn off the idle */ 154 /* turn off the idle */
179 IISCON &= ~(1<<3); 155 IISCON &= ~(1<<3);
180 156
181 pcm_playing = true;
182
183 /* start the IIS */ 157 /* start the IIS */
184 IISCON |= (1<<0); 158 IISCON |= (1<<0);
185 159
160 enable_fiq();
186} 161}
187 162
188 163static void pcm_play_dma_stop_fiq(void)
189
190/* Disconnect the DMA and wait for the FIFO to clear */
191void pcm_play_dma_stop(void)
192{ 164{
193 /* mask the DMA interrupt */ 165 /* mask the DMA interrupt */
194 INTMSK |= (1<<19); 166 INTMSK |= (1<<19);
@@ -207,17 +179,59 @@ void pcm_play_dma_stop(void)
207 179
208 /* Disconnect the IIS clock */ 180 /* Disconnect the IIS clock */
209 CLKCON &= ~(1<<17); 181 CLKCON &= ~(1<<17);
182}
210 183
211 disable_fiq(); 184void fiq(void)
185{
186 /* clear any pending interrupt */
187 SRCPND = (1<<19);
188
189 /* Buffer empty. Try to get more. */
190 if (pcm_callback_for_more)
191 {
192 pcm_callback_for_more((unsigned char**)&p, &p_size);
193 }
194 else
195 {
196 /* callback func is missing? */
197 pcm_play_dma_stop_fiq();
198 return;
199 }
200
201 if (p_size)
202 {
203 /* Flush any pending cache writes */
204 clean_dcache_range(p, p_size);
205
206 /* set the new DMA values */
207 DCON2 = DMA_CONTROL_SETUP | (p_size >> 1);
208 DISRC2 = (int)p + 0x30000000;
209
210 /* Re-Activate the channel */
211 DMASKTRIG2 = 0x2;
212 }
213 else
214 {
215 /* No more DMA to do */
216 pcm_play_dma_stop_fiq();
217 }
212 218
213} 219}
214 220
221/* Disconnect the DMA and wait for the FIFO to clear */
222void pcm_play_dma_stop(void)
223{
224 disable_fiq();
225 pcm_play_dma_stop_fiq();
226}
215 227
216 228
217void pcm_play_pause_pause(void) 229void pcm_play_pause_pause(void)
218{ 230{
219 /* stop servicing refills */ 231 /* stop servicing refills */
232 int oldstatus = set_fiq_status(FIQ_DISABLED);
220 INTMSK |= (1<<19); 233 INTMSK |= (1<<19);
234 set_fiq_status(oldstatus);
221} 235}
222 236
223 237
@@ -225,13 +239,14 @@ void pcm_play_pause_pause(void)
225void pcm_play_pause_unpause(void) 239void pcm_play_pause_unpause(void)
226{ 240{
227 /* refill buffer and keep going */ 241 /* refill buffer and keep going */
242 int oldstatus = set_fiq_status(FIQ_DISABLED);
243 _pcm_apply_settings();
228 INTMSK &= ~(1<<19); 244 INTMSK &= ~(1<<19);
245 set_fiq_status(oldstatus);
229} 246}
230 247
231void pcm_set_frequency(unsigned int frequency) 248void pcm_set_frequency(unsigned int frequency)
232{ 249{
233 int sr_ctrl;
234
235 switch(frequency) 250 switch(frequency)
236 { 251 {
237 case SAMPR_8: 252 case SAMPR_8:
@@ -240,38 +255,19 @@ void pcm_set_frequency(unsigned int frequency)
240 case SAMPR_11: 255 case SAMPR_11:
241 sr_ctrl = GIGABEAT_11025HZ; 256 sr_ctrl = GIGABEAT_11025HZ;
242 break; 257 break;
243 case SAMPR_12:
244 sr_ctrl = GIGABEAT_12000HZ;
245 break;
246 case SAMPR_16:
247 sr_ctrl = GIGABEAT_16000HZ;
248 break;
249 case SAMPR_22: 258 case SAMPR_22:
250 sr_ctrl = GIGABEAT_22050HZ; 259 sr_ctrl = GIGABEAT_22050HZ;
251 break; 260 break;
252 case SAMPR_24:
253 sr_ctrl = GIGABEAT_24000HZ;
254 break;
255 case SAMPR_32:
256 sr_ctrl = GIGABEAT_32000HZ;
257 break;
258 default: 261 default:
259 frequency = SAMPR_44; 262 frequency = SAMPR_44;
260 case SAMPR_44: 263 case SAMPR_44:
261 sr_ctrl = GIGABEAT_44100HZ; 264 sr_ctrl = GIGABEAT_44100HZ;
262 break; 265 break;
263 case SAMPR_48:
264 sr_ctrl = GIGABEAT_48000HZ;
265 break;
266 case SAMPR_88: 266 case SAMPR_88:
267 sr_ctrl = GIGABEAT_88200HZ; 267 sr_ctrl = GIGABEAT_88200HZ;
268 break; 268 break;
269 case SAMPR_96:
270 sr_ctrl = GIGABEAT_96000HZ;
271 break;
272 } 269 }
273 270
274 audiohw_set_sample_rate(sr_ctrl);
275 pcm_freq = frequency; 271 pcm_freq = frequency;
276} 272}
277 273
@@ -282,17 +278,12 @@ size_t pcm_get_bytes_waiting(void)
282 return (DSTAT2 & 0xFFFFF) * 2; 278 return (DSTAT2 & 0xFFFFF) * 2;
283} 279}
284 280
285 281#if 0
286
287/* dummy functions for those not actually supporting all this yet */
288void pcm_apply_settings(void)
289{
290}
291
292void pcm_set_monitor(int monitor) 282void pcm_set_monitor(int monitor)
293{ 283{
294 (void)monitor; 284 (void)monitor;
295} 285}
286#endif
296/** **/ 287/** **/
297 288
298void pcm_mute(bool mute) 289void pcm_mute(bool mute)
@@ -302,75 +293,79 @@ void pcm_mute(bool mute)
302 sleep(HZ/16); 293 sleep(HZ/16);
303} 294}
304 295
305/* 296/**
306 * This function goes directly into the DMA buffer to calculate the left and 297 * Return playback peaks - Peaks ahead in the DMA buffer based upon the
307 * right peak values. To avoid missing peaks it tries to look forward two full 298 * calling period to attempt to compensate for
308 * peek periods (2/HZ sec, 100% overlap), although it's always possible that 299 * delay.
309 * the entire period will not be visible. To reduce CPU load it only looks at
310 * every third sample, and this can be reduced even further if needed (even
311 * every tenth sample would still be pretty accurate).
312 */ 300 */
313
314/* Check for a peak every PEAK_STRIDE samples */
315#define PEAK_STRIDE 3
316/* Up to 1/50th of a second of audio for peak calculation */
317/* This should use NATIVE_FREQUENCY, or eventually an adjustable freq. value */
318#define PEAK_SAMPLES (44100/50)
319void pcm_calculate_peaks(int *left, int *right) 301void pcm_calculate_peaks(int *left, int *right)
320{ 302{
321 short *addr; 303 static unsigned long last_peak_tick = 0;
322 short *end; 304 static unsigned long frame_period = 0;
323 { 305 static int peaks_l = 0, peaks_r = 0;
324 size_t samples = p_size / 4;
325 addr = p;
326 306
327 if (samples > PEAK_SAMPLES) 307 /* Throttled peak ahead based on calling period */
328 samples = PEAK_SAMPLES - (PEAK_STRIDE - 1); 308 unsigned long period = current_tick - last_peak_tick;
329 else
330 samples -= MIN(PEAK_STRIDE - 1, samples);
331
332 end = &addr[samples * 2];
333 }
334 309
335 if (left && right) { 310 /* Keep reasonable limits on period */
336 int left_peak = 0, right_peak = 0; 311 if (period < 1)
312 period = 1;
313 else if (period > HZ/5)
314 period = HZ/5;
337 315
338 while (addr < end) { 316 frame_period = (3*frame_period + period) >> 2;
339 int value;
340 if ((value = addr [0]) > left_peak)
341 left_peak = value;
342 else if (-value > left_peak)
343 left_peak = -value;
344 317
345 if ((value = addr [PEAK_STRIDE | 1]) > right_peak) 318 last_peak_tick = current_tick;
346 right_peak = value;
347 else if (-value > right_peak)
348 right_peak = -value;
349 319
350 addr = &addr[PEAK_STRIDE * 2]; 320 if (pcm_playing && !pcm_paused)
321 {
322 unsigned long *addr = (unsigned long *)DCSRC2;
323 long samples = DSTAT2;
324 long samp_frames;
325
326 samples &= 0xFFFFE;
327 samp_frames = frame_period*pcm_freq/(HZ/2);
328 samples = MIN(samp_frames, samples) >> 1;
329
330 if (samples > 0)
331 {
332 long peak_l = 0, peak_r = 0;
333 long peaksq_l = 0, peaksq_r = 0;
334
335 addr -= 0x30000000 >> 2;
336 addr = (long *)((long)addr & ~3);
337
338 do
339 {
340 long value = *addr;
341 long ch, chsq;
342
343 ch = (int16_t)value;
344 chsq = ch*ch;
345 if (chsq > peaksq_l)
346 peak_l = ch, peaksq_l = chsq;
347
348 ch = value >> 16;
349 chsq = ch*ch;
350 if (chsq > peaksq_r)
351 peak_r = ch, peaksq_r = chsq;
352
353 addr += 4;
354 }
355 while ((samples -= 4) > 0);
356
357 peaks_l = abs(peak_l);
358 peaks_r = abs(peak_r);
351 } 359 }
352
353 *left = left_peak;
354 *right = right_peak;
355 } 360 }
356 else if (left || right) { 361 else
357 int peak_value = 0, value; 362 {
358 363 peaks_l = peaks_r = 0;
359 if (right) 364 }
360 addr += (PEAK_STRIDE | 1);
361 365
362 while (addr < end) { 366 if (left)
363 if ((value = addr [0]) > peak_value) 367 *left = peaks_l;
364 peak_value = value;
365 else if (-value > peak_value)
366 peak_value = -value;
367 368
368 addr += PEAK_STRIDE * 2; 369 if (right)
369 } 370 *right = peaks_r;
370 371} /* pcm_calculate_peaks */
371 if (left)
372 *left = peak_value;
373 else
374 *right = peak_value;
375 }
376}
diff --git a/firmware/target/arm/system-arm.h b/firmware/target/arm/system-arm.h
index 775b1ba1c9..26b8ac3513 100644
--- a/firmware/target/arm/system-arm.h
+++ b/firmware/target/arm/system-arm.h
@@ -132,4 +132,23 @@ static inline void disable_fiq(void)
132 ); 132 );
133} 133}
134 134
135/* This one returns the old status */
136#define FIQ_ENABLED 0x00
137#define FIQ_DISABLED 0x40
138static inline int set_fiq_status(int status)
139{
140 unsigned long cpsr;
141 int oldstatus;
142 /* Read the old level and set the new one */
143 asm volatile (
144 "mrs %1, cpsr \n"
145 "bic %0, %1, #0x40 \n"
146 "orr %0, %0, %2 \n"
147 "msr cpsr_c, %0 \n"
148 : "=&r,r"(cpsr), "=&r,r"(oldstatus) : "r,i"(status & 0x40)
149 );
150 return oldstatus;
151}
152
153
135#endif /* SYSTEM_ARM_H */ 154#endif /* SYSTEM_ARM_H */