diff options
65 files changed, 2916 insertions, 928 deletions
@@ -126,6 +126,7 @@ config SYS_BOOT_GET_KBD config SYS_MALLOC_F bool "Enable malloc() pool before relocation" default y if DM + help Before relocation, memory is very limited on many platforms. Still, we can provide a small malloc() pool if needed. Driver model in @@ -136,6 +137,7 @@ config SYS_MALLOC_F_LEN hex "Size of malloc() pool before relocation" depends on SYS_MALLOC_F default 0x1000 if AM33XX + default 0x2800 if SANDBOX default 0x400 help Before relocation, memory is very limited on many platforms. Still, diff --git a/arch/Kconfig b/arch/Kconfig index 947070fdd3..35e2712fce 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -116,6 +116,7 @@ config SANDBOX imply VIRTIO_SANDBOX imply VIRTIO_BLK imply VIRTIO_NET + imply DM_SOUND config SH bool "SuperH architecture" diff --git a/arch/arm/dts/exynos5250-smdk5250.dts b/arch/arm/dts/exynos5250-smdk5250.dts index 8b695442b1..e542a79076 100644 --- a/arch/arm/dts/exynos5250-smdk5250.dts +++ b/arch/arm/dts/exynos5250-smdk5250.dts @@ -60,9 +60,26 @@ }; i2c@12C70000 { - soundcodec@1a { + wm8994: soundcodec@1a { reg = <0x1a>; - compatible = "wolfson,wm8994-codec"; + u-boot,i2c-offset-len = <2>; + compatible = "wolfson,wm8994"; + #sound-dai-cells = <1>; + }; + }; + + sound { + compatible = "google,smdk5250-audio-wm8994"; + + samsung,model = "SMDK5250-I2S-WM8994"; + samsung,audio-codec = <&wm8994>; + + cpu { + sound-dai = <&i2s0 0>; + }; + + codec { + sound-dai = <&wm8994 0>; }; }; diff --git a/arch/arm/dts/exynos5250-snow.dts b/arch/arm/dts/exynos5250-snow.dts index 29c13c1257..7587dc0ff2 100644 --- a/arch/arm/dts/exynos5250-snow.dts +++ b/arch/arm/dts/exynos5250-snow.dts @@ -40,7 +40,6 @@ mmc3 = "/mmc@12230000"; serial0 = "/serial@12C30000"; console = "/serial@12C30000"; - i2s = "/sound@3830000"; }; memory { @@ -88,7 +87,7 @@ ro-boot { label = "u-boot"; - reg = <0x6000 0x9a000>; + reg = <0x6000 0xb0000>; read-only; type = "blob boot,dtb"; required; @@ -214,9 +213,10 @@ }; }; - soundcodec@22 { - reg = <0x22>; - compatible = "maxim,max98095-codec"; + max98095: codec@11 { + compatible = "maxim,max98095"; + reg = <0x11>; + #sound-dai-cells = <1>; }; }; @@ -273,9 +273,20 @@ }; }; - sound@3830000 { - samsung,codec-type = "max98095"; + sound { + compatible = "google,snow-audio-max98095"; + + samsung,model = "Snow-I2S-MAX98095"; + samsung,audio-codec = <&max98095>; codec-enable-gpio = <&gpx1 7 GPIO_ACTIVE_HIGH>; + + cpu { + sound-dai = <&i2s0 0>; + }; + + codec { + sound-dai = <&max98095 0>; + }; }; sound@12d60000 { diff --git a/arch/arm/dts/exynos5250-spring.dts b/arch/arm/dts/exynos5250-spring.dts index 7633d36874..191e12af6a 100644 --- a/arch/arm/dts/exynos5250-spring.dts +++ b/arch/arm/dts/exynos5250-spring.dts @@ -34,7 +34,6 @@ mmc0 = "/mmc@12200000"; serial0 = "/serial@12C30000"; console = "/serial@12C30000"; - i2s = "/sound@3830000"; }; memory { @@ -639,10 +638,27 @@ }; }; - soundcodec@20 { - reg = <0x20>; - compatible = "maxim,max98088-codec"; + max98095: soundcodec@10 { + reg = <0x10>; + compatible = "maxim,max98095"; + #sound-dai-cells = <1>; }; + + sound { + compatible = "google,spring-audio-max98095"; + + samsung,model = "Spring-I2S-MAX98095"; + samsung,audio-codec = <&max98095>; + + cpu { + sound-dai = <&i2s0 0>; + }; + + codec { + sound-dai = <&max98095 0>; + }; + }; + }; #include "cros-ec-keyboard.dtsi" diff --git a/arch/arm/dts/exynos5250.dtsi b/arch/arm/dts/exynos5250.dtsi index 502c687802..66c5b6dca9 100644 --- a/arch/arm/dts/exynos5250.dtsi +++ b/arch/arm/dts/exynos5250.dtsi @@ -78,9 +78,12 @@ #size-cells = <0>; }; - sound@3830000 { - compatible = "samsung,exynos-sound"; - reg = <0x3830000 0x50>; + i2s0: i2s@3830000 { + compatible = "samsung,s5pv210-i2s"; + reg = <0x03830000 0x100>; + samsung,idma-addr = <0x03000000>; + #clock-cells = <1>; + #sound-dai-cells = <1>; samsung,i2s-epll-clock-frequency = <192000000>; samsung,i2s-sampling-rate = <48000>; samsung,i2s-bits-per-sample = <16>; @@ -90,9 +93,11 @@ samsung,i2s-id = <0>; }; - sound@12d60000 { - compatible = "samsung,exynos-sound"; + i2s1: i2s@12d60000 { + compatible = "samsung,s5pv210-i2s"; reg = <0x12d60000 0x20>; + #clock-cells = <1>; + #sound-dai-cells = <1>; samsung,i2s-epll-clock-frequency = <192000000>; samsung,i2s-sampling-rate = <48000>; samsung,i2s-bits-per-sample = <16>; diff --git a/arch/arm/dts/exynos5420-peach-pit.dts b/arch/arm/dts/exynos5420-peach-pit.dts index c86f9d9035..4a96a18110 100644 --- a/arch/arm/dts/exynos5420-peach-pit.dts +++ b/arch/arm/dts/exynos5420-peach-pit.dts @@ -67,12 +67,28 @@ }; }; + sound { + compatible = "google,peach-audio-max98090"; + + samsung,model = "PEACH-I2S-MAX98090"; + samsung,audio-codec = <&max98090>; + + cpu { + sound-dai = <&i2s0 0>; + }; + + codec { + sound-dai = <&max98090 0>; + }; + }; + i2c@12CD0000 { /* i2c7 */ clock-frequency = <100000>; - soundcodec@20 { - reg = <0x20>; - compatible = "maxim,max98090-codec"; - }; + max98090: soundcodec@10 { + reg = <0x10>; + compatible = "maxim,max98090"; + #sound-dai-cells = <1>; + }; edp-lvds-bridge@48 { compatible = "parade,ps8625"; diff --git a/arch/arm/dts/exynos5420-smdk5420.dts b/arch/arm/dts/exynos5420-smdk5420.dts index cab5ddb61f..7a5da674fb 100644 --- a/arch/arm/dts/exynos5420-smdk5420.dts +++ b/arch/arm/dts/exynos5420-smdk5420.dts @@ -82,9 +82,26 @@ }; i2c@12C70000 { - soundcodec@1a { + wm8994: soundcodec@1a { reg = <0x1a>; - compatible = "wolfson,wm8994-codec"; + u-boot,i2c-offset-len = <2>; + compatible = "wolfson,wm8994"; + #sound-dai-cells = <1>; + }; + }; + + sound { + compatible = "samsung,smdk5420-audio-wm8994"; + + samsung,model = "Snow-I2S-MAX98095"; + samsung,audio-codec = <&wm8994>; + + cpu { + sound-dai = <&i2s0 0>; + }; + + codec { + sound-dai = <&wm8994 0>; }; }; diff --git a/arch/arm/dts/exynos54xx.dtsi b/arch/arm/dts/exynos54xx.dtsi index 09bef56e6c..221da8b485 100644 --- a/arch/arm/dts/exynos54xx.dtsi +++ b/arch/arm/dts/exynos54xx.dtsi @@ -104,6 +104,20 @@ interrupts = <0 203 0>; }; + i2s0: i2s@3830000 { + compatible = "samsung,s5pv210-i2s"; + reg = <0x03830000 0x100>; + #sound-dai-cells = <1>; + samsung,idma-addr = <0x03000000>; + samsung,i2s-epll-clock-frequency = <192000000>; + samsung,i2s-sampling-rate = <48000>; + samsung,i2s-bits-per-sample = <16>; + samsung,i2s-channels = <2>; + samsung,i2s-lr-clk-framesize = <256>; + samsung,i2s-bit-clk-framesize = <32>; + samsung,i2s-id = <0>; + }; + mmc@12200000 { samsung,bus-width = <8>; samsung,timing = <1 3 3>; diff --git a/arch/arm/dts/exynos5800-peach-pi.dts b/arch/arm/dts/exynos5800-peach-pi.dts index 7498519d6c..63c0b186e4 100644 --- a/arch/arm/dts/exynos5800-peach-pi.dts +++ b/arch/arm/dts/exynos5800-peach-pi.dts @@ -79,12 +79,28 @@ }; }; + sound { + compatible = "google,peach-audio-max98090"; + + samsung,model = "PEACH-I2S-MAX98090"; + samsung,audio-codec = <&max98090>; + + cpu { + sound-dai = <&i2s0 0>; + }; + + codec { + sound-dai = <&max98090 0>; + }; + }; + i2c@12CD0000 { /* i2c7 */ clock-frequency = <100000>; - soundcodec@20 { - reg = <0x20>; - compatible = "maxim,max98090-codec"; - }; + max98090: soundcodec@10 { + reg = <0x10>; + compatible = "maxim,max98090"; + #sound-dai-cells = <1>; + }; }; sound@3830000 { diff --git a/arch/arm/mach-exynos/clock.c b/arch/arm/mach-exynos/clock.c index 6a3cd44b5d..73aa4cdad3 100644 --- a/arch/arm/mach-exynos/clock.c +++ b/arch/arm/mach-exynos/clock.c @@ -345,7 +345,7 @@ static struct clk_bit_info *get_clk_bit_info(int peripheral) int i; struct clk_bit_info *info; - if (proid_is_exynos5420() || proid_is_exynos5422()) + if (proid_is_exynos542x()) info = exynos542x_bit_info; else info = exynos5_bit_info; @@ -557,7 +557,7 @@ static unsigned long exynos542x_get_periph_rate(int peripheral) unsigned long clock_get_periph_rate(int peripheral) { if (cpu_is_exynos5()) { - if (proid_is_exynos5420() || proid_is_exynos5422()) + if (proid_is_exynos542x()) return exynos542x_get_periph_rate(peripheral); return exynos5_get_periph_rate(peripheral); } else { @@ -1317,6 +1317,19 @@ int exynos5_set_epll_clk(unsigned long rate) return 0; } +static int exynos5420_set_i2s_clk_source(void) +{ + struct exynos5420_clock *clk = + (struct exynos5420_clock *)samsung_get_base_clock(); + + setbits_le32(&clk->src_top6, EXYNOS5420_CLK_SRC_MOUT_EPLL); + clrsetbits_le32(&clk->src_mau, EXYNOS5420_AUDIO0_SEL_MASK, + (EXYNOS5420_CLK_SRC_SCLK_EPLL)); + setbits_le32(EXYNOS5_AUDIOSS_BASE, 1 << 0); + + return 0; +} + int exynos5_set_i2s_clk_source(unsigned int i2s_id) { struct exynos5_clock *clk = @@ -1575,7 +1588,7 @@ static unsigned long exynos4_get_i2c_clk(void) unsigned long get_pll_clk(int pllreg) { if (cpu_is_exynos5()) { - if (proid_is_exynos5420() || proid_is_exynos5422()) + if (proid_is_exynos542x()) return exynos542x_get_pll_clk(pllreg); return exynos5_get_pll_clk(pllreg); } else if (cpu_is_exynos4()) { @@ -1691,7 +1704,7 @@ void set_mmc_clk(int dev_index, unsigned int div) div -= 1; if (cpu_is_exynos5()) { - if (proid_is_exynos5420() || proid_is_exynos5422()) + if (proid_is_exynos542x()) exynos5420_set_mmc_clk(dev_index, div); else exynos5_set_mmc_clk(dev_index, div); @@ -1739,7 +1752,7 @@ void set_mipi_clk(void) int set_spi_clk(int periph_id, unsigned int rate) { if (cpu_is_exynos5()) { - if (proid_is_exynos5420() || proid_is_exynos5422()) + if (proid_is_exynos542x()) return exynos5420_set_spi_clk(periph_id, rate); return exynos5_set_spi_clk(periph_id, rate); } @@ -1758,8 +1771,12 @@ int set_i2s_clk_prescaler(unsigned int src_frq, unsigned int dst_frq, int set_i2s_clk_source(unsigned int i2s_id) { - if (cpu_is_exynos5()) - return exynos5_set_i2s_clk_source(i2s_id); + if (cpu_is_exynos5()) { + if (proid_is_exynos542x()) + return exynos5420_set_i2s_clk_source(); + else + return exynos5_set_i2s_clk_source(i2s_id); + } return 0; } diff --git a/arch/arm/mach-exynos/clock_init_exynos5.c b/arch/arm/mach-exynos/clock_init_exynos5.c index e63ef64523..1cb8d391e7 100644 --- a/arch/arm/mach-exynos/clock_init_exynos5.c +++ b/arch/arm/mach-exynos/clock_init_exynos5.c @@ -968,7 +968,7 @@ static void exynos5420_system_clock_init(void) void system_clock_init(void) { - if (proid_is_exynos5420() || proid_is_exynos5422()) + if (proid_is_exynos542x()) exynos5420_system_clock_init(); else exynos5250_system_clock_init(); diff --git a/arch/arm/mach-exynos/common_setup.h b/arch/arm/mach-exynos/common_setup.h index 2829fb269e..4e3702b928 100644 --- a/arch/arm/mach-exynos/common_setup.h +++ b/arch/arm/mach-exynos/common_setup.h @@ -78,7 +78,7 @@ static inline void configure_l2_ctlr(void) CACHE_TAG_RAM_LATENCY_2_CYCLES | CACHE_DATA_RAM_LATENCY_2_CYCLES; - if (proid_is_exynos5420() || proid_is_exynos5422()) { + if (proid_is_exynos542x()) { val |= CACHE_ECC_AND_PARITY | CACHE_TAG_RAM_LATENCY_3_CYCLES | CACHE_DATA_RAM_LATENCY_3_CYCLES; @@ -97,7 +97,7 @@ static inline void configure_l2_actlr(void) { uint32_t val; - if (proid_is_exynos5420() || proid_is_exynos5422()) { + if (proid_is_exynos542x()) { mrc_l2_aux_ctlr(val); val |= CACHE_ENABLE_FORCE_L2_LOGIC | CACHE_DISABLE_CLEAN_EVICT; diff --git a/arch/arm/mach-exynos/include/mach/clock.h b/arch/arm/mach-exynos/include/mach/clock.h index edf62bdf85..e4c706adea 100644 --- a/arch/arm/mach-exynos/include/mach/clock.h +++ b/arch/arm/mach-exynos/include/mach/clock.h @@ -1370,10 +1370,13 @@ struct set_epll_con_val { #define AUDIO_1_RATIO_MASK 0x0f #define AUDIO0_SEL_MASK 0xf +#define EXYNOS5420_AUDIO0_SEL_MASK (0x3 << 28) #define AUDIO1_SEL_MASK 0xf #define CLK_SRC_SCLK_EPLL 0x7 +#define EXYNOS5420_CLK_SRC_SCLK_EPLL (0x6 << 28) #define CLK_SRC_MOUT_EPLL (1<<12) +#define EXYNOS5420_CLK_SRC_MOUT_EPLL BIT(20) #define AUDIO_CLKMUX_ASS (1<<0) /* CON0 bit-fields */ diff --git a/arch/arm/mach-exynos/include/mach/cpu.h b/arch/arm/mach-exynos/include/mach/cpu.h index aeb3755fe6..766edeeb29 100644 --- a/arch/arm/mach-exynos/include/mach/cpu.h +++ b/arch/arm/mach-exynos/include/mach/cpu.h @@ -268,6 +268,8 @@ IS_EXYNOS_TYPE(exynos5250, 0x5250) IS_EXYNOS_TYPE(exynos5420, 0x5420) IS_EXYNOS_TYPE(exynos5422, 0x5422) +#define proid_is_exynos542x() (proid_is_exynos5420() || proid_is_exynos5422()) + #define SAMSUNG_BASE(device, base) \ static inline unsigned long __attribute__((no_instrument_function)) \ samsung_get_base_##device(void) \ @@ -277,7 +279,7 @@ static inline unsigned long __attribute__((no_instrument_function)) \ return EXYNOS4X12_##base; \ return EXYNOS4_##base; \ } else if (cpu_is_exynos5()) { \ - if (proid_is_exynos5420() || proid_is_exynos5422()) \ + if (proid_is_exynos542x()) \ return EXYNOS5420_##base; \ return EXYNOS5_##base; \ } \ diff --git a/arch/arm/mach-exynos/include/mach/gpio.h b/arch/arm/mach-exynos/include/mach/gpio.h index 272e00bae8..f9975d7919 100644 --- a/arch/arm/mach-exynos/include/mach/gpio.h +++ b/arch/arm/mach-exynos/include/mach/gpio.h @@ -1397,7 +1397,7 @@ static struct gpio_info exynos5420_gpio_data[EXYNOS5420_GPIO_NUM_PARTS] = { static inline struct gpio_info *get_gpio_data(void) { if (cpu_is_exynos5()) { - if (proid_is_exynos5420() || proid_is_exynos5422()) + if (proid_is_exynos542x()) return exynos5420_gpio_data; else return exynos5_gpio_data; @@ -1414,7 +1414,7 @@ static inline struct gpio_info *get_gpio_data(void) static inline unsigned int get_bank_num(void) { if (cpu_is_exynos5()) { - if (proid_is_exynos5420() || proid_is_exynos5422()) + if (proid_is_exynos542x()) return EXYNOS5420_GPIO_NUM_PARTS; else return EXYNOS5_GPIO_NUM_PARTS; diff --git a/arch/arm/mach-exynos/pinmux.c b/arch/arm/mach-exynos/pinmux.c index f6743ca0f6..b24f1bb8f4 100644 --- a/arch/arm/mach-exynos/pinmux.c +++ b/arch/arm/mach-exynos/pinmux.c @@ -378,6 +378,20 @@ static void exynos5_i2s_config(int peripheral) } } +static void exynos5420_i2s_config(int peripheral) +{ + int i; + + switch (peripheral) { + case PERIPH_ID_I2S0: + for (i = 0; i < 5; i++) + gpio_cfg_pin(EXYNOS5420_GPIO_Z0 + i, + S5P_GPIO_FUNC(0x02)); + break; + } +} + + void exynos5_spi_config(int peripheral) { int cfg = 0, pin = 0, i; @@ -550,6 +564,9 @@ static int exynos5420_pinmux_config(int peripheral, int flags) case PERIPH_ID_I2C10: exynos5420_i2c_config(peripheral); break; + case PERIPH_ID_I2S0: + exynos5420_i2s_config(peripheral); + break; case PERIPH_ID_PWM0: gpio_cfg_pin(EXYNOS5420_GPIO_B20, S5P_GPIO_FUNC(2)); break; @@ -863,7 +880,7 @@ static int exynos4x12_pinmux_config(int peripheral, int flags) int exynos_pinmux_config(int peripheral, int flags) { if (cpu_is_exynos5()) { - if (proid_is_exynos5420() || proid_is_exynos5422()) + if (proid_is_exynos542x()) return exynos5420_pinmux_config(peripheral, flags); else if (proid_is_exynos5250()) return exynos5_pinmux_config(peripheral, flags); diff --git a/arch/arm/mach-exynos/power.c b/arch/arm/mach-exynos/power.c index 63c410acef..f2a6c00dd6 100644 --- a/arch/arm/mach-exynos/power.c +++ b/arch/arm/mach-exynos/power.c @@ -124,7 +124,7 @@ static void exynos5420_set_usbdev_phy_ctrl(unsigned int enable) void set_usbdrd_phy_ctrl(unsigned int enable) { if (cpu_is_exynos5()) { - if (proid_is_exynos5420() || proid_is_exynos5422()) + if (proid_is_exynos542x()) exynos5420_set_usbdev_phy_ctrl(enable); else exynos5_set_usbdrd_phy_ctrl(enable); diff --git a/arch/sandbox/cpu/sdl.c b/arch/sandbox/cpu/sdl.c index c7a8d94549..f668f5379a 100644 --- a/arch/sandbox/cpu/sdl.c +++ b/arch/sandbox/cpu/sdl.c @@ -4,13 +4,24 @@ */ #include <errno.h> +#include <unistd.h> #include <linux/input.h> #include <SDL/SDL.h> -#include <sound.h> #include <asm/state.h> -enum { - SAMPLE_RATE = 22050, +/** + * struct buf_info - a data buffer holding audio data + * + * @pos: Current position playing in audio buffer + * @size: Size of data in audio buffer (0=empty) + * @alloced: Allocated size of audio buffer (max size it can hold) + * @data: Audio data + */ +struct buf_info { + uint pos; + uint size; + uint alloced; + uint8_t *data; }; static struct sdl_info { @@ -20,12 +31,12 @@ static struct sdl_info { int depth; int pitch; uint frequency; - uint audio_pos; - uint audio_size; uint sample_rate; - uint8_t *audio_data; bool audio_active; bool inited; + int cur_buf; + struct buf_info buf[2]; + bool running; } sdl; static void sandbox_sdl_poll_events(void) @@ -243,24 +254,37 @@ int sandbox_sdl_key_pressed(int keycode) void sandbox_sdl_fill_audio(void *udata, Uint8 *stream, int len) { + struct buf_info *buf; int avail; + int i; - avail = sdl.audio_size - sdl.audio_pos; - if (avail < len) - len = avail; - - SDL_MixAudio(stream, sdl.audio_data + sdl.audio_pos, len, - SDL_MIX_MAXVOLUME); - sdl.audio_pos += len; - - /* Loop if we are at the end */ - if (sdl.audio_pos == sdl.audio_size) - sdl.audio_pos = 0; + for (i = 0; i < 2; i++) { + buf = &sdl.buf[sdl.cur_buf]; + avail = buf->size - buf->pos; + if (avail <= 0) { + sdl.cur_buf = 1 - sdl.cur_buf; + continue; + } + if (avail > len) + avail = len; + + SDL_MixAudio(stream, buf->data + buf->pos, avail, + SDL_MIX_MAXVOLUME); + buf->pos += avail; + len -= avail; + + /* Move to next buffer if we are at the end */ + if (buf->pos == buf->size) + buf->size = 0; + else + break; + } } -int sandbox_sdl_sound_init(void) +int sandbox_sdl_sound_init(int rate, int channels) { SDL_AudioSpec wanted; + int i; if (sandbox_sdl_ensure_init()) return -1; @@ -269,20 +293,27 @@ int sandbox_sdl_sound_init(void) return 0; /* Set the audio format */ - wanted.freq = SAMPLE_RATE; + wanted.freq = rate; wanted.format = AUDIO_S16; - wanted.channels = 1; /* 1 = mono, 2 = stereo */ + wanted.channels = channels; wanted.samples = 1024; /* Good low-latency value for callback */ wanted.callback = sandbox_sdl_fill_audio; wanted.userdata = NULL; - sdl.audio_size = sizeof(uint16_t) * wanted.freq; - sdl.audio_data = malloc(sdl.audio_size); - if (!sdl.audio_data) { - printf("%s: Out of memory\n", __func__); - return -1; + for (i = 0; i < 2; i++) { + struct buf_info *buf = &sdl.buf[i]; + + buf->alloced = sizeof(uint16_t) * wanted.freq * wanted.channels; + buf->data = malloc(buf->alloced); + if (!buf->data) { + printf("%s: Out of memory\n", __func__); + if (i == 1) + free(sdl.buf[0].data); + return -1; + } + buf->pos = 0; + buf->size = 0; } - sdl.audio_pos = 0; if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { printf("Unable to initialize SDL audio: %s\n", SDL_GetError()); @@ -296,33 +327,50 @@ int sandbox_sdl_sound_init(void) } sdl.audio_active = true; sdl.sample_rate = wanted.freq; + sdl.cur_buf = 0; + sdl.running = 0; return 0; err: - free(sdl.audio_data); + for (i = 0; i < 2; i++) + free(sdl.buf[i].data); return -1; } -int sandbox_sdl_sound_start(uint frequency) +int sandbox_sdl_sound_play(const void *data, uint size) { + struct buf_info *buf; + if (!sdl.audio_active) - return -1; - sdl.frequency = frequency; - sound_create_square_wave(sdl.sample_rate, - (unsigned short *)sdl.audio_data, - sdl.audio_size, frequency); - sdl.audio_pos = 0; - SDL_PauseAudio(0); + return 0; + + buf = &sdl.buf[0]; + if (buf->size) + buf = &sdl.buf[1]; + while (buf->size) + usleep(1000); + + if (size > buf->alloced) + return -E2BIG; + + memcpy(buf->data, data, size); + buf->size = size; + buf->pos = 0; + if (!sdl.running) { + SDL_PauseAudio(0); + sdl.running = 1; + } return 0; } int sandbox_sdl_sound_stop(void) { - if (!sdl.audio_active) - return -1; - SDL_PauseAudio(1); + if (sdl.running) { + SDL_PauseAudio(1); + sdl.running = 0; + } return 0; } diff --git a/arch/sandbox/dts/sandbox.dts b/arch/sandbox/dts/sandbox.dts index ce3c88c221..ae3189ec8c 100644 --- a/arch/sandbox/dts/sandbox.dts +++ b/arch/sandbox/dts/sandbox.dts @@ -18,6 +18,11 @@ stdout-path = "/serial"; }; + audio: audio-codec { + compatible = "sandbox,audio-codec"; + #sound-dai-cells = <1>; + }; + cros_ec: cros-ec { reg = <0 0>; u-boot,dm-pre-reloc; @@ -127,6 +132,11 @@ }; }; + i2s: i2s { + compatible = "sandbox,i2s"; + #sound-dai-cells = <1>; + }; + lcd { u-boot,dm-pre-reloc; compatible = "sandbox,lcd-sdl"; @@ -190,6 +200,17 @@ compatible = "sandbox,reset"; }; + sound { + compatible = "sandbox,sound"; + cpu { + sound-dai = <&i2s 0>; + }; + + codec { + sound-dai = <&audio 0>; + }; + }; + spi@0 { u-boot,dm-pre-reloc; #address-cells = <1>; diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 6b1c2692ba..3790b4c520 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -42,6 +42,11 @@ osd0 = "/osd"; }; + audio: audio-codec { + compatible = "sandbox,audio-codec"; + #sound-dai-cells = <1>; + }; + cros_ec: cros-ec { reg = <0 0>; compatible = "google,cros-ec-sandbox"; @@ -82,6 +87,8 @@ test2-gpios = <&gpio_a 1>, <&gpio_a 4>, <&gpio_b 6 1 3 2 1>, <&gpio_b 7 2 3 2 1>, <&gpio_b 8 4 3 2 1>, <&gpio_b 9 0xc 3 2 1>; + int-value = <1234>; + uint-value = <(-1234)>; }; junk { @@ -373,6 +380,11 @@ u-boot,dm-pre-reloc; }; + i2s: i2s { + compatible = "sandbox,i2s"; + #sound-dai-cells = <1>; + }; + misc-test { compatible = "sandbox,misc_sandbox"; }; @@ -528,6 +540,17 @@ compatible = "sandbox,smem"; }; + sound { + compatible = "sandbox,sound"; + cpu { + sound-dai = <&i2s 0>; + }; + + codec { + sound-dai = <&audio 0>; + }; + }; + spi@0 { #address-cells = <1>; #size-cells = <0>; diff --git a/arch/sandbox/include/asm/sdl.h b/arch/sandbox/include/asm/sdl.h index 1c4380c592..1027b59e73 100644 --- a/arch/sandbox/include/asm/sdl.h +++ b/arch/sandbox/include/asm/sdl.h @@ -54,12 +54,12 @@ int sandbox_sdl_scan_keys(int key[], int max_keys); int sandbox_sdl_key_pressed(int keycode); /** - * sandbox_sdl_sound_start() - start playing a sound + * sandbox_sdl_sound_play() - Play a sound * - * @frequency: Frequency of sounds in Hertz - * @return 0 if OK, -ENODEV if no sound is available + * @data: Data to play (typically 16-bit) + * @count: Number of bytes in data */ -int sandbox_sdl_sound_start(uint frequency); +int sandbox_sdl_sound_play(const void *data, uint count); /** * sandbox_sdl_sound_stop() - stop playing a sound @@ -71,9 +71,11 @@ int sandbox_sdl_sound_stop(void); /** * sandbox_sdl_sound_init() - set up the sound system * + * @rate: Sample rate to use + * @channels: Number of channels to use (1=mono, 2=stereo) * @return 0 if OK, -ENODEV if no sound is available */ -int sandbox_sdl_sound_init(void); +int sandbox_sdl_sound_init(int rate, int channels); #else static inline int sandbox_sdl_init_display(int width, int height, @@ -102,12 +104,17 @@ static inline int sandbox_sdl_sound_start(uint frequency) return -ENODEV; } +int sandbox_sdl_sound_play(const void *data, uint count) +{ + return -ENODEV; +} + static inline int sandbox_sdl_sound_stop(void) { return -ENODEV; } -static inline int sandbox_sdl_sound_init(void) +int sandbox_sdl_sound_init(int rate, int channels) { return -ENODEV; } diff --git a/arch/sandbox/include/asm/sound.h b/arch/sandbox/include/asm/sound.h deleted file mode 100644 index a6015b0f60..0000000000 --- a/arch/sandbox/include/asm/sound.h +++ /dev/null @@ -1,13 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (c) 2013 Google, Inc - */ - -#ifndef __SANDBOX_SOUND_H -#define __SANDBOX_SOUND_H - -int sound_play(unsigned int msec, unsigned int frequency); - -int sound_init(const void *blob); - -#endif diff --git a/arch/sandbox/include/asm/test.h b/arch/sandbox/include/asm/test.h index 5e81839295..74f9618822 100644 --- a/arch/sandbox/include/asm/test.h +++ b/arch/sandbox/include/asm/test.h @@ -121,4 +121,44 @@ int sandbox_pwm_get_config(struct udevice *dev, uint channel, uint *period_nsp, */ void sandbox_sf_set_block_protect(struct udevice *dev, int bp_mask); +/** + * sandbox_get_codec_params() - Read back codec parameters + * + * This reads back the parameters set by audio_codec_set_params() for the + * sandbox audio driver. Arguments are as for that function. + */ +void sandbox_get_codec_params(struct udevice *dev, int *interfacep, int *ratep, + int *mclk_freqp, int *bits_per_samplep, + uint *channelsp); + +/** + * sandbox_get_i2s_sum() - Read back the sum of the audio data so far + * + * This data is provided to the sandbox driver by the I2S tx_data() method. + * + * @dev: Device to check + * @return sum of audio data + */ +int sandbox_get_i2s_sum(struct udevice *dev); + +/** + * sandbox_get_setup_called() - Returns the number of times setup(*) was called + * + * This is used in the sound test + * + * @dev: Device to check + * @return call count for the setup() method + */ +int sandbox_get_setup_called(struct udevice *dev); + +/** + * sandbox_get_sound_sum() - Read back the sum of the sound data so far + * + * This data is provided to the sandbox driver by the sound play() method. + * + * @dev: Device to check + * @return sum of audio data + */ +int sandbox_get_sound_sum(struct udevice *dev); + #endif diff --git a/cmd/sound.c b/cmd/sound.c index d1cbc14f8d..638f29df21 100644 --- a/cmd/sound.c +++ b/cmd/sound.c @@ -6,6 +6,7 @@ #include <common.h> #include <command.h> +#include <dm.h> #include <fdtdec.h> #include <sound.h> @@ -14,11 +15,14 @@ DECLARE_GLOBAL_DATA_PTR; /* Initilaise sound subsystem */ static int do_init(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) { + struct udevice *dev; int ret; - ret = sound_init(gd->fdt_blob); + ret = uclass_first_device_err(UCLASS_SOUND, &dev); + if (!ret) + ret = sound_setup(dev); if (ret) { - printf("Initialise Audio driver failed\n"); + printf("Initialise Audio driver failed (ret=%d)\n", ret); return CMD_RET_FAILURE; } @@ -28,6 +32,7 @@ static int do_init(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) /* play sound from buffer */ static int do_play(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) { + struct udevice *dev; int ret = 0; int msec = 1000; int freq = 400; @@ -37,9 +42,11 @@ static int do_play(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) if (argc > 2) freq = simple_strtoul(argv[2], NULL, 10); - ret = sound_play(msec, freq); + ret = uclass_first_device_err(UCLASS_SOUND, &dev); + if (!ret) + ret = sound_beep(dev, msec, freq); if (ret) { - printf("play failed"); + printf("Sound device failed to play (err=%d)\n", ret); return CMD_RET_FAILURE; } diff --git a/configs/peach-pi_defconfig b/configs/peach-pi_defconfig index 338eae20b4..14c835cad8 100644 --- a/configs/peach-pi_defconfig +++ b/configs/peach-pi_defconfig @@ -21,6 +21,7 @@ CONFIG_CMD_USB=y # CONFIG_CMD_SETEXPR is not set CONFIG_CMD_CACHE=y CONFIG_CMD_TIME=y +CONFIG_CMD_SOUND=y CONFIG_CMD_PMIC=y CONFIG_CMD_REGULATOR=y CONFIG_CMD_TPM=y @@ -28,7 +29,6 @@ CONFIG_CMD_TPM_TEST=y CONFIG_CMD_EXT4_WRITE=y CONFIG_DEFAULT_DEVICE_TREE="exynos5800-peach-pi" CONFIG_ENV_IS_IN_SPI_FLASH=y -CONFIG_DM_I2C_COMPAT=y CONFIG_I2C_CROS_EC_TUNNEL=y CONFIG_I2C_MUX=y CONFIG_I2C_ARB_GPIO_CHALLENGE=y @@ -52,6 +52,7 @@ CONFIG_PWM_EXYNOS=y CONFIG_SOUND=y CONFIG_I2S=y CONFIG_I2S_SAMSUNG=y +CONFIG_SOUND_MAX98090=y CONFIG_SOUND_MAX98095=y CONFIG_SOUND_WM8994=y CONFIG_EXYNOS_SPI=y diff --git a/configs/peach-pit_defconfig b/configs/peach-pit_defconfig index 933c823ea8..9a3a11504d 100644 --- a/configs/peach-pit_defconfig +++ b/configs/peach-pit_defconfig @@ -20,6 +20,7 @@ CONFIG_CMD_USB=y # CONFIG_CMD_SETEXPR is not set CONFIG_CMD_CACHE=y CONFIG_CMD_TIME=y +CONFIG_CMD_SOUND=y CONFIG_CMD_PMIC=y CONFIG_CMD_REGULATOR=y CONFIG_CMD_TPM=y @@ -27,7 +28,6 @@ CONFIG_CMD_TPM_TEST=y CONFIG_CMD_EXT4_WRITE=y CONFIG_DEFAULT_DEVICE_TREE="exynos5420-peach-pit" CONFIG_ENV_IS_IN_SPI_FLASH=y -CONFIG_DM_I2C_COMPAT=y CONFIG_I2C_CROS_EC_TUNNEL=y CONFIG_I2C_MUX=y CONFIG_I2C_ARB_GPIO_CHALLENGE=y @@ -51,6 +51,7 @@ CONFIG_PWM_EXYNOS=y CONFIG_SOUND=y CONFIG_I2S=y CONFIG_I2S_SAMSUNG=y +CONFIG_SOUND_MAX98090=y CONFIG_SOUND_MAX98095=y CONFIG_SOUND_WM8994=y CONFIG_EXYNOS_SPI=y diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index e6680d9a59..aede14569d 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -1,5 +1,4 @@ CONFIG_SYS_TEXT_BASE=0 -CONFIG_SYS_MALLOC_F_LEN=0x2000 CONFIG_SANDBOX64=y CONFIG_DISTRO_DEFAULTS=y CONFIG_NR_DRAM_BANKS=1 diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 5b65c6157a..1e7d41d19e 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -1,5 +1,4 @@ CONFIG_SYS_TEXT_BASE=0 -CONFIG_SYS_MALLOC_F_LEN=0x2000 CONFIG_DEBUG_UART=y CONFIG_DISTRO_DEFAULTS=y CONFIG_NR_DRAM_BANKS=1 diff --git a/configs/sandbox_flattree_defconfig b/configs/sandbox_flattree_defconfig index 756b839b08..4f3757c8bc 100644 --- a/configs/sandbox_flattree_defconfig +++ b/configs/sandbox_flattree_defconfig @@ -1,5 +1,4 @@ CONFIG_SYS_TEXT_BASE=0 -CONFIG_SYS_MALLOC_F_LEN=0x2000 CONFIG_DISTRO_DEFAULTS=y CONFIG_NR_DRAM_BANKS=1 CONFIG_FIT=y diff --git a/configs/sandbox_noblk_defconfig b/configs/sandbox_noblk_defconfig index e71e2d3860..6c7d08e42e 100644 --- a/configs/sandbox_noblk_defconfig +++ b/configs/sandbox_noblk_defconfig @@ -1,5 +1,4 @@ CONFIG_SYS_TEXT_BASE=0 -CONFIG_SYS_MALLOC_F_LEN=0x2000 CONFIG_DISTRO_DEFAULTS=y CONFIG_NR_DRAM_BANKS=1 CONFIG_FIT=y diff --git a/configs/sandbox_spl_defconfig b/configs/sandbox_spl_defconfig index 452a2ef4a5..2f83812528 100644 --- a/configs/sandbox_spl_defconfig +++ b/configs/sandbox_spl_defconfig @@ -1,7 +1,6 @@ CONFIG_SYS_TEXT_BASE=0 CONFIG_SPL_LIBCOMMON_SUPPORT=y CONFIG_SPL_LIBGENERIC_SUPPORT=y -CONFIG_SYS_MALLOC_F_LEN=0x2000 CONFIG_SPL_SERIAL_SUPPORT=y CONFIG_SPL_DRIVERS_MISC_SUPPORT=y CONFIG_SPL=y diff --git a/configs/smdk5250_defconfig b/configs/smdk5250_defconfig index 161454b33f..21018d8b1b 100644 --- a/configs/smdk5250_defconfig +++ b/configs/smdk5250_defconfig @@ -30,7 +30,6 @@ CONFIG_CMD_REGULATOR=y CONFIG_CMD_EXT4_WRITE=y CONFIG_DEFAULT_DEVICE_TREE="exynos5250-smdk5250" CONFIG_ENV_IS_IN_SPI_FLASH=y -CONFIG_DM_I2C_COMPAT=y CONFIG_MMC_DW=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_S5P=y diff --git a/configs/smdk5420_defconfig b/configs/smdk5420_defconfig index 433e9a8447..4e4558944c 100644 --- a/configs/smdk5420_defconfig +++ b/configs/smdk5420_defconfig @@ -25,7 +25,6 @@ CONFIG_CMD_TIME=y CONFIG_CMD_EXT4_WRITE=y CONFIG_DEFAULT_DEVICE_TREE="exynos5420-smdk5420" CONFIG_ENV_IS_IN_SPI_FLASH=y -CONFIG_DM_I2C_COMPAT=y CONFIG_MMC_DW=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_S5P=y diff --git a/configs/snow_defconfig b/configs/snow_defconfig index 21080091a7..e0c1bf862c 100644 --- a/configs/snow_defconfig +++ b/configs/snow_defconfig @@ -34,7 +34,6 @@ CONFIG_CMD_TPM_TEST=y CONFIG_CMD_EXT4_WRITE=y CONFIG_DEFAULT_DEVICE_TREE="exynos5250-snow" CONFIG_ENV_IS_IN_SPI_FLASH=y -CONFIG_DM_I2C_COMPAT=y CONFIG_I2C_CROS_EC_LDO=y CONFIG_I2C_MUX=y CONFIG_I2C_ARB_GPIO_CHALLENGE=y diff --git a/configs/spring_defconfig b/configs/spring_defconfig index ca1799895e..c089517692 100644 --- a/configs/spring_defconfig +++ b/configs/spring_defconfig @@ -34,7 +34,6 @@ CONFIG_CMD_TPM_TEST=y CONFIG_CMD_EXT4_WRITE=y CONFIG_DEFAULT_DEVICE_TREE="exynos5250-spring" CONFIG_ENV_IS_IN_SPI_FLASH=y -CONFIG_DM_I2C_COMPAT=y CONFIG_I2C_CROS_EC_LDO=y CONFIG_I2C_MUX=y CONFIG_I2C_ARB_GPIO_CHALLENGE=y diff --git a/drivers/core/read.c b/drivers/core/read.c index cdd78be03e..3c46b3674e 100644 --- a/drivers/core/read.c +++ b/drivers/core/read.c @@ -21,6 +21,29 @@ int dev_read_u32_default(struct udevice *dev, const char *propname, int def) return ofnode_read_u32_default(dev_ofnode(dev), propname, def); } +int dev_read_s32(struct udevice *dev, const char *propname, s32 *outp) +{ + return ofnode_read_u32(dev_ofnode(dev), propname, (u32 *)outp); +} + +int dev_read_s32_default(struct udevice *dev, const char *propname, int def) +{ + return ofnode_read_u32_default(dev_ofnode(dev), propname, def); +} + +int dev_read_u32u(struct udevice *dev, const char *propname, uint *outp) +{ + u32 val; + int ret; + + ret = ofnode_read_u32(dev_ofnode(dev), propname, &val); + if (ret) + return ret; + *outp = val; + + return 0; +} + const char *dev_read_string(struct udevice *dev, const char *propname) { return ofnode_read_string(dev_ofnode(dev), propname); diff --git a/drivers/sound/Kconfig b/drivers/sound/Kconfig index 5de86c05c6..c0d97cca33 100644 --- a/drivers/sound/Kconfig +++ b/drivers/sound/Kconfig @@ -31,6 +31,14 @@ config I2S_SAMSUNG option provides an implementation for sound_init() and sound_play(). +config SOUND_MAX98090 + bool "Support Maxim max98090 audio codec" + depends on I2S_SAMSUNG + help + Enable the max98090 audio codec. This is connected via I2S for + audio data and I2C for codec control. At present it only works + with the Samsung I2S driver. + config SOUND_MAX98095 bool "Support Maxim max98095 audio codec" depends on I2S_SAMSUNG diff --git a/drivers/sound/Makefile b/drivers/sound/Makefile index 696c5aecbe..1de4346ec7 100644 --- a/drivers/sound/Makefile +++ b/drivers/sound/Makefile @@ -4,8 +4,12 @@ # R. Chandrasekar <rcsekar@samsung.com> obj-$(CONFIG_SOUND) += sound.o -obj-$(CONFIG_I2S) += sound-i2s.o +obj-$(CONFIG_SOUND) += codec-uclass.o +obj-$(CONFIG_SOUND) += i2s-uclass.o +obj-$(CONFIG_SOUND) += sound-uclass.o obj-$(CONFIG_I2S_SAMSUNG) += samsung-i2s.o obj-$(CONFIG_SOUND_SANDBOX) += sandbox.o +obj-$(CONFIG_I2S_SAMSUNG) += samsung_sound.o obj-$(CONFIG_SOUND_WM8994) += wm8994.o -obj-$(CONFIG_SOUND_MAX98095) += max98095.o +obj-$(CONFIG_SOUND_MAX98090) += max98090.o maxim_codec.o +obj-$(CONFIG_SOUND_MAX98095) += max98095.o maxim_codec.o diff --git a/drivers/sound/codec-uclass.c b/drivers/sound/codec-uclass.c new file mode 100644 index 0000000000..1ec77acfc1 --- /dev/null +++ b/drivers/sound/codec-uclass.c @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <dm.h> +#include <audio_codec.h> + +int audio_codec_set_params(struct udevice *dev, int interface, int rate, + int mclk_freq, int bits_per_sample, uint channels) +{ + struct audio_codec_ops *ops = audio_codec_get_ops(dev); + + if (!ops->set_params) + return -ENOSYS; + + return ops->set_params(dev, interface, rate, mclk_freq, bits_per_sample, + channels); +} + +UCLASS_DRIVER(audio_codec) = { + .id = UCLASS_AUDIO_CODEC, + .name = "audio-codec", +}; diff --git a/drivers/sound/i2s-uclass.c b/drivers/sound/i2s-uclass.c new file mode 100644 index 0000000000..b741e3952d --- /dev/null +++ b/drivers/sound/i2s-uclass.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <dm.h> +#include <i2s.h> + +int i2s_tx_data(struct udevice *dev, void *data, uint data_size) +{ + struct i2s_ops *ops = i2s_get_ops(dev); + + if (!ops->tx_data) + return -ENOSYS; + + return ops->tx_data(dev, data, data_size); +} + +UCLASS_DRIVER(i2s) = { + .id = UCLASS_I2S, + .name = "i2s", + .per_device_auto_alloc_size = sizeof(struct i2s_uc_priv), +}; diff --git a/drivers/sound/max98090.c b/drivers/sound/max98090.c new file mode 100644 index 0000000000..346ff5ffbe --- /dev/null +++ b/drivers/sound/max98090.c @@ -0,0 +1,377 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * max98090.c -- MAX98090 ALSA SoC Audio driver + * + * Copyright 2011 Maxim Integrated Products + */ + +#include <common.h> +#include <audio_codec.h> +#include <div64.h> +#include <dm.h> +#include <i2c.h> +#include <i2s.h> +#include <sound.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <asm/arch/clk.h> +#include <asm/arch/cpu.h> +#include <asm/arch/power.h> +#include "maxim_codec.h" +#include "max98090.h" + +/* + * Sets hw params for max98090 + * + * @priv: max98090 information pointer + * @rate: Sampling rate + * @bits_per_sample: Bits per sample + * + * @return -EIO for error, 0 for success. + */ +int max98090_hw_params(struct maxim_priv *priv, unsigned int rate, + unsigned int bits_per_sample) +{ + int error; + unsigned char value; + + switch (bits_per_sample) { + case 16: + maxim_i2c_read(priv, M98090_REG_INTERFACE_FORMAT, &value); + error = maxim_bic_or(priv, M98090_REG_INTERFACE_FORMAT, + M98090_WS_MASK, 0); + maxim_i2c_read(priv, M98090_REG_INTERFACE_FORMAT, &value); + break; + default: + debug("%s: Illegal bits per sample %d.\n", + __func__, bits_per_sample); + return -1; + } + + /* Update filter mode */ + if (rate < 240000) + error |= maxim_bic_or(priv, M98090_REG_FILTER_CONFIG, + M98090_MODE_MASK, 0); + else + error |= maxim_bic_or(priv, M98090_REG_FILTER_CONFIG, + M98090_MODE_MASK, M98090_MODE_MASK); + + /* Update sample rate mode */ + if (rate < 50000) + error |= maxim_bic_or(priv, M98090_REG_FILTER_CONFIG, + M98090_DHF_MASK, 0); + else + error |= maxim_bic_or(priv, M98090_REG_FILTER_CONFIG, + M98090_DHF_MASK, M98090_DHF_MASK); + + if (error < 0) { + debug("%s: Error setting hardware params.\n", __func__); + return -EIO; + } + priv->rate = rate; + + return 0; +} + +/* + * Configures Audio interface system clock for the given frequency + * + * @priv: max98090 information + * @freq: Sampling frequency in Hz + * + * @return -EIO for error, 0 for success. + */ +int max98090_set_sysclk(struct maxim_priv *priv, unsigned int freq) +{ + int error = 0; + + /* Requested clock frequency is already setup */ + if (freq == priv->sysclk) + return 0; + + /* Setup clocks for slave mode, and using the PLL + * PSCLK = 0x01 (when master clk is 10MHz to 20MHz) + * 0x02 (when master clk is 20MHz to 40MHz).. + * 0x03 (when master clk is 40MHz to 60MHz).. + */ + if (freq >= 10000000 && freq < 20000000) { + error = maxim_i2c_write(priv, M98090_REG_SYSTEM_CLOCK, + M98090_PSCLK_DIV1); + } else if (freq >= 20000000 && freq < 40000000) { + error = maxim_i2c_write(priv, M98090_REG_SYSTEM_CLOCK, + M98090_PSCLK_DIV2); + } else if (freq >= 40000000 && freq < 60000000) { + error = maxim_i2c_write(priv, M98090_REG_SYSTEM_CLOCK, + M98090_PSCLK_DIV4); + } else { + debug("%s: Invalid master clock frequency\n", __func__); + return -1; + } + + debug("%s: Clock at %uHz\n", __func__, freq); + + if (error < 0) + return -1; + + priv->sysclk = freq; + + return 0; +} + +/* + * Sets Max98090 I2S format + * + * @priv: max98090 information + * @fmt: i2S format - supports a subset of the options defined in i2s.h. + * + * @return -EIO for error, 0 for success. + */ +int max98090_set_fmt(struct maxim_priv *priv, int fmt) +{ + u8 regval = 0; + int error = 0; + + if (fmt == priv->fmt) + return 0; + + priv->fmt = fmt; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + /* Set to slave mode PLL - MAS mode off */ + error |= maxim_i2c_write(priv, M98090_REG_CLOCK_RATIO_NI_MSB, + 0x00); + error |= maxim_i2c_write(priv, M98090_REG_CLOCK_RATIO_NI_LSB, + 0x00); + error |= maxim_bic_or(priv, M98090_REG_CLOCK_MODE, + M98090_USE_M1_MASK, 0); + break; + case SND_SOC_DAIFMT_CBM_CFM: + /* Set to master mode */ + debug("Master mode not supported\n"); + break; + case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBM_CFS: + default: + debug("%s: Clock mode unsupported\n", __func__); + return -EINVAL; + } + + error |= maxim_i2c_write(priv, M98090_REG_MASTER_MODE, regval); + + regval = 0; + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + regval |= M98090_DLY_MASK; + break; + case SND_SOC_DAIFMT_LEFT_J: + break; + case SND_SOC_DAIFMT_RIGHT_J: + regval |= M98090_RJ_MASK; + break; + case SND_SOC_DAIFMT_DSP_A: + /* Not supported mode */ + default: + debug("%s: Unrecognized format.\n", __func__); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + regval |= M98090_WCI_MASK; + break; + case SND_SOC_DAIFMT_IB_NF: + regval |= M98090_BCI_MASK; + break; + case SND_SOC_DAIFMT_IB_IF: + regval |= M98090_BCI_MASK | M98090_WCI_MASK; + break; + default: + debug("%s: Unrecognized inversion settings.\n", __func__); + return -EINVAL; + } + + error |= maxim_i2c_write(priv, M98090_REG_INTERFACE_FORMAT, regval); + + if (error < 0) { + debug("%s: Error setting i2s format.\n", __func__); + return -EIO; + } + + return 0; +} + +/* + * resets the audio codec + * + * @priv: max98090 information + * @return -EIO for error, 0 for success. + */ +static int max98090_reset(struct maxim_priv *priv) +{ + int ret; + + /* + * Gracefully reset the DSP core and the codec hardware in a proper + * sequence. + */ + ret = maxim_i2c_write(priv, M98090_REG_SOFTWARE_RESET, + M98090_SWRESET_MASK); + if (ret != 0) { + debug("%s: Failed to reset DSP: %d\n", __func__, ret); + return ret; + } + mdelay(20); + + return 0; +} + +/* + * Initialise max98090 codec device + * + * @priv: max98090 information + * + * @return -EIO for error, 0 for success. + */ +int max98090_device_init(struct maxim_priv *priv) +{ + unsigned char id; + int error = 0; + + /* Enable codec clock */ + set_xclkout(); + + /* reset the codec, the DSP core, and disable all interrupts */ + error = max98090_reset(priv); + if (error != 0) { + debug("Reset\n"); + return error; + } + + /* initialize private data */ + priv->sysclk = -1U; + priv->rate = -1U; + priv->fmt = -1U; + + error = maxim_i2c_read(priv, M98090_REG_REVISION_ID, &id); + if (error < 0) { + debug("%s: Failure reading hardware revision: %d\n", + __func__, id); + return -EIO; + } + debug("%s: Hardware revision: %d\n", __func__, id); + + return 0; +} + +static int max98090_setup_interface(struct maxim_priv *priv) +{ + unsigned char id; + int error; + + /* Reading interrupt status to clear them */ + error = maxim_i2c_read(priv, M98090_REG_DEVICE_STATUS, &id); + + error |= maxim_i2c_write(priv, M98090_REG_DAC_CONTROL, + M98090_DACHP_MASK); + error |= maxim_i2c_write(priv, M98090_REG_BIAS_CONTROL, + M98090_VCM_MODE_MASK); + + error |= maxim_i2c_write(priv, M98090_REG_LEFT_SPK_MIXER, 0x1); + error |= maxim_i2c_write(priv, M98090_REG_RIGHT_SPK_MIXER, 0x2); + + error |= maxim_i2c_write(priv, M98090_REG_LEFT_SPK_VOLUME, 0x25); + error |= maxim_i2c_write(priv, M98090_REG_RIGHT_SPK_VOLUME, 0x25); + + error |= maxim_i2c_write(priv, M98090_REG_CLOCK_RATIO_NI_MSB, 0x0); + error |= maxim_i2c_write(priv, M98090_REG_CLOCK_RATIO_NI_LSB, 0x0); + error |= maxim_i2c_write(priv, M98090_REG_MASTER_MODE, 0x0); + error |= maxim_i2c_write(priv, M98090_REG_INTERFACE_FORMAT, 0x0); + error |= maxim_i2c_write(priv, M98090_REG_IO_CONFIGURATION, + M98090_SDIEN_MASK); + error |= maxim_i2c_write(priv, M98090_REG_DEVICE_SHUTDOWN, + M98090_SHDNN_MASK); + error |= maxim_i2c_write(priv, M98090_REG_OUTPUT_ENABLE, + M98090_HPREN_MASK | M98090_HPLEN_MASK | + M98090_SPREN_MASK | M98090_SPLEN_MASK | + M98090_DAREN_MASK | M98090_DALEN_MASK); + error |= maxim_i2c_write(priv, M98090_REG_IO_CONFIGURATION, + M98090_SDOEN_MASK | M98090_SDIEN_MASK); + + if (error < 0) + return -EIO; + + return 0; +} + +static int max98090_do_init(struct maxim_priv *priv, int sampling_rate, + int mclk_freq, int bits_per_sample) +{ + int ret = 0; + + ret = max98090_setup_interface(priv); + if (ret < 0) { + debug("%s: max98090 setup interface failed\n", __func__); + return ret; + } + + ret = max98090_set_sysclk(priv, mclk_freq); + if (ret < 0) { + debug("%s: max98090 codec set sys clock failed\n", __func__); + return ret; + } + + ret = max98090_hw_params(priv, sampling_rate, bits_per_sample); + + if (ret == 0) { + ret = max98090_set_fmt(priv, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS); + } + + return ret; +} + +static int max98090_set_params(struct udevice *dev, int interface, int rate, + int mclk_freq, int bits_per_sample, + uint channels) +{ + struct maxim_priv *priv = dev_get_priv(dev); + + return max98090_do_init(priv, rate, mclk_freq, bits_per_sample); +} + +static int max98090_probe(struct udevice *dev) +{ + struct maxim_priv *priv = dev_get_priv(dev); + int ret; + + priv->dev = dev; + ret = max98090_device_init(priv); + if (ret < 0) { + debug("%s: max98090 codec chip init failed\n", __func__); + return ret; + } + + return 0; +} + +static const struct audio_codec_ops max98090_ops = { + .set_params = max98090_set_params, +}; + +static const struct udevice_id max98090_ids[] = { + { .compatible = "maxim,max98090" }, + { } +}; + +U_BOOT_DRIVER(max98090) = { + .name = "max98090", + .id = UCLASS_AUDIO_CODEC, + .of_match = max98090_ids, + .probe = max98090_probe, + .ops = &max98090_ops, + .priv_auto_alloc_size = sizeof(struct maxim_priv), +}; diff --git a/drivers/sound/max98090.h b/drivers/sound/max98090.h new file mode 100644 index 0000000000..3a6983b8e2 --- /dev/null +++ b/drivers/sound/max98090.h @@ -0,0 +1,663 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * max98090.h -- MAX98090 ALSA SoC Audio driver + * + * Copyright 2011 Maxim Integrated Products + */ + +#ifndef _MAX98090_H +#define _MAX98090_H + +#include "maxim_codec.h" + +/* MAX98090 Registers Definition */ + +#define M98090_REG_SOFTWARE_RESET 0x00 +#define M98090_REG_DEVICE_STATUS 0x01 + +#define M98090_REG_QUICK_SAMPLE_RATE 0x05 +#define M98090_REG_DAI_INTERFACE 0x06 +#define M98090_REG_DAC_PATH 0x07 + +#define M98090_REG_MIC_BIAS_VOLTAGE 0x12 +#define M98090_REG_DIGITAL_MIC_ENABLE 0x13 +#define M98090_REG_DIGITAL_MIC_CONFIG 0x14 +#define M98090_REG_SYSTEM_CLOCK 0x1B +#define M98090_REG_CLOCK_RATIO_NI_MSB 0x1D +#define M98090_REG_CLOCK_MODE 0x1C +#define M98090_REG_CLOCK_RATIO_NI_LSB 0x1E + +#define M98090_REG_MASTER_MODE 0x21 +#define M98090_REG_INTERFACE_FORMAT 0x22 +#define M98090_REG_IO_CONFIGURATION 0x25 +#define M98090_REG_FILTER_CONFIG 0x26 + +#define M98090_REG_LEFT_HP_MIXER 0x29 +#define M98090_REG_RIGHT_HP_MIXER 0x2a +#define M98090_REG_HP_CONTROL 0x2b +#define M98090_REG_LEFT_HP_VOLUME 0x2c +#define M98090_REG_RIGHT_HP_VOLUME 0x2d +#define M98090_REG_LEFT_SPK_MIXER 0x2e +#define M98090_REG_RIGHT_SPK_MIXER 0x2f +#define M98090_REG_SPK_CONTROL 0x30 +#define M98090_REG_LEFT_SPK_VOLUME 0x31 +#define M98090_REG_RIGHT_SPK_VOLUME 0x32 + +#define M98090_REG_RCV_LOUTL_CONTROL 0x38 +#define M98090_REG_RCV_LOUTL_VOLUME 0x39 +#define M98090_REG_LOUTR_MIXER 0x3a +#define M98090_REG_LOUTR_CONTROL 0x3b +#define M98090_REG_LOUTR_VOLUME 0x3c +#define M98090_REG_JACK_DETECT 0x3d +#define M98090_REG_INPUT_ENABLE 0x3e +#define M98090_REG_OUTPUT_ENABLE 0x3f +#define M98090_REG_LEVEL_CONTROL 0x40 +#define M98090_REG_DSP_FILTER_ENABLE 0x41 +#define M98090_REG_BIAS_CONTROL 0x42 +#define M98090_REG_DAC_CONTROL 0x43 +#define M98090_REG_ADC_CONTROL 0x44 +#define M98090_REG_DEVICE_SHUTDOWN 0x45 + +#define M98090_REG_REVISION_ID 0xff + +#define M98090_REG_CNT (0xff + 1) +#define M98090_REG_MAX_CACHed 0x45 + +/* MAX98090 Registers Bit Fields */ + +/* + * M98090_REG_SOFTWARE_RESET 0x00 + */ +#define M98090_SWRESET_MASK BIT(7) + +/* + * M98090_REG_QUICK_SAMPLE_RATE 0x05 + */ +#define M98090_SR_96K_MASK BIT(5) +#define M98090_SR_96K_SHIFT 5 +#define M98090_SR_96K_WIDTH 1 +#define M98090_SR_32K_MASK BIT(4) +#define M98090_SR_32K_SHIFT 4 +#define M98090_SR_32K_WIDTH 1 +#define M98090_SR_48K_MASK BIT(3) +#define M98090_SR_48K_SHIFT 3 +#define M98090_SR_48K_WIDTH 1 +#define M98090_SR_44K1_MASK BIT(2) +#define M98090_SR_44K1_SHIFT 2 +#define M98090_SR_44K1_WIDTH 1 +#define M98090_SR_16K_MASK BIT(1) +#define M98090_SR_16K_SHIFT 1 +#define M98090_SR_16K_WIDTH 1 +#define M98090_SR_8K_MASK BIT(0) +#define M98090_SR_8K_SHIFT 0 +#define M98090_SR_8K_WIDTH 1 +#define M98090_SR_MASK 0x3F +#define M98090_SR_ALL_SHIFT 0 +#define M98090_SR_ALL_WIDTH 8 +#define M98090_SR_ALL_NUM BIT(M98090_SR_ALL_WIDTH) + +/* + * M98090_REG_DAI_INTERFACE 0x06 + */ +#define M98090_RJ_M_MASK BIT(5) +#define M98090_RJ_M_SHIFT 5 +#define M98090_RJ_M_WIDTH 1 +#define M98090_RJ_S_MASK BIT(4) +#define M98090_RJ_S_SHIFT 4 +#define M98090_RJ_S_WIDTH 1 +#define M98090_LJ_M_MASK BIT(3) +#define M98090_LJ_M_SHIFT 3 +#define M98090_LJ_M_WIDTH 1 +#define M98090_LJ_S_MASK BIT(2) +#define M98090_LJ_S_SHIFT 2 +#define M98090_LJ_S_WIDTH 1 +#define M98090_I2S_M_MASK BIT(1) +#define M98090_I2S_M_SHIFT 1 +#define M98090_I2S_M_WIDTH 1 +#define M98090_I2S_S_MASK BIT(0) +#define M98090_I2S_S_SHIFT 0 +#define M98090_I2S_S_WIDTH 1 +#define M98090_DAI_ALL_SHIFT 0 +#define M98090_DAI_ALL_WIDTH 8 +#define M98090_DAI_ALL_NUM BIT(M98090_DAI_ALL_WIDTH) + +/* + * M98090_REG_DAC_PATH 0x07 + */ +#define M98090_DIG2_HP_MASK BIT(7) +#define M98090_DIG2_HP_SHIFT 7 +#define M98090_DIG2_HP_WIDTH 1 +#define M98090_DIG2_EAR_MASK BIT(6) +#define M98090_DIG2_EAR_SHIFT 6 +#define M98090_DIG2_EAR_WIDTH 1 +#define M98090_DIG2_SPK_MASK BIT(5) +#define M98090_DIG2_SPK_SHIFT 5 +#define M98090_DIG2_SPK_WIDTH 1 +#define M98090_DIG2_LOUT_MASK BIT(4) +#define M98090_DIG2_LOUT_SHIFT 4 +#define M98090_DIG2_LOUT_WIDTH 1 +#define M98090_DIG2_ALL_SHIFT 0 +#define M98090_DIG2_ALL_WIDTH 8 +#define M98090_DIG2_ALL_NUM BIT(M98090_DIG2_ALL_WIDTH) + +/* + * M98090_REG_MIC_BIAS_VOLTAGE 0x12 + */ +#define M98090_MBVSEL_MASK (3 << 0) +#define M98090_MBVSEL_SHIFT 0 +#define M98090_MBVSEL_WIDTH 2 +#define M98090_MBVSEL_2V8 (3 << 0) +#define M98090_MBVSEL_2V55 (2 << 0) +#define M98090_MBVSEL_2V4 BIT(0) +#define M98090_MBVSEL_2V2 (0 << 0) + +/* + * M98090_REG_DIGITAL_MIC_ENABLE 0x13 + */ +#define M98090_MICCLK_MASK (7 << 4) +#define M98090_MICCLK_SHIFT 4 +#define M98090_MICCLK_WIDTH 3 +#define M98090_DIGMIC4_MASK BIT(3) +#define M98090_DIGMIC4_SHIFT 3 +#define M98090_DIGMIC4_WIDTH 1 +#define M98090_DIGMIC4_NUM BIT(M98090_DIGMIC4_WIDTH) +#define M98090_DIGMIC3_MASK BIT(2) +#define M98090_DIGMIC3_SHIFT 2 +#define M98090_DIGMIC3_WIDTH 1 +#define M98090_DIGMIC3_NUM BIT(M98090_DIGMIC3_WIDTH) +#define M98090_DIGMICR_MASK BIT(1) +#define M98090_DIGMICR_SHIFT 1 +#define M98090_DIGMICR_WIDTH 1 +#define M98090_DIGMICR_NUM BIT(M98090_DIGMICR_WIDTH) +#define M98090_DIGMICL_MASK BIT(0) +#define M98090_DIGMICL_SHIFT 0 +#define M98090_DIGMICL_WIDTH 1 +#define M98090_DIGMICL_NUM BIT(M98090_DIGMICL_WIDTH) + +/* + * M98090_REG_DIGITAL_MIC_CONFIG 0x14 + */ +#define M98090_DMIC_COMP_MASK (15 << 4) +#define M98090_DMIC_COMP_SHIFT 4 +#define M98090_DMIC_COMP_WIDTH 4 +#define M98090_DMIC_COMP_NUM BIT(M98090_DMIC_COMP_WIDTH) +#define M98090_DMIC_FREQ_MASK (3 << 0) +#define M98090_DMIC_FREQ_SHIFT 0 +#define M98090_DMIC_FREQ_WIDTH 2 + +/* + * M98090_REG_CLOCK_MODE 0x1B + */ +#define M98090_PSCLK_MASK (3 << 4) +#define M98090_PSCLK_SHIFT 4 +#define M98090_PSCLK_WIDTH 2 +#define M98090_PSCLK_DISABLED (0 << 4) +#define M98090_PSCLK_DIV1 BIT(4) +#define M98090_PSCLK_DIV2 (2 << 4) +#define M98090_PSCLK_DIV4 (3 << 4) + +/* + * M98090_REG_INTERFACE_FORMAT 0x22 + */ +#define M98090_RJ_MASK BIT(5) +#define M98090_RJ_SHIFT 5 +#define M98090_RJ_WIDTH 1 +#define M98090_WCI_MASK BIT(4) +#define M98090_WCI_SHIFT 4 +#define M98090_WCI_WIDTH 1 +#define M98090_BCI_MASK BIT(3) +#define M98090_BCI_SHIFT 3 +#define M98090_BCI_WIDTH 1 +#define M98090_DLY_MASK BIT(2) +#define M98090_DLY_SHIFT 2 +#define M98090_DLY_WIDTH 1 +#define M98090_WS_MASK (3 << 0) +#define M98090_WS_SHIFT 0 +#define M98090_WS_WIDTH 2 +#define M98090_WS_NUM BIT(M98090_WS_WIDTH) + +/* M98090_REG_IO_CONFIGURATION 0x25 */ +#define M98090_LTEN_MASK BIT(5) +#define M98090_LTEN_SHIFT 5 +#define M98090_LTEN_WIDTH 1 +#define M98090_LTEN_NUM BIT(M98090_LTEN_WIDTH) +#define M98090_LBEN_MASK BIT(4) +#define M98090_LBEN_SHIFT 4 +#define M98090_LBEN_WIDTH 1 +#define M98090_LBEN_NUM BIT(M98090_LBEN_WIDTH) +#define M98090_DMONO_MASK BIT(3) +#define M98090_DMONO_SHIFT 3 +#define M98090_DMONO_WIDTH 1 +#define M98090_DMONO_NUM BIT(M98090_DMONO_WIDTH) +#define M98090_HIZOFF_MASK BIT(2) +#define M98090_HIZOFF_SHIFT 2 +#define M98090_HIZOFF_WIDTH 1 +#define M98090_HIZOFF_NUM BIT(M98090_HIZOFF_WIDTH) +#define M98090_SDOEN_MASK BIT(1) +#define M98090_SDOEN_SHIFT 1 +#define M98090_SDOEN_WIDTH 1 +#define M98090_SDOEN_NUM BIT(M98090_SDOEN_WIDTH) +#define M98090_SDIEN_MASK BIT(0) +#define M98090_SDIEN_SHIFT 0 +#define M98090_SDIEN_WIDTH 1 +#define M98090_SDIEN_NUM BIT(M98090_SDIEN_WIDTH) + +/* + * M98090_REG_FILTER_CONFIG 0x26 + */ +#define M98090_MODE_MASK BIT(7) +#define M98090_MODE_SHIFT 7 +#define M98090_MODE_WIDTH 1 +#define M98090_AHPF_MASK BIT(6) +#define M98090_AHPF_SHIFT 6 +#define M98090_AHPF_WIDTH 1 +#define M98090_AHPF_NUM BIT(M98090_AHPF_WIDTH) +#define M98090_DHPF_MASK BIT(5) +#define M98090_DHPF_SHIFT 5 +#define M98090_DHPF_WIDTH 1 +#define M98090_DHPF_NUM BIT(M98090_DHPF_WIDTH) +#define M98090_DHF_MASK BIT(4) +#define M98090_DHF_SHIFT 4 +#define M98090_DHF_WIDTH 1 +#define M98090_FLT_DMIC34MODE_MASK BIT(3) +#define M98090_FLT_DMIC34MODE_SHIFT 3 +#define M98090_FLT_DMIC34MODE_WIDTH 1 +#define M98090_FLT_DMIC34HPF_MASK BIT(2) +#define M98090_FLT_DMIC34HPF_SHIFT 2 +#define M98090_FLT_DMIC34HPF_WIDTH 1 +#define M98090_FLT_DMIC34HPF_NUM BIT(M98090_FLT_DMIC34HPF_WIDTH) + +/* + * M98090_REG_CLOCK_MODE + */ +#define M98090_FREQ_MASK (15 << 4) +#define M98090_FREQ_SHIFT 4 +#define M98090_FREQ_WIDTH 4 +#define M98090_USE_M1_MASK BIT(0) +#define M98090_USE_M1_SHIFT 0 +#define M98090_USE_M1_WIDTH 1 +#define M98090_USE_M1_NUM BIT(M98090_USE_M1_WIDTH) + +/* + * M98090_REG_LEFT_HP_MIXER 0x29 + */ +#define M98090_MIXHPL_MIC2_MASK BIT(5) +#define M98090_MIXHPL_MIC2_SHIFT 5 +#define M98090_MIXHPL_MIC2_WIDTH 1 +#define M98090_MIXHPL_MIC1_MASK BIT(4) +#define M98090_MIXHPL_MIC1_SHIFT 4 +#define M98090_MIXHPL_MIC1_WIDTH 1 +#define M98090_MIXHPL_LINEB_MASK BIT(3) +#define M98090_MIXHPL_LINEB_SHIFT 3 +#define M98090_MIXHPL_LINEB_WIDTH 1 +#define M98090_MIXHPL_LINEA_MASK BIT(2) +#define M98090_MIXHPL_LINEA_SHIFT 2 +#define M98090_MIXHPL_LINEA_WIDTH 1 +#define M98090_MIXHPL_DACR_MASK BIT(1) +#define M98090_MIXHPL_DACR_SHIFT 1 +#define M98090_MIXHPL_DACR_WIDTH 1 +#define M98090_MIXHPL_DACL_MASK BIT(0) +#define M98090_MIXHPL_DACL_SHIFT 0 +#define M98090_MIXHPL_DACL_WIDTH 1 +#define M98090_MIXHPL_MASK (63 << 0) +#define M98090_MIXHPL_SHIFT 0 +#define M98090_MIXHPL_WIDTH 6 + +/* + * M98090_REG_RIGHT_HP_MIXER 0x2A + */ +#define M98090_MIXHPR_MIC2_MASK BIT(5) +#define M98090_MIXHPR_MIC2_SHIFT 5 +#define M98090_MIXHPR_MIC2_WIDTH 1 +#define M98090_MIXHPR_MIC1_MASK BIT(4) +#define M98090_MIXHPR_MIC1_SHIFT 4 +#define M98090_MIXHPR_MIC1_WIDTH 1 +#define M98090_MIXHPR_LINEB_MASK BIT(3) +#define M98090_MIXHPR_LINEB_SHIFT 3 +#define M98090_MIXHPR_LINEB_WIDTH 1 +#define M98090_MIXHPR_LINEA_MASK BIT(2) +#define M98090_MIXHPR_LINEA_SHIFT 2 +#define M98090_MIXHPR_LINEA_WIDTH 1 +#define M98090_MIXHPR_DACR_MASK BIT(1) +#define M98090_MIXHPR_DACR_SHIFT 1 +#define M98090_MIXHPR_DACR_WIDTH 1 +#define M98090_MIXHPR_DACL_MASK BIT(0) +#define M98090_MIXHPR_DACL_SHIFT 0 +#define M98090_MIXHPR_DACL_WIDTH 1 +#define M98090_MIXHPR_MASK (63 << 0) +#define M98090_MIXHPR_SHIFT 0 +#define M98090_MIXHPR_WIDTH 6 + +/* + * M98090_REG_LEFT_HP_VOLUME 0x2C + */ +#define M98090_HPLM_MASK BIT(7) +#define M98090_HPLM_SHIFT 7 +#define M98090_HPLM_WIDTH 1 +#define M98090_HPVOLL_MASK (31 << 0) +#define M98090_HPVOLL_SHIFT 0 +#define M98090_HPVOLL_WIDTH 5 +#define M98090_HPVOLL_NUM BIT(M98090_HPVOLL_WIDTH) + +/* + * M98090_REG_RIGHT_HP_VOLUME 0x2D + */ +#define M98090_HPRM_MASK BIT(7) +#define M98090_HPRM_SHIFT 7 +#define M98090_HPRM_WIDTH 1 +#define M98090_HPVOLR_MASK (31 << 0) +#define M98090_HPVOLR_SHIFT 0 +#define M98090_HPVOLR_WIDTH 5 +#define M98090_HPVOLR_NUM BIT(M98090_HPVOLR_WIDTH) + +/* + * M98090_REG_LEFT_SPK_MIXER 0x2E + */ +#define M98090_MIXSPL_MIC2_MASK BIT(5) +#define M98090_MIXSPL_MIC2_SHIFT 5 +#define M98090_MIXSPL_MIC2_WIDTH 1 +#define M98090_MIXSPL_MIC1_MASK BIT(4) +#define M98090_MIXSPL_MIC1_SHIFT 4 +#define M98090_MIXSPL_MIC1_WIDTH 1 +#define M98090_MIXSPL_LINEB_MASK BIT(3) +#define M98090_MIXSPL_LINEB_SHIFT 3 +#define M98090_MIXSPL_LINEB_WIDTH 1 +#define M98090_MIXSPL_LINEA_MASK BIT(2) +#define M98090_MIXSPL_LINEA_SHIFT 2 +#define M98090_MIXSPL_LINEA_WIDTH 1 +#define M98090_MIXSPL_DACR_MASK BIT(1) +#define M98090_MIXSPL_DACR_SHIFT 1 +#define M98090_MIXSPL_DACR_WIDTH 1 +#define M98090_MIXSPL_DACL_MASK BIT(0) +#define M98090_MIXSPL_DACL_SHIFT 0 +#define M98090_MIXSPL_DACL_WIDTH 1 +#define M98090_MIXSPL_MASK (63 << 0) +#define M98090_MIXSPL_SHIFT 0 +#define M98090_MIXSPL_WIDTH 6 +#define M98090_MIXSPR_DACR_MASK BIT(1) +#define M98090_MIXSPR_DACR_SHIFT 1 +#define M98090_MIXSPR_DACR_WIDTH 1 + +/* + * M98090_REG_RIGHT_SPK_MIXER 0x2F + */ +#define M98090_SPK_SLAVE_MASK BIT(6) +#define M98090_SPK_SLAVE_SHIFT 6 +#define M98090_SPK_SLAVE_WIDTH 1 +#define M98090_MIXSPR_MIC2_MASK BIT(5) +#define M98090_MIXSPR_MIC2_SHIFT 5 +#define M98090_MIXSPR_MIC2_WIDTH 1 +#define M98090_MIXSPR_MIC1_MASK BIT(4) +#define M98090_MIXSPR_MIC1_SHIFT 4 +#define M98090_MIXSPR_MIC1_WIDTH 1 +#define M98090_MIXSPR_LINEB_MASK BIT(3) +#define M98090_MIXSPR_LINEB_SHIFT 3 +#define M98090_MIXSPR_LINEB_WIDTH 1 +#define M98090_MIXSPR_LINEA_MASK BIT(2) +#define M98090_MIXSPR_LINEA_SHIFT 2 +#define M98090_MIXSPR_LINEA_WIDTH 1 +#define M98090_MIXSPR_DACR_MASK BIT(1) +#define M98090_MIXSPR_DACR_SHIFT 1 +#define M98090_MIXSPR_DACR_WIDTH 1 +#define M98090_MIXSPR_DACL_MASK BIT(0) +#define M98090_MIXSPR_DACL_SHIFT 0 +#define M98090_MIXSPR_DACL_WIDTH 1 +#define M98090_MIXSPR_MASK (63 << 0) +#define M98090_MIXSPR_SHIFT 0 +#define M98090_MIXSPR_WIDTH 6 + +/* + * M98090_REG_LEFT_SPK_VOLUME 0x31 + */ +#define M98090_SPLM_MASK BIT(7) +#define M98090_SPLM_SHIFT 7 +#define M98090_SPLM_WIDTH 1 +#define M98090_SPVOLL_MASK (63 << 0) +#define M98090_SPVOLL_SHIFT 0 +#define M98090_SPVOLL_WIDTH 6 +#define M98090_SPVOLL_NUM 40 + +/* + * M98090_REG_RIGHT_SPK_VOLUME 0x32 + */ +#define M98090_SPRM_MASK BIT(7) +#define M98090_SPRM_SHIFT 7 +#define M98090_SPRM_WIDTH 1 +#define M98090_SPVOLR_MASK (63 << 0) +#define M98090_SPVOLR_SHIFT 0 +#define M98090_SPVOLR_WIDTH 6 +#define M98090_SPVOLR_NUM 40 + +/* + * M98090_REG_RCV_LOUTL_MIXER 0x37 + */ +#define M98090_MIXRCVL_MIC2_MASK BIT(5) +#define M98090_MIXRCVL_MIC2_SHIFT 5 +#define M98090_MIXRCVL_MIC2_WIDTH 1 +#define M98090_MIXRCVL_MIC1_MASK BIT(4) +#define M98090_MIXRCVL_MIC1_SHIFT 4 +#define M98090_MIXRCVL_MIC1_WIDTH 1 +#define M98090_MIXRCVL_LINEB_MASK BIT(3) +#define M98090_MIXRCVL_LINEB_SHIFT 3 +#define M98090_MIXRCVL_LINEB_WIDTH 1 +#define M98090_MIXRCVL_LINEA_MASK BIT(2) +#define M98090_MIXRCVL_LINEA_SHIFT 2 +#define M98090_MIXRCVL_LINEA_WIDTH 1 +#define M98090_MIXRCVL_DACR_MASK BIT(1) +#define M98090_MIXRCVL_DACR_SHIFT 1 +#define M98090_MIXRCVL_DACR_WIDTH 1 +#define M98090_MIXRCVL_DACL_MASK BIT(0) +#define M98090_MIXRCVL_DACL_SHIFT 0 +#define M98090_MIXRCVL_DACL_WIDTH 1 +#define M98090_MIXRCVL_MASK (63 << 0) +#define M98090_MIXRCVL_SHIFT 0 +#define M98090_MIXRCVL_WIDTH 6 + +/* + * M98090_REG_RCV_LOUTL_CONTROL 0x38 + */ +#define M98090_MIXRCVLG_MASK (3 << 0) +#define M98090_MIXRCVLG_SHIFT 0 +#define M98090_MIXRCVLG_WIDTH 2 +#define M98090_MIXRCVLG_NUM BIT(M98090_MIXRCVLG_WIDTH) + +/* + * M98090_REG_RCV_LOUTL_VOLUME 0x39 + */ +#define M98090_RCVLM_MASK BIT(7) +#define M98090_RCVLM_SHIFT 7 +#define M98090_RCVLM_WIDTH 1 +#define M98090_RCVLVOL_MASK (31 << 0) +#define M98090_RCVLVOL_SHIFT 0 +#define M98090_RCVLVOL_WIDTH 5 +#define M98090_RCVLVOL_NUM BIT(M98090_RCVLVOL_WIDTH) + +/* + * M98090_REG_LOUTR_MIXER 0x3A + */ +#define M98090_LINMOD_MASK BIT(7) +#define M98090_LINMOD_SHIFT 7 +#define M98090_LINMOD_WIDTH 1 +#define M98090_MIXRCVR_MIC2_MASK BIT(5) +#define M98090_MIXRCVR_MIC2_SHIFT 5 +#define M98090_MIXRCVR_MIC2_WIDTH 1 +#define M98090_MIXRCVR_MIC1_MASK BIT(4) +#define M98090_MIXRCVR_MIC1_SHIFT 4 +#define M98090_MIXRCVR_MIC1_WIDTH 1 +#define M98090_MIXRCVR_LINEB_MASK BIT(3) +#define M98090_MIXRCVR_LINEB_SHIFT 3 +#define M98090_MIXRCVR_LINEB_WIDTH 1 +#define M98090_MIXRCVR_LINEA_MASK BIT(2) +#define M98090_MIXRCVR_LINEA_SHIFT 2 +#define M98090_MIXRCVR_LINEA_WIDTH 1 +#define M98090_MIXRCVR_DACR_MASK BIT(1) +#define M98090_MIXRCVR_DACR_SHIFT 1 +#define M98090_MIXRCVR_DACR_WIDTH 1 +#define M98090_MIXRCVR_DACL_MASK BIT(0) +#define M98090_MIXRCVR_DACL_SHIFT 0 +#define M98090_MIXRCVR_DACL_WIDTH 1 +#define M98090_MIXRCVR_MASK (63 << 0) +#define M98090_MIXRCVR_SHIFT 0 +#define M98090_MIXRCVR_WIDTH 6 + +/* + * M98090_REG_LOUTR_VOLUME 0x3C + */ +#define M98090_RCVRM_MASK BIT(7) +#define M98090_RCVRM_SHIFT 7 +#define M98090_RCVRM_WIDTH 1 +#define M98090_RCVRVOL_MASK (31 << 0) +#define M98090_RCVRVOL_SHIFT 0 +#define M98090_RCVRVOL_WIDTH 5 +#define M98090_RCVRVOL_NUM BIT(M98090_RCVRVOL_WIDTH) + +/* + * M98090_REG_JACK_DETECT 0x3D + */ +#define M98090_JDETEN_MASK BIT(7) +#define M98090_JDETEN_SHIFT 7 +#define M98090_JDETEN_WIDTH 1 +#define M98090_JDWK_MASK BIT(6) +#define M98090_JDWK_SHIFT 6 +#define M98090_JDWK_WIDTH 1 +#define M98090_JDEB_MASK (3 << 0) +#define M98090_JDEB_SHIFT 0 +#define M98090_JDEB_WIDTH 2 +#define M98090_JDEB_25MS (0 << 0) +#define M98090_JDEB_50MS BIT(0) +#define M98090_JDEB_100MS (2 << 0) +#define M98090_JDEB_200MS (3 << 0) + +/* + * M98090_REG_INPUT_ENABLE 0x3E + */ +#define M98090_MBEN_MASK BIT(4) +#define M98090_MBEN_SHIFT 4 +#define M98090_MBEN_WIDTH 1 +#define M98090_LINEAEN_MASK BIT(3) +#define M98090_LINEAEN_SHIFT 3 +#define M98090_LINEAEN_WIDTH 1 +#define M98090_LINEBEN_MASK BIT(2) +#define M98090_LINEBEN_SHIFT 2 +#define M98090_LINEBEN_WIDTH 1 +#define M98090_ADREN_MASK BIT(1) +#define M98090_ADREN_SHIFT 1 +#define M98090_ADREN_WIDTH 1 +#define M98090_ADLEN_MASK BIT(0) +#define M98090_ADLEN_SHIFT 0 +#define M98090_ADLEN_WIDTH 1 + +/* + * M98090_REG_OUTPUT_ENABLE 0x3F + */ +#define M98090_HPREN_MASK BIT(7) +#define M98090_HPREN_SHIFT 7 +#define M98090_HPREN_WIDTH 1 +#define M98090_HPLEN_MASK BIT(6) +#define M98090_HPLEN_SHIFT 6 +#define M98090_HPLEN_WIDTH 1 +#define M98090_SPREN_MASK BIT(5) +#define M98090_SPREN_SHIFT 5 +#define M98090_SPREN_WIDTH 1 +#define M98090_SPLEN_MASK BIT(4) +#define M98090_SPLEN_SHIFT 4 +#define M98090_SPLEN_WIDTH 1 +#define M98090_RCVLEN_MASK BIT(3) +#define M98090_RCVLEN_SHIFT 3 +#define M98090_RCVLEN_WIDTH 1 +#define M98090_RCVREN_MASK BIT(2) +#define M98090_RCVREN_SHIFT 2 +#define M98090_RCVREN_WIDTH 1 +#define M98090_DAREN_MASK BIT(1) +#define M98090_DAREN_SHIFT 1 +#define M98090_DAREN_WIDTH 1 +#define M98090_DALEN_MASK BIT(0) +#define M98090_DALEN_SHIFT 0 +#define M98090_DALEN_WIDTH 1 + +/* + * M98090_REG_LEVEL_CONTROL 0x40 + */ +#define M98090_ZDENN_MASK BIT(2) +#define M98090_ZDENN_SHIFT 2 +#define M98090_ZDENN_WIDTH 1 +#define M98090_ZDENN_NUM BIT(M98090_ZDENN_WIDTH) +#define M98090_VS2ENN_MASK BIT(1) +#define M98090_VS2ENN_SHIFT 1 +#define M98090_VS2ENN_WIDTH 1 +#define M98090_VS2ENN_NUM BIT(M98090_VS2ENN_WIDTH) +#define M98090_VSENN_MASK BIT(0) +#define M98090_VSENN_SHIFT 0 +#define M98090_VSENN_WIDTH 1 +#define M98090_VSENN_NUM BIT(M98090_VSENN_WIDTH) + +/* + * M98090_REG_BIAS_CONTROL 0x42 + */ +#define M98090_VCM_MODE_MASK BIT(0) +#define M98090_VCM_MODE_SHIFT 0 +#define M98090_VCM_MODE_WIDTH 1 +#define M98090_VCM_MODE_NUM BIT(M98090_VCM_MODE_WIDTH) + +/* + * M98090_REG_DAC_CONTROL 0x43 + */ +#define M98090_PERFMODE_MASK BIT(1) +#define M98090_PERFMODE_SHIFT 1 +#define M98090_PERFMODE_WIDTH 1 +#define M98090_PERFMODE_NUM BIT(M98090_PERFMODE_WIDTH) +#define M98090_DACHP_MASK BIT(0) +#define M98090_DACHP_SHIFT 0 +#define M98090_DACHP_WIDTH 1 +#define M98090_DACHP_NUM BIT(M98090_DACHP_WIDTH) + +/* + * M98090_REG_ADC_CONTROL 0x44 + */ +#define M98090_OSR128_MASK BIT(2) +#define M98090_OSR128_SHIFT 2 +#define M98090_OSR128_WIDTH 1 +#define M98090_ADCDITHER_MASK BIT(1) +#define M98090_ADCDITHER_SHIFT 1 +#define M98090_ADCDITHER_WIDTH 1 +#define M98090_ADCDITHER_NUM BIT(M98090_ADCDITHER_WIDTH) +#define M98090_ADCHP_MASK BIT(0) +#define M98090_ADCHP_SHIFT 0 +#define M98090_ADCHP_WIDTH 1 +#define M98090_ADCHP_NUM BIT(M98090_ADCHP_WIDTH) + +/* + * M98090_REG_DEVICE_SHUTDOWN 0x45 + */ +#define M98090_SHDNN_MASK BIT(7) +#define M98090_SHDNN_SHIFT 7 +#define M98090_SHDNN_WIDTH 1 + +/* + * M98090_REG_REVISION_ID 0xFF + */ +#define M98090_REVID_MASK (255 << 0) +#define M98090_REVID_SHIFT 0 +#define M98090_REVID_WIDTH 8 +#define M98090_REVID_NUM BIT(M98090_REVID_WIDTH) + +/* function prototype */ + +/* + * initialise max98090 sound codec device for the given configuration + * + * @param blob FDT node for codec values + * @param sampling_rate Sampling rate (Hz) + * @param mclk_freq MCLK Frequency (Hz) + * @param bits_per_sample bits per Sample (must be 16 or 24) + * + * @returns -1 for error and 0 Success. + */ +int max98090_init(const void *blob, int sampling_rate, int mclk_freq, + int bits_per_sample); +int max98090_set_sysclk(struct maxim_priv *max98090, uint freq); +int max98090_hw_params(struct maxim_priv *max98090, uint rate, + uint bits_per_sample); +int max98090_device_init(struct maxim_priv *max98090); +int max98090_set_fmt(struct maxim_priv *max98090, int fmt); +#endif diff --git a/drivers/sound/max98095.c b/drivers/sound/max98095.c index 7c37bd0701..99c0e996b4 100644 --- a/drivers/sound/max98095.c +++ b/drivers/sound/max98095.c @@ -1,113 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * max98095.c -- MAX98095 ALSA SoC Audio driver * * Copyright 2011 Maxim Integrated Products * - * Modified for uboot by R. Chandrasekar (rcsekar@samsung.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. + * Modified for U-Boot by R. Chandrasekar (rcsekar@samsung.com) */ #include <common.h> -#include <asm/arch/clk.h> -#include <asm/arch/cpu.h> -#include <asm/arch/power.h> -#include <asm/gpio.h> -#include <asm/io.h> -#include <common.h> +#include <audio_codec.h> +#include <dm.h> #include <div64.h> #include <fdtdec.h> #include <i2c.h> #include <sound.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <asm/arch/clk.h> +#include <asm/arch/cpu.h> +#include <asm/arch/power.h> #include "i2s.h" #include "max98095.h" -enum max98095_type { - MAX98095, -}; - -struct max98095_priv { - enum max98095_type devtype; - unsigned int sysclk; - unsigned int rate; - unsigned int fmt; -}; - -static struct sound_codec_info g_codec_info; -struct max98095_priv g_max98095_info; -unsigned int g_max98095_i2c_dev_addr; - /* Index 0 is reserved. */ int rate_table[] = {0, 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 88200, 96000}; /* - * Writes value to a device register through i2c - * - * @param reg reg number to be write - * @param data data to be writen to the above registor - * - * @return int value 1 for change, 0 for no change or negative error code. - */ -static int max98095_i2c_write(unsigned int reg, unsigned char data) -{ - debug("%s: Write Addr : 0x%02X, Data : 0x%02X\n", - __func__, reg, data); - return i2c_write(g_max98095_i2c_dev_addr, reg, 1, &data, 1); -} - -/* - * Read a value from a device register through i2c - * - * @param reg reg number to be read - * @param data address of read data to be stored - * - * @return int value 0 for success, -1 in case of error. - */ -static unsigned int max98095_i2c_read(unsigned int reg, unsigned char *data) -{ - int ret; - - ret = i2c_read(g_max98095_i2c_dev_addr, reg, 1, data, 1); - if (ret != 0) { - debug("%s: Error while reading register %#04x\n", - __func__, reg); - return -1; - } - - return 0; -} - -/* - * update device register bits through i2c - * - * @param reg codec register - * @param mask register mask - * @param value new value - * - * @return int value 0 for success, non-zero error code. - */ -static int max98095_update_bits(unsigned int reg, unsigned char mask, - unsigned char value) -{ - int change, ret = 0; - unsigned char old, new; - - if (max98095_i2c_read(reg, &old) != 0) - return -1; - new = (old & ~mask) | (value & mask); - change = (old != new) ? 1 : 0; - if (change) - ret = max98095_i2c_write(reg, new); - if (ret < 0) - return ret; - - return change; -} - -/* * codec mclk clock divider coefficients based on sampling rate * * @param rate sampling rate @@ -127,19 +46,19 @@ static int rate_value(int rate, u8 *value) } *value = 1; - return -1; + return -EINVAL; } /* * Sets hw params for max98095 * - * @param max98095 max98095 information pointer + * @param priv max98095 information pointer * @param rate Sampling rate * @param bits_per_sample Bits per sample * - * @return -1 for error and 0 Success. + * @return 0 for success or negative error code. */ -static int max98095_hw_params(struct max98095_priv *max98095, +static int max98095_hw_params(struct maxim_priv *priv, enum en_max_audio_interface aif_id, unsigned int rate, unsigned int bits_per_sample) { @@ -161,40 +80,39 @@ static int max98095_hw_params(struct max98095_priv *max98095, switch (bits_per_sample) { case 16: - error = max98095_update_bits(M98095_DAI_FORMAT, - M98095_DAI_WS, 0); + error = maxim_bic_or(priv, M98095_DAI_FORMAT, M98095_DAI_WS, 0); break; case 24: - error = max98095_update_bits(M98095_DAI_FORMAT, - M98095_DAI_WS, M98095_DAI_WS); + error = maxim_bic_or(priv, M98095_DAI_FORMAT, M98095_DAI_WS, + M98095_DAI_WS); break; default: debug("%s: Illegal bits per sample %d.\n", __func__, bits_per_sample); - return -1; + return -EINVAL; } if (rate_value(rate, ®val)) { debug("%s: Failed to set sample rate to %d.\n", __func__, rate); - return -1; + return -EINVAL; } - max98095->rate = rate; + priv->rate = rate; - error |= max98095_update_bits(M98095_DAI_CLKMODE, - M98095_CLKMODE_MASK, regval); + error |= maxim_bic_or(priv, M98095_DAI_CLKMODE, M98095_CLKMODE_MASK, + regval); /* Update sample rate mode */ if (rate < 50000) - error |= max98095_update_bits(M98095_DAI_FILTERS, - M98095_DAI_DHF, 0); + error |= maxim_bic_or(priv, M98095_DAI_FILTERS, + M98095_DAI_DHF, 0); else - error |= max98095_update_bits(M98095_DAI_FILTERS, - M98095_DAI_DHF, M98095_DAI_DHF); + error |= maxim_bic_or(priv, M98095_DAI_FILTERS, + M98095_DAI_DHF, M98095_DAI_DHF); if (error < 0) { debug("%s: Error setting hardware params.\n", __func__); - return -1; + return -EIO; } return 0; @@ -203,18 +121,17 @@ static int max98095_hw_params(struct max98095_priv *max98095, /* * Configures Audio interface system clock for the given frequency * - * @param max98095 max98095 information + * @param priv max98095 information * @param freq Sampling frequency in Hz * - * @return -1 for error and 0 success. + * @return 0 for success or negative error code. */ -static int max98095_set_sysclk(struct max98095_priv *max98095, - unsigned int freq) +static int max98095_set_sysclk(struct maxim_priv *priv, unsigned int freq) { int error = 0; /* Requested clock frequency is already setup */ - if (freq == max98095->sysclk) + if (freq == priv->sysclk) return 0; /* Setup clocks for slave mode, and using the PLL @@ -223,35 +140,35 @@ static int max98095_set_sysclk(struct max98095_priv *max98095, * 0x03 (when master clk is 40MHz to 60MHz).. */ if ((freq >= 10000000) && (freq < 20000000)) { - error = max98095_i2c_write(M98095_026_SYS_CLK, 0x10); + error = maxim_i2c_write(priv, M98095_026_SYS_CLK, 0x10); } else if ((freq >= 20000000) && (freq < 40000000)) { - error = max98095_i2c_write(M98095_026_SYS_CLK, 0x20); + error = maxim_i2c_write(priv, M98095_026_SYS_CLK, 0x20); } else if ((freq >= 40000000) && (freq < 60000000)) { - error = max98095_i2c_write(M98095_026_SYS_CLK, 0x30); + error = maxim_i2c_write(priv, M98095_026_SYS_CLK, 0x30); } else { debug("%s: Invalid master clock frequency\n", __func__); - return -1; + return -EINVAL; } debug("%s: Clock at %uHz\n", __func__, freq); if (error < 0) - return -1; + return -EIO; - max98095->sysclk = freq; + priv->sysclk = freq; return 0; } /* * Sets Max98095 I2S format * - * @param max98095 max98095 information + * @param priv max98095 information * @param fmt i2S format - supports a subset of the options defined * in i2s.h. * - * @return -1 for error and 0 Success. + * @return 0 for success or negative error code. */ -static int max98095_set_fmt(struct max98095_priv *max98095, int fmt, +static int max98095_set_fmt(struct maxim_priv *priv, int fmt, enum en_max_audio_interface aif_id) { u8 regval = 0; @@ -261,10 +178,10 @@ static int max98095_set_fmt(struct max98095_priv *max98095, int fmt, unsigned short M98095_DAI_FORMAT; unsigned short M98095_DAI_CLOCK; - if (fmt == max98095->fmt) + if (fmt == priv->fmt) return 0; - max98095->fmt = fmt; + priv->fmt = fmt; if (aif_id == AIF1) { M98095_DAI_CLKCFG_HI = M98095_028_DAI1_CLKCFG_HI; @@ -281,10 +198,8 @@ static int max98095_set_fmt(struct max98095_priv *max98095, int fmt, switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: /* Slave mode PLL */ - error |= max98095_i2c_write(M98095_DAI_CLKCFG_HI, - 0x80); - error |= max98095_i2c_write(M98095_DAI_CLKCFG_LO, - 0x00); + error |= maxim_i2c_write(priv, M98095_DAI_CLKCFG_HI, 0x80); + error |= maxim_i2c_write(priv, M98095_DAI_CLKCFG_LO, 0x00); break; case SND_SOC_DAIFMT_CBM_CFM: /* Set to master mode */ @@ -294,7 +209,7 @@ static int max98095_set_fmt(struct max98095_priv *max98095, int fmt, case SND_SOC_DAIFMT_CBM_CFS: default: debug("%s: Clock mode unsupported\n", __func__); - return -1; + return -EINVAL; } switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { @@ -305,7 +220,7 @@ static int max98095_set_fmt(struct max98095_priv *max98095, int fmt, break; default: debug("%s: Unrecognized format.\n", __func__); - return -1; + return -EINVAL; } switch (fmt & SND_SOC_DAIFMT_INV_MASK) { @@ -322,20 +237,18 @@ static int max98095_set_fmt(struct max98095_priv *max98095, int fmt, break; default: debug("%s: Unrecognized inversion settings.\n", __func__); - return -1; + return -EINVAL; } - error |= max98095_update_bits(M98095_DAI_FORMAT, - M98095_DAI_MAS | M98095_DAI_DLY | - M98095_DAI_BCI | M98095_DAI_WCI, - regval); + error |= maxim_bic_or(priv, M98095_DAI_FORMAT, + M98095_DAI_MAS | M98095_DAI_DLY | + M98095_DAI_BCI | M98095_DAI_WCI, regval); - error |= max98095_i2c_write(M98095_DAI_CLOCK, - M98095_DAI_BSEL64); + error |= maxim_i2c_write(priv, M98095_DAI_CLOCK, M98095_DAI_BSEL64); if (error < 0) { debug("%s: Error setting i2s format.\n", __func__); - return -1; + return -EIO; } return 0; @@ -344,9 +257,10 @@ static int max98095_set_fmt(struct max98095_priv *max98095, int fmt, /* * resets the audio codec * - * @return -1 for error and 0 success. + * @param priv Private data for driver + * @return 0 for success or negative error code. */ -static int max98095_reset(void) +static int max98095_reset(struct maxim_priv *priv) { int i, ret; @@ -354,13 +268,13 @@ static int max98095_reset(void) * Gracefully reset the DSP core and the codec hardware in a proper * sequence. */ - ret = max98095_i2c_write(M98095_00F_HOST_CFG, 0); + ret = maxim_i2c_write(priv, M98095_00F_HOST_CFG, 0); if (ret != 0) { debug("%s: Failed to reset DSP: %d\n", __func__, ret); return ret; } - ret = max98095_i2c_write(M98095_097_PWR_SYS, 0); + ret = maxim_i2c_write(priv, M98095_097_PWR_SYS, 0); if (ret != 0) { debug("%s: Failed to reset codec: %d\n", __func__, ret); return ret; @@ -371,7 +285,7 @@ static int max98095_reset(void) * reset hardware control register. */ for (i = M98095_010_HOST_INT_CFG; i < M98095_REG_MAX_CACHED; i++) { - ret = max98095_i2c_write(i, 0); + ret = maxim_i2c_write(priv, i, 0); if (ret < 0) { debug("%s: Failed to reset: %d\n", __func__, ret); return ret; @@ -384,132 +298,128 @@ static int max98095_reset(void) /* * Intialise max98095 codec device * - * @param max98095 max98095 information - * - * @returns -1 for error and 0 Success. + * @param priv max98095 information + * @return 0 for success or negative error code. */ -static int max98095_device_init(struct max98095_priv *max98095, - enum en_max_audio_interface aif_id) +static int max98095_device_init(struct maxim_priv *priv) { unsigned char id; - int error = 0; + int ret; + + /* Enable codec clock */ + set_xclkout(); /* reset the codec, the DSP core, and disable all interrupts */ - error = max98095_reset(); - if (error != 0) { + ret = max98095_reset(priv); + if (ret != 0) { debug("Reset\n"); - return error; + return ret; } /* initialize private data */ - max98095->sysclk = -1U; - max98095->rate = -1U; - max98095->fmt = -1U; + priv->sysclk = -1U; + priv->rate = -1U; + priv->fmt = -1U; - error = max98095_i2c_read(M98095_0FF_REV_ID, &id); - if (error < 0) { + ret = maxim_i2c_read(priv, M98095_0FF_REV_ID, &id); + if (ret < 0) { debug("%s: Failure reading hardware revision: %d\n", __func__, id); - goto err_access; + return ret; } debug("%s: Hardware revision: %c\n", __func__, (id - 0x40) + 'A'); - error |= max98095_i2c_write(M98095_097_PWR_SYS, M98095_PWRSV); + return 0; +} + +static int max98095_setup_interface(struct maxim_priv *priv, + enum en_max_audio_interface aif_id) +{ + int error; + + error = maxim_i2c_write(priv, M98095_097_PWR_SYS, M98095_PWRSV); /* * initialize registers to hardware default configuring audio * interface2 to DAC */ if (aif_id == AIF1) - error |= max98095_i2c_write(M98095_048_MIX_DAC_LR, + error |= maxim_i2c_write(priv, M98095_048_MIX_DAC_LR, M98095_DAI1L_TO_DACL | M98095_DAI1R_TO_DACR); else - error |= max98095_i2c_write(M98095_048_MIX_DAC_LR, + error |= maxim_i2c_write(priv, M98095_048_MIX_DAC_LR, M98095_DAI2M_TO_DACL | M98095_DAI2M_TO_DACR); - error |= max98095_i2c_write(M98095_092_PWR_EN_OUT, + error |= maxim_i2c_write(priv, M98095_092_PWR_EN_OUT, M98095_SPK_SPREADSPECTRUM); - error |= max98095_i2c_write(M98095_04E_CFG_HP, M98095_HPNORMAL); + error |= maxim_i2c_write(priv, M98095_04E_CFG_HP, M98095_HPNORMAL); if (aif_id == AIF1) - error |= max98095_i2c_write(M98095_02C_DAI1_IOCFG, + error |= maxim_i2c_write(priv, M98095_02C_DAI1_IOCFG, M98095_S1NORMAL | M98095_SDATA); else - error |= max98095_i2c_write(M98095_036_DAI2_IOCFG, + error |= maxim_i2c_write(priv, M98095_036_DAI2_IOCFG, M98095_S2NORMAL | M98095_SDATA); /* take the codec out of the shut down */ - error |= max98095_update_bits(M98095_097_PWR_SYS, M98095_SHDNRUN, - M98095_SHDNRUN); - /* route DACL and DACR output to HO and Spekers */ - error |= max98095_i2c_write(M98095_050_MIX_SPK_LEFT, 0x01); /* DACL */ - error |= max98095_i2c_write(M98095_051_MIX_SPK_RIGHT, 0x01);/* DACR */ - error |= max98095_i2c_write(M98095_04C_MIX_HP_LEFT, 0x01); /* DACL */ - error |= max98095_i2c_write(M98095_04D_MIX_HP_RIGHT, 0x01); /* DACR */ + error |= maxim_bic_or(priv, M98095_097_PWR_SYS, M98095_SHDNRUN, + M98095_SHDNRUN); + /* + * route DACL and DACR output to HO and Speakers + * Ordering: DACL, DACR, DACL, DACR + */ + error |= maxim_i2c_write(priv, M98095_050_MIX_SPK_LEFT, 0x01); + error |= maxim_i2c_write(priv, M98095_051_MIX_SPK_RIGHT, 0x01); + error |= maxim_i2c_write(priv, M98095_04C_MIX_HP_LEFT, 0x01); + error |= maxim_i2c_write(priv, M98095_04D_MIX_HP_RIGHT, 0x01); /* power Enable */ - error |= max98095_i2c_write(M98095_091_PWR_EN_OUT, 0xF3); + error |= maxim_i2c_write(priv, M98095_091_PWR_EN_OUT, 0xF3); /* set Volume */ - error |= max98095_i2c_write(M98095_064_LVL_HP_L, 15); - error |= max98095_i2c_write(M98095_065_LVL_HP_R, 15); - error |= max98095_i2c_write(M98095_067_LVL_SPK_L, 16); - error |= max98095_i2c_write(M98095_068_LVL_SPK_R, 16); + error |= maxim_i2c_write(priv, M98095_064_LVL_HP_L, 15); + error |= maxim_i2c_write(priv, M98095_065_LVL_HP_R, 15); + error |= maxim_i2c_write(priv, M98095_067_LVL_SPK_L, 16); + error |= maxim_i2c_write(priv, M98095_068_LVL_SPK_R, 16); /* Enable DAIs */ - error |= max98095_i2c_write(M98095_093_BIAS_CTRL, 0x30); + error |= maxim_i2c_write(priv, M98095_093_BIAS_CTRL, 0x30); if (aif_id == AIF1) - error |= max98095_i2c_write(M98095_096_PWR_DAC_CK, 0x01); + error |= maxim_i2c_write(priv, M98095_096_PWR_DAC_CK, 0x01); else - error |= max98095_i2c_write(M98095_096_PWR_DAC_CK, 0x07); + error |= maxim_i2c_write(priv, M98095_096_PWR_DAC_CK, 0x07); -err_access: if (error < 0) - return -1; + return -EIO; return 0; } -static int max98095_do_init(struct sound_codec_info *pcodec_info, +static int max98095_do_init(struct maxim_priv *priv, enum en_max_audio_interface aif_id, int sampling_rate, int mclk_freq, int bits_per_sample) { int ret = 0; - /* Enable codec clock */ - set_xclkout(); - - /* shift the device address by 1 for 7 bit addressing */ - g_max98095_i2c_dev_addr = pcodec_info->i2c_dev_addr >> 1; - - if (pcodec_info->codec_type == CODEC_MAX_98095) { - g_max98095_info.devtype = MAX98095; - } else { - debug("%s: Codec id [%d] not defined\n", __func__, - pcodec_info->codec_type); - return -1; - } - - ret = max98095_device_init(&g_max98095_info, aif_id); + ret = max98095_setup_interface(priv, aif_id); if (ret < 0) { - debug("%s: max98095 codec chip init failed\n", __func__); + debug("%s: max98095 setup interface failed\n", __func__); return ret; } - ret = max98095_set_sysclk(&g_max98095_info, mclk_freq); + ret = max98095_set_sysclk(priv, mclk_freq); if (ret < 0) { debug("%s: max98095 codec set sys clock failed\n", __func__); return ret; } - ret = max98095_hw_params(&g_max98095_info, aif_id, sampling_rate, + ret = max98095_hw_params(priv, aif_id, sampling_rate, bits_per_sample); if (ret == 0) { - ret = max98095_set_fmt(&g_max98095_info, - SND_SOC_DAIFMT_I2S | + ret = max98095_set_fmt(priv, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, aif_id); @@ -518,76 +428,45 @@ static int max98095_do_init(struct sound_codec_info *pcodec_info, return ret; } -static int get_max98095_codec_values(struct sound_codec_info *pcodec_info, - const void *blob) +static int max98095_set_params(struct udevice *dev, int interface, int rate, + int mclk_freq, int bits_per_sample, + uint channels) { - int error = 0; -#if CONFIG_IS_ENABLED(OF_CONTROL) - enum fdt_compat_id compat; - int node; - int parent; - - /* Get the node from FDT for codec */ - node = fdtdec_next_compatible(blob, 0, COMPAT_MAXIM_98095_CODEC); - if (node <= 0) { - debug("EXYNOS_SOUND: No node for codec in device tree\n"); - debug("node = %d\n", node); - return -1; - } - - parent = fdt_parent_offset(blob, node); - if (parent < 0) { - debug("%s: Cannot find node parent\n", __func__); - return -1; - } + struct maxim_priv *priv = dev_get_priv(dev); - compat = fdtdec_lookup(blob, parent); - switch (compat) { - case COMPAT_SAMSUNG_S3C2440_I2C: - pcodec_info->i2c_bus = i2c_get_bus_num_fdt(parent); - error |= pcodec_info->i2c_bus; - debug("i2c bus = %d\n", pcodec_info->i2c_bus); - pcodec_info->i2c_dev_addr = fdtdec_get_int(blob, node, - "reg", 0); - error |= pcodec_info->i2c_dev_addr; - debug("i2c dev addr = %x\n", pcodec_info->i2c_dev_addr); - break; - default: - debug("%s: Unknown compat id %d\n", __func__, compat); - return -1; - } -#else - pcodec_info->i2c_bus = AUDIO_I2C_BUS; - pcodec_info->i2c_dev_addr = AUDIO_I2C_REG; - debug("i2c dev addr = %d\n", pcodec_info->i2c_dev_addr); -#endif - pcodec_info->codec_type = CODEC_MAX_98095; - if (error == -1) { - debug("fail to get max98095 codec node properties\n"); - return -1; - } - - return 0; + return max98095_do_init(priv, interface, rate, mclk_freq, + bits_per_sample); } -/* max98095 Device Initialisation */ -int max98095_init(const void *blob, enum en_max_audio_interface aif_id, - int sampling_rate, int mclk_freq, - int bits_per_sample) +static int max98095_probe(struct udevice *dev) { + struct maxim_priv *priv = dev_get_priv(dev); int ret; - int old_bus = i2c_get_bus_num(); - struct sound_codec_info *pcodec_info = &g_codec_info; - if (get_max98095_codec_values(pcodec_info, blob) < 0) { - debug("FDT Codec values failed\n"); - return -1; + priv->dev = dev; + ret = max98095_device_init(priv); + if (ret < 0) { + debug("%s: max98095 codec chip init failed\n", __func__); + return ret; } - i2c_set_bus_num(pcodec_info->i2c_bus); - ret = max98095_do_init(pcodec_info, aif_id, sampling_rate, mclk_freq, - bits_per_sample); - i2c_set_bus_num(old_bus); - - return ret; + return 0; } + +static const struct audio_codec_ops max98095_ops = { + .set_params = max98095_set_params, +}; + +static const struct udevice_id max98095_ids[] = { + { .compatible = "maxim,max98095" }, + { } +}; + +U_BOOT_DRIVER(max98095) = { + .name = "max98095", + .id = UCLASS_AUDIO_CODEC, + .of_match = max98095_ids, + .probe = max98095_probe, + .ops = &max98095_ops, + .priv_auto_alloc_size = sizeof(struct maxim_priv), +}; diff --git a/drivers/sound/max98095.h b/drivers/sound/max98095.h index 44b1e3a97b..1521f3f02f 100644 --- a/drivers/sound/max98095.h +++ b/drivers/sound/max98095.h @@ -1,19 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * max98095.h -- MAX98095 ALSA SoC Audio driver * * Copyright 2011 Maxim Integrated Products - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #ifndef _MAX98095_H #define _MAX98095_H +#include "maxim_codec.h" + /* Available audio interface ports in wm8994 codec */ enum en_max_audio_interface { - AIF1 = 1, + AIF1, AIF2, }; diff --git a/drivers/sound/maxim_codec.c b/drivers/sound/maxim_codec.c new file mode 100644 index 0000000000..dcaf081988 --- /dev/null +++ b/drivers/sound/maxim_codec.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * maxim_codec.c -- MAXIM CODEC Common driver + * + * Copyright 2011 Maxim Integrated Products + */ + +#include <common.h> +#include <div64.h> +#include <i2c.h> +#include <i2s.h> +#include <sound.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <asm/arch/clk.h> +#include <asm/arch/cpu.h> +#include <asm/arch/power.h> +#include "maxim_codec.h" + +/* + * Writes value to a device register through i2c + * + * @param priv Private data for driver + * @param reg reg number to be write + * @param data data to be writen to the above registor + * + * @return int value 1 for change, 0 for no change or negative error code. + */ +int maxim_i2c_write(struct maxim_priv *priv, unsigned int reg, + unsigned char data) +{ + debug("%s: Write Addr : 0x%02X, Data : 0x%02X\n", + __func__, reg, data); + return dm_i2c_write(priv->dev, reg, &data, 1); +} + +/* + * Read a value from a device register through i2c + * + * @param priv Private data for driver + * @param reg reg number to be read + * @param data address of read data to be stored + * + * @return int value 0 for success, -1 in case of error. + */ +unsigned int maxim_i2c_read(struct maxim_priv *priv, unsigned int reg, + unsigned char *data) +{ + int ret; + + return dm_i2c_read(priv->dev, reg, data, 1); + if (ret != 0) { + debug("%s: Error while reading register %#04x\n", + __func__, reg); + return -1; + } + + return 0; +} + +/* + * update device register bits through i2c + * + * @param priv Private data for driver + * @param reg codec register + * @param mask register mask + * @param value new value + * + * @return int value 0 for success, non-zero error code. + */ +int maxim_bic_or(struct maxim_priv *priv, unsigned int reg, unsigned char mask, + unsigned char value) +{ + int change, ret = 0; + unsigned char old, new; + + if (maxim_i2c_read(priv, reg, &old) != 0) + return -1; + new = (old & ~mask) | (value & mask); + change = (old != new) ? 1 : 0; + if (change) + ret = maxim_i2c_write(priv, reg, new); + if (ret < 0) + return ret; + + return change; +} diff --git a/drivers/sound/maxim_codec.h b/drivers/sound/maxim_codec.h new file mode 100644 index 0000000000..a3128e0bb7 --- /dev/null +++ b/drivers/sound/maxim_codec.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * maxim_codec.h -- MAXIM codec common interface file + * + * Copyright (C) 2013 Samsung Electronics + * D Krishna Mohan <krishna.md@samsung.com> + */ + +#ifndef __MAXIM_COMMON_H__ +#define __MAXIM_COMMON_H__ + +enum maxim_codec_type { + MAX98095, + MAX98090, +}; + +struct maxim_priv { + enum maxim_codec_type devtype; + unsigned int sysclk; + unsigned int rate; + unsigned int fmt; + struct udevice *dev; +}; + +#define MAXIM_AUDIO_I2C_BUS 7 +#define MAXIM_AUDIO_I2C_REG_98095 0x22 + +#define MAXIM_AUDIO_I2C_REG MAXIM_AUDIO_I2C_REG_98095 + +/* + * Writes value to a device register through i2c + * + * @param priv Private data for driver + * @param reg reg number to be write + * @param data data to be writen to the above registor + * + * @return int value 1 for change, 0 for no change or negative error code. + */ +int maxim_i2c_write(struct maxim_priv *priv, unsigned int reg, + unsigned char data); + +/* + * Read a value from a device register through i2c + * + * @param priv Private data for driver + * @param reg reg number to be read + * @param data address of read data to be stored + * + * @return int value 0 for success, -1 in case of error. + */ +unsigned int maxim_i2c_read(struct maxim_priv *priv, unsigned int reg, + unsigned char *data); + +/* + * update device register bits through i2c + * + * @param priv Private data for driver + * @param reg codec register + * @param mask register mask + * @param value new value + * + * @return int value 0 for success, non-zero error code. + */ +int maxim_bic_or(struct maxim_priv *priv, unsigned int reg, unsigned char mask, + unsigned char value); + +#endif /* __MAXIM_COMMON_H__ */ diff --git a/drivers/sound/samsung-i2s.c b/drivers/sound/samsung-i2s.c index f39abf5e2a..c19e08e7e3 100644 --- a/drivers/sound/samsung-i2s.c +++ b/drivers/sound/samsung-i2s.c @@ -4,13 +4,14 @@ * R. Chandrasekar <rcsekar@samsung.com> */ +#include <common.h> +#include <dm.h> +#include <i2s.h> +#include <sound.h> #include <asm/arch/clk.h> #include <asm/arch/pinmux.h> #include <asm/arch/i2s-regs.h> #include <asm/io.h> -#include <common.h> -#include <sound.h> -#include <i2s.h> #define FIC_TX2COUNT(x) (((x) >> 24) & 0xf) #define FIC_TX1COUNT(x) (((x) >> 16) & 0xf) @@ -111,7 +112,7 @@ static void i2s_set_bitclk_framesize(struct i2s_reg *i2s_reg, unsigned bfs) * @param flush Tx fifo flush command (0x00 - do not flush * 0x80 - flush tx fifo) */ -void i2s_fifo(struct i2s_reg *i2s_reg, unsigned int flush) +static void i2s_fifo(struct i2s_reg *i2s_reg, unsigned int flush) { /* Flush the FIFO */ setbits_le32(&i2s_reg->fic, flush); @@ -126,7 +127,7 @@ void i2s_fifo(struct i2s_reg *i2s_reg, unsigned int flush) * * @return int value 0 for success, -1 in case of error */ -int i2s_set_sysclk_dir(struct i2s_reg *i2s_reg, int dir) +static int i2s_set_sysclk_dir(struct i2s_reg *i2s_reg, int dir) { unsigned int mod = readl(&i2s_reg->mod); @@ -148,7 +149,7 @@ int i2s_set_sysclk_dir(struct i2s_reg *i2s_reg, int dir) * * @return int value 0 for success, -1 in case of error */ -int i2s_set_fmt(struct i2s_reg *i2s_reg, unsigned int fmt) +static int i2s_set_fmt(struct i2s_reg *i2s_reg, unsigned int fmt) { unsigned int mod = readl(&i2s_reg->mod); unsigned int tmp = 0; @@ -170,7 +171,7 @@ int i2s_set_fmt(struct i2s_reg *i2s_reg, unsigned int fmt) default: debug("%s: Invalid format priority [0x%x]\n", __func__, (fmt & SND_SOC_DAIFMT_FORMAT_MASK)); - return -1; + return -ERANGE; } /* @@ -189,7 +190,7 @@ int i2s_set_fmt(struct i2s_reg *i2s_reg, unsigned int fmt) default: debug("%s: Invalid clock ploarity input [0x%x]\n", __func__, (fmt & SND_SOC_DAIFMT_INV_MASK)); - return -1; + return -ERANGE; } switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { @@ -201,13 +202,13 @@ int i2s_set_fmt(struct i2s_reg *i2s_reg, unsigned int fmt) ret = i2s_set_sysclk_dir(i2s_reg, SND_SOC_CLOCK_OUT); if (ret != 0) { debug("%s:set i2s clock direction failed\n", __func__); - return -1; + return ret; } break; default: debug("%s: Invalid master selection [0x%x]\n", __func__, (fmt & SND_SOC_DAIFMT_MASTER_MASK)); - return -1; + return -ERANGE; } mod &= ~(MOD_SDF_MASK | MOD_LR_RLOW | MOD_SLAVE); @@ -225,7 +226,7 @@ int i2s_set_fmt(struct i2s_reg *i2s_reg, unsigned int fmt) * * @return int value 0 for success, -1 in case of error */ -int i2s_set_samplesize(struct i2s_reg *i2s_reg, unsigned int blc) +static int i2s_set_samplesize(struct i2s_reg *i2s_reg, unsigned int blc) { unsigned int mod = readl(&i2s_reg->mod); @@ -248,43 +249,43 @@ int i2s_set_samplesize(struct i2s_reg *i2s_reg, unsigned int blc) default: debug("%s: Invalid sample size input [0x%x]\n", __func__, blc); - return -1; + return -ERANGE; } writel(mod, &i2s_reg->mod); return 0; } -int i2s_transfer_tx_data(struct i2stx_info *pi2s_tx, unsigned int *data, - unsigned long data_size) +int i2s_transfer_tx_data(struct i2s_uc_priv *pi2s_tx, void *data, + uint data_size) { + struct i2s_reg *i2s_reg = (struct i2s_reg *)pi2s_tx->base_address; + u32 *ptr; int i; int start; - struct i2s_reg *i2s_reg = - (struct i2s_reg *)pi2s_tx->base_address; if (data_size < FIFO_LENGTH) { debug("%s : Invalid data size\n", __func__); - return -1; /* invalid pcm data size */ + return -ENODATA; /* invalid pcm data size */ } /* fill the tx buffer before stating the tx transmit */ - for (i = 0; i < FIFO_LENGTH; i++) - writel(*data++, &i2s_reg->txd); + for (i = 0, ptr = data; i < FIFO_LENGTH; i++) + writel(*ptr++, &i2s_reg->txd); - data_size -= FIFO_LENGTH; + data_size -= sizeof(*ptr) * FIFO_LENGTH; i2s_txctrl(i2s_reg, I2S_TX_ON); while (data_size > 0) { start = get_timer(0); if (!(CON_TXFIFO_FULL & (readl(&i2s_reg->con)))) { - writel(*data++, &i2s_reg->txd); - data_size--; + writel(*ptr++, &i2s_reg->txd); + data_size -= sizeof(*ptr); } else { if (get_timer(start) > TIMEOUT_I2S_TX) { i2s_txctrl(i2s_reg, I2S_TX_OFF); debug("%s: I2S Transfer Timeout\n", __func__); - return -1; + return -ETIMEDOUT; } } } @@ -293,11 +294,11 @@ int i2s_transfer_tx_data(struct i2stx_info *pi2s_tx, unsigned int *data, return 0; } -int i2s_tx_init(struct i2stx_info *pi2s_tx) +int i2s_tx_init(struct i2s_uc_priv *pi2s_tx) { int ret; - struct i2s_reg *i2s_reg = - (struct i2s_reg *)pi2s_tx->base_address; + struct i2s_reg *i2s_reg = (struct i2s_reg *)pi2s_tx->base_address; + if (pi2s_tx->id == 0) { /* Initialize GPIO for I2S-0 */ exynos_pinmux_config(PERIPH_ID_I2S0, 0); @@ -312,20 +313,20 @@ int i2s_tx_init(struct i2stx_info *pi2s_tx) ret = set_epll_clk(pi2s_tx->audio_pll_clk); } else { debug("%s: unsupported i2s-%d bus\n", __func__, pi2s_tx->id); - return -1; + return -ERANGE; } - if (ret != 0) { + if (ret) { debug("%s: epll clock set rate failed\n", __func__); - return -1; + return ret; } /* Select Clk Source for Audio 0 or 1 */ ret = set_i2s_clk_source(pi2s_tx->id); - if (ret == -1) { + if (ret) { debug("%s: unsupported clock for i2s-%d\n", __func__, pi2s_tx->id); - return -1; + return ret; } if (pi2s_tx->id == 0) { @@ -341,21 +342,21 @@ int i2s_tx_init(struct i2stx_info *pi2s_tx) (pi2s_tx->samplingrate * (pi2s_tx->rfs)), pi2s_tx->id); } - if (ret == -1) { + if (ret) { debug("%s: unsupported prescalar for i2s-%d\n", __func__, pi2s_tx->id); - return -1; + return ret; } /* Configure I2s format */ - ret = i2s_set_fmt(i2s_reg, (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM)); + ret = i2s_set_fmt(i2s_reg, SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM); if (ret == 0) { i2s_set_lr_framesize(i2s_reg, pi2s_tx->rfs); ret = i2s_set_samplesize(i2s_reg, pi2s_tx->bitspersample); if (ret != 0) { debug("%s:set sample rate failed\n", __func__); - return -1; + return ret; } i2s_set_bitclk_framesize(i2s_reg, pi2s_tx->bfs); @@ -368,3 +369,87 @@ int i2s_tx_init(struct i2stx_info *pi2s_tx) return ret; } + +static int samsung_i2s_tx_data(struct udevice *dev, void *data, uint data_size) +{ + struct i2s_uc_priv *priv = dev_get_uclass_priv(dev); + + return i2s_transfer_tx_data(priv, data, data_size); +} + +static int samsung_i2s_probe(struct udevice *dev) +{ + struct i2s_uc_priv *priv = dev_get_uclass_priv(dev); + + return i2s_tx_init(priv); +} + +static int samsung_i2s_ofdata_to_platdata(struct udevice *dev) +{ + struct i2s_uc_priv *priv = dev_get_uclass_priv(dev); + ulong base; + + /* + * Get the pre-defined sound specific values from FDT. + * All of these are expected to be correct otherwise + * wrong register values in i2s setup parameters + * may result in no sound play. + */ + base = dev_read_addr(dev); + if (base == FDT_ADDR_T_NONE) { + debug("%s: Missing i2s base\n", __func__); + return -EINVAL; + } + priv->base_address = base; + + if (dev_read_u32u(dev, "samsung,i2s-epll-clock-frequency", + &priv->audio_pll_clk)) + goto err; + debug("audio_pll_clk = %d\n", priv->audio_pll_clk); + if (dev_read_u32u(dev, "samsung,i2s-sampling-rate", + &priv->samplingrate)) + goto err; + debug("samplingrate = %d\n", priv->samplingrate); + if (dev_read_u32u(dev, "samsung,i2s-bits-per-sample", + &priv->bitspersample)) + goto err; + debug("bitspersample = %d\n", priv->bitspersample); + if (dev_read_u32u(dev, "samsung,i2s-channels", &priv->channels)) + goto err; + debug("channels = %d\n", priv->channels); + if (dev_read_u32u(dev, "samsung,i2s-lr-clk-framesize", &priv->rfs)) + goto err; + debug("rfs = %d\n", priv->rfs); + if (dev_read_u32u(dev, "samsung,i2s-bit-clk-framesize", &priv->bfs)) + goto err; + debug("bfs = %d\n", priv->bfs); + + if (dev_read_u32u(dev, "samsung,i2s-id", &priv->id)) + goto err; + debug("id = %d\n", priv->id); + + return 0; + +err: + debug("fail to get sound i2s node properties\n"); + + return -EINVAL; +} + +static const struct i2s_ops samsung_i2s_ops = { + .tx_data = samsung_i2s_tx_data, +}; + +static const struct udevice_id samsung_i2s_ids[] = { + { .compatible = "samsung,s5pv210-i2s" }, + { } +}; + +U_BOOT_DRIVER(samsung_i2s) = { + .name = "samsung_i2s", + .id = UCLASS_I2S, + .of_match = samsung_i2s_ids, + .probe = samsung_i2s_probe, + .ofdata_to_platdata = samsung_i2s_ofdata_to_platdata, + .ops = &samsung_i2s_ops, +}; diff --git a/drivers/sound/samsung_sound.c b/drivers/sound/samsung_sound.c new file mode 100644 index 0000000000..1d711c8732 --- /dev/null +++ b/drivers/sound/samsung_sound.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 Google, LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <audio_codec.h> +#include <dm.h> +#include <i2s.h> +#include <sound.h> +#include <asm/gpio.h> + +static int samsung_sound_setup(struct udevice *dev) +{ + struct sound_uc_priv *uc_priv = dev_get_uclass_priv(dev); + struct i2s_uc_priv *i2c_priv = dev_get_uclass_priv(uc_priv->i2s); + int ret; + + if (uc_priv->setup_done) + return -EALREADY; + ret = audio_codec_set_params(uc_priv->codec, i2c_priv->id, + i2c_priv->samplingrate, + i2c_priv->samplingrate * i2c_priv->rfs, + i2c_priv->bitspersample, + i2c_priv->channels); + if (ret) + return ret; + uc_priv->setup_done = true; + + return 0; +} + +static int samsung_sound_play(struct udevice *dev, void *data, uint data_size) +{ + struct sound_uc_priv *uc_priv = dev_get_uclass_priv(dev); + + return i2s_tx_data(uc_priv->i2s, data, data_size); +} + +static int samsung_sound_probe(struct udevice *dev) +{ + struct sound_uc_priv *uc_priv = dev_get_uclass_priv(dev); + struct ofnode_phandle_args args; + struct gpio_desc en_gpio; + ofnode node; + int ret; + + ret = gpio_request_by_name(dev, "codec-enable-gpio", 0, &en_gpio, + GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); + + /* Turn on the GPIO which connects to the codec's "enable" line. */ + if (!ret) + gpio_set_pull(gpio_get_number(&en_gpio), S5P_GPIO_PULL_NONE); + + ret = uclass_get_device_by_phandle(UCLASS_AUDIO_CODEC, dev, + "samsung,audio-codec", + &uc_priv->codec); + if (ret) { + debug("Failed to probe audio codec\n"); + return ret; + } + node = ofnode_find_subnode(dev_ofnode(dev), "cpu"); + if (!ofnode_valid(node)) { + debug("Failed to find /cpu subnode\n"); + return -EINVAL; + } + ret = ofnode_parse_phandle_with_args(node, "sound-dai", + "#sound-dai-cells", 0, 0, &args); + if (ret) { + debug("Cannot find phandle: %d\n", ret); + return ret; + } + ret = uclass_get_device_by_ofnode(UCLASS_I2S, args.node, &uc_priv->i2s); + if (ret) { + debug("Cannot find i2s: %d\n", ret); + return ret; + } + debug("Probed sound '%s' with codec '%s' and i2s '%s'\n", dev->name, + uc_priv->codec->name, uc_priv->i2s->name); + + return 0; +} + +static const struct sound_ops samsung_sound_ops = { + .setup = samsung_sound_setup, + .play = samsung_sound_play, +}; + +static const struct udevice_id samsung_sound_ids[] = { + { .compatible = "google,snow-audio-max98095" }, + { .compatible = "google,spring-audio-max98095" }, + { .compatible = "samsung,smdk5420-audio-wm8994" }, + { .compatible = "google,peach-audio-max98090" }, + { } +}; + +U_BOOT_DRIVER(samsung_sound) = { + .name = "samsung_sound", + .id = UCLASS_SOUND, + .of_match = samsung_sound_ids, + .probe = samsung_sound_probe, + .ops = &samsung_sound_ops, +}; diff --git a/drivers/sound/sandbox.c b/drivers/sound/sandbox.c index 94eff54282..b0b07f3239 100644 --- a/drivers/sound/sandbox.c +++ b/drivers/sound/sandbox.c @@ -4,19 +4,185 @@ */ #include <common.h> -#include <asm/sound.h> +#include <audio_codec.h> +#include <dm.h> +#include <i2s.h> +#include <sound.h> #include <asm/sdl.h> -int sound_play(uint32_t msec, uint32_t frequency) +struct sandbox_codec_priv { + int interface; + int rate; + int mclk_freq; + int bits_per_sample; + uint channels; +}; + +struct sandbox_i2s_priv { + int sum; /* Use to sum the provided audio data */ +}; + +struct sandbox_sound_priv { + int setup_called; + int sum; /* Use to sum the provided audio data */ +}; + +void sandbox_get_codec_params(struct udevice *dev, int *interfacep, int *ratep, + int *mclk_freqp, int *bits_per_samplep, + uint *channelsp) +{ + struct sandbox_codec_priv *priv = dev_get_priv(dev); + + *interfacep = priv->interface; + *ratep = priv->rate; + *mclk_freqp = priv->mclk_freq; + *bits_per_samplep = priv->bits_per_sample; + *channelsp = priv->channels; +} + +int sandbox_get_i2s_sum(struct udevice *dev) +{ + struct sandbox_i2s_priv *priv = dev_get_priv(dev); + + return priv->sum; +} + +int sandbox_get_setup_called(struct udevice *dev) +{ + struct sandbox_sound_priv *priv = dev_get_priv(dev); + + return priv->setup_called; +} + +int sandbox_get_sound_sum(struct udevice *dev) +{ + struct sandbox_sound_priv *priv = dev_get_priv(dev); + + return priv->sum; +} + +static int sandbox_codec_set_params(struct udevice *dev, int interface, + int rate, int mclk_freq, + int bits_per_sample, uint channels) +{ + struct sandbox_codec_priv *priv = dev_get_priv(dev); + + priv->interface = interface; + priv->rate = rate; + priv->mclk_freq = mclk_freq; + priv->bits_per_sample = bits_per_sample; + priv->channels = channels; + + return 0; +} + +static int sandbox_i2s_tx_data(struct udevice *dev, void *data, + uint data_size) +{ + struct sandbox_i2s_priv *priv = dev_get_priv(dev); + int i; + + for (i = 0; i < data_size; i++) + priv->sum += ((uint8_t *)data)[i]; + + return sandbox_sdl_sound_play(data, data_size); +} + +static int sandbox_i2s_probe(struct udevice *dev) { - sandbox_sdl_sound_start(frequency); - mdelay(msec); - sandbox_sdl_sound_stop(); + struct i2s_uc_priv *uc_priv = dev_get_uclass_priv(dev); + + /* Use hard-coded values here */ + uc_priv->rfs = 256; + uc_priv->bfs = 32; + uc_priv->audio_pll_clk = 192000000; + uc_priv->samplingrate = 48000; + uc_priv->bitspersample = 16; + uc_priv->channels = 2; + uc_priv->id = 1; + + /* Ignore any error here - we'll just have no sound */ + sandbox_sdl_sound_init(uc_priv->samplingrate, uc_priv->channels); return 0; } -int sound_init(const void *blob) +static int sandbox_sound_setup(struct udevice *dev) +{ + struct sandbox_sound_priv *priv = dev_get_priv(dev); + + priv->setup_called++; + + return 0; +} + +static int sandbox_sound_play(struct udevice *dev, void *data, uint data_size) +{ + struct sound_uc_priv *uc_priv = dev_get_uclass_priv(dev); + struct sandbox_sound_priv *priv = dev_get_priv(dev); + int i; + + for (i = 0; i < data_size; i++) + priv->sum += ((uint8_t *)data)[i]; + + return i2s_tx_data(uc_priv->i2s, data, data_size); +} + +static int sandbox_sound_probe(struct udevice *dev) { - return sandbox_sdl_sound_init(); + return sound_find_codec_i2s(dev); } + +static const struct audio_codec_ops sandbox_codec_ops = { + .set_params = sandbox_codec_set_params, +}; + +static const struct udevice_id sandbox_codec_ids[] = { + { .compatible = "sandbox,audio-codec" }, + { } +}; + +U_BOOT_DRIVER(sandbox_codec) = { + .name = "sandbox_codec", + .id = UCLASS_AUDIO_CODEC, + .of_match = sandbox_codec_ids, + .ops = &sandbox_codec_ops, + .priv_auto_alloc_size = sizeof(struct sandbox_codec_priv), +}; + +static const struct i2s_ops sandbox_i2s_ops = { + .tx_data = sandbox_i2s_tx_data, +}; + +static const struct udevice_id sandbox_i2s_ids[] = { + { .compatible = "sandbox,i2s" }, + { } +}; + +U_BOOT_DRIVER(sandbox_i2s) = { + .name = "sandbox_i2s", + .id = UCLASS_I2S, + .of_match = sandbox_i2s_ids, + .ops = &sandbox_i2s_ops, + .probe = sandbox_i2s_probe, + .priv_auto_alloc_size = sizeof(struct sandbox_i2s_priv), +}; + +static const struct sound_ops sandbox_sound_ops = { + .setup = sandbox_sound_setup, + .play = sandbox_sound_play, +}; + +static const struct udevice_id sandbox_sound_ids[] = { + { .compatible = "sandbox,sound" }, + { } +}; + +U_BOOT_DRIVER(sandbox_sound) = { + .name = "sandbox_sound", + .id = UCLASS_SOUND, + .of_match = sandbox_sound_ids, + .ops = &sandbox_sound_ops, + .priv_auto_alloc_size = sizeof(struct sandbox_sound_priv), + .probe = sandbox_sound_probe, +}; diff --git a/drivers/sound/sound-i2s.c b/drivers/sound/sound-i2s.c deleted file mode 100644 index f0f0b79bc5..0000000000 --- a/drivers/sound/sound-i2s.c +++ /dev/null @@ -1,208 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2012 Samsung Electronics - * R. Chandrasekar <rcsekar@samsung.com> - */ - -#include <malloc.h> -#include <common.h> -#include <asm/io.h> -#include <linux/libfdt.h> -#include <fdtdec.h> -#include <i2c.h> -#include <i2s.h> -#include <sound.h> -#include <asm/arch/sound.h> -#include "wm8994.h" -#include "max98095.h" - -/* defines */ -#define SOUND_400_HZ 400 -#define SOUND_BITS_IN_BYTE 8 - -static struct i2stx_info g_i2stx_pri; - -/* - * get_sound_i2s_values gets values for i2s parameters - * - * @param i2stx_info i2s transmitter transfer param structure - * @param blob FDT blob if enabled else NULL - */ -static int get_sound_i2s_values(struct i2stx_info *i2s, const void *blob) -{ - int node; - int error = 0; - int base; - - node = fdt_path_offset(blob, "i2s"); - if (node <= 0) { - debug("EXYNOS_SOUND: No node for sound in device tree\n"); - return -1; - } - - /* - * Get the pre-defined sound specific values from FDT. - * All of these are expected to be correct otherwise - * wrong register values in i2s setup parameters - * may result in no sound play. - */ - base = fdtdec_get_addr(blob, node, "reg"); - if (base == FDT_ADDR_T_NONE) { - debug("%s: Missing i2s base\n", __func__); - return -1; - } - i2s->base_address = base; - - i2s->audio_pll_clk = fdtdec_get_int(blob, - node, "samsung,i2s-epll-clock-frequency", -1); - error |= i2s->audio_pll_clk; - debug("audio_pll_clk = %d\n", i2s->audio_pll_clk); - i2s->samplingrate = fdtdec_get_int(blob, - node, "samsung,i2s-sampling-rate", -1); - error |= i2s->samplingrate; - debug("samplingrate = %d\n", i2s->samplingrate); - i2s->bitspersample = fdtdec_get_int(blob, - node, "samsung,i2s-bits-per-sample", -1); - error |= i2s->bitspersample; - debug("bitspersample = %d\n", i2s->bitspersample); - i2s->channels = fdtdec_get_int(blob, - node, "samsung,i2s-channels", -1); - error |= i2s->channels; - debug("channels = %d\n", i2s->channels); - i2s->rfs = fdtdec_get_int(blob, - node, "samsung,i2s-lr-clk-framesize", -1); - error |= i2s->rfs; - debug("rfs = %d\n", i2s->rfs); - i2s->bfs = fdtdec_get_int(blob, - node, "samsung,i2s-bit-clk-framesize", -1); - error |= i2s->bfs; - debug("bfs = %d\n", i2s->bfs); - - i2s->id = fdtdec_get_int(blob, node, "samsung,i2s-id", -1); - error |= i2s->id; - debug("id = %d\n", i2s->id); - - if (error == -1) { - debug("fail to get sound i2s node properties\n"); - return -1; - } - - return 0; -} - -/* - * Init codec - * - * @param blob FDT blob - * @param pi2s_tx i2s parameters required by codec - * @return int value, 0 for success - */ -static int codec_init(const void *blob, struct i2stx_info *pi2s_tx) -{ - int ret; - const char *codectype; - int node; - - /* Get the node from FDT for sound */ - node = fdt_path_offset(blob, "i2s"); - if (node <= 0) { - debug("EXYNOS_SOUND: No node for sound in device tree\n"); - debug("node = %d\n", node); - return -1; - } - - /* - * Get the pre-defined sound codec specific values from FDT. - * All of these are expected to be correct otherwise sound - * can not be played - */ - codectype = fdt_getprop(blob, node, "samsung,codec-type", NULL); - debug("device = %s\n", codectype); - if (!strcmp(codectype, "wm8994")) { - /* Check the codec type and initialise the same */ - ret = wm8994_init(blob, pi2s_tx->id + 1, - pi2s_tx->samplingrate, - (pi2s_tx->samplingrate * (pi2s_tx->rfs)), - pi2s_tx->bitspersample, pi2s_tx->channels); - } else if (!strcmp(codectype, "max98095")) { - ret = max98095_init(blob, pi2s_tx->id + 1, - pi2s_tx->samplingrate, - (pi2s_tx->samplingrate * (pi2s_tx->rfs)), - pi2s_tx->bitspersample); - } else { - debug("%s: Unknown codec type %s\n", __func__, codectype); - return -1; - } - - if (ret) { - debug("%s: Codec init failed\n", __func__); - return -1; - } - - return 0; -} - -int sound_init(const void *blob) -{ - int ret; - struct i2stx_info *pi2s_tx = &g_i2stx_pri; - - /* Get the I2S Values */ - if (get_sound_i2s_values(pi2s_tx, blob) < 0) { - debug(" FDT I2S values failed\n"); - return -1; - } - - if (codec_init(blob, pi2s_tx) < 0) { - debug(" Codec init failed\n"); - return -1; - } - - ret = i2s_tx_init(pi2s_tx); - if (ret) { - debug("%s: Failed to init i2c transmit: ret=%d\n", __func__, - ret); - return ret; - } - - - return ret; -} - -int sound_play(uint32_t msec, uint32_t frequency) -{ - unsigned int *data; - unsigned long data_size; - unsigned int ret = 0; - - /*Buffer length computation */ - data_size = g_i2stx_pri.samplingrate * g_i2stx_pri.channels; - data_size *= (g_i2stx_pri.bitspersample / SOUND_BITS_IN_BYTE); - data = malloc(data_size); - - if (data == NULL) { - debug("%s: malloc failed\n", __func__); - return -1; - } - - sound_create_square_wave(g_i2stx_pri.samplingrate, - (unsigned short *)data, - data_size / sizeof(unsigned short), - frequency); - - while (msec >= 1000) { - ret = i2s_transfer_tx_data(&g_i2stx_pri, data, - (data_size / sizeof(int))); - msec -= 1000; - } - if (msec) { - unsigned long size = - (data_size * msec) / (sizeof(int) * 1000); - - ret = i2s_transfer_tx_data(&g_i2stx_pri, data, size); - } - - free(data); - - return ret; -} diff --git a/drivers/sound/sound-uclass.c b/drivers/sound/sound-uclass.c new file mode 100644 index 0000000000..2b83626889 --- /dev/null +++ b/drivers/sound/sound-uclass.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <dm.h> +#include <i2s.h> +#include <sound.h> + +#define SOUND_BITS_IN_BYTE 8 + +int sound_setup(struct udevice *dev) +{ + struct sound_ops *ops = sound_get_ops(dev); + + if (!ops->setup) + return -ENOSYS; + + return ops->setup(dev); +} + +int sound_play(struct udevice *dev, void *data, uint data_size) +{ + struct sound_ops *ops = sound_get_ops(dev); + + if (!ops->play) + return -ENOSYS; + + return ops->play(dev, data, data_size); +} + +int sound_beep(struct udevice *dev, int msecs, int frequency_hz) +{ + struct sound_uc_priv *uc_priv = dev_get_uclass_priv(dev); + struct i2s_uc_priv *i2s_uc_priv = dev_get_uclass_priv(uc_priv->i2s); + unsigned short *data; + uint data_size; + int ret; + + ret = sound_setup(dev); + if (ret && ret != -EALREADY) + return ret; + + /* Buffer length computation */ + data_size = i2s_uc_priv->samplingrate * i2s_uc_priv->channels; + data_size *= (i2s_uc_priv->bitspersample / SOUND_BITS_IN_BYTE); + data = malloc(data_size); + if (!data) { + debug("%s: malloc failed\n", __func__); + return -ENOMEM; + } + + sound_create_square_wave(i2s_uc_priv->samplingrate, data, data_size, + frequency_hz, i2s_uc_priv->channels); + + while (msecs >= 1000) { + ret = sound_play(dev, data, data_size); + msecs -= 1000; + } + if (msecs) { + unsigned long size = + (data_size * msecs) / (sizeof(int) * 1000); + + ret = sound_play(dev, data, size); + } + + free(data); + + return ret; +} + +int sound_find_codec_i2s(struct udevice *dev) +{ + struct sound_uc_priv *uc_priv = dev_get_uclass_priv(dev); + struct ofnode_phandle_args args; + ofnode node; + int ret; + + /* First the codec */ + node = ofnode_find_subnode(dev_ofnode(dev), "codec"); + if (!ofnode_valid(node)) { + debug("Failed to find /cpu subnode\n"); + return -EINVAL; + } + ret = ofnode_parse_phandle_with_args(node, "sound-dai", + "#sound-dai-cells", 0, 0, &args); + if (ret) { + debug("Cannot find phandle: %d\n", ret); + return ret; + } + ret = uclass_get_device_by_ofnode(UCLASS_AUDIO_CODEC, args.node, + &uc_priv->codec); + if (ret) { + debug("Cannot find codec: %d\n", ret); + return ret; + } + + /* Now the i2s */ + node = ofnode_find_subnode(dev_ofnode(dev), "cpu"); + if (!ofnode_valid(node)) { + debug("Failed to find /cpu subnode\n"); + return -EINVAL; + } + ret = ofnode_parse_phandle_with_args(node, "sound-dai", + "#sound-dai-cells", 0, 0, &args); + if (ret) { + debug("Cannot find phandle: %d\n", ret); + return ret; + } + ret = uclass_get_device_by_ofnode(UCLASS_I2S, args.node, &uc_priv->i2s); + if (ret) { + debug("Cannot find i2s: %d\n", ret); + return ret; + } + debug("Probed sound '%s' with codec '%s' and i2s '%s'\n", dev->name, + uc_priv->codec->name, uc_priv->i2s->name); + + return 0; +} + +UCLASS_DRIVER(sound) = { + .id = UCLASS_SOUND, + .name = "sound", + .per_device_auto_alloc_size = sizeof(struct sound_uc_priv), +}; diff --git a/drivers/sound/sound.c b/drivers/sound/sound.c index 4f0ad0d8f0..dd3f9db4f7 100644 --- a/drivers/sound/sound.c +++ b/drivers/sound/sound.c @@ -8,7 +8,7 @@ #include <sound.h> void sound_create_square_wave(uint sample_rate, unsigned short *data, int size, - uint freq) + uint freq, uint channels) { const unsigned short amplitude = 16000; /* between 1 and 32767 */ const int period = freq ? sample_rate / freq : 0; @@ -21,14 +21,17 @@ void sound_create_square_wave(uint sample_rate, unsigned short *data, int size, size--; while (size) { - int i; + int i, j; + for (i = 0; size && i < half; i++) { size -= 2; - *data++ = amplitude; + for (j = 0; j < channels; j++) + *data++ = amplitude; } for (i = 0; size && i < period - half; i++) { size -= 2; - *data++ = -amplitude; + for (j = 0; j < channels; j++) + *data++ = -amplitude; } } } diff --git a/drivers/sound/wm8994.c b/drivers/sound/wm8994.c index aaaa3241aa..b290c4e879 100644 --- a/drivers/sound/wm8994.c +++ b/drivers/sound/wm8994.c @@ -4,15 +4,17 @@ * R. Chandrasekar <rcsekar@samsung.com> */ #include <common.h> -#include <asm/arch/clk.h> -#include <asm/arch/cpu.h> -#include <asm/gpio.h> -#include <asm/io.h> +#include <audio_codec.h> +#include <dm.h> #include <div64.h> #include <fdtdec.h> #include <i2c.h> #include <i2s.h> #include <sound.h> +#include <asm/gpio.h> +#include <asm/io.h> +#include <asm/arch/clk.h> +#include <asm/arch/cpu.h> #include <asm/arch/sound.h> #include "wm8994.h" #include "wm8994_registers.h" @@ -38,6 +40,7 @@ struct wm8994_priv { int mclk[WM8994_MAX_AIF]; /* master clock frequency in Hz */ int aifclk[WM8994_MAX_AIF]; /* audio interface clock in Hz */ struct wm8994_fll_config fll[2]; /* fll config to configure fll */ + struct udevice *dev; }; /* wm 8994 supported sampling rate values */ @@ -60,29 +63,17 @@ static int bclk_divs[] = { 640, 880, 960, 1280, 1760, 1920 }; -static struct wm8994_priv g_wm8994_info; -static unsigned char g_wm8994_i2c_dev_addr; -static struct sound_codec_info g_codec_info; - -/* - * Initialise I2C for wm 8994 - * - * @param bus no i2c bus number in which wm8994 is connected - */ -static void wm8994_i2c_init(int bus_no) -{ - i2c_set_bus_num(bus_no); -} - /* * Writes value to a device register through i2c * + * @param priv Private data for driver * @param reg reg number to be write * @param data data to be writen to the above registor * * @return int value 1 for change, 0 for no change or negative error code. */ -static int wm8994_i2c_write(unsigned int reg, unsigned short data) +static int wm8994_i2c_write(struct wm8994_priv *priv, unsigned int reg, + unsigned short data) { unsigned char val[2]; @@ -90,23 +81,25 @@ static int wm8994_i2c_write(unsigned int reg, unsigned short data) val[1] = (unsigned char)(data & 0xff); debug("Write Addr : 0x%04X, Data : 0x%04X\n", reg, data); - return i2c_write(g_wm8994_i2c_dev_addr, reg, 2, val, 2); + return dm_i2c_write(priv->dev, reg, val, 2); } /* * Read a value from a device register through i2c * + * @param priv Private data for driver * @param reg reg number to be read * @param data address of read data to be stored * * @return int value 0 for success, -1 in case of error. */ -static unsigned int wm8994_i2c_read(unsigned int reg , unsigned short *data) +static unsigned int wm8994_i2c_read(struct wm8994_priv *priv, unsigned int reg, + unsigned short *data) { unsigned char val[2]; int ret; - ret = i2c_read(g_wm8994_i2c_dev_addr, reg, 2, val, 2); + ret = dm_i2c_read(priv->dev, reg, val, 1); if (ret != 0) { debug("%s: Error while reading register %#04x\n", __func__, reg); @@ -123,6 +116,7 @@ static unsigned int wm8994_i2c_read(unsigned int reg , unsigned short *data) /* * update device register bits through i2c * + * @param priv Private data for driver * @param reg codec register * @param mask register mask * @param value new value @@ -130,18 +124,18 @@ static unsigned int wm8994_i2c_read(unsigned int reg , unsigned short *data) * @return int value 1 if change in the register value, * 0 for no change or negative error code. */ -static int wm8994_update_bits(unsigned int reg, unsigned short mask, - unsigned short value) +static int wm8994_bic_or(struct wm8994_priv *priv, unsigned int reg, + unsigned short mask, unsigned short value) { int change , ret = 0; unsigned short old, new; - if (wm8994_i2c_read(reg, &old) != 0) + if (wm8994_i2c_read(priv, reg, &old) != 0) return -1; new = (old & ~mask) | (value & mask); change = (old != new) ? 1 : 0; if (change) - ret = wm8994_i2c_write(reg, new); + ret = wm8994_i2c_write(priv, reg, new); if (ret < 0) return ret; @@ -151,12 +145,13 @@ static int wm8994_update_bits(unsigned int reg, unsigned short mask, /* * Sets i2s set format * + * @param priv wm8994 information * @param aif_id Interface ID * @param fmt i2S format * * @return -1 for error and 0 Success. */ -int wm8994_set_fmt(int aif_id, unsigned int fmt) +static int wm8994_set_fmt(struct wm8994_priv *priv, int aif_id, uint fmt) { int ms_reg; int aif_reg; @@ -254,12 +249,13 @@ int wm8994_set_fmt(int aif_id, unsigned int fmt) return -1; } - error = wm8994_update_bits(aif_reg, WM8994_AIF1_BCLK_INV | - WM8994_AIF1_LRCLK_INV_MASK | WM8994_AIF1_FMT_MASK, aif); + error = wm8994_bic_or(priv, aif_reg, WM8994_AIF1_BCLK_INV | + WM8994_AIF1_LRCLK_INV_MASK | + WM8994_AIF1_FMT_MASK, aif); - error |= wm8994_update_bits(ms_reg, WM8994_AIF1_MSTR_MASK, ms); - error |= wm8994_update_bits(aif_clk, WM8994_AIF1CLK_ENA_MASK, - WM8994_AIF1CLK_ENA); + error |= wm8994_bic_or(priv, ms_reg, WM8994_AIF1_MSTR_MASK, ms); + error |= wm8994_bic_or(priv, aif_clk, WM8994_AIF1CLK_ENA_MASK, + WM8994_AIF1CLK_ENA); if (error < 0) { debug("%s: codec register access error\n", __func__); return -1; @@ -271,7 +267,7 @@ int wm8994_set_fmt(int aif_id, unsigned int fmt) /* * Sets hw params FOR WM8994 * - * @param wm8994 wm8994 information pointer + * @param priv wm8994 information pointer * @param aif_id Audio interface ID * @param sampling_rate Sampling rate * @param bits_per_sample Bits per sample @@ -279,9 +275,9 @@ int wm8994_set_fmt(int aif_id, unsigned int fmt) * * @return -1 for error and 0 Success. */ -static int wm8994_hw_params(struct wm8994_priv *wm8994, int aif_id, - unsigned int sampling_rate, unsigned int bits_per_sample, - unsigned int channels) +static int wm8994_hw_params(struct wm8994_priv *priv, int aif_id, + uint sampling_rate, uint bits_per_sample, + uint channels) { int aif1_reg; int aif2_reg; @@ -349,12 +345,10 @@ static int wm8994_hw_params(struct wm8994_priv *wm8994, int aif_id, /* AIFCLK/fs ratio; look for a close match in either direction */ best = 0; - best_val = abs((fs_ratios[0] * sampling_rate) - - wm8994->aifclk[id]); + best_val = abs((fs_ratios[0] * sampling_rate) - priv->aifclk[id]); for (i = 1; i < ARRAY_SIZE(fs_ratios); i++) { - cur_val = abs((fs_ratios[i] * sampling_rate) - - wm8994->aifclk[id]); + cur_val = abs(fs_ratios[i] * sampling_rate - priv->aifclk[id]); if (cur_val >= best_val) continue; best = i; @@ -371,7 +365,7 @@ static int wm8994_hw_params(struct wm8994_priv *wm8994, int aif_id, */ best = 0; for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) { - cur_val = (wm8994->aifclk[id] * 10 / bclk_divs[i]) - bclk_rate; + cur_val = (priv->aifclk[id] * 10 / bclk_divs[i]) - bclk_rate; if (cur_val < 0) /* BCLK table is sorted */ break; best = i; @@ -383,10 +377,10 @@ static int wm8994_hw_params(struct wm8994_priv *wm8994, int aif_id, return -1; } - bclk_rate = wm8994->aifclk[id] * 10 / bclk_divs[best]; + bclk_rate = priv->aifclk[id] * 10 / bclk_divs[best]; bclk |= best << WM8994_AIF1_BCLK_DIV_SHIFT; - if (wm8994_i2c_read(aif1_reg, ®_data) != 0) { + if (wm8994_i2c_read(priv, aif1_reg, ®_data) != 0) { debug("%s: AIF1 register read Failed\n", __func__); return -1; } @@ -394,16 +388,17 @@ static int wm8994_hw_params(struct wm8994_priv *wm8994, int aif_id, if ((channels == 1) && ((reg_data & 0x18) == 0x18)) aif2 |= WM8994_AIF1_MONO; - if (wm8994->aifclk[id] == 0) { + if (priv->aifclk[id] == 0) { debug("%s:Audio interface clock not set\n", __func__); return -1; } - ret = wm8994_update_bits(aif1_reg, WM8994_AIF1_WL_MASK, aif1); - ret |= wm8994_update_bits(aif2_reg, WM8994_AIF1_MONO, aif2); - ret |= wm8994_update_bits(bclk_reg, WM8994_AIF1_BCLK_DIV_MASK, bclk); - ret |= wm8994_update_bits(rate_reg, WM8994_AIF1_SR_MASK | - WM8994_AIF1CLK_RATE_MASK, rate_val); + ret = wm8994_bic_or(priv, aif1_reg, WM8994_AIF1_WL_MASK, aif1); + ret |= wm8994_bic_or(priv, aif2_reg, WM8994_AIF1_MONO, aif2); + ret |= wm8994_bic_or(priv, bclk_reg, WM8994_AIF1_BCLK_DIV_MASK, + bclk); + ret |= wm8994_bic_or(priv, rate_reg, WM8994_AIF1_SR_MASK | + WM8994_AIF1CLK_RATE_MASK, rate_val); debug("rate vale = %x , bclk val= %x\n", rate_val, bclk); @@ -418,12 +413,12 @@ static int wm8994_hw_params(struct wm8994_priv *wm8994, int aif_id, /* * Configures Audio interface Clock * - * @param wm8994 wm8994 information pointer + * @param priv wm8994 information pointer * @param aif Audio Interface ID * * @return -1 for error and 0 Success. */ -static int configure_aif_clock(struct wm8994_priv *wm8994, int aif) +static int configure_aif_clock(struct wm8994_priv *priv, int aif) { int rate; int reg1 = 0; @@ -436,30 +431,30 @@ static int configure_aif_clock(struct wm8994_priv *wm8994, int aif) else offset = 0; - switch (wm8994->sysclk[aif-1]) { + switch (priv->sysclk[aif - 1]) { case WM8994_SYSCLK_MCLK1: reg1 |= SEL_MCLK1; - rate = wm8994->mclk[0]; + rate = priv->mclk[0]; break; case WM8994_SYSCLK_MCLK2: reg1 |= SEL_MCLK2; - rate = wm8994->mclk[1]; + rate = priv->mclk[1]; break; case WM8994_SYSCLK_FLL1: reg1 |= SEL_FLL1; - rate = wm8994->fll[0].out; + rate = priv->fll[0].out; break; case WM8994_SYSCLK_FLL2: reg1 |= SEL_FLL2; - rate = wm8994->fll[1].out; + rate = priv->fll[1].out; break; default: debug("%s: Invalid input clock selection [%d]\n", - __func__, wm8994->sysclk[aif-1]); + __func__, priv->sysclk[aif - 1]); return -1; } @@ -469,18 +464,18 @@ static int configure_aif_clock(struct wm8994_priv *wm8994, int aif) reg1 |= WM8994_AIF1CLK_DIV; } - wm8994->aifclk[aif-1] = rate; + priv->aifclk[aif - 1] = rate; - ret = wm8994_update_bits(WM8994_AIF1_CLOCKING_1 + offset, - WM8994_AIF1CLK_SRC_MASK | WM8994_AIF1CLK_DIV, - reg1); + ret = wm8994_bic_or(priv, WM8994_AIF1_CLOCKING_1 + offset, + WM8994_AIF1CLK_SRC_MASK | WM8994_AIF1CLK_DIV, + reg1); if (aif == WM8994_AIF1) - ret |= wm8994_update_bits(WM8994_CLOCKING_1, + ret |= wm8994_bic_or(priv, WM8994_CLOCKING_1, WM8994_AIF1DSPCLK_ENA_MASK | WM8994_SYSDSPCLK_ENA_MASK, WM8994_AIF1DSPCLK_ENA | WM8994_SYSDSPCLK_ENA); else if (aif == WM8994_AIF2) - ret |= wm8994_update_bits(WM8994_CLOCKING_1, + ret |= wm8994_bic_or(priv, WM8994_CLOCKING_1, WM8994_SYSCLK_SRC | WM8994_AIF2DSPCLK_ENA_MASK | WM8994_SYSDSPCLK_ENA_MASK, WM8994_SYSCLK_SRC | WM8994_AIF2DSPCLK_ENA | WM8994_SYSDSPCLK_ENA); @@ -496,33 +491,33 @@ static int configure_aif_clock(struct wm8994_priv *wm8994, int aif) /* * Configures Audio interface for the given frequency * - * @param wm8994 wm8994 information + * @param priv wm8994 information * @param aif_id Audio Interface * @param clk_id Input Clock ID * @param freq Sampling frequency in Hz * * @return -1 for error and 0 success. */ -static int wm8994_set_sysclk(struct wm8994_priv *wm8994, int aif_id, - int clk_id, unsigned int freq) +static int wm8994_set_sysclk(struct wm8994_priv *priv, int aif_id, int clk_id, + unsigned int freq) { int i; int ret = 0; - wm8994->sysclk[aif_id - 1] = clk_id; + priv->sysclk[aif_id - 1] = clk_id; switch (clk_id) { case WM8994_SYSCLK_MCLK1: - wm8994->mclk[0] = freq; + priv->mclk[0] = freq; if (aif_id == 2) { - ret = wm8994_update_bits(WM8994_AIF1_CLOCKING_2 , - WM8994_AIF2DAC_DIV_MASK , 0); + ret = wm8994_bic_or(priv, WM8994_AIF1_CLOCKING_2, + WM8994_AIF2DAC_DIV_MASK, 0); } break; case WM8994_SYSCLK_MCLK2: /* TODO: Set GPIO AF */ - wm8994->mclk[1] = freq; + priv->mclk[1] = freq; break; case WM8994_SYSCLK_FLL1: @@ -543,13 +538,14 @@ static int wm8994_set_sysclk(struct wm8994_priv *wm8994, int aif_id, __func__); return -1; } - ret = wm8994_update_bits(WM8994_CLOCKING_2, + ret = wm8994_bic_or(priv, WM8994_CLOCKING_2, WM8994_OPCLK_DIV_MASK, i); - ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_2, - WM8994_OPCLK_ENA, WM8994_OPCLK_ENA); + ret |= wm8994_bic_or(priv, WM8994_POWER_MANAGEMENT_2, + WM8994_OPCLK_ENA, + WM8994_OPCLK_ENA); } else { - ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_2, - WM8994_OPCLK_ENA, 0); + ret |= wm8994_bic_or(priv, WM8994_POWER_MANAGEMENT_2, + WM8994_OPCLK_ENA, 0); } default: @@ -558,7 +554,7 @@ static int wm8994_set_sysclk(struct wm8994_priv *wm8994, int aif_id, return -1; } - ret |= configure_aif_clock(wm8994, aif_id); + ret |= configure_aif_clock(priv, aif_id); if (ret < 0) { debug("%s: codec register access error\n", __func__); @@ -571,37 +567,38 @@ static int wm8994_set_sysclk(struct wm8994_priv *wm8994, int aif_id, /* * Initializes Volume for AIF2 to HP path * + * @param priv wm8994 information * @returns -1 for error and 0 Success. * */ -static int wm8994_init_volume_aif2_dac1(void) +static int wm8994_init_volume_aif2_dac1(struct wm8994_priv *priv) { int ret; /* Unmute AIF2DAC */ - ret = wm8994_update_bits(WM8994_AIF2_DAC_FILTERS_1, - WM8994_AIF2DAC_MUTE_MASK, 0); + ret = wm8994_bic_or(priv, WM8994_AIF2_DAC_FILTERS_1, + WM8994_AIF2DAC_MUTE_MASK, 0); - ret |= wm8994_update_bits(WM8994_AIF2_DAC_LEFT_VOLUME, - WM8994_AIF2DAC_VU_MASK | WM8994_AIF2DACL_VOL_MASK, - WM8994_AIF2DAC_VU | 0xff); + ret |= wm8994_bic_or(priv, WM8994_AIF2_DAC_LEFT_VOLUME, + WM8994_AIF2DAC_VU_MASK | WM8994_AIF2DACL_VOL_MASK, + WM8994_AIF2DAC_VU | 0xff); - ret |= wm8994_update_bits(WM8994_AIF2_DAC_RIGHT_VOLUME, - WM8994_AIF2DAC_VU_MASK | WM8994_AIF2DACR_VOL_MASK, - WM8994_AIF2DAC_VU | 0xff); + ret |= wm8994_bic_or(priv, WM8994_AIF2_DAC_RIGHT_VOLUME, + WM8994_AIF2DAC_VU_MASK | WM8994_AIF2DACR_VOL_MASK, + WM8994_AIF2DAC_VU | 0xff); - ret |= wm8994_update_bits(WM8994_DAC1_LEFT_VOLUME, - WM8994_DAC1_VU_MASK | WM8994_DAC1L_VOL_MASK | - WM8994_DAC1L_MUTE_MASK, WM8994_DAC1_VU | 0xc0); + ret |= wm8994_bic_or(priv, WM8994_DAC1_LEFT_VOLUME, + WM8994_DAC1_VU_MASK | WM8994_DAC1L_VOL_MASK | + WM8994_DAC1L_MUTE_MASK, WM8994_DAC1_VU | 0xc0); - ret |= wm8994_update_bits(WM8994_DAC1_RIGHT_VOLUME, - WM8994_DAC1_VU_MASK | WM8994_DAC1R_VOL_MASK | - WM8994_DAC1R_MUTE_MASK, WM8994_DAC1_VU | 0xc0); + ret |= wm8994_bic_or(priv, WM8994_DAC1_RIGHT_VOLUME, + WM8994_DAC1_VU_MASK | WM8994_DAC1R_VOL_MASK | + WM8994_DAC1R_MUTE_MASK, WM8994_DAC1_VU | 0xc0); /* Head Phone Volume */ - ret |= wm8994_i2c_write(WM8994_LEFT_OUTPUT_VOLUME, 0x12D); - ret |= wm8994_i2c_write(WM8994_RIGHT_OUTPUT_VOLUME, 0x12D); + ret |= wm8994_i2c_write(priv, WM8994_LEFT_OUTPUT_VOLUME, 0x12D); + ret |= wm8994_i2c_write(priv, WM8994_RIGHT_OUTPUT_VOLUME, 0x12D); if (ret < 0) { debug("%s: codec register access error\n", __func__); @@ -614,26 +611,27 @@ static int wm8994_init_volume_aif2_dac1(void) /* * Initializes Volume for AIF1 to HP path * + * @param priv wm8994 information * @returns -1 for error and 0 Success. * */ -static int wm8994_init_volume_aif1_dac1(void) +static int wm8994_init_volume_aif1_dac1(struct wm8994_priv *priv) { int ret = 0; /* Unmute AIF1DAC */ - ret |= wm8994_i2c_write(WM8994_AIF1_DAC_FILTERS_1, 0x0000); + ret |= wm8994_i2c_write(priv, WM8994_AIF1_DAC_FILTERS_1, 0x0000); - ret |= wm8994_update_bits(WM8994_DAC1_LEFT_VOLUME, - WM8994_DAC1_VU_MASK | WM8994_DAC1L_VOL_MASK | - WM8994_DAC1L_MUTE_MASK, WM8994_DAC1_VU | 0xc0); + ret |= wm8994_bic_or(priv, WM8994_DAC1_LEFT_VOLUME, + WM8994_DAC1_VU_MASK | WM8994_DAC1L_VOL_MASK | + WM8994_DAC1L_MUTE_MASK, WM8994_DAC1_VU | 0xc0); - ret |= wm8994_update_bits(WM8994_DAC1_RIGHT_VOLUME, - WM8994_DAC1_VU_MASK | WM8994_DAC1R_VOL_MASK | - WM8994_DAC1R_MUTE_MASK, WM8994_DAC1_VU | 0xc0); + ret |= wm8994_bic_or(priv, WM8994_DAC1_RIGHT_VOLUME, + WM8994_DAC1_VU_MASK | WM8994_DAC1R_VOL_MASK | + WM8994_DAC1R_MUTE_MASK, WM8994_DAC1_VU | 0xc0); /* Head Phone Volume */ - ret |= wm8994_i2c_write(WM8994_LEFT_OUTPUT_VOLUME, 0x12D); - ret |= wm8994_i2c_write(WM8994_RIGHT_OUTPUT_VOLUME, 0x12D); + ret |= wm8994_i2c_write(priv, WM8994_LEFT_OUTPUT_VOLUME, 0x12D); + ret |= wm8994_i2c_write(priv, WM8994_RIGHT_OUTPUT_VOLUME, 0x12D); if (ret < 0) { debug("%s: codec register access error\n", __func__); @@ -646,93 +644,99 @@ static int wm8994_init_volume_aif1_dac1(void) /* * Intialise wm8994 codec device * - * @param wm8994 wm8994 information + * @param priv wm8994 information * * @returns -1 for error and 0 Success. */ -static int wm8994_device_init(struct wm8994_priv *wm8994, - enum en_audio_interface aif_id) +static int wm8994_device_init(struct wm8994_priv *priv) { const char *devname; unsigned short reg_data; int ret; - wm8994_i2c_write(WM8994_SOFTWARE_RESET, WM8994_SW_RESET);/* Reset */ + wm8994_i2c_write(priv, WM8994_SOFTWARE_RESET, WM8994_SW_RESET); - ret = wm8994_i2c_read(WM8994_SOFTWARE_RESET, ®_data); + ret = wm8994_i2c_read(priv, WM8994_SOFTWARE_RESET, ®_data); if (ret < 0) { debug("Failed to read ID register\n"); - goto err; + return ret; } if (reg_data == WM8994_ID) { devname = "WM8994"; - debug("Device registered as type %d\n", wm8994->type); - wm8994->type = WM8994; + debug("Device registered as type %d\n", priv->type); + priv->type = WM8994; } else { debug("Device is not a WM8994, ID is %x\n", ret); - ret = -1; - goto err; + return -ENXIO; } - ret = wm8994_i2c_read(WM8994_CHIP_REVISION, ®_data); + ret = wm8994_i2c_read(priv, WM8994_CHIP_REVISION, ®_data); if (ret < 0) { debug("Failed to read revision register: %d\n", ret); - goto err; + return ret; } - wm8994->revision = reg_data; - debug("%s revision %c\n", devname, 'A' + wm8994->revision); + priv->revision = reg_data; + debug("%s revision %c\n", devname, 'A' + priv->revision); + + return 0; +} + +static int wm8994_setup_interface(struct wm8994_priv *priv, + enum en_audio_interface aif_id) +{ + int ret; /* VMID Selection */ - ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_1, - WM8994_VMID_SEL_MASK | WM8994_BIAS_ENA_MASK, 0x3); + ret = wm8994_bic_or(priv, WM8994_POWER_MANAGEMENT_1, + WM8994_VMID_SEL_MASK | WM8994_BIAS_ENA_MASK, 0x3); /* Charge Pump Enable */ - ret |= wm8994_update_bits(WM8994_CHARGE_PUMP_1, WM8994_CP_ENA_MASK, - WM8994_CP_ENA); + ret |= wm8994_bic_or(priv, WM8994_CHARGE_PUMP_1, WM8994_CP_ENA_MASK, + WM8994_CP_ENA); /* Head Phone Power Enable */ - ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_1, - WM8994_HPOUT1L_ENA_MASK, WM8994_HPOUT1L_ENA); + ret |= wm8994_bic_or(priv, WM8994_POWER_MANAGEMENT_1, + WM8994_HPOUT1L_ENA_MASK, WM8994_HPOUT1L_ENA); - ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_1, - WM8994_HPOUT1R_ENA_MASK, WM8994_HPOUT1R_ENA); + ret |= wm8994_bic_or(priv, WM8994_POWER_MANAGEMENT_1, + WM8994_HPOUT1R_ENA_MASK, WM8994_HPOUT1R_ENA); if (aif_id == WM8994_AIF1) { - ret |= wm8994_i2c_write(WM8994_POWER_MANAGEMENT_2, + ret |= wm8994_i2c_write(priv, WM8994_POWER_MANAGEMENT_2, WM8994_TSHUT_ENA | WM8994_MIXINL_ENA | WM8994_MIXINR_ENA | WM8994_IN2L_ENA | WM8994_IN2R_ENA); - ret |= wm8994_i2c_write(WM8994_POWER_MANAGEMENT_4, + ret |= wm8994_i2c_write(priv, WM8994_POWER_MANAGEMENT_4, WM8994_ADCL_ENA | WM8994_ADCR_ENA | WM8994_AIF1ADC1R_ENA | WM8994_AIF1ADC1L_ENA); /* Power enable for AIF1 and DAC1 */ - ret |= wm8994_i2c_write(WM8994_POWER_MANAGEMENT_5, + ret |= wm8994_i2c_write(priv, WM8994_POWER_MANAGEMENT_5, WM8994_AIF1DACL_ENA | WM8994_AIF1DACR_ENA | WM8994_DAC1L_ENA | WM8994_DAC1R_ENA); } else if (aif_id == WM8994_AIF2) { /* Power enable for AIF2 and DAC1 */ - ret |= wm8994_update_bits(WM8994_POWER_MANAGEMENT_5, + ret |= wm8994_bic_or(priv, WM8994_POWER_MANAGEMENT_5, WM8994_AIF2DACL_ENA_MASK | WM8994_AIF2DACR_ENA_MASK | WM8994_DAC1L_ENA_MASK | WM8994_DAC1R_ENA_MASK, WM8994_AIF2DACL_ENA | WM8994_AIF2DACR_ENA | WM8994_DAC1L_ENA | WM8994_DAC1R_ENA); } /* Head Phone Initialisation */ - ret |= wm8994_update_bits(WM8994_ANALOGUE_HP_1, + ret |= wm8994_bic_or(priv, WM8994_ANALOGUE_HP_1, WM8994_HPOUT1L_DLY_MASK | WM8994_HPOUT1R_DLY_MASK, WM8994_HPOUT1L_DLY | WM8994_HPOUT1R_DLY); - ret |= wm8994_update_bits(WM8994_DC_SERVO_1, + ret |= wm8994_bic_or(priv, WM8994_DC_SERVO_1, WM8994_DCS_ENA_CHAN_0_MASK | WM8994_DCS_ENA_CHAN_1_MASK , WM8994_DCS_ENA_CHAN_0 | WM8994_DCS_ENA_CHAN_1); - ret |= wm8994_update_bits(WM8994_ANALOGUE_HP_1, + ret |= wm8994_bic_or(priv, WM8994_ANALOGUE_HP_1, WM8994_HPOUT1L_DLY_MASK | WM8994_HPOUT1R_DLY_MASK | WM8994_HPOUT1L_OUTP_MASK | WM8994_HPOUT1R_OUTP_MASK | @@ -743,172 +747,130 @@ static int wm8994_device_init(struct wm8994_priv *wm8994, WM8994_HPOUT1R_RMV_SHORT); /* MIXER Config DAC1 to HP */ - ret |= wm8994_update_bits(WM8994_OUTPUT_MIXER_1, - WM8994_DAC1L_TO_HPOUT1L_MASK, WM8994_DAC1L_TO_HPOUT1L); + ret |= wm8994_bic_or(priv, WM8994_OUTPUT_MIXER_1, + WM8994_DAC1L_TO_HPOUT1L_MASK, + WM8994_DAC1L_TO_HPOUT1L); - ret |= wm8994_update_bits(WM8994_OUTPUT_MIXER_2, - WM8994_DAC1R_TO_HPOUT1R_MASK, WM8994_DAC1R_TO_HPOUT1R); + ret |= wm8994_bic_or(priv, WM8994_OUTPUT_MIXER_2, + WM8994_DAC1R_TO_HPOUT1R_MASK, + WM8994_DAC1R_TO_HPOUT1R); if (aif_id == WM8994_AIF1) { /* Routing AIF1 to DAC1 */ - ret |= wm8994_i2c_write(WM8994_DAC1_LEFT_MIXER_ROUTING, - WM8994_AIF1DAC1L_TO_DAC1L); + ret |= wm8994_i2c_write(priv, WM8994_DAC1_LEFT_MIXER_ROUTING, + WM8994_AIF1DAC1L_TO_DAC1L); - ret |= wm8994_i2c_write(WM8994_DAC1_RIGHT_MIXER_ROUTING, + ret |= wm8994_i2c_write(priv, WM8994_DAC1_RIGHT_MIXER_ROUTING, WM8994_AIF1DAC1R_TO_DAC1R); /* GPIO Settings for AIF1 */ - ret |= wm8994_i2c_write(WM8994_GPIO_1, WM8994_GPIO_DIR_OUTPUT - | WM8994_GPIO_FUNCTION_I2S_CLK - | WM8994_GPIO_INPUT_DEBOUNCE); + ret |= wm8994_i2c_write(priv, WM8994_GPIO_1, + WM8994_GPIO_DIR_OUTPUT | + WM8994_GPIO_FUNCTION_I2S_CLK | + WM8994_GPIO_INPUT_DEBOUNCE); - ret |= wm8994_init_volume_aif1_dac1(); + ret |= wm8994_init_volume_aif1_dac1(priv); } else if (aif_id == WM8994_AIF2) { /* Routing AIF2 to DAC1 */ - ret |= wm8994_update_bits(WM8994_DAC1_LEFT_MIXER_ROUTING, - WM8994_AIF2DACL_TO_DAC1L_MASK, - WM8994_AIF2DACL_TO_DAC1L); + ret |= wm8994_bic_or(priv, WM8994_DAC1_LEFT_MIXER_ROUTING, + WM8994_AIF2DACL_TO_DAC1L_MASK, + WM8994_AIF2DACL_TO_DAC1L); - ret |= wm8994_update_bits(WM8994_DAC1_RIGHT_MIXER_ROUTING, - WM8994_AIF2DACR_TO_DAC1R_MASK, - WM8994_AIF2DACR_TO_DAC1R); + ret |= wm8994_bic_or(priv, WM8994_DAC1_RIGHT_MIXER_ROUTING, + WM8994_AIF2DACR_TO_DAC1R_MASK, + WM8994_AIF2DACR_TO_DAC1R); /* GPIO Settings for AIF2 */ /* B CLK */ - ret |= wm8994_update_bits(WM8994_GPIO_3, WM8994_GPIO_DIR_MASK | - WM8994_GPIO_FUNCTION_MASK , - WM8994_GPIO_DIR_OUTPUT); + ret |= wm8994_bic_or(priv, WM8994_GPIO_3, WM8994_GPIO_DIR_MASK | + WM8994_GPIO_FUNCTION_MASK, + WM8994_GPIO_DIR_OUTPUT); /* LR CLK */ - ret |= wm8994_update_bits(WM8994_GPIO_4, WM8994_GPIO_DIR_MASK | - WM8994_GPIO_FUNCTION_MASK, - WM8994_GPIO_DIR_OUTPUT); + ret |= wm8994_bic_or(priv, WM8994_GPIO_4, WM8994_GPIO_DIR_MASK | + WM8994_GPIO_FUNCTION_MASK, + WM8994_GPIO_DIR_OUTPUT); /* DATA */ - ret |= wm8994_update_bits(WM8994_GPIO_5, WM8994_GPIO_DIR_MASK | - WM8994_GPIO_FUNCTION_MASK, - WM8994_GPIO_DIR_OUTPUT); + ret |= wm8994_bic_or(priv, WM8994_GPIO_5, WM8994_GPIO_DIR_MASK | + WM8994_GPIO_FUNCTION_MASK, + WM8994_GPIO_DIR_OUTPUT); - ret |= wm8994_init_volume_aif2_dac1(); + ret |= wm8994_init_volume_aif2_dac1(priv); } if (ret < 0) goto err; - debug("%s: Codec chip init ok\n", __func__); + debug("%s: Codec chip setup ok\n", __func__); return 0; err: - debug("%s: Codec chip init error\n", __func__); + debug("%s: Codec chip setup error\n", __func__); return -1; } -/* - * Gets fdt values for wm8994 config parameters - * - * @param pcodec_info codec information structure - * @param blob FDT blob - * @return int value, 0 for success - */ -static int get_codec_values(struct sound_codec_info *pcodec_info, - const void *blob) +static int _wm8994_init(struct wm8994_priv *priv, + enum en_audio_interface aif_id, int sampling_rate, + int mclk_freq, int bits_per_sample, + unsigned int channels) { - int error = 0; -#if CONFIG_IS_ENABLED(OF_CONTROL) - enum fdt_compat_id compat; - int node; - int parent; - - /* Get the node from FDT for codec */ - node = fdtdec_next_compatible(blob, 0, COMPAT_WOLFSON_WM8994_CODEC); - if (node <= 0) { - debug("EXYNOS_SOUND: No node for codec in device tree\n"); - debug("node = %d\n", node); - return -1; - } + int ret; - parent = fdt_parent_offset(blob, node); - if (parent < 0) { - debug("%s: Cannot find node parent\n", __func__); - return -1; + ret = wm8994_setup_interface(priv, aif_id); + if (ret < 0) { + debug("%s: wm8994 codec chip init failed\n", __func__); + return ret; } - compat = fdtdec_lookup(blob, parent); - switch (compat) { - case COMPAT_SAMSUNG_S3C2440_I2C: - pcodec_info->i2c_bus = i2c_get_bus_num_fdt(parent); - error |= pcodec_info->i2c_bus; - debug("i2c bus = %d\n", pcodec_info->i2c_bus); - pcodec_info->i2c_dev_addr = fdtdec_get_int(blob, node, - "reg", 0); - error |= pcodec_info->i2c_dev_addr; - debug("i2c dev addr = %d\n", pcodec_info->i2c_dev_addr); - break; - default: - debug("%s: Unknown compat id %d\n", __func__, compat); - return -1; + ret = wm8994_set_sysclk(priv, aif_id, WM8994_SYSCLK_MCLK1, mclk_freq); + if (ret < 0) { + debug("%s: wm8994 codec set sys clock failed\n", __func__); + return ret; } -#else - pcodec_info->i2c_bus = AUDIO_I2C_BUS; - pcodec_info->i2c_dev_addr = AUDIO_I2C_REG; - debug("i2c dev addr = %d\n", pcodec_info->i2c_dev_addr); -#endif - pcodec_info->codec_type = CODEC_WM_8994; + ret = wm8994_hw_params(priv, aif_id, sampling_rate, bits_per_sample, + channels); - if (error == -1) { - debug("fail to get wm8994 codec node properties\n"); - return -1; + if (ret == 0) { + ret = wm8994_set_fmt(priv, aif_id, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS); } - return 0; + return ret; } -/* WM8994 Device Initialisation */ -int wm8994_init(const void *blob, enum en_audio_interface aif_id, - int sampling_rate, int mclk_freq, - int bits_per_sample, unsigned int channels) +static int wm8994_set_params(struct udevice *dev, int interface, int rate, + int mclk_freq, int bits_per_sample, uint channels) { - int ret = 0; - struct sound_codec_info *pcodec_info = &g_codec_info; + struct wm8994_priv *priv = dev_get_priv(dev); - /* Get the codec Values */ - if (get_codec_values(pcodec_info, blob) < 0) { - debug("FDT Codec values failed\n"); - return -1; - } - - /* shift the device address by 1 for 7 bit addressing */ - g_wm8994_i2c_dev_addr = pcodec_info->i2c_dev_addr; - wm8994_i2c_init(pcodec_info->i2c_bus); + return _wm8994_init(priv, interface, rate, mclk_freq, bits_per_sample, + channels); +} - if (pcodec_info->codec_type == CODEC_WM_8994) { - g_wm8994_info.type = WM8994; - } else { - debug("%s: Codec id [%d] not defined\n", __func__, - pcodec_info->codec_type); - return -1; - } +static int wm8994_probe(struct udevice *dev) +{ + struct wm8994_priv *priv = dev_get_priv(dev); - ret = wm8994_device_init(&g_wm8994_info, aif_id); - if (ret < 0) { - debug("%s: wm8994 codec chip init failed\n", __func__); - return ret; - } + priv->dev = dev; + return wm8994_device_init(priv); +} - ret = wm8994_set_sysclk(&g_wm8994_info, aif_id, WM8994_SYSCLK_MCLK1, - mclk_freq); - if (ret < 0) { - debug("%s: wm8994 codec set sys clock failed\n", __func__); - return ret; - } +static const struct audio_codec_ops wm8994_ops = { + .set_params = wm8994_set_params, +}; - ret = wm8994_hw_params(&g_wm8994_info, aif_id, sampling_rate, - bits_per_sample, channels); +static const struct udevice_id wm8994_ids[] = { + { .compatible = "wolfson,wm8994" }, + { } +}; - if (ret == 0) { - ret = wm8994_set_fmt(aif_id, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS); - } - return ret; -} +U_BOOT_DRIVER(wm8994) = { + .name = "wm8994", + .id = UCLASS_AUDIO_CODEC, + .of_match = wm8994_ids, + .probe = wm8994_probe, + .ops = &wm8994_ops, + .priv_auto_alloc_size = sizeof(struct wm8994_priv), +}; diff --git a/drivers/sound/wm8994.h b/drivers/sound/wm8994.h index ef2878f87c..e36e6269f0 100644 --- a/drivers/sound/wm8994.h +++ b/drivers/sound/wm8994.h @@ -15,7 +15,7 @@ /* Avilable audi interface ports in wm8994 codec */ enum en_audio_interface { - WM8994_AIF1 = 1, + WM8994_AIF1, WM8994_AIF2, WM8994_AIF3 }; diff --git a/include/audio_codec.h b/include/audio_codec.h new file mode 100644 index 0000000000..2587099546 --- /dev/null +++ b/include/audio_codec.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2018 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#ifndef __AUDIO_CODEC_H__ +#define __AUDIO_CODEC_H__ + +/* + * An audio codec turns digital data into sound with various parameters to + * control its operation. + */ + +/* Operations for sound */ +struct audio_codec_ops { + /** + * set_params() - Set audio codec parameters + * + * @dev: Sound device + * @inteface: Interface number to use on codec + * @rate: Sampling rate in Hz + * @mclk_freq: Codec clock frequency in Hz + * @bits_per_sample: Must be 16 or 24 + * @channels: Number of channels to use (1=mono, 2=stereo) + * @return 0 if OK, -ve on error + */ + int (*set_params)(struct udevice *dev, int interface, int rate, + int mclk_freq, int bits_per_sample, uint channels); +}; + +#define audio_codec_get_ops(dev) ((struct audio_codec_ops *)(dev)->driver->ops) + +/** + * audio_codec_set_params() - Set audio codec parameters + * + * @dev: Sound device + * @inteface: Interface number to use on codec + * @rate: Sampling rate in Hz + * @mclk_freq: Codec clock frequency in Hz + * @bits_per_sample: Must be 16 or 24 + * @channels: Number of channels to use (1=mono, 2=stereo) + * @return 0 if OK, -ve on error + */ +int audio_codec_set_params(struct udevice *dev, int interface, int rate, + int mclk_freq, int bits_per_sample, uint channels); + +#endif /* __AUDIO_CODEC_H__ */ diff --git a/include/dm/read.h b/include/dm/read.h index efcbee15ec..389e30e7fb 100644 --- a/include/dm/read.h +++ b/include/dm/read.h @@ -65,6 +65,38 @@ int dev_read_u32(struct udevice *dev, const char *propname, u32 *outp); int dev_read_u32_default(struct udevice *dev, const char *propname, int def); /** + * dev_read_s32() - read a signed 32-bit integer from a device's DT property + * + * @dev: device to read DT property from + * @propname: name of the property to read from + * @outp: place to put value (if found) + * @return 0 if OK, -ve on error + */ +int dev_read_s32(struct udevice *dev, const char *propname, s32 *outp); + +/** + * dev_read_s32_default() - read a signed 32-bit int from a device's DT property + * + * @dev: device to read DT property from + * @propname: name of the property to read from + * @def: default value to return if the property has no value + * @return property value, or @def if not found + */ +int dev_read_s32_default(struct udevice *dev, const char *propname, int def); + +/** + * dev_read_u32u() - read a 32-bit integer from a device's DT property + * + * This version uses a standard uint type. + * + * @dev: device to read DT property from + * @propname: name of the property to read from + * @outp: place to put value (if found) + * @return 0 if OK, -ve on error + */ +int dev_read_u32u(struct udevice *dev, const char *propname, uint *outp); + +/** * dev_read_string() - Read a string from a device's DT property * * @dev: device to read DT property from @@ -492,6 +524,32 @@ static inline int dev_read_u32_default(struct udevice *dev, return ofnode_read_u32_default(dev_ofnode(dev), propname, def); } +static inline int dev_read_s32(struct udevice *dev, + const char *propname, s32 *outp) +{ + return ofnode_read_s32(dev_ofnode(dev), propname, outp); +} + +static inline int dev_read_s32_default(struct udevice *dev, + const char *propname, int def) +{ + return ofnode_read_s32_default(dev_ofnode(dev), propname, def); +} + +static inline int dev_read_u32u(struct udevice *dev, + const char *propname, uint *outp) +{ + u32 val; + int ret; + + ret = ofnode_read_u32(dev_ofnode(dev), propname, &val); + if (ret) + return ret; + *outp = val; + + return 0; +} + static inline const char *dev_read_string(struct udevice *dev, const char *propname) { diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index e960e48b85..f3bafb3c63 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -29,6 +29,7 @@ enum uclass_id { /* U-Boot uclasses start here - in alphabetical order */ UCLASS_ADC, /* Analog-to-digital converter */ UCLASS_AHCI, /* SATA disk controller */ + UCLASS_AUDIO_CODEC, /* Audio codec with control and data path */ UCLASS_AXI, /* AXI bus */ UCLASS_BLK, /* Block device */ UCLASS_BOARD, /* Device information from hardware */ @@ -48,6 +49,7 @@ enum uclass_id { UCLASS_I2C_EEPROM, /* I2C EEPROM device */ UCLASS_I2C_GENERIC, /* Generic I2C device */ UCLASS_I2C_MUX, /* I2C multiplexer */ + UCLASS_I2S, /* I2S bus */ UCLASS_IDE, /* IDE device */ UCLASS_IRQ, /* Interrupt controller */ UCLASS_KEYBOARD, /* Keyboard input device */ @@ -82,6 +84,7 @@ enum uclass_id { UCLASS_SERIAL, /* Serial UART */ UCLASS_SIMPLE_BUS, /* Bus with child devices */ UCLASS_SMEM, /* Shared memory interface */ + UCLASS_SOUND, /* Playing simple sounds */ UCLASS_SPI, /* SPI bus */ UCLASS_SPI_FLASH, /* SPI flash */ UCLASS_SPI_GENERIC, /* Generic SPI flash target */ diff --git a/include/i2s.h b/include/i2s.h index e6d45ec26d..28f6184811 100644 --- a/include/i2s.h +++ b/include/i2s.h @@ -76,7 +76,7 @@ struct i2s_reg { }; /* This structure stores the i2s related information */ -struct i2stx_info { +struct i2s_uc_priv { unsigned int rfs; /* LR clock frame size */ unsigned int bfs; /* Bit slock frame size */ unsigned int audio_pll_clk; /* Audio pll frequency in Hz */ @@ -87,17 +87,41 @@ struct i2stx_info { unsigned int id; /* I2S controller id */ }; +/* Operations for i2s devices */ +struct i2s_ops { + /** + * tx_data() - Transmit audio data + * + * @dev: I2C device + * @data: Data buffer to play + * @data_size: Size of data buffer in bytes + * @return 0 if OK, -ve on error + */ + int (*tx_data)(struct udevice *dev, void *data, uint data_size); +}; + +#define i2s_get_ops(dev) ((struct i2s_ops *)(dev)->driver->ops) + +/** + * i2s_tx_data() - Transmit audio data + * + * @dev: I2C device + * @data: Data buffer to play + * @data_size: Size of data buffer in bytes + * @return 0 if OK, -ve on error + */ +int i2s_tx_data(struct udevice *dev, void *data, uint data_size); + /* * Sends the given data through i2s tx * * @param pi2s_tx pointer of i2s transmitter parameter structure. * @param data address of the data buffer - * @param data_size array size of the int buffer (total size / size of int) - * + * @param data_size size of the data (in bytes) * @return int value 0 for success, -1 in case of error */ -int i2s_transfer_tx_data(struct i2stx_info *pi2s_tx, unsigned *data, - unsigned long data_size); +int i2s_transfer_tx_data(struct i2s_uc_priv *pi2s_tx, void *data, + uint data_size); /* * Initialise i2s transmiter @@ -106,6 +130,6 @@ int i2s_transfer_tx_data(struct i2stx_info *pi2s_tx, unsigned *data, * * @return int value 0 for success, -1 in case of error */ -int i2s_tx_init(struct i2stx_info *pi2s_tx); +int i2s_tx_init(struct i2s_uc_priv *pi2s_tx); #endif /* __I2S_H__ */ diff --git a/include/sound.h b/include/sound.h index 77bfe6a93b..b7959cc260 100644 --- a/include/sound.h +++ b/include/sound.h @@ -8,14 +8,6 @@ #define __SOUND_H__ /* sound codec enum */ -enum en_sound_codec { - CODEC_WM_8994, - CODEC_WM_8995, - CODEC_MAX_98095, - CODEC_MAX -}; - -/* sound codec enum */ enum sound_compat { AUDIO_COMPAT_SPI, AUDIO_COMPAT_I2C, @@ -25,33 +17,81 @@ enum sound_compat { struct sound_codec_info { int i2c_bus; int i2c_dev_addr; - enum en_sound_codec codec_type; }; -/* +/** + * struct sound_uc_priv - private uclass information about each sound device + * + * This is used to line the codec and i2s together + * + * @codec: Codec that is used for this sound device + * @i2s: I2S bus that is used for this sound device + * @setup_done: true if setup() has been called + */ +struct sound_uc_priv { + struct udevice *codec; + struct udevice *i2s; + int setup_done; +}; + +/** * Generates square wave sound data for 1 second * - * @param sample_rate Sample rate in Hz - * @param data data buffer pointer - * @param size size of the buffer - * @param freq frequency of the wave + * @sample_rate: Sample rate in Hz + * @data: data buffer pointer + * @size: size of the buffer in bytes + * @freq: frequency of the wave + * @channels: Number of channels to use */ void sound_create_square_wave(uint sample_rate, unsigned short *data, int size, - uint freq); + uint freq, uint channels); /* - * Initialises audio sub system - * @param blob Pointer of device tree node or NULL if none. - * @return int value 0 for success, -1 for error + * The sound uclass brings together a data transport (currently only I2C) and a + * codec (currently connected over I2C). */ -int sound_init(const void *blob); -/* - * plays the pcm data buffer in pcm_data.h through i2s1 to make the - * sine wave sound +/* Operations for sound */ +struct sound_ops { + /** + * setup() - Set up to play a sound + */ + int (*setup)(struct udevice *dev); + + /** + * play() - Play a beep + * + * @dev: Sound device + * @data: Data buffer to play + * @data_size: Size of data buffer in bytes + * @return 0 if OK, -ve on error + */ + int (*play)(struct udevice *dev, void *data, uint data_size); +}; + +#define sound_get_ops(dev) ((struct sound_ops *)(dev)->driver->ops) + +/** + * setup() - Set up to play a sound + */ +int sound_setup(struct udevice *dev); + +/** + * play() - Play a beep + * + * @dev: Sound device + * @msecs: Duration of beep in milliseconds + * @frequency_hz: Frequency of the beep in Hertz + * @return 0 if OK, -ve on error + */ +int sound_beep(struct udevice *dev, int msecs, int frequency_hz); + +/** + * sound_find_codec_i2s() - Called by sound drivers to locate codec and i2s * - * @return int 0 for success, -1 for error + * This finds the audio codec and i2s devices and puts them in the uclass's + * private data for this device. */ -int sound_play(uint32_t msec, uint32_t frequency); +int sound_find_codec_i2s(struct udevice *dev); #endif /* __SOUND__H__ */ diff --git a/test/dm/Makefile b/test/dm/Makefile index 2c9081e4dd..6b451060d8 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_UT_DM) += test-uclass.o # subsystem you must add sandbox tests here. obj-$(CONFIG_UT_DM) += core.o ifneq ($(CONFIG_SANDBOX),) +obj-$(CONFIG_SOUND) += audio.o obj-$(CONFIG_BLK) += blk.o obj-$(CONFIG_BOARD) += board.o obj-$(CONFIG_CLK) += clk.o @@ -21,6 +22,7 @@ obj-$(CONFIG_FIRMWARE) += firmware.o obj-$(CONFIG_DM_GPIO) += gpio.o obj-$(CONFIG_DM_HWSPINLOCK) += hwspinlock.o obj-$(CONFIG_DM_I2C) += i2c.o +obj-$(CONFIG_SOUND) += i2s.o obj-$(CONFIG_LED) += led.o obj-$(CONFIG_DM_MAILBOX) += mailbox.o obj-$(CONFIG_DM_MMC) += mmc.o @@ -53,6 +55,7 @@ obj-$(CONFIG_AXI) += axi.o obj-$(CONFIG_MISC) += misc.o obj-$(CONFIG_DM_SERIAL) += serial.o obj-$(CONFIG_CPU) += cpu.o +obj-$(CONFIG_SOUND) += sound.o obj-$(CONFIG_TEE) += tee.o obj-$(CONFIG_VIRTIO_SANDBOX) += virtio.o obj-$(CONFIG_DMA) += dma.o diff --git a/test/dm/audio.c b/test/dm/audio.c new file mode 100644 index 0000000000..77c3a3625b --- /dev/null +++ b/test/dm/audio.c @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <audio_codec.h> +#include <dm.h> +#include <dm/test.h> +#include <test/ut.h> +#include <asm/test.h> + +/* Basic test of the audio codec uclass */ +static int dm_test_audio(struct unit_test_state *uts) +{ + int interface, rate, mclk_freq, bits_per_sample; + struct udevice *dev; + uint channels; + + /* check probe success */ + ut_assertok(uclass_first_device_err(UCLASS_AUDIO_CODEC, &dev)); + ut_assertok(audio_codec_set_params(dev, 1, 2, 3, 4, 5)); + sandbox_get_codec_params(dev, &interface, &rate, &mclk_freq, + &bits_per_sample, &channels); + ut_asserteq(1, interface); + ut_asserteq(2, rate); + ut_asserteq(3, mclk_freq); + ut_asserteq(4, bits_per_sample); + ut_asserteq(5, channels); + + return 0; +} +DM_TEST(dm_test_audio, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); diff --git a/test/dm/i2s.c b/test/dm/i2s.c new file mode 100644 index 0000000000..49ebc3523c --- /dev/null +++ b/test/dm/i2s.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <dm.h> +#include <i2s.h> +#include <dm/test.h> +#include <test/ut.h> +#include <asm/test.h> + +/* Basic test of the i2s codec uclass */ +static int dm_test_i2s(struct unit_test_state *uts) +{ + struct udevice *dev; + u8 data[3]; + + /* check probe success */ + ut_assertok(uclass_first_device_err(UCLASS_I2S, &dev)); + data[0] = 1; + data[1] = 4; + data[2] = 6; + ut_assertok(i2s_tx_data(dev, data, ARRAY_SIZE(data))); + ut_asserteq(11, sandbox_get_i2s_sum(dev)); + ut_assertok(i2s_tx_data(dev, data, 1)); + ut_asserteq(12, sandbox_get_i2s_sum(dev)); + + return 0; +} +DM_TEST(dm_test_i2s, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); diff --git a/test/dm/sound.c b/test/dm/sound.c new file mode 100644 index 0000000000..7d0b36e7a5 --- /dev/null +++ b/test/dm/sound.c @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <dm.h> +#include <sound.h> +#include <dm/test.h> +#include <test/ut.h> +#include <asm/test.h> + +/* Basic test of the sound codec uclass */ +static int dm_test_sound(struct unit_test_state *uts) +{ + struct sound_uc_priv *uc_priv; + struct udevice *dev; + + /* check probe success */ + ut_assertok(uclass_first_device_err(UCLASS_SOUND, &dev)); + uc_priv = dev_get_uclass_priv(dev); + ut_asserteq_str("audio-codec", uc_priv->codec->name); + ut_asserteq_str("i2s", uc_priv->i2s->name); + ut_asserteq(0, sandbox_get_setup_called(dev)); + + ut_assertok(sound_beep(dev, 1, 100)); + ut_asserteq(4560, sandbox_get_sound_sum(dev)); + ut_assertok(sound_beep(dev, 1, 100)); + ut_asserteq(9120, sandbox_get_sound_sum(dev)); + + return 0; +} +DM_TEST(dm_test_sound, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); diff --git a/test/dm/test-fdt.c b/test/dm/test-fdt.c index 96d2528acc..984b80c02c 100644 --- a/test/dm/test-fdt.c +++ b/test/dm/test-fdt.c @@ -736,3 +736,38 @@ static int dm_test_first_child(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_first_child, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); + +/* Test integer functions in dm_read_...() */ +static int dm_test_read_int(struct unit_test_state *uts) +{ + struct udevice *dev; + u32 val32; + s32 sval; + uint val; + + ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev)); + ut_asserteq_str("a-test", dev->name); + ut_assertok(dev_read_u32(dev, "int-value", &val32)); + ut_asserteq(1234, val32); + + ut_asserteq(-EINVAL, dev_read_u32(dev, "missing", &val32)); + ut_asserteq(6, dev_read_u32_default(dev, "missing", 6)); + + ut_asserteq(1234, dev_read_u32_default(dev, "int-value", 6)); + ut_asserteq(1234, val32); + + ut_asserteq(-EINVAL, dev_read_s32(dev, "missing", &sval)); + ut_asserteq(6, dev_read_s32_default(dev, "missing", 6)); + + ut_asserteq(-1234, dev_read_s32_default(dev, "uint-value", 6)); + ut_assertok(dev_read_s32(dev, "uint-value", &sval)); + ut_asserteq(-1234, sval); + + val = 0; + ut_asserteq(-EINVAL, dev_read_u32u(dev, "missing", &val)); + ut_assertok(dev_read_u32u(dev, "uint-value", &val)); + ut_asserteq(-1234, val); + + return 0; +} +DM_TEST(dm_test_read_int, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT); |