diff options
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/drivers/audio/wm8978.c | 130 | ||||
-rw-r--r-- | firmware/export/config-gigabeat-s.h | 6 | ||||
-rw-r--r-- | firmware/export/wm8978.h | 1 | ||||
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c | 74 | ||||
-rw-r--r-- | firmware/target/arm/imx31/gigabeat-s/wmcodec-imx31.c | 20 |
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 | ||
358 | void 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 | ||
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 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 | ||
40 | static unsigned long pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */ | 40 | static unsigned long pcm_freq; /* 44.1 is default */ |
41 | static int sr_ctrl; | ||
41 | 42 | ||
42 | static struct dma_data dma_play_data = | 43 | static 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 | ||
111 | void pcm_apply_settings(void) | 112 | void 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 | ||
120 | void pcm_play_dma_init(void) | 127 | void 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. */ |
281 | void pcm_set_frequency(unsigned int frequency) | 302 | void 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 | ||
42 | void audiohw_init(void) | 42 | void 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(); |