Complete conversion of sound to driver model

-----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEEslwAIq+Gp8wWVbYnfxc6PpAIreYFAlwTzzsACgkQfxc6PpAI
 reYztgf+I1jVm7q/pPAMqtUWArQaXabcHIbqR/if+ZTssK/RRJQXd0ZmZa/WbVLS
 FdORnuPNrUY+M4NEHQPAce2bclLxb6P0TFbfE5FRoCRxkaZdjSCglibpWe4qWu1m
 rmifPRGoSKqzMhuC+Xb2qRSyT40J0gjCMuKd053TIcblMvus0foavxq8JMk5iiMD
 vmUlaCdacYH2BSltFTExkAH/6AVspSIhkXl5TSGk8zEpOkRDeEiuOf+DGmBEE+aa
 Qpw8DqUhC0EOqyxGWfpT1IiqYWF+JqJDrrfKEViTZ0U/fSBP3suMewarBXAZrEuw
 UFXNa0luFHMoeVarhk4bbbDTPnNEeQ==
 =Y3Tt
 -----END PGP SIGNATURE-----

Merge tag 'dm-pull-14dec18' of git://git.denx.de/u-boot-dm

Complete conversion of sound to driver model
This commit is contained in:
Tom Rini 2018-12-14 14:18:47 -05:00
commit 8fc26fce41
65 changed files with 2914 additions and 926 deletions

View file

@ -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,

View file

@ -116,6 +116,7 @@ config SANDBOX
imply VIRTIO_SANDBOX
imply VIRTIO_BLK
imply VIRTIO_NET
imply DM_SOUND
config SH
bool "SuperH architecture"

View file

@ -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>;
};
};

View file

@ -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 {

View file

@ -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"

View file

@ -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>;

View file

@ -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";

View file

@ -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>;
};
};

View file

@ -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>;

View file

@ -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 {

View file

@ -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;
}

View file

@ -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();

View file

@ -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;

View file

@ -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 */

View file

@ -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; \
} \

View file

@ -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;

View file

@ -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);

View file

@ -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);

View file

@ -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;
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, sdl.audio_data + sdl.audio_pos, len,
SDL_MIX_MAXVOLUME);
sdl.audio_pos += len;
SDL_MixAudio(stream, buf->data + buf->pos, avail,
SDL_MIX_MAXVOLUME);
buf->pos += avail;
len -= avail;
/* Loop if we are at the end */
if (sdl.audio_pos == sdl.audio_size)
sdl.audio_pos = 0;
/* 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;
}

View file

@ -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>;

View file

@ -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>;

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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",
};

View file

@ -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),
};

377
drivers/sound/max98090.c Normal file
View file

@ -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),
};

663
drivers/sound/max98090.h Normal file
View file

@ -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

View file

@ -1,112 +1,31 @@
// 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
*
@ -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, &regval)) {
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;
struct maxim_priv *priv = dev_get_priv(dev);
/* 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;
}
return max98095_do_init(priv, interface, rate, mclk_freq,
bits_per_sample);
}
parent = fdt_parent_offset(blob, node);
if (parent < 0) {
debug("%s: Cannot find node parent\n", __func__);
return -1;
}
static int max98095_probe(struct udevice *dev)
{
struct maxim_priv *priv = dev_get_priv(dev);
int 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 = %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;
priv->dev = dev;
ret = max98095_device_init(priv);
if (ret < 0) {
debug("%s: max98095 codec chip init failed\n", __func__);
return ret;
}
return 0;
}
/* 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)
{
int ret;
int old_bus = i2c_get_bus_num();
struct sound_codec_info *pcodec_info = &g_codec_info;
static const struct audio_codec_ops max98095_ops = {
.set_params = max98095_set_params,
};
if (get_max98095_codec_values(pcodec_info, blob) < 0) {
debug("FDT Codec values failed\n");
return -1;
}
static const struct udevice_id max98095_ids[] = {
{ .compatible = "maxim,max98095" },
{ }
};
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;
}
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),
};

View file

@ -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,
};

View file

@ -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;
}

View file

@ -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__ */

View file

@ -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,
};

View file

@ -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,
};

View file

@ -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)
{
sandbox_sdl_sound_start(frequency);
mdelay(msec);
sandbox_sdl_sound_stop();
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;
}
int sound_init(const void *blob)
static int sandbox_i2s_tx_data(struct udevice *dev, void *data,
uint data_size)
{
return sandbox_sdl_sound_init();
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)
{
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;
}
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 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,
};

View file

@ -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;
}

View file

@ -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),
};

View file

@ -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;
}
}
}

View file

@ -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, &reg_data) != 0) {
if (wm8994_i2c_read(priv, aif1_reg, &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, &reg_data);
ret = wm8994_i2c_read(priv, WM8994_SOFTWARE_RESET, &reg_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, &reg_data);
ret = wm8994_i2c_read(priv, WM8994_CHIP_REVISION, &reg_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;
int ret;
/* 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;
}
parent = fdt_parent_offset(blob, node);
if (parent < 0) {
debug("%s: Cannot find node parent\n", __func__);
return -1;
}
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;
}
#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;
if (error == -1) {
debug("fail to get wm8994 codec node properties\n");
return -1;
}
return 0;
}
/* 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)
{
int ret = 0;
struct sound_codec_info *pcodec_info = &g_codec_info;
/* 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);
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;
}
ret = wm8994_device_init(&g_wm8994_info, aif_id);
ret = wm8994_setup_interface(priv, aif_id);
if (ret < 0) {
debug("%s: wm8994 codec chip init failed\n", __func__);
return ret;
}
ret = wm8994_set_sysclk(&g_wm8994_info, aif_id, WM8994_SYSCLK_MCLK1,
mclk_freq);
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;
}
ret = wm8994_hw_params(&g_wm8994_info, aif_id, sampling_rate,
bits_per_sample, channels);
ret = wm8994_hw_params(priv, aif_id, sampling_rate, bits_per_sample,
channels);
if (ret == 0) {
ret = wm8994_set_fmt(aif_id, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS);
ret = wm8994_set_fmt(priv, aif_id, SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS);
}
return ret;
}
static int wm8994_set_params(struct udevice *dev, int interface, int rate,
int mclk_freq, int bits_per_sample, uint channels)
{
struct wm8994_priv *priv = dev_get_priv(dev);
return _wm8994_init(priv, interface, rate, mclk_freq, bits_per_sample,
channels);
}
static int wm8994_probe(struct udevice *dev)
{
struct wm8994_priv *priv = dev_get_priv(dev);
priv->dev = dev;
return wm8994_device_init(priv);
}
static const struct audio_codec_ops wm8994_ops = {
.set_params = wm8994_set_params,
};
static const struct udevice_id wm8994_ids[] = {
{ .compatible = "wolfson,wm8994" },
{ }
};
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),
};

View file

@ -15,7 +15,7 @@
/* Avilable audi interface ports in wm8994 codec */
enum en_audio_interface {
WM8994_AIF1 = 1,
WM8994_AIF1,
WM8994_AIF2,
WM8994_AIF3
};

48
include/audio_codec.h Normal file
View file

@ -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__ */

View file

@ -64,6 +64,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
*
@ -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)
{

View file

@ -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 */

View file

@ -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__ */

View file

@ -7,14 +7,6 @@
#ifndef __SOUND_H__
#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,
@ -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
*
* @return int 0 for success, -1 for error
* @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_play(uint32_t msec, uint32_t frequency);
int sound_beep(struct udevice *dev, int msecs, int frequency_hz);
/**
* sound_find_codec_i2s() - Called by sound drivers to locate codec and i2s
*
* This finds the audio codec and i2s devices and puts them in the uclass's
* private data for this device.
*/
int sound_find_codec_i2s(struct udevice *dev);
#endif /* __SOUND__H__ */

View file

@ -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

34
test/dm/audio.c Normal file
View file

@ -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);

32
test/dm/i2s.c Normal file
View file

@ -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);

34
test/dm/sound.c Normal file
View file

@ -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);

View file

@ -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);