From 159f8ee41aa0d8c6434867775547d9e030a963d7 Mon Sep 17 00:00:00 2001 From: Rob Purchase Date: Wed, 9 Apr 2008 22:20:04 +0000 Subject: D2: Beginnings of a WM8985 driver (based on WM8758, using EQ1 and EQ5 as HW treble/bass). Untested, but harmless. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@17057 a1c6a512-1295-4272-9138-f99709370657 --- firmware/drivers/audio/wm8985.c | 296 +++++++++++++++++++++++++++++++++------ firmware/export/audiohw.h | 2 +- firmware/export/config-cowond2.h | 4 +- firmware/export/wm8985.h | 12 +- firmware/sound.c | 10 +- 5 files changed, 264 insertions(+), 60 deletions(-) diff --git a/firmware/drivers/audio/wm8985.c b/firmware/drivers/audio/wm8985.c index ad19a589fe..163e9079be 100644 --- a/firmware/drivers/audio/wm8985.c +++ b/firmware/drivers/audio/wm8985.c @@ -7,8 +7,6 @@ * \/ \/ \/ \/ \/ * $Id$ * - * Stubs for WM8985 audio codec, (unwisely?) based on 8975 driver. - * * All files in this archive are subject to the GNU General Public License. * See the file COPYING in the source tree root for full license agreement. * @@ -25,117 +23,325 @@ #include "audiohw.h" #include "i2s.h" -/* TODO: fix these values, they're copied straight from WM8975 */ +/* Register addresses as per datasheet Rev.4.4 */ +#define RESET 0x00 +#define PWRMGMT1 0x01 +#define PWRMGMT2 0x02 +#define PWRMGMT3 0x03 +#define AINTFCE 0x04 +#define COMPAND 0x05 +#define CLKGEN 0x06 +#define SRATECTRL 0x07 +#define GPIOCTL 0x08 +#define JACKDETECT0 0x09 +#define DACCTRL 0x0a +#define LDACVOL 0x0b +#define RDACVOL 0x0c +#define JACKDETECT1 0x0d +#define ADCCTL 0x0e +#define LADCVOL 0x0f +#define RADCVOL 0x10 + +#define EQ1 0x12 +#define EQ2 0x13 +#define EQ3 0x14 +#define EQ4 0x15 +#define EQ5 0x16 +#define EQ_GAIN_MASK 0x001f +#define EQ_CUTOFF_MASK 0x0060 +#define EQ_GAIN_VALUE(x) (((-x) + 12) & 0x1f) +#define EQ_CUTOFF_VALUE(x) ((((x) - 1) & 0x03) << 5) + +#define CLASSDCTL 0x17 +#define DACLIMIT1 0x18 +#define DACLIMIT2 0x19 +#define NOTCH1 0x1b +#define NOTCH2 0x1c +#define NOTCH3 0x1d +#define NOTCH4 0x1e +#define ALCCTL1 0x20 +#define ALCCTL2 0x21 +#define ALCCTL3 0x22 +#define NOISEGATE 0x23 +#define PLLN 0x24 +#define PLLK1 0x25 +#define PLLK2 0x26 +#define PLLK3 0x27 +#define THREEDCTL 0x29 +#define OUT4ADC 0x2a +#define BEEPCTRL 0x2b +#define INCTRL 0x2c +#define LINPGAGAIN 0x2d +#define RINPGAGAIN 0x2e +#define LADCBOOST 0x2f +#define RADCBOOST 0x30 +#define OUTCTRL 0x31 +#define LOUTMIX 0x32 +#define ROUTMIX 0x33 +#define LOUT1VOL 0x34 +#define ROUT1VOL 0x35 +#define LOUT2VOL 0x36 +#define ROUT2VOL 0x37 +#define OUT3MIX 0x38 +#define OUT4MIX 0x39 +#define BIASCTL 0x3d + +/* Register settings for the supported samplerates: */ +#define WM8985_8000HZ 0x4d +#define WM8985_12000HZ 0x61 +#define WM8985_16000HZ 0x55 +#define WM8985_22050HZ 0x77 +#define WM8985_24000HZ 0x79 +#define WM8985_32000HZ 0x59 +#define WM8985_44100HZ 0x63 +#define WM8985_48000HZ 0x41 +#define WM8985_88200HZ 0x7f +#define WM8985_96000HZ 0x5d + const struct sound_settings_info audiohw_settings[] = { - [SOUND_VOLUME] = {"dB", 0, 1, -74, 6, -25}, - [SOUND_BASS] = {"dB", 0, 1, -6, 9, 0}, - [SOUND_TREBLE] = {"dB", 0, 1, -6, 9, 0}, + [SOUND_VOLUME] = {"dB", 0, 1, -58, 6, -25}, + [SOUND_BASS] = {"dB", 0, 1, -12, 12, 0}, + [SOUND_TREBLE] = {"dB", 0, 1, -12, 12, 0}, [SOUND_BALANCE] = {"%", 0, 1,-100, 100, 0}, [SOUND_CHANNELS] = {"", 0, 1, 0, 5, 0}, [SOUND_STEREO_WIDTH] = {"%", 0, 5, 0, 250, 100}, [SOUND_LEFT_GAIN] = {"dB", 1, 1,-128, 96, 0}, [SOUND_RIGHT_GAIN] = {"dB", 1, 1,-128, 96, 0}, [SOUND_MIC_GAIN] = {"dB", 1, 1,-128, 108, 16}, + [SOUND_BASS_CUTOFF] = {"", 0, 1, 1, 4, 1}, + [SOUND_TREBLE_CUTOFF] = {"", 0, 1, 1, 4, 1}, }; -/* convert tenth of dB volume to master volume register value */ +/* shadow registers */ +unsigned int eq1_reg; +unsigned int eq5_reg; + +/* convert tenth of dB volume (-57..6) to master volume register value */ int tenthdb2master(int db) { - #warning function not implemented - - (void)db; - return 0; + /* +6 to -57dB in 1dB steps == 64 levels = 6 bits */ + /* 0111111 == +6dB (0x3f) = 63) */ + /* 0111001 == 0dB (0x39) = 57) */ + /* 0000001 == -56dB (0x01) = */ + /* 0000000 == -57dB (0x00) */ + + /* 1000000 == Mute (0x40) */ + + if (db < VOLUME_MIN) { + return 0x40; + } else { + return((db/10)+57); + } +} + +/* convert tenth of dB volume (-780..0) to mixer volume register value */ +int tenthdb2mixer(int db) +{ + if (db < -660) /* 1.5 dB steps */ + return (2640 - db) / 15; + else if (db < -600) /* 0.75 dB steps */ + return (990 - db) * 2 / 15; + else if (db < -460) /* 0.5 dB steps */ + return (460 - db) / 5; + else /* 0.25 dB steps */ + return -db * 2 / 5; } /* Silently enable / disable audio output */ void audiohw_enable_output(bool enable) { - #warning function not implemented - - (void)enable; + if (enable) + { + /* TODO: reset the I2S controller into known state */ + //i2s_reset(); + + /* TODO: Review the power-up sequence to prevent pops (see datasheet) */ + + wmcodec_write(RESET, 0x1ff); /*Reset*/ + + wmcodec_write(PWRMGMT1, 0x2b); + wmcodec_write(PWRMGMT2, 0x180); + wmcodec_write(PWRMGMT3, 0x6f); + + wmcodec_write(AINTFCE, 0x10); + wmcodec_write(CLKCTRL, 0x49); + + wmcodec_write(OUTCTRL, 1); + + /* The iPod can handle multiple frequencies, but fix at 44.1KHz + for now */ + audiohw_set_sample_rate(WM8985_44100HZ); + + wmcodec_write(LOUTMIX,0x1); /* Enable mixer */ + wmcodec_write(ROUTMIX,0x1); /* Enable mixer */ + audiohw_mute(0); + } else { + audiohw_mute(1); + } } void audiohw_set_master_vol(int vol_l, int vol_r) { - #warning function not implemented - - (void)vol_l; - (void)vol_r; + /* OUT1 */ + wmcodec_write(LOUT1VOL, 0x080 | vol_l); + wmcodec_write(ROUT1VOL, 0x180 | vol_r); } void audiohw_set_lineout_vol(int vol_l, int vol_r) { - #warning function not implemented + /* OUT2 */ + wmcodec_write(LOUT2VOL, vol_l); + wmcodec_write(ROUT2VOL, 0x100 | vol_r); +} - (void)vol_l; - (void)vol_r; +void audiohw_set_mixer_vol(int channel1, int channel2) +{ + (void)channel1; + (void)channel2; } void audiohw_set_bass(int value) { - #warning function not implemented + eq1_reg = (eq1_reg & ~EQ_GAIN_MASK) | EQ_GAIN_VALUE(value); + wmcodec_write(EQ1, 0x100 | eq1_reg); +} - (void)value; +void audiohw_set_bass_cutoff(int value) +{ + eq1_reg = (eq1_reg & ~EQ_CUTOFF_MASK) | EQ_CUTOFF_VALUE(value); + wmcodec_write(EQ1, 0x100 | eq1_reg); } void audiohw_set_treble(int value) { - #warning function not implemented + eq5_reg = (eq5_reg & ~EQ_GAIN_MASK) | EQ_GAIN_VALUE(value); + wmcodec_write(EQ5, eq5_reg); +} - (void)value; +void audiohw_set_treble_cutoff(int value) +{ + eq5_reg = (eq5_reg & ~EQ_CUTOFF_MASK) | EQ_CUTOFF_VALUE(value); + wmcodec_write(EQ5, eq5_reg); } void audiohw_mute(bool mute) { - #warning function not implemented - - (void)mute; + if (mute) + { + /* Set DACMU = 1 to soft-mute the audio DACs. */ + wmcodec_write(DACCTRL, 0x40); + } else { + /* Set DACMU = 0 to soft-un-mute the audio DACs. */ + wmcodec_write(DACCTRL, 0x0); + } } +/* Nice shutdown of WM8758 codec */ void audiohw_close(void) { - #warning function not implemented + audiohw_mute(1); + + wmcodec_write(PWRMGMT3, 0x0); + + wmcodec_write(PWRMGMT1, 0x0); + + wmcodec_write(PWRMGMT2, 0x40); } +/* Change the order of the noise shaper, 5th order is recommended above 32kHz */ void audiohw_set_nsorder(int order) { - #warning function not implemented - (void)order; } /* Note: Disable output before calling this function */ void audiohw_set_sample_rate(int sampling_control) { - #warning function not implemented - + /**** We force 44.1KHz for now. ****/ (void)sampling_control; + + /* set clock div */ + wmcodec_write(CLKCTRL, 1 | (0 << 2) | (2 << 5)); + + /* setup PLL for MHZ=11.2896 */ + wmcodec_write(PLLN, (1 << 4) | 0x7); + wmcodec_write(PLLK1, 0x21); + wmcodec_write(PLLK2, 0x161); + wmcodec_write(PLLK3, 0x26); + + /* set clock div */ + wmcodec_write(CLKCTRL, 1 | (1 << 2) | (2 << 5) | (1 << 8)); + + /* set srate */ + wmcodec_write(SRATECTRL, (0 << 1)); } void audiohw_enable_recording(bool source_mic) { - #warning function not implemented + (void)source_mic; /* We only have a line-in (I think) */ + + /* TODO: reset the I2S controller into known state */ + //i2s_reset(); + + wmcodec_write(RESET, 0x1ff); /*Reset*/ + + wmcodec_write(PWRMGMT1, 0x2b); + wmcodec_write(PWRMGMT2, 0x18f); /* Enable ADC - 0x0c enables left/right PGA input, and 0x03 turns on power to the ADCs */ + wmcodec_write(PWRMGMT3, 0x6f); + + wmcodec_write(AINTFCE, 0x10); + wmcodec_write(CLKCTRL, 0x49); - (void)source_mic; + wmcodec_write(OUTCTRL, 1); + + /* The iPod can handle multiple frequencies, but fix at 44.1KHz + for now */ + audiohw_set_sample_rate(WM8985_44100HZ); + + wmcodec_write(INCTRL,0x44); /* Connect L2 and R2 inputs */ + + /* Set L2/R2_2BOOSTVOL to 0db (bits 4-6) */ + /* 000 = disabled + 001 = -12dB + 010 = -9dB + 011 = -6dB + 100 = -3dB + 101 = 0dB + 110 = 3dB + 111 = 6dB + */ + wmcodec_write(LADCBOOST,0x50); + wmcodec_write(RADCBOOST,0x50); + + /* Set L/R input PGA Volume to 0db */ + // wm8758_write(LINPGAVOL,0x3f); + // wm8758_write(RINPGAVOL,0x13f); + + /* Enable monitoring */ + wmcodec_write(LOUTMIX,0x17); /* Enable output mixer - BYPL2LMIX @ 0db*/ + wmcodec_write(ROUTMIX,0x17); /* Enable output mixer - BYPR2RMIX @ 0db*/ + + audiohw_mute(0); } - -void audiohw_disable_recording(void) -{ - #warning function not implemented + +void audiohw_disable_recording(void) { + audiohw_mute(1); + + wmcodec_write(PWRMGMT3, 0x0); + + wmcodec_write(PWRMGMT1, 0x0); + + wmcodec_write(PWRMGMT2, 0x40); } -void audiohw_set_recvol(int left, int right, int type) -{ - #warning function not implemented +void audiohw_set_recvol(int left, int right, int type) { (void)left; (void)right; (void)type; } -void audiohw_set_monitor(bool enable) -{ - #warning function not implemented +void audiohw_set_monitor(bool enable) { (void)enable; } diff --git a/firmware/export/audiohw.h b/firmware/export/audiohw.h index ca706811a0..a4ff75c22c 100644 --- a/firmware/export/audiohw.h +++ b/firmware/export/audiohw.h @@ -69,7 +69,7 @@ enum { SOUND_RIGHT_GAIN, SOUND_MIC_GAIN, #endif -#ifdef HAVE_WM8758 +#if defined(HAVE_WM8758) || defined(HAVE_WM8985) SOUND_BASS_CUTOFF, SOUND_TREBLE_CUTOFF, #endif diff --git a/firmware/export/config-cowond2.h b/firmware/export/config-cowond2.h index 78becb3161..74cf90eac4 100644 --- a/firmware/export/config-cowond2.h +++ b/firmware/export/config-cowond2.h @@ -83,8 +83,8 @@ /* The D2 uses a WM8985 codec */ #define HAVE_WM8985 -/* There is no hardware tone control */ -#define HAVE_SW_TONE_CONTROLS +/* Use WM8985 EQ1 & EQ5 as hardware tone controls */ +/* #define HAVE_SW_TONE_CONTROLS */ /* Define this for LCD backlight available */ #define HAVE_BACKLIGHT diff --git a/firmware/export/wm8985.h b/firmware/export/wm8985.h index a12d7cda57..8fa58425bf 100644 --- a/firmware/export/wm8985.h +++ b/firmware/export/wm8985.h @@ -21,22 +21,20 @@ #define _WM8985_H /* volume/balance/treble/bass interdependency */ -#define VOLUME_MIN -730 +#define VOLUME_MIN -570 #define VOLUME_MAX 60 extern int tenthdb2master(int db); +extern int tenthdb2mixer(int db); extern void audiohw_set_master_vol(int vol_l, int vol_r); extern void audiohw_set_lineout_vol(int vol_l, int vol_r); +extern void audiohw_set_mixer_vol(int channel1, int channel2); extern void audiohw_set_bass(int value); +extern void audiohw_set_bass_cutoff(int value); extern void audiohw_set_treble(int value); +extern void audiohw_set_treble_cutoff(int value); extern void audiohw_set_nsorder(int order); extern void audiohw_set_sample_rate(int sampling_control); -/* Register addresses */ -// .. tbc - -/* Register settings for the supported samplerates: */ -// .. tbc - #endif /* _WM8985_H */ diff --git a/firmware/sound.c b/firmware/sound.c index b559fe2b5e..123675cbde 100644 --- a/firmware/sound.c +++ b/firmware/sound.c @@ -244,7 +244,7 @@ static void set_prescaled_volume(void) * the prescaler stay at 0 for these unless SW tone controls are in use */ #if defined(HAVE_SW_TONE_CONTROLS) || !(defined(HAVE_WM8975) \ || defined(HAVE_WM8731) || defined(HAVE_WM8721) || defined(HAVE_WM8751) \ - || defined(HAVE_WM8758)) + || defined(HAVE_WM8758) || defined(HAVE_WM8985)) prescale = MAX(current_bass, current_treble); if (prescale < 0) @@ -453,7 +453,7 @@ void sound_set_bass(int value) audiohw_set_bass(value); set_prescaled_volume(); #elif defined HAVE_WM8975 || defined HAVE_WM8758 || defined(HAVE_UDA1380) \ - || defined HAVE_WM8731 || defined(HAVE_WM8721) + || defined HAVE_WM8731 || defined(HAVE_WM8721) || defined(HAVE_WM8985) current_bass = value * 10; audiohw_set_bass(value); set_prescaled_volume(); @@ -483,7 +483,7 @@ void sound_set_treble(int value) current_treble = value; set_prescaled_volume(); #elif defined(HAVE_WM8975) || defined(HAVE_WM8758) || defined(HAVE_UDA1380) \ - || defined(HAVE_WM8731) || defined(HAVE_WM8721) + || defined(HAVE_WM8731) || defined(HAVE_WM8721) || defined(HAVE_WM8985) audiohw_set_treble(value); current_treble = value * 10; set_prescaled_volume(); @@ -518,7 +518,7 @@ void sound_set_stereo_width(int value) #endif } -#ifdef HAVE_WM8758 +#if defined(HAVE_WM8758) || defined(HAVE_WM8985) void sound_set_bass_cutoff(int value) { if(!audio_is_initialized) @@ -697,7 +697,7 @@ void sound_set_superbass(int value) } #endif /* (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F) */ -#ifdef HAVE_WM8758 +#if defined(HAVE_WM8758) || defined(HAVE_WM8985) void sound_set_bass_cutoff(int value) { (void) value; -- cgit v1.2.3