summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2008-11-22 12:17:26 +0000
committerMichael Sevakis <jethead71@rockbox.org>2008-11-22 12:17:26 +0000
commit7c007f5d87309de2f0d19d8dea5da7ec5a199c30 (patch)
tree3e7b7b96ff6665dc81fd1bff6b026ba22f2459e4
parent0394ebe44df99f8fc725dc4a66ab37209ef4d55b (diff)
downloadrockbox-7c007f5d87309de2f0d19d8dea5da7ec5a199c30.tar.gz
rockbox-7c007f5d87309de2f0d19d8dea5da7ec5a199c30.zip
Audio samplerate control for Gigabeat S: 8, 11.025, 12, 16, 22.050, 24, 32, 44.1 and 48 kHz.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@19178 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--firmware/drivers/audio/wm8978.c130
-rw-r--r--firmware/export/config-gigabeat-s.h6
-rw-r--r--firmware/export/wm8978.h1
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c74
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/wmcodec-imx31.c20
5 files changed, 214 insertions, 17 deletions
diff --git a/firmware/drivers/audio/wm8978.c b/firmware/drivers/audio/wm8978.c
index c50500356a..f392c21d4c 100644
--- a/firmware/drivers/audio/wm8978.c
+++ b/firmware/drivers/audio/wm8978.c
@@ -227,8 +227,9 @@ void audiohw_postinit(void)
227 wmc_write(WMC_DAC_CONTROL, WMC_DACOSR_128 | WMC_AMUTE); 227 wmc_write(WMC_DAC_CONTROL, WMC_DACOSR_128 | WMC_AMUTE);
228 228
229 /* Specific to HW clocking */ 229 /* Specific to HW clocking */
230 wmc_write(WMC_CLOCK_GEN_CTRL, WMC_MCLKDIV_1_5 | WMC_BCLKDIV_8 | WMC_MS); 230 wmc_write_masked(WMC_CLOCK_GEN_CTRL, WMC_BCLKDIV_4 | WMC_MS,
231 wmc_write(WMC_ADDITIONAL_CTRL, WMC_SR_48KHZ); /* 44.1 */ 231 WMC_BCLKDIV | WMC_MS | WMC_CLKSEL);
232 audiohw_set_frequency(HW_SAMPR_DEFAULT);
232 233
233 /* ADC silenced */ 234 /* ADC silenced */
234 wmc_write_masked(WMC_LEFT_ADC_DIGITAL_VOL, 0x00, WMC_DVOL); 235 wmc_write_masked(WMC_LEFT_ADC_DIGITAL_VOL, 0x00, WMC_DVOL);
@@ -354,6 +355,131 @@ void audiohw_mute(bool mute)
354 } 355 }
355} 356}
356 357
358void audiohw_set_frequency(int sampling_control)
359{
360 /* For 16.9344MHz MCLK */
361 static const struct
362 {
363 uint32_t plln : 8;
364 uint32_t pllk0 : 6;
365 uint32_t pllk1 : 9;
366 uint32_t pllk2 : 9;
367 unsigned char mclkdiv;
368 unsigned char filter;
369 } sctrl_table[HW_NUM_FREQ] =
370 {
371 [HW_FREQ_8] = /* PLL = 65.536MHz */
372 {
373 .plln = WMC_PLLNw(7) | WMC_PLL_PRESCALE,
374 .pllk0 = WMC_PLLK_23_18w(12414886 >> 18),
375 .pllk1 = WMC_PLLK_17_9w(12414886 >> 9),
376 .pllk2 = WMC_PLLK_8_0w(12414886 >> 0),
377 .mclkdiv = WMC_MCLKDIV_8, /* 2.0480 MHz */
378 .filter = WMC_SR_8KHZ,
379 },
380 [HW_FREQ_11] = /* PLL = off */
381 {
382 .mclkdiv = WMC_MCLKDIV_6, /* 2.8224 MHz */
383 .filter = WMC_SR_12KHZ,
384 },
385 [HW_FREQ_12] = /* PLL = 73.728 MHz */
386 {
387 .plln = WMC_PLLNw(8) | WMC_PLL_PRESCALE,
388 .pllk0 = WMC_PLLK_23_18w(11869595 >> 18),
389 .pllk1 = WMC_PLLK_17_9w(11869595 >> 9),
390 .pllk2 = WMC_PLLK_8_0w(11869595 >> 0),
391 .mclkdiv = WMC_MCLKDIV_6, /* 3.0720 MHz */
392 .filter = WMC_SR_12KHZ,
393 },
394 [HW_FREQ_16] = /* PLL = 65.536MHz */
395 {
396 .plln = WMC_PLLNw(7) | WMC_PLL_PRESCALE,
397 .pllk0 = WMC_PLLK_23_18w(12414886 >> 18),
398 .pllk1 = WMC_PLLK_17_9w(12414886 >> 9),
399 .pllk2 = WMC_PLLK_8_0w(12414886 >> 0),
400 .mclkdiv = WMC_MCLKDIV_4, /* 4.0960 MHz */
401 .filter = WMC_SR_16KHZ,
402 },
403 [HW_FREQ_22] = /* PLL = off */
404 {
405 .mclkdiv = WMC_MCLKDIV_3, /* 5.6448 MHz */
406 .filter = WMC_SR_24KHZ,
407 },
408 [HW_FREQ_24] = /* PLL = 73.728 MHz */
409 {
410 .plln = WMC_PLLNw(8) | WMC_PLL_PRESCALE,
411 .pllk0 = WMC_PLLK_23_18w(11869595 >> 18),
412 .pllk1 = WMC_PLLK_17_9w(11869595 >> 9),
413 .pllk2 = WMC_PLLK_8_0w(11869595 >> 0),
414 .mclkdiv = WMC_MCLKDIV_3, /* 6.1440 MHz */
415 .filter = WMC_SR_24KHZ,
416 },
417 [HW_FREQ_32] = /* PLL = 65.536MHz */
418 {
419 .plln = WMC_PLLNw(7) | WMC_PLL_PRESCALE,
420 .pllk0 = WMC_PLLK_23_18w(12414886 >> 18),
421 .pllk1 = WMC_PLLK_17_9w(12414886 >> 9),
422 .pllk2 = WMC_PLLK_8_0w(12414886 >> 0),
423 .mclkdiv = WMC_MCLKDIV_2, /* 8.1920 MHz */
424 .filter = WMC_SR_32KHZ,
425 },
426 [HW_FREQ_44] = /* PLL = off */
427 {
428 .mclkdiv = WMC_MCLKDIV_1_5, /* 11.2896 MHz */
429 .filter = WMC_SR_48KHZ,
430 },
431 [HW_FREQ_48] = /* PLL = 73.728 MHz */
432 {
433 .plln = WMC_PLLNw(8) | WMC_PLL_PRESCALE,
434 .pllk0 = WMC_PLLK_23_18w(11869595 >> 18),
435 .pllk1 = WMC_PLLK_17_9w(11869595 >> 9),
436 .pllk2 = WMC_PLLK_8_0w(11869595 >> 0),
437 .mclkdiv = WMC_MCLKDIV_1_5, /* 12.2880 MHz */
438 .filter = WMC_SR_48KHZ,
439 },
440 };
441
442 unsigned int plln;
443 unsigned int mclkdiv;
444
445 if ((unsigned)sampling_control >= ARRAYLEN(sctrl_table))
446 sampling_control = HW_FREQ_DEFAULT;
447
448
449 /* Setup filters. */
450 wmc_write(WMC_ADDITIONAL_CTRL,
451 sctrl_table[sampling_control].filter);
452
453 plln = sctrl_table[sampling_control].plln;
454 mclkdiv = sctrl_table[sampling_control].mclkdiv;
455
456 if (plln != 0)
457 {
458 /* Using PLL to generate SYSCLK */
459
460 /* Program PLL. */
461 wmc_write(WMC_PLL_N, plln);
462 wmc_write(WMC_PLLK_23_18, sctrl_table[sampling_control].pllk0);
463 wmc_write(WMC_PLLK_17_9, sctrl_table[sampling_control].pllk1);
464 wmc_write(WMC_PLLK_8_0, sctrl_table[sampling_control].pllk2);
465
466 /* Turn on PLL. */
467 wmc_set(WMC_POWER_MANAGEMENT1, WMC_PLLEN);
468
469 /* Switch to PLL and set divider. */
470 wmc_write_masked(WMC_CLOCK_GEN_CTRL, mclkdiv | WMC_CLKSEL,
471 WMC_MCLKDIV | WMC_CLKSEL);
472 }
473 else
474 {
475 /* Switch away from PLL and set MCLKDIV. */
476 wmc_write_masked(WMC_CLOCK_GEN_CTRL, mclkdiv,
477 WMC_MCLKDIV | WMC_CLKSEL);
478
479 /* Turn off PLL. */
480 wmc_clear(WMC_POWER_MANAGEMENT1, WMC_PLLEN);
481 }
482}
357 483
358#ifdef HAVE_RECORDING 484#ifdef HAVE_RECORDING
359/* TODO */ 485/* TODO */
diff --git a/firmware/export/config-gigabeat-s.h b/firmware/export/config-gigabeat-s.h
index 57129321ca..e6fa6c9fc6 100644
--- a/firmware/export/config-gigabeat-s.h
+++ b/firmware/export/config-gigabeat-s.h
@@ -72,8 +72,10 @@
72/* Define this if you have the WM8978 audio codec */ 72/* Define this if you have the WM8978 audio codec */
73#define HAVE_WM8978 73#define HAVE_WM8978
74 74
75#define HW_SAMPR_CAPS (SAMPR_CAP_88 | SAMPR_CAP_44 | SAMPR_CAP_22 | \ 75#define HW_SAMPR_CAPS (SAMPR_CAP_48 | SAMPR_CAP_44 | SAMPR_CAP_32 | \
76 SAMPR_CAP_11) 76 SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16 | \
77 SAMPR_CAP_16 | SAMPR_CAP_12 | SAMPR_CAP_11 | \
78 SAMPR_CAP_8)
77 79
78#ifndef BOOTLOADER 80#ifndef BOOTLOADER
79/* Not for bootloader */ 81/* Not for bootloader */
diff --git a/firmware/export/wm8978.h b/firmware/export/wm8978.h
index 3c01f76bef..f4ed46a6e2 100644
--- a/firmware/export/wm8978.h
+++ b/firmware/export/wm8978.h
@@ -28,6 +28,7 @@
28 28
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 sampling_control);
31 32
32#define WMC_I2C_ADDR 0x34 33#define WMC_I2C_ADDR 0x34
33 34
diff --git a/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c b/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c
index ed3650cd60..99aa66a781 100644
--- a/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c
+++ b/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c
@@ -37,7 +37,8 @@ struct dma_data
37 int state; 37 int state;
38}; 38};
39 39
40static unsigned long pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */ 40static unsigned long pcm_freq; /* 44.1 is default */
41static int sr_ctrl;
41 42
42static struct dma_data dma_play_data = 43static struct dma_data dma_play_data =
43{ 44{
@@ -71,7 +72,7 @@ static void _pcm_apply_settings(void)
71 if (pcm_freq != pcm_curr_sampr) 72 if (pcm_freq != pcm_curr_sampr)
72 { 73 {
73 pcm_curr_sampr = pcm_freq; 74 pcm_curr_sampr = pcm_freq;
74 // TODO: audiohw_set_frequency(sr_ctrl); 75 audiohw_set_frequency(sr_ctrl);
75 } 76 }
76} 77}
77 78
@@ -110,11 +111,17 @@ static void __attribute__((interrupt("IRQ"))) SSI1_HANDLER(void)
110 111
111void pcm_apply_settings(void) 112void pcm_apply_settings(void)
112{ 113{
113 int oldstatus = disable_fiq_save(); 114 pcm_play_lock();
115#ifdef HAVE_RECORDING
116 pcm_rec_lock();
117#endif
114 118
115 _pcm_apply_settings(); 119 _pcm_apply_settings();
116 120
117 restore_fiq(oldstatus); 121#ifdef HAVE_RECORDING
122 pcm_rec_unlock();
123#endif
124 pcm_play_unlock();
118} 125}
119 126
120void pcm_play_dma_init(void) 127void pcm_play_dma_init(void)
@@ -189,11 +196,25 @@ void pcm_play_dma_init(void)
189 SSI_SCR2 = 0; 196 SSI_SCR2 = 0;
190 SSI_SRCR2 = 0; 197 SSI_SRCR2 = 0;
191 SSI_STCR2 = SSI_STCR_TXDIR; 198 SSI_STCR2 = SSI_STCR_TXDIR;
192 SSI_STCCR2 = SSI_STRCCR_PMw(0);
193 199
194 /* Enable SSIs */ 200 /* f(INT_BIT_CLK) =
201 * f(SYS_CLK) / [(DIV2 + 1)*(7*PSR + 1)*(PM + 1)*2] =
202 * 677737600 / [(1 + 1)*(7*0 + 1)*(0 + 1)*2] =
203 * 677737600 / 4 = 169344000 Hz
204 *
205 * 45.4.2.2 DIV2, PSR, and PM Bit Description states:
206 * Bits DIV2, PSR, and PM should not be all set to zero at the same
207 * time.
208 *
209 * The hardware seems to force a divide by 4 even if all bits are
210 * zero but comply by setting DIV2 and the others to zero.
211 */
212 SSI_STCCR2 = SSI_STRCCR_DIV2 | SSI_STRCCR_PMw(1-1);
213
214 /* Enable SSI2 (codec clock) */
195 SSI_SCR2 |= SSI_SCR_SSIEN; 215 SSI_SCR2 |= SSI_SCR_SSIEN;
196 216
217 pcm_set_frequency(HW_SAMPR_DEFAULT);
197 audiohw_init(); 218 audiohw_init();
198} 219}
199 220
@@ -280,8 +301,45 @@ void pcm_play_dma_pause(bool pause)
280 hardware here but simply cache it. */ 301 hardware here but simply cache it. */
281void pcm_set_frequency(unsigned int frequency) 302void pcm_set_frequency(unsigned int frequency)
282{ 303{
283 /* TODO */ 304 int index;
284 (void)frequency; 305
306 switch (frequency)
307 {
308 case SAMPR_48:
309 index = HW_FREQ_48;
310 break;
311 case SAMPR_44:
312 index = HW_FREQ_44;
313 break;
314 case SAMPR_32:
315 index = HW_FREQ_32;
316 break;
317 case SAMPR_24:
318 index = HW_FREQ_24;
319 break;
320 case SAMPR_22:
321 index = HW_FREQ_22;
322 break;
323 case SAMPR_16:
324 index = HW_FREQ_16;
325 break;
326 case SAMPR_12:
327 index = HW_FREQ_12;
328 break;
329 case SAMPR_11:
330 index = HW_FREQ_11;
331 break;
332 case SAMPR_8:
333 index = HW_FREQ_8;
334 break;
335 default:
336 /* Invalid = default */
337 frequency = HW_SAMPR_DEFAULT;
338 index = HW_FREQ_DEFAULT;
339 }
340
341 pcm_freq = frequency;
342 sr_ctrl = index;
285} 343}
286 344
287/* Return the number of bytes waiting - full L-R sample pairs only */ 345/* Return the number of bytes waiting - full L-R sample pairs only */
diff --git a/firmware/target/arm/imx31/gigabeat-s/wmcodec-imx31.c b/firmware/target/arm/imx31/gigabeat-s/wmcodec-imx31.c
index 7a877e1415..0bb9e49506 100644
--- a/firmware/target/arm/imx31/gigabeat-s/wmcodec-imx31.c
+++ b/firmware/target/arm/imx31/gigabeat-s/wmcodec-imx31.c
@@ -41,13 +41,23 @@ static struct i2c_node wm8978_i2c_node =
41 41
42void audiohw_init(void) 42void audiohw_init(void)
43{ 43{
44 /* USB PLL = 338.688MHz, /30 = 11.2896MHz = 256Fs */ 44 /* How SYSCLK for codec is derived (USBPLL=338.688MHz).
45 *
46 * SSI post dividers (SSI2 PODF=4, SSI2 PRE PODF=0):
47 * 338688000Hz / 5 = 67737600Hz = ssi2_clk
48 *
49 * SSI bit clock dividers (DIV2=1, PSR=0, PM=0):
50 * ssi2_clk / 4 = 16934400Hz = INT_BIT_CLK (MCLK)
51 *
52 * WM Codec post divider (MCLKDIV=1.5):
53 * INT_BIT_CLK (MCLK) / 1.5 = 11289600Hz = 256*fs = SYSCLK
54 */
45 imx31_regmod32(&CLKCTL_PDR1, 55 imx31_regmod32(&CLKCTL_PDR1,
46 PDR1_SSI1_PODFw(64-1) | PDR1_SSI2_PODFw(5-1), 56 PDR1_SSI1_PODFw(64-1) | PDR1_SSI2_PODFw(5-1) |
47 PDR1_SSI1_PODF | PDR1_SSI2_PODF); 57 PDR1_SSI1_PRE_PODFw(8-1) | PDR1_SSI2_PRE_PODFw(1-1),
48 imx31_regmod32(&CLKCTL_PDR1, 58 PDR1_SSI1_PODF | PDR1_SSI2_PODF |
49 PDR1_SSI1_PRE_PODFw(4-1) | PDR1_SSI2_PRE_PODFw(1-1),
50 PDR1_SSI1_PRE_PODF | PDR1_SSI2_PRE_PODF); 59 PDR1_SSI1_PRE_PODF | PDR1_SSI2_PRE_PODF);
60
51 i2c_enable_node(&wm8978_i2c_node, true); 61 i2c_enable_node(&wm8978_i2c_node, true);
52 62
53 audiohw_preinit(); 63 audiohw_preinit();